]> git.ipfire.org Git - thirdparty/strongswan.git/blob - src/libipsec/ip_packet.c
Merge branch 'android-dns-proxy'
[thirdparty/strongswan.git] / src / libipsec / ip_packet.c
1 /*
2 * Copyright (C) 2012-2014 Tobias Brunner
3 * Hochschule fuer Technik Rapperswil
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16
17 #include "ip_packet.h"
18
19 #include <library.h>
20 #include <utils/debug.h>
21
22 #include <sys/types.h>
23 #include <netinet/in.h>
24 #include <netinet/ip.h>
25 #include <netinet/udp.h>
26 #include <netinet/tcp.h>
27 #ifdef HAVE_NETINET_IP6_H
28 #include <netinet/ip6.h>
29 #endif
30
31 typedef struct private_ip_packet_t private_ip_packet_t;
32
33 /**
34 * Private additions to ip_packet_t.
35 */
36 struct private_ip_packet_t {
37
38 /**
39 * Public members
40 */
41 ip_packet_t public;
42
43 /**
44 * Source address
45 */
46 host_t *src;
47
48 /**
49 * Destination address
50 */
51 host_t *dst;
52
53 /**
54 * IP packet
55 */
56 chunk_t packet;
57
58 /**
59 * IP payload (points into packet)
60 */
61 chunk_t payload;
62
63 /**
64 * IP version
65 */
66 u_int8_t version;
67
68 /**
69 * Protocol|Next Header field
70 */
71 u_int8_t next_header;
72
73 };
74
75 METHOD(ip_packet_t, get_version, u_int8_t,
76 private_ip_packet_t *this)
77 {
78 return this->version;
79 }
80
81 METHOD(ip_packet_t, get_source, host_t*,
82 private_ip_packet_t *this)
83 {
84 return this->src;
85 }
86
87 METHOD(ip_packet_t, get_destination, host_t*,
88 private_ip_packet_t *this)
89 {
90 return this->dst;
91 }
92
93 METHOD(ip_packet_t, get_encoding, chunk_t,
94 private_ip_packet_t *this)
95 {
96 return this->packet;
97 }
98
99 METHOD(ip_packet_t, get_payload, chunk_t,
100 private_ip_packet_t *this)
101 {
102 return this->payload;
103 }
104
105 METHOD(ip_packet_t, get_next_header, u_int8_t,
106 private_ip_packet_t *this)
107 {
108 return this->next_header;
109 }
110
111 METHOD(ip_packet_t, clone_, ip_packet_t*,
112 private_ip_packet_t *this)
113 {
114 return ip_packet_create(chunk_clone(this->packet));
115 }
116
117 METHOD(ip_packet_t, destroy, void,
118 private_ip_packet_t *this)
119 {
120 this->src->destroy(this->src);
121 this->dst->destroy(this->dst);
122 chunk_free(&this->packet);
123 free(this);
124 }
125
126 /**
127 * Parse transport protocol header
128 */
129 static bool parse_transport_header(chunk_t packet, u_int8_t proto,
130 u_int16_t *sport, u_int16_t *dport)
131 {
132 switch (proto)
133 {
134 case IPPROTO_UDP:
135 {
136 struct udphdr *udp;
137
138 if (packet.len < sizeof(*udp))
139 {
140 DBG1(DBG_ESP, "UDP packet too short");
141 return FALSE;
142 }
143 udp = (struct udphdr*)packet.ptr;
144 *sport = ntohs(udp->source);
145 *dport = ntohs(udp->dest);
146 break;
147 }
148 case IPPROTO_TCP:
149 {
150 struct tcphdr *tcp;
151
152 if (packet.len < sizeof(*tcp))
153 {
154 DBG1(DBG_ESP, "TCP packet too short");
155 return FALSE;
156 }
157 tcp = (struct tcphdr*)packet.ptr;
158 *sport = ntohs(tcp->source);
159 *dport = ntohs(tcp->dest);
160 break;
161 }
162 default:
163 break;
164 }
165 return TRUE;
166 }
167
168 /**
169 * Described in header.
170 */
171 ip_packet_t *ip_packet_create(chunk_t packet)
172 {
173 private_ip_packet_t *this;
174 u_int8_t version, next_header;
175 u_int16_t sport = 0, dport = 0;
176 host_t *src, *dst;
177 chunk_t payload;
178
179 if (packet.len < 1)
180 {
181 DBG1(DBG_ESP, "IP packet too short");
182 goto failed;
183 }
184
185 version = (packet.ptr[0] & 0xf0) >> 4;
186
187 switch (version)
188 {
189 case 4:
190 {
191 struct ip *ip;
192
193 if (packet.len < sizeof(struct ip))
194 {
195 DBG1(DBG_ESP, "IPv4 packet too short");
196 goto failed;
197 }
198 ip = (struct ip*)packet.ptr;
199 /* remove any RFC 4303 TFC extra padding */
200 packet.len = min(packet.len, untoh16(&ip->ip_len));
201 payload = chunk_skip(packet, ip->ip_hl * 4);
202 if (!parse_transport_header(payload, ip->ip_p, &sport, &dport))
203 {
204 goto failed;
205 }
206 src = host_create_from_chunk(AF_INET,
207 chunk_from_thing(ip->ip_src), sport);
208 dst = host_create_from_chunk(AF_INET,
209 chunk_from_thing(ip->ip_dst), dport);
210 next_header = ip->ip_p;
211 break;
212 }
213 #ifdef HAVE_NETINET_IP6_H
214 case 6:
215 {
216 struct ip6_hdr *ip;
217
218 if (packet.len < sizeof(*ip))
219 {
220 DBG1(DBG_ESP, "IPv6 packet too short");
221 goto failed;
222 }
223 ip = (struct ip6_hdr*)packet.ptr;
224 /* remove any RFC 4303 TFC extra padding */
225 packet.len = min(packet.len, untoh16(&ip->ip6_plen));
226 /* we only handle packets without extension headers, just skip the
227 * basic IPv6 header */
228 payload = chunk_skip(packet, 40);
229 if (!parse_transport_header(payload, ip->ip6_nxt, &sport, &dport))
230 {
231 goto failed;
232 }
233 src = host_create_from_chunk(AF_INET6,
234 chunk_from_thing(ip->ip6_src), sport);
235 dst = host_create_from_chunk(AF_INET6,
236 chunk_from_thing(ip->ip6_dst), dport);
237 next_header = ip->ip6_nxt;
238 break;
239 }
240 #endif /* HAVE_NETINET_IP6_H */
241 default:
242 DBG1(DBG_ESP, "unsupported IP version");
243 goto failed;
244 }
245
246 INIT(this,
247 .public = {
248 .get_version = _get_version,
249 .get_source = _get_source,
250 .get_destination = _get_destination,
251 .get_next_header = _get_next_header,
252 .get_encoding = _get_encoding,
253 .get_payload = _get_payload,
254 .clone = _clone_,
255 .destroy = _destroy,
256 },
257 .src = src,
258 .dst = dst,
259 .packet = packet,
260 .payload = payload,
261 .version = version,
262 .next_header = next_header,
263 );
264 return &this->public;
265
266 failed:
267 chunk_free(&packet);
268 return NULL;
269 }
270
271 /**
272 * Calculate the checksum for the pseudo IP header
273 */
274 static u_int16_t pseudo_header_checksum(host_t *src, host_t *dst,
275 u_int8_t proto, chunk_t payload)
276 {
277 switch (src->get_family(src))
278 {
279 case AF_INET:
280 {
281 struct __attribute__((packed)) {
282 u_int32_t src;
283 u_int32_t dst;
284 u_char zero;
285 u_char proto;
286 u_int16_t len;
287 } pseudo = {
288 .proto = proto,
289 .len = htons(payload.len),
290 };
291 memcpy(&pseudo.src, src->get_address(src).ptr,
292 sizeof(pseudo.src));
293 memcpy(&pseudo.dst, dst->get_address(dst).ptr,
294 sizeof(pseudo.dst));
295 return chunk_internet_checksum(chunk_from_thing(pseudo));
296 }
297 case AF_INET6:
298 {
299 struct __attribute__((packed)) {
300 u_char src[16];
301 u_char dst[16];
302 u_int32_t len;
303 u_char zero[3];
304 u_char next_header;
305 } pseudo = {
306 .next_header = proto,
307 .len = htons(payload.len),
308 };
309 memcpy(&pseudo.src, src->get_address(src).ptr,
310 sizeof(pseudo.src));
311 memcpy(&pseudo.dst, dst->get_address(dst).ptr,
312 sizeof(pseudo.dst));
313 return chunk_internet_checksum(chunk_from_thing(pseudo));
314 }
315 }
316 return 0xffff;
317 }
318
319 /**
320 * Apply transport ports and calculate header checksums
321 */
322 static void fix_transport_header(host_t *src, host_t *dst, u_int8_t proto,
323 chunk_t payload)
324 {
325 u_int16_t sum = 0, sport, dport;
326
327 sport = src->get_port(src);
328 dport = dst->get_port(dst);
329
330 switch (proto)
331 {
332 case IPPROTO_UDP:
333 {
334 struct udphdr *udp;
335
336 if (payload.len < sizeof(*udp))
337 {
338 return;
339 }
340 udp = (struct udphdr*)payload.ptr;
341 if (sport != 0)
342 {
343 udp->source = htons(sport);
344 }
345 if (dport != 0)
346 {
347 udp->dest = htons(dport);
348 }
349 udp->check = 0;
350 sum = pseudo_header_checksum(src, dst, proto, payload);
351 udp->check = chunk_internet_checksum_inc(payload, sum);
352 break;
353 }
354 case IPPROTO_TCP:
355 {
356 struct tcphdr *tcp;
357
358 if (payload.len < sizeof(*tcp))
359 {
360 return;
361 }
362 tcp = (struct tcphdr*)payload.ptr;
363 if (sport != 0)
364 {
365 tcp->source = htons(sport);
366 }
367 if (dport != 0)
368 {
369 tcp->dest = htons(dport);
370 }
371 tcp->check = 0;
372 sum = pseudo_header_checksum(src, dst, proto, payload);
373 tcp->check = chunk_internet_checksum_inc(payload, sum);
374 break;
375 }
376 default:
377 break;
378 }
379 }
380
381 /**
382 * Described in header.
383 */
384 ip_packet_t *ip_packet_create_from_data(host_t *src, host_t *dst,
385 u_int8_t next_header, chunk_t data)
386 {
387 chunk_t packet;
388 int family;
389
390 family = src->get_family(src);
391 if (family != dst->get_family(dst))
392 {
393 DBG1(DBG_ESP, "address family does not match");
394 return NULL;
395 }
396
397 switch (family)
398 {
399 case AF_INET:
400 {
401 struct ip ip = {
402 .ip_v = 4,
403 .ip_hl = 5,
404 .ip_len = htons(20 + data.len),
405 .ip_ttl = 0x80,
406 .ip_p = next_header,
407 };
408 memcpy(&ip.ip_src, src->get_address(src).ptr, sizeof(ip.ip_src));
409 memcpy(&ip.ip_dst, dst->get_address(dst).ptr, sizeof(ip.ip_dst));
410 ip.ip_sum = chunk_internet_checksum(chunk_from_thing(ip));
411
412 packet = chunk_cat("cc", chunk_from_thing(ip), data);
413 fix_transport_header(src, dst, next_header, chunk_skip(packet, 20));
414 return ip_packet_create(packet);
415 }
416 #ifdef HAVE_NETINET_IP6_H
417 case AF_INET6:
418 {
419 struct ip6_hdr ip = {
420 .ip6_flow = htonl(6),
421 .ip6_plen = htons(40 + data.len),
422 .ip6_nxt = next_header,
423 .ip6_hlim = 0x80,
424 };
425 memcpy(&ip.ip6_src, src->get_address(src).ptr, sizeof(ip.ip6_src));
426 memcpy(&ip.ip6_dst, dst->get_address(dst).ptr, sizeof(ip.ip6_dst));
427
428 packet = chunk_cat("cc", chunk_from_thing(ip), data);
429 fix_transport_header(src, dst, next_header, chunk_skip(packet, 40));
430 return ip_packet_create(packet);
431 }
432 #endif /* HAVE_NETINET_IP6_H */
433 default:
434 DBG1(DBG_ESP, "unsupported address family");
435 return NULL;
436 }
437 }
438
439 /**
440 * Described in header.
441 */
442 ip_packet_t *ip_packet_create_udp_from_data(host_t *src, host_t *dst,
443 chunk_t data)
444 {
445 struct udphdr udp = {
446 .len = htons(8 + data.len),
447 .check = 0,
448 };
449 ip_packet_t *packet;
450
451 data = chunk_cat("cc", chunk_from_thing(udp), data);
452 packet = ip_packet_create_from_data(src, dst, IPPROTO_UDP, data);
453 chunk_free(&data);
454 return packet;
455 }