diff --git a/README.md b/README.md index 9797f7f..a643d07 100644 --- a/README.md +++ b/README.md @@ -198,12 +198,13 @@ The gateway rejects Linux interfaces that sysfs identifies as Wi-Fi, and rejects wired interfaces whose sysfs carrier state reports no link; managed wireless NICs are not supported for the physical LAN bridge. It tracks remote-client MACs from relay lifecycle events and periodically emits -small CAM refresh frames so the physical switch keeps those MACs associated -with the gateway port. A newly observed client also triggers an immediate CAM -refresh frame instead of waiting for the first periodic refresh tick. When -control events and frame work are both ready, the bridge handles the lifecycle -event first so first packets after a client joins use the freshest remote-MAC -state available locally. Gateway +small CAM refresh frames, logged with `reason=periodic`, so the physical +switch keeps those MACs associated with the gateway port. A newly observed +client also triggers an immediate CAM refresh frame logged with +`reason=peer_joined` instead of waiting for the first periodic refresh tick. +When control events and frame work are both ready, the bridge handles the +lifecycle event first so first packets after a client joins use the freshest +remote-MAC state available locally. Gateway frame logs include direction, peer id when present, MACs, ethertype/length, frame length, action, and drop reason. The gateway also tracks frame/datagram counters and periodically sends stats snapshots to the relay. Malformed or runt diff --git a/TESTING.md b/TESTING.md index 649e1f1..8259500 100644 --- a/TESTING.md +++ b/TESTING.md @@ -221,6 +221,7 @@ gateway control event: client peer ... joined with MAC ... gateway frame interface=eth0 direction=LanToRemote ... action=Forwarded gateway frame interface=eth0 direction=RemoteToLan ... action=Forwarded gateway CAM refresh interface=eth0 peer_id=... mac=... reason=peer_joined +gateway CAM refresh interface=eth0 peer_id=... mac=... reason=periodic ``` Client health: @@ -293,9 +294,10 @@ firewall, and whether the LAN subnet conflicts with the client's home LAN. Uncommon LAN subnets such as `10.73.42.0/24` are safer than `192.168.0.0/24`. If switch MAC learning does not show the Windows client MAC on the gateway -port, look for `gateway CAM refresh ... reason=peer_joined`. If that line is -present but the switch still does not learn it, check the selected gateway -interface and switch port first. +port, look for `gateway CAM refresh ... reason=peer_joined` immediately after +join and `gateway CAM refresh ... reason=periodic` about once per minute after +that. If those lines are present but the switch still does not learn it, check +the selected gateway interface and switch port first. ## Cleanup diff --git a/crates/lanparty-gateway/src/lib.rs b/crates/lanparty-gateway/src/lib.rs index e6451fe..48e34bc 100644 --- a/crates/lanparty-gateway/src/lib.rs +++ b/crates/lanparty-gateway/src/lib.rs @@ -458,8 +458,17 @@ impl GatewayConnection { } } _ = cam_refresh_tick.tick() => { - for frame in remote_clients.refresh_frames() { - write_lan_ethernet(&packet_socket, &frame).await?; + for refresh in remote_clients.refreshes() { + write_lan_ethernet(&packet_socket, refresh.frame()).await?; + println!( + "{}", + gateway_cam_refresh_log_line( + packet_socket.get_ref().interface(), + refresh.peer_id(), + refresh.mac(), + "periodic", + ) + ); } } _ = stats_tick.tick() => { @@ -932,10 +941,10 @@ impl RemoteClientTable { self.remote_clients.remove(&peer_id); } - fn refresh_frames(&self) -> Vec> { + fn refreshes(&self) -> Vec { self.remote_clients - .values() - .map(|source| cam_refresh_frame(*source, self.gateway_mac)) + .iter() + .map(|(peer_id, mac)| CamRefresh::new(*peer_id, *mac, self.gateway_mac)) .collect() } @@ -1412,8 +1421,12 @@ mod tests { assert_eq!(immediate_frame.source(), remote_mac); assert_eq!(immediate_frame.destination(), gateway_mac); assert_eq!(refresh.remote_mac_count(), 1); + let periodic_refreshes = refresh.refreshes(); + assert_eq!(periodic_refreshes.len(), 1); + assert_eq!(periodic_refreshes[0].peer_id(), 7); + assert_eq!(periodic_refreshes[0].mac(), remote_mac); assert_eq!( - EthernetFrame::parse(&refresh.refresh_frames()[0]) + EthernetFrame::parse(periodic_refreshes[0].frame()) .unwrap() .source(), remote_mac @@ -1427,7 +1440,7 @@ mod tests { None ); assert_eq!(refresh.remote_mac_count(), 0); - assert!(refresh.refresh_frames().is_empty()); + assert!(refresh.refreshes().is_empty()); } #[cfg(target_os = "linux")]