feat(relay): classify safety rejects as filtered
PLAN.md describes relay frame logs with separate forwarded, dropped, filtered, and rate-limited actions. The relay had the shared `Filtered` action in its log vocabulary, but safety-policy rejects were still reported as generic drops. Classify forged client source MACs and L2 safety-filter matches as filtered forwarding decisions. Malformed frames and unknown destinations remain drops, while rate limits continue to use the rate-limited action. Document the distinction in the relay README section. Test Plan: - cargo fmt --check - cargo test -p lanparty-relay - cargo test --workspace - cargo clippy --workspace --all-targets -- -D warnings - git diff --check Refs: PLAN.md
This commit is contained in:
@@ -146,6 +146,15 @@ impl ForwardingDecision {
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
fn filtered(drop_reason: DropReason) -> Self {
|
||||
Self {
|
||||
targets: Vec::new(),
|
||||
action: FrameAction::Filtered,
|
||||
drop_reason: Some(drop_reason),
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
fn rate_limited() -> Self {
|
||||
Self {
|
||||
@@ -510,7 +519,7 @@ impl Room {
|
||||
if ingress_role == Role::Client {
|
||||
let expected_source = ingress_mac.expect("client peers have MAC addresses");
|
||||
if frame.source() != expected_source {
|
||||
return Ok(ForwardingDecision::dropped(
|
||||
return Ok(ForwardingDecision::filtered(
|
||||
DropReason::UnauthorizedSourceMac,
|
||||
));
|
||||
}
|
||||
@@ -519,7 +528,7 @@ impl Room {
|
||||
self.mark_peer_seen(ingress_peer_id, now);
|
||||
|
||||
if let Some(drop_reason) = safety_drop_reason(ingress_role, frame) {
|
||||
return Ok(ForwardingDecision::dropped(drop_reason));
|
||||
return Ok(ForwardingDecision::filtered(drop_reason));
|
||||
}
|
||||
|
||||
if ingress_role == Role::Client
|
||||
@@ -859,6 +868,12 @@ mod tests {
|
||||
assert!(decision.targets().is_empty());
|
||||
}
|
||||
|
||||
fn assert_filtered(decision: &ForwardingDecision, reason: DropReason) {
|
||||
assert_eq!(decision.action(), FrameAction::Filtered);
|
||||
assert_eq!(decision.drop_reason(), Some(reason));
|
||||
assert!(decision.targets().is_empty());
|
||||
}
|
||||
|
||||
fn assert_rate_limited(decision: &ForwardingDecision) {
|
||||
assert_eq!(decision.action(), FrameAction::RateLimited);
|
||||
assert_eq!(decision.drop_reason(), Some(DropReason::RateLimit));
|
||||
@@ -1139,7 +1154,7 @@ mod tests {
|
||||
before + Duration::from_secs(5),
|
||||
)
|
||||
.unwrap();
|
||||
assert_dropped(&decision, DropReason::UnauthorizedSourceMac);
|
||||
assert_filtered(&decision, DropReason::UnauthorizedSourceMac);
|
||||
|
||||
assert_eq!(
|
||||
registry
|
||||
@@ -1275,7 +1290,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn drops_client_frames_with_forged_source_mac() {
|
||||
fn filters_client_frames_with_forged_source_mac() {
|
||||
let mut registry = RoomRegistry::default();
|
||||
let client = registry.join(client_hello(1)).unwrap();
|
||||
let frame = ethernet(MacAddr::BROADCAST, mac(2));
|
||||
@@ -1284,16 +1299,11 @@ mod tests {
|
||||
.forward_ethernet(&room(), client.peer().peer_id(), &frame)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(decision.action(), FrameAction::Dropped);
|
||||
assert_eq!(
|
||||
decision.drop_reason(),
|
||||
Some(DropReason::UnauthorizedSourceMac)
|
||||
);
|
||||
assert!(decision.targets().is_empty());
|
||||
assert_filtered(&decision, DropReason::UnauthorizedSourceMac);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn drops_jumbo_frames() {
|
||||
fn filters_jumbo_frames() {
|
||||
let mut registry = RoomRegistry::default();
|
||||
registry.join(gateway_hello()).unwrap();
|
||||
let client = registry.join(client_hello(1)).unwrap();
|
||||
@@ -1304,11 +1314,11 @@ mod tests {
|
||||
.forward_ethernet(&room(), client.peer().peer_id(), &frame)
|
||||
.unwrap();
|
||||
|
||||
assert_dropped(&decision, DropReason::JumboFrame);
|
||||
assert_filtered(&decision, DropReason::JumboFrame);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn drops_l2_control_plane_frames_from_clients_and_gateway() {
|
||||
fn filters_l2_control_plane_frames_from_clients_and_gateway() {
|
||||
let mut registry = RoomRegistry::default();
|
||||
let gateway = registry.join(gateway_hello()).unwrap();
|
||||
let client = registry.join(client_hello(1)).unwrap();
|
||||
@@ -1324,12 +1334,12 @@ mod tests {
|
||||
.forward_ethernet(&room(), gateway.peer().peer_id(), &gateway_frame)
|
||||
.unwrap();
|
||||
|
||||
assert_dropped(&client_decision, DropReason::ControlPlaneEtherType);
|
||||
assert_dropped(&gateway_decision, DropReason::ControlPlaneEtherType);
|
||||
assert_filtered(&client_decision, DropReason::ControlPlaneEtherType);
|
||||
assert_filtered(&gateway_decision, DropReason::ControlPlaneEtherType);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn drops_remote_dhcp_server_replies_but_allows_lan_replies() {
|
||||
fn filters_remote_dhcp_server_replies_but_allows_lan_replies() {
|
||||
let mut registry = RoomRegistry::default();
|
||||
let gateway = registry.join(gateway_hello()).unwrap();
|
||||
let client = registry.join(client_hello(1)).unwrap();
|
||||
@@ -1346,13 +1356,13 @@ mod tests {
|
||||
.forward_ethernet(&room(), gateway.peer().peer_id(), &gateway_frame)
|
||||
.unwrap();
|
||||
|
||||
assert_dropped(&client_decision, DropReason::DhcpServerReply);
|
||||
assert_filtered(&client_decision, DropReason::DhcpServerReply);
|
||||
assert_eq!(gateway_decision.action(), FrameAction::Forwarded);
|
||||
assert_eq!(gateway_decision.targets(), &[client.peer().peer_id()]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn drops_remote_ipv6_router_advertisements() {
|
||||
fn filters_remote_ipv6_router_advertisements() {
|
||||
let mut registry = RoomRegistry::default();
|
||||
registry.join(gateway_hello()).unwrap();
|
||||
let client = registry.join(client_hello(1)).unwrap();
|
||||
@@ -1368,7 +1378,7 @@ mod tests {
|
||||
.forward_ethernet(&room(), client.peer().peer_id(), &frame)
|
||||
.unwrap();
|
||||
|
||||
assert_dropped(&decision, DropReason::Ipv6RouterAdvertisement);
|
||||
assert_filtered(&decision, DropReason::Ipv6RouterAdvertisement);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user