ether_gen_addr_byname() derives a MAC address by SHA1-hashing the
hostuuid, interface name and jail name, then truncating the digest to
only 16 bits (digest[0]<<8 | digest[1]) before placing it in the
OUI_FREEBSD_GENERATED pool. That pool was reserved as 16 bits in
ieee_oui.h, matching the truncation, even though the equivalent bhyve
pool was already widened to 20 bits in 2014 for the same
birthday-paradox reason.
For interfaces created outside of a jail (as in the reproduction
steps below), both the hostuuid and jail name are constant for every
call, since getjailname(9) returns the calling thread's own jail name
(empty for the base system) rather than anything related to the
interface being created. In that case only the interface name varies
from one call to the next, so the effective input entropy feeding the
hash is lower than the three-component description above suggests,
making the undersized 16-bit truncation easier to collide against in
practice.
Creating a moderate number of epair(4) interfaces (which use
ether_gen_addr(9) by default since net.link.epair.ether_gen_addr=1)
reliably produces duplicate MAC addresses: ~34 collisions out of 100
pairs, growing with volume.
Widen OUI_FREEBSD_GENERATED_MASK to 0x1fffff (20 bits, matching the
already-reserved low boundary and bordering OUI_FREEBSD_NVME's
0x200000 start with no overlap) and consume 20 bits of the SHA1
digest instead of 16. This reduces, but does not eliminate, the
collision probability for interfaces created concurrently; it has no
effect on an interface deterministically regenerating the exact same
address another interface previously held, since that is a repeated
hash input rather than a collision.
PR: 300751
Relnotes: yes