]> git.ipfire.org Git - thirdparty/strongswan.git/blob - src/libipsec/ip_packet.c
Use standard unsigned integer types
[thirdparty/strongswan.git] / src / libipsec / ip_packet.c
1 /*
2 * Copyright (C) 2012-2014 Tobias Brunner
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>
20 #include <utils/debug.h>
21
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>
27 #endif
28
29 /**
30 * TCP header, defined here because platforms disagree regarding member names
31 * and unfortunately Android does not define a variant with BSD names.
32 */
33 struct tcphdr {
34 uint16_t source;
35 uint16_t dest;
36 uint32_t seq;
37 uint32_t ack_seq;
38 uint16_t flags;
39 uint16_t window;
40 uint16_t check;
41 uint16_t urg_ptr;
42 } __attribute__((packed));
43
44 /**
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.
48 */
49 struct udphdr {
50 uint16_t source;
51 uint16_t dest;
52 uint16_t len;
53 uint16_t check;
54 } __attribute__((packed));
55
56 typedef struct private_ip_packet_t private_ip_packet_t;
57
58 /**
59 * Private additions to ip_packet_t.
60 */
61 struct private_ip_packet_t {
62
63 /**
64 * Public members
65 */
66 ip_packet_t public;
67
68 /**
69 * Source address
70 */
71 host_t *src;
72
73 /**
74 * Destination address
75 */
76 host_t *dst;
77
78 /**
79 * IP packet
80 */
81 chunk_t packet;
82
83 /**
84 * IP payload (points into packet)
85 */
86 chunk_t payload;
87
88 /**
89 * IP version
90 */
91 uint8_t version;
92
93 /**
94 * Protocol|Next Header field
95 */
96 uint8_t next_header;
97
98 };
99
100 METHOD(ip_packet_t, get_version, uint8_t,
101 private_ip_packet_t *this)
102 {
103 return this->version;
104 }
105
106 METHOD(ip_packet_t, get_source, host_t*,
107 private_ip_packet_t *this)
108 {
109 return this->src;
110 }
111
112 METHOD(ip_packet_t, get_destination, host_t*,
113 private_ip_packet_t *this)
114 {
115 return this->dst;
116 }
117
118 METHOD(ip_packet_t, get_encoding, chunk_t,
119 private_ip_packet_t *this)
120 {
121 return this->packet;
122 }
123
124 METHOD(ip_packet_t, get_payload, chunk_t,
125 private_ip_packet_t *this)
126 {
127 return this->payload;
128 }
129
130 METHOD(ip_packet_t, get_next_header, uint8_t,
131 private_ip_packet_t *this)
132 {
133 return this->next_header;
134 }
135
136 METHOD(ip_packet_t, clone_, ip_packet_t*,
137 private_ip_packet_t *this)
138 {
139 return ip_packet_create(chunk_clone(this->packet));
140 }
141
142 METHOD(ip_packet_t, destroy, void,
143 private_ip_packet_t *this)
144 {
145 this->src->destroy(this->src);
146 this->dst->destroy(this->dst);
147 chunk_free(&this->packet);
148 free(this);
149 }
150
151 /**
152 * Parse transport protocol header
153 */
154 static bool parse_transport_header(chunk_t packet, uint8_t proto,
155 uint16_t *sport, uint16_t *dport)
156 {
157 switch (proto)
158 {
159 case IPPROTO_UDP:
160 {
161 struct udphdr *udp;
162
163 if (packet.len < sizeof(*udp))
164 {
165 DBG1(DBG_ESP, "UDP packet too short");
166 return FALSE;
167 }
168 udp = (struct udphdr*)packet.ptr;
169 *sport = ntohs(udp->source);
170 *dport = ntohs(udp->dest);
171 break;
172 }
173 case IPPROTO_TCP:
174 {
175 struct tcphdr *tcp;
176
177 if (packet.len < sizeof(*tcp))
178 {
179 DBG1(DBG_ESP, "TCP packet too short");
180 return FALSE;
181 }
182 tcp = (struct tcphdr*)packet.ptr;
183 *sport = ntohs(tcp->source);
184 *dport = ntohs(tcp->dest);
185 break;
186 }
187 default:
188 break;
189 }
190 return TRUE;
191 }
192
193 /**
194 * Described in header.
195 */
196 ip_packet_t *ip_packet_create(chunk_t packet)
197 {
198 private_ip_packet_t *this;
199 uint8_t version, next_header;
200 uint16_t sport = 0, dport = 0;
201 host_t *src, *dst;
202 chunk_t payload;
203
204 if (packet.len < 1)
205 {
206 DBG1(DBG_ESP, "IP packet too short");
207 goto failed;
208 }
209
210 version = (packet.ptr[0] & 0xf0) >> 4;
211
212 switch (version)
213 {
214 case 4:
215 {
216 struct ip *ip;
217
218 if (packet.len < sizeof(struct ip))
219 {
220 DBG1(DBG_ESP, "IPv4 packet too short");
221 goto failed;
222 }
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))
228 {
229 goto failed;
230 }
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;
236 break;
237 }
238 #ifdef HAVE_NETINET_IP6_H
239 case 6:
240 {
241 struct ip6_hdr *ip;
242
243 if (packet.len < sizeof(*ip))
244 {
245 DBG1(DBG_ESP, "IPv6 packet too short");
246 goto failed;
247 }
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))
255 {
256 goto failed;
257 }
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;
263 break;
264 }
265 #endif /* HAVE_NETINET_IP6_H */
266 default:
267 DBG1(DBG_ESP, "unsupported IP version");
268 goto failed;
269 }
270
271 INIT(this,
272 .public = {
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,
279 .clone = _clone_,
280 .destroy = _destroy,
281 },
282 .src = src,
283 .dst = dst,
284 .packet = packet,
285 .payload = payload,
286 .version = version,
287 .next_header = next_header,
288 );
289 return &this->public;
290
291 failed:
292 chunk_free(&packet);
293 return NULL;
294 }
295
296 /**
297 * Calculate the checksum for the pseudo IP header
298 */
299 static uint16_t pseudo_header_checksum(host_t *src, host_t *dst,
300 uint8_t proto, chunk_t payload)
301 {
302 switch (src->get_family(src))
303 {
304 case AF_INET:
305 {
306 struct __attribute__((packed)) {
307 uint32_t src;
308 uint32_t dst;
309 u_char zero;
310 u_char proto;
311 uint16_t len;
312 } pseudo = {
313 .proto = proto,
314 .len = htons(payload.len),
315 };
316 memcpy(&pseudo.src, src->get_address(src).ptr,
317 sizeof(pseudo.src));
318 memcpy(&pseudo.dst, dst->get_address(dst).ptr,
319 sizeof(pseudo.dst));
320 return chunk_internet_checksum(chunk_from_thing(pseudo));
321 }
322 case AF_INET6:
323 {
324 struct __attribute__((packed)) {
325 u_char src[16];
326 u_char dst[16];
327 uint32_t len;
328 u_char zero[3];
329 u_char next_header;
330 } pseudo = {
331 .next_header = proto,
332 .len = htons(payload.len),
333 };
334 memcpy(&pseudo.src, src->get_address(src).ptr,
335 sizeof(pseudo.src));
336 memcpy(&pseudo.dst, dst->get_address(dst).ptr,
337 sizeof(pseudo.dst));
338 return chunk_internet_checksum(chunk_from_thing(pseudo));
339 }
340 }
341 return 0xffff;
342 }
343
344 /**
345 * Apply transport ports and calculate header checksums
346 */
347 static void fix_transport_header(host_t *src, host_t *dst, uint8_t proto,
348 chunk_t payload)
349 {
350 uint16_t sum = 0, sport, dport;
351
352 sport = src->get_port(src);
353 dport = dst->get_port(dst);
354
355 switch (proto)
356 {
357 case IPPROTO_UDP:
358 {
359 struct udphdr *udp;
360
361 if (payload.len < sizeof(*udp))
362 {
363 return;
364 }
365 udp = (struct udphdr*)payload.ptr;
366 if (sport != 0)
367 {
368 udp->source = htons(sport);
369 }
370 if (dport != 0)
371 {
372 udp->dest = htons(dport);
373 }
374 udp->check = 0;
375 sum = pseudo_header_checksum(src, dst, proto, payload);
376 udp->check = chunk_internet_checksum_inc(payload, sum);
377 break;
378 }
379 case IPPROTO_TCP:
380 {
381 struct tcphdr *tcp;
382
383 if (payload.len < sizeof(*tcp))
384 {
385 return;
386 }
387 tcp = (struct tcphdr*)payload.ptr;
388 if (sport != 0)
389 {
390 tcp->source = htons(sport);
391 }
392 if (dport != 0)
393 {
394 tcp->dest = htons(dport);
395 }
396 tcp->check = 0;
397 sum = pseudo_header_checksum(src, dst, proto, payload);
398 tcp->check = chunk_internet_checksum_inc(payload, sum);
399 break;
400 }
401 default:
402 break;
403 }
404 }
405
406 /**
407 * Described in header.
408 */
409 ip_packet_t *ip_packet_create_from_data(host_t *src, host_t *dst,
410 uint8_t next_header, chunk_t data)
411 {
412 chunk_t packet;
413 int family;
414
415 family = src->get_family(src);
416 if (family != dst->get_family(dst))
417 {
418 DBG1(DBG_ESP, "address family does not match");
419 return NULL;
420 }
421
422 switch (family)
423 {
424 case AF_INET:
425 {
426 struct ip ip = {
427 .ip_v = 4,
428 .ip_hl = 5,
429 .ip_len = htons(20 + data.len),
430 .ip_ttl = 0x80,
431 .ip_p = next_header,
432 };
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));
436
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);
440 }
441 #ifdef HAVE_NETINET_IP6_H
442 case AF_INET6:
443 {
444 struct ip6_hdr ip = {
445 .ip6_flow = htonl(6),
446 .ip6_plen = htons(data.len),
447 .ip6_nxt = next_header,
448 .ip6_hlim = 0x80,
449 };
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));
452
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);
456 }
457 #endif /* HAVE_NETINET_IP6_H */
458 default:
459 DBG1(DBG_ESP, "unsupported address family");
460 return NULL;
461 }
462 }
463
464 /**
465 * Described in header.
466 */
467 ip_packet_t *ip_packet_create_udp_from_data(host_t *src, host_t *dst,
468 chunk_t data)
469 {
470 struct udphdr udp = {
471 .len = htons(8 + data.len),
472 .check = 0,
473 };
474 ip_packet_t *packet;
475
476 data = chunk_cat("cc", chunk_from_thing(udp), data);
477 packet = ip_packet_create_from_data(src, dst, IPPROTO_UDP, data);
478 chunk_free(&data);
479 return packet;
480 }