| Daniel Drown | a45056e | 2012-03-23 10:42:54 -0500 | [diff] [blame] | 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 | */ | 
| Lorenzo Colitti | 0cd5aa5 | 2021-12-09 15:05:52 +0900 | [diff] [blame] | 18 | #include <arpa/inet.h> | 
| Daniel Drown | a45056e | 2012-03-23 10:42:54 -0500 | [diff] [blame] | 19 | #include <string.h> | 
|  | 20 |  | 
| Lorenzo Colitti | 0cd5aa5 | 2021-12-09 15:05:52 +0900 | [diff] [blame] | 21 | #include "checksum.h" | 
| Daniel Drown | a45056e | 2012-03-23 10:42:54 -0500 | [diff] [blame] | 22 | #include "config.h" | 
|  | 23 | #include "debug.h" | 
| junyulai | c4e591a | 2018-11-26 22:36:10 +0900 | [diff] [blame] | 24 | #include "dump.h" | 
|  | 25 | #include "logging.h" | 
|  | 26 | #include "translate.h" | 
| Daniel Drown | a45056e | 2012-03-23 10:42:54 -0500 | [diff] [blame] | 27 |  | 
|  | 28 | /* function: icmp6_packet | 
|  | 29 | * takes an icmp6 packet and sets it up for translation | 
| Lorenzo Colitti | d908418 | 2013-03-22 00:42:21 +0900 | [diff] [blame] | 30 | * out      - output packet | 
|  | 31 | * icmp6    - pointer to icmp6 header in packet | 
|  | 32 | * checksum - pseudo-header checksum (unused) | 
|  | 33 | * len      - size of ip payload | 
|  | 34 | * returns: the highest position in the output clat_packet that's filled in | 
| Daniel Drown | a45056e | 2012-03-23 10:42:54 -0500 | [diff] [blame] | 35 | */ | 
| Lorenzo Colitti | a4454bf | 2014-02-25 09:27:31 +0900 | [diff] [blame] | 36 | int icmp6_packet(clat_packet out, clat_packet_index pos, const struct icmp6_hdr *icmp6, | 
|  | 37 | size_t len) { | 
| Brian Carlstrom | fcac410 | 2014-02-24 20:03:01 -0800 | [diff] [blame] | 38 | const uint8_t *payload; | 
| Daniel Drown | a45056e | 2012-03-23 10:42:54 -0500 | [diff] [blame] | 39 | size_t payload_size; | 
|  | 40 |  | 
| junyulai | c4e591a | 2018-11-26 22:36:10 +0900 | [diff] [blame] | 41 | if (len < sizeof(struct icmp6_hdr)) { | 
| Lorenzo Colitti | d908418 | 2013-03-22 00:42:21 +0900 | [diff] [blame] | 42 | logmsg_dbg(ANDROID_LOG_ERROR, "icmp6_packet/(too small)"); | 
|  | 43 | return 0; | 
| Daniel Drown | a45056e | 2012-03-23 10:42:54 -0500 | [diff] [blame] | 44 | } | 
|  | 45 |  | 
| junyulai | c4e591a | 2018-11-26 22:36:10 +0900 | [diff] [blame] | 46 | payload      = (const uint8_t *)(icmp6 + 1); | 
| Lorenzo Colitti | d908418 | 2013-03-22 00:42:21 +0900 | [diff] [blame] | 47 | payload_size = len - sizeof(struct icmp6_hdr); | 
| Daniel Drown | a45056e | 2012-03-23 10:42:54 -0500 | [diff] [blame] | 48 |  | 
| Lorenzo Colitti | 9477a46 | 2013-11-18 15:56:02 +0900 | [diff] [blame] | 49 | return icmp6_to_icmp(out, pos, icmp6, payload, payload_size); | 
| Daniel Drown | a45056e | 2012-03-23 10:42:54 -0500 | [diff] [blame] | 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 | */ | 
| Daniel Drown | a45056e | 2012-03-23 10:42:54 -0500 | [diff] [blame] | 57 | #if CLAT_DEBUG | 
| Lorenzo Colitti | 9477a46 | 2013-11-18 15:56:02 +0900 | [diff] [blame] | 58 | void log_bad_address(const char *fmt, const struct in6_addr *src, const struct in6_addr *dst) { | 
| Lorenzo Colitti | cd70b35 | 2013-04-10 12:24:56 +0900 | [diff] [blame] | 59 | char srcstr[INET6_ADDRSTRLEN]; | 
|  | 60 | char dststr[INET6_ADDRSTRLEN]; | 
| Daniel Drown | a45056e | 2012-03-23 10:42:54 -0500 | [diff] [blame] | 61 |  | 
| Lorenzo Colitti | cd70b35 | 2013-04-10 12:24:56 +0900 | [diff] [blame] | 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); | 
| Daniel Drown | a45056e | 2012-03-23 10:42:54 -0500 | [diff] [blame] | 65 | } | 
| Lorenzo Colitti | 9477a46 | 2013-11-18 15:56:02 +0900 | [diff] [blame] | 66 | #else | 
|  | 67 | #define log_bad_address(fmt, src, dst) | 
|  | 68 | #endif | 
| Daniel Drown | a45056e | 2012-03-23 10:42:54 -0500 | [diff] [blame] | 69 |  | 
|  | 70 | /* function: ipv6_packet | 
|  | 71 | * takes an ipv6 packet and hands it off to the layer 4 protocol function | 
| Lorenzo Colitti | d908418 | 2013-03-22 00:42:21 +0900 | [diff] [blame] | 72 | * out    - output packet | 
| Daniel Drown | a45056e | 2012-03-23 10:42:54 -0500 | [diff] [blame] | 73 | * packet - packet data | 
|  | 74 | * len    - size of packet | 
| Lorenzo Colitti | d908418 | 2013-03-22 00:42:21 +0900 | [diff] [blame] | 75 | * returns: the highest position in the output clat_packet that's filled in | 
| Daniel Drown | a45056e | 2012-03-23 10:42:54 -0500 | [diff] [blame] | 76 | */ | 
| Lorenzo Colitti | a4454bf | 2014-02-25 09:27:31 +0900 | [diff] [blame] | 77 | int ipv6_packet(clat_packet out, clat_packet_index pos, const uint8_t *packet, size_t len) { | 
| junyulai | c4e591a | 2018-11-26 22:36:10 +0900 | [diff] [blame] | 78 | const struct ip6_hdr *ip6 = (struct ip6_hdr *)packet; | 
|  | 79 | struct iphdr *ip_targ     = (struct iphdr *)out[pos].iov_base; | 
| Lorenzo Colitti | 57d480d | 2014-02-09 10:35:38 +0900 | [diff] [blame] | 80 | struct ip6_frag *frag_hdr = NULL; | 
| Lorenzo Colitti | d908418 | 2013-03-22 00:42:21 +0900 | [diff] [blame] | 81 | uint8_t protocol; | 
| Brian Carlstrom | fcac410 | 2014-02-24 20:03:01 -0800 | [diff] [blame] | 82 | const uint8_t *next_header; | 
| Daniel Drown | a45056e | 2012-03-23 10:42:54 -0500 | [diff] [blame] | 83 | size_t len_left; | 
| Lorenzo Colitti | 5a50c02 | 2014-02-10 09:20:05 +0900 | [diff] [blame] | 84 | uint32_t old_sum, new_sum; | 
| Lorenzo Colitti | d908418 | 2013-03-22 00:42:21 +0900 | [diff] [blame] | 85 | int iov_len; | 
| Daniel Drown | a45056e | 2012-03-23 10:42:54 -0500 | [diff] [blame] | 86 |  | 
| junyulai | c4e591a | 2018-11-26 22:36:10 +0900 | [diff] [blame] | 87 | if (len < sizeof(struct ip6_hdr)) { | 
| Lorenzo Colitti | cd70b35 | 2013-04-10 12:24:56 +0900 | [diff] [blame] | 88 | logmsg_dbg(ANDROID_LOG_ERROR, "ipv6_packet/too short for an ip6 header: %d", len); | 
| Lorenzo Colitti | d908418 | 2013-03-22 00:42:21 +0900 | [diff] [blame] | 89 | return 0; | 
| Daniel Drown | a45056e | 2012-03-23 10:42:54 -0500 | [diff] [blame] | 90 | } | 
|  | 91 |  | 
| junyulai | c4e591a | 2018-11-26 22:36:10 +0900 | [diff] [blame] | 92 | if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) { | 
| Lorenzo Colitti | cd70b35 | 2013-04-10 12:24:56 +0900 | [diff] [blame] | 93 | log_bad_address("ipv6_packet/multicast %s->%s", &ip6->ip6_src, &ip6->ip6_dst); | 
| junyulai | c4e591a | 2018-11-26 22:36:10 +0900 | [diff] [blame] | 94 | return 0;  // silently ignore | 
| Daniel Drown | a45056e | 2012-03-23 10:42:54 -0500 | [diff] [blame] | 95 | } | 
|  | 96 |  | 
| Lorenzo Colitti | cd70b35 | 2013-04-10 12:24:56 +0900 | [diff] [blame] | 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); | 
| Lorenzo Colitti | d908418 | 2013-03-22 00:42:21 +0900 | [diff] [blame] | 109 | return 0; | 
| Daniel Drown | a45056e | 2012-03-23 10:42:54 -0500 | [diff] [blame] | 110 | } | 
|  | 111 |  | 
| Lorenzo Colitti | d908418 | 2013-03-22 00:42:21 +0900 | [diff] [blame] | 112 | next_header = packet + sizeof(struct ip6_hdr); | 
| junyulai | c4e591a | 2018-11-26 22:36:10 +0900 | [diff] [blame] | 113 | len_left    = len - sizeof(struct ip6_hdr); | 
| Lorenzo Colitti | d908418 | 2013-03-22 00:42:21 +0900 | [diff] [blame] | 114 |  | 
|  | 115 | protocol = ip6->ip6_nxt; | 
| Lorenzo Colitti | d908418 | 2013-03-22 00:42:21 +0900 | [diff] [blame] | 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 |  | 
| Lorenzo Colitti | 57d480d | 2014-02-09 10:35:38 +0900 | [diff] [blame] | 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) { | 
| junyulai | c4e591a | 2018-11-26 22:36:10 +0900 | [diff] [blame] | 127 | frag_hdr = (struct ip6_frag *)next_header; | 
| Lorenzo Colitti | 57d480d | 2014-02-09 10:35:38 +0900 | [diff] [blame] | 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) { | 
| junyulai | c4e591a | 2018-11-26 22:36:10 +0900 | [diff] [blame] | 141 | protocol          = IPPROTO_ICMP; | 
| Lorenzo Colitti | 57d480d | 2014-02-09 10:35:38 +0900 | [diff] [blame] | 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 | */ | 
| Lorenzo Colitti | 07f0265 | 2014-02-20 14:28:43 +0900 | [diff] [blame] | 150 | old_sum = ipv6_pseudo_header_checksum(ip6, len_left, protocol); | 
|  | 151 | new_sum = ipv4_pseudo_header_checksum(ip_targ, len_left); | 
| Lorenzo Colitti | d908418 | 2013-03-22 00:42:21 +0900 | [diff] [blame] | 152 |  | 
| Lorenzo Colitti | 57d480d | 2014-02-09 10:35:38 +0900 | [diff] [blame] | 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) { | 
| junyulai | c4e591a | 2018-11-26 22:36:10 +0900 | [diff] [blame] | 157 | iov_len = icmp6_packet(out, pos + 2, (const struct icmp6_hdr *)next_header, len_left); | 
| Lorenzo Colitti | 57d480d | 2014-02-09 10:35:38 +0900 | [diff] [blame] | 158 | } else if (protocol == IPPROTO_TCP) { | 
| junyulai | c4e591a | 2018-11-26 22:36:10 +0900 | [diff] [blame] | 159 | iov_len = | 
|  | 160 | tcp_packet(out, pos + 2, (const struct tcphdr *)next_header, old_sum, new_sum, len_left); | 
| Lorenzo Colitti | 57d480d | 2014-02-09 10:35:38 +0900 | [diff] [blame] | 161 | } else if (protocol == IPPROTO_UDP) { | 
| junyulai | c4e591a | 2018-11-26 22:36:10 +0900 | [diff] [blame] | 162 | iov_len = | 
|  | 163 | udp_packet(out, pos + 2, (const struct udphdr *)next_header, old_sum, new_sum, len_left); | 
| Maciej Żenczykowski | 5f68982 | 2019-08-20 17:48:48 -0700 | [diff] [blame] | 164 | } else if (protocol == IPPROTO_GRE || protocol == IPPROTO_ESP) { | 
| Lorenzo Colitti | 57d480d | 2014-02-09 10:35:38 +0900 | [diff] [blame] | 165 | iov_len = generic_packet(out, pos + 2, next_header, len_left); | 
| Daniel Drown | a45056e | 2012-03-23 10:42:54 -0500 | [diff] [blame] | 166 | } else { | 
|  | 167 | #if CLAT_DEBUG | 
| Lorenzo Colitti | cd70b35 | 2013-04-10 12:24:56 +0900 | [diff] [blame] | 168 | logmsg(ANDROID_LOG_ERROR, "ipv6_packet/unknown next header type: %x", ip6->ip6_nxt); | 
| Daniel Drown | a45056e | 2012-03-23 10:42:54 -0500 | [diff] [blame] | 169 | logcat_hexdump("ipv6/nxthdr", packet, len); | 
|  | 170 | #endif | 
| Lorenzo Colitti | d908418 | 2013-03-22 00:42:21 +0900 | [diff] [blame] | 171 | return 0; | 
| Daniel Drown | a45056e | 2012-03-23 10:42:54 -0500 | [diff] [blame] | 172 | } | 
| Lorenzo Colitti | d908418 | 2013-03-22 00:42:21 +0900 | [diff] [blame] | 173 |  | 
|  | 174 | // Set the length and calculate the checksum. | 
| Lorenzo Colitti | ee80ca6 | 2013-04-10 16:52:22 +0900 | [diff] [blame] | 175 | ip_targ->tot_len = htons(ntohs(ip_targ->tot_len) + packet_length(out, pos)); | 
| junyulai | c4e591a | 2018-11-26 22:36:10 +0900 | [diff] [blame] | 176 | ip_targ->check   = ip_checksum(ip_targ, sizeof(struct iphdr)); | 
| Lorenzo Colitti | d908418 | 2013-03-22 00:42:21 +0900 | [diff] [blame] | 177 | return iov_len; | 
| Daniel Drown | a45056e | 2012-03-23 10:42:54 -0500 | [diff] [blame] | 178 | } |