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