From dff08f9739a34e1d32763db8b1004b56bb5dba7d Mon Sep 17 00:00:00 2001 From: kieranm Date: Mon, 22 Sep 2003 09:38:09 +0000 Subject: [PATCH] Applied patch #1912, but had to make changes to get it to compile --- src/api/sockets.c | 24 ++++--- src/core/tcp.c | 101 +++++++++++++++++++++++++++- src/core/tcp_in.c | 62 ++++++++++++++++- src/core/udp.c | 148 ++++++++++++++++++++++++++++++++++++++++- src/include/lwip/opt.h | 8 +++ 5 files changed, 331 insertions(+), 12 deletions(-) diff --git a/src/api/sockets.c b/src/api/sockets.c index f18ad2a8..8fd4225a 100644 --- a/src/api/sockets.c +++ b/src/api/sockets.c @@ -1007,8 +1007,10 @@ int lwip_getsockopt (int s, int level, int optname, void *optval, socklen_t *opt /* UNIMPL case SO_SNDBUF: */ /* UNIMPL case SO_RCVLOWAT: */ /* UNIMPL case SO_SNDLOWAT: */ - /* UNIMPL case SO_REUSEADDR: */ - /* UNIMPL case SO_REUSEPORT: */ +#ifdef SO_REUSE + case SO_REUSEADDR: + case SO_REUSEPORT: +#endif /* SO_REUSE */ case SO_TYPE: /* UNIMPL case SO_USELOOPBACK: */ if( *optlen < sizeof(int) ) { @@ -1091,8 +1093,10 @@ int lwip_getsockopt (int s, int level, int optname, void *optval, socklen_t *opt /* UNIMPL case SO_DONTROUTE: */ case SO_KEEPALIVE: /* UNIMPL case SO_OOBINCLUDE: */ - /* UNIMPL case SO_REUSEADDR: */ - /* UNIMPL case SO_REUSEPORT: */ +#ifdef SO_REUSE + case SO_REUSEADDR: + case SO_REUSEPORT: +#endif /* SO_REUSE */ /*case SO_USELOOPBACK: UNIMPL */ *(int*)optval = sock->conn->pcb.tcp->so_options & optname; LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, optname=0x%x, ..) = %s\n", s, optname, (*(int*)optval?"on":"off"))); @@ -1192,8 +1196,10 @@ int lwip_setsockopt (int s, int level, int optname, const void *optval, socklen_ /* UNIMPL case SO_SNDBUF: */ /* UNIMPL case SO_RCVLOWAT: */ /* UNIMPL case SO_SNDLOWAT: */ - /* UNIMPL case SO_REUSEADDR: */ - /* UNIMPL case SO_REUSEPORT: */ +#ifdef SO_REUSE + case SO_REUSEADDR: + case SO_REUSEPORT: +#endif /* SO_REUSE */ /* UNIMPL case SO_USELOOPBACK: */ if( optlen < sizeof(int) ) { err = EINVAL; @@ -1272,8 +1278,10 @@ int lwip_setsockopt (int s, int level, int optname, const void *optval, socklen_ /* UNIMPL case SO_DONTROUTE: */ case SO_KEEPALIVE: /* UNIMPL case SO_OOBINCLUDE: */ - /* UNIMPL case SO_REUSEADDR: */ - /* UNIMPL case SO_REUSEPORT: */ +#ifdef SO_REUSE + case SO_REUSEADDR: + case SO_REUSEPORT: +#endif /* SO_REUSE */ /* UNIMPL case SO_USELOOPBACK: */ if ( *(int*)optval ) { sock->conn->pcb.tcp->so_options |= optname; diff --git a/src/core/tcp.c b/src/core/tcp.c index 4aa51bdf..62a4f49a 100644 --- a/src/core/tcp.c +++ b/src/core/tcp.c @@ -247,11 +247,14 @@ err_t tcp_bind(struct tcp_pcb *pcb, struct ip_addr *ipaddr, u16_t port) { struct tcp_pcb *cpcb; +#ifdef SO_REUSE + int reuse_port_all_set = 1; +#endif /* SO_REUSE */ if (port == 0) { port = tcp_new_port(); } - +#ifndef SO_REUSE /* Check if the address already is in use. */ for(cpcb = (struct tcp_pcb *)tcp_listen_pcbs; cpcb != NULL; cpcb = cpcb->next) { @@ -273,6 +276,102 @@ tcp_bind(struct tcp_pcb *pcb, struct ip_addr *ipaddr, u16_t port) } } } +#else /* SO_REUSE */ + /* Search through list of PCB's in LISTEN state. + + If there is a PCB bound to specified port and IP_ADDR_ANY another PCB can be bound to the interface IP + or to the loopback address on the same port if SOF_REUSEADDR is set. Any combination of PCB's bound to + the same local port, but to one address out of {IP_ADDR_ANY, 127.0.0.1, interface IP} at a time is valid. + But no two PCB's bound to same local port and same local address is valid. + + If SOF_REUSEPORT is set several PCB's can be bound to same local port and same local address also. But then + all PCB's must have the SOF_REUSEPORT option set. + + When the two options aren't set and specified port is already bound, ERR_USE is returned saying that + address is already in use. */ + for(cpcb = (struct tcp_pcb *)tcp_listen_pcbs; cpcb != NULL; cpcb = cpcb->next) { + if(cpcb->local_port == port) { + if(ip_addr_cmp(&(cpcb->local_ip), ipaddr)) { + if(pcb->so_options & SOF_REUSEPORT) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_bind: in listening PCB's: SO_REUSEPORT set and same address.\n")); + reuse_port_all_set = (reuse_port_all_set && (cpcb->so_options & SOF_REUSEPORT)); + } + else { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_bind: in listening PCB's: SO_REUSEPORT not set and same address.\n")); + return ERR_USE; + } + } + else if((ip_addr_isany(ipaddr) && !ip_addr_isany(&(cpcb->local_ip))) || + (!ip_addr_isany(ipaddr) && ip_addr_isany(&(cpcb->local_ip)))) { + if(!(pcb->so_options & SOF_REUSEADDR) && !(pcb->so_options & SOF_REUSEPORT)) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_bind: in listening PCB's SO_REUSEPORT or SO_REUSEADDR not set and not the same address.\n")); + return ERR_USE; + } + else { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_bind: in listening PCB's SO_REUSEPORT or SO_REUSEADDR set and not the same address.\n")); + } + } + } + } + + /* Search through list of PCB's in a state in which they can accept or send data. Same decription as for + PCB's in state LISTEN applies to this PCB's regarding the options SOF_REUSEADDR and SOF_REUSEPORT. */ + for(cpcb = tcp_active_pcbs; cpcb != NULL; cpcb = cpcb->next) { + if(cpcb->local_port == port) { + if(ip_addr_cmp(&(cpcb->local_ip), ipaddr)) { + if(pcb->so_options & SOF_REUSEPORT) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_bind: in active PCB's SO_REUSEPORT set and same address.\n")); + reuse_port_all_set = (reuse_port_all_set && (cpcb->so_options & SOF_REUSEPORT)); + } + else { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_bind: in active PCB's SO_REUSEPORT not set and same address.\n")); + return ERR_USE; + } + } + else if((ip_addr_isany(ipaddr) && !ip_addr_isany(&(cpcb->local_ip))) || + (!ip_addr_isany(ipaddr) && ip_addr_isany(&(cpcb->local_ip)))) { + if(!(pcb->so_options & SOF_REUSEADDR) && !(pcb->so_options & SOF_REUSEPORT)) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_bind: in active PCB's SO_REUSEPORT or SO_REUSEADDR not set and not the same address.\n")); + return ERR_USE; + } + else { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_bind: in active PCB's SO_REUSEPORT or SO_REUSEADDR set and not the same address.\n")); + } + } + } + } + + /* Search through list of PCB's in TIME_WAIT state. If SO_REUSEADDR is set a bound combination [IP, port} + can be rebound. The same applies when SOF_REUSEPORT is set. + + If SOF_REUSEPORT is set several PCB's can be bound to same local port and same local address also. But then + all PCB's must have the SOF_REUSEPORT option set. + + When the two options aren't set and specified port is already bound, ERR_USE is returned saying that + address is already in use. */ + for(cpcb = tcp_tw_pcbs; cpcb != NULL; cpcb = cpcb->next) { + if(cpcb->local_port == port) { + if(ip_addr_cmp(&(cpcb->local_ip), ipaddr)) { + if(!(pcb->so_options & SOF_REUSEADDR) && !(pcb->so_options & SOF_REUSEPORT)) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_bind: in TIME_WAIT PCB's SO_REUSEPORT or SO_REUSEADDR not set and same address.\n")); + return ERR_USE; + } + else if(pcb->so_options & SOF_REUSEPORT) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_bind: in TIME_WAIT PCB's SO_REUSEPORT set and same address.\n")); + reuse_port_all_set = (reuse_port_all_set && (cpcb->so_options & SOF_REUSEPORT)); + } + } + } + } + + /* If SOF_REUSEPORT isn't set in all PCB's bound to specified port and local address specified then + {IP, port} can't be reused. */ + if(!reuse_port_all_set) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_bind: not all sockets have SO_REUSEPORT set.\n")); + return ERR_USE; + } +#endif /* SO_REUSE */ + if (!ip_addr_isany(ipaddr)) { pcb->local_ip = *ipaddr; } diff --git a/src/core/tcp_in.c b/src/core/tcp_in.c index 190f42b8..22a4b59c 100644 --- a/src/core/tcp_in.c +++ b/src/core/tcp_in.c @@ -102,6 +102,11 @@ tcp_input(struct pbuf *p, struct netif *inp) u8_t hdrlen; err_t err; +#ifdef SO_REUSE + struct tcp_pcb *pcb_temp; + int reuse = 0; + int reuse_port = 0; +#endif /* SO_REUSE */ PERF_START; @@ -174,7 +179,17 @@ tcp_input(struct pbuf *p, struct netif *inp) /* Demultiplex an incoming segment. First, we check if it is destined for an active connection. */ prev = NULL; + +#ifdef SO_REUSE + pcb_temp = tcp_active_pcbs; + + again_1: + + /* Iterate through the TCP pcb list for a fully matching pcb */ + for(pcb = pcb_temp; pcb != NULL; pcb = pcb->next) { +#else /* SO_REUSE */ for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) { +#endif /* SO_REUSE */ LWIP_ASSERT("tcp_input: active pcb->state != CLOSED", pcb->state != CLOSED); LWIP_ASSERT("tcp_input: active pcb->state != TIME-WAIT", pcb->state != TIME_WAIT); LWIP_ASSERT("tcp_input: active pcb->state != LISTEN", pcb->state != LISTEN); @@ -183,6 +198,32 @@ tcp_input(struct pbuf *p, struct netif *inp) ip_addr_cmp(&(pcb->remote_ip), &(iphdr->src)) && ip_addr_cmp(&(pcb->local_ip), &(iphdr->dest))) { +#ifdef SO_REUSE + if(pcb->so_options & SOF_REUSEPORT) { + if(reuse) { + /* We processed one PCB already */ + LWIP_DEBUGF(TCP_INPUT_DEBUG,("tcp_input: second or later PCB and SOF_REUSEPORT set.\n")); + } else { + /* First PCB with this address */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: first PCB and SOF_REUSEPORT set.\n")); + reuse = 1; + } + + reuse_port = 1; + p->ref++; + + /* We want to search on next socket after receiving */ + pcb_temp = pcb->next; + + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: reference counter on PBUF set to %i\n", p->ref)); + } else { + if(reuse) { + /* We processed one PCB already */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: second or later PCB but SOF_REUSEPORT not set !\n")); + } + } +#endif /* SO_REUSE */ + /* Move this PCB to the front of the list so that subsequent lookups will be faster (we exploit locality in TCP segment arrivals). */ @@ -327,8 +368,25 @@ tcp_input(struct pbuf *p, struct netif *inp) tcp_debug_print_state(pcb->state); #endif /* TCP_DEBUG */ #endif /* TCP_INPUT_DEBUG */ +#ifdef SO_REUSE + /* First socket should receive now */ + if(reuse_port) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: searching next PCB.\n")); + reuse_port = 0; + + /* We are searching connected sockets */ + goto again_1; + } +#endif /* SO_REUSE */ } else { +#ifdef SO_REUSE + if(reuse) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: freeing PBUF with reference counter set to %i\n", p->ref)); + pbuf_free(p); + goto end; + } +#endif /* SO_REUSE */ /* If no matching PCB was found, send a TCP RST (reset) to the sender. */ LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_input: no PCB match found, resetting.\n")); @@ -343,7 +401,9 @@ tcp_input(struct pbuf *p, struct netif *inp) } pbuf_free(p); } - +#ifdef SO_REUSE + end: +#endif /* SO_REUSE */ LWIP_ASSERT("tcp_input: tcp_pcbs_sane()", tcp_pcbs_sane()); PERF_STOP("tcp_input"); } diff --git a/src/core/udp.c b/src/core/udp.c index a29234d4..123c5e52 100644 --- a/src/core/udp.c +++ b/src/core/udp.c @@ -163,6 +163,13 @@ udp_input(struct pbuf *p, struct netif *inp) struct ip_hdr *iphdr; u16_t src, dest; +#ifdef SO_REUSE + struct udp_pcb *pcb_temp; + int reuse = 0; + int reuse_port_1 = 0; + int reuse_port_2 = 0; +#endif /* SO_REUSE */ + PERF_START; #ifdef UDP_STATS @@ -200,8 +207,18 @@ udp_input(struct pbuf *p, struct netif *inp) ip4_addr3(&iphdr->dest), ip4_addr4(&iphdr->dest), ntohs(udphdr->dest), ip4_addr1(&iphdr->src), ip4_addr2(&iphdr->src), ip4_addr3(&iphdr->src), ip4_addr4(&iphdr->src), ntohs(udphdr->src))); + +#ifdef SO_REUSE + pcb_temp = udp_pcbs; + + again_1: + + /* Iterate through the UDP pcb list for a fully matching pcb */ + for(pcb = pcb_temp; pcb != NULL; pcb = pcb->next) { +#else /* SO_REUSE */ /* Iterate through the UDP pcb list for a fully matching pcb */ for(pcb = udp_pcbs; pcb != NULL; pcb = pcb->next) { +#endif /* SO_REUSE */ /* print the PCB local and remote address */ LWIP_DEBUGF(UDP_DEBUG, ("pcb (%u.%u.%u.%u, %u) --- (%u.%u.%u.%u, %u)\n", ip4_addr1(&pcb->local_ip), ip4_addr2(&pcb->local_ip), @@ -221,6 +238,27 @@ udp_input(struct pbuf *p, struct netif *inp) (ip_addr_isany(&pcb->local_ip) || /* PCB local IP address matches UDP destination IP address? */ ip_addr_cmp(&(pcb->local_ip), &(iphdr->dest)))) { +#ifdef SO_REUSE + if(pcb->so_options & SOF_REUSEPORT) { + if(reuse) { + /* We processed one PCB already */ + LWIP_DEBUGF(UDP_DEBUG, ("udp_input: second or later PCB and SOF_REUSEPORT set.\n")); + } else { + /* First PCB with this address */ + LWIP_DEBUGF(UDP_DEBUG, ("udp_input: first PCB and SOF_REUSEPORT set.\n")); + reuse = 1; + } + + reuse_port_1 = 1; + p->ref++; + LWIP_DEBUGF(UDP_DEBUG, ("udp_input: reference counter on PBUF set to %i\n", p->ref)); + } else { + if(reuse) { + /* We processed one PCB already */ + LWIP_DEBUGF(UDP_DEBUG, ("udp_input: second or later PCB but SOF_REUSEPORT not set !\n")); + } + } +#endif /* SO_REUSE */ break; } } @@ -228,7 +266,16 @@ udp_input(struct pbuf *p, struct netif *inp) if (pcb == NULL) { /* Iterate through the UDP PCB list for a pcb that matches the local address. */ + +#ifdef SO_REUSE + pcb_temp = udp_pcbs; + + again_2: + + for(pcb = pcb_temp; pcb != NULL; pcb = pcb->next) { +#else /* SO_REUSE */ for(pcb = udp_pcbs; pcb != NULL; pcb = pcb->next) { +#endif /* SO_REUSE */ LWIP_DEBUGF(UDP_DEBUG, ("pcb (%u.%u.%u.%u, %u) --- (%u.%u.%u.%u, %u)\n", ip4_addr1(&pcb->local_ip), ip4_addr2(&pcb->local_ip), ip4_addr3(&pcb->local_ip), ip4_addr4(&pcb->local_ip), pcb->local_port, @@ -242,7 +289,28 @@ udp_input(struct pbuf *p, struct netif *inp) (ip_addr_isany(&pcb->local_ip) || /* ...matching interface address? */ ip_addr_cmp(&(pcb->local_ip), &(iphdr->dest)))) { - break; +#ifdef SO_REUSE + if(pcb->so_options & SOF_REUSEPORT) { + if(reuse) { + /* We processed one PCB already */ + LWIP_DEBUGF(UDP_DEBUG, ("udp_input: second or later PCB and SOF_REUSEPORT set.\n")); + } else { + /* First PCB with this address */ + LWIP_DEBUGF(UDP_DEBUG, ("udp_input: first PCB and SOF_REUSEPORT set.\n")); + reuse = 1; + } + + reuse_port_2 = 1; + p->ref++; + LWIP_DEBUGF(UDP_DEBUG, ("udp_input: reference counter on PBUF set to %i\n", p->ref)); + } else { + if(reuse) { + /* We processed one PCB already */ + LWIP_DEBUGF(UDP_DEBUG, ("udp_input: second or later PCB but SOF_REUSEPORT not set !\n")); + } + } +#endif /* SO_REUSE */ + break; } } } @@ -291,7 +359,33 @@ udp_input(struct pbuf *p, struct netif *inp) if (pcb != NULL) { snmp_inc_udpindatagrams(); pcb->recv(pcb->recv_arg, pcb, p, &(iphdr->src), src); +#ifdef SO_REUSE + /* First socket should receive now */ + if(reuse_port_1 || reuse_port_2) { + /* We want to search on next socket after receiving */ + pcb_temp = pcb->next; + + if(reuse_port_1) { + /* We are searching connected sockets */ + reuse_port_1 = 0; + reuse_port_2 = 0; + goto again_1; + } else { + /* We are searching unconnected sockets */ + reuse_port_1 = 0; + reuse_port_2 = 0; + goto again_2; + } + } +#endif /* SO_REUSE */ } else { +#ifdef SO_REUSE + if(reuse) { + LWIP_DEBUGF(UDP_DEBUG, ("udp_input: freeing PBUF with reference counter set to %i\n", p->ref)); + pbuf_free(p); + goto end; + } +#endif /* SO_REUSE */ LWIP_DEBUGF(UDP_DEBUG | DBG_TRACE, ("udp_input: not for us.\n")); /* No match was found, send ICMP destination port unreachable unless @@ -457,9 +551,13 @@ udp_bind(struct udp_pcb *pcb, struct ip_addr *ipaddr, u16_t port) { struct udp_pcb *ipcb; u8_t rebind; +#ifdef SO_REUSE + int reuse_port_all_set = 1; +#endif /* SO_REUSE */ LWIP_DEBUGF(UDP_DEBUG | DBG_TRACE | 3, ("udp_bind(ipaddr = ")); ip_addr_debug_print(UDP_DEBUG, ipaddr); LWIP_DEBUGF(UDP_DEBUG | DBG_TRACE | 3, (", port = %u)\n", port)); + rebind = 0; /* Check for double bind and rebind of the same pcb */ for (ipcb = udp_pcbs; ipcb != NULL; ipcb = ipcb->next) { @@ -470,6 +568,8 @@ udp_bind(struct udp_pcb *pcb, struct ip_addr *ipaddr, u16_t port) /* pcb already in list, just rebind */ rebind = 1; } + +#ifndef SO_REUSE /* this code does not allow upper layer to share a UDP port for listening to broadcast or multicast traffic (See SO_REUSE_ADDR and SO_REUSE_PORT under *BSD). TODO: See where it fits instead, OR @@ -486,8 +586,52 @@ udp_bind(struct udp_pcb *pcb, struct ip_addr *ipaddr, u16_t port) return ERR_USE; } #endif + +#else /* SO_REUSE */ + /* Search through list of PCB's. + + If there is a PCB bound to specified port and IP_ADDR_ANY another PCB can be bound to the interface IP + or to the loopback address on the same port if SOF_REUSEADDR is set. Any combination of PCB's bound to + the same local port, but to one address out of {IP_ADDR_ANY, 127.0.0.1, interface IP} at a time is valid. + But no two PCB's bound to same local port and same local address is valid. + + If SOF_REUSEPORT is set several PCB's can be bound to same local port and same local address also. But then + all PCB's must have the SOF_REUSEPORT option set. + + When the two options aren't set and specified port is already bound, ERR_USE is returned saying that + address is already in use. */ + else if (ipcb->local_port == port) { + if(ip_addr_cmp(&(ipcb->local_ip), ipaddr)) { + if(pcb->so_options & SOF_REUSEPORT) { + LWIP_DEBUGF(UDP_DEBUG, ("udp_bind: in UDP PCB's SO_REUSEPORT set and same address.\n")); + reuse_port_all_set = (reuse_port_all_set && (ipcb->so_options & SOF_REUSEPORT)); + } + else { + LWIP_DEBUGF(UDP_DEBUG, ("udp_bind: in UDP PCB's SO_REUSEPORT not set and same address.\n")); + return ERR_USE; + } + } + else if((ip_addr_isany(ipaddr) && !ip_addr_isany(&(ipcb->local_ip))) || + (!ip_addr_isany(ipaddr) && ip_addr_isany(&(ipcb->local_ip)))) { + if(!(pcb->so_options & SOF_REUSEADDR) && !(pcb->so_options & SOF_REUSEPORT)) { + LWIP_DEBUGF(UDP_DEBUG, ("udp_bind: in UDP PCB's SO_REUSEPORT or SO_REUSEADDR not set and not the same address.\n")); + return ERR_USE; + } + } + } +#endif /* SO_REUSE */ + } - /* bind local address */ + +#ifdef SO_REUSE + /* If SOF_REUSEPORT isn't set in all PCB's bound to specified port and local address specified then + {IP, port} can't be reused. */ + if(!reuse_port_all_set) { + LWIP_DEBUGF(UDP_DEBUG, ("udp_bind: not all sockets have SO_REUSEPORT set.\n")); + return ERR_USE; + } +#endif /* SO_REUSE */ + ip_addr_set(&pcb->local_ip, ipaddr); /* no port specified? */ if (port == 0) { diff --git a/src/include/lwip/opt.h b/src/include/lwip/opt.h index 20b34eea..c03372bb 100644 --- a/src/include/lwip/opt.h +++ b/src/include/lwip/opt.h @@ -325,6 +325,14 @@ a lot of data that needs to be copied, this should be set high. */ #define DEFAULT_THREAD_PRIO 1 #endif + +/* ---------- Socket Options ---------- */ +/* Enable SO_REUSEADDR and SO_REUSEPORT options */ +#ifndef SO_REUSE +# define SO_REUSE 1 +#endif + + /* ---------- Statistics options ---------- */ #ifndef LWIP_STATS #define LWIP_STATS 1