]> git.sur5r.net Git - freertos/blob - Demo/lwIP_MCF5235_GCC/lwip/src/core/ipv4/ip_frag.c
Add PIC24, dsPIC and Coldfire files.
[freertos] / Demo / lwIP_MCF5235_GCC / lwip / src / 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/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  */
55 static struct pbuf *
56 copy_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
80 static u8_t ip_reassbuf[IP_HLEN + IP_REASS_BUFSIZE];
81 static u8_t ip_reassbitmap[IP_REASS_BUFSIZE / (8 * 8) + 1];
82 static const u8_t bitmap_bits[8] = { 0xff, 0x7f, 0x3f, 0x1f,
83   0x0f, 0x07, 0x03, 0x01
84 };
85 static u16_t ip_reasslen;
86 static u8_t ip_reassflags;
87 #define IP_REASS_FLAG_LASTFRAG 0x01
88
89 static 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  */
97 void
98 ip_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  */
111 struct pbuf *
112 ip_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
276 nullreturn:
277   IPFRAG_STATS_INC(ip_frag.drop);
278   pbuf_free(p);
279   return NULL;
280 }
281
282 #define MAX_MTU 1500
283 static 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  */
291 err_t 
292 ip_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 }