2 * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
5 * Redistribution and use in source and binary forms, with or without modification,
6 * are permitted provided that the following conditions are met:
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 * 3. The name of the author may not be used to endorse or promote products
14 * derived from this software without specific prior written permission.
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
19 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
21 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
24 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
27 * This file is part of the lwIP TCP/IP stack.
29 * Author: Jani Monoses <jani@iv.ro>
30 * original reassembly code by Adam Dunkels <adam@sics.se>
37 * This is the code for IP segmentation and reassembly
45 #include "lwip/ip_frag.h"
46 #include "lwip/netif.h"
48 #include "lwip/stats.h"
56 * Copy len bytes from offset in pbuf to buffer
58 * helper used by both ip_reass and ip_frag
60 static struct pbuf
* copy_from_pbuf (
61 struct pbuf
*p
, u16_t
* offset
, u8_t
* buffer
, u16_t len
66 p
-> payload
= ( u8_t
* )p
-> payload
+ *offset
;
69 for ( ; len
; p
= p
-> next
) {
70 l
= len
< p
-> len
? len
: p
-> len
;
71 mips_memcpy ( buffer
, p
-> payload
, l
);
80 } /* end copy_from_pbuf */
82 #define IP_REASS_BUFSIZE 5760
83 #define IP_REASS_MAXAGE 30
84 #define IP_REASS_TMO 250
86 static iop_sys_clock_t s_Clock
= {
87 0x01194000, 0x00000000
90 static u8_t ip_reassbuf
[IP_HLEN
+ IP_REASS_BUFSIZE
];
91 static u8_t ip_reassbitmap
[IP_REASS_BUFSIZE
/ (8 * 8)];
93 static const u8_t bitmap_bits
[ 8 ] = {
94 0xFF, 0x7F, 0x3F, 0x1F,
95 0x0F, 0x07, 0x03, 0x01
98 static u16_t ip_reasslen
;
99 static u8_t ip_reassflags
;
101 #define IP_REASS_FLAG_LASTFRAG 0x01
103 static u8_t ip_reasstmr
;
105 static unsigned ip_reass_timer ( void* apArg
) {
107 iop_sys_clock_t
* lpClock
= ( iop_sys_clock_t
* )apArg
;
111 return lpClock
-> lo
;
114 } /* end ip_reass_timer */
117 ip_reass(struct pbuf
*p
)
120 struct ip_hdr
*fraghdr
, *iphdr
;
124 iphdr
= (struct ip_hdr
*) ip_reassbuf
;
125 fraghdr
= (struct ip_hdr
*) p
->payload
;
126 /* If ip_reasstmr is zero, no packet is present in the buffer, so we
127 write the IP header of the fragment into the reassembly
128 buffer. The timer is updated with the maximum age. */
129 if (ip_reasstmr
== 0) {
130 mips_memcpy(iphdr
, fraghdr
, IP_HLEN
);
131 ip_reasstmr
= IP_REASS_MAXAGE
;
132 SetAlarm ( &s_Clock
, ip_reass_timer
, &s_Clock
);
134 /* Clear the bitmap. */
135 mips_memset(ip_reassbitmap
, 0, sizeof(ip_reassbitmap
));
138 /* Check if the incoming fragment matches the one currently present
139 in the reasembly buffer. If so, we proceed with copying the
140 fragment into the buffer. */
141 if (ip_addr_cmp(&iphdr
->src
, &fraghdr
->src
) &&
142 ip_addr_cmp(&iphdr
->dest
, &fraghdr
->dest
) &&
143 IPH_ID(iphdr
) == IPH_ID(fraghdr
)) {
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;
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 CancelAlarm ( ip_reass_timer
, &s_Clock
);
157 /* Copy the fragment into the reassembly buffer, at the right
159 LWIP_DEBUGF(IP_REASS_DEBUG
,
160 ("ip_reass: copying with offset %d into %d:%d\n", offset
,
161 IP_HLEN
+ offset
, IP_HLEN
+ offset
+ len
));
162 i
= IPH_HL(fraghdr
) * 4;
163 copy_from_pbuf(p
, &i
, &ip_reassbuf
[IP_HLEN
+ offset
], len
);
165 /* Update the bitmap. */
166 if (offset
/ (8 * 8) == (offset
+ len
) / (8 * 8)) {
167 /* If the two endpoints are in the same byte, we only update
169 ip_reassbitmap
[offset
/ (8 * 8)] |=
170 bitmap_bits
[(offset
/ 8) & 7] &
171 ~bitmap_bits
[((offset
+ len
) / 8) & 7];
173 /* If the two endpoints are in different bytes, we update the
174 bytes in the endpoints and fill the stuff inbetween with
176 ip_reassbitmap
[offset
/ (8 * 8)] |= bitmap_bits
[(offset
/ 8) & 7];
177 for (i
= 1 + offset
/ (8 * 8); i
< (offset
+ len
) / (8 * 8); ++i
) {
178 ip_reassbitmap
[i
] = 0xff;
180 ip_reassbitmap
[(offset
+ len
) / (8 * 8)] |=
181 ~bitmap_bits
[((offset
+ len
) / 8) & 7];
184 /* If this fragment has the More Fragments flag set to zero, we
185 know that this is the last fragment, so we can calculate the
186 size of the entire packet. We also set the
187 IP_REASS_FLAG_LASTFRAG flag to indicate that we have received
188 the final fragment. */
190 if ((ntohs(IPH_OFFSET(fraghdr
)) & IP_MF
) == 0) {
191 ip_reassflags
|= IP_REASS_FLAG_LASTFRAG
;
192 ip_reasslen
= offset
+ len
;
195 /* Finally, we check if we have a full packet in the buffer. We do
196 this by checking if we have the last fragment and if all bits
197 in the bitmap are set. */
198 if (ip_reassflags
& IP_REASS_FLAG_LASTFRAG
) {
199 /* Check all bytes up to and including all but the last byte in
201 for (i
= 0; i
< ip_reasslen
/ (8 * 8) - 1; ++i
) {
202 if (ip_reassbitmap
[i
] != 0xff) {
206 /* Check the last byte in the bitmap. It should contain just the
207 right amount of bits. */
208 if (ip_reassbitmap
[ip_reasslen
/ (8 * 8)] !=
209 (u8_t
) ~ bitmap_bits
[ip_reasslen
/ 8 & 7]) {
213 /* Pretend to be a "normal" (i.e., not fragmented) IP packet
215 ip_reasslen
+= IP_HLEN
;
217 IPH_LEN_SET(iphdr
, htons(ip_reasslen
));
218 IPH_OFFSET_SET(iphdr
, 0);
219 IPH_CHKSUM_SET(iphdr
, 0);
220 IPH_CHKSUM_SET(iphdr
, inet_chksum(iphdr
, IP_HLEN
));
222 /* If we have come this far, we have a full packet in the
223 buffer, so we allocate a pbuf and copy the packet into it. We
224 also reset the timer. */
225 CancelAlarm ( ip_reass_timer
, &s_Clock
);
229 p
= pbuf_alloc(PBUF_LINK
, ip_reasslen
, PBUF_POOL
);
232 for (q
= p
; q
!= NULL
; q
= q
->next
) {
233 /* Copy enough bytes to fill this pbuf in the chain. The
234 available data in the pbuf is given by the q->len
236 mips_memcpy(q
->payload
, &ip_reassbuf
[i
],
237 q
->len
> ip_reasslen
- i
? ip_reasslen
- i
: q
->len
);
250 static u8_t buf
[MEM_ALIGN_SIZE(MAX_MTU
)];
253 * Fragment an IP packet if too large
255 * Chop the packet in mtu sized chunks and send them in order
256 * by using a fixed size static memory buffer (PBUF_ROM)
259 ip_frag(struct pbuf
*p
, struct netif
*netif
, struct ip_addr
*dest
)
263 struct ip_hdr
*iphdr
;
266 u16_t mtu
= netif
->mtu
;
269 u16_t poff
= IP_HLEN
;
272 /* Get a RAM based MTU sized pbuf */
273 rambuf
= pbuf_alloc(PBUF_LINK
, 0, PBUF_REF
);
274 rambuf
->tot_len
= rambuf
->len
= mtu
;
275 rambuf
->payload
= MEM_ALIGN((void *)buf
);
278 /* Copy the IP header in it */
279 iphdr
= rambuf
->payload
;
280 mips_memcpy(iphdr
, p
->payload
, IP_HLEN
);
282 /* Save original offset */
283 tmp
= ntohs(IPH_OFFSET(iphdr
));
284 ofo
= tmp
& IP_OFFMASK
;
287 left
= p
->tot_len
- IP_HLEN
;
290 last
= (left
<= mtu
- IP_HLEN
);
292 /* Set new offset and MF flag */
294 tmp
= omf
| (IP_OFFMASK
& (ofo
));
297 IPH_OFFSET_SET(iphdr
, htons(tmp
));
299 /* Fill this fragment */
300 nfb
= (mtu
- IP_HLEN
) / 8;
301 cop
= last
? left
: nfb
* 8;
303 p
= copy_from_pbuf(p
, &poff
, (u8_t
*) iphdr
+ IP_HLEN
, cop
);
306 IPH_LEN_SET(iphdr
, htons(cop
+ IP_HLEN
));
307 IPH_CHKSUM_SET(iphdr
, 0);
308 IPH_CHKSUM_SET(iphdr
, inet_chksum(iphdr
, IP_HLEN
));
311 pbuf_realloc(rambuf
, left
+ IP_HLEN
);
312 /* This part is ugly: we alloc a RAM based pbuf for
313 * the link level header for each chunk and then
314 * free it.A PBUF_ROM style pbuf for which pbuf_header
315 * worked would make things simpler.
317 header
= pbuf_alloc(PBUF_LINK
, 0, PBUF_RAM
);
318 pbuf_chain(header
, rambuf
);
319 netif
->output(netif
, header
, dest
);
320 IPFRAG_STATS_INC(ip_frag
.xmit
);