Skip to content

Commit eeef7f4

Browse files
zerox80claude
andauthored
refactor(backend): dedupe TUN reader routing and slim stats snapshot (#173)
The TUN reader hot loop in `spawn_tun_reader` had the IPv4 and IPv6 packet-delivery logic copy-pasted: ~50 lines that differed only in the peer map and the `no_peer` counter. The send-outcome handling (routed / channel-full drop accounting / closed -> remove_peer_if_same) was identical, so a fix to the send path could silently drift between the two address families. - Extract a generic `deliver_to_client<K>()` helper over the map key type; collapse the branches into a `match packet[0] >> 4`. - Derive `Clone, Copy` on `TunReaderStats` and replace the manual 9-field `*previous = Self { .. }` snapshot with `*previous = *self`, so adding a field later can't silently corrupt per-interval deltas. Pure structural cleanup, behavior unchanged. cargo build/clippy clean; all 150 backend tests pass (including the reused-IP race guards). Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 99ba52b commit eeef7f4

1 file changed

Lines changed: 74 additions & 65 deletions

File tree

backend/src/routing.rs

Lines changed: 74 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ where
2727
peers.remove_if(key, |_, tx| tx.same_channel(observed));
2828
}
2929

30-
#[derive(Default)]
30+
#[derive(Default, Clone, Copy)]
3131
struct TunReaderStats {
3232
read_packets: u64,
3333
read_bytes: u64,
@@ -66,17 +66,8 @@ impl TunReaderStats {
6666
channel_closed,
6767
);
6868

69-
*previous = Self {
70-
read_packets: self.read_packets,
71-
read_bytes: self.read_bytes,
72-
routed_packets: self.routed_packets,
73-
routed_bytes: self.routed_bytes,
74-
no_peer_v4: self.no_peer_v4,
75-
no_peer_v6: self.no_peer_v6,
76-
invalid_ip: self.invalid_ip,
77-
channel_full: self.channel_full,
78-
channel_closed: self.channel_closed,
79-
};
69+
// Snapshot the running totals so the next interval reports deltas.
70+
*previous = *self;
8071
}
8172
}
8273

@@ -94,6 +85,46 @@ pub fn spawn_tun_writer(
9485
});
9586
}
9687

