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 * ipv4.c - takes ipv4 packets, finds their headers, and then calls translation functions on them 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 "translate.h" 30 #include "checksum.h" 31 #include "ipv4.h" 32 #include "logging.h" 33 #include "debug.h" 34 #include "dump.h" 35 36 /* function: icmp_packet 37 * translates an icmp packet 38 * out - output packet 39 * icmp - pointer to icmp header in packet 40 * checksum - pseudo-header checksum 41 * len - size of ip payload 42 * returns: the highest position in the output clat_packet that's filled in 43 */ 44 int icmp_packet(clat_packet out, int pos, const struct icmphdr *icmp, uint32_t checksum, 45 size_t len) { 46 const char *payload; 47 size_t payload_size; 48 49 if(len < sizeof(struct icmphdr)) { 50 logmsg_dbg(ANDROID_LOG_ERROR, "icmp_packet/(too small)"); 51 return 0; 52 } 53 54 payload = (const char *) (icmp + 1); 55 payload_size = len - sizeof(struct icmphdr); 56 57 return icmp_to_icmp6(out, pos, icmp, checksum, payload, payload_size); 58 } 59 60 /* function: ipv4_packet 61 * translates an ipv4 packet 62 * out - output packet 63 * packet - packet data 64 * len - size of packet 65 * returns: the highest position in the output clat_packet that's filled in 66 */ 67 int ipv4_packet(clat_packet out, int pos, const char *packet, size_t len) { 68 const struct iphdr *header = (struct iphdr *) packet; 69 struct ip6_hdr *ip6_targ = (struct ip6_hdr *) out[pos].iov_base; 70 uint16_t frag_flags; 71 uint8_t nxthdr; 72 const char *next_header; 73 size_t len_left; 74 uint32_t checksum; 75 int iov_len; 76 77 if(len < sizeof(struct iphdr)) { 78 logmsg_dbg(ANDROID_LOG_ERROR, "ip_packet/too short for an ip header"); 79 return 0; 80 } 81 82 frag_flags = ntohs(header->frag_off); 83 if(frag_flags & IP_MF) { // this could theoretically be supported, but isn't 84 logmsg_dbg(ANDROID_LOG_ERROR, "ip_packet/more fragments set, dropping"); 85 return 0; 86 } 87 88 if(header->ihl < 5) { 89 logmsg_dbg(ANDROID_LOG_ERROR, "ip_packet/ip header length set to less than 5: %x", header->ihl); 90 return 0; 91 } 92 93 if((size_t) header->ihl * 4 > len) { // ip header length larger than entire packet 94 logmsg_dbg(ANDROID_LOG_ERROR, "ip_packet/ip header length set too large: %x", header->ihl); 95 return 0; 96 } 97 98 if(header->version != 4) { 99 logmsg_dbg(ANDROID_LOG_ERROR, "ip_packet/ip header version not 4: %x", header->version); 100 return 0; 101 } 102 103 /* rfc6145 - If any IPv4 options are present in the IPv4 packet, they MUST be 104 * ignored and the packet translated normally; there is no attempt to 105 * translate the options. 106 */ 107 108 next_header = packet + header->ihl*4; 109 len_left = len - header->ihl * 4; 110 111 nxthdr = header->protocol; 112 if (nxthdr == IPPROTO_ICMP) { 113 // ICMP and ICMPv6 have different protocol numbers. 114 nxthdr = IPPROTO_ICMPV6; 115 } 116 117 /* Fill in the IPv6 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_ip6_header(ip6_targ, 0, nxthdr, header); 122 out[pos].iov_len = sizeof(struct ip6_hdr); 123 124 // Calculate the pseudo-header checksum. 125 checksum = ipv6_pseudo_header_checksum(0, ip6_targ, len_left); 126 127 if(nxthdr == IPPROTO_ICMPV6) { 128 iov_len = icmp_packet(out, pos + 1, (const struct icmphdr *) next_header, checksum, len_left); 129 } else if(nxthdr == IPPROTO_TCP) { 130 iov_len = tcp_packet(out, pos + 1, (const struct tcphdr *) next_header, checksum, len_left); 131 } else if(nxthdr == IPPROTO_UDP) { 132 iov_len = udp_packet(out, pos + 1, (const struct udphdr *) next_header, checksum, len_left); 133 } else { 134 #if CLAT_DEBUG 135 logmsg_dbg(ANDROID_LOG_ERROR, "ip_packet/unknown protocol: %x",header->protocol); 136 logcat_hexdump("ipv4/protocol", packet, len); 137 #endif 138 return 0; 139 } 140 141 // Set the length. 142 ip6_targ->ip6_plen = htons(packet_length(out, pos)); 143 return iov_len; 144 } 145