summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorAlexander Bluhm <bluhm@cvs.openbsd.org>2017-08-04 18:16:43 +0000
committerAlexander Bluhm <bluhm@cvs.openbsd.org>2017-08-04 18:16:43 +0000
commit69b37d52b4fe09149f33b2a7037218c51beeef3c (patch)
tree70a5be2e4a078f205aec493f9cf8c0abbc3ac86f /sys
parent3219473338481bbf002644f4789331d0e0cb4188 (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.c18
-rw-r--r--sys/netinet6/in6_pcb.c18
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;