diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d307afd9..604d3817 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -327,6 +327,31 @@ jobs: - name: Install cargo-nextest uses: taiki-e/install-action@nextest + # The Darwin half of the address-less presence contract. The Linux legs + # pin that `getifaddrs` reports an interface with no addresses as + # present, on both glibc and musl; without this the same claim on the + # BSD-derived implementation the macOS backend actually calls was + # untested, and the test skipped itself silently on this runner. + # + # `feth` is macOS's fake-Ethernet pseudo-interface. It is created + # address-less, and the check below fails the leg rather than testing the + # wrong thing if this runner hands it one anyway — the same shape as the + # Linux fixture step, which needs `addrgenmode none` for exactly that + # reason. + - name: Create an address-less interface for the presence probe + run: | + sudo ifconfig feth0 create + sudo ifconfig feth0 up + ifconfig feth0 + if ifconfig feth0 | grep -qE "^[[:space:]]*inet6? "; then + echo "feth0 has an address; it cannot test the address-less case" >&2 + exit 1 + fi + echo "FIPS_TEST_ADDRLESS_IFACE=feth0" >> "$GITHUB_ENV" + # Declare that this runner has fixtures, so a test that depends on + # one fails when the fixture is missing instead of skipping silently. + echo "FIPS_TEST_REQUIRE_FIXTURES=1" >> "$GITHUB_ENV" + - name: Run unit tests run: cargo nextest run --all --profile ci diff --git a/src/transport/ethernet/mod.rs b/src/transport/ethernet/mod.rs index 84348e2f..48c8433a 100644 --- a/src/transport/ethernet/mod.rs +++ b/src/transport/ethernet/mod.rs @@ -1899,15 +1899,28 @@ mod tests { the presence probe; if this fails on musl, every address-less \ interface on OpenWrt is invisible to interface binding" ); - // It has no carrier either — a dummy device is up but not running — - // which pins that presence and carrier really are separate reads. - // Asserted rather than discarded: if these two ever collapsed into the - // same read, a carrier-less bridge would report absent and the whole - // IFF_UP-not-IFF_RUNNING decision would be silently undone. + // Carrier is asserted rather than discarded, but the expected answer + // is `true`, not `false`: a Linux `dummy` brought up reports + // `UP,LOWER_UP`, so `IFF_RUNNING` is set and it has carrier. The + // comment this replaces claimed the opposite — "up but not running" — + // which is why the result was discarded rather than checked. + // + // So this fixture pins address-less *presence*, and cannot demonstrate + // the presence-vs-carrier split; an interface that is up with no + // carrier is a bridge with nothing plugged in, which no fixture here + // creates. `carrier_is_reported_separately_from_presence` pins that + // split from the other side, on a missing interface having neither. + // + // Linux only, because the expected answer is a property of the fixture + // device rather than of the code: a `dummy` that is up reports + // `IFF_RUNNING`, and macOS's `feth` has its own semantics that are not + // pinned here. What both platforms do assert is the part that matters + // — an interface with no addresses is still *present*. + #[cfg(target_os = "linux")] assert!( - !io::interface_carrier(&iface), - "{iface} is up with no carrier, so presence and carrier must \ - disagree here — if they agree, they are the same read" + io::interface_carrier(&iface), + "a dummy interface that is up reports IFF_RUNNING; if this fails, \ + the fixture is no longer a dummy and what it pins has changed" ); // And it resolves to an index, which is what a bind would attach to. assert!(io::interface_index(&iface).is_some());