diff options
author | Alexander Bluhm <bluhm@cvs.openbsd.org> | 2017-08-04 18:16:43 +0000 |
---|---|---|
committer | Alexander Bluhm <bluhm@cvs.openbsd.org> | 2017-08-04 18:16:43 +0000 |
commit | 69b37d52b4fe09149f33b2a7037218c51beeef3c (patch) | |
tree | 70a5be2e4a078f205aec493f9cf8c0abbc3ac86f /sys | |
parent | 3219473338481bbf002644f4789331d0e0cb4188 (diff) |
The in_pcbhashlookup() in in_pcbconnect() enforces that the 4 tupel
of src/dst ip/port is unique for TCP. But if the socket is not
bound, the automatic bind by connect happens after the check. If
the socket has the SO_REUSEADDR flag, in_pcbbind() may select an
existing local port. Then we had two colliding TCP PCBs. This
resulted in a packet storm of ACK packets on loopback. The softnet
task was constantly holding the netlock and has a high priority,
so the system hung.
Do the in_pcbhashlookup() again after in_pcbbind(). This creates
sporadic "connect: Address already in use" errors instead of a hang.
bug report and testing Olivier Antoine; OK mpi@
Diffstat (limited to 'sys')
-rw-r--r-- | sys/netinet/in_pcb.c | 18 | ||||
-rw-r--r-- | sys/netinet6/in6_pcb.c | 18 |
2 files changed, 26 insertions, 10 deletions
diff --git a/sys/netinet/in_pcb.c b/sys/netinet/in_pcb.c index 81dac0fffff..481a374be81 100644 --- a/sys/netinet/in_pcb.c +++ b/sys/netinet/in_pcb.c @@ -1,4 +1,4 @@ -/* $OpenBSD: in_pcb.c,v 1.222 2017/06/09 12:56:43 mpi Exp $ */ +/* $OpenBSD: in_pcb.c,v 1.223 2017/08/04 18:16:42 bluhm Exp $ */ /* $NetBSD: in_pcb.c,v 1.25 1996/02/13 23:41:53 christos Exp $ */ /* @@ -533,15 +533,23 @@ in_pcbconnect(struct inpcb *inp, struct mbuf *nam) return (error); if (in_pcbhashlookup(inp->inp_table, sin->sin_addr, sin->sin_port, - *ina, inp->inp_lport, inp->inp_rtableid) != 0) + *ina, inp->inp_lport, inp->inp_rtableid) != NULL) return (EADDRINUSE); KASSERT(inp->inp_laddr.s_addr == INADDR_ANY || inp->inp_lport); if (inp->inp_laddr.s_addr == INADDR_ANY) { - if (inp->inp_lport == 0 && - in_pcbbind(inp, NULL, curproc) == EADDRNOTAVAIL) - return (EADDRNOTAVAIL); + if (inp->inp_lport == 0) { + error = in_pcbbind(inp, NULL, curproc); + if (error) + return (error); + if (in_pcbhashlookup(inp->inp_table, sin->sin_addr, + sin->sin_port, *ina, inp->inp_lport, + inp->inp_rtableid) != NULL) { + inp->inp_lport = 0; + return (EADDRINUSE); + } + } inp->inp_laddr = *ina; } inp->inp_faddr = sin->sin_addr; diff --git a/sys/netinet6/in6_pcb.c b/sys/netinet6/in6_pcb.c index 59f1bb0d562..ae7eb146bfe 100644 --- a/sys/netinet6/in6_pcb.c +++ b/sys/netinet6/in6_pcb.c @@ -1,4 +1,4 @@ -/* $OpenBSD: in6_pcb.c,v 1.98 2017/05/13 17:42:55 bluhm Exp $ */ +/* $OpenBSD: in6_pcb.c,v 1.99 2017/08/04 18:16:42 bluhm Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -284,16 +284,24 @@ in6_pcbconnect(struct inpcb *inp, struct mbuf *nam) if (in6_pcbhashlookup(inp->inp_table, &sin6->sin6_addr, sin6->sin6_port, IN6_IS_ADDR_UNSPECIFIED(&inp->inp_laddr6) ? in6a : &inp->inp_laddr6, - inp->inp_lport, inp->inp_rtableid)) { + inp->inp_lport, inp->inp_rtableid) != NULL) { return (EADDRINUSE); } KASSERT(IN6_IS_ADDR_UNSPECIFIED(&inp->inp_laddr6) || inp->inp_lport); if (IN6_IS_ADDR_UNSPECIFIED(&inp->inp_laddr6)) { - if (inp->inp_lport == 0 && - in_pcbbind(inp, NULL, curproc) == EADDRNOTAVAIL) - return (EADDRNOTAVAIL); + if (inp->inp_lport == 0) { + error = in_pcbbind(inp, NULL, curproc); + if (error) + return (error); + if (in6_pcbhashlookup(inp->inp_table, &sin6->sin6_addr, + sin6->sin6_port, in6a, inp->inp_lport, + inp->inp_rtableid) != NULL) { + inp->inp_lport = 0; + return (EADDRINUSE); + } + } inp->inp_laddr6 = *in6a; } inp->inp_faddr6 = sin6->sin6_addr; |