Hi, As discussed on misc@, I've come across a bug in OpenBSD's handling of gratuitous Ethernet traffic, and it was suggested that I move the discussion over here.
What I've found is that with certain configurations, an OpenBSD box receiving Ethernet traffic with a destination MAC address that is not its own will respond to that traffic with spoofed TCP RST packet. That is, if I try and initiate a TCP connection from machine A to machine B across a hub, with an OpenBSD machine connected to the same hub, the OpenBSD machine will spoof a TCP reset packet from B to A and kill the connection attempt. See [1] for sample tcpdump output. The following conditions are required for this to happen: - A trunk configured with one or more promiscuous slave interfaces - One or more of these slave interfaces should be receiving gratuitous unicast Ethernet frames, as for a non-switched network - The trunk must not be in promiscuous mode (meaning running tcpdump to analyze the problem makes it go away! :) - pf enabled, and the line 'set block-policy return' in pf.conf I've tracked the issue down to ether_input() (sys/net/if_ethersubr.c:530). The check at line 687 looks to see if the interface is in promisc mode before it does the destination check, but packets from a trunk interface get their received interface changed from the physical interface to the trunk around line 559. Thus, if the child interface is promisc and the trunk interface is not, packets not destined for the local machine will be passed into pf's filtering routines, with the above results. The attached patch [2] fixes the issue for me. Not knowing the OpenBSD network stack, I've tried to fix it in the least intrusive way possible, which may not be the best solution. Feedback appreciated. Cheers, Patrick -- http://www.labyrinthdata.net.au - WA Backup, Web and VPS Hosting [1] Two examples of the problem here. 10.10.50.1 sends a SYN to 10.10.50.2 (00:16:cb:d1:5b:fc), but OpenBSD (10.10.0.2/00:04:23:c9:bd:d0) spoofs a RST response from 10.10.50.2 and closes the connection. 11:41:39.202711 00:25:00:a0:2a:6e > 00:16:cb:d1:5b:fc, ethertype IPv4 (0x0800), length 78: 10.10.50.1.51743 > 10.10.50.2.5555: Flags [S], seq 4101611714, win 65535, options [mss 1460,nop,wscale 3,nop,nop,TS val 844767319 ecr 0,sackOK,eol], length 0 11:41:39.203219 00:04:23:c9:bd:d0 > 00:25:00:a0:2a:6e, ethertype IPv4 (0x0800), length 60: 10.10.50.2.5555 > 10.10.50.1.51743: Flags [R.], seq 0, ack 4101611715, win 0, length 0 11:41:39.411521 00:25:00:a0:2a:6e > 00:16:cb:d1:5b:fc, ethertype IPv4 (0x0800), length 78: 10.10.50.1.51744 > 10.10.50.2.5555: Flags [S], seq 2093396000, win 65535, options [mss 1460,nop,wscale 3,nop,nop,TS val 844767321 ecr 0,sackOK,eol], length 0 11:41:39.411987 00:04:23:c9:bd:d0 > 00:25:00:a0:2a:6e, ethertype IPv4 (0x0800), length 60: 10.10.50.2.5555 > 10.10.50.1.51744: Flags [R.], seq 0, ack 2093396001, win 0, length 0 [2] Also up at http://patrick.ld.net.au/20100616-fix-gratuitous-reset.patch, for less tab clobberyness. Index: if_ethersubr.c =================================================================== RCS file: /cvs/src/sys/net/if_ethersubr.c,v retrieving revision 1.139 diff -N -u -u -p if_ethersubr.c --- if_ethersubr.c 12 Jan 2010 06:47:25 -0000 1.139 +++ if_ethersubr.c 16 Jun 2010 09:07:42 -0000 @@ -538,6 +538,7 @@ ether_input(ifp0, eh, m) struct llc *l; struct arpcom *ac; struct ifnet *ifp = ifp0; + struct ifnet *ifp_orig = ifp0; #if NTRUNK > 0 int i = 0; #endif @@ -565,7 +566,10 @@ ether_input(ifp0, eh, m) if (trunk_input(ifp, eh, m) != 0) return; - /* Has been set to the trunk interface */ + /* + * Has been set to the trunk interface. ifp_orig preserves the physical + * input interface. + */ ifp = m->m_pkthdr.rcvif; } #endif @@ -683,9 +687,14 @@ ether_input(ifp0, eh, m) /* * If packet is unicast and we're in promiscuous mode, make sure it * is for us. Drop otherwise. + * + * Check ifp and ifp_orig, to catch promiscuous members of a + * non-promiscuous trunk. */ if ((m->m_flags & (M_BCAST|M_MCAST)) == 0 && - (ifp->if_flags & IFF_PROMISC)) { + ((ifp->if_flags & IFF_PROMISC) || + (ifp_orig->if_flags & IFF_PROMISC))) { + if (bcmp(ac->ac_enaddr, (caddr_t)eh->ether_dhost, ETHER_ADDR_LEN)) { m_freem(m);
