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

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

add basic infrastructure for IPv6 (inactive)
make inet_addr_t a universal address type

  • Property mode set to 100644
File size: 7.6 KB
RevLine 
[ceba4bed]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
[e767dbf]37#include <align.h>
38#include <bitops.h>
39#include <byteorder.h>
[ceba4bed]40#include <errno.h>
[1cc8b42]41#include <fibril_synch.h>
[e767dbf]42#include <io/log.h>
[347768d]43#include <macros.h>
[e767dbf]44#include <mem.h>
[ceba4bed]45#include <stdlib.h>
[02a09ed]46#include <net/socket_codes.h>
[b4ec1ea]47#include "inetsrv.h"
[e767dbf]48#include "inet_std.h"
[ceba4bed]49#include "pdu.h"
50
[1cc8b42]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
[637a3b4]66uint16_t inet_checksum_calc(uint16_t ivalue, void *data, size_t size)
[1cc8b42]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
[ceba4bed]90/** Encode Internet PDU.
[347768d]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 offs Offset into packet payload (in bytes)
100 * @param mtu MTU (Maximum Transmission Unit) in bytes
101 * @param rdata Place to store pointer to allocated data buffer
102 * @param rsize Place to store size of allocated data buffer
103 * @param roffs Place to store offset of remaning data
[ceba4bed]104 */
[347768d]105int inet_pdu_encode(inet_packet_t *packet, size_t offs, size_t mtu,
106 void **rdata, size_t *rsize, size_t *roffs)
[ceba4bed]107{
[02a09ed]108 addr32_t src_v4;
109 addr128_t src_v6;
110 uint16_t src_af = inet_addr_get(&packet->src, &src_v4, &src_v6);
111
112 addr32_t dest_v4;
113 addr128_t dest_v6;
114 uint16_t dest_af = inet_addr_get(&packet->dest, &dest_v4, &dest_v6);
[a2e3ee6]115
[02a09ed]116 if (src_af != dest_af)
117 return EINVAL;
[a2e3ee6]118
[347768d]119 /* Upper bound for fragment offset field */
[a2e3ee6]120 size_t fragoff_limit = 1 << (FF_FRAGOFF_h - FF_FRAGOFF_l);
121
[347768d]122 /* Verify that total size of datagram is within reasonable bounds */
123 if (offs + packet->size > FRAG_OFFS_UNIT * fragoff_limit)
124 return ELIMIT;
[a2e3ee6]125
[02a09ed]126 size_t hdr_size;
127
128 switch (src_af) {
129 case AF_INET:
130 hdr_size = sizeof(ip_header_t);
131 break;
132 case AF_INET6:
133 // FIXME TODO
134 assert(false);
135 default:
136 assert(false);
137 }
138
[a2e3ee6]139 size_t data_offs = ROUND_UP(hdr_size, 4);
140
[347768d]141 assert(offs % FRAG_OFFS_UNIT == 0);
142 assert(offs / FRAG_OFFS_UNIT < fragoff_limit);
[a2e3ee6]143
[347768d]144 /* Value for the fragment offset field */
[a2e3ee6]145 uint16_t foff = offs / FRAG_OFFS_UNIT;
146
[347768d]147 if (hdr_size >= mtu)
148 return EINVAL;
[a2e3ee6]149
[347768d]150 /* Amount of space in the PDU available for payload */
[a2e3ee6]151 size_t spc_avail = mtu - hdr_size;
[7fda2e0]152 spc_avail -= (spc_avail % FRAG_OFFS_UNIT);
[a2e3ee6]153
[347768d]154 /* Amount of data (payload) to transfer */
[a2e3ee6]155 size_t xfer_size = min(packet->size - offs, spc_avail);
156
[347768d]157 /* Total PDU size */
[a2e3ee6]158 size_t size = hdr_size + xfer_size;
159
[347768d]160 /* Offset of remaining payload */
[a2e3ee6]161 size_t rem_offs = offs + xfer_size;
162
[347768d]163 /* Flags */
[a2e3ee6]164 uint16_t flags_foff =
[347768d]165 (packet->df ? BIT_V(uint16_t, FF_FLAG_DF) : 0) +
166 (rem_offs < packet->size ? BIT_V(uint16_t, FF_FLAG_MF) : 0) +
167 (foff << FF_FRAGOFF_l);
[a2e3ee6]168
169 void *data = calloc(size, 1);
[ceba4bed]170 if (data == NULL)
171 return ENOMEM;
[a2e3ee6]172
[347768d]173 /* Allocate identifier */
174 fibril_mutex_lock(&ip_ident_lock);
[a2e3ee6]175 uint16_t ident = ++ip_ident;
[347768d]176 fibril_mutex_unlock(&ip_ident_lock);
[a2e3ee6]177
[347768d]178 /* Encode header fields */
[02a09ed]179 ip_header_t *hdr;
[a2e3ee6]180
[02a09ed]181 switch (src_af) {
182 case AF_INET:
183 hdr = (ip_header_t *) data;
184
185 hdr->ver_ihl =
186 (4 << VI_VERSION_l) | (hdr_size / sizeof(uint32_t));
187 hdr->tos = packet->tos;
188 hdr->tot_len = host2uint16_t_be(size);
189 hdr->id = host2uint16_t_be(ident);
190 hdr->flags_foff = host2uint16_t_be(flags_foff);
191 hdr->ttl = packet->ttl;
192 hdr->proto = packet->proto;
193 hdr->chksum = 0;
194 hdr->src_addr = host2uint32_t_be(src_v4);
195 hdr->dest_addr = host2uint32_t_be(dest_v4);
196
197 /* Compute checksum */
198 uint16_t chksum = inet_checksum_calc(INET_CHECKSUM_INIT,
199 (void *) hdr, hdr_size);
200 hdr->chksum = host2uint16_t_be(chksum);
201
202 break;
203 case AF_INET6:
204 // FIXME TODO
205 return ENOTSUP;
206 default:
207 assert(false);
208 }
[a2e3ee6]209
[347768d]210 /* Copy payload */
[a2e3ee6]211 memcpy((uint8_t *) data + data_offs, packet->data + offs, xfer_size);
212
[ceba4bed]213 *rdata = data;
214 *rsize = size;
[347768d]215 *roffs = rem_offs;
[a2e3ee6]216
[ceba4bed]217 return EOK;
218}
219
[fe4310f]220int inet_pdu_decode(void *data, size_t size, inet_packet_t *packet)
[e767dbf]221{
[a1a101d]222 log_msg(LOG_DEFAULT, LVL_DEBUG, "inet_pdu_decode()");
[257feec]223
[e767dbf]224 if (size < sizeof(ip_header_t)) {
[a1a101d]225 log_msg(LOG_DEFAULT, LVL_DEBUG, "PDU too short (%zu)", size);
[e767dbf]226 return EINVAL;
227 }
[02a09ed]228
229 ip_header_t *hdr = (ip_header_t *) data;
230
231 uint8_t version = BIT_RANGE_EXTRACT(uint8_t, VI_VERSION_h,
232 VI_VERSION_l, hdr->ver_ihl);
[e767dbf]233 if (version != 4) {
[a1a101d]234 log_msg(LOG_DEFAULT, LVL_DEBUG, "Version (%d) != 4", version);
[e767dbf]235 return EINVAL;
236 }
[02a09ed]237
238 size_t tot_len = uint16_t_be2host(hdr->tot_len);
[e767dbf]239 if (tot_len < sizeof(ip_header_t)) {
[a1a101d]240 log_msg(LOG_DEFAULT, LVL_DEBUG, "Total Length too small (%zu)", tot_len);
[e767dbf]241 return EINVAL;
242 }
[257feec]243
[e767dbf]244 if (tot_len > size) {
[a1a101d]245 log_msg(LOG_DEFAULT, LVL_DEBUG, "Total Length = %zu > PDU size = %zu",
[e767dbf]246 tot_len, size);
247 return EINVAL;
248 }
[02a09ed]249
250 uint16_t ident = uint16_t_be2host(hdr->id);
251 uint16_t flags_foff = uint16_t_be2host(hdr->flags_foff);
252 uint16_t foff = BIT_RANGE_EXTRACT(uint16_t, FF_FRAGOFF_h, FF_FRAGOFF_l,
[7f95c904]253 flags_foff);
[e767dbf]254 /* XXX Checksum */
[02a09ed]255
256 inet_addr_set(uint32_t_be2host(hdr->src_addr), &packet->src);
257 inet_addr_set(uint32_t_be2host(hdr->dest_addr), &packet->dest);
[fe4310f]258 packet->tos = hdr->tos;
[2ff150e]259 packet->proto = hdr->proto;
[fe4310f]260 packet->ttl = hdr->ttl;
[7f95c904]261 packet->ident = ident;
[257feec]262
[7f95c904]263 packet->df = (flags_foff & BIT_V(uint16_t, FF_FLAG_DF)) != 0;
264 packet->mf = (flags_foff & BIT_V(uint16_t, FF_FLAG_MF)) != 0;
265 packet->offs = foff * FRAG_OFFS_UNIT;
[257feec]266
[e767dbf]267 /* XXX IP options */
[02a09ed]268 size_t data_offs = sizeof(uint32_t) *
269 BIT_RANGE_EXTRACT(uint8_t, VI_IHL_h, VI_IHL_l, hdr->ver_ihl);
270
[fe4310f]271 packet->size = tot_len - data_offs;
272 packet->data = calloc(packet->size, 1);
273 if (packet->data == NULL) {
[a1a101d]274 log_msg(LOG_DEFAULT, LVL_WARN, "Out of memory.");
[e767dbf]275 return ENOMEM;
276 }
[257feec]277
278 memcpy(packet->data, (uint8_t *) data + data_offs, packet->size);
279
[e767dbf]280 return EOK;
281}
282
[ceba4bed]283/** @}
284 */
Note: See TracBrowser for help on using the repository browser.