OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [rtos/] [freertos-6.1.1/] [Demo/] [Common/] [ethernet/] [lwIP/] [core/] [ipv4/] [ip_frag.c] - Blame information for rev 867

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 606 jeremybenn
/* @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
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.