• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2011 Daniel Drown
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  * ipv6.c - takes ipv6 packets, finds their headers, and then calls translation functions on them
17  */
18 #include <string.h>
19 
20 #include <arpa/inet.h>
21 
22 #include "translate.h"
23 #include "checksum.h"
24 #include "logging.h"
25 #include "dump.h"
26 #include "config.h"
27 #include "debug.h"
28 
29 /* function: icmp6_packet
30  * takes an icmp6 packet and sets it up for translation
31  * out      - output packet
32  * icmp6    - pointer to icmp6 header in packet
33  * checksum - pseudo-header checksum (unused)
34  * len      - size of ip payload
35  * returns: the highest position in the output clat_packet that's filled in
36  */
icmp6_packet(clat_packet out,int pos,const struct icmp6_hdr * icmp6,size_t len)37 int icmp6_packet(clat_packet out, int pos, const struct icmp6_hdr *icmp6, size_t len) {
38   const char *payload;
39   size_t payload_size;
40 
41   if(len < sizeof(struct icmp6_hdr)) {
42     logmsg_dbg(ANDROID_LOG_ERROR, "icmp6_packet/(too small)");
43     return 0;
44   }
45 
46   payload = (const char *) (icmp6 + 1);
47   payload_size = len - sizeof(struct icmp6_hdr);
48 
49   return icmp6_to_icmp(out, pos, icmp6, payload, payload_size);
50 }
51 
52 /* function: log_bad_address
53  * logs a bad address to android's log buffer if debugging is turned on
54  * fmt     - printf-style format, use %s to place the address
55  * badaddr - the bad address in question
56  */
57 #if CLAT_DEBUG
log_bad_address(const char * fmt,const struct in6_addr * src,const struct in6_addr * dst)58 void log_bad_address(const char *fmt, const struct in6_addr *src, const struct in6_addr *dst) {
59   char srcstr[INET6_ADDRSTRLEN];
60   char dststr[INET6_ADDRSTRLEN];
61 
62   inet_ntop(AF_INET6, src, srcstr, sizeof(srcstr));
63   inet_ntop(AF_INET6, dst, dststr, sizeof(dststr));
64   logmsg_dbg(ANDROID_LOG_ERROR, fmt, srcstr, dststr);
65 }
66 #else
67 #define log_bad_address(fmt, src, dst)
68 #endif
69 
70 /* function: ipv6_packet
71  * takes an ipv6 packet and hands it off to the layer 4 protocol function
72  * out    - output packet
73  * packet - packet data
74  * len    - size of packet
75  * returns: the highest position in the output clat_packet that's filled in
76  */
ipv6_packet(clat_packet out,int pos,const char * packet,size_t len)77 int ipv6_packet(clat_packet out, int pos, const char *packet, size_t len) {
78   const struct ip6_hdr *ip6 = (struct ip6_hdr *) packet;
79   struct iphdr *ip_targ = (struct iphdr *) out[pos].iov_base;
80   struct ip6_frag *frag_hdr = NULL;
81   uint8_t protocol;
82   const char *next_header;
83   size_t len_left;
84   uint32_t old_sum, new_sum;
85   int iov_len;
86 
87   if(len < sizeof(struct ip6_hdr)) {
88     logmsg_dbg(ANDROID_LOG_ERROR, "ipv6_packet/too short for an ip6 header: %d", len);
89     return 0;
90   }
91 
92   if(IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
93     log_bad_address("ipv6_packet/multicast %s->%s", &ip6->ip6_src, &ip6->ip6_dst);
94     return 0; // silently ignore
95   }
96 
97   // If the packet is not from the plat subnet to the local subnet, or vice versa, drop it, unless
98   // it's an ICMP packet (which can come from anywhere). We do not send IPv6 packets from the plat
99   // subnet to the local subnet, but these can appear as inner packets in ICMP errors, so we need
100   // to translate them. We accept third-party ICMPv6 errors, even though their source addresses
101   // cannot be translated, so that things like unreachables and traceroute will work. fill_ip_header
102   // takes care of faking a source address for them.
103   if (!(is_in_plat_subnet(&ip6->ip6_src) &&
104         IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &Global_Clatd_Config.ipv6_local_subnet)) &&
105       !(is_in_plat_subnet(&ip6->ip6_dst) &&
106         IN6_ARE_ADDR_EQUAL(&ip6->ip6_src, &Global_Clatd_Config.ipv6_local_subnet)) &&
107       ip6->ip6_nxt != IPPROTO_ICMPV6) {
108     log_bad_address("ipv6_packet/wrong source address: %s->%s", &ip6->ip6_src, &ip6->ip6_dst);
109     return 0;
110   }
111 
112   next_header = packet + sizeof(struct ip6_hdr);
113   len_left = len - sizeof(struct ip6_hdr);
114 
115   protocol = ip6->ip6_nxt;
116 
117   /* Fill in the IPv4 header. We need to do this before we translate the packet because TCP and
118    * UDP include parts of the IP header in the checksum. Set the length to zero because we don't
119    * know it yet.
120    */
121   fill_ip_header(ip_targ, 0, protocol, ip6);
122   out[pos].iov_len = sizeof(struct iphdr);
123 
124   // If there's a Fragment header, parse it and decide what the next header is.
125   // Do this before calculating the pseudo-header checksum because it updates the next header value.
126   if (protocol == IPPROTO_FRAGMENT) {
127     frag_hdr = (struct ip6_frag *) next_header;
128     if (len_left < sizeof(*frag_hdr)) {
129       logmsg_dbg(ANDROID_LOG_ERROR, "ipv6_packet/too short for fragment header: %d", len);
130       return 0;
131     }
132 
133     next_header += sizeof(*frag_hdr);
134     len_left -= sizeof(*frag_hdr);
135 
136     protocol = parse_frag_header(frag_hdr, ip_targ);
137   }
138 
139   // ICMP and ICMPv6 have different protocol numbers.
140   if (protocol == IPPROTO_ICMPV6) {
141     protocol = IPPROTO_ICMP;
142     ip_targ->protocol = IPPROTO_ICMP;
143   }
144 
145   /* Calculate the pseudo-header checksum.
146    * Technically, the length that is used in the pseudo-header checksum is the transport layer
147    * length, which is not the same as len_left in the case of fragmented packets. But since
148    * translation does not change the transport layer length, the checksum is unaffected.
149    */
150   old_sum = ipv6_pseudo_header_checksum(ip6, len_left, protocol);
151   new_sum = ipv4_pseudo_header_checksum(ip_targ, len_left);
152 
153   // Does not support IPv6 extension headers except Fragment.
154   if (frag_hdr && (frag_hdr->ip6f_offlg & IP6F_OFF_MASK)) {
155     iov_len = generic_packet(out, pos + 2, next_header, len_left);
156   } else if (protocol == IPPROTO_ICMP) {
157     iov_len = icmp6_packet(out, pos + 2, (const struct icmp6_hdr *) next_header, len_left);
158   } else if (protocol == IPPROTO_TCP) {
159     iov_len = tcp_packet(out, pos + 2, (const struct tcphdr *) next_header, old_sum, new_sum,
160                          len_left);
161   } else if (protocol == IPPROTO_UDP) {
162     iov_len = udp_packet(out, pos + 2, (const struct udphdr *) next_header, old_sum, new_sum,
163                          len_left);
164   } else if (protocol == IPPROTO_GRE) {
165     iov_len = generic_packet(out, pos + 2, next_header, len_left);
166   } else {
167 #if CLAT_DEBUG
168     logmsg(ANDROID_LOG_ERROR, "ipv6_packet/unknown next header type: %x", ip6->ip6_nxt);
169     logcat_hexdump("ipv6/nxthdr", packet, len);
170 #endif
171     return 0;
172   }
173 
174   // Set the length and calculate the checksum.
175   ip_targ->tot_len = htons(ntohs(ip_targ->tot_len) + packet_length(out, pos));
176   ip_targ->check = ip_checksum(ip_targ, sizeof(struct iphdr));
177   return iov_len;
178 }
179