fips-core 0.3.1

Reusable FIPS mesh, endpoint, transport, and protocol library
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
//! Node-level statistics for routing, forwarding, and discovery operations.
//!
//! Unlike `EthernetStats` (which uses `AtomicU64` + `Arc` for cross-task
//! sharing), these counters use plain `u64` because `Node` handlers run
//! on a single `&mut self` context. A `snapshot()` method produces a
//! copyable struct for control socket queries.

use serde::Serialize;

/// Forwarding statistics — packets and bytes for each outcome.
#[derive(Default)]
pub struct ForwardingStats {
    pub received_packets: u64,
    pub received_bytes: u64,
    pub decode_error_packets: u64,
    pub decode_error_bytes: u64,
    pub ttl_exhausted_packets: u64,
    pub ttl_exhausted_bytes: u64,
    pub delivered_packets: u64,
    pub delivered_bytes: u64,
    pub forwarded_packets: u64,
    pub forwarded_bytes: u64,
    pub drop_no_route_packets: u64,
    pub drop_no_route_bytes: u64,
    pub drop_mtu_exceeded_packets: u64,
    pub drop_mtu_exceeded_bytes: u64,
    pub drop_send_error_packets: u64,
    pub drop_send_error_bytes: u64,
    pub originated_packets: u64,
    pub originated_bytes: u64,
}

impl ForwardingStats {
    pub fn record_received(&mut self, bytes: usize) {
        self.received_packets += 1;
        self.received_bytes += bytes as u64;
    }

    pub fn record_decode_error(&mut self, bytes: usize) {
        self.decode_error_packets += 1;
        self.decode_error_bytes += bytes as u64;
    }

    pub fn record_ttl_exhausted(&mut self, bytes: usize) {
        self.ttl_exhausted_packets += 1;
        self.ttl_exhausted_bytes += bytes as u64;
    }

    pub fn record_delivered(&mut self, bytes: usize) {
        self.delivered_packets += 1;
        self.delivered_bytes += bytes as u64;
    }

    pub fn record_forwarded(&mut self, bytes: usize) {
        self.forwarded_packets += 1;
        self.forwarded_bytes += bytes as u64;
    }

    pub fn record_drop_no_route(&mut self, bytes: usize) {
        self.drop_no_route_packets += 1;
        self.drop_no_route_bytes += bytes as u64;
    }

    pub fn record_drop_mtu_exceeded(&mut self, bytes: usize) {
        self.drop_mtu_exceeded_packets += 1;
        self.drop_mtu_exceeded_bytes += bytes as u64;
    }

    pub fn record_drop_send_error(&mut self, bytes: usize) {
        self.drop_send_error_packets += 1;
        self.drop_send_error_bytes += bytes as u64;
    }

    pub fn record_originated(&mut self, bytes: usize) {
        self.originated_packets += 1;
        self.originated_bytes += bytes as u64;
    }

    pub fn snapshot(&self) -> ForwardingStatsSnapshot {
        ForwardingStatsSnapshot {
            received_packets: self.received_packets,
            received_bytes: self.received_bytes,
            decode_error_packets: self.decode_error_packets,
            decode_error_bytes: self.decode_error_bytes,
            ttl_exhausted_packets: self.ttl_exhausted_packets,
            ttl_exhausted_bytes: self.ttl_exhausted_bytes,
            delivered_packets: self.delivered_packets,
            delivered_bytes: self.delivered_bytes,
            forwarded_packets: self.forwarded_packets,
            forwarded_bytes: self.forwarded_bytes,
            drop_no_route_packets: self.drop_no_route_packets,
            drop_no_route_bytes: self.drop_no_route_bytes,
            drop_mtu_exceeded_packets: self.drop_mtu_exceeded_packets,
            drop_mtu_exceeded_bytes: self.drop_mtu_exceeded_bytes,
            drop_send_error_packets: self.drop_send_error_packets,
            drop_send_error_bytes: self.drop_send_error_bytes,
            originated_packets: self.originated_packets,
            originated_bytes: self.originated_bytes,
        }
    }
}

/// Discovery statistics — packet counts for request and response handling.
#[derive(Default)]
pub struct DiscoveryStats {
    // Request counters
    pub req_received: u64,
    pub req_decode_error: u64,
    pub req_duplicate: u64,
    pub req_target_is_us: u64,
    pub req_forwarded: u64,
    pub req_ttl_exhausted: u64,
    pub req_initiated: u64,
    pub req_deduplicated: u64,
    pub req_backoff_suppressed: u64,
    pub req_forward_rate_limited: u64,
    pub req_bloom_miss: u64,
    pub req_no_tree_peer: u64,
    pub req_fallback_forwarded: u64,
    // Response counters
    pub resp_received: u64,
    pub resp_decode_error: u64,
    pub resp_forwarded: u64,
    pub resp_identity_miss: u64,
    pub resp_proof_failed: u64,
    pub resp_accepted: u64,
    pub resp_timed_out: u64,
}

