2 * Copyright (C) 2012-2014 Tobias Brunner
3 * Hochschule fuer Technik Rapperswil
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>.
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
17 #include "ip_packet.h"
20 #include <utils/debug.h>
22 #include <sys/types.h>
25 #include <netinet/in.h>
26 #include <netinet/ip.h>
27 #ifdef HAVE_NETINET_IP6_H
28 #include <netinet/ip6.h>
32 #if BYTE_ORDER == LITTLE_ENDIAN
35 #elif BYTE_ORDER == BIG_ENDIAN
46 struct in_addr ip_src
, ip_dst
;
47 } __attribute__((packed
));
49 uint32_t ip6_flow
; /* 4 bit version, 8 bit TC, 20 bit flow label */
53 struct in6_addr ip6_src
, ip6_dst
;
54 } __attribute__((packed
));
58 } __attribute__((packed
));
59 #define HAVE_NETINET_IP6_H /* not really, but we only need the structs above */
63 #define IP_OFFMASK 0x1fff
67 * TCP header, defined here because platforms disagree regarding member names
68 * and unfortunately Android does not define a variant with BSD names.
79 } __attribute__((packed
));
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.
91 } __attribute__((packed
));
93 typedef struct private_ip_packet_t private_ip_packet_t
;
96 * Private additions to ip_packet_t.
98 struct private_ip_packet_t
{
111 * Destination address
121 * IP payload (points into packet)
131 * Protocol|Next Header field
137 METHOD(ip_packet_t
, get_version
, uint8_t,
138 private_ip_packet_t
*this)
140 return this->version
;
143 METHOD(ip_packet_t
, get_source
, host_t
*,
144 private_ip_packet_t
*this)
149 METHOD(ip_packet_t
, get_destination
, host_t
*,
150 private_ip_packet_t
*this)
155 METHOD(ip_packet_t
, get_encoding
, chunk_t
,
156 private_ip_packet_t
*this)
161 METHOD(ip_packet_t
, get_payload
, chunk_t
,
162 private_ip_packet_t
*this)
164 return this->payload
;
167 METHOD(ip_packet_t
, get_next_header
, uint8_t,
168 private_ip_packet_t
*this)
170 return this->next_header
;
173 METHOD(ip_packet_t
, clone_
, ip_packet_t
*,
174 private_ip_packet_t
*this)
176 return ip_packet_create(chunk_clone(this->packet
));
179 METHOD(ip_packet_t
, destroy
, void,
180 private_ip_packet_t
*this)
182 this->src
->destroy(this->src
);
183 this->dst
->destroy(this->dst
);
184 chunk_free(&this->packet
);
189 * Parse transport protocol header
191 static bool parse_transport_header(chunk_t packet
, uint8_t proto
,
192 uint16_t *sport
, uint16_t *dport
)
200 if (packet
.len
< sizeof(*udp
))
202 DBG1(DBG_ESP
, "UDP packet too short");
205 udp
= (struct udphdr
*)packet
.ptr
;
206 *sport
= ntohs(udp
->source
);
207 *dport
= ntohs(udp
->dest
);
214 if (packet
.len
< sizeof(*tcp
))
216 DBG1(DBG_ESP
, "TCP packet too short");
219 tcp
= (struct tcphdr
*)packet
.ptr
;
220 *sport
= ntohs(tcp
->source
);
221 *dport
= ntohs(tcp
->dest
);
230 #ifdef HAVE_NETINET_IP6_H
232 * Skip to the actual payload and parse the transport header.
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
)
239 bool fragment
= FALSE
;
241 *proto
= ip
->ip6_nxt
;
242 *payload
= chunk_skip(packet
, 40);
243 while (payload
->len
>= sizeof(struct ip6_ext
))
247 case 44: /* Fragment Header */
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 */
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));
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 */
268 !parse_transport_header(*payload
, *proto
, sport
, dport
))
278 #endif /* HAVE_NETINET_IP6_H */
281 * Described in header.
283 ip_packet_t
*ip_packet_create(chunk_t packet
)
285 private_ip_packet_t
*this;
286 uint8_t version
, next_header
;
287 uint16_t sport
= 0, dport
= 0;
293 DBG1(DBG_ESP
, "IP packet too short");
297 version
= (packet
.ptr
[0] & 0xf0) >> 4;
305 if (packet
.len
< sizeof(struct ip
))
307 DBG1(DBG_ESP
, "IPv4 packet too short");
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
))
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
;
326 #ifdef HAVE_NETINET_IP6_H
331 if (packet
.len
< sizeof(*ip
))
333 DBG1(DBG_ESP
, "IPv6 packet too short");
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
,
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
);
350 #endif /* HAVE_NETINET_IP6_H */
352 DBG1(DBG_ESP
, "unsupported IP version");
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
,
372 .next_header
= next_header
,
374 return &this->public;
382 * Calculate the checksum for the pseudo IP header
384 static uint16_t pseudo_header_checksum(host_t
*src
, host_t
*dst
,
385 uint8_t proto
, chunk_t payload
)
387 switch (src
->get_family(src
))
391 struct __attribute__((packed
)) {
399 .len
= htons(payload
.len
),
401 memcpy(&pseudo
.src
, src
->get_address(src
).ptr
,
403 memcpy(&pseudo
.dst
, dst
->get_address(dst
).ptr
,
405 return chunk_internet_checksum(chunk_from_thing(pseudo
));
409 struct __attribute__((packed
)) {
416 .next_header
= proto
,
417 .len
= htons(payload
.len
),
419 memcpy(&pseudo
.src
, src
->get_address(src
).ptr
,
421 memcpy(&pseudo
.dst
, dst
->get_address(dst
).ptr
,
423 return chunk_internet_checksum(chunk_from_thing(pseudo
));
430 * Apply transport ports and calculate header checksums
432 static void fix_transport_header(host_t
*src
, host_t
*dst
, uint8_t proto
,
435 uint16_t sum
= 0, sport
, dport
;
437 sport
= src
->get_port(src
);
438 dport
= dst
->get_port(dst
);
446 if (payload
.len
< sizeof(*udp
))
450 udp
= (struct udphdr
*)payload
.ptr
;
453 udp
->source
= htons(sport
);
457 udp
->dest
= htons(dport
);
460 sum
= pseudo_header_checksum(src
, dst
, proto
, payload
);
461 udp
->check
= chunk_internet_checksum_inc(payload
, sum
);
468 if (payload
.len
< sizeof(*tcp
))
472 tcp
= (struct tcphdr
*)payload
.ptr
;
475 tcp
->source
= htons(sport
);
479 tcp
->dest
= htons(dport
);
482 sum
= pseudo_header_checksum(src
, dst
, proto
, payload
);
483 tcp
->check
= chunk_internet_checksum_inc(payload
, sum
);
492 * Described in header.
494 ip_packet_t
*ip_packet_create_from_data(host_t
*src
, host_t
*dst
,
495 uint8_t next_header
, chunk_t data
)
500 family
= src
->get_family(src
);
501 if (family
!= dst
->get_family(dst
))
503 DBG1(DBG_ESP
, "address family does not match");
514 .ip_len
= htons(20 + data
.len
),
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
));
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
);
526 #ifdef HAVE_NETINET_IP6_H
529 struct ip6_hdr ip
= {
530 .ip6_flow
= htonl(6),
531 .ip6_plen
= htons(data
.len
),
532 .ip6_nxt
= next_header
,
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
));
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
);
542 #endif /* HAVE_NETINET_IP6_H */
544 DBG1(DBG_ESP
, "unsupported address family");
550 * Described in header.
552 ip_packet_t
*ip_packet_create_udp_from_data(host_t
*src
, host_t
*dst
,
555 struct udphdr udp
= {
556 .len
= htons(8 + data
.len
),
561 data
= chunk_cat("cc", chunk_from_thing(udp
), data
);
562 packet
= ip_packet_create_from_data(src
, dst
, IPPROTO_UDP
, data
);