SO_REUSE: tcp_input: correctly handle multiple pcbs listening on the same port (but different address): first search for a specific address an only pass to ANY if no specific address has been found listening

This commit is contained in:
goldsimon 2010-05-15 16:45:43 +00:00
parent 7e5b0a9eb6
commit a945bf07af

View File

@ -93,6 +93,10 @@ tcp_input(struct pbuf *p, struct netif *inp)
{ {
struct tcp_pcb *pcb, *prev; struct tcp_pcb *pcb, *prev;
struct tcp_pcb_listen *lpcb; struct tcp_pcb_listen *lpcb;
#if SO_REUSE
struct tcp_pcb *lpcb_prev = NULL;
struct tcp_pcb_listen *lpcb_any = NULL;
#endif /* SO_REUSE */
u8_t hdrlen; u8_t hdrlen;
err_t err; err_t err;
@ -218,31 +222,55 @@ tcp_input(struct pbuf *p, struct netif *inp)
} }
} }
/* Finally, if we still did not get a match, we check all PCBs that /* Finally, if we still did not get a match, we check all PCBs that
are LISTENing for incoming connections. */ are LISTENing for incoming connections. */
prev = NULL; prev = NULL;
for(lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) { for(lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) {
if ((ip_addr_isany(&(lpcb->local_ip)) || if (lpcb->local_port == tcphdr->dest) {
ip_addr_cmp(&(lpcb->local_ip), &(iphdr->dest))) && #if SO_REUSE
lpcb->local_port == tcphdr->dest) { if (ip_addr_cmp(&(lpcb->local_ip), &(iphdr->dest))) {
/* Move this PCB to the front of the list so that subsequent /* found an exact match */
lookups will be faster (we exploit locality in TCP segment break;
arrivals). */ } else if(ip_addr_isany(&(lpcb->local_ip))) {
if (prev != NULL) { /* found an ANY-match */
((struct tcp_pcb_listen *)prev)->next = lpcb->next; lpcb_any = lpcb;
/* our successor is the remainder of the listening list */ lpcb_prev = prev;
lpcb->next = tcp_listen_pcbs.listen_pcbs;
/* put this listening pcb at the head of the listening list */
tcp_listen_pcbs.listen_pcbs = lpcb;
} }
#else /* SO_REUSE */
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packed for LISTENing connection.\n")); if (ip_addr_cmp(&(lpcb->local_ip), &(iphdr->dest)) ||
tcp_listen_input(lpcb); ip_addr_isany(&(lpcb->local_ip))) {
pbuf_free(p); /* found a match */
return; break;
}
#endif /* SO_REUSE */
} }
prev = (struct tcp_pcb *)lpcb; prev = (struct tcp_pcb *)lpcb;
} }
#if SO_REUSE
/* first try specific local IP */
if (lpcb == NULL) {
/* only pass to ANY if no specific local IP has been found */
lpcb = lpcb_any;
prev = lpcb_prev;
}
#endif /* SO_REUSE */
if (lpcb != NULL) {
/* Move this PCB to the front of the list so that subsequent
lookups will be faster (we exploit locality in TCP segment
arrivals). */
if (prev != NULL) {
((struct tcp_pcb_listen *)prev)->next = lpcb->next;
/* our successor is the remainder of the listening list */
lpcb->next = tcp_listen_pcbs.listen_pcbs;
/* put this listening pcb at the head of the listening list */
tcp_listen_pcbs.listen_pcbs = lpcb;
}
LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packed for LISTENing connection.\n"));
tcp_listen_input(lpcb);
pbuf_free(p);
return;
}
} }
#if TCP_INPUT_DEBUG #if TCP_INPUT_DEBUG