DatagramChannel::disconnect appears to rebind socket to the wildcard address

Kurt Miller kurt at intricatesoftware.com
Wed Sep 25 20:03:11 UTC 2019


On Wed, 2019-09-25 at 20:37 +0100, Alan Bateman wrote:
> On 25/09/2019 19:50, Kurt Miller wrote:
> > 
> > :
> > Yes, I have observed that bug on OpenBSD as well. It is indeed
> > a kernel bug on OpenBSD. I have kernel patches to correct it
> > but haven't pushed them forward through the review process there.
> > 
> > I just did a quick test on FreeBSD 11 on amd64 and it also
> > doesn't retain the bound address.
> > 
> Thanks for confirming the issue on OpenBSD and also checking FreeBSD 
> too. I'm curious if the issue in the OpenBSD kernel is something simple 
> or whether this is a significant change. My guess is that this is an 
> issue that could have lurked for years as not many applications or 
> usages would observe the local address changing like this.
> 

Your welcome. It is something simple. Roughly speaking:

- set flag when bind on dgram socket sets IP addr
- when disconnecting (connect(2) called w/AF_UNSPEC) if flag
is set, don't reset it to INADDR_ANY

Here's a copy of the diff if you're curious. I guess I should
move this diff along through the review process.

Index: netinet/udp_usrreq.c
===================================================================
RCS file: /cvs/src/sys/netinet/udp_usrreq.c,v
retrieving revision 1.255
diff -u -p -u -r1.255 udp_usrreq.c
--- netinet/udp_usrreq.c	4 Feb 2019 21:40:52 -0000	1.255
+++ netinet/udp_usrreq.c	25 Sep 2019 19:59:17 -0000
@@ -1021,6 +1021,7 @@ udp_usrreq(struct socket *so, int req, s
 {
 	struct inpcb *inp;
 	int error = 0;
+	udp_flags flags = 0;
 
 	if (req == PRU_CONTROL) {
 #ifdef INET6
@@ -1048,7 +1049,30 @@ udp_usrreq(struct socket *so, int req, s
 	switch (req) {
 
 	case PRU_BIND:
+#ifdef INET6
+		if (inp->inp_flags & INP_IPV6) {
+			struct sockaddr_in6 *sin6;
+
+			if ((error = in6_nam2sin6(addr, &sin6)))
+				break;
+			if (!IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr))
+				flags |= UF_ADDR_BOUND;
+		}
+		else
+#endif /* INET6 */
+		{
+			struct sockaddr_in *sin;
+
+			if ((error = in_nam2sin(addr, &sin)))
+				break;
+			if (sin->sin_addr.s_addr != INADDR_ANY)
+				flags |= UF_ADDR_BOUND;
+		}
+
 		error = in_pcbbind(inp, addr, p);
+		if (error == 0)
+			*intoudpflgp(inp) |= flags;
+
 		break;
 
 	case PRU_LISTEN:
@@ -1086,12 +1110,15 @@ udp_usrreq(struct socket *so, int req, s
 		break;
 
 	case PRU_DISCONNECT:
+		flags = *intoudpflgp(inp);
 #ifdef INET6
 		if (inp->inp_flags & INP_IPV6) {
 			if (IN6_IS_ADDR_UNSPECIFIED(&inp->inp_faddr6)) {
 				error = ENOTCONN;
 				break;
 			}
+			if ((flags & UF_ADDR_BOUND) == 0)
+				inp->inp_laddr6 = in6addr_any;
 		} else
 #endif /* INET6 */
 		{
@@ -1099,14 +1126,10 @@ udp_usrreq(struct socket *so, int req, s
 				error = ENOTCONN;
 				break;
 			}
+			if ((flags & UF_ADDR_BOUND) == 0)
+				inp->inp_laddr.s_addr = INADDR_ANY;
 		}
 
-#ifdef INET6
-		if (inp->inp_flags & INP_IPV6)
-			inp->inp_laddr6 = in6addr_any;
-		else
-#endif /* INET6 */
-			inp->inp_laddr.s_addr = INADDR_ANY;
 		in_pcbdisconnect(inp);
 
 		so->so_state &= ~SS_ISCONNECTED;		/* XXX */
Index: netinet/udp_var.h
===================================================================
RCS file: /cvs/src/sys/netinet/udp_var.h,v
retrieving revision 1.34
diff -u -p -u -r1.34 udp_var.h
--- netinet/udp_var.h	2 Nov 2017 14:01:18 -0000	1.34
+++ netinet/udp_var.h	25 Sep 2019 19:59:17 -0000
@@ -101,6 +101,14 @@ struct	udpstat {
 	NULL \
 }
 
+/*
+ * Udp control block is just flags:
+ */
+typedef u_int udp_flags;
+#define	UF_ADDR_BOUND	0x0001		/* IP Addr Bound */
+
+#define	intoudpflgp(ip)	((udp_flags *)&(ip)->inp_ppcb)
+
 #ifdef _KERNEL
 
 #include <sys/percpu.h>


More information about the bsd-port-dev mailing list