mirror of
https://github.com/lwip-tcpip/lwip.git
synced 2024-08-23 06:35:57 +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>
|
#include <string.h>
|
||||||
|
|
||||||
#if 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** 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
|
#define IP_REASS_FLAG_LASTFRAG 0x01
|
||||||
|
|
||||||
/** This is a helper struct which holds the starting
|
/** This is a helper struct which holds the starting
|
||||||
|
@ -61,35 +86,23 @@ struct ip_reass_helper {
|
||||||
u16_t end;
|
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 struct ip_reassdata *reasspackets;
|
||||||
static u16_t ip_reass_pbufcount;
|
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 */
|
/* 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
|
* Reassembly timer base function
|
||||||
* for both NO_SYS == 0 and 1 (!).
|
* 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
|
void
|
||||||
ip_reass_tmr(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. */
|
/* Then, unchain the struct ip_reassdata from the list and free it. */
|
||||||
del = r;
|
del = r;
|
||||||
r = r->next;
|
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
|
* Enqueues a new fragment into the fragment queue
|
||||||
* @param fraghdr points to the new fragments IP hdr
|
* @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
|
* @return A pointer to the queue location into which the fragment was enqueued
|
||||||
*/
|
*/
|
||||||
static struct ip_reassdata*
|
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;
|
struct ip_reassdata* ipr;
|
||||||
/* No matching previous fragment found, allocate a new reassdata struct */
|
/* No matching previous fragment found, allocate a new reassdata struct */
|
||||||
ipr = memp_malloc(MEMP_REASSDATA);
|
ipr = memp_malloc(MEMP_REASSDATA);
|
||||||
if (ipr == NULL) {
|
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);
|
IPFRAG_STATS_INC(ip_frag.memerr);
|
||||||
LWIP_DEBUGF(IP_REASS_DEBUG,("Failed to alloc reassdata struct"));
|
LWIP_DEBUGF(IP_REASS_DEBUG,("Failed to alloc reassdata struct"));
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
memset(ipr, 0, sizeof(struct ip_reassdata));
|
memset(ipr, 0, sizeof(struct ip_reassdata));
|
||||||
ipr->timer = IP_REASS_MAXAGE;
|
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
|
* @param ipr points to the queue entry to dequeue
|
||||||
*/
|
*/
|
||||||
static void
|
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 */
|
/* dequeue the reass struct */
|
||||||
|
@ -194,7 +287,7 @@ dequeue_packet(struct ip_reassdata *ipr, struct ip_reassdata *prev)
|
||||||
* @return 0 if invalid, >0 otherwise
|
* @return 0 if invalid, >0 otherwise
|
||||||
*/
|
*/
|
||||||
static int
|
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 ip_reass_helper *iprh, *iprh_tmp, *iprh_prev=NULL;
|
||||||
struct pbuf *q;
|
struct pbuf *q;
|
||||||
|
@ -363,12 +456,19 @@ ip_reass(struct pbuf *p)
|
||||||
/* Check if we are allowed to enqueue more packets. */
|
/* Check if we are allowed to enqueue more packets. */
|
||||||
clen = pbuf_clen(p);
|
clen = pbuf_clen(p);
|
||||||
if ((ip_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS) {
|
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",
|
LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: Overflow condition: pbufct=%d, clen=%d, MAX=%d",
|
||||||
ip_reass_pbufcount,clen,IP_REASS_MAX_PBUFS));
|
ip_reass_pbufcount,clen,IP_REASS_MAX_PBUFS));
|
||||||
IPFRAG_STATS_INC(ip_frag.memerr);
|
IPFRAG_STATS_INC(ip_frag.memerr);
|
||||||
/* drop this pbuf */
|
/* drop this pbuf */
|
||||||
goto nullreturn;
|
goto nullreturn;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Look for the packet the fragment belongs to in the current packet queue,
|
/* Look for the packet the fragment belongs to in the current packet queue,
|
||||||
* remembering the previous in the queue for later dequeueing. */
|
* 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
|
/* Check if the incoming fragment matches the one currently present
|
||||||
in the reassembly buffer. If so, we proceed with copying the
|
in the reassembly buffer. If so, we proceed with copying the
|
||||||
fragment into the buffer. */
|
fragment into the buffer. */
|
||||||
if (ip_addr_cmp(&ipr->iphdr.src, &fraghdr->src) &&
|
if (IP_ADDRESSES_AND_ID_MATCH(&ipr->iphdr, fraghdr)) {
|
||||||
ip_addr_cmp(&ipr->iphdr.dest, &fraghdr->dest) &&
|
|
||||||
IPH_ID(&ipr->iphdr) == IPH_ID(fraghdr)) {
|
|
||||||
LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass: matching previous fragment ID=%"X16_F"\n",
|
LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass: matching previous fragment ID=%"X16_F"\n",
|
||||||
ntohs(IPH_ID(fraghdr))));
|
ntohs(IPH_ID(fraghdr))));
|
||||||
IPFRAG_STATS_INC(ip_frag.cachehit);
|
IPFRAG_STATS_INC(ip_frag.cachehit);
|
||||||
|
@ -389,7 +487,7 @@ ip_reass(struct pbuf *p)
|
||||||
|
|
||||||
if (ipr == NULL) {
|
if (ipr == NULL) {
|
||||||
/* Enqueue a new packet into the packet queue */
|
/* 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 */
|
/* Bail if unable to enqueue */
|
||||||
if(ipr == NULL) {
|
if(ipr == NULL) {
|
||||||
goto nullreturn;
|
goto nullreturn;
|
||||||
|
@ -412,7 +510,7 @@ ip_reass(struct pbuf *p)
|
||||||
}
|
}
|
||||||
/* find the right place to insert this pbuf */
|
/* find the right place to insert this pbuf */
|
||||||
/* @todo: trim pbufs if fragments are overlapping */
|
/* @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
|
/* the totally last fragment (flag more fragments = 0) was received at least
|
||||||
* once AND all fragments are received */
|
* once AND all fragments are received */
|
||||||
ipr->packet_len += IP_HLEN;
|
ipr->packet_len += IP_HLEN;
|
||||||
|
@ -441,7 +539,7 @@ ip_reass(struct pbuf *p)
|
||||||
r = iprh->next_pbuf;
|
r = iprh->next_pbuf;
|
||||||
}
|
}
|
||||||
/* release the sources allocate for the fragment queue entry */
|
/* 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. */
|
/* and adjust the number of pbufs currently queued for reassembly. */
|
||||||
ip_reass_pbufcount -= pbuf_clen(p);
|
ip_reass_pbufcount -= pbuf_clen(p);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user