|  | /* | 
|  | * Copyright 2011 Daniel Drown | 
|  | * | 
|  | * Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | * you may not use this file except in compliance with the License. | 
|  | * You may obtain a copy of the License at | 
|  | * | 
|  | * http://www.apache.org/licenses/LICENSE-2.0 | 
|  | * | 
|  | * Unless required by applicable law or agreed to in writing, software | 
|  | * distributed under the License is distributed on an "AS IS" BASIS, | 
|  | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | * See the License for the specific language governing permissions and | 
|  | * limitations under the License. | 
|  | * | 
|  | * ipv6.c - takes ipv6 packets, finds their headers, and then calls translation functions on them | 
|  | */ | 
|  | #include <arpa/inet.h> | 
|  | #include <string.h> | 
|  |  | 
|  | #include "checksum.h" | 
|  | #include "config.h" | 
|  | #include "debug.h" | 
|  | #include "dump.h" | 
|  | #include "logging.h" | 
|  | #include "translate.h" | 
|  |  | 
|  | /* function: icmp6_packet | 
|  | * takes an icmp6 packet and sets it up for translation | 
|  | * out      - output packet | 
|  | * icmp6    - pointer to icmp6 header in packet | 
|  | * checksum - pseudo-header checksum (unused) | 
|  | * len      - size of ip payload | 
|  | * returns: the highest position in the output clat_packet that's filled in | 
|  | */ | 
|  | int icmp6_packet(clat_packet out, clat_packet_index pos, const struct icmp6_hdr *icmp6, | 
|  | size_t len) { | 
|  | const uint8_t *payload; | 
|  | size_t payload_size; | 
|  |  | 
|  | if (len < sizeof(struct icmp6_hdr)) { | 
|  | logmsg_dbg(ANDROID_LOG_ERROR, "icmp6_packet/(too small)"); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | payload      = (const uint8_t *)(icmp6 + 1); | 
|  | payload_size = len - sizeof(struct icmp6_hdr); | 
|  |  | 
|  | return icmp6_to_icmp(out, pos, icmp6, payload, payload_size); | 
|  | } | 
|  |  | 
|  | /* function: log_bad_address | 
|  | * logs a bad address to android's log buffer if debugging is turned on | 
|  | * fmt     - printf-style format, use %s to place the address | 
|  | * badaddr - the bad address in question | 
|  | */ | 
|  | #if CLAT_DEBUG | 
|  | void log_bad_address(const char *fmt, const struct in6_addr *src, const struct in6_addr *dst) { | 
|  | char srcstr[INET6_ADDRSTRLEN]; | 
|  | char dststr[INET6_ADDRSTRLEN]; | 
|  |  | 
|  | inet_ntop(AF_INET6, src, srcstr, sizeof(srcstr)); | 
|  | inet_ntop(AF_INET6, dst, dststr, sizeof(dststr)); | 
|  | logmsg_dbg(ANDROID_LOG_ERROR, fmt, srcstr, dststr); | 
|  | } | 
|  | #else | 
|  | #define log_bad_address(fmt, src, dst) | 
|  | #endif | 
|  |  | 
|  | /* function: ipv6_packet | 
|  | * takes an ipv6 packet and hands it off to the layer 4 protocol function | 
|  | * out    - output packet | 
|  | * packet - packet data | 
|  | * len    - size of packet | 
|  | * returns: the highest position in the output clat_packet that's filled in | 
|  | */ | 
|  | int ipv6_packet(clat_packet out, clat_packet_index pos, const uint8_t *packet, size_t len) { | 
|  | const struct ip6_hdr *ip6 = (struct ip6_hdr *)packet; | 
|  | struct iphdr *ip_targ     = (struct iphdr *)out[pos].iov_base; | 
|  | struct ip6_frag *frag_hdr = NULL; | 
|  | uint8_t protocol; | 
|  | const uint8_t *next_header; | 
|  | size_t len_left; | 
|  | uint32_t old_sum, new_sum; | 
|  | int iov_len; | 
|  |  | 
|  | if (len < sizeof(struct ip6_hdr)) { | 
|  | logmsg_dbg(ANDROID_LOG_ERROR, "ipv6_packet/too short for an ip6 header: %d", len); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) { | 
|  | log_bad_address("ipv6_packet/multicast %s->%s", &ip6->ip6_src, &ip6->ip6_dst); | 
|  | return 0;  // silently ignore | 
|  | } | 
|  |  | 
|  | // If the packet is not from the plat subnet to the local subnet, or vice versa, drop it, unless | 
|  | // it's an ICMP packet (which can come from anywhere). We do not send IPv6 packets from the plat | 
|  | // subnet to the local subnet, but these can appear as inner packets in ICMP errors, so we need | 
|  | // to translate them. We accept third-party ICMPv6 errors, even though their source addresses | 
|  | // cannot be translated, so that things like unreachables and traceroute will work. fill_ip_header | 
|  | // takes care of faking a source address for them. | 
|  | if (!(is_in_plat_subnet(&ip6->ip6_src) && | 
|  | IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &Global_Clatd_Config.ipv6_local_subnet)) && | 
|  | !(is_in_plat_subnet(&ip6->ip6_dst) && | 
|  | IN6_ARE_ADDR_EQUAL(&ip6->ip6_src, &Global_Clatd_Config.ipv6_local_subnet)) && | 
|  | ip6->ip6_nxt != IPPROTO_ICMPV6) { | 
|  | log_bad_address("ipv6_packet/wrong source address: %s->%s", &ip6->ip6_src, &ip6->ip6_dst); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | next_header = packet + sizeof(struct ip6_hdr); | 
|  | len_left    = len - sizeof(struct ip6_hdr); | 
|  |  | 
|  | protocol = ip6->ip6_nxt; | 
|  |  | 
|  | /* Fill in the IPv4 header. We need to do this before we translate the packet because TCP and | 
|  | * UDP include parts of the IP header in the checksum. Set the length to zero because we don't | 
|  | * know it yet. | 
|  | */ | 
|  | fill_ip_header(ip_targ, 0, protocol, ip6); | 
|  | out[pos].iov_len = sizeof(struct iphdr); | 
|  |  | 
|  | // If there's a Fragment header, parse it and decide what the next header is. | 
|  | // Do this before calculating the pseudo-header checksum because it updates the next header value. | 
|  | if (protocol == IPPROTO_FRAGMENT) { | 
|  | frag_hdr = (struct ip6_frag *)next_header; | 
|  | if (len_left < sizeof(*frag_hdr)) { | 
|  | logmsg_dbg(ANDROID_LOG_ERROR, "ipv6_packet/too short for fragment header: %d", len); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | next_header += sizeof(*frag_hdr); | 
|  | len_left -= sizeof(*frag_hdr); | 
|  |  | 
|  | protocol = parse_frag_header(frag_hdr, ip_targ); | 
|  | } | 
|  |  | 
|  | // ICMP and ICMPv6 have different protocol numbers. | 
|  | if (protocol == IPPROTO_ICMPV6) { | 
|  | protocol          = IPPROTO_ICMP; | 
|  | ip_targ->protocol = IPPROTO_ICMP; | 
|  | } | 
|  |  | 
|  | /* Calculate the pseudo-header checksum. | 
|  | * Technically, the length that is used in the pseudo-header checksum is the transport layer | 
|  | * length, which is not the same as len_left in the case of fragmented packets. But since | 
|  | * translation does not change the transport layer length, the checksum is unaffected. | 
|  | */ | 
|  | old_sum = ipv6_pseudo_header_checksum(ip6, len_left, protocol); | 
|  | new_sum = ipv4_pseudo_header_checksum(ip_targ, len_left); | 
|  |  | 
|  | // Does not support IPv6 extension headers except Fragment. | 
|  | if (frag_hdr && (frag_hdr->ip6f_offlg & IP6F_OFF_MASK)) { | 
|  | iov_len = generic_packet(out, pos + 2, next_header, len_left); | 
|  | } else if (protocol == IPPROTO_ICMP) { | 
|  | iov_len = icmp6_packet(out, pos + 2, (const struct icmp6_hdr *)next_header, len_left); | 
|  | } else if (protocol == IPPROTO_TCP) { | 
|  | iov_len = | 
|  | tcp_packet(out, pos + 2, (const struct tcphdr *)next_header, old_sum, new_sum, len_left); | 
|  | } else if (protocol == IPPROTO_UDP) { | 
|  | iov_len = | 
|  | udp_packet(out, pos + 2, (const struct udphdr *)next_header, old_sum, new_sum, len_left); | 
|  | } else if (protocol == IPPROTO_GRE || protocol == IPPROTO_ESP) { | 
|  | iov_len = generic_packet(out, pos + 2, next_header, len_left); | 
|  | } else { | 
|  | #if CLAT_DEBUG | 
|  | logmsg(ANDROID_LOG_ERROR, "ipv6_packet/unknown next header type: %x", ip6->ip6_nxt); | 
|  | logcat_hexdump("ipv6/nxthdr", packet, len); | 
|  | #endif | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | // Set the length and calculate the checksum. | 
|  | ip_targ->tot_len = htons(ntohs(ip_targ->tot_len) + packet_length(out, pos)); | 
|  | ip_targ->check   = ip_checksum(ip_targ, sizeof(struct iphdr)); | 
|  | return iov_len; | 
|  | } |