impl DiscoveryStats {
    pub fn snapshot(&self) -> DiscoveryStatsSnapshot {
        DiscoveryStatsSnapshot {
            req_received: self.req_received,
            req_decode_error: self.req_decode_error,
            req_duplicate: self.req_duplicate,
            req_target_is_us: self.req_target_is_us,
            req_forwarded: self.req_forwarded,
            req_ttl_exhausted: self.req_ttl_exhausted,
            req_initiated: self.req_initiated,
            req_deduplicated: self.req_deduplicated,
            req_backoff_suppressed: self.req_backoff_suppressed,
            req_forward_rate_limited: self.req_forward_rate_limited,
            req_bloom_miss: self.req_bloom_miss,
            req_no_tree_peer: self.req_no_tree_peer,
            req_fallback_forwarded: self.req_fallback_forwarded,
            resp_received: self.resp_received,
            resp_decode_error: self.resp_decode_error,
            resp_forwarded: self.resp_forwarded,
            resp_identity_miss: self.resp_identity_miss,
            resp_proof_failed: self.resp_proof_failed,
            resp_accepted: self.resp_accepted,
            resp_timed_out: self.resp_timed_out,
        }
    }
}

/// Spanning tree statistics — announce handling and parent tracking.
#[derive(Default)]
pub struct TreeStats {
    // Inbound announce handling
    pub received: u64,
    pub decode_error: u64,
    pub unknown_peer: u64,
    pub addr_mismatch: u64,
    pub sig_failed: u64,
    pub stale: u64,
    pub accepted: u64,
    pub parent_switched: u64,
    pub loop_detected: u64,
    pub ancestry_changed: u64,
    // Outbound announce sending
    pub sent: u64,
    pub rate_limited: u64,
    pub send_failed: u64,
    // Cumulative events
    pub parent_switches: u64,
    pub parent_losses: u64,
    pub flap_dampened: u64,
}

impl TreeStats {
    pub fn snapshot(&self) -> TreeStatsSnapshot {
        TreeStatsSnapshot {
            received: self.received,
            decode_error: self.decode_error,
            unknown_peer: self.unknown_peer,
            addr_mismatch: self.addr_mismatch,
            sig_failed: self.sig_failed,
            stale: self.stale,
            accepted: self.accepted,
            parent_switched: self.parent_switched,
            loop_detected: self.loop_detected,
            ancestry_changed: self.ancestry_changed,
            sent: self.sent,
            rate_limited: self.rate_limited,
            send_failed: self.send_failed,
            parent_switches: self.parent_switches,
            parent_losses: self.parent_losses,
            flap_dampened: self.flap_dampened,
        }
    }
}

/// Bloom filter statistics — filter announce handling.
#[derive(Default)]
pub struct BloomStats {
    // Inbound announce handling
    pub received: u64,
    pub decode_error: u64,
    pub invalid: u64,
    pub non_v1: u64,
    pub unknown_peer: u64,
    pub stale: u64,
    pub fill_exceeded: u64,
    pub accepted: u64,
    // Outbound announce sending
    pub sent: u64,
    pub debounce_suppressed: u64,
    pub send_failed: u64,
}

impl BloomStats {
    pub fn snapshot(&self) -> BloomStatsSnapshot {
        BloomStatsSnapshot {
            received: self.received,
            decode_error: self.decode_error,
            invalid: self.invalid,
            non_v1: self.non_v1,
            unknown_peer: self.unknown_peer,
            stale: self.stale,
            fill_exceeded: self.fill_exceeded,
            accepted: self.accepted,
            sent: self.sent,
            debounce_suppressed: self.debounce_suppressed,
            send_failed: self.send_failed,
        }
    }
}

/// Error signal statistics — counts of each error signal type received.
#[derive(Default)]
pub struct ErrorSignalStats {
    pub coords_required: u64,
    pub path_broken: u64,
    pub mtu_exceeded: u64,
}

impl ErrorSignalStats {
    pub fn snapshot(&self) -> ErrorSignalStatsSnapshot {
        ErrorSignalStatsSnapshot {
            coords_required: self.coords_required,
            path_broken: self.path_broken,
            mtu_exceeded: self.mtu_exceeded,
        }
    }
}

/// Congestion event statistics — ECN CE tracking and detection triggers.
#[derive(Default)]
pub struct CongestionStats {
    /// Packets forwarded with the CE flag set (incoming CE or locally detected).
    pub ce_forwarded: u64,
    /// CE-flagged packets received at this node as final destination.
    pub ce_received: u64,
    /// Number of times detect_congestion() returned true.
    pub congestion_detected: u64,
    /// Rising-edge transport kernel drop events (not-dropping → dropping).
    pub kernel_drop_events: u64,
}

impl CongestionStats {
    pub fn record_ce_forwarded(&mut self) {
        self.ce_forwarded += 1;
    }

    pub fn record_ce_received(&mut self) {
        self.ce_received += 1;
    }

    pub fn record_congestion_detected(&mut self) {
        self.congestion_detected += 1;
    }

