IP_REASSEMBLY: send ICMP time exceeded when discarding datagrams for which the first fragment was received; try to keep the header of the first (octet 0) fragment; combined code to make it smaller; fixed bug in timer: when freeing packets, counter was not updated correctly

This commit is contained in:
goldsimon 2007-10-15 19:33:16 +00:00
parent e864ba6c91
commit 6d9b44e243
2 changed files with 86 additions and 48 deletions

View File

@ -255,7 +255,7 @@ icmp_dest_unreach(struct pbuf *p, enum icmp_dur_type t)
pbuf_free(q); pbuf_free(q);
} }
#if IP_FORWARD #if IP_FORWARD || IP_REASSEMBLY
/** /**
* Send a 'time exceeded' packet, called from ip_forward() if TTL is 0. * Send a 'time exceeded' packet, called from ip_forward() if TTL is 0.
* *

View File

@ -33,6 +33,7 @@
* This file is part of the lwIP TCP/IP stack. * This file is part of the lwIP TCP/IP stack.
* *
* Author: Jani Monoses <jani@iv.ro> * Author: Jani Monoses <jani@iv.ro>
* Simon Goldschmidt
* original reassembly code by Adam Dunkels <adam@sics.se> * original reassembly code by Adam Dunkels <adam@sics.se>
* *
*/ */
@ -43,6 +44,9 @@
#include "lwip/netif.h" #include "lwip/netif.h"
#include "lwip/snmp.h" #include "lwip/snmp.h"
#include "lwip/stats.h" #include "lwip/stats.h"
#if LWIP_ICMP
#include "lwip/icmp.h"
#endif /* LWIP_ICMP */
#include <string.h> #include <string.h>
@ -77,8 +81,6 @@
/** This is a helper struct which holds the starting /** This is a helper struct which holds the starting
* offset and the ending offset of this fragment to * offset and the ending offset of this fragment to
* easily chain the fragments. * easily chain the fragments.
* It must be datagram because the memory of the ip header
* of fragments is misused for holding this data.
*/ */
struct ip_reass_helper { struct ip_reass_helper {
struct pbuf *next_pbuf; struct pbuf *next_pbuf;
@ -97,6 +99,7 @@ static u16_t ip_reass_pbufcount;
/* function prototypes */ /* function prototypes */
static void ip_reass_dequeue_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev); static void ip_reass_dequeue_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev);
static int ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev);
/** /**
* Reassembly timer base function * Reassembly timer base function
@ -108,7 +111,6 @@ void
ip_reass_tmr(void) ip_reass_tmr(void)
{ {
struct ip_reassdata *r, *prev = NULL; struct ip_reassdata *r, *prev = NULL;
struct ip_reass_helper *iprh;
r = reassdatagrams; r = reassdatagrams;
while (r != NULL) { while (r != NULL) {
@ -120,33 +122,75 @@ ip_reass_tmr(void)
prev = r; prev = r;
r = r->next; r = r->next;
} else { } else {
/* reassembly timed out */ /* reassembly timed out */
struct pbuf *p; struct ip_reassdata *tmp;
struct ip_reassdata *del;
LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass_tmr: timer timed out")); LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass_tmr: timer timed out"));
tmp = r;
snmp_inc_ipreasmfails(); /* get the next pointer before freeing */
/* First, free all received pbufs. The individual pbufs need to be released
separately as they have not yet been chained */
p = r->p;
while (p != NULL) {
struct pbuf *pcur;
iprh = (struct ip_reass_helper *)p->payload;
pcur = p;
p = iprh->next_pbuf;
pbuf_free(pcur);
ip_reass_pbufcount--;
}
/* Then, unchain the struct ip_reassdata from the list and free it. */
del = r;
r = r->next; r = r->next;
ip_reass_dequeue_datagram(del, prev); /* free the helper struct and all enqueued pbufs */
ip_reass_free_complete_datagram(tmp, prev);
} }
} }
} }
/**
* Free a datagram (struct ip_reassdata) and all its pbufs.
* Updates the total count of enqueued pbufs (ip_reass_pbufcount),
* SNMP counters and sends an ICMP time exceeded packet.
*
* @param ipr datagram to free
* @param prev the previous datagram in the linked list
* @return the number of pbufs freed
*/
static int
ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev)
{
int pbufs_freed = 0;
struct pbuf *p;
struct ip_reass_helper *iprh;
LWIP_ASSERT("prev != ipr", prev != ipr);
if (prev != NULL) {
LWIP_ASSERT("prev->next == ipr", prev->next == ipr);
}
snmp_inc_ipreasmfails();
#if LWIP_ICMP
iprh = (struct ip_reass_helper *)ipr->p->payload;
if (iprh->start == 0) {
/* The first fragment was received, send ICMP time exceeded. */
/* First, de-queue the first pbuf from r->p. */
p = ipr->p;
ipr->p = iprh->next_pbuf;
/* Then, copy the original header into it. */
SMEMCPY(p->payload, &ipr->iphdr, IP_HLEN);
icmp_time_exceeded(p, ICMP_TE_FRAG);
pbufs_freed += pbuf_clen(p);
pbuf_free(p);
}
#endif /* LWIP_ICMP */
/* First, free all received pbufs. The individual pbufs need to be released
separately as they have not yet been chained */
p = ipr->p;
while (p != NULL) {
struct pbuf *pcur;
iprh = (struct ip_reass_helper *)p->payload;
pcur = p;
/* get the next pointer before freeing */
p = iprh->next_pbuf;
pbufs_freed += pbuf_clen(pcur);
pbuf_free(pcur);
}
/* Then, unchain the struct ip_reassdata from the list and free it. */
ip_reass_dequeue_datagram(ipr, prev);
LWIP_ASSERT("ip_reass_pbufcount >= clen", ip_reass_pbufcount >= pbufs_freed);
ip_reass_pbufcount -= pbufs_freed;
return pbufs_freed;
}
#if IP_REASS_FREE_OLDEST #if IP_REASS_FREE_OLDEST
/** /**
* Free the oldest datagram to make room for enqueueing new fragments. * Free the oldest datagram to make room for enqueueing new fragments.
@ -191,26 +235,7 @@ ip_reass_remove_oldest_datagram(struct ip_hdr *fraghdr, int pbufs_needed)
r = r->next; r = r->next;
} }
if (oldest != NULL) { if (oldest != NULL) {
struct ip_reass_helper *iprh; pbufs_freed_current = ip_reass_free_complete_datagram(oldest, prev);
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 datagram. */
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_datagram(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; pbufs_freed += pbufs_freed_current;
} }
} while ((pbufs_freed < pbufs_needed) && (other_datagrams > 1)); } while ((pbufs_freed < pbufs_needed) && (other_datagrams > 1));
@ -331,9 +356,12 @@ ip_reass_chain_frag_into_datagram_and_validate(struct ip_reassdata *ipr, struct
ipr->p = new_p; ipr->p = new_p;
} }
break; break;
} else if(iprh->start == iprh_tmp->start) {
/* received the same datagram twice: no need to keep the datagram */
goto freepbuf;
#if IP_REASS_CHECK_OVERLAP #if IP_REASS_CHECK_OVERLAP
} else if((iprh->start == iprh_tmp->start) || (iprh->start < iprh_tmp->end)) { } else if(iprh->start < iprh_tmp->end) {
/* received the same datagram twice, or overlap: no need to keep the new datagram */ /* overlap: no need to keep the new datagram */
goto freepbuf; goto freepbuf;
#endif /* IP_REASS_CHECK_OVERLAP */ #endif /* IP_REASS_CHECK_OVERLAP */
} else { } else {
@ -463,8 +491,9 @@ ip_reass(struct pbuf *p)
{ {
/* No datagram could be freed and still too many pbufs enqueued */ /* No datagram 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);
/* @todo: send ICMP time exceeded here? */
/* drop this pbuf */ /* drop this pbuf */
goto nullreturn; goto nullreturn;
} }
@ -492,6 +521,15 @@ ip_reass(struct pbuf *p)
if(ipr == NULL) { if(ipr == NULL) {
goto nullreturn; goto nullreturn;
} }
} else {
if (((ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) == 0) &&
((ntohs(IPH_OFFSET(&ipr->iphdr)) & IP_OFFMASK) != 0)) {
/* ipr->iphdr is not the header from the first fragment, but fraghdr is
* -> copy fraghdr into ipr->iphdr since we want to have the header
* of the first fragment (for ICMP time exceeded and later, for copying
* all options, if supported)*/
SMEMCPY(&ipr->iphdr, fraghdr, IP_HLEN);
}
} }
/* Track the current number of pbufs current 'in-flight', in order to limit /* Track the current number of pbufs current 'in-flight', in order to limit
the number of fragments that may be enqueued at any one time */ the number of fragments that may be enqueued at any one time */