blob: 5a57138ce32299c32bc1d2926e99d705cb0c3828 [file] [log] [blame]
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -07001/* @file
2 *
3 * This is the IP packet segmentation and reassembly implementation.
4 *
5 */
6
7/*
8 * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
9 * All rights reserved.
10 *
11 * Redistribution and use in source and binary forms, with or without modification,
12 * are permitted provided that the following conditions are met:
13 *
14 * 1. Redistributions of source code must retain the above copyright notice,
15 * this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright notice,
17 * this list of conditions and the following disclaimer in the documentation
18 * and/or other materials provided with the distribution.
19 * 3. The name of the author may not be used to endorse or promote products
20 * derived from this software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
23 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
24 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
25 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
26 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
27 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
30 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
31 * OF SUCH DAMAGE.
32 *
33 * This file is part of the lwIP TCP/IP stack.
34 *
35 * Author: Jani Monoses <jani@iv.ro>
36 * original reassembly code by Adam Dunkels <adam@sics.se>
37 *
38 */
39
40#include <string.h>
41
42#include "lwip/opt.h"
43/* #include "lwip/sys.h" */
44#include "lwip/ip.h"
45#include "lwip/ip_frag.h"
46#include "lwip/netif.h"
47#include "lwip/stats.h"
48
49
50/*
51 * Copy len bytes from offset in pbuf to buffer
52 *
53 * helper used by both ip_reass and ip_frag
54 */
55static struct pbuf *
56copy_from_pbuf(struct pbuf *p, u16_t * offset,
57 u8_t * buffer, u16_t len)
58{
59 u16_t l;
60
61 p->payload = (u8_t *)p->payload + *offset;
62 p->len -= *offset;
63 while (len) {
64 l = len < p->len ? len : p->len;
65 memcpy(buffer, p->payload, l);
66 buffer += l;
67 len -= l;
68 if (len)
69 p = p->next;
70 else
71 *offset = l;
72 }
73 return p;
74}
75
76#define IP_REASS_BUFSIZE 5760
77#define IP_REASS_MAXAGE 30
78#define IP_REASS_TMO 1000
79
80static u8_t ip_reassbuf[IP_HLEN + IP_REASS_BUFSIZE];
81static u8_t ip_reassbitmap[IP_REASS_BUFSIZE / (8 * 8) + 1];
82static const u8_t bitmap_bits[8] = { 0xff, 0x7f, 0x3f, 0x1f,
83 0x0f, 0x07, 0x03, 0x01
84};
85static u16_t ip_reasslen;
86static u8_t ip_reassflags;
87#define IP_REASS_FLAG_LASTFRAG 0x01
88
89static u8_t ip_reasstmr;
90
91/**
92 * Reassembly timer base function
93 * for both NO_SYS == 0 and 1 (!).
94 *
95 * Should be called every 1000 msec.
96 */
97void
98ip_reass_tmr(void)
99{
100 if (ip_reasstmr > 0) {
101 ip_reasstmr--;
102 }
103}
104
105/**
106 * Reassembles incoming IP fragments into an IP datagram.
107 *
108 * @param p points to a pbuf chain of the fragment
109 * @return NULL if reassembly is incomplete, ? otherwise
110 */
111struct pbuf *
112ip_reass(struct pbuf *p)
113{
114 struct pbuf *q;
115 struct ip_hdr *fraghdr, *iphdr;
116 u16_t offset, len;
117 u16_t i;
118
119 IPFRAG_STATS_INC(ip_frag.recv);
120
121 iphdr = (struct ip_hdr *) ip_reassbuf;
122 fraghdr = (struct ip_hdr *) p->payload;
123 /* If ip_reasstmr is zero, no packet is present in the buffer, so we
124 write the IP header of the fragment into the reassembly
125 buffer. The timer is updated with the maximum age. */
126 if (ip_reasstmr == 0) {
127 LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass: new packet\n"));
128 memcpy(iphdr, fraghdr, IP_HLEN);
129 ip_reasstmr = IP_REASS_MAXAGE;
130 ip_reassflags = 0;
131 /* Clear the bitmap. */
132 memset(ip_reassbitmap, 0, sizeof(ip_reassbitmap));
133 }
134
135 /* Check if the incoming fragment matches the one currently present
136 in the reasembly buffer. If so, we proceed with copying the
137 fragment into the buffer. */
138 if (ip_addr_cmp(&iphdr->src, &fraghdr->src) &&
139 ip_addr_cmp(&iphdr->dest, &fraghdr->dest) &&
140 IPH_ID(iphdr) == IPH_ID(fraghdr)) {
141 LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass: matching previous fragment ID=%"X16_F"\n",
142 ntohs(IPH_ID(fraghdr))));
143 IPFRAG_STATS_INC(ip_frag.cachehit);
144 /* Find out the offset in the reassembly buffer where we should
145 copy the fragment. */
146 len = ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4;
147 offset = (ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8;
148
149 /* If the offset or the offset + fragment length overflows the
150 reassembly buffer, we discard the entire packet. */
151 if (offset > IP_REASS_BUFSIZE || offset + len > IP_REASS_BUFSIZE) {
152 LWIP_DEBUGF(IP_REASS_DEBUG,
153 ("ip_reass: fragment outside of buffer (%"S16_F":%"S16_F"/%"S16_F").\n", offset,
154 offset + len, IP_REASS_BUFSIZE));
155 ip_reasstmr = 0;
156 goto nullreturn;
157 }
158
159 /* Copy the fragment into the reassembly buffer, at the right
160 offset. */
161 LWIP_DEBUGF(IP_REASS_DEBUG,
162 ("ip_reass: copying with offset %"S16_F" into %"S16_F":%"S16_F"\n", offset,
163 IP_HLEN + offset, IP_HLEN + offset + len));
164 i = IPH_HL(fraghdr) * 4;
165 copy_from_pbuf(p, &i, &ip_reassbuf[IP_HLEN + offset], len);
166
167 /* Update the bitmap. */
168 if (offset / (8 * 8) == (offset + len) / (8 * 8)) {
169 LWIP_DEBUGF(IP_REASS_DEBUG,
170 ("ip_reass: updating single byte in bitmap.\n"));
171 /* If the two endpoints are in the same byte, we only update that byte. */
172 LWIP_ASSERT("offset / (8 * 8) < sizeof(ip_reassbitmap)",
173 offset / (8 * 8) < sizeof(ip_reassbitmap));
174 ip_reassbitmap[offset / (8 * 8)] |=
175 bitmap_bits[(offset / 8) & 7] &
176 ~bitmap_bits[((offset + len) / 8) & 7];
177 } else {
178 /* If the two endpoints are in different bytes, we update the
179 bytes in the endpoints and fill the stuff inbetween with
180 0xff. */
181 LWIP_ASSERT("offset / (8 * 8) < sizeof(ip_reassbitmap)",
182 offset / (8 * 8) < sizeof(ip_reassbitmap));
183 ip_reassbitmap[offset / (8 * 8)] |= bitmap_bits[(offset / 8) & 7];
184 LWIP_DEBUGF(IP_REASS_DEBUG,
185 ("ip_reass: updating many bytes in bitmap (%"S16_F":%"S16_F").\n",
186 1 + offset / (8 * 8), (offset + len) / (8 * 8)));
187 for (i = 1 + offset / (8 * 8); i < (offset + len) / (8 * 8); ++i) {
188 ip_reassbitmap[i] = 0xff;
189 }
190 LWIP_ASSERT("(offset + len) / (8 * 8) < sizeof(ip_reassbitmap)",
191 (offset + len) / (8 * 8) < sizeof(ip_reassbitmap));
192 ip_reassbitmap[(offset + len) / (8 * 8)] |=
193 ~bitmap_bits[((offset + len) / 8) & 7];
194 }
195
196 /* If this fragment has the More Fragments flag set to zero, we
197 know that this is the last fragment, so we can calculate the
198 size of the entire packet. We also set the
199 IP_REASS_FLAG_LASTFRAG flag to indicate that we have received
200 the final fragment. */
201
202 if ((ntohs(IPH_OFFSET(fraghdr)) & IP_MF) == 0) {
203 ip_reassflags |= IP_REASS_FLAG_LASTFRAG;
204 ip_reasslen = offset + len;
205 LWIP_DEBUGF(IP_REASS_DEBUG,
206 ("ip_reass: last fragment seen, total len %"S16_F"\n",
207 ip_reasslen));
208 }
209
210 /* Finally, we check if we have a full packet in the buffer. We do
211 this by checking if we have the last fragment and if all bits
212 in the bitmap are set. */
213 if (ip_reassflags & IP_REASS_FLAG_LASTFRAG) {
214 /* Check all bytes up to and including all but the last byte in
215 the bitmap. */
216 LWIP_ASSERT("ip_reasslen / (8 * 8) - 1 < sizeof(ip_reassbitmap)",
217 ip_reasslen / (8 * 8) - 1 < sizeof(ip_reassbitmap));
218 for (i = 0; i < ip_reasslen / (8 * 8) - 1; ++i) {
219 if (ip_reassbitmap[i] != 0xff) {
220 LWIP_DEBUGF(IP_REASS_DEBUG,
221 ("ip_reass: last fragment seen, bitmap %"S16_F"/%"S16_F" failed (%"X16_F")\n",
222 i, ip_reasslen / (8 * 8) - 1, ip_reassbitmap[i]));
223 goto nullreturn;
224 }
225 }
226 /* Check the last byte in the bitmap. It should contain just the
227 right amount of bits. */
228 LWIP_ASSERT("ip_reasslen / (8 * 8) < sizeof(ip_reassbitmap)",
229 ip_reasslen / (8 * 8) < sizeof(ip_reassbitmap));
230 if (ip_reassbitmap[ip_reasslen / (8 * 8)] !=
231 (u8_t) ~ bitmap_bits[ip_reasslen / 8 & 7]) {
232 LWIP_DEBUGF(IP_REASS_DEBUG,
233 ("ip_reass: last fragment seen, bitmap %"S16_F" didn't contain %"X16_F" (%"X16_F")\n",
234 ip_reasslen / (8 * 8), ~bitmap_bits[ip_reasslen / 8 & 7],
235 ip_reassbitmap[ip_reasslen / (8 * 8)]));
236 goto nullreturn;
237 }
238
239 /* Pretend to be a "normal" (i.e., not fragmented) IP packet
240 from now on. */
241 ip_reasslen += IP_HLEN;
242
243 IPH_LEN_SET(iphdr, htons(ip_reasslen));
244 IPH_OFFSET_SET(iphdr, 0);
245 IPH_CHKSUM_SET(iphdr, 0);
246 IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN));
247
248 /* If we have come this far, we have a full packet in the
249 buffer, so we allocate a pbuf and copy the packet into it. We
250 also reset the timer. */
251 ip_reasstmr = 0;
252 pbuf_free(p);
253 p = pbuf_alloc(PBUF_LINK, ip_reasslen, PBUF_POOL);
254 if (p != NULL) {
255 i = 0;
256 for (q = p; q != NULL; q = q->next) {
257 /* Copy enough bytes to fill this pbuf in the chain. The
258 available data in the pbuf is given by the q->len variable. */
259 LWIP_DEBUGF(IP_REASS_DEBUG,
260 ("ip_reass: memcpy from %p (%"S16_F") to %p, %"S16_F" bytes\n",
261 (void *)&ip_reassbuf[i], i, q->payload,
262 q->len > ip_reasslen - i ? ip_reasslen - i : q->len));
263 memcpy(q->payload, &ip_reassbuf[i],
264 q->len > ip_reasslen - i ? ip_reasslen - i : q->len);
265 i += q->len;
266 }
267 IPFRAG_STATS_INC(ip_frag.fw);
268 } else {
269 IPFRAG_STATS_INC(ip_frag.memerr);
270 }
271 LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass: p %p\n", (void*)p));
272 return p;
273 }
274 }
275
276nullreturn:
277 IPFRAG_STATS_INC(ip_frag.drop);
278 pbuf_free(p);
279 return NULL;
280}
281
282#define MAX_MTU 1500
283static u8_t buf[MEM_ALIGN_SIZE(MAX_MTU)];
284
285/**
286 * Fragment an IP datagram if too large for the netif.
287 *
288 * Chop the datagram in MTU sized chunks and send them in order
289 * by using a fixed size static memory buffer (PBUF_ROM)
290 */
291err_t
292ip_frag(struct pbuf *p, struct netif *netif, struct ip_addr *dest)
293{
294 struct pbuf *rambuf;
295 struct pbuf *header;
296 struct ip_hdr *iphdr;
297 u16_t nfb = 0;
298 u16_t left, cop;
299 u16_t mtu = netif->mtu;
300 u16_t ofo, omf;
301 u16_t last;
302 u16_t poff = IP_HLEN;
303 u16_t tmp;
304
305 /* Get a RAM based MTU sized pbuf */
306 rambuf = pbuf_alloc(PBUF_LINK, 0, PBUF_REF);
307 if (rambuf == NULL) {
308 return ERR_MEM;
309 }
310 rambuf->tot_len = rambuf->len = mtu;
311 rambuf->payload = MEM_ALIGN((void *)buf);
312
313 /* Copy the IP header in it */
314 iphdr = rambuf->payload;
315 memcpy(iphdr, p->payload, IP_HLEN);
316
317 /* Save original offset */
318 tmp = ntohs(IPH_OFFSET(iphdr));
319 ofo = tmp & IP_OFFMASK;
320 omf = tmp & IP_MF;
321
322 left = p->tot_len - IP_HLEN;
323
324 while (left) {
325 last = (left <= mtu - IP_HLEN);
326
327 /* Set new offset and MF flag */
328 ofo += nfb;
329 tmp = omf | (IP_OFFMASK & (ofo));
330 if (!last)
331 tmp = tmp | IP_MF;
332 IPH_OFFSET_SET(iphdr, htons(tmp));
333
334 /* Fill this fragment */
335 nfb = (mtu - IP_HLEN) / 8;
336 cop = last ? left : nfb * 8;
337
338 p = copy_from_pbuf(p, &poff, (u8_t *) iphdr + IP_HLEN, cop);
339
340 /* Correct header */
341 IPH_LEN_SET(iphdr, htons(cop + IP_HLEN));
342 IPH_CHKSUM_SET(iphdr, 0);
343 IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN));
344
345 if (last)
346 pbuf_realloc(rambuf, left + IP_HLEN);
347 /* This part is ugly: we alloc a RAM based pbuf for
348 * the link level header for each chunk and then
349 * free it.A PBUF_ROM style pbuf for which pbuf_header
350 * worked would make things simpler.
351 */
352 header = pbuf_alloc(PBUF_LINK, 0, PBUF_RAM);
353 if (header != NULL) {
354 pbuf_chain(header, rambuf);
355 netif->output(netif, header, dest);
356 IPFRAG_STATS_INC(ip_frag.xmit);
357 pbuf_free(header);
358 } else {
359 pbuf_free(rambuf);
360 return ERR_MEM;
361 }
362 left -= cop;
363 }
364 pbuf_free(rambuf);
365 return ERR_OK;
366}