Get rid of log_log_msg()
[helenos.git] / uspace / srv / net / inetsrv / pdu.c
blob933c0b03f72794544e1fcf4967187644ad18d58a
1 /*
2 * Copyright (c) 2012 Jiri Svoboda
3 * All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
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.
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.
29 /** @addtogroup inet
30 * @{
32 /**
33 * @file
34 * @brief
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>
47 #include "inetsrv.h"
48 #include "inet_std.h"
49 #include "pdu.h"
51 static FIBRIL_MUTEX_INITIALIZE(ip_ident_lock);
52 static uint16_t ip_ident = 0;
54 /** One's complement addition.
56 * Result is a + b + carry.
58 static uint16_t inet_ocadd16(uint16_t a, uint16_t b)
60 uint32_t s;
62 s = (uint32_t)a + (uint32_t)b;
63 return (s & 0xffff) + (s >> 16);
66 uint16_t inet_checksum_calc(uint16_t ivalue, void *data, size_t size)
68 uint16_t sum;
69 uint16_t w;
70 size_t words, i;
71 uint8_t *bdata;
73 sum = ~ivalue;
74 words = size / 2;
75 bdata = (uint8_t *)data;
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);
82 if (size % 2 != 0) {
83 w = ((uint16_t)bdata[2*words] << 8);
84 sum = inet_ocadd16(sum, w);
87 return ~sum;
90 /** Encode Internet PDU.
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.
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
105 int inet_pdu_encode(inet_packet_t *packet, size_t offs, size_t mtu,
106 void **rdata, size_t *rsize, size_t *roffs)
108 void *data;
109 size_t size;
110 ip_header_t *hdr;
111 size_t hdr_size;
112 size_t data_offs;
113 uint16_t chksum;
114 uint16_t ident;
115 uint16_t flags_foff;
116 uint16_t foff;
117 size_t fragoff_limit;
118 size_t xfer_size;
119 size_t spc_avail;
120 size_t rem_offs;
122 /* Upper bound for fragment offset field */
123 fragoff_limit = 1 << (FF_FRAGOFF_h - FF_FRAGOFF_l);
125 /* Verify that total size of datagram is within reasonable bounds */
126 if (offs + packet->size > FRAG_OFFS_UNIT * fragoff_limit)
127 return ELIMIT;
129 hdr_size = sizeof(ip_header_t);
130 data_offs = ROUND_UP(hdr_size, 4);
132 assert(offs % FRAG_OFFS_UNIT == 0);
133 assert(offs / FRAG_OFFS_UNIT < fragoff_limit);
135 /* Value for the fragment offset field */
136 foff = offs / FRAG_OFFS_UNIT;
138 if (hdr_size >= mtu)
139 return EINVAL;
141 /* Amount of space in the PDU available for payload */
142 spc_avail = mtu - hdr_size;
143 spc_avail -= (spc_avail % FRAG_OFFS_UNIT);
145 /* Amount of data (payload) to transfer */
146 xfer_size = min(packet->size - offs, spc_avail);
148 /* Total PDU size */
149 size = hdr_size + xfer_size;
151 /* Offset of remaining payload */
152 rem_offs = offs + xfer_size;
154 /* Flags */
155 flags_foff =
156 (packet->df ? BIT_V(uint16_t, FF_FLAG_DF) : 0) +
157 (rem_offs < packet->size ? BIT_V(uint16_t, FF_FLAG_MF) : 0) +
158 (foff << FF_FRAGOFF_l);
160 data = calloc(size, 1);
161 if (data == NULL)
162 return ENOMEM;
164 /* Allocate identifier */
165 fibril_mutex_lock(&ip_ident_lock);
166 ident = ++ip_ident;
167 fibril_mutex_unlock(&ip_ident_lock);
169 /* Encode header fields */
170 hdr = (ip_header_t *)data;
171 hdr->ver_ihl = (4 << VI_VERSION_l) | (hdr_size / sizeof(uint32_t));
172 hdr->tos = packet->tos;
173 hdr->tot_len = host2uint16_t_be(size);
174 hdr->id = host2uint16_t_be(ident);
175 hdr->flags_foff = host2uint16_t_be(flags_foff);
176 hdr->ttl = packet->ttl;
177 hdr->proto = packet->proto;
178 hdr->chksum = 0;
179 hdr->src_addr = host2uint32_t_be(packet->src.ipv4);
180 hdr->dest_addr = host2uint32_t_be(packet->dest.ipv4);
182 /* Compute checksum */
183 chksum = inet_checksum_calc(INET_CHECKSUM_INIT, (void *)hdr, hdr_size);
184 hdr->chksum = host2uint16_t_be(chksum);
186 /* Copy payload */
187 memcpy((uint8_t *)data + data_offs, packet->data + offs, xfer_size);
189 *rdata = data;
190 *rsize = size;
191 *roffs = rem_offs;
193 return EOK;
196 int inet_pdu_decode(void *data, size_t size, inet_packet_t *packet)
198 ip_header_t *hdr;
199 size_t tot_len;
200 size_t data_offs;
201 uint8_t version;
202 uint16_t ident;
203 uint16_t flags_foff;
204 uint16_t foff;
206 log_msg(LOG_DEFAULT, LVL_DEBUG, "inet_pdu_decode()");
208 if (size < sizeof(ip_header_t)) {
209 log_msg(LOG_DEFAULT, LVL_DEBUG, "PDU too short (%zu)", size);
210 return EINVAL;
213 hdr = (ip_header_t *)data;
215 version = BIT_RANGE_EXTRACT(uint8_t, VI_VERSION_h, VI_VERSION_l,
216 hdr->ver_ihl);
217 if (version != 4) {
218 log_msg(LOG_DEFAULT, LVL_DEBUG, "Version (%d) != 4", version);
219 return EINVAL;
222 tot_len = uint16_t_be2host(hdr->tot_len);
223 if (tot_len < sizeof(ip_header_t)) {
224 log_msg(LOG_DEFAULT, LVL_DEBUG, "Total Length too small (%zu)", tot_len);
225 return EINVAL;
228 if (tot_len > size) {
229 log_msg(LOG_DEFAULT, LVL_DEBUG, "Total Length = %zu > PDU size = %zu",
230 tot_len, size);
231 return EINVAL;
234 ident = uint16_t_be2host(hdr->id);
235 flags_foff = uint16_t_be2host(hdr->flags_foff);
236 foff = BIT_RANGE_EXTRACT(uint16_t, FF_FRAGOFF_h, FF_FRAGOFF_l,
237 flags_foff);
238 /* XXX Checksum */
240 packet->src.ipv4 = uint32_t_be2host(hdr->src_addr);
241 packet->dest.ipv4 = uint32_t_be2host(hdr->dest_addr);
242 packet->tos = hdr->tos;
243 packet->proto = hdr->proto;
244 packet->ttl = hdr->ttl;
245 packet->ident = ident;
247 packet->df = (flags_foff & BIT_V(uint16_t, FF_FLAG_DF)) != 0;
248 packet->mf = (flags_foff & BIT_V(uint16_t, FF_FLAG_MF)) != 0;
249 packet->offs = foff * FRAG_OFFS_UNIT;
251 /* XXX IP options */
252 data_offs = sizeof(uint32_t) * BIT_RANGE_EXTRACT(uint8_t, VI_IHL_h,
253 VI_IHL_l, hdr->ver_ihl);
255 packet->size = tot_len - data_offs;
256 packet->data = calloc(packet->size, 1);
257 if (packet->data == NULL) {
258 log_msg(LOG_DEFAULT, LVL_WARN, "Out of memory.");
259 return ENOMEM;
262 memcpy(packet->data, (uint8_t *)data + data_offs, packet->size);
264 return EOK;
267 /** @}