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>
23 #include <netinet/in.h>
24 #include <netinet/ip.h>
25 #ifdef HAVE_NETINET_IP6_H
26 #include <netinet/ip6.h>
30 * TCP header, defined here because platforms disagree regarding member names
31 * and unfortunately Android does not define a variant with BSD names.
42 } __attribute__((packed
));
45 * UDP header, similar to the TCP header the system headers disagree on member
46 * names. Linux uses a union and on Android we could define __FAVOR_BSD to get
47 * the BSD member names, but this is simpler and more consistent with the above.
54 } __attribute__((packed
));
56 typedef struct private_ip_packet_t private_ip_packet_t
;
59 * Private additions to ip_packet_t.
61 struct private_ip_packet_t
{
84 * IP payload (points into packet)
94 * Protocol|Next Header field
100 METHOD(ip_packet_t
, get_version
, u_int8_t
,
101 private_ip_packet_t
*this)
103 return this->version
;
106 METHOD(ip_packet_t
, get_source
, host_t
*,
107 private_ip_packet_t
*this)
112 METHOD(ip_packet_t
, get_destination
, host_t
*,
113 private_ip_packet_t
*this)
118 METHOD(ip_packet_t
, get_encoding
, chunk_t
,
119 private_ip_packet_t
*this)
124 METHOD(ip_packet_t
, get_payload
, chunk_t
,
125 private_ip_packet_t
*this)
127 return this->payload
;
130 METHOD(ip_packet_t
, get_next_header
, u_int8_t
,
131 private_ip_packet_t
*this)
133 return this->next_header
;
136 METHOD(ip_packet_t
, clone_
, ip_packet_t
*,
137 private_ip_packet_t
*this)
139 return ip_packet_create(chunk_clone(this->packet
));
142 METHOD(ip_packet_t
, destroy
, void,
143 private_ip_packet_t
*this)
145 this->src
->destroy(this->src
);
146 this->dst
->destroy(this->dst
);
147 chunk_free(&this->packet
);
152 * Parse transport protocol header
154 static bool parse_transport_header(chunk_t packet
, u_int8_t proto
,
155 u_int16_t
*sport
, u_int16_t
*dport
)
163 if (packet
.len
< sizeof(*udp
))
165 DBG1(DBG_ESP
, "UDP packet too short");
168 udp
= (struct udphdr
*)packet
.ptr
;
169 *sport
= ntohs(udp
->source
);
170 *dport
= ntohs(udp
->dest
);
177 if (packet
.len
< sizeof(*tcp
))
179 DBG1(DBG_ESP
, "TCP packet too short");
182 tcp
= (struct tcphdr
*)packet
.ptr
;
183 *sport
= ntohs(tcp
->source
);
184 *dport
= ntohs(tcp
->dest
);
194 * Described in header.
196 ip_packet_t
*ip_packet_create(chunk_t packet
)
198 private_ip_packet_t
*this;
199 u_int8_t version
, next_header
;
200 u_int16_t sport
= 0, dport
= 0;
206 DBG1(DBG_ESP
, "IP packet too short");
210 version
= (packet
.ptr
[0] & 0xf0) >> 4;
218 if (packet
.len
< sizeof(struct ip
))
220 DBG1(DBG_ESP
, "IPv4 packet too short");
223 ip
= (struct ip
*)packet
.ptr
;
224 /* remove any RFC 4303 TFC extra padding */
225 packet
.len
= min(packet
.len
, untoh16(&ip
->ip_len
));
226 payload
= chunk_skip(packet
, ip
->ip_hl
* 4);
227 if (!parse_transport_header(payload
, ip
->ip_p
, &sport
, &dport
))
231 src
= host_create_from_chunk(AF_INET
,
232 chunk_from_thing(ip
->ip_src
), sport
);
233 dst
= host_create_from_chunk(AF_INET
,
234 chunk_from_thing(ip
->ip_dst
), dport
);
235 next_header
= ip
->ip_p
;
238 #ifdef HAVE_NETINET_IP6_H
243 if (packet
.len
< sizeof(*ip
))
245 DBG1(DBG_ESP
, "IPv6 packet too short");
248 ip
= (struct ip6_hdr
*)packet
.ptr
;
249 /* remove any RFC 4303 TFC extra padding */
250 packet
.len
= min(packet
.len
, 40 + untoh16(&ip
->ip6_plen
));
251 /* we only handle packets without extension headers, just skip the
252 * basic IPv6 header */
253 payload
= chunk_skip(packet
, 40);
254 if (!parse_transport_header(payload
, ip
->ip6_nxt
, &sport
, &dport
))
258 src
= host_create_from_chunk(AF_INET6
,
259 chunk_from_thing(ip
->ip6_src
), sport
);
260 dst
= host_create_from_chunk(AF_INET6
,
261 chunk_from_thing(ip
->ip6_dst
), dport
);
262 next_header
= ip
->ip6_nxt
;
265 #endif /* HAVE_NETINET_IP6_H */
267 DBG1(DBG_ESP
, "unsupported IP version");
273 .get_version
= _get_version
,
274 .get_source
= _get_source
,
275 .get_destination
= _get_destination
,
276 .get_next_header
= _get_next_header
,
277 .get_encoding
= _get_encoding
,
278 .get_payload
= _get_payload
,
287 .next_header
= next_header
,
289 return &this->public;
297 * Calculate the checksum for the pseudo IP header
299 static u_int16_t
pseudo_header_checksum(host_t
*src
, host_t
*dst
,
300 u_int8_t proto
, chunk_t payload
)
302 switch (src
->get_family(src
))
306 struct __attribute__((packed
)) {
314 .len
= htons(payload
.len
),
316 memcpy(&pseudo
.src
, src
->get_address(src
).ptr
,
318 memcpy(&pseudo
.dst
, dst
->get_address(dst
).ptr
,
320 return chunk_internet_checksum(chunk_from_thing(pseudo
));
324 struct __attribute__((packed
)) {
331 .next_header
= proto
,
332 .len
= htons(payload
.len
),
334 memcpy(&pseudo
.src
, src
->get_address(src
).ptr
,
336 memcpy(&pseudo
.dst
, dst
->get_address(dst
).ptr
,
338 return chunk_internet_checksum(chunk_from_thing(pseudo
));
345 * Apply transport ports and calculate header checksums
347 static void fix_transport_header(host_t
*src
, host_t
*dst
, u_int8_t proto
,
350 u_int16_t sum
= 0, sport
, dport
;
352 sport
= src
->get_port(src
);
353 dport
= dst
->get_port(dst
);
361 if (payload
.len
< sizeof(*udp
))
365 udp
= (struct udphdr
*)payload
.ptr
;
368 udp
->source
= htons(sport
);
372 udp
->dest
= htons(dport
);
375 sum
= pseudo_header_checksum(src
, dst
, proto
, payload
);
376 udp
->check
= chunk_internet_checksum_inc(payload
, sum
);
383 if (payload
.len
< sizeof(*tcp
))
387 tcp
= (struct tcphdr
*)payload
.ptr
;
390 tcp
->source
= htons(sport
);
394 tcp
->dest
= htons(dport
);
397 sum
= pseudo_header_checksum(src
, dst
, proto
, payload
);
398 tcp
->check
= chunk_internet_checksum_inc(payload
, sum
);
407 * Described in header.
409 ip_packet_t
*ip_packet_create_from_data(host_t
*src
, host_t
*dst
,
410 u_int8_t next_header
, chunk_t data
)
415 family
= src
->get_family(src
);
416 if (family
!= dst
->get_family(dst
))
418 DBG1(DBG_ESP
, "address family does not match");
429 .ip_len
= htons(20 + data
.len
),
433 memcpy(&ip
.ip_src
, src
->get_address(src
).ptr
, sizeof(ip
.ip_src
));
434 memcpy(&ip
.ip_dst
, dst
->get_address(dst
).ptr
, sizeof(ip
.ip_dst
));
435 ip
.ip_sum
= chunk_internet_checksum(chunk_from_thing(ip
));
437 packet
= chunk_cat("cc", chunk_from_thing(ip
), data
);
438 fix_transport_header(src
, dst
, next_header
, chunk_skip(packet
, 20));
439 return ip_packet_create(packet
);
441 #ifdef HAVE_NETINET_IP6_H
444 struct ip6_hdr ip
= {
445 .ip6_flow
= htonl(6),
446 .ip6_plen
= htons(40 + data
.len
),
447 .ip6_nxt
= next_header
,
450 memcpy(&ip
.ip6_src
, src
->get_address(src
).ptr
, sizeof(ip
.ip6_src
));
451 memcpy(&ip
.ip6_dst
, dst
->get_address(dst
).ptr
, sizeof(ip
.ip6_dst
));
453 packet
= chunk_cat("cc", chunk_from_thing(ip
), data
);
454 fix_transport_header(src
, dst
, next_header
, chunk_skip(packet
, 40));
455 return ip_packet_create(packet
);
457 #endif /* HAVE_NETINET_IP6_H */
459 DBG1(DBG_ESP
, "unsupported address family");
465 * Described in header.
467 ip_packet_t
*ip_packet_create_udp_from_data(host_t
*src
, host_t
*dst
,
470 struct udphdr udp
= {
471 .len
= htons(8 + data
.len
),
476 data
= chunk_cat("cc", chunk_from_thing(udp
), data
);
477 packet
= ip_packet_create_from_data(src
, dst
, IPPROTO_UDP
, data
);