4ef8fd5b8eeacfe7b39b559c94c56d5d669f19a4
[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 #include <netinet/udp.h>
26 #include <netinet/tcp.h>
27 #ifdef HAVE_NETINET_IP6_H
28 #include <netinet/ip6.h>
29 #endif
30
31 typedef struct private_ip_packet_t private_ip_packet_t;
32
33 /**
34 * Private additions to ip_packet_t.
35 */
36 struct private_ip_packet_t {
37
38 /**
39 * Public members
40 */
41 ip_packet_t public;
42
43 /**
44 * Source address
45 */
46 host_t *src;
47
48 /**
49 * Destination address
50 */
51 host_t *dst;
52
53 /**
54 * IP packet
55 */
56 chunk_t packet;
57
58 /**
59 * IP payload (points into packet)
60 */
61 chunk_t payload;
62
63 /**
64 * IP version
65 */
66 u_int8_t version;
67
68 /**
69 * Protocol|Next Header field
70 */
71 u_int8_t next_header;
72
73 };
74
75 METHOD(ip_packet_t, get_version, u_int8_t,
76 private_ip_packet_t *this)
77 {
78 return this->version;
79 }
80
81 METHOD(ip_packet_t, get_source, host_t*,
82 private_ip_packet_t *this)
83 {
84 return this->src;
85 }
86
87 METHOD(ip_packet_t, get_destination, host_t*,
88 private_ip_packet_t *this)
89 {
90 return this->dst;
91 }
92
93 METHOD(ip_packet_t, get_encoding, chunk_t,
94 private_ip_packet_t *this)
95 {
96 return this->packet;
97 }
98
99 METHOD(ip_packet_t, get_payload, chunk_t,
100 private_ip_packet_t *this)
101 {
102 return this->payload;
103 }
104
105 METHOD(ip_packet_t, get_next_header, u_int8_t,
106 private_ip_packet_t *this)
107 {
108 return this->next_header;
109 }
110
111 METHOD(ip_packet_t, clone_, ip_packet_t*,
112 private_ip_packet_t *this)
113 {
114 return ip_packet_create(chunk_clone(this->packet));
115 }
116
117 METHOD(ip_packet_t, destroy, void,
118 private_ip_packet_t *this)
119 {
120 this->src->destroy(this->src);
121 this->dst->destroy(this->dst);
122 chunk_free(&this->packet);
123 free(this);
124 }
125
126 /**
127 * Parse transport protocol header
128 */
129 static bool parse_transport_header(chunk_t packet, u_int8_t proto,
130 u_int16_t *sport, u_int16_t *dport)
131 {
132 switch (proto)
133 {
134 case IPPROTO_UDP:
135 {
136 struct udphdr *udp;
137
138 if (packet.len < sizeof(*udp))
139 {
140 DBG1(DBG_ESP, "UDP packet too short");
141 return FALSE;
142 }
143 udp = (struct udphdr*)packet.ptr;
144 *sport = ntohs(udp->source);
145 *dport = ntohs(udp->dest);
146 break;
147 }
148 case IPPROTO_TCP:
149 {
150 struct tcphdr *tcp;
151
152 if (packet.len < sizeof(*tcp))
153 {
154 DBG1(DBG_ESP, "TCP packet too short");
155 return FALSE;
156 }
157 tcp = (struct tcphdr*)packet.ptr;
158 *sport = ntohs(tcp->source);
159 *dport = ntohs(tcp->dest);
160 break;
161 }
162 default:
163 break;
164 }
165 return TRUE;
166 }
167
168 /**
169 * Described in header.
170 */
171 ip_packet_t *ip_packet_create(chunk_t packet)
172 {
173 private_ip_packet_t *this;
174 u_int8_t version, next_header;
175 u_int16_t sport = 0, dport = 0;
176 host_t *src, *dst;
177 chunk_t payload;
178
179 if (packet.len < 1)
180 {
181 DBG1(DBG_ESP, "IP packet too short");
182 goto failed;
183 }
184
185 version = (packet.ptr[0] & 0xf0) >> 4;
186
187 switch (version)
188 {
189 case 4:
190 {
191 struct ip *ip;
192
193 if (packet.len < sizeof(struct ip))
194 {
195 DBG1(DBG_ESP, "IPv4 packet too short");
196 goto failed;
197 }
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))
203 {
204 goto failed;
205 }
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;
211 break;
212 }
213 #ifdef HAVE_NETINET_IP6_H
214 case 6:
215 {
216 struct ip6_hdr *ip;
217
218 if (packet.len < sizeof(*ip))
219 {
220 DBG1(DBG_ESP, "IPv6 packet too short");
221 goto failed;
222 }
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))
230 {
231 goto failed;
232 }
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;
238 break;
239 }
240 #endif /* HAVE_NETINET_IP6_H */
241 default:
242 DBG1(DBG_ESP, "unsupported IP version");
243 goto failed;
244 }
245
246 INIT(this,
247 .public = {
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,
254 .clone = _clone_,
255 .destroy = _destroy,
256 },
257 .src = src,
258 .dst = dst,
259 .packet = packet,
260 .payload = payload,
261 .version = version,
262 .next_header = next_header,
263 );
264 return &this->public;
265
266 failed:
267 chunk_free(&packet);
268 return NULL;
269 }
270
271 /**
272 * Calculate the checksum for the pseudo IP header
273 */
274 static u_int16_t pseudo_header_checksum(host_t *src, host_t *dst,
275 u_int8_t proto, chunk_t payload)
276 {
277 switch (src->get_family(src))
278 {
279 case AF_INET:
280 {
281 struct __attribute__((packed)) {
282 u_int32_t src;
283 u_int32_t dst;
284 u_char zero;
285 u_char proto;
286 u_int16_t len;
287 } pseudo = {
288 .proto = proto,
289 .len = htons(payload.len),
290 };
291 memcpy(&pseudo.src, src->get_address(src).ptr,
292 sizeof(pseudo.src));
293 memcpy(&pseudo.dst, dst->get_address(dst).ptr,
294 sizeof(pseudo.dst));
295 return chunk_internet_checksum(chunk_from_thing(pseudo));
296 }
297 case AF_INET6:
298 {
299 struct __attribute__((packed)) {
300 u_char src[16];
301 u_char dst[16];
302 u_int32_t len;
303 u_char zero[3];
304 u_char next_header;
305 } pseudo = {
306 .next_header = proto,
307 .len = htons(payload.len),
308 };
309 memcpy(&pseudo.src, src->get_address(src).ptr,
310 sizeof(pseudo.src));
311 memcpy(&pseudo.dst, dst->get_address(dst).ptr,
312 sizeof(pseudo.dst));
313 return chunk_internet_checksum(chunk_from_thing(pseudo));
314 }
315 }
316 return 0xffff;
317 }
318
319 /**
320 * Calculate transport header checksums
321 */
322 static void fix_transport_checksum(host_t *src, host_t *dst, u_int8_t proto,
323 chunk_t payload)
324 {
325 u_int16_t sum = 0;
326
327 switch (proto)
328 {
329 case IPPROTO_UDP:
330 {
331 struct udphdr *udp;
332
333 if (payload.len < sizeof(*udp))
334 {
335 return;
336 }
337 udp = (struct udphdr*)payload.ptr;
338 udp->check = 0;
339 sum = pseudo_header_checksum(src, dst, proto, payload);
340 udp->check = chunk_internet_checksum_inc(payload, sum);
341 break;
342 }
343 case IPPROTO_TCP:
344 {
345 struct tcphdr *tcp;
346
347 if (payload.len < sizeof(*tcp))
348 {
349 return;
350 }
351 tcp = (struct tcphdr*)payload.ptr;
352 tcp->check = 0;
353 sum = pseudo_header_checksum(src, dst, proto, payload);
354 tcp->check = chunk_internet_checksum_inc(payload, sum);
355 break;
356 }
357 default:
358 break;
359 }
360 }
361
362 /**
363 * Described in header.
364 */
365 ip_packet_t *ip_packet_create_from_data(host_t *src, host_t *dst,
366 u_int8_t next_header, chunk_t data)
367 {
368 chunk_t packet;
369 int family;
370
371 family = src->get_family(src);
372 if (family != dst->get_family(dst))
373 {
374 DBG1(DBG_ESP, "address family does not match");
375 return NULL;
376 }
377
378 switch (family)
379 {
380 case AF_INET:
381 {
382 struct ip ip = {
383 .ip_v = 4,
384 .ip_hl = 5,
385 .ip_len = htons(20 + data.len),
386 .ip_ttl = 0x80,
387 .ip_p = next_header,
388 };
389 memcpy(&ip.ip_src, src->get_address(src).ptr, sizeof(ip.ip_src));
390 memcpy(&ip.ip_dst, dst->get_address(dst).ptr, sizeof(ip.ip_dst));
391 ip.ip_sum = chunk_internet_checksum(chunk_from_thing(ip));
392
393 packet = chunk_cat("cc", chunk_from_thing(ip), data);
394 fix_transport_checksum(src, dst, next_header,
395 chunk_skip(packet, 20));
396 return ip_packet_create(packet);
397 }
398 #ifdef HAVE_NETINET_IP6_H
399 case AF_INET6:
400 {
401 struct ip6_hdr ip = {
402 .ip6_flow = htonl(6),
403 .ip6_plen = htons(40 + data.len),
404 .ip6_nxt = next_header,
405 .ip6_hlim = 0x80,
406 };
407 memcpy(&ip.ip6_src, src->get_address(src).ptr, sizeof(ip.ip6_src));
408 memcpy(&ip.ip6_dst, dst->get_address(dst).ptr, sizeof(ip.ip6_dst));
409
410 packet = chunk_cat("cc", chunk_from_thing(ip), data);
411 fix_transport_checksum(src, dst, next_header,
412 chunk_skip(packet, 40));
413 return ip_packet_create(packet);
414 }
415 #endif /* HAVE_NETINET_IP6_H */
416 default:
417 DBG1(DBG_ESP, "unsupported address family");
418 return NULL;
419 }
420 }