]> git.ipfire.org Git - thirdparty/strongswan.git/blame - src/libipsec/ip_packet.c
libipsec: Fix ip_packet_create_from_data() version field in IPv6 header
[thirdparty/strongswan.git] / src / libipsec / ip_packet.c
CommitLineData
2dd47c24 1/*
a10eb935 2 * Copyright (C) 2012-2014 Tobias Brunner
2dd47c24
TB
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>
f05b4272 20#include <utils/debug.h>
2dd47c24 21
ae8ac58c 22#include <sys/types.h>
1da56773
TB
23
24#ifndef WIN32
2dd47c24
TB
25#include <netinet/in.h>
26#include <netinet/ip.h>
b6a07151 27#ifdef HAVE_NETINET_IP6_H
2dd47c24 28#include <netinet/ip6.h>
b6a07151 29#endif
1da56773
TB
30#else
31struct 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));
48struct 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));
bdcaca76
TB
55struct 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 */
1da56773 60#endif
2dd47c24 61
b3cc4638
TB
62#ifndef IP_OFFMASK
63#define IP_OFFMASK 0x1fff
64#endif
65
f8613abc
TB
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 */
70struct tcphdr {
b12c53ce
AS
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;
f8613abc
TB
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 */
86struct udphdr {
b12c53ce
AS
87 uint16_t source;
88 uint16_t dest;
89 uint16_t len;
90 uint16_t check;
f8613abc
TB
91} __attribute__((packed));
92
2dd47c24
TB
93typedef struct private_ip_packet_t private_ip_packet_t;
94
95/**
96 * Private additions to ip_packet_t.
97 */
98struct 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
46bb3698
TB
120 /**
121 * IP payload (points into packet)
122 */
123 chunk_t payload;
124
2dd47c24
TB
125 /**
126 * IP version
127 */
b12c53ce 128 uint8_t version;
2dd47c24
TB
129
130 /**
131 * Protocol|Next Header field
132 */
b12c53ce 133 uint8_t next_header;
2dd47c24
TB
134
135};
136
b12c53ce 137METHOD(ip_packet_t, get_version, uint8_t,
2dd47c24
TB
138 private_ip_packet_t *this)
139{
140 return this->version;
141}
142
143METHOD(ip_packet_t, get_source, host_t*,
144 private_ip_packet_t *this)
145{
146 return this->src;
147}
148
149METHOD(ip_packet_t, get_destination, host_t*,
150 private_ip_packet_t *this)
151{
152 return this->dst;
153}
154
155METHOD(ip_packet_t, get_encoding, chunk_t,
156 private_ip_packet_t *this)
157{
158 return this->packet;
159}
160
46bb3698
TB
161METHOD(ip_packet_t, get_payload, chunk_t,
162 private_ip_packet_t *this)
163{
164 return this->payload;
165}
166
b12c53ce 167METHOD(ip_packet_t, get_next_header, uint8_t,
2dd47c24
TB
168 private_ip_packet_t *this)
169{
170 return this->next_header;
171}
172
5f35b733 173METHOD(ip_packet_t, clone_, ip_packet_t*,
2dd47c24
TB
174 private_ip_packet_t *this)
175{
2b84ccd6 176 return ip_packet_create(chunk_clone(this->packet));
2dd47c24
TB
177}
178
179METHOD(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
a10eb935
TB
188/**
189 * Parse transport protocol header
190 */
b12c53ce
AS
191static bool parse_transport_header(chunk_t packet, uint8_t proto,
192 uint16_t *sport, uint16_t *dport)
a10eb935
TB
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
bdcaca76
TB
230#ifdef HAVE_NETINET_IP6_H
231/**
232 * Skip to the actual payload and parse the transport header.
233 */
234static 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
2dd47c24
TB
280/**
281 * Described in header.
282 */
283ip_packet_t *ip_packet_create(chunk_t packet)
284{
285 private_ip_packet_t *this;
b12c53ce
AS
286 uint8_t version, next_header;
287 uint16_t sport = 0, dport = 0;
2dd47c24 288 host_t *src, *dst;
46bb3698 289 chunk_t payload;
2dd47c24
TB
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 {
39e9af96 303 struct ip *ip;
2dd47c24 304
39e9af96 305 if (packet.len < sizeof(struct ip))
2dd47c24
TB
306 {
307 DBG1(DBG_ESP, "IPv4 packet too short");
308 goto failed;
309 }
39e9af96 310 ip = (struct ip*)packet.ptr;
293515f9
MW
311 /* remove any RFC 4303 TFC extra padding */
312 packet.len = min(packet.len, untoh16(&ip->ip_len));
46bb3698 313 payload = chunk_skip(packet, ip->ip_hl * 4);
b3cc4638
TB
314 if ((ip->ip_off & htons(IP_OFFMASK)) == 0 &&
315 !parse_transport_header(payload, ip->ip_p, &sport, &dport))
a10eb935
TB
316 {
317 goto failed;
318 }
2dd47c24 319 src = host_create_from_chunk(AF_INET,
a10eb935 320 chunk_from_thing(ip->ip_src), sport);
2dd47c24 321 dst = host_create_from_chunk(AF_INET,
a10eb935 322 chunk_from_thing(ip->ip_dst), dport);
39e9af96 323 next_header = ip->ip_p;
2dd47c24
TB
324 break;
325 }
b6a07151 326#ifdef HAVE_NETINET_IP6_H
2dd47c24
TB
327 case 6:
328 {
329 struct ip6_hdr *ip;
330
a10eb935 331 if (packet.len < sizeof(*ip))
2dd47c24
TB
332 {
333 DBG1(DBG_ESP, "IPv6 packet too short");
334 goto failed;
335 }
336 ip = (struct ip6_hdr*)packet.ptr;
293515f9 337 /* remove any RFC 4303 TFC extra padding */
15dee933 338 packet.len = min(packet.len, 40 + untoh16(&ip->ip6_plen));
bdcaca76
TB
339 if (!parse_transport_header_v6(ip, packet, &payload, &next_header,
340 &sport, &dport))
a10eb935
TB
341 {
342 goto failed;
343 }
2dd47c24 344 src = host_create_from_chunk(AF_INET6,
a10eb935 345 chunk_from_thing(ip->ip6_src), sport);
2dd47c24 346 dst = host_create_from_chunk(AF_INET6,
a10eb935 347 chunk_from_thing(ip->ip6_dst), dport);
43e0cb65 348 break;
2dd47c24 349 }
b6a07151 350#endif /* HAVE_NETINET_IP6_H */
2dd47c24
TB
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,
46bb3698 363 .get_payload = _get_payload,
5f35b733 364 .clone = _clone_,
2dd47c24
TB
365 .destroy = _destroy,
366 },
367 .src = src,
368 .dst = dst,
369 .packet = packet,
46bb3698 370 .payload = payload,
2dd47c24
TB
371 .version = version,
372 .next_header = next_header,
373 );
374 return &this->public;
375
376failed:
377 chunk_free(&packet);
378 return NULL;
379}
d56d9a45
TB
380
381/**
382 * Calculate the checksum for the pseudo IP header
383 */
b12c53ce
AS
384static uint16_t pseudo_header_checksum(host_t *src, host_t *dst,
385 uint8_t proto, chunk_t payload)
d56d9a45
TB
386{
387 switch (src->get_family(src))
388 {
389 case AF_INET:
390 {
391 struct __attribute__((packed)) {
b12c53ce
AS
392 uint32_t src;
393 uint32_t dst;
d56d9a45
TB
394 u_char zero;
395 u_char proto;
b12c53ce 396 uint16_t len;
d56d9a45
TB
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];
b12c53ce 412 uint32_t len;
d56d9a45
TB
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/**
108a6789 430 * Apply transport ports and calculate header checksums
d56d9a45 431 */
b12c53ce 432static void fix_transport_header(host_t *src, host_t *dst, uint8_t proto,
108a6789 433 chunk_t payload)
d56d9a45 434{
b12c53ce 435 uint16_t sum = 0, sport, dport;
108a6789
TB
436
437 sport = src->get_port(src);
438 dport = dst->get_port(dst);
d56d9a45
TB
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;
108a6789
TB
451 if (sport != 0)
452 {
453 udp->source = htons(sport);
454 }
455 if (dport != 0)
456 {
457 udp->dest = htons(dport);
458 }
d56d9a45
TB
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;
108a6789
TB
473 if (sport != 0)
474 {
475 tcp->source = htons(sport);
476 }
477 if (dport != 0)
478 {
479 tcp->dest = htons(dport);
480 }
d56d9a45
TB
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 */
494ip_packet_t *ip_packet_create_from_data(host_t *src, host_t *dst,
b12c53ce 495 uint8_t next_header, chunk_t data)
d56d9a45
TB
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);
108a6789 523 fix_transport_header(src, dst, next_header, chunk_skip(packet, 20));
d56d9a45
TB
524 return ip_packet_create(packet);
525 }
526#ifdef HAVE_NETINET_IP6_H
527 case AF_INET6:
528 {
529 struct ip6_hdr ip = {
04ede658 530 .ip6_flow = htonl(6 << 28),
9e20fdba 531 .ip6_plen = htons(data.len),
d56d9a45
TB
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);
108a6789 539 fix_transport_header(src, dst, next_header, chunk_skip(packet, 40));
d56d9a45
TB
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}
16e519d4
TB
548
549/**
550 * Described in header.
551 */
552ip_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}