source: mainline/uspace/srv/net/inetsrv/pdu.c@ 1d94e21

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 1d94e21 was 1d94e21, checked in by Martin Decky <martin@…>, 12 years ago

cherrypick lp:~as-s/helenos/ipv6

  • Property mode set to 100644
File size: 14.6 KB
Line 
1/*
2 * Copyright (c) 2012 Jiri Svoboda
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup inet
30 * @{
31 */
32/**
33 * @file
34 * @brief
35 */
36
37#include <align.h>
38#include <bitops.h>
39#include <byteorder.h>
40#include <errno.h>
41#include <fibril_synch.h>
42#include <io/log.h>
43#include <macros.h>
44#include <mem.h>
45#include <stdlib.h>
46#include <net/socket_codes.h>
47#include "inetsrv.h"
48#include "inet_std.h"
49#include "pdu.h"
50
51/** One's complement addition.
52 *
53 * Result is a + b + carry.
54 */
55static uint16_t inet_ocadd16(uint16_t a, uint16_t b)
56{
57 uint32_t s;
58
59 s = (uint32_t)a + (uint32_t)b;
60 return (s & 0xffff) + (s >> 16);
61}
62
63uint16_t inet_checksum_calc(uint16_t ivalue, void *data, size_t size)
64{
65 uint16_t sum;
66 uint16_t w;
67 size_t words, i;
68 uint8_t *bdata;
69
70 sum = ~ivalue;
71 words = size / 2;
72 bdata = (uint8_t *)data;
73
74 for (i = 0; i < words; i++) {
75 w = ((uint16_t)bdata[2*i] << 8) | bdata[2*i + 1];
76 sum = inet_ocadd16(sum, w);
77 }
78
79 if (size % 2 != 0) {
80 w = ((uint16_t)bdata[2*words] << 8);
81 sum = inet_ocadd16(sum, w);
82 }
83
84 return ~sum;
85}
86
87/** Encode IPv4 PDU.
88 *
89 * Encode internet packet into PDU (serialized form). Will encode a
90 * fragment of the payload starting at offset @a offs. The resulting
91 * PDU will have at most @a mtu bytes. @a *roffs will be set to the offset
92 * of remaining payload. If some data is remaining, the MF flag will
93 * be set in the header, otherwise the offset will equal @a packet->size.
94 *
95 * @param packet Packet to encode
96 * @param src Source address
97 * @param dest Destination address
98 * @param offs Offset into packet payload (in bytes)
99 * @param mtu MTU (Maximum Transmission Unit) in bytes
100 * @param rdata Place to store pointer to allocated data buffer
101 * @param rsize Place to store size of allocated data buffer
102 * @param roffs Place to store offset of remaning data
103 *
104 */
105int inet_pdu_encode(inet_packet_t *packet, addr32_t src, addr32_t dest,
106 size_t offs, size_t mtu, void **rdata, size_t *rsize, size_t *roffs)
107{
108 /* Upper bound for fragment offset field */
109 size_t fragoff_limit = 1 << (FF_FRAGOFF_h - FF_FRAGOFF_l);
110
111 /* Verify that total size of datagram is within reasonable bounds */
112 if (offs + packet->size > FRAG_OFFS_UNIT * fragoff_limit)
113 return ELIMIT;
114
115 size_t hdr_size = sizeof(ip_header_t);
116
117 size_t data_offs = ROUND_UP(hdr_size, 4);
118
119 assert(offs % FRAG_OFFS_UNIT == 0);
120 assert(offs / FRAG_OFFS_UNIT < fragoff_limit);
121
122 /* Value for the fragment offset field */
123 uint16_t foff = offs / FRAG_OFFS_UNIT;
124
125 if (hdr_size >= mtu)
126 return EINVAL;
127
128 /* Amount of space in the PDU available for payload */
129 size_t spc_avail = mtu - hdr_size;
130 spc_avail -= (spc_avail % FRAG_OFFS_UNIT);
131
132 /* Amount of data (payload) to transfer */
133 size_t xfer_size = min(packet->size - offs, spc_avail);
134
135 /* Total PDU size */
136 size_t size = hdr_size + xfer_size;
137
138 /* Offset of remaining payload */
139 size_t rem_offs = offs + xfer_size;
140
141 /* Flags */
142 uint16_t flags_foff =
143 (packet->df ? BIT_V(uint16_t, FF_FLAG_DF) : 0) +
144 (rem_offs < packet->size ? BIT_V(uint16_t, FF_FLAG_MF) : 0) +
145 (foff << FF_FRAGOFF_l);
146
147 void *data = calloc(size, 1);
148 if (data == NULL)
149 return ENOMEM;
150
151 /* Encode header fields */
152 ip_header_t *hdr = (ip_header_t *) data;
153
154 hdr->ver_ihl =
155 (4 << VI_VERSION_l) | (hdr_size / sizeof(uint32_t));
156 hdr->tos = packet->tos;
157 hdr->tot_len = host2uint16_t_be(size);
158 hdr->id = host2uint16_t_be(packet->ident);
159 hdr->flags_foff = host2uint16_t_be(flags_foff);
160 hdr->ttl = packet->ttl;
161 hdr->proto = packet->proto;
162 hdr->chksum = 0;
163 hdr->src_addr = host2uint32_t_be(src);
164 hdr->dest_addr = host2uint32_t_be(dest);
165
166 /* Compute checksum */
167 uint16_t chksum = inet_checksum_calc(INET_CHECKSUM_INIT,
168 (void *) hdr, hdr_size);
169 hdr->chksum = host2uint16_t_be(chksum);
170
171 /* Copy payload */
172 memcpy((uint8_t *) data + data_offs, packet->data + offs, xfer_size);
173
174 *rdata = data;
175 *rsize = size;
176 *roffs = rem_offs;
177
178 return EOK;
179}
180
181/** Encode IPv6 PDU.
182 *
183 * Encode internet packet into PDU (serialized form). Will encode a
184 * fragment of the payload starting at offset @a offs. The resulting
185 * PDU will have at most @a mtu bytes. @a *roffs will be set to the offset
186 * of remaining payload. If some data is remaining, the MF flag will
187 * be set in the header, otherwise the offset will equal @a packet->size.
188 *
189 * @param packet Packet to encode
190 * @param src Source address
191 * @param dest Destination address
192 * @param offs Offset into packet payload (in bytes)
193 * @param mtu MTU (Maximum Transmission Unit) in bytes
194 * @param rdata Place to store pointer to allocated data buffer
195 * @param rsize Place to store size of allocated data buffer
196 * @param roffs Place to store offset of remaning data
197 *
198 */
199int inet_pdu_encode6(inet_packet_t *packet, addr128_t src, addr128_t dest,
200 size_t offs, size_t mtu, void **rdata, size_t *rsize, size_t *roffs)
201{
202 /* Upper bound for fragment offset field */
203 size_t fragoff_limit = 1 << (FF_FRAGOFF_h - FF_FRAGOFF_l);
204
205 /* Verify that total size of datagram is within reasonable bounds */
206 if (offs + packet->size > FRAG_OFFS_UNIT * fragoff_limit)
207 return ELIMIT;
208
209 size_t hdr_size = sizeof(ip6_header_t);
210
211 size_t data_offs = ROUND_UP(hdr_size, 4);
212
213 assert(offs % FRAG_OFFS_UNIT == 0);
214 assert(offs / FRAG_OFFS_UNIT < fragoff_limit);
215
216#if 0
217 // FIXME TODO fragmentation
218
219 /* Value for the fragment offset field */
220 uint16_t foff = offs / FRAG_OFFS_UNIT;
221#endif
222
223 if (hdr_size >= mtu)
224 return EINVAL;
225
226 /* Amount of space in the PDU available for payload */
227 size_t spc_avail = mtu - hdr_size;
228 spc_avail -= (spc_avail % FRAG_OFFS_UNIT);
229
230 /* Amount of data (payload) to transfer */
231 size_t xfer_size = min(packet->size - offs, spc_avail);
232
233 /* Total PDU size */
234 size_t size = hdr_size + xfer_size;
235
236 /* Offset of remaining payload */
237 size_t rem_offs = offs + xfer_size;
238
239#if 0
240 // FIXME TODO fragmentation
241
242 /* Flags */
243 uint16_t flags_foff =
244 (packet->df ? BIT_V(uint16_t, FF_FLAG_DF) : 0) +
245 (rem_offs < packet->size ? BIT_V(uint16_t, FF_FLAG_MF) : 0) +
246 (foff << FF_FRAGOFF_l);
247#endif
248
249 void *data = calloc(size, 1);
250 if (data == NULL)
251 return ENOMEM;
252
253 /* Encode header fields */
254 ip6_header_t *hdr6 = (ip6_header_t *) data;
255
256 hdr6->ver_tc = (6 << (VI_VERSION_l));
257 memset(hdr6->tc_fl, 0, 3);
258 hdr6->payload_len = host2uint16_t_be(packet->size);
259 hdr6->next = packet->proto;
260 hdr6->hop_limit = packet->ttl;
261
262 host2addr128_t_be(src, hdr6->src_addr);
263 host2addr128_t_be(dest, hdr6->dest_addr);
264
265 /* Copy payload */
266 memcpy((uint8_t *) data + data_offs, packet->data + offs, xfer_size);
267
268 *rdata = data;
269 *rsize = size;
270 *roffs = rem_offs;
271
272 return EOK;
273}
274
275/** Decode IPv4 datagram
276 *
277 * @param data Serialized IPv4 datagram
278 * @param size Length of serialized IPv4 datagram
279 * @param packet IP datagram structure to be filled
280 *
281 * @return EOK on success
282 * @return EINVAL if the datagram is invalid or damaged
283 * @return ENOMEM if not enough memory
284 *
285 */
286int inet_pdu_decode(void *data, size_t size, inet_packet_t *packet)
287{
288 log_msg(LOG_DEFAULT, LVL_DEBUG, "inet_pdu_decode()");
289
290 if (size < sizeof(ip_header_t)) {
291 log_msg(LOG_DEFAULT, LVL_DEBUG, "PDU too short (%zu)", size);
292 return EINVAL;
293 }
294
295 ip_header_t *hdr = (ip_header_t *) data;
296
297 uint8_t version = BIT_RANGE_EXTRACT(uint8_t, VI_VERSION_h,
298 VI_VERSION_l, hdr->ver_ihl);
299 if (version != 4) {
300 log_msg(LOG_DEFAULT, LVL_DEBUG, "Version (%d) != 4", version);
301 return EINVAL;
302 }
303
304 size_t tot_len = uint16_t_be2host(hdr->tot_len);
305 if (tot_len < sizeof(ip_header_t)) {
306 log_msg(LOG_DEFAULT, LVL_DEBUG, "Total Length too small (%zu)", tot_len);
307 return EINVAL;
308 }
309
310 if (tot_len > size) {
311 log_msg(LOG_DEFAULT, LVL_DEBUG, "Total Length = %zu > PDU size = %zu",
312 tot_len, size);
313 return EINVAL;
314 }
315
316 uint16_t ident = uint16_t_be2host(hdr->id);
317 uint16_t flags_foff = uint16_t_be2host(hdr->flags_foff);
318 uint16_t foff = BIT_RANGE_EXTRACT(uint16_t, FF_FRAGOFF_h, FF_FRAGOFF_l,
319 flags_foff);
320 /* XXX Checksum */
321
322 inet_addr_set(uint32_t_be2host(hdr->src_addr), &packet->src);
323 inet_addr_set(uint32_t_be2host(hdr->dest_addr), &packet->dest);
324 packet->tos = hdr->tos;
325 packet->proto = hdr->proto;
326 packet->ttl = hdr->ttl;
327 packet->ident = ident;
328
329 packet->df = (flags_foff & BIT_V(uint16_t, FF_FLAG_DF)) != 0;
330 packet->mf = (flags_foff & BIT_V(uint16_t, FF_FLAG_MF)) != 0;
331 packet->offs = foff * FRAG_OFFS_UNIT;
332
333 /* XXX IP options */
334 size_t data_offs = sizeof(uint32_t) *
335 BIT_RANGE_EXTRACT(uint8_t, VI_IHL_h, VI_IHL_l, hdr->ver_ihl);
336
337 packet->size = tot_len - data_offs;
338 packet->data = calloc(packet->size, 1);
339 if (packet->data == NULL) {
340 log_msg(LOG_DEFAULT, LVL_WARN, "Out of memory.");
341 return ENOMEM;
342 }
343
344 memcpy(packet->data, (uint8_t *) data + data_offs, packet->size);
345
346 return EOK;
347}
348
349/** Decode IPv6 datagram
350 *
351 * @param data Serialized IPv6 datagram
352 * @param size Length of serialized IPv6 datagram
353 * @param packet IP datagram structure to be filled
354 *
355 * @return EOK on success
356 * @return EINVAL if the datagram is invalid or damaged
357 * @return ENOMEM if not enough memory
358 *
359 */
360int inet_pdu_decode6(void *data, size_t size, inet_packet_t *packet)
361{
362 log_msg(LOG_DEFAULT, LVL_DEBUG, "inet_pdu_decode6()");
363
364 if (size < sizeof(ip6_header_t)) {
365 log_msg(LOG_DEFAULT, LVL_DEBUG, "PDU too short (%zu)", size);
366 return EINVAL;
367 }
368
369 ip6_header_t *hdr6 = (ip6_header_t *) data;
370
371 uint8_t version = BIT_RANGE_EXTRACT(uint8_t, VI_VERSION_h,
372 VI_VERSION_l, hdr6->ver_tc);
373 if (version != 6) {
374 log_msg(LOG_DEFAULT, LVL_DEBUG, "Version (%d) != 6", version);
375 return EINVAL;
376 }
377
378 size_t payload_len = uint16_t_be2host(hdr6->payload_len);
379 if (payload_len + sizeof(ip6_header_t) > size) {
380 log_msg(LOG_DEFAULT, LVL_DEBUG, "Total Length = %zu > PDU size = %zu",
381 payload_len + sizeof(ip6_header_t), size);
382 return EINVAL;
383 }
384
385#if 0
386 // FIXME TODO fragmentation
387
388 uint16_t ident = uint16_t_be2host(hdr->id);
389 uint16_t flags_foff = uint16_t_be2host(hdr->flags_foff);
390 uint16_t foff = BIT_RANGE_EXTRACT(uint16_t, FF_FRAGOFF_h, FF_FRAGOFF_l,
391 flags_foff);
392#endif
393
394 /* XXX Checksum */
395
396 addr128_t src;
397 addr128_t dest;
398
399 addr128_t_be2host(hdr6->src_addr, src);
400 inet_addr_set6(src, &packet->src);
401
402 addr128_t_be2host(hdr6->dest_addr, dest);
403 inet_addr_set6(dest, &packet->dest);
404
405 packet->tos = 0;
406 packet->proto = hdr6->next;
407 packet->ttl = hdr6->hop_limit;
408
409#if 0
410 // FIXME TODO fragmentation
411
412 packet->ident = ident;
413 packet->df = (flags_foff & BIT_V(uint16_t, FF_FLAG_DF)) != 0;
414 packet->mf = (flags_foff & BIT_V(uint16_t, FF_FLAG_MF)) != 0;
415 packet->offs = foff * FRAG_OFFS_UNIT;
416
417 /* XXX IP options */
418 size_t data_offs = sizeof(uint32_t) *
419 BIT_RANGE_EXTRACT(uint8_t, VI_IHL_h, VI_IHL_l, hdr->ver_ihl);
420#endif
421
422 packet->ident = 0;
423 packet->df = 0;
424 packet->mf = 0;
425 packet->offs = 0;
426
427 packet->size = payload_len;
428 packet->data = calloc(packet->size, 1);
429 if (packet->data == NULL) {
430 log_msg(LOG_DEFAULT, LVL_WARN, "Out of memory.");
431 return ENOMEM;
432 }
433
434 memcpy(packet->data, (uint8_t *) data + sizeof(ip6_header_t), packet->size);
435
436 return EOK;
437}
438
439/** Encode NDP packet
440 *
441 * @param ndp NDP packet structure to be serialized
442 * @param dgram IPv6 datagram structure to be filled
443 *
444 * @return EOK on success
445 *
446 */
447int ndp_pdu_encode(ndp_packet_t *ndp, inet_dgram_t *dgram)
448{
449 inet_addr_set6(ndp->sender_proto_addr, &dgram->src);
450 inet_addr_set6(ndp->target_proto_addr, &dgram->dest);
451 dgram->tos = 0;
452 dgram->size = sizeof(icmpv6_message_t) + sizeof(ndp_message_t);
453
454 dgram->data = calloc(1, dgram->size);
455 if (dgram->data == NULL)
456 return ENOMEM;
457
458 icmpv6_message_t *icmpv6 = (icmpv6_message_t *) dgram->data;
459
460 icmpv6->type = ndp->opcode;
461 icmpv6->code = 0;
462 memset(icmpv6->un.ndp.reserved, 0, 3);
463
464 ndp_message_t *message = (ndp_message_t *) (icmpv6 + 1);
465
466 if (ndp->opcode == ICMPV6_NEIGHBOUR_SOLICITATION) {
467 host2addr128_t_be(ndp->solicited_ip, message->target_address);
468 message->option = 1;
469 icmpv6->un.ndp.flags = 0;
470 } else {
471 host2addr128_t_be(ndp->sender_proto_addr, message->target_address);
472 message->option = 2;
473 icmpv6->un.ndp.flags = NDP_FLAG_OVERRIDE | NDP_FLAG_SOLICITED;
474 }
475
476 message->length = 1;
477 addr48(ndp->sender_hw_addr, message->mac);
478
479 icmpv6_phdr_t phdr;
480
481 host2addr128_t_be(ndp->sender_proto_addr, phdr.src_addr);
482 host2addr128_t_be(ndp->target_proto_addr, phdr.dest_addr);
483 phdr.length = host2uint32_t_be(dgram->size);
484 memset(phdr.zeroes, 0, 3);
485 phdr.next = IP_PROTO_ICMPV6;
486
487 uint16_t cs_phdr =
488 inet_checksum_calc(INET_CHECKSUM_INIT, &phdr,
489 sizeof(icmpv6_phdr_t));
490
491 uint16_t cs_all = inet_checksum_calc(cs_phdr, dgram->data,
492 dgram->size);
493
494 icmpv6->checksum = host2uint16_t_be(cs_all);
495
496 return EOK;
497}
498
499/** Decode NDP packet
500 *
501 * @param dgram Incoming IPv6 datagram encapsulating NDP packet
502 * @param ndp NDP packet structure to be filled
503 *
504 * @return EOK on success
505 * @return EINVAL if the Datagram is invalid
506 *
507 */
508int ndp_pdu_decode(inet_dgram_t *dgram, ndp_packet_t *ndp)
509{
510 uint16_t src_af = inet_addr_get(&dgram->src, NULL,
511 &ndp->sender_proto_addr);
512 if (src_af != AF_INET6)
513 return EINVAL;
514
515 if (dgram->size < sizeof(icmpv6_message_t) + sizeof(ndp_message_t))
516 return EINVAL;
517
518 icmpv6_message_t *icmpv6 = (icmpv6_message_t *) dgram->data;
519
520 ndp->opcode = icmpv6->type;
521
522 ndp_message_t *message = (ndp_message_t *) (icmpv6 + 1);
523
524 addr128_t_be2host(message->target_address, ndp->target_proto_addr);
525 addr48(message->mac, ndp->sender_hw_addr);
526
527 return EOK;
528}
529
530/** @}
531 */
Note: See TracBrowser for help on using the repository browser.