Home | History | Annotate | Download | only in android-clat
      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  * translate.c - CLAT functions / partial implementation of rfc6145
     17  */
     18 #include <string.h>
     19 
     20 #include <netinet/in.h>
     21 #include <netinet/ip.h>
     22 #include <netinet/ip_icmp.h>
     23 #include <netinet/udp.h>
     24 #include <netinet/tcp.h>
     25 #include <netinet/ip6.h>
     26 #include <netinet/icmp6.h>
     27 #include <linux/icmp.h>
     28 
     29 #include "icmp.h"
     30 #include "translate.h"
     31 #include "checksum.h"
     32 #include "clatd.h"
     33 #include "config.h"
     34 #include "logging.h"
     35 #include "debug.h"
     36 
     37 /* function: packet_checksum
     38  * calculates the checksum over all the packet components starting from pos
     39  * checksum - checksum of packet components before pos
     40  * packet   - packet to calculate the checksum of
     41  * pos      - position to start counting from
     42  * returns  - the completed 16-bit checksum, ready to write into a checksum header field
     43  */
     44 uint16_t packet_checksum(uint32_t checksum, clat_packet packet, int pos) {
     45   int i;
     46   for (i = pos; i < CLAT_POS_MAX; i++) {
     47     if (packet[i].iov_len > 0) {
     48       checksum = ip_checksum_add(checksum, packet[i].iov_base, packet[i].iov_len);
     49     }
     50   }
     51   return ip_checksum_finish(checksum);
     52 }
     53 
     54 /* function: packet_length
     55  * returns the total length of all the packet components after pos
     56  * packet - packet to calculate the length of
     57  * pos    - position to start counting after
     58  * returns: the total length of the packet components after pos
     59  */
     60 uint16_t packet_length(clat_packet packet, int pos) {
     61   size_t len = 0;
     62   int i;
     63   for (i = pos + 1; i < CLAT_POS_MAX; i++) {
     64     len += packet[i].iov_len;
     65   }
     66   return len;
     67 }
     68 
     69 /* function: is_in_plat_subnet
     70  * returns true iff the given IPv6 address is in the plat subnet.
     71  * addr - IPv6 address
     72  */
     73 int is_in_plat_subnet(const struct in6_addr *addr6) {
     74   // Assumes a /96 plat subnet.
     75   return (addr6 != NULL) && (memcmp(addr6, &Global_Clatd_Config.plat_subnet, 12) == 0);
     76 }
     77 
     78 /* function: ipv6_addr_to_ipv4_addr
     79  * return the corresponding ipv4 address for the given ipv6 address
     80  * addr6 - ipv6 address
     81  * returns: the IPv4 address
     82  */
     83 uint32_t ipv6_addr_to_ipv4_addr(const struct in6_addr *addr6) {
     84   if (is_in_plat_subnet(addr6)) {
     85     // Assumes a /96 plat subnet.
     86     return addr6->s6_addr32[3];
     87   } else if (IN6_ARE_ADDR_EQUAL(addr6, &Global_Clatd_Config.ipv6_local_subnet)) {
     88     // Special-case our own address.
     89     return Global_Clatd_Config.ipv4_local_subnet.s_addr;
     90   } else {
     91     // Third party packet. Let the caller deal with it.
     92     return INADDR_NONE;
     93   }
     94 }
     95 
     96 /* function: ipv4_addr_to_ipv6_addr
     97  * return the corresponding ipv6 address for the given ipv4 address
     98  * addr4 - ipv4 address
     99  */
    100 struct in6_addr ipv4_addr_to_ipv6_addr(uint32_t addr4) {
    101   struct in6_addr addr6;
    102   // Both addresses are in network byte order (addr4 comes from a network packet, and the config
    103   // file entry is read using inet_ntop).
    104   if (addr4 == Global_Clatd_Config.ipv4_local_subnet.s_addr) {
    105     return Global_Clatd_Config.ipv6_local_subnet;
    106   } else {
    107     // Assumes a /96 plat subnet.
    108     addr6 = Global_Clatd_Config.plat_subnet;
    109     addr6.s6_addr32[3] = addr4;
    110     return addr6;
    111   }
    112 }
    113 
    114 /* function: fill_tun_header
    115  * fill in the header for the tun fd
    116  * tun_header - tunnel header, already allocated
    117  * proto      - ethernet protocol id: ETH_P_IP(ipv4) or ETH_P_IPV6(ipv6)
    118  */
    119 void fill_tun_header(struct tun_pi *tun_header, uint16_t proto) {
    120   tun_header->flags = 0;
    121   tun_header->proto = htons(proto);
    122 }
    123 
    124 /* function: fill_ip_header
    125  * generate an ipv4 header from an ipv6 header
    126  * ip_targ     - (ipv4) target packet header, source: original ipv4 addr, dest: local subnet addr
    127  * payload_len - length of other data inside packet
    128  * protocol    - protocol number (tcp, udp, etc)
    129  * old_header  - (ipv6) source packet header, source: nat64 prefix, dest: local subnet prefix
    130  */
    131 void fill_ip_header(struct iphdr *ip, uint16_t payload_len, uint8_t protocol,
    132                     const struct ip6_hdr *old_header) {
    133   int ttl_guess;
    134   memset(ip, 0, sizeof(struct iphdr));
    135 
    136   ip->ihl = 5;
    137   ip->version = 4;
    138   ip->tos = 0;
    139   ip->tot_len = htons(sizeof(struct iphdr) + payload_len);
    140   ip->id = 0;
    141   ip->frag_off = htons(IP_DF);
    142   ip->ttl = old_header->ip6_hlim;
    143   ip->protocol = protocol;
    144   ip->check = 0;
    145 
    146   ip->saddr = ipv6_addr_to_ipv4_addr(&old_header->ip6_src);
    147   ip->daddr = ipv6_addr_to_ipv4_addr(&old_header->ip6_dst);
    148 
    149   // Third-party ICMPv6 message. This may have been originated by an native IPv6 address.
    150   // In that case, the source IPv6 address can't be translated and we need to make up an IPv4
    151   // source address. For now, use 255.0.0.<ttl>, which at least looks useful in traceroute.
    152   if (ip->saddr == (uint32_t) INADDR_NONE) {
    153     ttl_guess = icmp_guess_ttl(old_header->ip6_hlim);
    154     ip->saddr = htonl((0xff << 24) + ttl_guess);
    155   }
    156 }
    157 
    158 /* function: fill_ip6_header
    159  * generate an ipv6 header from an ipv4 header
    160  * ip6         - (ipv6) target packet header, source: local subnet prefix, dest: nat64 prefix
    161  * payload_len - length of other data inside packet
    162  * protocol    - protocol number (tcp, udp, etc)
    163  * old_header  - (ipv4) source packet header, source: local subnet addr, dest: internet's ipv4 addr
    164  */
    165 void fill_ip6_header(struct ip6_hdr *ip6, uint16_t payload_len, uint8_t protocol,
    166                      const struct iphdr *old_header) {
    167   memset(ip6, 0, sizeof(struct ip6_hdr));
    168 
    169   ip6->ip6_vfc = 6 << 4;
    170   ip6->ip6_plen = htons(payload_len);
    171   ip6->ip6_nxt = protocol;
    172   ip6->ip6_hlim = old_header->ttl;
    173 
    174   ip6->ip6_src = ipv4_addr_to_ipv6_addr(old_header->saddr);
    175   ip6->ip6_dst = ipv4_addr_to_ipv6_addr(old_header->daddr);
    176 }
    177 
    178 /* function: icmp_to_icmp6
    179  * translate ipv4 icmp to ipv6 icmp
    180  * out          - output packet
    181  * icmp         - source packet icmp header
    182  * checksum     - pseudo-header checksum
    183  * payload      - icmp payload
    184  * payload_size - size of payload
    185  * returns: the highest position in the output clat_packet that's filled in
    186  */
    187 int icmp_to_icmp6(clat_packet out, int pos, const struct icmphdr *icmp, uint32_t checksum,
    188                   const char *payload, size_t payload_size) {
    189   struct icmp6_hdr *icmp6_targ = out[pos].iov_base;
    190   uint8_t icmp6_type;
    191   int clat_packet_len;
    192 
    193   memset(icmp6_targ, 0, sizeof(struct icmp6_hdr));
    194 
    195   icmp6_type = icmp_to_icmp6_type(icmp->type, icmp->code);
    196   icmp6_targ->icmp6_type = icmp6_type;
    197   icmp6_targ->icmp6_code = icmp_to_icmp6_code(icmp->type, icmp->code);
    198 
    199   out[pos].iov_len = sizeof(struct icmp6_hdr);
    200 
    201   if (pos == CLAT_POS_TRANSPORTHDR &&
    202       is_icmp_error(icmp->type) &&
    203       icmp6_type != ICMP6_PARAM_PROB) {
    204     // An ICMP error we understand, one level deep.
    205     // Translate the nested packet (the one that caused the error).
    206     clat_packet_len = ipv4_packet(out, pos + 1, payload, payload_size);
    207 
    208     // The pseudo-header checksum was calculated on the transport length of the original IPv4
    209     // packet that we were asked to translate. This transport length is 20 bytes smaller than it
    210     // needs to be, because the ICMP error contains an IPv4 header, which we will be translating to
    211     // an IPv6 header, which is 20 bytes longer. Fix it up here. This is simpler than the
    212     // alternative, which is to always update the pseudo-header checksum in all UDP/TCP/ICMP
    213     // translation functions (rather than pre-calculating it when translating the IPv4 header).
    214     // We only need to do this for ICMP->ICMPv6, not ICMPv6->ICMP, because ICMP does not use the
    215     // pseudo-header when calculating its checksum (as the IPv4 header has its own checksum).
    216     checksum = htonl(ntohl(checksum) + 20);
    217   } else if (icmp6_type == ICMP6_ECHO_REQUEST || icmp6_type == ICMP6_ECHO_REPLY) {
    218     // Ping packet.
    219     icmp6_targ->icmp6_id = icmp->un.echo.id;
    220     icmp6_targ->icmp6_seq = icmp->un.echo.sequence;
    221     out[CLAT_POS_PAYLOAD].iov_base = (char *) payload;
    222     out[CLAT_POS_PAYLOAD].iov_len = payload_size;
    223     clat_packet_len = CLAT_POS_PAYLOAD + 1;
    224   } else {
    225     // Unknown type/code. The type/code conversion functions have already logged an error.
    226     return 0;
    227   }
    228 
    229   icmp6_targ->icmp6_cksum = 0;  // Checksum field must be 0 when calculating checksum.
    230   icmp6_targ->icmp6_cksum = packet_checksum(checksum, out, pos);
    231 
    232   return clat_packet_len;
    233 }
    234 
    235 /* function: icmp6_to_icmp
    236  * translate ipv6 icmp to ipv4 icmp
    237  * out          - output packet
    238  * icmp6        - source packet icmp6 header
    239  * checksum     - pseudo-header checksum (unused)
    240  * payload      - icmp6 payload
    241  * payload_size - size of payload
    242  * returns: the highest position in the output clat_packet that's filled in
    243  */
    244 int icmp6_to_icmp(clat_packet out, int pos, const struct icmp6_hdr *icmp6, uint32_t checksum,
    245                   const char *payload, size_t payload_size) {
    246   struct icmphdr *icmp_targ = out[pos].iov_base;
    247   uint8_t icmp_type;
    248   int ttl;
    249   int clat_packet_len;
    250 
    251   memset(icmp_targ, 0, sizeof(struct icmphdr));
    252 
    253   icmp_type = icmp6_to_icmp_type(icmp6->icmp6_type, icmp6->icmp6_code);
    254   icmp_targ->type = icmp_type;
    255   icmp_targ->code = icmp6_to_icmp_code(icmp6->icmp6_type, icmp6->icmp6_code);
    256 
    257   out[pos].iov_len = sizeof(struct icmphdr);
    258 
    259   if (pos == CLAT_POS_TRANSPORTHDR &&
    260       is_icmp6_error(icmp6->icmp6_type) &&
    261       icmp_type != ICMP_PARAMETERPROB) {
    262     // An ICMPv6 error we understand, one level deep.
    263     // Translate the nested packet (the one that caused the error).
    264     clat_packet_len = ipv6_packet(out, pos + 1, payload, payload_size);
    265   } else if (icmp_type == ICMP_ECHO || icmp_type == ICMP_ECHOREPLY) {
    266     // Ping packet.
    267     icmp_targ->un.echo.id = icmp6->icmp6_id;
    268     icmp_targ->un.echo.sequence = icmp6->icmp6_seq;
    269     out[CLAT_POS_PAYLOAD].iov_base = (char *) payload;
    270     out[CLAT_POS_PAYLOAD].iov_len = payload_size;
    271     clat_packet_len = CLAT_POS_PAYLOAD + 1;
    272   } else {
    273       // Unknown type/code. The type/code conversion functions have already logged an error.
    274     return 0;
    275   }
    276 
    277   icmp_targ->checksum = 0;  // Checksum field must be 0 when calculating checksum.
    278   icmp_targ->checksum = packet_checksum(0, out, pos);
    279 
    280   return clat_packet_len;
    281 }
    282 
    283 /* function: udp_packet
    284  * takes a udp packet and sets it up for translation
    285  * out      - output packet
    286  * udp      - pointer to udp header in packet
    287  * checksum - pseudo-header checksum
    288  * len      - size of ip payload
    289  */
    290 int udp_packet(clat_packet out, int pos, const struct udphdr *udp, uint32_t checksum, size_t len) {
    291   const char *payload;
    292   size_t payload_size;
    293 
    294   if(len < sizeof(struct udphdr)) {
    295     logmsg_dbg(ANDROID_LOG_ERROR,"udp_packet/(too small)");
    296     return 0;
    297   }
    298 
    299   payload = (const char *) (udp + 1);
    300   payload_size = len - sizeof(struct udphdr);
    301 
    302   return udp_translate(out, pos, udp, checksum, payload, payload_size);
    303 }
    304 
    305 /* function: tcp_packet
    306  * takes a tcp packet and sets it up for translation
    307  * out      - output packet
    308  * tcp      - pointer to tcp header in packet
    309  * checksum - pseudo-header checksum
    310  * len      - size of ip payload
    311  * returns: the highest position in the output clat_packet that's filled in
    312  */
    313 int tcp_packet(clat_packet out, int pos, const struct tcphdr *tcp, uint32_t checksum, size_t len) {
    314   const char *payload;
    315   size_t payload_size, header_size;
    316 
    317   if(len < sizeof(struct tcphdr)) {
    318     logmsg_dbg(ANDROID_LOG_ERROR,"tcp_packet/(too small)");
    319     return 0;
    320   }
    321 
    322   if(tcp->doff < 5) {
    323     logmsg_dbg(ANDROID_LOG_ERROR,"tcp_packet/tcp header length set to less than 5: %x", tcp->doff);
    324     return 0;
    325   }
    326 
    327   if((size_t) tcp->doff*4 > len) {
    328     logmsg_dbg(ANDROID_LOG_ERROR,"tcp_packet/tcp header length set too large: %x", tcp->doff);
    329     return 0;
    330   }
    331 
    332   header_size = tcp->doff * 4;
    333   payload = ((const char *) tcp) + header_size;
    334   payload_size = len - header_size;
    335 
    336   return tcp_translate(out, pos, tcp, header_size, checksum, payload, payload_size);
    337 }
    338 
    339 /* function: udp_translate
    340  * common between ipv4/ipv6 - setup checksum and send udp packet
    341  * out          - output packet
    342  * udp          - udp header
    343  * checksum     - pseudo-header checksum
    344  * payload      - tcp payload
    345  * payload_size - size of payload
    346  * returns: the highest position in the output clat_packet that's filled in
    347  */
    348 int udp_translate(clat_packet out, int pos, const struct udphdr *udp, uint32_t checksum,
    349                   const char *payload, size_t payload_size) {
    350   struct udphdr *udp_targ = out[pos].iov_base;
    351 
    352   memcpy(udp_targ, udp, sizeof(struct udphdr));
    353 
    354   out[pos].iov_len = sizeof(struct udphdr);
    355   out[CLAT_POS_PAYLOAD].iov_base = (char *) payload;
    356   out[CLAT_POS_PAYLOAD].iov_len = payload_size;
    357 
    358   udp_targ->check = 0;  // Checksum field must be 0 when calculating checksum.
    359   udp_targ->check = packet_checksum(checksum, out, pos);
    360 
    361   return CLAT_POS_PAYLOAD + 1;
    362 }
    363 
    364 /* function: tcp_translate
    365  * common between ipv4/ipv6 - setup checksum and send tcp packet
    366  * out          - output packet
    367  * tcp          - tcp header
    368  * header_size  - size of tcp header including options
    369  * checksum     - partial checksum covering ipv4/ipv6 header
    370  * payload      - tcp payload
    371  * payload_size - size of payload
    372  * returns: the highest position in the output clat_packet that's filled in
    373  *
    374  * TODO: mss rewrite
    375  * TODO: hosts without pmtu discovery - non DF packets will rely on fragmentation (unimplemented)
    376  */
    377 int tcp_translate(clat_packet out, int pos, const struct tcphdr *tcp, size_t header_size,
    378                   uint32_t checksum, const char *payload, size_t payload_size) {
    379   struct tcphdr *tcp_targ = out[pos].iov_base;
    380   out[pos].iov_len = header_size;
    381 
    382   if (header_size > MAX_TCP_HDR) {
    383     // A TCP header cannot be more than MAX_TCP_HDR bytes long because it's a 4-bit field that
    384     // counts in 4-byte words. So this can never happen unless there is a bug in the caller.
    385     logmsg(ANDROID_LOG_ERROR, "tcp_translate: header too long %d > %d, truncating",
    386            header_size, MAX_TCP_HDR);
    387     header_size = MAX_TCP_HDR;
    388   }
    389 
    390   memcpy(tcp_targ, tcp, header_size);
    391 
    392   out[CLAT_POS_PAYLOAD].iov_base = (char *)payload;
    393   out[CLAT_POS_PAYLOAD].iov_len = payload_size;
    394 
    395   tcp_targ->check = 0;  // Checksum field must be 0 when calculating checksum.
    396   tcp_targ->check = packet_checksum(checksum, out, pos);
    397 
    398   return CLAT_POS_PAYLOAD + 1;
    399 }
    400