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>