diff options
Diffstat (limited to 'ipv6.c')
-rw-r--r-- | ipv6.c | 178 |
1 files changed, 0 insertions, 178 deletions
@@ -1,178 +0,0 @@ -/* - * 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; -} |