Bridging needs promiscuous receive, so ng_bridge(4) setups enable
it on ng_ether(4) lower (setpromisc 1; see ether.bridge in
share/examples/netgraph). A promiscuous interface can receive a
packet this node just sent out that hook. After minStableAge, that
looks like the host jumped, so the table entry moves onto the hook
we transmitted on. Inbound unicast to that host is then dropped
(destination already known on the incoming hook). Classic link
hooks (learnMac=1) see this as well as uplink.
Remember the last hook we sent each source out. A flooded frame
(broadcast, multicast, unknown unicast) is copied out every other
hook, so also remember when a source was last flooded. If the same
source arrives on the recorded hook, or on any hook after a flood,
within minStableAge, drop the packet without moving the host and
without muting the hook. A genuine move is delayed at most
minStableAge, the same tolerance the loop detector already imposes.
Userland NGM_BRIDGE_MOVE_HOST is unchanged. Independent of
learnMac=0 (D58902).
The LOOP DETECTION section of ng_bridge(4) promises that a host
reappearing on another link within the minimum stable time mutes
that link. This change carves out an exception, so document it
there: a reflected transmit is dropped without a move and without
a mute.
The loop test expected ELOOP once the loop closed; the returning
frame is now a reflected transmit, dropped with no error, so check
for that and for loopDrops without loopDetects on the returning
hooks. Add a reflect test for a single bridge: the echo is dropped
within minStableAge and becomes a genuine move after.
MFC after: 1 week