]> git.sur5r.net Git - freertos/blob - Demo/Common/ethernet/lwIP/core/ipv4/ip_frag.c
c43422ca413186eb7ce50c7132ef49c8e167efad
[freertos] / Demo / Common / ethernet / lwIP / core / ipv4 / ip_frag.c
1 /* @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/ip.h"
44 #include "lwip/ip_frag.h"
45 #include "lwip/netif.h"
46 #include "lwip/snmp.h"
47 #include "lwip/stats.h"
48
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
53 };
54 static u16_t ip_reasslen;
55 static u8_t ip_reassflags;
56 #define IP_REASS_FLAG_LASTFRAG 0x01
57
58 static u8_t ip_reasstmr;
59
60 /*
61  * Copy len bytes from offset in pbuf to buffer 
62  *
63  * helper used by both ip_reass and ip_frag
64  */
65 static struct pbuf *
66 copy_from_pbuf(struct pbuf *p, u16_t * offset,
67            u8_t * buffer, u16_t len)
68 {
69   u16_t l;
70
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;
82   }
83   return p;
84 }
85
86
87 /**
88  * Initializes IP reassembly and fragmentation states.
89  */
90 void
91 ip_frag_init(void)
92 {
93   ip_reasstmr = 0;
94   ip_reassflags = 0;
95   ip_reasslen = 0;
96   memset(ip_reassbitmap, 0, sizeof(ip_reassbitmap));
97 }
98
99 /**
100  * Reassembly timer base function
101  * for both NO_SYS == 0 and 1 (!).
102  *
103  * Should be called every 1000 msec.
104  */
105 void
106 ip_reass_tmr(void)
107 {
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();
114     }
115   }
116 }
117
118 /**
119  * Reassembles incoming IP fragments into an IP datagram.
120  *
121  * @param p points to a pbuf chain of the fragment
122  * @return NULL if reassembly is incomplete, ? otherwise
123  */
124 struct pbuf *
125 ip_reass(struct pbuf *p)
126 {
127   struct pbuf *q;
128   struct ip_hdr *fraghdr, *iphdr;
129   u16_t offset, len;
130   u16_t i;
131
132   IPFRAG_STATS_INC(ip_frag.recv);
133   snmp_inc_ipreasmreqds();
134
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));
147   }
148
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;
162
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;
172     }
173
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);
181
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;
204       }
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];
209     }
210
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. */
216
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));
223     }
224
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;
239         }
240       }
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;
252       }
253
254       /* Pretend to be a "normal" (i.e., not fragmented) IP packet
255          from now on. */
256       ip_reasslen += IP_HLEN;
257
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));
262
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;
281         }
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();
289       }
290       LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass: p %p\n", (void*)p));
291       return p;
292     }
293   }
294
295 nullreturn:
296   IPFRAG_STATS_INC(ip_frag.drop);
297   pbuf_free(p);
298   return NULL;
299 }
300
301 static u8_t buf[MEM_ALIGN_SIZE(IP_FRAG_MAX_MTU)];
302
303 /**
304  * Fragment an IP datagram if too large for the netif.
305  *
306  * Chop the datagram in MTU sized chunks and send them in order
307  * by using a fixed size static memory buffer (PBUF_ROM)
308  */
309 err_t 
310 ip_frag(struct pbuf *p, struct netif *netif, struct ip_addr *dest)
311 {
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;
322
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;
328   }
329   rambuf->tot_len = rambuf->len = mtu;
330   rambuf->payload = MEM_ALIGN((void *)buf);
331
332   /* Copy the IP header in it */
333   iphdr = rambuf->payload;
334   memcpy(iphdr, p->payload, IP_HLEN);
335
336   /* Save original offset */
337   tmp = ntohs(IPH_OFFSET(iphdr));
338   ofo = tmp & IP_OFFMASK;
339   omf = tmp & IP_MF;
340
341   left = p->tot_len - IP_HLEN;
342
343   while (left) {
344     last = (left <= mtu - IP_HLEN);
345
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));
352
353     /* Fill this fragment */
354     nfb = (mtu - IP_HLEN) / 8;
355     cop = last ? left : nfb * 8;
356
357     p = copy_from_pbuf(p, &poff, (u8_t *) iphdr + IP_HLEN, cop);
358
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));
363
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.
370      */
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;    
382     }
383     left -= cop;
384   }
385   pbuf_free(rambuf);
386   snmp_inc_ipfragoks();
387   return ERR_OK;
388 }