mirror of
https://github.com/lwip-tcpip/lwip.git
synced 2024-10-02 12:52:10 +00:00
task #6995: Implement SO_REUSEADDR (correctly)
This commit is contained in:
parent
ef0a7ecbcd
commit
d0348e0c60
@ -13,6 +13,9 @@ HISTORY
|
|||||||
|
|
||||||
++ New features:
|
++ New features:
|
||||||
|
|
||||||
|
2010-05-13: Simon Goldschmidt
|
||||||
|
* tcp.c, udp.c: task #6995: Implement SO_REUSEADDR (correctly)
|
||||||
|
|
||||||
2010-05-02: Simon Goldschmidt
|
2010-05-02: Simon Goldschmidt
|
||||||
* netbuf.h/.c, sockets.c, api_msg.c: use checksum-on-copy for sending
|
* netbuf.h/.c, sockets.c, api_msg.c: use checksum-on-copy for sending
|
||||||
UDP data for LWIP_NETIF_TX_SINGLE_PBUF==1
|
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 */
|
/** List of all TCP PCBs in TIME-WAIT state */
|
||||||
struct tcp_pcb *tcp_tw_pcbs;
|
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. */
|
/** Only used for temporary storage. */
|
||||||
struct tcp_pcb *tcp_tmp_pcb;
|
struct tcp_pcb *tcp_tmp_pcb;
|
||||||
|
|
||||||
@ -390,55 +396,46 @@ tcp_abort(struct tcp_pcb *pcb)
|
|||||||
err_t
|
err_t
|
||||||
tcp_bind(struct tcp_pcb *pcb, ip_addr_t *ipaddr, u16_t port)
|
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;
|
struct tcp_pcb *cpcb;
|
||||||
|
|
||||||
LWIP_ERROR("tcp_bind: can only bind in state CLOSED", pcb->state == CLOSED, return ERR_ISCONN);
|
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) {
|
if (port == 0) {
|
||||||
port = tcp_new_port();
|
port = tcp_new_port();
|
||||||
}
|
}
|
||||||
/* Check if the address already is in use. */
|
|
||||||
/* Check the listen pcbs. */
|
/* Check if the address already is in use (on all lists) */
|
||||||
for(cpcb = tcp_listen_pcbs.pcbs;
|
for (i = 0; i < max_pcb_list; i++) {
|
||||||
cpcb != NULL; cpcb = cpcb->next) {
|
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* 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) {
|
|
||||||
if (cpcb->local_port == port) {
|
if (cpcb->local_port == port) {
|
||||||
if (ip_addr_isany(&(cpcb->local_ip)) ||
|
#if SO_REUSE
|
||||||
ip_addr_isany(ipaddr) ||
|
/* Omit checking for the same port if both pcbs have REUSEADDR set.
|
||||||
ip_addr_cmp(&(cpcb->local_ip), ipaddr)) {
|
For SO_REUSEADDR, the duplicate-check for a 5-tuple is done in
|
||||||
return ERR_USE;
|
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
|
static u16_t
|
||||||
tcp_new_port(void)
|
tcp_new_port(void)
|
||||||
{
|
{
|
||||||
|
int i;
|
||||||
struct tcp_pcb *pcb;
|
struct tcp_pcb *pcb;
|
||||||
#ifndef TCP_LOCAL_PORT_RANGE_START
|
#ifndef TCP_LOCAL_PORT_RANGE_START
|
||||||
#define TCP_LOCAL_PORT_RANGE_START 4096
|
#define TCP_LOCAL_PORT_RANGE_START 4096
|
||||||
@ -604,20 +602,12 @@ tcp_new_port(void)
|
|||||||
if (++port > TCP_LOCAL_PORT_RANGE_END) {
|
if (++port > TCP_LOCAL_PORT_RANGE_END) {
|
||||||
port = TCP_LOCAL_PORT_RANGE_START;
|
port = TCP_LOCAL_PORT_RANGE_START;
|
||||||
}
|
}
|
||||||
|
/* Check all PCB lists. */
|
||||||
for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {
|
for (i = 1; i < NUM_TCP_PCB_LISTS; i++) {
|
||||||
if (pcb->local_port == port) {
|
for(pcb = *tcp_pcb_lists[i]; pcb != NULL; pcb = pcb->next) {
|
||||||
goto again;
|
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return port;
|
return port;
|
||||||
@ -651,9 +641,43 @@ tcp_connect(struct tcp_pcb *pcb, ip_addr_t *ipaddr, u16_t port,
|
|||||||
return ERR_VAL;
|
return ERR_VAL;
|
||||||
}
|
}
|
||||||
pcb->remote_port = port;
|
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) {
|
if (pcb->local_port == 0) {
|
||||||
pcb->local_port = tcp_new_port();
|
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();
|
iss = tcp_next_iss();
|
||||||
pcb->rcv_nxt = 0;
|
pcb->rcv_nxt = 0;
|
||||||
pcb->snd_nxt = iss;
|
pcb->snd_nxt = iss;
|
||||||
|
@ -660,13 +660,16 @@ udp_bind(struct udp_pcb *pcb, ip_addr_t *ipaddr, u16_t port)
|
|||||||
rebind = 1;
|
rebind = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* this code does not allow upper layer to share a UDP port for
|
/* By default, we don't allow to bind to a port that any other udp
|
||||||
listening to broadcast or multicast traffic (See SO_REUSEADDR and
|
PCB is alread bound to, unless *all* PCBs with that port have tha
|
||||||
SO_REUSEPORT under *BSD). TODO: See where it fits instead, OR
|
REUSEADDR flag set. */
|
||||||
combine with implementation of UDP PCB flags. Leon Woestenberg. */
|
#if SO_REUSE
|
||||||
#ifdef LWIP_UDP_TODO
|
else if (((pcb->so_options & SOF_REUSEADDR) == 0) &&
|
||||||
/* port matches that of PCB in list? */
|
((ipcb->so_options & SOF_REUSEADDR) == 0)) {
|
||||||
|
#else /* SO_REUSE */
|
||||||
|
/* port matches that of PCB in list and REUSEADDR not set -> reject */
|
||||||
else {
|
else {
|
||||||
|
#endif /* SO_REUSE */
|
||||||
if ((ipcb->local_port == port) &&
|
if ((ipcb->local_port == port) &&
|
||||||
/* IP address matches, or one is IP_ADDR_ANY? */
|
/* IP address matches, or one is IP_ADDR_ANY? */
|
||||||
(ip_addr_isany(&(ipcb->local_ip)) ||
|
(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;
|
return ERR_USE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ip_addr_set(&pcb->local_ip, ipaddr);
|
ip_addr_set(&pcb->local_ip, ipaddr);
|
||||||
|
Loading…
Reference in New Issue
Block a user