blob: 784f10c5897e544fed5387c3d6b5f57c4609b23d [file] [log] [blame]
Daniel Drowna45056e2012-03-23 10:42:54 -05001/*
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
35/* function: icmp_packet
36 * takes an icmp packet and sets it up for translation
37 * fd - tun interface fd
38 * packet - ip payload
39 * len - size of ip payload
40 * ip - ip header
41 */
42void icmp_packet(int fd, const char *packet, size_t len, struct iphdr *ip) {
43 struct icmphdr icmp;
44 const char *payload;
45 size_t payload_size;
46
47 if(len < sizeof(icmp)) {
48 logmsg_dbg(ANDROID_LOG_ERROR,"icmp_packet/(too small)");
49 return;
50 }
51
52 memcpy(&icmp, packet, sizeof(icmp));
53 payload = packet + sizeof(icmp);
54 payload_size = len - sizeof(icmp);
55
56 icmp_to_icmp6(fd,ip,&icmp,payload,payload_size);
57}
58
59/* function: tcp_packet
60 * takes a tcp packet and sets it up for translation
61 * fd - tun interface fd
62 * packet - ip payload
63 * len - size of ip payload
64 * ip - ip header
65 */
66void tcp_packet(int fd, const char *packet, size_t len, struct iphdr *ip) {
67 struct tcphdr tcp;
68 const char *payload;
69 const char *options;
70 size_t payload_size, options_size;
71
72 if(len < sizeof(tcp)) {
73 logmsg_dbg(ANDROID_LOG_ERROR,"tcp_packet/(too small)");
74 return;
75 }
76
77 memcpy(&tcp, packet, sizeof(tcp));
78
79 if(tcp.doff < 5) {
80 logmsg_dbg(ANDROID_LOG_ERROR,"tcp_packet/tcp header length set to less than 5: %x",tcp.doff);
81 return;
82 }
83
84 if((size_t)tcp.doff*4 > len) {
85 logmsg_dbg(ANDROID_LOG_ERROR,"tcp_packet/tcp header length set too large: %x",tcp.doff);
86 return;
87 }
88
89 if(tcp.doff > 5) {
90 options = packet + sizeof(tcp);
91 options_size = tcp.doff*4 - sizeof(tcp);
92 } else {
93 options = NULL;
94 options_size = 0;
95 }
96
97 payload = packet + tcp.doff*4;
98 payload_size = len - tcp.doff*4;
99
100 tcp_to_tcp6(fd,ip,&tcp,payload,payload_size,options,options_size);
101}
102
103/* function: udp_packet
104 * takes a udp packet and sets it up for translation
105 * fd - tun interface fd
106 * packet - ip payload
107 * len - size of ip payload
108 * ip - ip header
109 */
110void udp_packet(int fd, const char *packet, size_t len, const struct iphdr *ip) {
111 struct udphdr udp;
112 const char *payload;
113 size_t payload_size;
114
115 if(len < sizeof(udp)) {
116 logmsg_dbg(ANDROID_LOG_ERROR,"udp_packet/(too small)");
117 return;
118 }
119
120 memcpy(&udp, packet, sizeof(udp));
121 payload = packet + sizeof(udp);
122 payload_size = len - sizeof(udp);
123
124 udp_to_udp6(fd,ip,&udp,payload,payload_size);
125}
126
127/* function: ip_packet
128 * takes an ip packet and hands it off to the layer 4 protocol function
129 * fd - tun interface fd
130 * packet - packet data
131 * len - size of packet
132 */
133void ip_packet(int fd, const char *packet, size_t len) {
134 struct iphdr header;
135 uint16_t frag_flags;
136 const char *next_header;
137 size_t len_left;
138
139 if(len < sizeof(header)) {
140 logmsg_dbg(ANDROID_LOG_ERROR,"ip_packet/too short for an ip header");
141 return;
142 }
143
144 memcpy(&header, packet, sizeof(header));
145
146 frag_flags = ntohs(header.frag_off);
147 if(frag_flags & IP_MF) { // this could theoretically be supported, but isn't
148 logmsg_dbg(ANDROID_LOG_ERROR,"ip_packet/more fragments set, dropping");
149 return;
150 }
151
152 if(header.ihl < 5) {
153 logmsg_dbg(ANDROID_LOG_ERROR,"ip_packet/ip header length set to less than 5: %x",header.ihl);
154 return;
155 }
156
157 if((size_t)header.ihl*4 > len) { // ip header length larger than entire packet
158 logmsg_dbg(ANDROID_LOG_ERROR,"ip_packet/ip header length set too large: %x",header.ihl);
159 return;
160 }
161
162 if(header.version != 4) {
163 logmsg_dbg(ANDROID_LOG_ERROR,"ip_packet/ip header version not 4: %x",header.version);
164 return;
165 }
166
167 /* rfc6145 - If any IPv4 options are present in the IPv4 packet, they MUST be
168 * ignored and the packet translated normally; there is no attempt to
169 * translate the options.
170 */
171
172 next_header = packet + header.ihl*4;
173 len_left = len - header.ihl*4;
174
175 if(header.protocol == IPPROTO_ICMP) {
176 icmp_packet(fd,next_header,len_left,&header);
177 } else if(header.protocol == IPPROTO_TCP) {
178 tcp_packet(fd,next_header,len_left,&header);
179 } else if(header.protocol == IPPROTO_UDP) {
180 udp_packet(fd,next_header,len_left,&header);
181 } else {
182#if CLAT_DEBUG
183 logmsg_dbg(ANDROID_LOG_ERROR,"ip_packet/unknown protocol: %x",header.protocol);
184 logcat_hexdump("ipv4/protocol", packet, len);
185#endif
186 }
187}