Main Page | Alphabetical List | Data Structures | File List | Data Fields | Globals | Related Pages

ip_frag.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
00003  * All rights reserved. 
00004  * 
00005  * Redistribution and use in source and binary forms, with or without modification, 
00006  * are permitted provided that the following conditions are met:
00007  *
00008  * 1. Redistributions of source code must retain the above copyright notice,
00009  *    this list of conditions and the following disclaimer.
00010  * 2. Redistributions in binary form must reproduce the above copyright notice,
00011  *    this list of conditions and the following disclaimer in the documentation
00012  *    and/or other materials provided with the distribution.
00013  * 3. The name of the author may not be used to endorse or promote products
00014  *    derived from this software without specific prior written permission. 
00015  *
00016  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 
00017  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
00018  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
00019  * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
00020  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
00021  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
00022  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
00023  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
00024  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
00025  * OF SUCH DAMAGE.
00026  *
00027  * This file is part of the lwIP TCP/IP stack.
00028  * 
00029  * Author: Jani Monoses <jani@iv.ro> 
00030  * original reassembly code by Adam Dunkels <adam@sics.se>
00031  * 
00032  */
00033 
00034 /*-----------------------------------------------------------------------------------*/
00035 /* ip_frag.c
00036  *
00037  * This is the code for IP segmentation and reassembly
00038  *
00039  */
00040 /*-----------------------------------------------------------------------------------*/
00041 
00042 #include "lwip/opt.h"
00043 #include "lwip/sys.h"
00044 #include "lwip/ip.h"
00045 #include "lwip/ip_frag.h"
00046 #include "lwip/netif.h"
00047 
00048 #include "lwip/stats.h"
00049 
00050 #define htons HTONS
00051 #define htonl HTONL
00052 
00053 /*
00054  * Copy len bytes from offset in pbuf to buffer 
00055  *
00056  * helper used by both ip_reass and ip_frag
00057  */
00058 static struct pbuf *
00059 copy_from_pbuf(struct pbuf *p, u16_t * offset,
00060                                    u8_t * buffer, u16_t len)
00061 {
00062   u16_t l;
00063 
00064   p->payload = (u8_t *)p->payload + *offset;
00065   p->len -= *offset;
00066   while (len) {
00067     l = len < p->len ? len : p->len;
00068     memcpy(buffer, p->payload, l);
00069     buffer += l;
00070     len -= l;
00071     if (len)
00072       p = p->next;
00073     else
00074       *offset = l;
00075   }
00076   return p;
00077 }
00078 
00079 #define IP_REASS_BUFSIZE 5760
00080 #define IP_REASS_MAXAGE 30
00081 #define IP_REASS_TMO 1000
00082 
00083 static u8_t ip_reassbuf[IP_HLEN + IP_REASS_BUFSIZE];
00084 static u8_t ip_reassbitmap[IP_REASS_BUFSIZE / (8 * 8)];
00085 static const u8_t bitmap_bits[8] = { 0xff, 0x7f, 0x3f, 0x1f,
00086   0x0f, 0x07, 0x03, 0x01
00087 };
00088 static u16_t ip_reasslen;
00089 static u8_t ip_reassflags;
00090 #define IP_REASS_FLAG_LASTFRAG 0x01
00091 
00092 static u8_t ip_reasstmr;
00093 
00094 /* Reassembly timer */ 
00095 static void
00096 ip_reass_timer(int signo)
00097 {
00098   if(ip_reasstmr > 1) {
00099     ip_reasstmr--;
00100     sys_timeout(IP_REASS_TMO, ip_reass_timer, NULL);
00101   } else if(ip_reasstmr == 1)
00102         ip_reasstmr = 0;
00103 }
00104 
00105 struct pbuf *
00106 ip_reass(struct pbuf *p)
00107 {
00108   struct pbuf *q;
00109   struct ip_hdr *fraghdr, *iphdr;
00110   u16_t offset, len;
00111   u16_t i;
00112 
00113 #ifdef IP_STATS
00114   ++lwip_stats.ip_frag.recv;
00115 #endif /* IP_STATS */
00116 
00117   iphdr = (struct ip_hdr *) ip_reassbuf;
00118   fraghdr = (struct ip_hdr *) p->payload;
00119   /* If ip_reasstmr is zero, no packet is present in the buffer, so we
00120      write the IP header of the fragment into the reassembly
00121      buffer. The timer is updated with the maximum age. */
00122   if (ip_reasstmr == 0) {
00123     DEBUGF(IP_REASS_DEBUG, ("ip_reass: new packet\n"));
00124     memcpy(iphdr, fraghdr, IP_HLEN);
00125     ip_reasstmr = IP_REASS_MAXAGE;
00126     sys_timeout(IP_REASS_TMO, ip_reass_timer, NULL);
00127     ip_reassflags = 0;
00128     /* Clear the bitmap. */
00129     memset(ip_reassbitmap, 0, sizeof(ip_reassbitmap));
00130   }
00131 
00132   /* Check if the incoming fragment matches the one currently present
00133      in the reasembly buffer. If so, we proceed with copying the
00134      fragment into the buffer. */
00135   if (ip_addr_cmp(&iphdr->src, &fraghdr->src) &&
00136       ip_addr_cmp(&iphdr->dest, &fraghdr->dest) &&
00137       IPH_ID(iphdr) == IPH_ID(fraghdr)) {
00138     DEBUGF(IP_REASS_DEBUG, ("ip_reass: matching old packet\n"));
00139 #ifdef IP_STATS
00140     ++lwip_stats.ip_frag.cachehit;
00141 #endif /* IP_STATS */
00142     /* Find out the offset in the reassembly buffer where we should
00143        copy the fragment. */
00144     len = ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4;
00145     offset = (ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8;
00146 
00147     /* If the offset or the offset + fragment length overflows the
00148        reassembly buffer, we discard the entire packet. */
00149     if (offset > IP_REASS_BUFSIZE || offset + len > IP_REASS_BUFSIZE) {
00150       DEBUGF(IP_REASS_DEBUG,
00151              ("ip_reass: fragment outside of buffer (%d:%d/%d).\n", offset,
00152               offset + len, IP_REASS_BUFSIZE));
00153       sys_untimeout(ip_reass_timer, NULL);
00154       ip_reasstmr = 0;
00155       goto nullreturn;
00156     }
00157 
00158     /* Copy the fragment into the reassembly buffer, at the right
00159        offset. */
00160     DEBUGF(IP_REASS_DEBUG,
00161            ("ip_reass: copying with offset %d into %d:%d\n", offset,
00162             IP_HLEN + offset, IP_HLEN + offset + len));
00163     i = IPH_HL(fraghdr) * 4;
00164     copy_from_pbuf(p, &i, &ip_reassbuf[IP_HLEN + offset], len);
00165 
00166     /* Update the bitmap. */
00167     if (offset / (8 * 8) == (offset + len) / (8 * 8)) {
00168       DEBUGF(IP_REASS_DEBUG,
00169              ("ip_reass: updating single byte in bitmap.\n"));
00170       /* If the two endpoints are in the same byte, we only update
00171          that byte. */
00172       ip_reassbitmap[offset / (8 * 8)] |=
00173           bitmap_bits[(offset / 8) & 7] &
00174           ~bitmap_bits[((offset + len) / 8) & 7];
00175     } else {
00176       /* If the two endpoints are in different bytes, we update the
00177          bytes in the endpoints and fill the stuff inbetween with
00178          0xff. */
00179       ip_reassbitmap[offset / (8 * 8)] |= bitmap_bits[(offset / 8) & 7];
00180       DEBUGF(IP_REASS_DEBUG,
00181              ("ip_reass: updating many bytes in bitmap (%d:%d).\n",
00182               1 + offset / (8 * 8), (offset + len) / (8 * 8)));
00183       for (i = 1 + offset / (8 * 8); i < (offset + len) / (8 * 8); ++i) {
00184         ip_reassbitmap[i] = 0xff;
00185       }
00186       ip_reassbitmap[(offset + len) / (8 * 8)] |=
00187           ~bitmap_bits[((offset + len) / 8) & 7];
00188     }
00189 
00190     /* If this fragment has the More Fragments flag set to zero, we
00191        know that this is the last fragment, so we can calculate the
00192        size of the entire packet. We also set the
00193        IP_REASS_FLAG_LASTFRAG flag to indicate that we have received
00194        the final fragment. */
00195 
00196     if ((ntohs(IPH_OFFSET(fraghdr)) & IP_MF) == 0) {
00197       ip_reassflags |= IP_REASS_FLAG_LASTFRAG;
00198       ip_reasslen = offset + len;
00199       DEBUGF(IP_REASS_DEBUG,
00200              ("ip_reass: last fragment seen, total len %d\n",
00201               ip_reasslen));
00202     }
00203 
00204     /* Finally, we check if we have a full packet in the buffer. We do
00205        this by checking if we have the last fragment and if all bits
00206        in the bitmap are set. */
00207     if (ip_reassflags & IP_REASS_FLAG_LASTFRAG) {
00208       /* Check all bytes up to and including all but the last byte in
00209          the bitmap. */
00210       for (i = 0; i < ip_reasslen / (8 * 8) - 1; ++i) {
00211         if (ip_reassbitmap[i] != 0xff) {
00212           DEBUGF(IP_REASS_DEBUG,
00213                  ("ip_reass: last fragment seen, bitmap %d/%d failed (%x)\n",
00214                   i, ip_reasslen / (8 * 8) - 1, ip_reassbitmap[i]));
00215           goto nullreturn;
00216         }
00217       }
00218       /* Check the last byte in the bitmap. It should contain just the
00219          right amount of bits. */
00220       if (ip_reassbitmap[ip_reasslen / (8 * 8)] !=
00221           (u8_t) ~ bitmap_bits[ip_reasslen / 8 & 7]) {
00222         DEBUGF(IP_REASS_DEBUG,
00223                ("ip_reass: last fragment seen, bitmap %d didn't contain %x (%x)\n",
00224                 ip_reasslen / (8 * 8), ~bitmap_bits[ip_reasslen / 8 & 7],
00225                 ip_reassbitmap[ip_reasslen / (8 * 8)]));
00226         goto nullreturn;
00227       }
00228 
00229       /* Pretend to be a "normal" (i.e., not fragmented) IP packet
00230          from now on. */
00231       ip_reasslen += IP_HLEN;
00232 
00233       IPH_LEN_SET(iphdr, htons(ip_reasslen));
00234       IPH_OFFSET_SET(iphdr, 0);
00235       IPH_CHKSUM_SET(iphdr, 0);
00236       IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN));
00237 
00238       /* If we have come this far, we have a full packet in the
00239          buffer, so we allocate a pbuf and copy the packet into it. We
00240          also reset the timer. */
00241       sys_untimeout(ip_reass_timer, NULL);
00242       ip_reasstmr = 0;
00243       pbuf_free(p);
00244       p = pbuf_alloc(PBUF_LINK, ip_reasslen, PBUF_POOL);
00245       if (p != NULL) {
00246         i = 0;
00247         for (q = p; q != NULL; q = q->next) {
00248           /* Copy enough bytes to fill this pbuf in the chain. The
00249              available data in the pbuf is given by the q->len
00250              variable. */
00251           DEBUGF(IP_REASS_DEBUG,
00252                  ("ip_reass: memcpy from %p (%d) to %p, %d bytes\n",
00253                   &ip_reassbuf[i], i, q->payload,
00254                   q->len > ip_reasslen - i ? ip_reasslen - i : q->len));
00255           memcpy(q->payload, &ip_reassbuf[i],
00256                 q->len > ip_reasslen - i ? ip_reasslen - i : q->len);
00257           i += q->len;
00258         }
00259 #ifdef IP_STATS
00260         ++lwip_stats.ip_frag.fw;
00261 #endif /* IP_STATS */
00262       } else {
00263 #ifdef IP_STATS
00264         ++lwip_stats.ip_frag.memerr;
00265 #endif /* IP_STATS */
00266       }
00267       DEBUGF(IP_REASS_DEBUG, ("ip_reass: p %p\n", (void*)p));
00268       return p;
00269     }
00270   }
00271 
00272 nullreturn:
00273 #ifdef IP_STATS
00274   ++lwip_stats.ip_frag.drop;
00275 #endif /* IP_STATS */
00276   pbuf_free(p);
00277   return NULL;
00278 }
00279 
00280 #define MAX_MTU 1500
00281 static u8_t buf[MAX_MTU];
00282 
00289 err_t 
00290 ip_frag(struct pbuf *p, struct netif *netif, struct ip_addr *dest)
00291 {
00292   struct pbuf *rambuf;
00293   struct pbuf *header;
00294   struct ip_hdr *iphdr;
00295   u16_t nfb = 0;
00296   u16_t left, cop;
00297   u16_t mtu = netif->mtu;
00298   u16_t ofo, omf;
00299   u16_t last;
00300   u16_t poff = IP_HLEN;
00301   u16_t tmp;
00302 
00303   /* Get a RAM based MTU sized pbuf */
00304   rambuf = pbuf_alloc(PBUF_LINK, 0, PBUF_REF);
00305   rambuf->tot_len = rambuf->len = mtu;
00306   rambuf->payload = buf;
00307 
00308 
00309   /* Copy the IP header in it */
00310   iphdr = rambuf->payload;
00311   memcpy(iphdr, p->payload, IP_HLEN);
00312 
00313   /* Save original offset */
00314   tmp = ntohs(IPH_OFFSET(iphdr));
00315   ofo = tmp & IP_OFFMASK;
00316   omf = tmp & IP_MF;
00317 
00318   left = p->tot_len - IP_HLEN;
00319 
00320   while (left) {
00321     last = (left <= mtu - IP_HLEN);
00322 
00323     /* Set new offset and MF flag */
00324     ofo += nfb;
00325     tmp = omf | (IP_OFFMASK & (ofo));
00326     if (!last)
00327       tmp = tmp | IP_MF;
00328     IPH_OFFSET_SET(iphdr, htons(tmp));
00329 
00330     /* Fill this fragment */
00331     nfb = (mtu - IP_HLEN) / 8;
00332     cop = last ? left : nfb * 8;
00333 
00334     p = copy_from_pbuf(p, &poff, (u8_t *) iphdr + IP_HLEN, cop);
00335 
00336     /* Correct header */
00337     IPH_LEN_SET(iphdr, htons(cop + IP_HLEN));
00338     IPH_CHKSUM_SET(iphdr, 0);
00339     IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN));
00340 
00341     if (last)
00342       pbuf_realloc(rambuf, left + IP_HLEN);
00343     /* This part is ugly: we alloc a RAM based pbuf for 
00344      * the link level header for each chunk and then 
00345      * free it.A PBUF_ROM style pbuf for which pbuf_header
00346      * worked would make things simpler.
00347      */
00348     header = pbuf_alloc(PBUF_LINK, 0, PBUF_RAM);
00349     pbuf_chain(header, rambuf);
00350     netif->output(netif, header, dest);
00351 #ifdef IP_STATS
00352     ++lwip_stats.ip_frag.xmit;
00353 #endif /* IP_STATS */
00354     pbuf_free(header);
00355 
00356     left -= cop;
00357   }
00358   pbuf_free(rambuf);
00359   return ERR_OK;
00360 }

Generated on Wed Jan 14 12:58:56 2004 for RTL-lwIP-0.4 by doxygen 1.3.4