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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since a35b458 was a35b458, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 7 years ago

style: Remove trailing whitespace on _all_ lines, including empty ones, for particular file types.

Command used: tools/srepl '\s\+$' '' -- *.c *.h *.py *.sh *.s *.S *.ag

Currently, whitespace on empty lines is very inconsistent.
There are two basic choices: Either remove the whitespace, or keep empty lines
indented to the level of surrounding code. The former is AFAICT more common,
and also much easier to do automatically.

Alternatively, we could write script for automatic indentation, and use that
instead. However, if such a script exists, it's possible to use the indented
style locally, by having the editor apply relevant conversions on load/save,
without affecting remote repository. IMO, it makes more sense to adopt
the simpler rule.

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