2 * Copyright (c) 2012 Jiri Svoboda
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
39 #include <byteorder.h>
41 #include <fibril_synch.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
)
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
)
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
);
83 w
= ((uint16_t)bdata
[2*words
] << 8);
84 sum
= inet_ocadd16(sum
, w
);
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
)
117 size_t fragoff_limit
;
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
)
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
;
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
);
149 size
= hdr_size
+ xfer_size
;
151 /* Offset of remaining payload */
152 rem_offs
= offs
+ xfer_size
;
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);
164 /* Allocate identifier */
165 fibril_mutex_lock(&ip_ident_lock
);
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
;
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
);
187 memcpy((uint8_t *)data
+ data_offs
, packet
->data
+ offs
, xfer_size
);
196 int inet_pdu_decode(void *data
, size_t size
, inet_packet_t
*packet
)
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
);
213 hdr
= (ip_header_t
*)data
;
215 version
= BIT_RANGE_EXTRACT(uint8_t, VI_VERSION_h
, VI_VERSION_l
,
218 log_msg(LOG_DEFAULT
, LVL_DEBUG
, "Version (%d) != 4", version
);
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
);
228 if (tot_len
> size
) {
229 log_msg(LOG_DEFAULT
, LVL_DEBUG
, "Total Length = %zu > PDU size = %zu",
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
,
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
;
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.");
262 memcpy(packet
->data
, (uint8_t *)data
+ data_offs
, packet
->size
);