mirror of
https://github.com/lwip-tcpip/lwip.git
synced 2024-11-04 14:29:39 +00:00
task #6995: Implement SO_REUSEADDR (correctly)
This commit is contained in:
parent
ef0a7ecbcd
commit
d0348e0c60
@ -13,6 +13,9 @@ HISTORY
|
||||
|
||||
++ New features:
|
||||
|
||||
2010-05-13: Simon Goldschmidt
|
||||
* tcp.c, udp.c: task #6995: Implement SO_REUSEADDR (correctly)
|
||||
|
||||
2010-05-02: Simon Goldschmidt
|
||||
* netbuf.h/.c, sockets.c, api_msg.c: use checksum-on-copy for sending
|
||||
UDP data for LWIP_NETIF_TX_SINGLE_PBUF==1
|
||||
|
134
src/core/tcp.c
134
src/core/tcp.c
@ -88,6 +88,12 @@ struct tcp_pcb *tcp_active_pcbs;
|
||||
/** 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;
|
||||
|
||||
@ -390,55 +396,46 @@ tcp_abort(struct tcp_pcb *pcb)
|
||||
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 = 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -593,6 +590,7 @@ tcp_recved(struct tcp_pcb *pcb, u16_t len)
|
||||
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
|
||||
@ -604,20 +602,12 @@ tcp_new_port(void)
|
||||
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;
|
||||
@ -651,9 +641,43 @@ tcp_connect(struct tcp_pcb *pcb, ip_addr_t *ipaddr, u16_t port,
|
||||
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 not 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 == pcb->remote_port) &&
|
||||
ip_addr_cmp(&cpcb->local_ip, &pcb->local_ip) &&
|
||||
ip_addr_cmp(&cpcb->local_ip, &pcb->local_ip)) {
|
||||
/* 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;
|
||||
|
@ -660,13 +660,16 @@ udp_bind(struct udp_pcb *pcb, ip_addr_t *ipaddr, u16_t port)
|
||||
rebind = 1;
|
||||
}
|
||||
|
||||
/* this code does not allow upper layer to share a UDP port for
|
||||
listening to broadcast or multicast traffic (See SO_REUSEADDR and
|
||||
SO_REUSEPORT under *BSD). TODO: See where it fits instead, OR
|
||||
combine with implementation of UDP PCB flags. Leon Woestenberg. */
|
||||
#ifdef LWIP_UDP_TODO
|
||||
/* port matches that of PCB in list? */
|
||||
/* By default, we don't allow to bind to a port that any other udp
|
||||
PCB is alread bound to, unless *all* PCBs with that port have tha
|
||||
REUSEADDR flag set. */
|
||||
#if SO_REUSE
|
||||
else if (((pcb->so_options & SOF_REUSEADDR) == 0) &&
|
||||
((ipcb->so_options & SOF_REUSEADDR) == 0)) {
|
||||
#else /* SO_REUSE */
|
||||
/* port matches that of PCB in list and REUSEADDR not set -> reject */
|
||||
else {
|
||||
#endif /* SO_REUSE */
|
||||
if ((ipcb->local_port == port) &&
|
||||
/* IP address matches, or one is IP_ADDR_ANY? */
|
||||
(ip_addr_isany(&(ipcb->local_ip)) ||
|
||||
@ -678,7 +681,6 @@ udp_bind(struct udp_pcb *pcb, ip_addr_t *ipaddr, u16_t port)
|
||||
return ERR_USE;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
ip_addr_set(&pcb->local_ip, ipaddr);
|
||||
|
Loading…
Reference in New Issue
Block a user