2 * Copyright (C) 2012-2014 Tobias Brunner
4 * Copyright (C) secunet Security Networks AG
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>.
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
18 #include "ip_packet.h"
21 #include <utils/debug.h>
23 #include <sys/types.h>
26 #include <netinet/in.h>
27 #include <netinet/ip.h>
28 #ifdef HAVE_NETINET_IP6_H
29 #include <netinet/ip6.h>
33 #if BYTE_ORDER == LITTLE_ENDIAN
36 #elif BYTE_ORDER == BIG_ENDIAN
47 struct in_addr ip_src
, ip_dst
;
48 } __attribute__((packed
));
50 uint32_t ip6_flow
; /* 4 bit version, 8 bit TC, 20 bit flow label */
54 struct in6_addr ip6_src
, ip6_dst
;
55 } __attribute__((packed
));
59 } __attribute__((packed
));
60 #define HAVE_NETINET_IP6_H /* not really, but we only need the structs above */
64 #define IP_OFFMASK 0x1fff
68 * TCP header, defined here because platforms disagree regarding member names
69 * and unfortunately Android does not define a variant with BSD names.
80 } __attribute__((packed
));
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.
92 } __attribute__((packed
));
94 typedef struct private_ip_packet_t private_ip_packet_t
;
97 * Private additions to ip_packet_t.
99 struct private_ip_packet_t
{
112 * Destination address
122 * IP payload (points into packet)
132 * Protocol|Next Header field
138 METHOD(ip_packet_t
, get_version
, uint8_t,
139 private_ip_packet_t
*this)
141 return this->version
;
144 METHOD(ip_packet_t
, get_source
, host_t
*,
145 private_ip_packet_t
*this)
150 METHOD(ip_packet_t
, get_destination
, host_t
*,
151 private_ip_packet_t
*this)
156 METHOD(ip_packet_t
, get_encoding
, chunk_t
,
157 private_ip_packet_t
*this)
162 METHOD(ip_packet_t
, get_payload
, chunk_t
,
163 private_ip_packet_t
*this)
165 return this->payload
;
168 METHOD(ip_packet_t
, get_next_header
, uint8_t,
169 private_ip_packet_t
*this)
171 return this->next_header
;
174 METHOD(ip_packet_t
, clone_
, ip_packet_t
*,
175 private_ip_packet_t
*this)
177 return ip_packet_create(chunk_clone(this->packet
));
180 METHOD(ip_packet_t
, destroy
, void,
181 private_ip_packet_t
*this)
183 this->src
->destroy(this->src
);
184 this->dst
->destroy(this->dst
);
185 chunk_free(&this->packet
);
190 * Parse transport protocol header
192 static bool parse_transport_header(chunk_t packet
, uint8_t proto
,
193 uint16_t *sport
, uint16_t *dport
)
201 if (packet
.len
< sizeof(*udp
))
203 DBG1(DBG_ESP
, "UDP packet too short");
206 udp
= (struct udphdr
*)packet
.ptr
;
207 *sport
= ntohs(udp
->source
);
208 *dport
= ntohs(udp
->dest
);
215 if (packet
.len
< sizeof(*tcp
))
217 DBG1(DBG_ESP
, "TCP packet too short");
220 tcp
= (struct tcphdr
*)packet
.ptr
;
221 *sport
= ntohs(tcp
->source
);
222 *dport
= ntohs(tcp
->dest
);
231 #ifdef HAVE_NETINET_IP6_H
233 * Skip to the actual payload and parse the transport header.
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
)
240 bool fragment
= FALSE
;
242 *proto
= ip
->ip6_nxt
;
243 *payload
= chunk_skip(packet
, 40);
244 while (payload
->len
>= sizeof(struct ip6_ext
))
248 case 44: /* Fragment Header */
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 */
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));
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 */
269 !parse_transport_header(*payload
, *proto
, sport
, dport
))
279 #endif /* HAVE_NETINET_IP6_H */
282 * Described in header.
284 ip_packet_t
*ip_packet_create(chunk_t packet
)
286 private_ip_packet_t
*this;
287 uint8_t version
, next_header
;
288 uint16_t sport
= 0, dport
= 0;
294 DBG1(DBG_ESP
, "IP packet too short");
298 version
= (packet
.ptr
[0] & 0xf0) >> 4;
306 if (packet
.len
< sizeof(struct ip
))
308 DBG1(DBG_ESP
, "IPv4 packet too short");
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
))
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
;
327 #ifdef HAVE_NETINET_IP6_H
332 if (packet
.len
< sizeof(*ip
))
334 DBG1(DBG_ESP
, "IPv6 packet too short");
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
,
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
);
351 #endif /* HAVE_NETINET_IP6_H */
353 DBG1(DBG_ESP
, "unsupported IP version");
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
,
373 .next_header
= next_header
,
375 return &this->public;
383 * Calculate the checksum for the pseudo IP header
385 static uint16_t pseudo_header_checksum(host_t
*src
, host_t
*dst
,
386 uint8_t proto
, chunk_t payload
)
388 switch (src
->get_family(src
))
392 struct __attribute__((packed
)) {
400 .len
= htons(payload
.len
),
402 memcpy(&pseudo
.src
, src
->get_address(src
).ptr
,
404 memcpy(&pseudo
.dst
, dst
->get_address(dst
).ptr
,
406 return chunk_internet_checksum(chunk_from_thing(pseudo
));
410 struct __attribute__((packed
)) {
417 .next_header
= proto
,
418 .len
= htons(payload
.len
),
420 memcpy(&pseudo
.src
, src
->get_address(src
).ptr
,
422 memcpy(&pseudo
.dst
, dst
->get_address(dst
).ptr
,
424 return chunk_internet_checksum(chunk_from_thing(pseudo
));
431 * Apply transport ports and calculate header checksums
433 static void fix_transport_header(host_t
*src
, host_t
*dst
, uint8_t proto
,
436 uint16_t sum
= 0, sport
, dport
;
438 sport
= src
->get_port(src
);
439 dport
= dst
->get_port(dst
);
447 if (payload
.len
< sizeof(*udp
))
451 udp
= (struct udphdr
*)payload
.ptr
;
454 udp
->source
= htons(sport
);
458 udp
->dest
= htons(dport
);
461 sum
= pseudo_header_checksum(src
, dst
, proto
, payload
);
462 udp
->check
= chunk_internet_checksum_inc(payload
, sum
);
469 if (payload
.len
< sizeof(*tcp
))
473 tcp
= (struct tcphdr
*)payload
.ptr
;
476 tcp
->source
= htons(sport
);
480 tcp
->dest
= htons(dport
);
483 sum
= pseudo_header_checksum(src
, dst
, proto
, payload
);
484 tcp
->check
= chunk_internet_checksum_inc(payload
, sum
);
493 * Described in header.
495 ip_packet_t
*ip_packet_create_from_data(host_t
*src
, host_t
*dst
,
496 uint8_t next_header
, chunk_t data
)
501 family
= src
->get_family(src
);
502 if (family
!= dst
->get_family(dst
))
504 DBG1(DBG_ESP
, "address family does not match");
515 .ip_len
= htons(20 + data
.len
),
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
));
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
);
527 #ifdef HAVE_NETINET_IP6_H
530 struct ip6_hdr ip
= {
531 .ip6_flow
= htonl(6 << 28),
532 .ip6_plen
= htons(data
.len
),
533 .ip6_nxt
= next_header
,
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
));
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
);
543 #endif /* HAVE_NETINET_IP6_H */
545 DBG1(DBG_ESP
, "unsupported address family");
551 * Described in header.
553 ip_packet_t
*ip_packet_create_udp_from_data(host_t
*src
, host_t
*dst
,
556 struct udphdr udp
= {
557 .len
= htons(8 + data
.len
),
562 data
= chunk_cat("cc", chunk_from_thing(udp
), data
);
563 packet
= ip_packet_create_from_data(src
, dst
, IPPROTO_UDP
, data
);