NetServices Stack source
Dependents: HelloWorld ServoInterfaceBoardExample1 4180_Lab4
Diff: lwip/core/tcp.c
- Revision:
- 5:dd63a1e02b1b
- Parent:
- 0:632c9925f013
--- a/lwip/core/tcp.c Fri Jul 09 14:46:47 2010 +0000 +++ b/lwip/core/tcp.c Tue Jul 27 15:59:42 2010 +0000 @@ -55,7 +55,7 @@ #include <string.h> -const char *tcp_state_str[] = { +const char * const tcp_state_str[] = { "CLOSED", "LISTEN", "SYN_SENT", @@ -88,6 +88,12 @@ /** List of all TCP PCBs in TIME-WAIT state */ struct tcp_pcb *tcp_tw_pcbs; +#define NUM_TCP_PCB_LISTS 4 +#define NUM_TCP_PCB_LISTS_NO_TIME_WAIT 3 +/** An array with all (non-temporary) PCB lists, mainly used for smaller code size */ +struct tcp_pcb **tcp_pcb_lists[] = {&tcp_listen_pcbs.pcbs, &tcp_bound_pcbs, + &tcp_active_pcbs, &tcp_tw_pcbs}; + /** Only used for temporary storage. */ struct tcp_pcb *tcp_tmp_pcb; @@ -134,11 +140,26 @@ err_t err; if (rst_on_unacked_data && (pcb->state != LISTEN)) { - if ((pcb->refused_data != NULL) || (pcb->rcv_wnd != TCP_WND)) { + if ((pcb->refused_data != NULL) || (pcb->rcv_wnd != TCP_WND)) { /* Not all data received by application, send RST to tell the remote side about this. */ + LWIP_ASSERT("pcb->flags & TF_RXCLOSED", pcb->flags & TF_RXCLOSED); + + /* don't call tcp_abort here: we must not deallocate the pcb since + that might not be expected when calling tcp_close */ tcp_rst(pcb->snd_nxt, pcb->rcv_nxt, &pcb->local_ip, &pcb->remote_ip, pcb->local_port, pcb->remote_port); + + tcp_pcb_purge(pcb); + + /* TODO: to which state do we move now? */ + + /* move to TIME_WAIT since we close actively */ + TCP_RMV(&tcp_active_pcbs, pcb); + pcb->state = TIME_WAIT; + TCP_REG(&tcp_tw_pcbs, pcb); + + return ERR_OK; } } @@ -158,7 +179,7 @@ break; case LISTEN: err = ERR_OK; - tcp_pcb_remove((struct tcp_pcb **)&tcp_listen_pcbs.pcbs, pcb); + tcp_pcb_remove(&tcp_listen_pcbs.pcbs, pcb); memp_free(MEMP_TCP_PCB_LISTEN, pcb); pcb = NULL; break; @@ -302,7 +323,9 @@ #endif /* LWIP_CALLBACK_API */ void *errf_arg; - + /* pcb->state LISTEN not allowed here */ + LWIP_ASSERT("don't call tcp_abort/tcp_abandon for listen-pcbs", + pcb->state != LISTEN); /* Figure out on which TCP PCB list we are, and remove us. If we are in an active state, call the receive function associated with the PCB with a NULL argument, and send an RST to the remote end. */ @@ -310,7 +333,6 @@ tcp_pcb_remove(&tcp_tw_pcbs, pcb); memp_free(MEMP_TCP_PCB, pcb); } else { - /* @todo: pcb->state, LISTEN not allowed */ seqno = pcb->snd_nxt; ackno = pcb->rcv_nxt; ip_addr_copy(local_ip, pcb->local_ip); @@ -374,55 +396,46 @@ err_t tcp_bind(struct tcp_pcb *pcb, ip_addr_t *ipaddr, u16_t port) { + int i; + int max_pcb_list = NUM_TCP_PCB_LISTS; struct tcp_pcb *cpcb; LWIP_ERROR("tcp_bind: can only bind in state CLOSED", pcb->state == CLOSED, return ERR_ISCONN); +#if SO_REUSE + /* Unless the REUSEADDR flag is set, + we have to check the pcbs in TIME-WAIT state, also. + We do not dump TIME_WAIT pcb's; they can still be matched by incoming + packets using both local and remote IP addresses and ports to distinguish. + */ +#if SO_REUSE + if ((pcb->so_options & SOF_REUSEADDR) != 0) { + max_pcb_list = NUM_TCP_PCB_LISTS_NO_TIME_WAIT; + } +#endif /* SO_REUSE */ +#endif /* SO_REUSE */ + if (port == 0) { port = tcp_new_port(); } - /* Check if the address already is in use. */ - /* Check the listen pcbs. */ - for(cpcb = (struct tcp_pcb *)tcp_listen_pcbs.pcbs; - cpcb != NULL; cpcb = cpcb->next) { - if (cpcb->local_port == port) { - if (ip_addr_isany(&(cpcb->local_ip)) || - ip_addr_isany(ipaddr) || - ip_addr_cmp(&(cpcb->local_ip), ipaddr)) { - return ERR_USE; - } - } - } - /* Check the connected pcbs. */ - for(cpcb = tcp_active_pcbs; - cpcb != NULL; cpcb = cpcb->next) { - if (cpcb->local_port == port) { - if (ip_addr_isany(&(cpcb->local_ip)) || - ip_addr_isany(ipaddr) || - ip_addr_cmp(&(cpcb->local_ip), ipaddr)) { - return ERR_USE; - } - } - } - /* Check the bound, not yet connected pcbs. */ - for(cpcb = tcp_bound_pcbs; cpcb != NULL; cpcb = cpcb->next) { - if (cpcb->local_port == port) { - if (ip_addr_isany(&(cpcb->local_ip)) || - ip_addr_isany(ipaddr) || - ip_addr_cmp(&(cpcb->local_ip), ipaddr)) { - return ERR_USE; - } - } - } - /* Unless the REUSEADDR flag is set, - * we have to check the pcbs in TIME-WAIT state, also: */ - if ((pcb->so_options & SOF_REUSEADDR) == 0) { - for(cpcb = tcp_tw_pcbs; cpcb != NULL; cpcb = cpcb->next) { + + /* Check if the address already is in use (on all lists) */ + for (i = 0; i < max_pcb_list; i++) { + for(cpcb = *tcp_pcb_lists[i]; cpcb != NULL; cpcb = cpcb->next) { if (cpcb->local_port == port) { - if (ip_addr_isany(&(cpcb->local_ip)) || - ip_addr_isany(ipaddr) || - ip_addr_cmp(&(cpcb->local_ip), ipaddr)) { - return ERR_USE; +#if SO_REUSE + /* Omit checking for the same port if both pcbs have REUSEADDR set. + For SO_REUSEADDR, the duplicate-check for a 5-tuple is done in + tcp_connect. */ + if (((pcb->so_options & SOF_REUSEADDR) == 0) || + ((cpcb->so_options & SOF_REUSEADDR) == 0)) +#endif /* SO_REUSE */ + { + if (ip_addr_isany(&(cpcb->local_ip)) || + ip_addr_isany(ipaddr) || + ip_addr_cmp(&(cpcb->local_ip), ipaddr)) { + return ERR_USE; + } } } } @@ -477,6 +490,21 @@ if (pcb->state == LISTEN) { return pcb; } +#if SO_REUSE + if ((pcb->so_options & SOF_REUSEADDR) != 0) { + /* Since SOF_REUSEADDR allows reusing a local address before the pcb's usage + is declared (listen-/connection-pcb), we have to make sure now that + this port is only used once for every local IP. */ + for(lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) { + if (lpcb->local_port == pcb->local_port) { + if (ip_addr_cmp(&lpcb->local_ip, &pcb->local_ip)) { + /* this address/port is already used */ + return NULL; + } + } + } + } +#endif /* SO_REUSE */ lpcb = (struct tcp_pcb_listen *)memp_malloc(MEMP_TCP_PCB_LISTEN); if (lpcb == NULL) { return NULL; @@ -484,6 +512,7 @@ lpcb->callback_arg = pcb->callback_arg; lpcb->local_port = pcb->local_port; lpcb->state = LISTEN; + lpcb->prio = pcb->prio; lpcb->so_options = pcb->so_options; lpcb->so_options |= SOF_ACCEPTCONN; lpcb->ttl = pcb->ttl; @@ -498,7 +527,7 @@ lpcb->accepts_pending = 0; lpcb->backlog = (backlog ? backlog : 1); #endif /* TCP_LISTEN_BACKLOG */ - TCP_REG(&tcp_listen_pcbs.listen_pcbs, lpcb); + TCP_REG(&tcp_listen_pcbs.pcbs, (struct tcp_pcb *)lpcb); return (struct tcp_pcb *)lpcb; } @@ -576,6 +605,7 @@ static u16_t tcp_new_port(void) { + int i; struct tcp_pcb *pcb; #ifndef TCP_LOCAL_PORT_RANGE_START #define TCP_LOCAL_PORT_RANGE_START 4096 @@ -587,20 +617,12 @@ if (++port > TCP_LOCAL_PORT_RANGE_END) { port = TCP_LOCAL_PORT_RANGE_START; } - - for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) { - if (pcb->local_port == port) { - goto again; - } - } - for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) { - if (pcb->local_port == port) { - goto again; - } - } - for(pcb = (struct tcp_pcb *)tcp_listen_pcbs.pcbs; pcb != NULL; pcb = pcb->next) { - if (pcb->local_port == port) { - goto again; + /* Check all PCB lists. */ + for (i = 1; i < NUM_TCP_PCB_LISTS; i++) { + for(pcb = *tcp_pcb_lists[i]; pcb != NULL; pcb = pcb->next) { + if (pcb->local_port == port) { + goto again; + } } } return port; @@ -634,9 +656,43 @@ return ERR_VAL; } pcb->remote_port = port; + + /* check if we have a route to the remote host */ + if (ip_addr_isany(&(pcb->local_ip))) { + /* no local IP address set, yet. */ + struct netif *netif = ip_route(&(pcb->remote_ip)); + if (netif == NULL) { + /* Don't even try to send a SYN packet if we have no route + since that will fail. */ + return ERR_RTE; + } + /* Use the netif's IP address as local address. */ + ip_addr_copy(pcb->local_ip, netif->ip_addr); + } + if (pcb->local_port == 0) { pcb->local_port = tcp_new_port(); } +#if SO_REUSE + if ((pcb->so_options & SOF_REUSEADDR) != 0) { + /* Since SOF_REUSEADDR allows reusing a local address, we have to make sure + now that the 5-tuple is unique. */ + struct tcp_pcb *cpcb; + int i; + /* Don't check listen PCBs, check bound-, active- and TIME-WAIT PCBs. */ + for (i = 1; i < NUM_TCP_PCB_LISTS; i++) { + for(cpcb = *tcp_pcb_lists[i]; cpcb != NULL; cpcb = cpcb->next) { + if ((cpcb->local_port == pcb->local_port) && + (cpcb->remote_port == port) && + ip_addr_cmp(&cpcb->local_ip, &pcb->local_ip) && + ip_addr_cmp(&cpcb->remote_ip, ipaddr)) { + /* linux returns EISCONN here, but ERR_USE should be OK for us */ + return ERR_USE; + } + } + } + } +#endif /* SO_REUSE */ iss = tcp_next_iss(); pcb->rcv_nxt = 0; pcb->snd_nxt = iss; @@ -656,8 +712,12 @@ pcb->ssthresh = pcb->mss * 10; #if LWIP_CALLBACK_API pcb->connected = connected; +#else /* LWIP_CALLBACK_API */ + LWIP_UNUSED_ARG(connected); #endif /* LWIP_CALLBACK_API */ + LWIP_DEBUGF(TCP_DEBUG, ("tcp_connect: tcp_ticks=%"U32_F" iss=%"U32_F"\n", tcp_ticks, iss)); + /* Send a SYN together with the MSS option. */ ret = tcp_enqueue_flags(pcb, TCP_SYN); if (ret == ERR_OK) { @@ -687,10 +747,13 @@ u8_t pcb_remove; /* flag if a PCB should be removed */ u8_t pcb_reset; /* flag if a RST should be sent when removing */ err_t err; + + tcp_debug_print_pcbs(); //DG err = ERR_OK; ++tcp_ticks; + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: tcp_ticks=%"U32_F"\n", tcp_ticks)); /* Steps through all of the active PCBs. */ prev = NULL; @@ -998,8 +1061,8 @@ { pcb->prio = prio; } + #if TCP_QUEUE_OOSEQ - /** * Returns a copy of the given TCP segment. * The pbuf and data are not copied, only the pointers @@ -1020,7 +1083,7 @@ pbuf_ref(cseg->p); return cseg; } -#endif +#endif /* TCP_QUEUE_OOSEQ */ #if LWIP_CALLBACK_API /** @@ -1072,7 +1135,7 @@ LWIP_DEBUGF(TCP_DEBUG, ("tcp_kill_prio: killing oldest PCB %p (%"S32_F")\n", (void *)inactive, inactivity)); tcp_abort(inactive); - } + } } /** @@ -1098,7 +1161,7 @@ LWIP_DEBUGF(TCP_DEBUG, ("tcp_kill_timewait: killing oldest TIME-WAIT PCB %p (%"S32_F")\n", (void *)inactive, inactivity)); tcp_abort(inactive); - } + } } /** @@ -1159,6 +1222,8 @@ pcb->lastack = iss; pcb->snd_lbb = iss; pcb->tmr = tcp_ticks; + + LWIP_DEBUGF(TCP_DEBUG, ("tcp_alloc: tcp_ticks=%"U32_F" iss=%"U32_F"\n", tcp_ticks, iss)); pcb->polltmr = 0; @@ -1278,6 +1343,8 @@ { #if LWIP_CALLBACK_API pcb->poll = poll; +#else /* LWIP_CALLBACK_API */ + LWIP_UNUSED_ARG(poll); #endif /* LWIP_CALLBACK_API */ pcb->pollinterval = interval; } @@ -1329,7 +1396,7 @@ if (pcb->unacked != NULL) { LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: data left on ->unacked\n")); } -#if TCP_QUEUE_OOSEQ /* LW */ +#if TCP_QUEUE_OOSEQ if (pcb->ooseq != NULL) { LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: data left on ->ooseq\n")); }