Whenever a connected UDP socket is connected to another peer, any previous
state should be cleared in PF so that only datagrams from the newly connected
peer are deliverd to the socket.

Without this patch the following scenario can occur on machines with PF
enabled using keep state on matching rules (keep state is the default). Say
there are two peers, peer1 and peer2 and we have one connected UDP socket
"sock".

first connect(2) to the first peer: connect(sock, peer1)
        Now "sock" only receives datagrams from peer1. Whenever peer2 sends a
        datagram to the local end of "sock" it is not delivered to "sock"
        because the foreign address in the pcb does not match. This is all
        good and expected.

Assume peer1 has sent a datagram to "sock" so that state is created in pf(4).

Now we connect the same socket to the second peer: connect(sock, peer2)
        Now "sock" receives datagrams from peer2 as expected but for as long
        as "keep state" remains (30 to 60 seconds, see udp.first, udp.multiple
        and udp.single in pf.conf(5)) datagrams from peer1 are also delivered
        to "sock" even though it is connected to peer2.

The following patch fixes this behaviour and clears state on PRU_DISCONNECT.
I'm not sure if this is the best place to fix it nor whether there are more
places that should be looked at.

Found out about this while debugging a WireGuard client that was switching
back and forth between a WiFi and LTE while connected to my OpenBSD server
that uses connected UDP sockets.
Index: udp_usrreq.c
===================================================================
RCS file: /cvs/src/sys/netinet/udp_usrreq.c,v
retrieving revision 1.257
diff -u -p -c -r1.257 udp_usrreq.c
*** udp_usrreq.c        6 Dec 2019 14:43:14 -0000       1.257
--- udp_usrreq.c        21 Apr 2020 08:47:18 -0000
*************** udp_usrreq(struct socket *so, int req, s
*** 1105,1110 ****
--- 1105,1115 ----
                        }
                }
  
+ #if NPF > 0
+               if (so->so_state & SS_ISCONNECTED)
+                       pf_inp_unlink(inp);
+ #endif
+ 
  #ifdef INET6
                if (inp->inp_flags & INP_IPV6)
                        inp->inp_laddr6 = in6addr_any;

Reply via email to