88+
/// Routes one framed packet to the client registered for `dest_ip`, updating the
89+
/// shared TUN-reader counters for the send outcome.
90+
///
91+
/// Returns `false` when no peer is registered for the address, so the caller can
92+
/// bump the address-family-specific `no_peer` counter. This is the single code
93+
/// path shared by the IPv4 and IPv6 branches of the TUN reader's hot loop.
94+
#[allow(clippy::too_many_arguments)]
95+
fn deliver_to_client<K>(
96+
peers: &DashMap<K, ClientTx>,
97+
dest_ip: K,
98+
framed: Bytes,
99+
packet_len: u64,
100+
stats: &mut TunReaderStats,
101+
drop_count: &mut u64,
102+
last_drop_warn: &mut std::time::Instant,
103+
) -> bool
104+
where
105+
K: Hash + Eq + Copy + Display,
106+
{
107+
let Some(tx_client) = peers.get(&dest_ip).map(|tx_ref| tx_ref.value().clone()) else {
108+
return false;
109+
};
110+
111+
match tx_client.try_send(framed) {
112+
Ok(()) => {
113+
stats.routed_packets += 1;
114+
stats.routed_bytes += packet_len;
115+
}
116+
Err(TrySendError::Full(_)) => {
117+
stats.channel_full += 1;
118+
record_s2c_channel_drop(drop_count, last_drop_warn, dest_ip);
119+
}
120+
Err(TrySendError::Closed(_)) => {
121+
stats.channel_closed += 1;
122+
remove_peer_if_same(peers, &dest_ip, &tx_client);
123+
}
124+
}
125+
true
126+
}
127+
97128
fn record_s2c_channel_drop<D: Display>(
98129
drop_count: &mut u64,
99130
last_drop_warn: &mut std::time::Instant,
@@ -161,63 +192,41 @@ pub fn spawn_tun_reader(
161192
continue;
162193
}
163194

164-
let version = packet[0] >> 4;
165-
if version == 4 {
166-
if let Ok(ipv4_header) = Ipv4HeaderSlice::from_slice(&packet) {
167-
let dest_ip = ipv4_header.destination_addr();
168-
let packet_len = packet.len() as u64;
169-
170-
let tx_client_opt = state_reader.peers.get(&dest_ip).map(|tx_ref| tx_ref.value().clone());
171-
if let Some(tx_client) = tx_client_opt {
172-
match tx_client.try_send(framed) {
173-
Ok(()) => {
174-
stats.routed_packets += 1;
175-
stats.routed_bytes += packet_len;
176-
}
177-
Err(TrySendError::Full(_)) => {
178-
stats.channel_full += 1;
179-
record_s2c_channel_drop(&mut drop_count, &mut last_drop_warn, dest_ip);
180-
}
181-
Err(TrySendError::Closed(_)) => {
182-
stats.channel_closed += 1;
183-
remove_peer_if_same(&state_reader.peers, &dest_ip, &tx_client);
184-
}
195+
let packet_len = packet.len() as u64;
196+
match packet[0] >> 4 {
197+
4 => match Ipv4HeaderSlice::from_slice(&packet) {
198+
Ok(header) => {
199+
if !deliver_to_client(
200+
&state_reader.peers,
201+
header.destination_addr(),
202+
framed,
203+
packet_len,
204+
&mut stats,
205+
&mut drop_count,
206+
&mut last_drop_warn,
207+
) {
208+
stats.no_peer_v4 += 1;
185209
}
186-
} else {
187-
stats.no_peer_v4 += 1;
188210
}
189-
} else {
190-
stats.invalid_ip += 1;
191-
}
192-
} else if version == 6 {
193-
if let Ok(ipv6_header) = Ipv6HeaderSlice::from_slice(&packet) {
194-
let dest_ip = ipv6_header.destination_addr();
195-
let packet_len = packet.len() as u64;
196-
197-
let tx_client_opt = state_reader.peers_v6.get(&dest_ip).map(|tx_ref| tx_ref.value().clone());
198-
if let Some(tx_client) = tx_client_opt {
199-
match tx_client.try_send(framed) {
200-
Ok(()) => {
201-
stats.routed_packets += 1;
202-
stats.routed_bytes += packet_len;
203-
}
204-
Err(TrySendError::Full(_)) => {
205-
stats.channel_full += 1;
206-
record_s2c_channel_drop(&mut drop_count, &mut last_drop_warn, dest_ip);
207-
}
208-
Err(TrySendError::Closed(_)) => {
209-
stats.channel_closed += 1;
210-
remove_peer_if_same(&state_reader.peers_v6, &dest_ip, &tx_client);
211-
}
211+
Err(_) => stats.invalid_ip += 1,
212+
},
213+
6 => match Ipv6HeaderSlice::from_slice(&packet) {
214+
Ok(header) => {
215+
if !deliver_to_client(
216+
&state_reader.peers_v6,
217+
header.destination_addr(),
218+
framed,
219+
packet_len,
220+
&mut stats,
221+
&mut drop_count,
222+
&mut last_drop_warn,
223+
) {
224+
stats.no_peer_v6 += 1;
212225
}
213-
} else {
214-
stats.no_peer_v6 += 1;
215226
}
216-
} else {
217-
stats.invalid_ip += 1;
218-
}
219-
} else {
220-
stats.invalid_ip += 1;
227+
Err(_) => stats.invalid_ip += 1,
228+
},
229+
_ => stats.invalid_ip += 1,
221230
}
222231
}
223232
Err(e) => {

0 commit comments

Comments
 (0)