]> git.ipfire.org Git - thirdparty/strongswan.git/blob - src/libipsec/ip_packet.c
Merge branch 'libipsec-ip-frag'
[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
24 #ifndef WIN32
25 #include <netinet/in.h>
26 #include <netinet/ip.h>
27 #ifdef HAVE_NETINET_IP6_H
28 #include <netinet/ip6.h>
29 #endif
30 #else
31 struct ip {
32 #if BYTE_ORDER == LITTLE_ENDIAN
33 uint8_t ip_hl: 4;
34 uint8_t ip_v: 4;
35 #elif BYTE_ORDER == BIG_ENDIAN
36 uint8_t ip_v: 4;
37 uint8_t ip_hl: 4;
38 #endif
39 uint8_t ip_tos;
40 uint16_t ip_len;
41 uint16_t ip_id;
42 uint16_t ip_off;
43 uint8_t ip_ttl;
44 uint8_t ip_p;
45 uint16_t ip_sum;
46 struct in_addr ip_src, ip_dst;
47 } __attribute__((packed));
48 struct ip6_hdr {
49 uint32_t ip6_flow; /* 4 bit version, 8 bit TC, 20 bit flow label */
50 uint16_t ip6_plen;
51 uint8_t ip6_nxt;
52 uint8_t ip6_hlim;
53 struct in6_addr ip6_src, ip6_dst;
54 } __attribute__((packed));
55 struct ip6_ext {
56 uint8_t ip6e_nxt;
57 uint8_t ip6e_len;
58 } __attribute__((packed));
59 #define HAVE_NETINET_IP6_H /* not really, but we only need the structs above */
60 #endif
61
62 #ifndef IP_OFFMASK
63 #define IP_OFFMASK 0x1fff
64 #endif
65
66 /**
67 * TCP header, defined here because platforms disagree regarding member names
68 * and unfortunately Android does not define a variant with BSD names.
69 */
70 struct tcphdr {
71 uint16_t source;
72 uint16_t dest;
73 uint32_t seq;
74 uint32_t ack_seq;
75 uint16_t flags;
76 uint16_t window;
77 uint16_t check;
78 uint16_t urg_ptr;
79 } __attribute__((packed));
80
81 /**
82 * UDP header, similar to the TCP header the system headers disagree on member
83 * names. Linux uses a union and on Android we could define __FAVOR_BSD to get
84 * the BSD member names, but this is simpler and more consistent with the above.
85 */
86 struct udphdr {
87 uint16_t source;
88 uint16_t dest;
89 uint16_t len;
90 uint16_t check;
91 } __attribute__((packed));
92
93 typedef struct private_ip_packet_t private_ip_packet_t;
94
95 /**
96 * Private additions to ip_packet_t.
97 */
98 struct private_ip_packet_t {
99
100 /**
101 * Public members
102 */
103 ip_packet_t public;
104
105 /**
106 * Source address
107 */
108 host_t *src;
109
110 /**
111 * Destination address
112 */
113 host_t *dst;
114
115 /**
116 * IP packet
117 */
118 chunk_t packet;
119
120 /**
121 * IP payload (points into packet)
122 */
123 chunk_t payload;
124
125 /**
126 * IP version
127 */
128 uint8_t version;
129
130 /**
131 * Protocol|Next Header field
132 */
133 uint8_t next_header;
134
135 };
136
137 METHOD(ip_packet_t, get_version, uint8_t,
138 private_ip_packet_t *this)
139 {
140 return this->version;
141 }
142
143 METHOD(ip_packet_t, get_source, host_t*,
144 private_ip_packet_t *this)
145 {
146 return this->src;
147 }
148
149 METHOD(ip_packet_t, get_destination, host_t*,
150 private_ip_packet_t *this)
151 {
152 return this->dst;
153 }
154
155 METHOD(ip_packet_t, get_encoding, chunk_t,
156 private_ip_packet_t *this)
157 {
158 return this->packet;
159 }
160
161 METHOD(ip_packet_t, get_payload, chunk_t,
162 private_ip_packet_t *this)
163 {
164 return this->payload;
165 }
166
167 METHOD(ip_packet_t, get_next_header, uint8_t,
168 private_ip_packet_t *this)
169 {
170 return this->next_header;
171 }
172
173 METHOD(ip_packet_t, clone_, ip_packet_t*,
174 private_ip_packet_t *this)
175 {
176 return ip_packet_create(chunk_clone(this->packet));
177 }
178
179 METHOD(ip_packet_t, destroy, void,
180 private_ip_packet_t *this)
181 {
182 this->src->destroy(this->src);
183 this->dst->destroy(this->dst);
184 chunk_free(&this->packet);
185 free(this);
186 }
187
188 /**
189 * Parse transport protocol header
190 */
191 static bool parse_transport_header(chunk_t packet, uint8_t proto,
192 uint16_t *sport, uint16_t *dport)
193 {
194 switch (proto)
195 {
196 case IPPROTO_UDP:
197 {
198 struct udphdr *udp;
199
200 if (packet.len < sizeof(*udp))
201 {
202 DBG1(DBG_ESP, "UDP packet too short");
203 return FALSE;
204 }
205 udp = (struct udphdr*)packet.ptr;
206 *sport = ntohs(udp->source);
207 *dport = ntohs(udp->dest);
208 break;
209 }
210 case IPPROTO_TCP:
211 {
212 struct tcphdr *tcp;
213
214 if (packet.len < sizeof(*tcp))
215 {
216 DBG1(DBG_ESP, "TCP packet too short");
217 return FALSE;
218 }
219 tcp = (struct tcphdr*)packet.ptr;
220 *sport = ntohs(tcp->source);
221 *dport = ntohs(tcp->dest);
222 break;
223 }
224 default:
225 break;
226 }
227 return TRUE;
228 }
229
230 #ifdef HAVE_NETINET_IP6_H
231 /**
232 * Skip to the actual payload and parse the transport header.
233 */
234 static bool parse_transport_header_v6(struct ip6_hdr *ip, chunk_t packet,
235 chunk_t *payload, uint8_t *proto,
236 uint16_t *sport, uint16_t *dport)
237 {
238 struct ip6_ext *ext;
239 bool fragment = FALSE;
240
241 *proto = ip->ip6_nxt;
242 *payload = chunk_skip(packet, 40);
243 while (payload->len >= sizeof(struct ip6_ext))
244 {
245 switch (*proto)
246 {
247 case 44: /* Fragment Header */
248 fragment = TRUE;
249 /* skip the header */
250 case 0: /* Hop-by-Hop Options Header */
251 case 43: /* Routing Header */
252 case 60: /* Destination Options Header */
253 case 135: /* Mobility Header */
254 case 139: /* HIP */
255 case 140: /* Shim6 */
256 /* simply skip over these headers for now */
257 ext = (struct ip6_ext*)payload->ptr;
258 *proto = ext->ip6e_nxt;
259 *payload = chunk_skip(*payload, 8 * (ext->ip6e_len + 1));
260 continue;
261 default:
262 /* assume anything else is an upper layer protocol but only
263 * attempt to parse the transport header for non-fragmented
264 * packets as there is no guarantee that initial fragments
265 * contain the transport header, depending on the number and
266 * type of extension headers */
267 if (!fragment &&
268 !parse_transport_header(*payload, *proto, sport, dport))
269 {
270 return FALSE;
271 }
272 break;
273 }
274 break;
275 }
276 return TRUE;
277 }
278 #endif /* HAVE_NETINET_IP6_H */
279
280 /**
281 * Described in header.
282 */
283 ip_packet_t *ip_packet_create(chunk_t packet)
284 {
285 private_ip_packet_t *this;
286 uint8_t version, next_header;
287 uint16_t sport = 0, dport = 0;
288 host_t *src, *dst;
289 chunk_t payload;
290
291 if (packet.len < 1)
292 {
293 DBG1(DBG_ESP, "IP packet too short");
294 goto failed;
295 }
296
297 version = (packet.ptr[0] & 0xf0) >> 4;
298
299 switch (version)
300 {
301 case 4:
302 {
303 struct ip *ip;
304
305 if (packet.len < sizeof(struct ip))
306 {
307 DBG1(DBG_ESP, "IPv4 packet too short");
308 goto failed;
309 }
310 ip = (struct ip*)packet.ptr;
311 /* remove any RFC 4303 TFC extra padding */
312 packet.len = min(packet.len, untoh16(&ip->ip_len));
313 payload = chunk_skip(packet, ip->ip_hl * 4);
314 if ((ip->ip_off & htons(IP_OFFMASK)) == 0 &&
315 !parse_transport_header(payload, ip->ip_p, &sport, &dport))
316 {
317 goto failed;
318 }
319 src = host_create_from_chunk(AF_INET,
320 chunk_from_thing(ip->ip_src), sport);
321 dst = host_create_from_chunk(AF_INET,
322 chunk_from_thing(ip->ip_dst), dport);
323 next_header = ip->ip_p;
324 break;
325 }
326 #ifdef HAVE_NETINET_IP6_H
327 case 6:
328 {
329 struct ip6_hdr *ip;
330
331 if (packet.len < sizeof(*ip))
332 {
333 DBG1(DBG_ESP, "IPv6 packet too short");
334 goto failed;
335 }
336 ip = (struct ip6_hdr*)packet.ptr;
337 /* remove any RFC 4303 TFC extra padding */
338 packet.len = min(packet.len, 40 + untoh16(&ip->ip6_plen));
339 if (!parse_transport_header_v6(ip, packet, &payload, &next_header,
340 &sport, &dport))
341 {
342 goto failed;
343 }
344 src = host_create_from_chunk(AF_INET6,
345 chunk_from_thing(ip->ip6_src), sport);
346 dst = host_create_from_chunk(AF_INET6,
347 chunk_from_thing(ip->ip6_dst), dport);
348 break;
349 }
350 #endif /* HAVE_NETINET_IP6_H */
351 default:
352 DBG1(DBG_ESP, "unsupported IP version");
353 goto failed;
354 }
355
356 INIT(this,
357 .public = {
358 .get_version = _get_version,
359 .get_source = _get_source,
360 .get_destination = _get_destination,
361 .get_next_header = _get_next_header,
362 .get_encoding = _get_encoding,
363 .get_payload = _get_payload,
364 .clone = _clone_,
365 .destroy = _destroy,
366 },
367 .src = src,
368 .dst = dst,
369 .packet = packet,
370 .payload = payload,
371 .version = version,
372 .next_header = next_header,
373 );
374 return &this->public;
375
376 failed:
377 chunk_free(&packet);
378 return NULL;
379 }
380
381 /**
382 * Calculate the checksum for the pseudo IP header
383 */
384 static uint16_t pseudo_header_checksum(host_t *src, host_t *dst,
385 uint8_t proto, chunk_t payload)
386 {
387 switch (src->get_family(src))
388 {
389 case AF_INET:
390 {
391 struct __attribute__((packed)) {
392 uint32_t src;
393 uint32_t dst;
394 u_char zero;
395 u_char proto;
396 uint16_t len;
397 } pseudo = {
398 .proto = proto,
399 .len = htons(payload.len),
400 };
401 memcpy(&pseudo.src, src->get_address(src).ptr,
402 sizeof(pseudo.src));
403 memcpy(&pseudo.dst, dst->get_address(dst).ptr,
404 sizeof(pseudo.dst));
405 return chunk_internet_checksum(chunk_from_thing(pseudo));
406 }
407 case AF_INET6:
408 {
409 struct __attribute__((packed)) {
410 u_char src[16];
411 u_char dst[16];
412 uint32_t len;
413 u_char zero[3];
414 u_char next_header;
415 } pseudo = {
416 .next_header = proto,
417 .len = htons(payload.len),
418 };
419 memcpy(&pseudo.src, src->get_address(src).ptr,
420 sizeof(pseudo.src));
421 memcpy(&pseudo.dst, dst->get_address(dst).ptr,
422 sizeof(pseudo.dst));
423 return chunk_internet_checksum(chunk_from_thing(pseudo));
424 }
425 }
426 return 0xffff;
427 }
428
429 /**
430 * Apply transport ports and calculate header checksums
431 */
432 static void fix_transport_header(host_t *src, host_t *dst, uint8_t proto,
433 chunk_t payload)
434 {
435 uint16_t sum = 0, sport, dport;
436
437 sport = src->get_port(src);
438 dport = dst->get_port(dst);
439
440 switch (proto)
441 {
442 case IPPROTO_UDP:
443 {
444 struct udphdr *udp;
445
446 if (payload.len < sizeof(*udp))
447 {
448 return;
449 }
450 udp = (struct udphdr*)payload.ptr;
451 if (sport != 0)
452 {
453 udp->source = htons(sport);
454 }
455 if (dport != 0)
456 {
457 udp->dest = htons(dport);
458 }
459 udp->check = 0;
460 sum = pseudo_header_checksum(src, dst, proto, payload);
461 udp->check = chunk_internet_checksum_inc(payload, sum);
462 break;
463 }
464 case IPPROTO_TCP:
465 {
466 struct tcphdr *tcp;
467
468 if (payload.len < sizeof(*tcp))
469 {
470 return;
471 }
472 tcp = (struct tcphdr*)payload.ptr;
473 if (sport != 0)
474 {
475 tcp->source = htons(sport);
476 }
477 if (dport != 0)
478 {
479 tcp->dest = htons(dport);
480 }
481 tcp->check = 0;
482 sum = pseudo_header_checksum(src, dst, proto, payload);
483 tcp->check = chunk_internet_checksum_inc(payload, sum);
484 break;
485 }
486 default:
487 break;
488 }
489 }
490
491 /**
492 * Described in header.
493 */
494 ip_packet_t *ip_packet_create_from_data(host_t *src, host_t *dst,
495 uint8_t next_header, chunk_t data)
496 {
497 chunk_t packet;
498 int family;
499
500 family = src->get_family(src);
501 if (family != dst->get_family(dst))
502 {
503 DBG1(DBG_ESP, "address family does not match");
504 return NULL;
505 }
506
507 switch (family)
508 {
509 case AF_INET:
510 {
511 struct ip ip = {
512 .ip_v = 4,
513 .ip_hl = 5,
514 .ip_len = htons(20 + data.len),
515 .ip_ttl = 0x80,
516 .ip_p = next_header,
517 };
518 memcpy(&ip.ip_src, src->get_address(src).ptr, sizeof(ip.ip_src));
519 memcpy(&ip.ip_dst, dst->get_address(dst).ptr, sizeof(ip.ip_dst));
520 ip.ip_sum = chunk_internet_checksum(chunk_from_thing(ip));
521
522 packet = chunk_cat("cc", chunk_from_thing(ip), data);
523 fix_transport_header(src, dst, next_header, chunk_skip(packet, 20));
524 return ip_packet_create(packet);
525 }
526 #ifdef HAVE_NETINET_IP6_H
527 case AF_INET6:
528 {
529 struct ip6_hdr ip = {
530 .ip6_flow = htonl(6),
531 .ip6_plen = htons(data.len),
532 .ip6_nxt = next_header,
533 .ip6_hlim = 0x80,
534 };
535 memcpy(&ip.ip6_src, src->get_address(src).ptr, sizeof(ip.ip6_src));
536 memcpy(&ip.ip6_dst, dst->get_address(dst).ptr, sizeof(ip.ip6_dst));
537
538 packet = chunk_cat("cc", chunk_from_thing(ip), data);
539 fix_transport_header(src, dst, next_header, chunk_skip(packet, 40));
540 return ip_packet_create(packet);
541 }
542 #endif /* HAVE_NETINET_IP6_H */
543 default:
544 DBG1(DBG_ESP, "unsupported address family");
545 return NULL;
546 }
547 }
548
549 /**
550 * Described in header.
551 */
552 ip_packet_t *ip_packet_create_udp_from_data(host_t *src, host_t *dst,
553 chunk_t data)
554 {
555 struct udphdr udp = {
556 .len = htons(8 + data.len),
557 .check = 0,
558 };
559 ip_packet_t *packet;
560
561 data = chunk_cat("cc", chunk_from_thing(udp), data);
562 packet = ip_packet_create_from_data(src, dst, IPPROTO_UDP, data);
563 chunk_free(&data);
564 return packet;
565 }