FreeRTOS
[armadillo_firmware.git] / FreeRTOS / Common / ethernet / lwIP / core / ipv4 / ip_frag.c
blob0256a5020a52507e5f93201f151dae8f11e2cc96
1 /* @file
2 *
3 * This is the IP packet segmentation and reassembly implementation.
5 */
7 /*
8 * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
9 * All rights reserved.
11 * Redistribution and use in source and binary forms, with or without modification,
12 * are permitted provided that the following conditions are met:
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.
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.
33 * This file is part of the lwIP TCP/IP stack.
35 * Author: Jani Monoses <jani@iv.ro>
36 * original reassembly code by Adam Dunkels <adam@sics.se>
40 #include <string.h>
42 #include "lwip/opt.h"
43 #include "lwip/ip.h"
44 #include "lwip/ip_frag.h"
45 #include "lwip/netif.h"
46 #include "lwip/snmp.h"
47 #include "lwip/stats.h"
49 static u8_t ip_reassbuf[IP_HLEN + IP_REASS_BUFSIZE];
50 static u8_t ip_reassbitmap[IP_REASS_BUFSIZE / (8 * 8) + 1];
51 static const u8_t bitmap_bits[8] = { 0xff, 0x7f, 0x3f, 0x1f,
52 0x0f, 0x07, 0x03, 0x01
54 static u16_t ip_reasslen;
55 static u8_t ip_reassflags;
56 #define IP_REASS_FLAG_LASTFRAG 0x01
58 static u8_t ip_reasstmr;
61 * Copy len bytes from offset in pbuf to buffer
63 * helper used by both ip_reass and ip_frag
65 static struct pbuf *
66 copy_from_pbuf(struct pbuf *p, u16_t * offset,
67 u8_t * buffer, u16_t len)
69 u16_t l;
71 p->payload = (u8_t *)p->payload + *offset;
72 p->len -= *offset;
73 while (len) {
74 l = len < p->len ? len : p->len;
75 memcpy(buffer, p->payload, l);
76 buffer += l;
77 len -= l;
78 if (len)
79 p = p->next;
80 else
81 *offset = l;
83 return p;
87 /**
88 * Initializes IP reassembly and fragmentation states.
90 void
91 ip_frag_init(void)
93 ip_reasstmr = 0;
94 ip_reassflags = 0;
95 ip_reasslen = 0;
96 memset(ip_reassbitmap, 0, sizeof(ip_reassbitmap));
99 /**
100 * Reassembly timer base function
101 * for both NO_SYS == 0 and 1 (!).
103 * Should be called every 1000 msec.
105 void
106 ip_reass_tmr(void)
108 if (ip_reasstmr > 0) {
109 ip_reasstmr--;
110 LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass_tmr: timer dec %"U16_F"\n",(u16_t)ip_reasstmr));
111 if (ip_reasstmr == 0) {
112 /* reassembly timed out */
113 snmp_inc_ipreasmfails();
119 * Reassembles incoming IP fragments into an IP datagram.
121 * @param p points to a pbuf chain of the fragment
122 * @return NULL if reassembly is incomplete, ? otherwise
124 struct pbuf *
125 ip_reass(struct pbuf *p)
127 struct pbuf *q;
128 struct ip_hdr *fraghdr, *iphdr;
129 u16_t offset, len;
130 u16_t i;
132 IPFRAG_STATS_INC(ip_frag.recv);
133 snmp_inc_ipreasmreqds();
135 iphdr = (struct ip_hdr *) ip_reassbuf;
136 fraghdr = (struct ip_hdr *) p->payload;
137 /* If ip_reasstmr is zero, no packet is present in the buffer, so we
138 write the IP header of the fragment into the reassembly
139 buffer. The timer is updated with the maximum age. */
140 if (ip_reasstmr == 0) {
141 LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass: new packet\n"));
142 memcpy(iphdr, fraghdr, IP_HLEN);
143 ip_reasstmr = IP_REASS_MAXAGE;
144 ip_reassflags = 0;
145 /* Clear the bitmap. */
146 memset(ip_reassbitmap, 0, sizeof(ip_reassbitmap));
149 /* Check if the incoming fragment matches the one currently present
150 in the reasembly buffer. If so, we proceed with copying the
151 fragment into the buffer. */
152 if (ip_addr_cmp(&iphdr->src, &fraghdr->src) &&
153 ip_addr_cmp(&iphdr->dest, &fraghdr->dest) &&
154 IPH_ID(iphdr) == IPH_ID(fraghdr)) {
155 LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass: matching previous fragment ID=%"X16_F"\n",
156 ntohs(IPH_ID(fraghdr))));
157 IPFRAG_STATS_INC(ip_frag.cachehit);
158 /* Find out the offset in the reassembly buffer where we should
159 copy the fragment. */
160 len = ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4;
161 offset = (ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8;
163 /* If the offset or the offset + fragment length overflows the
164 reassembly buffer, we discard the entire packet. */
165 if ((offset > IP_REASS_BUFSIZE) || ((offset + len) > IP_REASS_BUFSIZE)) {
166 LWIP_DEBUGF(IP_REASS_DEBUG,
167 ("ip_reass: fragment outside of buffer (%"S16_F":%"S16_F"/%"S16_F").\n", offset,
168 offset + len, IP_REASS_BUFSIZE));
169 ip_reasstmr = 0;
170 snmp_inc_ipreasmfails();
171 goto nullreturn;
174 /* Copy the fragment into the reassembly buffer, at the right
175 offset. */
176 LWIP_DEBUGF(IP_REASS_DEBUG,
177 ("ip_reass: copying with offset %"S16_F" into %"S16_F":%"S16_F"\n", offset,
178 IP_HLEN + offset, IP_HLEN + offset + len));
179 i = IPH_HL(fraghdr) * 4;
180 copy_from_pbuf(p, &i, &ip_reassbuf[IP_HLEN + offset], len);
182 /* Update the bitmap. */
183 if (offset / (8 * 8) == (offset + len) / (8 * 8)) {
184 LWIP_DEBUGF(IP_REASS_DEBUG,
185 ("ip_reass: updating single byte in bitmap.\n"));
186 /* If the two endpoints are in the same byte, we only update that byte. */
187 LWIP_ASSERT("offset / (8 * 8) < sizeof(ip_reassbitmap)",
188 offset / (8 * 8) < sizeof(ip_reassbitmap));
189 ip_reassbitmap[offset / (8 * 8)] |=
190 bitmap_bits[(offset / 8) & 7] &
191 ~bitmap_bits[((offset + len) / 8) & 7];
192 } else {
193 /* If the two endpoints are in different bytes, we update the
194 bytes in the endpoints and fill the stuff inbetween with
195 0xff. */
196 LWIP_ASSERT("offset / (8 * 8) < sizeof(ip_reassbitmap)",
197 offset / (8 * 8) < sizeof(ip_reassbitmap));
198 ip_reassbitmap[offset / (8 * 8)] |= bitmap_bits[(offset / 8) & 7];
199 LWIP_DEBUGF(IP_REASS_DEBUG,
200 ("ip_reass: updating many bytes in bitmap (%"S16_F":%"S16_F").\n",
201 1 + offset / (8 * 8), (offset + len) / (8 * 8)));
202 for (i = 1 + offset / (8 * 8); i < (offset + len) / (8 * 8); ++i) {
203 ip_reassbitmap[i] = 0xff;
205 LWIP_ASSERT("(offset + len) / (8 * 8) < sizeof(ip_reassbitmap)",
206 (offset + len) / (8 * 8) < sizeof(ip_reassbitmap));
207 ip_reassbitmap[(offset + len) / (8 * 8)] |=
208 ~bitmap_bits[((offset + len) / 8) & 7];
211 /* If this fragment has the More Fragments flag set to zero, we
212 know that this is the last fragment, so we can calculate the
213 size of the entire packet. We also set the
214 IP_REASS_FLAG_LASTFRAG flag to indicate that we have received
215 the final fragment. */
217 if ((ntohs(IPH_OFFSET(fraghdr)) & IP_MF) == 0) {
218 ip_reassflags |= IP_REASS_FLAG_LASTFRAG;
219 ip_reasslen = offset + len;
220 LWIP_DEBUGF(IP_REASS_DEBUG,
221 ("ip_reass: last fragment seen, total len %"S16_F"\n",
222 ip_reasslen));
225 /* Finally, we check if we have a full packet in the buffer. We do
226 this by checking if we have the last fragment and if all bits
227 in the bitmap are set. */
228 if (ip_reassflags & IP_REASS_FLAG_LASTFRAG) {
229 /* Check all bytes up to and including all but the last byte in
230 the bitmap. */
231 LWIP_ASSERT("ip_reasslen / (8 * 8) - 1 < sizeof(ip_reassbitmap)",
232 ip_reasslen / (8 * 8) - 1 < ((u16_t) sizeof(ip_reassbitmap)));
233 for (i = 0; i < ip_reasslen / (8 * 8) - 1; ++i) {
234 if (ip_reassbitmap[i] != 0xff) {
235 LWIP_DEBUGF(IP_REASS_DEBUG,
236 ("ip_reass: last fragment seen, bitmap %"S16_F"/%"S16_F" failed (%"X16_F")\n",
237 i, ip_reasslen / (8 * 8) - 1, ip_reassbitmap[i]));
238 goto nullreturn;
241 /* Check the last byte in the bitmap. It should contain just the
242 right amount of bits. */
243 LWIP_ASSERT("ip_reasslen / (8 * 8) < sizeof(ip_reassbitmap)",
244 ip_reasslen / (8 * 8) < sizeof(ip_reassbitmap));
245 if (ip_reassbitmap[ip_reasslen / (8 * 8)] !=
246 (u8_t) ~ bitmap_bits[ip_reasslen / 8 & 7]) {
247 LWIP_DEBUGF(IP_REASS_DEBUG,
248 ("ip_reass: last fragment seen, bitmap %"S16_F" didn't contain %"X16_F" (%"X16_F")\n",
249 ip_reasslen / (8 * 8), ~bitmap_bits[ip_reasslen / 8 & 7],
250 ip_reassbitmap[ip_reasslen / (8 * 8)]));
251 goto nullreturn;
254 /* Pretend to be a "normal" (i.e., not fragmented) IP packet
255 from now on. */
256 ip_reasslen += IP_HLEN;
258 IPH_LEN_SET(iphdr, htons(ip_reasslen));
259 IPH_OFFSET_SET(iphdr, 0);
260 IPH_CHKSUM_SET(iphdr, 0);
261 IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN));
263 /* If we have come this far, we have a full packet in the
264 buffer, so we allocate a pbuf and copy the packet into it. We
265 also reset the timer. */
266 ip_reasstmr = 0;
267 pbuf_free(p);
268 p = pbuf_alloc(PBUF_LINK, ip_reasslen, PBUF_POOL);
269 if (p != NULL) {
270 i = 0;
271 for (q = p; q != NULL; q = q->next) {
272 /* Copy enough bytes to fill this pbuf in the chain. The
273 available data in the pbuf is given by the q->len variable. */
274 LWIP_DEBUGF(IP_REASS_DEBUG,
275 ("ip_reass: memcpy from %p (%"S16_F") to %p, %"S16_F" bytes\n",
276 (void *)&ip_reassbuf[i], i, q->payload,
277 q->len > ip_reasslen - i ? ip_reasslen - i : q->len));
278 memcpy(q->payload, &ip_reassbuf[i],
279 q->len > ip_reasslen - i ? ip_reasslen - i : q->len);
280 i += q->len;
282 IPFRAG_STATS_INC(ip_frag.fw);
283 snmp_inc_ipreasmoks();
284 } else {
285 LWIP_DEBUGF(IP_REASS_DEBUG,
286 ("ip_reass: pbuf_alloc(PBUF_LINK, ip_reasslen=%"U16_F", PBUF_POOL) failed\n", ip_reasslen));
287 IPFRAG_STATS_INC(ip_frag.memerr);
288 snmp_inc_ipreasmfails();
290 LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass: p %p\n", (void*)p));
291 return p;
295 nullreturn:
296 IPFRAG_STATS_INC(ip_frag.drop);
297 pbuf_free(p);
298 return NULL;
301 static u8_t buf[MEM_ALIGN_SIZE(IP_FRAG_MAX_MTU)];
304 * Fragment an IP datagram if too large for the netif.
306 * Chop the datagram in MTU sized chunks and send them in order
307 * by using a fixed size static memory buffer (PBUF_ROM)
309 err_t
310 ip_frag(struct pbuf *p, struct netif *netif, struct ip_addr *dest)
312 struct pbuf *rambuf;
313 struct pbuf *header;
314 struct ip_hdr *iphdr;
315 u16_t nfb = 0;
316 u16_t left, cop;
317 u16_t mtu = netif->mtu;
318 u16_t ofo, omf;
319 u16_t last;
320 u16_t poff = IP_HLEN;
321 u16_t tmp;
323 /* Get a RAM based MTU sized pbuf */
324 rambuf = pbuf_alloc(PBUF_LINK, 0, PBUF_REF);
325 if (rambuf == NULL) {
326 LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_frag: pbuf_alloc(PBUF_LINK, 0, PBUF_REF) failed\n"));
327 return ERR_MEM;
329 rambuf->tot_len = rambuf->len = mtu;
330 rambuf->payload = MEM_ALIGN((void *)buf);
332 /* Copy the IP header in it */
333 iphdr = rambuf->payload;
334 memcpy(iphdr, p->payload, IP_HLEN);
336 /* Save original offset */
337 tmp = ntohs(IPH_OFFSET(iphdr));
338 ofo = tmp & IP_OFFMASK;
339 omf = tmp & IP_MF;
341 left = p->tot_len - IP_HLEN;
343 while (left) {
344 last = (left <= mtu - IP_HLEN);
346 /* Set new offset and MF flag */
347 ofo += nfb;
348 tmp = omf | (IP_OFFMASK & (ofo));
349 if (!last)
350 tmp = tmp | IP_MF;
351 IPH_OFFSET_SET(iphdr, htons(tmp));
353 /* Fill this fragment */
354 nfb = (mtu - IP_HLEN) / 8;
355 cop = last ? left : nfb * 8;
357 p = copy_from_pbuf(p, &poff, (u8_t *) iphdr + IP_HLEN, cop);
359 /* Correct header */
360 IPH_LEN_SET(iphdr, htons(cop + IP_HLEN));
361 IPH_CHKSUM_SET(iphdr, 0);
362 IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN));
364 if (last)
365 pbuf_realloc(rambuf, left + IP_HLEN);
366 /* This part is ugly: we alloc a RAM based pbuf for
367 * the link level header for each chunk and then
368 * free it.A PBUF_ROM style pbuf for which pbuf_header
369 * worked would make things simpler.
371 header = pbuf_alloc(PBUF_LINK, 0, PBUF_RAM);
372 if (header != NULL) {
373 pbuf_chain(header, rambuf);
374 netif->output(netif, header, dest);
375 IPFRAG_STATS_INC(ip_frag.xmit);
376 snmp_inc_ipfragcreates();
377 pbuf_free(header);
378 } else {
379 LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_frag: pbuf_alloc() for header failed\n"));
380 pbuf_free(rambuf);
381 return ERR_MEM;
383 left -= cop;
385 pbuf_free(rambuf);
386 snmp_inc_ipfragoks();
387 return ERR_OK;