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

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

cherrypick trivial changes from lp:~as-s/helenos/ipv6

  • Property mode set to 100644
File size: 14.0 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
51static FIBRIL_MUTEX_INITIALIZE(ip_ident_lock);
52static uint16_t ip_ident = 0;
53
54/** One's complement addition.
55 *
56 * Result is a + b + carry.
57 */
58static uint16_t inet_ocadd16(uint16_t a, uint16_t b)
59{
60 uint32_t s;
61
62 s = (uint32_t)a + (uint32_t)b;
63 return (s & 0xffff) + (s >> 16);
64}
65
66uint16_t inet_checksum_calc(uint16_t ivalue, void *data, size_t size)
67{
68 uint16_t sum;
69 uint16_t w;
70 size_t words, i;
71 uint8_t *bdata;
72
73 sum = ~ivalue;
74 words = size / 2;
75 bdata = (uint8_t *)data;
76
77 for (i = 0; i < words; i++) {
78 w = ((uint16_t)bdata[2*i] << 8) | bdata[2*i + 1];
79 sum = inet_ocadd16(sum, w);
80 }
81
82 if (size % 2 != 0) {
83 w = ((uint16_t)bdata[2*words] << 8);
84 sum = inet_ocadd16(sum, w);
85 }
86
87 return ~sum;
88}
89
90/** Encode IPv4 PDU.
91 *
92 * Encode internet packet into PDU (serialized form). Will encode a
93 * fragment of the payload starting at offset @a offs. The resulting
94 * PDU will have at most @a mtu bytes. @a *roffs will be set to the offset
95 * of remaining payload. If some data is remaining, the MF flag will
96 * be set in the header, otherwise the offset will equal @a packet->size.
97 *
98 * @param packet Packet to encode
99 * @param src Source address
100 * @param dest Destination address
101 * @param offs Offset into packet payload (in bytes)
102 * @param mtu MTU (Maximum Transmission Unit) in bytes
103 * @param rdata Place to store pointer to allocated data buffer
104 * @param rsize Place to store size of allocated data buffer
105 * @param roffs Place to store offset of remaning data
106 *
107 */
108int inet_pdu_encode(inet_packet_t *packet, addr32_t src, addr32_t dest,
109 size_t offs, size_t mtu, void **rdata, size_t *rsize, size_t *roffs)
110{
111 /* Upper bound for fragment offset field */
112 size_t fragoff_limit = 1 << (FF_FRAGOFF_h - FF_FRAGOFF_l);
113
114 /* Verify that total size of datagram is within reasonable bounds */
115 if (offs + packet->size > FRAG_OFFS_UNIT * fragoff_limit)
116 return ELIMIT;
117
118 size_t hdr_size = sizeof(ip_header_t);
119
120 size_t data_offs = ROUND_UP(hdr_size, 4);
121
122 assert(offs % FRAG_OFFS_UNIT == 0);
123 assert(offs / FRAG_OFFS_UNIT < fragoff_limit);
124
125 /* Value for the fragment offset field */
126 uint16_t foff = offs / FRAG_OFFS_UNIT;
127
128 if (hdr_size >= mtu)
129 return EINVAL;
130
131 /* Amount of space in the PDU available for payload */
132 size_t spc_avail = mtu - hdr_size;
133 spc_avail -= (spc_avail % FRAG_OFFS_UNIT);
134
135 /* Amount of data (payload) to transfer */
136 size_t xfer_size = min(packet->size - offs, spc_avail);
137
138 /* Total PDU size */
139 size_t size = hdr_size + xfer_size;
140
141 /* Offset of remaining payload */
142 size_t rem_offs = offs + xfer_size;
143
144 /* Flags */
145 uint16_t flags_foff =
146 (packet->df ? BIT_V(uint16_t, FF_FLAG_DF) : 0) +
147 (rem_offs < packet->size ? BIT_V(uint16_t, FF_FLAG_MF) : 0) +
148 (foff << FF_FRAGOFF_l);
149
150 void *data = calloc(size, 1);
151 if (data == NULL)
152 return ENOMEM;
153
154 /* Allocate identifier */
155 fibril_mutex_lock(&ip_ident_lock);
156 uint16_t ident = ++ip_ident;
157 fibril_mutex_unlock(&ip_ident_lock);
158
159 /* Encode header fields */
160 ip_header_t *hdr = (ip_header_t *) data;
161
162 hdr->ver_ihl =
163 (4 << VI_VERSION_l) | (hdr_size / sizeof(uint32_t));
164 hdr->tos = packet->tos;
165 hdr->tot_len = host2uint16_t_be(size);
166 hdr->id = host2uint16_t_be(ident);
167 hdr->flags_foff = host2uint16_t_be(flags_foff);
168 hdr->ttl = packet->ttl;
169 hdr->proto = packet->proto;
170 hdr->chksum = 0;
171 hdr->src_addr = host2uint32_t_be(src);
172 hdr->dest_addr = host2uint32_t_be(dest);
173
174 /* Compute checksum */
175 uint16_t chksum = inet_checksum_calc(INET_CHECKSUM_INIT,
176 (void *) hdr, hdr_size);
177 hdr->chksum = host2uint16_t_be(chksum);
178
179 /* Copy payload */
180 memcpy((uint8_t *) data + data_offs, packet->data + offs, xfer_size);
181
182 *rdata = data;
183 *rsize = size;
184 *roffs = rem_offs;
185
186 return EOK;
187}
188
189/** Encode IPv6 PDU.
190 *
191 * Encode internet packet into PDU (serialized form). Will encode a
192 * fragment of the payload starting at offset @a offs. The resulting
193 * PDU will have at most @a mtu bytes. @a *roffs will be set to the offset
194 * of remaining payload. If some data is remaining, the MF flag will
195 * be set in the header, otherwise the offset will equal @a packet->size.
196 *
197 * @param packet Packet to encode
198 * @param src Source address
199 * @param dest Destination address
200 * @param offs Offset into packet payload (in bytes)
201 * @param mtu MTU (Maximum Transmission Unit) in bytes
202 * @param rdata Place to store pointer to allocated data buffer
203 * @param rsize Place to store size of allocated data buffer
204 * @param roffs Place to store offset of remaning data
205 *
206 */
207int inet_pdu_encode6(inet_packet_t *packet, addr128_t src, addr128_t dest,
208 size_t offs, size_t mtu, void **rdata, size_t *rsize, size_t *roffs)
209{
210 /* Upper bound for fragment offset field */
211 size_t fragoff_limit = 1 << (FF_FRAGOFF_h - FF_FRAGOFF_l);
212
213 /* Verify that total size of datagram is within reasonable bounds */
214 if (offs + packet->size > FRAG_OFFS_UNIT * fragoff_limit)
215 return ELIMIT;
216
217 size_t hdr_size = sizeof(ip6_header_t);
218
219 size_t data_offs = ROUND_UP(hdr_size, 4);
220
221 assert(offs % FRAG_OFFS_UNIT == 0);
222 assert(offs / FRAG_OFFS_UNIT < fragoff_limit);
223
224#if 0
225 // FIXME TODO fragmentation
226
227 /* Value for the fragment offset field */
228 uint16_t foff = offs / FRAG_OFFS_UNIT;
229#endif
230
231 if (hdr_size >= mtu)
232 return EINVAL;
233
234 /* Amount of space in the PDU available for payload */
235 size_t spc_avail = mtu - hdr_size;
236 spc_avail -= (spc_avail % FRAG_OFFS_UNIT);
237
238 /* Amount of data (payload) to transfer */
239 size_t xfer_size = min(packet->size - offs, spc_avail);
240
241 /* Total PDU size */
242 size_t size = hdr_size + xfer_size;
243
244 /* Offset of remaining payload */
245 size_t rem_offs = offs + xfer_size;
246
247#if 0
248 // FIXME TODO fragmentation
249
250 /* Flags */
251 uint16_t flags_foff =
252 (packet->df ? BIT_V(uint16_t, FF_FLAG_DF) : 0) +
253 (rem_offs < packet->size ? BIT_V(uint16_t, FF_FLAG_MF) : 0) +
254 (foff << FF_FRAGOFF_l);
255#endif
256
257 void *data = calloc(size, 1);
258 if (data == NULL)
259 return ENOMEM;
260
261#if 0
262 // FIXME TODO fragmentation
263
264 /* Allocate identifier */
265 fibril_mutex_lock(&ip_ident_lock);
266 uint16_t ident = ++ip_ident;
267 fibril_mutex_unlock(&ip_ident_lock);
268#endif
269
270 /* Encode header fields */
271 ip6_header_t *hdr6 = (ip6_header_t *) data;
272
273 hdr6->ver_tc = (6 << (VI_VERSION_l));
274 memset(hdr6->tc_fl, 0, 3);
275 hdr6->payload_len = host2uint16_t_be(packet->size);
276 hdr6->next = packet->proto;
277 hdr6->hop_limit = packet->ttl;
278
279 host2addr128_t_be(src, hdr6->src_addr);
280 host2addr128_t_be(dest, hdr6->dest_addr);
281
282 /* Copy payload */
283 memcpy((uint8_t *) data + data_offs, packet->data + offs, xfer_size);
284
285 *rdata = data;
286 *rsize = size;
287 *roffs = rem_offs;
288
289 return EOK;
290}
291
292int inet_pdu_decode(void *data, size_t size, inet_packet_t *packet)
293{
294 log_msg(LOG_DEFAULT, LVL_DEBUG, "inet_pdu_decode()");
295
296 if (size < sizeof(ip_header_t)) {
297 log_msg(LOG_DEFAULT, LVL_DEBUG, "PDU too short (%zu)", size);
298 return EINVAL;
299 }
300
301 ip_header_t *hdr = (ip_header_t *) data;
302
303 uint8_t version = BIT_RANGE_EXTRACT(uint8_t, VI_VERSION_h,
304 VI_VERSION_l, hdr->ver_ihl);
305 if (version != 4) {
306 log_msg(LOG_DEFAULT, LVL_DEBUG, "Version (%d) != 4", version);
307 return EINVAL;
308 }
309
310 size_t tot_len = uint16_t_be2host(hdr->tot_len);
311 if (tot_len < sizeof(ip_header_t)) {
312 log_msg(LOG_DEFAULT, LVL_DEBUG, "Total Length too small (%zu)", tot_len);
313 return EINVAL;
314 }
315
316 if (tot_len > size) {
317 log_msg(LOG_DEFAULT, LVL_DEBUG, "Total Length = %zu > PDU size = %zu",
318 tot_len, size);
319 return EINVAL;
320 }
321
322 uint16_t ident = uint16_t_be2host(hdr->id);
323 uint16_t flags_foff = uint16_t_be2host(hdr->flags_foff);
324 uint16_t foff = BIT_RANGE_EXTRACT(uint16_t, FF_FRAGOFF_h, FF_FRAGOFF_l,
325 flags_foff);
326 /* XXX Checksum */
327
328 inet_addr_set(uint32_t_be2host(hdr->src_addr), &packet->src);
329 inet_addr_set(uint32_t_be2host(hdr->dest_addr), &packet->dest);
330 packet->tos = hdr->tos;
331 packet->proto = hdr->proto;
332 packet->ttl = hdr->ttl;
333 packet->ident = ident;
334
335 packet->df = (flags_foff & BIT_V(uint16_t, FF_FLAG_DF)) != 0;
336 packet->mf = (flags_foff & BIT_V(uint16_t, FF_FLAG_MF)) != 0;
337 packet->offs = foff * FRAG_OFFS_UNIT;
338
339 /* XXX IP options */
340 size_t data_offs = sizeof(uint32_t) *
341 BIT_RANGE_EXTRACT(uint8_t, VI_IHL_h, VI_IHL_l, hdr->ver_ihl);
342
343 packet->size = tot_len - data_offs;
344 packet->data = calloc(packet->size, 1);
345 if (packet->data == NULL) {
346 log_msg(LOG_DEFAULT, LVL_WARN, "Out of memory.");
347 return ENOMEM;
348 }
349
350 memcpy(packet->data, (uint8_t *) data + data_offs, packet->size);
351
352 return EOK;
353}
354
355int inet_pdu_decode6(void *data, size_t size, inet_packet_t *packet)
356{
357 log_msg(LOG_DEFAULT, LVL_DEBUG, "inet_pdu_decode6()");
358
359 if (size < sizeof(ip6_header_t)) {
360 log_msg(LOG_DEFAULT, LVL_DEBUG, "PDU too short (%zu)", size);
361 return EINVAL;
362 }
363
364 ip6_header_t *hdr6 = (ip6_header_t *) data;
365
366 uint8_t version = BIT_RANGE_EXTRACT(uint8_t, VI_VERSION_h,
367 VI_VERSION_l, hdr6->ver_tc);
368 if (version != 6) {
369 log_msg(LOG_DEFAULT, LVL_DEBUG, "Version (%d) != 6", version);
370 return EINVAL;
371 }
372
373 size_t payload_len = uint16_t_be2host(hdr6->payload_len);
374 if (payload_len + sizeof(ip6_header_t) > size) {
375 log_msg(LOG_DEFAULT, LVL_DEBUG, "Total Length = %zu > PDU size = %zu",
376 payload_len + sizeof(ip6_header_t), size);
377 return EINVAL;
378 }
379
380#if 0
381 // FIXME TODO fragmentation
382
383 uint16_t ident = uint16_t_be2host(hdr->id);
384 uint16_t flags_foff = uint16_t_be2host(hdr->flags_foff);
385 uint16_t foff = BIT_RANGE_EXTRACT(uint16_t, FF_FRAGOFF_h, FF_FRAGOFF_l,
386 flags_foff);
387#endif
388
389 /* XXX Checksum */
390
391 addr128_t src;
392 addr128_t dest;
393
394 addr128_t_be2host(hdr6->src_addr, src);
395 inet_addr_set6(src, &packet->src);
396
397 addr128_t_be2host(hdr6->dest_addr, dest);
398 inet_addr_set6(dest, &packet->dest);
399
400 packet->tos = 0;
401 packet->proto = hdr6->next;
402 packet->ttl = hdr6->hop_limit;
403
404#if 0
405 // FIXME TODO fragmentation
406
407 packet->ident = ident;
408 packet->df = (flags_foff & BIT_V(uint16_t, FF_FLAG_DF)) != 0;
409 packet->mf = (flags_foff & BIT_V(uint16_t, FF_FLAG_MF)) != 0;
410 packet->offs = foff * FRAG_OFFS_UNIT;
411
412 /* XXX IP options */
413 size_t data_offs = sizeof(uint32_t) *
414 BIT_RANGE_EXTRACT(uint8_t, VI_IHL_h, VI_IHL_l, hdr->ver_ihl);
415#endif
416
417 packet->ident = 0;
418 packet->df = 0;
419 packet->mf = 0;
420 packet->offs = 0;
421
422 packet->size = payload_len;
423 packet->data = calloc(packet->size, 1);
424 if (packet->data == NULL) {
425 log_msg(LOG_DEFAULT, LVL_WARN, "Out of memory.");
426 return ENOMEM;
427 }
428
429 memcpy(packet->data, (uint8_t *) data + sizeof(ip6_header_t), packet->size);
430
431 return EOK;
432}
433
434int ndp_pdu_encode(ndp_packet_t *ndp, inet_dgram_t *dgram)
435{
436 inet_addr_set6(ndp->sender_proto_addr, &dgram->src);
437 inet_addr_set6(ndp->target_proto_addr, &dgram->dest);
438 dgram->tos = 0;
439 dgram->size = sizeof(icmpv6_message_t) + sizeof(ndp_message_t);
440
441 dgram->data = calloc(1, dgram->size);
442 if (dgram->data == NULL)
443 return ENOMEM;
444
445 icmpv6_message_t *icmpv6 = (icmpv6_message_t *) dgram->data;
446
447 icmpv6->type = ndp->opcode;
448 icmpv6->code = 0;
449 memset(icmpv6->un.ndp.reserved, 0, 3);
450
451 ndp_message_t *message = (ndp_message_t *) (icmpv6 + 1);
452
453 if (ndp->opcode == ICMPV6_NEIGHBOUR_SOLICITATION) {
454 host2addr128_t_be(ndp->solicited_ip, message->target_address);
455 message->option = 1;
456 icmpv6->un.ndp.flags = 0;
457 } else {
458 host2addr128_t_be(ndp->sender_proto_addr, message->target_address);
459 message->option = 2;
460 icmpv6->un.ndp.flags = NDP_FLAG_OVERRIDE | NDP_FLAG_SOLICITED;
461 }
462
463 message->length = 1;
464 addr48(ndp->sender_hw_addr, message->mac);
465
466 icmpv6_phdr_t phdr;
467
468 host2addr128_t_be(ndp->sender_proto_addr, phdr.src_addr);
469 host2addr128_t_be(ndp->target_proto_addr, phdr.dest_addr);
470 phdr.length = host2uint32_t_be(dgram->size);
471 memset(phdr.zeroes, 0, 3);
472 phdr.next = IP_PROTO_ICMPV6;
473
474 uint16_t cs_phdr =
475 inet_checksum_calc(INET_CHECKSUM_INIT, &phdr,
476 sizeof(icmpv6_phdr_t));
477
478 uint16_t cs_all = inet_checksum_calc(cs_phdr, dgram->data,
479 dgram->size);
480
481 icmpv6->checksum = host2uint16_t_be(cs_all);
482
483 return EOK;
484}
485
486int ndp_pdu_decode(inet_dgram_t *dgram, ndp_packet_t *ndp)
487{
488 uint16_t src_af = inet_addr_get(&dgram->src, NULL,
489 &ndp->sender_proto_addr);
490 if (src_af != AF_INET6)
491 return EINVAL;
492
493 if (dgram->size < sizeof(icmpv6_message_t) + sizeof(ndp_message_t))
494 return EINVAL;
495
496 icmpv6_message_t *icmpv6 = (icmpv6_message_t *) dgram->data;
497
498 ndp->opcode = icmpv6->type;
499
500 ndp_message_t *message = (ndp_message_t *) (icmpv6 + 1);
501
502 addr128_t_be2host(message->target_address, ndp->target_proto_addr);
503 addr48(message->mac, ndp->sender_hw_addr);
504
505 return EOK;
506}
507
508/** @}
509 */
Note: See TracBrowser for help on using the repository browser.