fix(relay): keep unknown unicast on gateway path
The relay models the physical LAN as the gateway port, not as another remote client. Client-originated unknown unicast now forwards only to the gateway, and gateway-originated unknown unicast is dropped unless it resolves to a registered remote client. Broadcast and multicast fanout is unchanged. This prevents promiscuous gateway capture of unrelated LAN unicast from being flooded to every remote client. It also keeps client-to-LAN traffic from needlessly leaking to other clients in the room. 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:
@@ -105,6 +105,8 @@ self-signed development certificate; `--dev-cert-der-out` writes that
|
||||
certificate so the gateway and client can pin it in development. Production
|
||||
certificate handling remains future work. Ethernet forwarding decisions are
|
||||
logged with room, peer, MAC, ethertype, action, drop reason, and target count.
|
||||
Unknown unicast from a client is forwarded only to the gateway port; unknown
|
||||
unicast from the gateway is dropped instead of flooded to every remote client.
|
||||
|
||||
## Gateway
|
||||
|
||||
|
||||
@@ -507,7 +507,10 @@ impl Room {
|
||||
{
|
||||
return Ok(ForwardingDecision::rate_limited());
|
||||
}
|
||||
self.all_peer_ids_except(ingress_peer_id)
|
||||
match ingress_role {
|
||||
Role::Client => self.gateway_peer_id_except(ingress_peer_id),
|
||||
Role::Gateway => Vec::new(),
|
||||
}
|
||||
};
|
||||
|
||||
if targets.is_empty() {
|
||||
@@ -564,6 +567,13 @@ impl Room {
|
||||
peer_ids.sort_unstable();
|
||||
peer_ids
|
||||
}
|
||||
|
||||
fn gateway_peer_id_except(&self, ingress_peer_id: u32) -> Vec<u32> {
|
||||
self.gateway
|
||||
.as_ref()
|
||||
.filter(|gateway| gateway.peer_id() != ingress_peer_id)
|
||||
.map_or_else(Vec::new, |gateway| vec![gateway.peer_id()])
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -933,18 +943,35 @@ mod tests {
|
||||
fn forwards_unknown_client_unicast_to_gateway() {
|
||||
let mut registry = RoomRegistry::default();
|
||||
let gateway = registry.join(gateway_hello()).unwrap();
|
||||
let client = registry.join(client_hello(1)).unwrap();
|
||||
let client_one = registry.join(client_hello(1)).unwrap();
|
||||
let client_two = registry.join(client_hello(2)).unwrap();
|
||||
let frame = ethernet(MacAddr::new([0x00, 1, 2, 3, 4, 5]), mac(1));
|
||||
|
||||
let decision = registry
|
||||
.forward_ethernet(&room(), client.peer().peer_id(), &frame)
|
||||
.forward_ethernet(&room(), client_one.peer().peer_id(), &frame)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(decision.action(), FrameAction::Forwarded);
|
||||
assert_eq!(decision.targets(), &[gateway.peer().peer_id()]);
|
||||
assert!(!decision.targets().contains(&client_two.peer().peer_id()));
|
||||
assert_eq!(decision.drop_reason(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn drops_gateway_unicast_to_unknown_remote_mac() {
|
||||
let mut registry = RoomRegistry::default();
|
||||
let gateway = registry.join(gateway_hello()).unwrap();
|
||||
registry.join(client_hello(1)).unwrap();
|
||||
registry.join(client_hello(2)).unwrap();
|
||||
let frame = ethernet(physical_mac(), MacAddr::new([0x00, 1, 2, 3, 4, 5]));
|
||||
|
||||
let decision = registry
|
||||
.forward_ethernet(&room(), gateway.peer().peer_id(), &frame)
|
||||
.unwrap();
|
||||
|
||||
assert_dropped(&decision, DropReason::UnknownDestination);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn forwards_gateway_unicast_to_matching_client() {
|
||||
let mut registry = RoomRegistry::default();
|
||||
@@ -1032,10 +1059,7 @@ mod tests {
|
||||
.forward_ethernet_at(&room(), client_one.peer().peer_id(), &unknown_unicast, now)
|
||||
.unwrap();
|
||||
assert_eq!(decision.action(), FrameAction::Forwarded);
|
||||
assert_eq!(
|
||||
decision.targets(),
|
||||
&[gateway.peer().peer_id(), client_two.peer().peer_id()]
|
||||
);
|
||||
assert_eq!(decision.targets(), &[gateway.peer().peer_id()]);
|
||||
}
|
||||
|
||||
let decision = registry
|
||||
|
||||
Reference in New Issue
Block a user