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