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;