mirror of
https://github.com/lwip-tcpip/lwip.git
synced 2024-10-05 22:29:49 +00:00
Added code to free the oldest packets if the IP_REASS_MAX_PBUFS limit is reached or MEMP_REASSDATA is empty.
This commit is contained in:
parent
c41508cc8d
commit
1f08a5e5f5
@ -47,6 +47,31 @@
|
||||
#include <string.h>
|
||||
|
||||
#if IP_REASSEMBLY
|
||||
/**
|
||||
* The IP reassembly code currently has the following limitations:
|
||||
* - IP header options are not supported
|
||||
* - fragments must not overlap (e.g. due to different routes),
|
||||
* currently, overlapping or duplicate fragments are thrown away
|
||||
* if IP_REASS_CHECK_OVERLAP=1 (the default)!
|
||||
*
|
||||
* @todo: work with IP header options
|
||||
*/
|
||||
|
||||
/** Setting this to 0, you can turn off checking the fragments for overlapping
|
||||
* regions. The code gets a little smaller. Only use this if you know that
|
||||
* overlapping won't occur on your network! */
|
||||
#ifndef IP_REASS_CHECK_OVERLAP
|
||||
#define IP_REASS_CHECK_OVERLAP 1
|
||||
#endif /* IP_REASS_CHECK_OVERLAP */
|
||||
|
||||
/** Set to 0 to prevent freeing the oldest packet when the reassembly buffer is
|
||||
* full (IP_REASS_MAX_PBUFS pbufs are enqueued). The code gets a little smaller.
|
||||
* Packets will be freed by timeout only. Especially useful when MEMP_NUM_REASSDATA
|
||||
* is set to 1, so one packet can be reassembled at a time, only. */
|
||||
#ifndef IP_REASS_FREE_OLDEST
|
||||
#define IP_REASS_FREE_OLDEST 1
|
||||
#endif /* IP_REASS_FREE_OLDEST */
|
||||
|
||||
#define IP_REASS_FLAG_LASTFRAG 0x01
|
||||
|
||||
/** This is a helper struct which holds the starting
|
||||
@ -61,35 +86,23 @@ struct ip_reass_helper {
|
||||
u16_t end;
|
||||
};
|
||||
|
||||
#define IP_ADDRESSES_AND_ID_MATCH(iphdrA, iphdrB) \
|
||||
(ip_addr_cmp(&(iphdrA)->src, &(iphdrB)->src) && \
|
||||
ip_addr_cmp(&(iphdrA)->dest, &(iphdrB)->dest) && \
|
||||
IPH_ID(iphdrA) == IPH_ID(iphdrB)) ? 1 : 0
|
||||
|
||||
/* global variables */
|
||||
static struct ip_reassdata *reasspackets;
|
||||
static u16_t ip_reass_pbufcount;
|
||||
#endif /* IP_REASSEMBLY */
|
||||
|
||||
#if IP_REASSEMBLY
|
||||
/**
|
||||
* The IP reassembly code currently has the following limitations:
|
||||
* - IP header options are not supported
|
||||
* - fragments must not overlap (e.g. due to different routes),
|
||||
* currently, overlapping or duplicate fragments are thrown away
|
||||
* if IP_REASS_CHECK_OVERLAP=1 (the default)!
|
||||
*
|
||||
* @todo: work with IP header options
|
||||
* @todo: free the oldest entry when the MEMP_REASSDATA pool is empty or
|
||||
* the IP_REASS_MAX_PBUFS limit is reached
|
||||
*/
|
||||
|
||||
#ifndef IP_REASS_CHECK_OVERLAP
|
||||
#define IP_REASS_CHECK_OVERLAP 1
|
||||
#endif /* IP_REASS_CHECK_OVERLAP */
|
||||
|
||||
/* function prototypes */
|
||||
static void dequeue_packet(struct ip_reassdata *ipr, struct ip_reassdata *prev);
|
||||
static void ip_reass_dequeue_packet(struct ip_reassdata *ipr, struct ip_reassdata *prev);
|
||||
|
||||
/**
|
||||
* Reassembly timer base function
|
||||
* for both NO_SYS == 0 and 1 (!).
|
||||
*
|
||||
* Should be called every 1000 msec.
|
||||
* Should be called every 1000 msec (defined by IP_TMR_INTERVAL).
|
||||
*/
|
||||
void
|
||||
ip_reass_tmr(void)
|
||||
@ -129,27 +142,107 @@ ip_reass_tmr(void)
|
||||
/* Then, unchain the struct ip_reassdata from the list and free it. */
|
||||
del = r;
|
||||
r = r->next;
|
||||
dequeue_packet(del, prev);
|
||||
ip_reass_dequeue_packet(del, prev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if IP_REASS_FREE_OLDEST
|
||||
/**
|
||||
* Free the oldest packet to make room for enqueueing new fragments.
|
||||
* The packet 'fraghdr' belongs to is not freed!
|
||||
*
|
||||
* @param fraghdr IP header of the current fragment
|
||||
* @param pbufs_needed number of pbufs needed to enqueue
|
||||
* (used for freeing other packets if not enough space)
|
||||
* @return the number of pbufs freed
|
||||
*/
|
||||
static int
|
||||
ip_reass_remove_oldest_packet(struct ip_hdr *fraghdr, int pbufs_needed)
|
||||
{
|
||||
/* @todo Can't we simply remove the last packet in the
|
||||
* linked list behind reasspackets?
|
||||
*/
|
||||
struct ip_reassdata *r, *oldest, *prev;
|
||||
int pbufs_freed = 0, pbufs_freed_current;
|
||||
int other_packets;
|
||||
|
||||
/* Free packets until being allowed to enqueue 'pbufs_needed' pbufs,
|
||||
* but don't free the packet that 'fraghdr' belongs to! */
|
||||
do {
|
||||
oldest = NULL;
|
||||
prev = NULL;
|
||||
other_packets = 0;
|
||||
r = reasspackets;
|
||||
while (r != NULL) {
|
||||
if (!IP_ADDRESSES_AND_ID_MATCH(&r->iphdr, fraghdr)) {
|
||||
/* Not the same packet as fraghdr */
|
||||
other_packets++;
|
||||
if (oldest == NULL) {
|
||||
oldest = r;
|
||||
} else if (r->timer <= oldest->timer) {
|
||||
/* older than the previous oldest */
|
||||
oldest = r;
|
||||
}
|
||||
}
|
||||
if (r->next != NULL) {
|
||||
prev = r;
|
||||
}
|
||||
r = r->next;
|
||||
}
|
||||
if (oldest != NULL) {
|
||||
struct ip_reass_helper *iprh;
|
||||
struct pbuf *p = oldest->p;
|
||||
|
||||
LWIP_ASSERT("prev != oldest", prev != oldest);
|
||||
if (prev != NULL) {
|
||||
LWIP_ASSERT("prev->next == oldest", prev->next == oldest);
|
||||
}
|
||||
|
||||
/* Free all pbufs from all fragments enqueued for this packet. */
|
||||
pbufs_freed_current = 0;
|
||||
while (p != NULL) {
|
||||
pbufs_freed_current += pbuf_clen(p);
|
||||
iprh = (struct ip_reass_helper*)p->payload;
|
||||
p = iprh->next_pbuf;
|
||||
}
|
||||
/* Free the helper struct. */
|
||||
ip_reass_dequeue_packet(oldest, prev);
|
||||
LWIP_ASSERT("ip_reass_pbufcount >= pbufs_freed_current", ip_reass_pbufcount >= pbufs_freed_current);
|
||||
/* Update the pbuf counter. */
|
||||
ip_reass_pbufcount -= pbufs_freed_current;
|
||||
pbufs_freed += pbufs_freed_current;
|
||||
}
|
||||
} while ((pbufs_freed < pbufs_needed) && (other_packets > 1));
|
||||
return pbufs_freed;
|
||||
}
|
||||
#endif /* IP_REASS_FREE_OLDEST */
|
||||
|
||||
/**
|
||||
* Enqueues a new fragment into the fragment queue
|
||||
* @param fraghdr points to the new fragments IP hdr
|
||||
* @param clen number of pbufs needed to enqueue (used for freeing other packets if not enough space)
|
||||
* @return A pointer to the queue location into which the fragment was enqueued
|
||||
*/
|
||||
static struct ip_reassdata*
|
||||
enqueue_new_packet(struct ip_hdr *fraghdr)
|
||||
ip_reass_enqueue_new_packet(struct ip_hdr *fraghdr, int clen)
|
||||
{
|
||||
struct ip_reassdata* ipr;
|
||||
/* No matching previous fragment found, allocate a new reassdata struct */
|
||||
ipr = memp_malloc(MEMP_REASSDATA);
|
||||
if (ipr == NULL) {
|
||||
#if IP_REASS_FREE_OLDEST
|
||||
if (ip_reass_remove_oldest_packet(fraghdr, clen) >= clen) {
|
||||
ipr = memp_malloc(MEMP_REASSDATA);
|
||||
}
|
||||
if (ipr == NULL)
|
||||
#endif /* IP_REASS_FREE_OLDEST */
|
||||
{
|
||||
IPFRAG_STATS_INC(ip_frag.memerr);
|
||||
LWIP_DEBUGF(IP_REASS_DEBUG,("Failed to alloc reassdata struct"));
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
memset(ipr, 0, sizeof(struct ip_reassdata));
|
||||
ipr->timer = IP_REASS_MAXAGE;
|
||||
|
||||
@ -167,7 +260,7 @@ enqueue_new_packet(struct ip_hdr *fraghdr)
|
||||
* @param ipr points to the queue entry to dequeue
|
||||
*/
|
||||
static void
|
||||
dequeue_packet(struct ip_reassdata *ipr, struct ip_reassdata *prev)
|
||||
ip_reass_dequeue_packet(struct ip_reassdata *ipr, struct ip_reassdata *prev)
|
||||
{
|
||||
|
||||
/* dequeue the reass struct */
|
||||
@ -194,7 +287,7 @@ dequeue_packet(struct ip_reassdata *ipr, struct ip_reassdata *prev)
|
||||
* @return 0 if invalid, >0 otherwise
|
||||
*/
|
||||
static int
|
||||
chain_frag_into_packet_and_validate(struct ip_reassdata *ipr, struct pbuf *new_p)
|
||||
ip_reass_chain_frag_into_packet_and_validate(struct ip_reassdata *ipr, struct pbuf *new_p)
|
||||
{
|
||||
struct ip_reass_helper *iprh, *iprh_tmp, *iprh_prev=NULL;
|
||||
struct pbuf *q;
|
||||
@ -363,12 +456,19 @@ ip_reass(struct pbuf *p)
|
||||
/* Check if we are allowed to enqueue more packets. */
|
||||
clen = pbuf_clen(p);
|
||||
if ((ip_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS) {
|
||||
#if IP_REASS_FREE_OLDEST
|
||||
if (!ip_reass_remove_oldest_packet(fraghdr, clen) ||
|
||||
((ip_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS))
|
||||
#endif /* IP_REASS_FREE_OLDEST */
|
||||
{
|
||||
/* No packet could be freed and still too many pbufs enqueued */
|
||||
LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: Overflow condition: pbufct=%d, clen=%d, MAX=%d",
|
||||
ip_reass_pbufcount,clen,IP_REASS_MAX_PBUFS));
|
||||
IPFRAG_STATS_INC(ip_frag.memerr);
|
||||
/* drop this pbuf */
|
||||
goto nullreturn;
|
||||
}
|
||||
}
|
||||
|
||||
/* Look for the packet the fragment belongs to in the current packet queue,
|
||||
* remembering the previous in the queue for later dequeueing. */
|
||||
@ -376,9 +476,7 @@ ip_reass(struct pbuf *p)
|
||||
/* Check if the incoming fragment matches the one currently present
|
||||
in the reassembly buffer. If so, we proceed with copying the
|
||||
fragment into the buffer. */
|
||||
if (ip_addr_cmp(&ipr->iphdr.src, &fraghdr->src) &&
|
||||
ip_addr_cmp(&ipr->iphdr.dest, &fraghdr->dest) &&
|
||||
IPH_ID(&ipr->iphdr) == IPH_ID(fraghdr)) {
|
||||
if (IP_ADDRESSES_AND_ID_MATCH(&ipr->iphdr, fraghdr)) {
|
||||
LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass: matching previous fragment ID=%"X16_F"\n",
|
||||
ntohs(IPH_ID(fraghdr))));
|
||||
IPFRAG_STATS_INC(ip_frag.cachehit);
|
||||
@ -389,7 +487,7 @@ ip_reass(struct pbuf *p)
|
||||
|
||||
if (ipr == NULL) {
|
||||
/* Enqueue a new packet into the packet queue */
|
||||
ipr = enqueue_new_packet(fraghdr);
|
||||
ipr = ip_reass_enqueue_new_packet(fraghdr, clen);
|
||||
/* Bail if unable to enqueue */
|
||||
if(ipr == NULL) {
|
||||
goto nullreturn;
|
||||
@ -412,7 +510,7 @@ ip_reass(struct pbuf *p)
|
||||
}
|
||||
/* find the right place to insert this pbuf */
|
||||
/* @todo: trim pbufs if fragments are overlapping */
|
||||
if (chain_frag_into_packet_and_validate(ipr, p)) {
|
||||
if (ip_reass_chain_frag_into_packet_and_validate(ipr, p)) {
|
||||
/* the totally last fragment (flag more fragments = 0) was received at least
|
||||
* once AND all fragments are received */
|
||||
ipr->packet_len += IP_HLEN;
|
||||
@ -441,7 +539,7 @@ ip_reass(struct pbuf *p)
|
||||
r = iprh->next_pbuf;
|
||||
}
|
||||
/* release the sources allocate for the fragment queue entry */
|
||||
dequeue_packet(ipr, ipr_prev);
|
||||
ip_reass_dequeue_packet(ipr, ipr_prev);
|
||||
|
||||
/* and adjust the number of pbufs currently queued for reassembly. */
|
||||
ip_reass_pbufcount -= pbuf_clen(p);
|
||||
|
Loading…
Reference in New Issue
Block a user