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>

Reply via email to