mirror of
https://github.com/jmcorgan/fips.git
synced 2026-07-30 19:46:15 +00:00
Retire the three ignored Ethernet tests, which covered nothing unique
All three required root or CAP_NET_RAW, neither runner passes --ignored, and so none had ever executed in CI. Before deleting them I checked what each covered and whether anything else covered it: - The two-node handshake is covered by the ethernet-only chaos scenario, whose baseline assertion cannot pass unless handshakes complete over AF_PACKET. - Tree convergence and root election by smallest NodeAddr are covered by ethernet-only's single-root assertion and by the in-process spanning-tree tests respectively. - The mixed-transport coexistence property is covered twice over by test_tcp_mixed_transport_coexistence and test_ble_mixed_transport, which call the same verify_tree_convergence_components helper with the same two-component shape and both run today. So the only thing running them would have added is an AF_PACKET variant of a property already proven on two other transports. The counts confirm nothing running was lost: 1392 tests pass before and after, and the ignored count falls from 7 to 4. The ethernet-only comment block cited these tests as the reason its assertion was the only Ethernet coverage in CI. It now records the sharper fact found while reading them: that coverage is control plane only. Both Ethernet scenarios disable traffic, so no datagram crosses an Ethernet link anywhere, which leaves the frame length field that trims NIC minimum frame padding ahead of AEAD verification unexercised. Deleting these tests does not widen that gap, because the test named for data exchange did not exchange any.
This commit is contained in:
@@ -1,214 +0,0 @@
|
|||||||
//! Ethernet transport integration tests.
|
|
||||||
//!
|
|
||||||
//! Tests that the Ethernet transport works end-to-end using veth pairs.
|
|
||||||
//! All tests require root or CAP_NET_RAW and are marked `#[ignore]`.
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
use crate::config::EthernetConfig;
|
|
||||||
use crate::transport::ethernet::EthernetTransport;
|
|
||||||
use crate::transport::{TransportAddr, TransportHandle, TransportId, packet_channel};
|
|
||||||
use spanning_tree::{TestNode, cleanup_nodes, drain_all_packets, initiate_handshake};
|
|
||||||
|
|
||||||
use std::process::Command;
|
|
||||||
use std::sync::atomic::{AtomicU32, Ordering};
|
|
||||||
|
|
||||||
/// Atomic counter for unique veth names across tests.
|
|
||||||
static VETH_COUNTER: AtomicU32 = AtomicU32::new(0);
|
|
||||||
|
|
||||||
/// RAII wrapper for a veth pair.
|
|
||||||
///
|
|
||||||
/// Creates a pair of connected virtual Ethernet interfaces. Destroying
|
|
||||||
/// one end automatically destroys the other.
|
|
||||||
struct VethPair {
|
|
||||||
name_a: String,
|
|
||||||
name_b: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl VethPair {
|
|
||||||
/// Create a new veth pair with unique interface names.
|
|
||||||
///
|
|
||||||
/// Names are kept under 15 chars (IFNAMSIZ limit). Format: `ftXXa`/`ftXXb`
|
|
||||||
/// where XX is an atomic counter combined with PID for cross-process uniqueness.
|
|
||||||
fn create() -> Self {
|
|
||||||
let id = VETH_COUNTER.fetch_add(1, Ordering::Relaxed);
|
|
||||||
let pid = std::process::id() % 10000;
|
|
||||||
let name_a = format!("ft{}{}a", pid, id);
|
|
||||||
let name_b = format!("ft{}{}b", pid, id);
|
|
||||||
|
|
||||||
assert!(name_a.len() <= 15, "veth name too long: {}", name_a);
|
|
||||||
assert!(name_b.len() <= 15, "veth name too long: {}", name_b);
|
|
||||||
|
|
||||||
// Create veth pair
|
|
||||||
let status = Command::new("ip")
|
|
||||||
.args([
|
|
||||||
"link", "add", &name_a, "type", "veth", "peer", "name", &name_b,
|
|
||||||
])
|
|
||||||
.status()
|
|
||||||
.expect("failed to run 'ip link add'");
|
|
||||||
assert!(status.success(), "failed to create veth pair");
|
|
||||||
|
|
||||||
// Bring both ends up
|
|
||||||
let status = Command::new("ip")
|
|
||||||
.args(["link", "set", &name_a, "up"])
|
|
||||||
.status()
|
|
||||||
.expect("failed to run 'ip link set up'");
|
|
||||||
assert!(status.success(), "failed to bring up {}", name_a);
|
|
||||||
|
|
||||||
let status = Command::new("ip")
|
|
||||||
.args(["link", "set", &name_b, "up"])
|
|
||||||
.status()
|
|
||||||
.expect("failed to run 'ip link set up'");
|
|
||||||
assert!(status.success(), "failed to bring up {}", name_b);
|
|
||||||
|
|
||||||
VethPair { name_a, name_b }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for VethPair {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
// Deleting one end destroys both
|
|
||||||
let _ = Command::new("ip")
|
|
||||||
.args(["link", "delete", &self.name_a])
|
|
||||||
.status();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a test node with a live Ethernet transport on the given interface.
|
|
||||||
///
|
|
||||||
/// Parallel to `make_test_node()` in spanning_tree.rs but uses
|
|
||||||
/// EthernetTransport instead of UDP.
|
|
||||||
async fn make_test_node_ethernet(interface: &str) -> TestNode {
|
|
||||||
let mut node = make_node();
|
|
||||||
let transport_id = TransportId::new(1);
|
|
||||||
|
|
||||||
let config = EthernetConfig {
|
|
||||||
interface: interface.to_string(),
|
|
||||||
discovery: Some(false),
|
|
||||||
announce: Some(false),
|
|
||||||
accept_connections: Some(true),
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
let (packet_tx, packet_rx) = packet_channel(256);
|
|
||||||
let mut transport = EthernetTransport::new(transport_id, None, config, packet_tx);
|
|
||||||
transport.start_async().await.unwrap();
|
|
||||||
|
|
||||||
let mac = transport
|
|
||||||
.local_mac()
|
|
||||||
.expect("transport should have MAC after start");
|
|
||||||
let addr = TransportAddr::from_bytes(&mac);
|
|
||||||
|
|
||||||
node.transports
|
|
||||||
.insert(transport_id, TransportHandle::Ethernet(transport));
|
|
||||||
|
|
||||||
TestNode {
|
|
||||||
node,
|
|
||||||
transport_id,
|
|
||||||
packet_rx: spanning_tree::bridge_to_unbounded(packet_rx),
|
|
||||||
addr,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Two nodes on a veth pair complete a Noise handshake and establish peering.
|
|
||||||
#[tokio::test]
|
|
||||||
#[ignore] // Requires root or CAP_NET_RAW
|
|
||||||
async fn test_ethernet_two_node_handshake() {
|
|
||||||
let veth = VethPair::create();
|
|
||||||
|
|
||||||
let mut nodes = vec![
|
|
||||||
make_test_node_ethernet(&veth.name_a).await,
|
|
||||||
make_test_node_ethernet(&veth.name_b).await,
|
|
||||||
];
|
|
||||||
|
|
||||||
// Initiate handshake from node 0 to node 1
|
|
||||||
initiate_handshake(&mut nodes, 0, 1).await;
|
|
||||||
|
|
||||||
// Drain all packets (handshake + tree announce)
|
|
||||||
let total = drain_all_packets(&mut nodes, false).await;
|
|
||||||
assert!(total > 0, "should have processed packets");
|
|
||||||
|
|
||||||
// Verify bidirectional peering
|
|
||||||
let addr_0 = *nodes[0].node.node_addr();
|
|
||||||
let addr_1 = *nodes[1].node.node_addr();
|
|
||||||
assert!(
|
|
||||||
nodes[0].node.get_peer(&addr_1).is_some(),
|
|
||||||
"node 0 should have node 1 as peer"
|
|
||||||
);
|
|
||||||
assert!(
|
|
||||||
nodes[1].node.get_peer(&addr_0).is_some(),
|
|
||||||
"node 1 should have node 0 as peer"
|
|
||||||
);
|
|
||||||
|
|
||||||
cleanup_nodes(&mut nodes).await;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Two Ethernet nodes converge to a correct spanning tree (2-node tree).
|
|
||||||
#[tokio::test]
|
|
||||||
#[ignore] // Requires root or CAP_NET_RAW
|
|
||||||
async fn test_ethernet_data_exchange() {
|
|
||||||
use spanning_tree::verify_tree_convergence;
|
|
||||||
|
|
||||||
let veth = VethPair::create();
|
|
||||||
|
|
||||||
let mut nodes = vec![
|
|
||||||
make_test_node_ethernet(&veth.name_a).await,
|
|
||||||
make_test_node_ethernet(&veth.name_b).await,
|
|
||||||
];
|
|
||||||
|
|
||||||
initiate_handshake(&mut nodes, 0, 1).await;
|
|
||||||
let total = drain_all_packets(&mut nodes, false).await;
|
|
||||||
assert!(total > 0);
|
|
||||||
|
|
||||||
// Verify spanning tree convergence
|
|
||||||
verify_tree_convergence(&nodes);
|
|
||||||
|
|
||||||
// The root should be the node with the smallest NodeAddr
|
|
||||||
let expected_root = std::cmp::min(*nodes[0].node.node_addr(), *nodes[1].node.node_addr());
|
|
||||||
assert_eq!(*nodes[0].node.tree_state().root(), expected_root);
|
|
||||||
assert_eq!(*nodes[1].node.tree_state().root(), expected_root);
|
|
||||||
|
|
||||||
cleanup_nodes(&mut nodes).await;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Mixed transport: 2 Ethernet nodes + 2 UDP nodes coexist.
|
|
||||||
///
|
|
||||||
/// Each transport forms its own connected component. Validates that
|
|
||||||
/// `process_available_packets()` handles heterogeneous transport types.
|
|
||||||
#[tokio::test]
|
|
||||||
#[ignore] // Requires root or CAP_NET_RAW
|
|
||||||
async fn test_mixed_transport_coexistence() {
|
|
||||||
use spanning_tree::{make_test_node, verify_tree_convergence_components};
|
|
||||||
|
|
||||||
let veth = VethPair::create();
|
|
||||||
|
|
||||||
// Create 2 Ethernet nodes and 2 UDP nodes
|
|
||||||
let eth_0 = make_test_node_ethernet(&veth.name_a).await;
|
|
||||||
let eth_1 = make_test_node_ethernet(&veth.name_b).await;
|
|
||||||
let udp_0 = make_test_node().await;
|
|
||||||
let udp_1 = make_test_node().await;
|
|
||||||
|
|
||||||
let mut nodes = vec![eth_0, eth_1, udp_0, udp_1];
|
|
||||||
|
|
||||||
// Handshake within each component
|
|
||||||
initiate_handshake(&mut nodes, 0, 1).await; // Ethernet pair
|
|
||||||
initiate_handshake(&mut nodes, 2, 3).await; // UDP pair
|
|
||||||
|
|
||||||
// Drain all packets across both transports
|
|
||||||
let total = drain_all_packets(&mut nodes, false).await;
|
|
||||||
assert!(total > 0);
|
|
||||||
|
|
||||||
// Verify each component converges independently
|
|
||||||
verify_tree_convergence_components(&nodes, &[vec![0, 1], vec![2, 3]]);
|
|
||||||
|
|
||||||
// Ethernet component has its own root
|
|
||||||
let eth_root = std::cmp::min(*nodes[0].node.node_addr(), *nodes[1].node.node_addr());
|
|
||||||
assert_eq!(*nodes[0].node.tree_state().root(), eth_root);
|
|
||||||
assert_eq!(*nodes[1].node.tree_state().root(), eth_root);
|
|
||||||
|
|
||||||
// UDP component has its own root
|
|
||||||
let udp_root = std::cmp::min(*nodes[2].node.node_addr(), *nodes[3].node.node_addr());
|
|
||||||
assert_eq!(*nodes[2].node.tree_state().root(), udp_root);
|
|
||||||
assert_eq!(*nodes[3].node.tree_state().root(), udp_root);
|
|
||||||
|
|
||||||
cleanup_nodes(&mut nodes).await;
|
|
||||||
}
|
|
||||||
@@ -13,8 +13,6 @@ mod bootstrap;
|
|||||||
mod decrypt_failure;
|
mod decrypt_failure;
|
||||||
mod disconnect;
|
mod disconnect;
|
||||||
mod discovery;
|
mod discovery;
|
||||||
#[cfg(target_os = "linux")]
|
|
||||||
mod ethernet;
|
|
||||||
mod forwarding;
|
mod forwarding;
|
||||||
mod handshake;
|
mod handshake;
|
||||||
mod heartbeat;
|
mod heartbeat;
|
||||||
|
|||||||
@@ -50,10 +50,11 @@ traffic:
|
|||||||
# A 4-node mesh forms a spanning tree: one root and three nodes
|
# A 4-node mesh forms a spanning tree: one root and three nodes
|
||||||
# with a parent. All six provably-completed archived runs show exactly
|
# with a parent. All six provably-completed archived runs show exactly
|
||||||
# that, so these are the shape of a converged mesh rather than a
|
# that, so these are the shape of a converged mesh rather than a
|
||||||
# tolerance fitted to observations. This is currently the only
|
# tolerance fitted to observations. This is the only assertion covering
|
||||||
# assertion covering Ethernet transport anywhere in CI: the three
|
# Ethernet transport anywhere in CI, and it covers the control plane
|
||||||
# tests in src/node/tests/ethernet.rs are #[ignore]d for requiring
|
# only: traffic is disabled above, so no datagram crosses an Ethernet
|
||||||
# CAP_NET_RAW and neither runner passes --ignored.
|
# link in any test. Framing, the length field that trims NIC minimum-
|
||||||
|
# frame padding, and AEAD over Ethernet are all unexercised as a result.
|
||||||
assertions:
|
assertions:
|
||||||
baseline:
|
baseline:
|
||||||
min_nodes_reporting: 4
|
min_nodes_reporting: 4
|
||||||
|
|||||||
Reference in New Issue
Block a user