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 #include <netinet/udp.h>
26 #include <netinet/tcp.h>
27 #ifdef HAVE_NETINET_IP6_H
28 #include <netinet/ip6.h>
31 typedef struct private_ip_packet_t private_ip_packet_t
;
34 * Private additions to ip_packet_t.
36 struct private_ip_packet_t
{
59 * IP payload (points into packet)
69 * Protocol|Next Header field
75 METHOD(ip_packet_t
, get_version
, u_int8_t
,
76 private_ip_packet_t
*this)
81 METHOD(ip_packet_t
, get_source
, host_t
*,
82 private_ip_packet_t
*this)
87 METHOD(ip_packet_t
, get_destination
, host_t
*,
88 private_ip_packet_t
*this)
93 METHOD(ip_packet_t
, get_encoding
, chunk_t
,
94 private_ip_packet_t
*this)
99 METHOD(ip_packet_t
, get_payload
, chunk_t
,
100 private_ip_packet_t
*this)
102 return this->payload
;
105 METHOD(ip_packet_t
, get_next_header
, u_int8_t
,
106 private_ip_packet_t
*this)
108 return this->next_header
;
111 METHOD(ip_packet_t
, clone_
, ip_packet_t
*,
112 private_ip_packet_t
*this)
114 return ip_packet_create(chunk_clone(this->packet
));
117 METHOD(ip_packet_t
, destroy
, void,
118 private_ip_packet_t
*this)
120 this->src
->destroy(this->src
);
121 this->dst
->destroy(this->dst
);
122 chunk_free(&this->packet
);
127 * Parse transport protocol header
129 static bool parse_transport_header(chunk_t packet
, u_int8_t proto
,
130 u_int16_t
*sport
, u_int16_t
*dport
)
138 if (packet
.len
< sizeof(*udp
))
140 DBG1(DBG_ESP
, "UDP packet too short");
143 udp
= (struct udphdr
*)packet
.ptr
;
144 *sport
= ntohs(udp
->source
);
145 *dport
= ntohs(udp
->dest
);
152 if (packet
.len
< sizeof(*tcp
))
154 DBG1(DBG_ESP
, "TCP packet too short");
157 tcp
= (struct tcphdr
*)packet
.ptr
;
158 *sport
= ntohs(tcp
->source
);
159 *dport
= ntohs(tcp
->dest
);
169 * Described in header.
171 ip_packet_t
*ip_packet_create(chunk_t packet
)
173 private_ip_packet_t
*this;
174 u_int8_t version
, next_header
;
175 u_int16_t sport
= 0, dport
= 0;
181 DBG1(DBG_ESP
, "IP packet too short");
185 version
= (packet
.ptr
[0] & 0xf0) >> 4;
193 if (packet
.len
< sizeof(struct ip
))
195 DBG1(DBG_ESP
, "IPv4 packet too short");
198 ip
= (struct ip
*)packet
.ptr
;
199 /* remove any RFC 4303 TFC extra padding */
200 packet
.len
= min(packet
.len
, untoh16(&ip
->ip_len
));
201 payload
= chunk_skip(packet
, ip
->ip_hl
* 4);
202 if (!parse_transport_header(payload
, ip
->ip_p
, &sport
, &dport
))
206 src
= host_create_from_chunk(AF_INET
,
207 chunk_from_thing(ip
->ip_src
), sport
);
208 dst
= host_create_from_chunk(AF_INET
,
209 chunk_from_thing(ip
->ip_dst
), dport
);
210 next_header
= ip
->ip_p
;
213 #ifdef HAVE_NETINET_IP6_H
218 if (packet
.len
< sizeof(*ip
))
220 DBG1(DBG_ESP
, "IPv6 packet too short");
223 ip
= (struct ip6_hdr
*)packet
.ptr
;
224 /* remove any RFC 4303 TFC extra padding */
225 packet
.len
= min(packet
.len
, untoh16(&ip
->ip6_plen
));
226 /* we only handle packets without extension headers, just skip the
227 * basic IPv6 header */
228 payload
= chunk_skip(packet
, 40);
229 if (!parse_transport_header(payload
, ip
->ip6_nxt
, &sport
, &dport
))
233 src
= host_create_from_chunk(AF_INET6
,
234 chunk_from_thing(ip
->ip6_src
), sport
);
235 dst
= host_create_from_chunk(AF_INET6
,
236 chunk_from_thing(ip
->ip6_dst
), dport
);
237 next_header
= ip
->ip6_nxt
;
240 #endif /* HAVE_NETINET_IP6_H */
242 DBG1(DBG_ESP
, "unsupported IP version");
248 .get_version
= _get_version
,
249 .get_source
= _get_source
,
250 .get_destination
= _get_destination
,
251 .get_next_header
= _get_next_header
,
252 .get_encoding
= _get_encoding
,
253 .get_payload
= _get_payload
,
262 .next_header
= next_header
,
264 return &this->public;
272 * Calculate the checksum for the pseudo IP header
274 static u_int16_t
pseudo_header_checksum(host_t
*src
, host_t
*dst
,
275 u_int8_t proto
, chunk_t payload
)
277 switch (src
->get_family(src
))
281 struct __attribute__((packed
)) {
289 .len
= htons(payload
.len
),
291 memcpy(&pseudo
.src
, src
->get_address(src
).ptr
,
293 memcpy(&pseudo
.dst
, dst
->get_address(dst
).ptr
,
295 return chunk_internet_checksum(chunk_from_thing(pseudo
));
299 struct __attribute__((packed
)) {
306 .next_header
= proto
,
307 .len
= htons(payload
.len
),
309 memcpy(&pseudo
.src
, src
->get_address(src
).ptr
,
311 memcpy(&pseudo
.dst
, dst
->get_address(dst
).ptr
,
313 return chunk_internet_checksum(chunk_from_thing(pseudo
));
320 * Apply transport ports and calculate header checksums
322 static void fix_transport_header(host_t
*src
, host_t
*dst
, u_int8_t proto
,
325 u_int16_t sum
= 0, sport
, dport
;
327 sport
= src
->get_port(src
);
328 dport
= dst
->get_port(dst
);
336 if (payload
.len
< sizeof(*udp
))
340 udp
= (struct udphdr
*)payload
.ptr
;
343 udp
->source
= htons(sport
);
347 udp
->dest
= htons(dport
);
350 sum
= pseudo_header_checksum(src
, dst
, proto
, payload
);
351 udp
->check
= chunk_internet_checksum_inc(payload
, sum
);
358 if (payload
.len
< sizeof(*tcp
))
362 tcp
= (struct tcphdr
*)payload
.ptr
;
365 tcp
->source
= htons(sport
);
369 tcp
->dest
= htons(dport
);
372 sum
= pseudo_header_checksum(src
, dst
, proto
, payload
);
373 tcp
->check
= chunk_internet_checksum_inc(payload
, sum
);
382 * Described in header.
384 ip_packet_t
*ip_packet_create_from_data(host_t
*src
, host_t
*dst
,
385 u_int8_t next_header
, chunk_t data
)
390 family
= src
->get_family(src
);
391 if (family
!= dst
->get_family(dst
))
393 DBG1(DBG_ESP
, "address family does not match");
404 .ip_len
= htons(20 + data
.len
),
408 memcpy(&ip
.ip_src
, src
->get_address(src
).ptr
, sizeof(ip
.ip_src
));
409 memcpy(&ip
.ip_dst
, dst
->get_address(dst
).ptr
, sizeof(ip
.ip_dst
));
410 ip
.ip_sum
= chunk_internet_checksum(chunk_from_thing(ip
));
412 packet
= chunk_cat("cc", chunk_from_thing(ip
), data
);
413 fix_transport_header(src
, dst
, next_header
, chunk_skip(packet
, 20));
414 return ip_packet_create(packet
);
416 #ifdef HAVE_NETINET_IP6_H
419 struct ip6_hdr ip
= {
420 .ip6_flow
= htonl(6),
421 .ip6_plen
= htons(40 + data
.len
),
422 .ip6_nxt
= next_header
,
425 memcpy(&ip
.ip6_src
, src
->get_address(src
).ptr
, sizeof(ip
.ip6_src
));
426 memcpy(&ip
.ip6_dst
, dst
->get_address(dst
).ptr
, sizeof(ip
.ip6_dst
));
428 packet
= chunk_cat("cc", chunk_from_thing(ip
), data
);
429 fix_transport_header(src
, dst
, next_header
, chunk_skip(packet
, 40));
430 return ip_packet_create(packet
);
432 #endif /* HAVE_NETINET_IP6_H */
434 DBG1(DBG_ESP
, "unsupported address family");
440 * Described in header.
442 ip_packet_t
*ip_packet_create_udp_from_data(host_t
*src
, host_t
*dst
,
445 struct udphdr udp
= {
446 .len
= htons(8 + data
.len
),
451 data
= chunk_cat("cc", chunk_from_thing(udp
), data
);
452 packet
= ip_packet_create_from_data(src
, dst
, IPPROTO_UDP
, data
);