Fix BUG#11400 - don't corrupt existing pbuf chain when enqueuing multiple pbufs to a pending ARP request

This commit is contained in:
goldsimon
2007-03-04 12:12:42 +00:00
parent e1b6a4cb21
commit 1f544e087b
6 changed files with 93 additions and 28 deletions

View File

@@ -93,7 +93,7 @@ struct etharp_entry {
/**
* Pointer to queue of pending outgoing packets on this ARP entry.
*/
struct pbuf *p;
struct etharp_q_entry *q;
#endif
struct ip_addr ipaddr;
struct eth_addr ethaddr;
@@ -123,13 +123,29 @@ etharp_init(void)
for(i = 0; i < ARP_TABLE_SIZE; ++i) {
arp_table[i].state = ETHARP_STATE_EMPTY;
#if ARP_QUEUEING
arp_table[i].p = NULL;
arp_table[i].q = NULL;
#endif
arp_table[i].ctime = 0;
arp_table[i].netif = NULL;
}
}
#if ARP_QUEUEING
/** Free a complete queue of etharp entrys */
static void free_etharp_q(struct etharp_q_entry *q)
{
struct etharp_q_entry *r;
LWIP_ASSERT("q != NULL", q != NULL);
LWIP_ASSERT("q->p != NULL", q->p != NULL);
while(q) {
r = q;
q = q->next;
pbuf_free(r->p);
memp_free(MEMP_ARP_QUEUE, r);
}
}
#endif
/**
* Clears expired entries in the ARP table.
*
@@ -158,7 +174,7 @@ etharp_tmr(void)
LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_timer: expired pending entry %"U16_F".\n", (u16_t)i));
arp_table[i].state = ETHARP_STATE_EXPIRED;
#if ARP_QUEUEING
} else if (arp_table[i].p != NULL) {
} else if (arp_table[i].q != NULL) {
/* resend an ARP query here */
#endif
}
@@ -169,11 +185,11 @@ etharp_tmr(void)
snmp_delete_arpidx_tree(arp_table[i].netif, &arp_table[i].ipaddr);
#if ARP_QUEUEING
/* and empty packet queue */
if (arp_table[i].p != NULL) {
if (arp_table[i].q != NULL) {
/* remove all queued packets */
LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_timer: freeing entry %"U16_F", packet queue %p.\n", (u16_t)i, (void *)(arp_table[i].p)));
pbuf_free(arp_table[i].p);
arp_table[i].p = NULL;
LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_timer: freeing entry %"U16_F", packet queue %p.\n", (u16_t)i, (void *)(arp_table[i].q)));
free_etharp_q(arp_table[i].q);
arp_table[i].q = NULL;
}
#endif
/* recycle entry for re-use */
@@ -247,7 +263,7 @@ static s8_t find_entry(struct ip_addr *ipaddr, u8_t flags)
return i;
#if ARP_QUEUEING
/* pending with queued packets? */
} else if (arp_table[i].p != NULL) {
} else if (arp_table[i].q != NULL) {
if (arp_table[i].ctime >= age_queue) {
old_queue = i;
age_queue = arp_table[i].ctime;
@@ -304,7 +320,7 @@ static s8_t find_entry(struct ip_addr *ipaddr, u8_t flags)
LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("find_entry: selecting oldest stable entry %"U16_F"\n", (u16_t)i));
#if ARP_QUEUEING
/* no queued packets should exist on stable entries */
LWIP_ASSERT("arp_table[i].p == NULL", arp_table[i].p == NULL);
LWIP_ASSERT("arp_table[i].q == NULL", arp_table[i].q == NULL);
#endif
/* 3) found recyclable pending entry without queued packets? */
} else if (old_pending < ARP_TABLE_SIZE) {
@@ -316,9 +332,9 @@ static s8_t find_entry(struct ip_addr *ipaddr, u8_t flags)
} else if (old_queue < ARP_TABLE_SIZE) {
/* recycle oldest pending */
i = old_queue;
LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("find_entry: selecting oldest pending entry %"U16_F", freeing packet queue %p\n", (u16_t)i, (void *)(arp_table[i].p)));
pbuf_free(arp_table[i].p);
arp_table[i].p = NULL;
LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("find_entry: selecting oldest pending entry %"U16_F", freeing packet queue %p\n", (u16_t)i, (void *)(arp_table[i].q)));
free_etharp_q(arp_table[i].q);
arp_table[i].q = NULL;
#endif
/* no empty or recyclable entries found */
} else {
@@ -405,14 +421,19 @@ update_arp_entry(struct netif *netif, struct ip_addr *ipaddr, struct eth_addr *e
arp_table[i].ctime = 0;
/* this is where we will send out queued packets! */
#if ARP_QUEUEING
while (arp_table[i].p != NULL) {
/* get the first packet on the queue */
struct pbuf *p = arp_table[i].p;
while (arp_table[i].q != NULL) {
struct pbuf *p;
struct eth_hdr *ethhdr;
/* remember remainder of queue */
struct etharp_q_entry *q = arp_table[i].q;
/* pop first item off the queue */
arp_table[i].q = q->next;
/* get the packet pointer */
p = q->p;
/* now queue entry can be freed */
memp_free(MEMP_ARP_QUEUE, q);
/* Ethernet header */
struct eth_hdr *ethhdr = p->payload;
/* remember (and reference) remainder of queue */
/* note: this will also terminate the p pbuf chain */
arp_table[i].p = pbuf_dequeue(p);
ethhdr = p->payload;
/* fill-in Ethernet header */
k = netif->hwaddr_len;
while(k > 0) {
@@ -810,19 +831,29 @@ err_t etharp_query(struct netif *netif, struct ip_addr *ipaddr, struct pbuf *q)
/* copy any PBUF_REF referenced payloads into PBUF_RAM */
/* (the caller of lwIP assumes the referenced payload can be
* freed after it returns from the lwIP call that brought us here) */
/* @todo: if q->type == PBUF_REF, do we have a memory leak (since we call pbuf_ref(p) later)? */
p = pbuf_take(q);
/* packet could be taken over? */
if (p != NULL) {
/* queue packet ... */
if (arp_table[i].p == NULL) {
/* ... in the empty queue */
pbuf_ref(p);
arp_table[i].p = p;
#if 0 /* multi-packet-queueing disabled, see bug #11400 */
struct etharp_q_entry *newEntry;
/* allocate a new arp queue entry */
newEntry = memp_malloc(MEMP_ARP_QUEUE);
LWIP_ASSERT("newEntry != NULL", newEntry != NULL);
newEntry->next = 0;
newEntry->p = p;
pbuf_ref(p);
if(arp_table[i].q != NULL) {
/* queue was already existent, append the new entry to the end */
struct etharp_q_entry *r;
r = arp_table[i].q;
while(r->next != NULL) {
r = r->next;
}
r->next = newEntry;
} else {
/* ... at tail of non-empty queue */
pbuf_queue(arp_table[i].p, p);
#endif
/* queue did not exist, first item in queue */
arp_table[i].q = newEntry;
}
LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_query: queued packet %p on ARP entry %"S16_F"\n", (void *)q, (s16_t)i));
result = ERR_OK;