Annotation of embedaddon/strongswan/src/libipsec/ip_packet.c, revision 1.1.1.1
1.1 misho 1: /*
2: * Copyright (C) 2012-2014 Tobias Brunner
3: * HSR 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:
24: #ifndef WIN32
25: #include <netinet/in.h>
26: #include <netinet/ip.h>
27: #ifdef HAVE_NETINET_IP6_H
28: #include <netinet/ip6.h>
29: #endif
30: #else
31: struct ip {
32: #if BYTE_ORDER == LITTLE_ENDIAN
33: uint8_t ip_hl: 4;
34: uint8_t ip_v: 4;
35: #elif BYTE_ORDER == BIG_ENDIAN
36: uint8_t ip_v: 4;
37: uint8_t ip_hl: 4;
38: #endif
39: uint8_t ip_tos;
40: uint16_t ip_len;
41: uint16_t ip_id;
42: uint16_t ip_off;
43: uint8_t ip_ttl;
44: uint8_t ip_p;
45: uint16_t ip_sum;
46: struct in_addr ip_src, ip_dst;
47: } __attribute__((packed));
48: struct ip6_hdr {
49: uint32_t ip6_flow; /* 4 bit version, 8 bit TC, 20 bit flow label */
50: uint16_t ip6_plen;
51: uint8_t ip6_nxt;
52: uint8_t ip6_hlim;
53: struct in6_addr ip6_src, ip6_dst;
54: } __attribute__((packed));
55: struct ip6_ext {
56: uint8_t ip6e_nxt;
57: uint8_t ip6e_len;
58: } __attribute__((packed));
59: #define HAVE_NETINET_IP6_H /* not really, but we only need the structs above */
60: #endif
61:
62: #ifndef IP_OFFMASK
63: #define IP_OFFMASK 0x1fff
64: #endif
65:
66: /**
67: * TCP header, defined here because platforms disagree regarding member names
68: * and unfortunately Android does not define a variant with BSD names.
69: */
70: struct tcphdr {
71: uint16_t source;
72: uint16_t dest;
73: uint32_t seq;
74: uint32_t ack_seq;
75: uint16_t flags;
76: uint16_t window;
77: uint16_t check;
78: uint16_t urg_ptr;
79: } __attribute__((packed));
80:
81: /**
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.
85: */
86: struct udphdr {
87: uint16_t source;
88: uint16_t dest;
89: uint16_t len;
90: uint16_t check;
91: } __attribute__((packed));
92:
93: typedef struct private_ip_packet_t private_ip_packet_t;
94:
95: /**
96: * Private additions to ip_packet_t.
97: */
98: struct private_ip_packet_t {
99:
100: /**
101: * Public members
102: */
103: ip_packet_t public;
104:
105: /**
106: * Source address
107: */
108: host_t *src;
109:
110: /**
111: * Destination address
112: */
113: host_t *dst;
114:
115: /**
116: * IP packet
117: */
118: chunk_t packet;
119:
120: /**
121: * IP payload (points into packet)
122: */
123: chunk_t payload;
124:
125: /**
126: * IP version
127: */
128: uint8_t version;
129:
130: /**
131: * Protocol|Next Header field
132: */
133: uint8_t next_header;
134:
135: };
136:
137: METHOD(ip_packet_t, get_version, uint8_t,
138: private_ip_packet_t *this)
139: {
140: return this->version;
141: }
142:
143: METHOD(ip_packet_t, get_source, host_t*,
144: private_ip_packet_t *this)
145: {
146: return this->src;
147: }
148:
149: METHOD(ip_packet_t, get_destination, host_t*,
150: private_ip_packet_t *this)
151: {
152: return this->dst;
153: }
154:
155: METHOD(ip_packet_t, get_encoding, chunk_t,
156: private_ip_packet_t *this)
157: {
158: return this->packet;
159: }
160:
161: METHOD(ip_packet_t, get_payload, chunk_t,
162: private_ip_packet_t *this)
163: {
164: return this->payload;
165: }
166:
167: METHOD(ip_packet_t, get_next_header, uint8_t,
168: private_ip_packet_t *this)
169: {
170: return this->next_header;
171: }
172:
173: METHOD(ip_packet_t, clone_, ip_packet_t*,
174: private_ip_packet_t *this)
175: {
176: return ip_packet_create(chunk_clone(this->packet));
177: }
178:
179: METHOD(ip_packet_t, destroy, void,
180: private_ip_packet_t *this)
181: {
182: this->src->destroy(this->src);
183: this->dst->destroy(this->dst);
184: chunk_free(&this->packet);
185: free(this);
186: }
187:
188: /**
189: * Parse transport protocol header
190: */
191: static bool parse_transport_header(chunk_t packet, uint8_t proto,
192: uint16_t *sport, uint16_t *dport)
193: {
194: switch (proto)
195: {
196: case IPPROTO_UDP:
197: {
198: struct udphdr *udp;
199:
200: if (packet.len < sizeof(*udp))
201: {
202: DBG1(DBG_ESP, "UDP packet too short");
203: return FALSE;
204: }
205: udp = (struct udphdr*)packet.ptr;
206: *sport = ntohs(udp->source);
207: *dport = ntohs(udp->dest);
208: break;
209: }
210: case IPPROTO_TCP:
211: {
212: struct tcphdr *tcp;
213:
214: if (packet.len < sizeof(*tcp))
215: {
216: DBG1(DBG_ESP, "TCP packet too short");
217: return FALSE;
218: }
219: tcp = (struct tcphdr*)packet.ptr;
220: *sport = ntohs(tcp->source);
221: *dport = ntohs(tcp->dest);
222: break;
223: }
224: default:
225: break;
226: }
227: return TRUE;
228: }
229:
230: #ifdef HAVE_NETINET_IP6_H
231: /**
232: * Skip to the actual payload and parse the transport header.
233: */
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)
237: {
238: struct ip6_ext *ext;
239: bool fragment = FALSE;
240:
241: *proto = ip->ip6_nxt;
242: *payload = chunk_skip(packet, 40);
243: while (payload->len >= sizeof(struct ip6_ext))
244: {
245: switch (*proto)
246: {
247: case 44: /* Fragment Header */
248: fragment = TRUE;
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 */
254: case 139: /* HIP */
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));
260: continue;
261: default:
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 */
267: if (!fragment &&
268: !parse_transport_header(*payload, *proto, sport, dport))
269: {
270: return FALSE;
271: }
272: break;
273: }
274: break;
275: }
276: return TRUE;
277: }
278: #endif /* HAVE_NETINET_IP6_H */
279:
280: /**
281: * Described in header.
282: */
283: ip_packet_t *ip_packet_create(chunk_t packet)
284: {
285: private_ip_packet_t *this;
286: uint8_t version, next_header;
287: uint16_t sport = 0, dport = 0;
288: host_t *src, *dst;
289: chunk_t payload;
290:
291: if (packet.len < 1)
292: {
293: DBG1(DBG_ESP, "IP packet too short");
294: goto failed;
295: }
296:
297: version = (packet.ptr[0] & 0xf0) >> 4;
298:
299: switch (version)
300: {
301: case 4:
302: {
303: struct ip *ip;
304:
305: if (packet.len < sizeof(struct ip))
306: {
307: DBG1(DBG_ESP, "IPv4 packet too short");
308: goto failed;
309: }
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))
316: {
317: goto failed;
318: }
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;
324: break;
325: }
326: #ifdef HAVE_NETINET_IP6_H
327: case 6:
328: {
329: struct ip6_hdr *ip;
330:
331: if (packet.len < sizeof(*ip))
332: {
333: DBG1(DBG_ESP, "IPv6 packet too short");
334: goto failed;
335: }
336: ip = (struct ip6_hdr*)packet.ptr;
337: /* remove any RFC 4303 TFC extra padding */
338: packet.len = min(packet.len, 40 + untoh16((void*)&ip->ip6_plen));
339: if (!parse_transport_header_v6(ip, packet, &payload, &next_header,
340: &sport, &dport))
341: {
342: goto failed;
343: }
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);
348: break;
349: }
350: #endif /* HAVE_NETINET_IP6_H */
351: default:
352: DBG1(DBG_ESP, "unsupported IP version");
353: goto failed;
354: }
355:
356: INIT(this,
357: .public = {
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,
364: .clone = _clone_,
365: .destroy = _destroy,
366: },
367: .src = src,
368: .dst = dst,
369: .packet = packet,
370: .payload = payload,
371: .version = version,
372: .next_header = next_header,
373: );
374: return &this->public;
375:
376: failed:
377: chunk_free(&packet);
378: return NULL;
379: }
380:
381: /**
382: * Calculate the checksum for the pseudo IP header
383: */
384: static uint16_t pseudo_header_checksum(host_t *src, host_t *dst,
385: uint8_t proto, chunk_t payload)
386: {
387: switch (src->get_family(src))
388: {
389: case AF_INET:
390: {
391: struct __attribute__((packed)) {
392: uint32_t src;
393: uint32_t dst;
394: u_char zero;
395: u_char proto;
396: uint16_t len;
397: } pseudo = {
398: .proto = proto,
399: .len = htons(payload.len),
400: };
401: memcpy(&pseudo.src, src->get_address(src).ptr,
402: sizeof(pseudo.src));
403: memcpy(&pseudo.dst, dst->get_address(dst).ptr,
404: sizeof(pseudo.dst));
405: return chunk_internet_checksum(chunk_from_thing(pseudo));
406: }
407: case AF_INET6:
408: {
409: struct __attribute__((packed)) {
410: u_char src[16];
411: u_char dst[16];
412: uint32_t len;
413: u_char zero[3];
414: u_char next_header;
415: } pseudo = {
416: .next_header = proto,
417: .len = htons(payload.len),
418: };
419: memcpy(&pseudo.src, src->get_address(src).ptr,
420: sizeof(pseudo.src));
421: memcpy(&pseudo.dst, dst->get_address(dst).ptr,
422: sizeof(pseudo.dst));
423: return chunk_internet_checksum(chunk_from_thing(pseudo));
424: }
425: }
426: return 0xffff;
427: }
428:
429: /**
430: * Apply transport ports and calculate header checksums
431: */
432: static void fix_transport_header(host_t *src, host_t *dst, uint8_t proto,
433: chunk_t payload)
434: {
435: uint16_t sum = 0, sport, dport;
436:
437: sport = src->get_port(src);
438: dport = dst->get_port(dst);
439:
440: switch (proto)
441: {
442: case IPPROTO_UDP:
443: {
444: struct udphdr *udp;
445:
446: if (payload.len < sizeof(*udp))
447: {
448: return;
449: }
450: udp = (struct udphdr*)payload.ptr;
451: if (sport != 0)
452: {
453: udp->source = htons(sport);
454: }
455: if (dport != 0)
456: {
457: udp->dest = htons(dport);
458: }
459: udp->check = 0;
460: sum = pseudo_header_checksum(src, dst, proto, payload);
461: udp->check = chunk_internet_checksum_inc(payload, sum);
462: break;
463: }
464: case IPPROTO_TCP:
465: {
466: struct tcphdr *tcp;
467:
468: if (payload.len < sizeof(*tcp))
469: {
470: return;
471: }
472: tcp = (struct tcphdr*)payload.ptr;
473: if (sport != 0)
474: {
475: tcp->source = htons(sport);
476: }
477: if (dport != 0)
478: {
479: tcp->dest = htons(dport);
480: }
481: tcp->check = 0;
482: sum = pseudo_header_checksum(src, dst, proto, payload);
483: tcp->check = chunk_internet_checksum_inc(payload, sum);
484: break;
485: }
486: default:
487: break;
488: }
489: }
490:
491: /**
492: * Described in header.
493: */
494: ip_packet_t *ip_packet_create_from_data(host_t *src, host_t *dst,
495: uint8_t next_header, chunk_t data)
496: {
497: chunk_t packet;
498: int family;
499:
500: family = src->get_family(src);
501: if (family != dst->get_family(dst))
502: {
503: DBG1(DBG_ESP, "address family does not match");
504: return NULL;
505: }
506:
507: switch (family)
508: {
509: case AF_INET:
510: {
511: struct ip ip = {
512: .ip_v = 4,
513: .ip_hl = 5,
514: .ip_len = htons(20 + data.len),
515: .ip_ttl = 0x80,
516: .ip_p = next_header,
517: };
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));
521:
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);
525: }
526: #ifdef HAVE_NETINET_IP6_H
527: case AF_INET6:
528: {
529: struct ip6_hdr ip = {
530: .ip6_flow = htonl(6 << 28),
531: .ip6_plen = htons(data.len),
532: .ip6_nxt = next_header,
533: .ip6_hlim = 0x80,
534: };
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));
537:
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);
541: }
542: #endif /* HAVE_NETINET_IP6_H */
543: default:
544: DBG1(DBG_ESP, "unsupported address family");
545: return NULL;
546: }
547: }
548:
549: /**
550: * Described in header.
551: */
552: ip_packet_t *ip_packet_create_udp_from_data(host_t *src, host_t *dst,
553: chunk_t data)
554: {
555: struct udphdr udp = {
556: .len = htons(8 + data.len),
557: .check = 0,
558: };
559: ip_packet_t *packet;
560:
561: data = chunk_cat("cc", chunk_from_thing(udp), data);
562: packet = ip_packet_create_from_data(src, dst, IPPROTO_UDP, data);
563: chunk_free(&data);
564: return packet;
565: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>