    pub fn record_kernel_drop_event(&mut self) {
        self.kernel_drop_events += 1;
    }

    pub fn snapshot(&self) -> CongestionStatsSnapshot {
        CongestionStatsSnapshot {
            ce_forwarded: self.ce_forwarded,
            ce_received: self.ce_received,
            congestion_detected: self.congestion_detected,
            kernel_drop_events: self.kernel_drop_events,
        }
    }
}

/// Aggregate node statistics.
#[derive(Default)]
pub struct NodeStats {
    pub forwarding: ForwardingStats,
    pub discovery: DiscoveryStats,
    pub tree: TreeStats,
    pub bloom: BloomStats,
    pub errors: ErrorSignalStats,
    pub congestion: CongestionStats,
}

impl NodeStats {
    pub fn new() -> Self {
        Self::default()
    }

    pub fn snapshot(&self) -> NodeStatsSnapshot {
        NodeStatsSnapshot {
            forwarding: self.forwarding.snapshot(),
            discovery: self.discovery.snapshot(),
            tree: self.tree.snapshot(),
            bloom: self.bloom.snapshot(),
            errors: self.errors.snapshot(),
            congestion: self.congestion.snapshot(),
        }
    }
}

// --- Snapshot types (copyable, serializable) ---

#[derive(Clone, Debug, Default, Serialize)]
pub struct ForwardingStatsSnapshot {
    pub received_packets: u64,
    pub received_bytes: u64,
    pub decode_error_packets: u64,
    pub decode_error_bytes: u64,
    pub ttl_exhausted_packets: u64,
    pub ttl_exhausted_bytes: u64,
    pub delivered_packets: u64,
    pub delivered_bytes: u64,
    pub forwarded_packets: u64,
    pub forwarded_bytes: u64,
    pub drop_no_route_packets: u64,
    pub drop_no_route_bytes: u64,
    pub drop_mtu_exceeded_packets: u64,
    pub drop_mtu_exceeded_bytes: u64,
    pub drop_send_error_packets: u64,
    pub drop_send_error_bytes: u64,
    pub originated_packets: u64,
    pub originated_bytes: u64,
}

#[derive(Clone, Debug, Default, Serialize)]
pub struct DiscoveryStatsSnapshot {
    pub req_received: u64,
    pub req_decode_error: u64,
    pub req_duplicate: u64,
    pub req_target_is_us: u64,
    pub req_forwarded: u64,
    pub req_ttl_exhausted: u64,
    pub req_initiated: u64,
    pub req_deduplicated: u64,
    pub req_backoff_suppressed: u64,
    pub req_forward_rate_limited: u64,
    pub req_bloom_miss: u64,
    pub req_no_tree_peer: u64,
    pub req_fallback_forwarded: u64,
    pub resp_received: u64,
    pub resp_decode_error: u64,
    pub resp_forwarded: u64,
    pub resp_identity_miss: u64,
    pub resp_proof_failed: u64,
    pub resp_accepted: u64,
    pub resp_timed_out: u64,
}

#[derive(Clone, Debug, Default, Serialize)]
pub struct TreeStatsSnapshot {
    pub received: u64,
    pub decode_error: u64,
    pub unknown_peer: u64,
    pub addr_mismatch: u64,
    pub sig_failed: u64,
    pub stale: u64,
    pub accepted: u64,
    pub parent_switched: u64,
    pub loop_detected: u64,
    pub ancestry_changed: u64,
    pub sent: u64,
    pub rate_limited: u64,
    pub send_failed: u64,
    pub parent_switches: u64,
    pub parent_losses: u64,
    pub flap_dampened: u64,
}

#[derive(Clone, Debug, Default, Serialize)]
pub struct BloomStatsSnapshot {
    pub received: u64,
    pub decode_error: u64,
    pub invalid: u64,
    pub non_v1: u64,
    pub unknown_peer: u64,
    pub stale: u64,
    pub fill_exceeded: u64,
    pub accepted: u64,
    pub sent: u64,
    pub debounce_suppressed: u64,
    pub send_failed: u64,
}

#[derive(Clone, Debug, Default, Serialize)]
pub struct ErrorSignalStatsSnapshot {
    pub coords_required: u64,
    pub path_broken: u64,
    pub mtu_exceeded: u64,
}

#[derive(Clone, Debug, Default, Serialize)]
pub struct CongestionStatsSnapshot {
    pub ce_forwarded: u64,
    pub ce_received: u64,
    pub congestion_detected: u64,
    pub kernel_drop_events: u64,
}

#[derive(Clone, Debug, Default, Serialize)]
pub struct NodeStatsSnapshot {
    pub forwarding: ForwardingStatsSnapshot,
    pub discovery: DiscoveryStatsSnapshot,
    pub tree: TreeStatsSnapshot,
    pub bloom: BloomStatsSnapshot,
    pub errors: ErrorSignalStatsSnapshot,
    pub congestion: CongestionStatsSnapshot,
}