1use rns_core::constants;
11use rns_core::destination::destination_hash;
12use rns_core::hash::truncated_hash;
13use rns_core::msgpack::{self, Value};
14use rns_core::transport::types::{InterfaceId, InterfaceInfo};
15use rns_core::transport::TransportEngine;
16
17use super::interface_stats::InterfaceStats;
18use super::time;
19
20pub trait InterfaceStatusView {
22 fn id(&self) -> InterfaceId;
23 fn info(&self) -> &InterfaceInfo;
24 fn online(&self) -> bool;
25 fn stats(&self) -> &InterfaceStats;
26 fn tx_drops(&self) -> u64 {
27 0
28 }
29 fn tx_dropped_bytes(&self) -> u64 {
30 0
31 }
32 fn tx_stalled(&self) -> bool {
33 false
34 }
35 fn tx_buffered(&self) -> usize {
36 0
37 }
38}
39
40pub fn status_path_hash() -> [u8; 16] {
42 truncated_hash(b"/status")
43}
44
45pub fn path_path_hash() -> [u8; 16] {
47 truncated_hash(b"/path")
48}
49
50pub fn list_path_hash() -> [u8; 16] {
52 truncated_hash(b"/list")
53}
54
55pub fn is_management_path(path_hash: &[u8; 16]) -> bool {
57 *path_hash == status_path_hash()
58 || *path_hash == path_path_hash()
59 || *path_hash == list_path_hash()
60}
61
62pub fn management_dest_hash(transport_identity_hash: &[u8; 16]) -> [u8; 16] {
66 destination_hash(
67 "rnstransport",
68 &["remote", "management"],
69 Some(transport_identity_hash),
70 )
71}
72
73pub fn blackhole_dest_hash(transport_identity_hash: &[u8; 16]) -> [u8; 16] {
77 destination_hash(
78 "rnstransport",
79 &["info", "blackhole"],
80 Some(transport_identity_hash),
81 )
82}
83
84pub fn probe_dest_hash(transport_identity_hash: &[u8; 16]) -> [u8; 16] {
88 destination_hash("rnstransport", &["probe"], Some(transport_identity_hash))
89}
90
91pub fn announce_random_blob(rng: &mut dyn rns_crypto::Rng, now: f64) -> [u8; 10] {
97 let mut random_hash = [0u8; 10];
98 rng.fill_bytes(&mut random_hash[..5]);
99 let now_secs = if now.is_finite() && now > 0.0 {
100 now as u64
101 } else {
102 0
103 };
104 random_hash[5..10].copy_from_slice(&now_secs.to_be_bytes()[3..8]);
105 random_hash
106}
107
108fn announce_context(path_response: bool) -> u8 {
111 if path_response {
112 constants::CONTEXT_PATH_RESPONSE
113 } else {
114 constants::CONTEXT_NONE
115 }
116}
117
118pub fn build_probe_announce(
122 identity: &rns_crypto::identity::Identity,
123 rng: &mut dyn rns_crypto::Rng,
124 now: f64,
125 path_response: bool,
126) -> Option<Vec<u8>> {
127 let identity_hash = *identity.hash();
128 let dest_hash = probe_dest_hash(&identity_hash);
129 let name_hash = rns_core::destination::name_hash("rnstransport", &["probe"]);
130 let random_hash = announce_random_blob(rng, now);
131
132 let (announce_data, _has_ratchet) = rns_core::announce::AnnounceData::pack(
133 identity,
134 &dest_hash,
135 &name_hash,
136 &random_hash,
137 None,
138 None,
139 )
140 .ok()?;
141
142 let flags = rns_core::packet::PacketFlags {
143 header_type: constants::HEADER_1,
144 context_flag: constants::FLAG_UNSET,
145 transport_type: constants::TRANSPORT_BROADCAST,
146 destination_type: constants::DESTINATION_SINGLE,
147 packet_type: constants::PACKET_TYPE_ANNOUNCE,
148 };
149
150 let packet = rns_core::packet::RawPacket::pack(
151 flags,
152 0,
153 &dest_hash,
154 None,
155 announce_context(path_response),
156 &announce_data,
157 )
158 .ok()?;
159
160 Some(packet.raw)
161}
162
163#[derive(Debug, Clone, Default)]
165pub struct ManagementConfig {
166 pub enable_remote_management: bool,
168 pub remote_management_allowed: Vec<[u8; 16]>,
170 pub publish_blackhole: bool,
172}
173
174pub fn handle_status_request(
180 data: &[u8],
181 engine: &TransportEngine,
182 interfaces: &[&dyn InterfaceStatusView],
183 started: f64,
184 probe_responder_hash: Option<[u8; 16]>,
185) -> Option<Vec<u8>> {
186 let (include_lstats, include_profiling) = match msgpack::unpack_exact(data) {
188 Ok(Value::Array(arr)) if !arr.is_empty() => (
189 arr[0].as_bool().unwrap_or(false),
190 arr.get(1).and_then(Value::as_bool).unwrap_or(false),
191 ),
192 _ => (false, false),
193 };
194
195 let mut iface_list = Vec::new();
197 let mut total_rxb: u64 = 0;
198 let mut total_txb: u64 = 0;
199 let mut total_rxpps = 0.0;
200 let mut total_txpps = 0.0;
201 let mut total_arxb: u64 = 0;
202 let mut total_atxb: u64 = 0;
203 let mut total_arxc: u64 = 0;
204 let mut total_atxc: u64 = 0;
205 let mut total_prxb: u64 = 0;
206 let mut total_ptxb: u64 = 0;
207 let mut total_prxc: u64 = 0;
208 let mut total_ptxc: u64 = 0;
209 let mut total_rxs = 0.0;
210 let mut total_txs = 0.0;
211 let mut total_arxs = 0.0;
212 let mut total_atxs = 0.0;
213 let mut total_prxs = 0.0;
214 let mut total_ptxs = 0.0;
215 let mut total_arxf = 0.0;
216 let mut total_atxf = 0.0;
217 let mut total_prxf = 0.0;
218 let mut total_ptxf = 0.0;
219
220 for entry in interfaces {
221 let id = entry.id();
222 let info = entry.info();
223 let stats = entry.stats();
224
225 if !info.is_local_client {
226 total_rxb += stats.rxb;
227 total_txb += stats.txb;
228 total_rxpps += stats.traffic_rates.rxpps;
229 total_txpps += stats.traffic_rates.txpps;
230 total_arxb += stats.arxb;
231 total_atxb += stats.atxb;
232 total_arxc += stats.arxc;
233 total_atxc += stats.atxc;
234 total_prxb += stats.prxb;
235 total_ptxb += stats.ptxb;
236 total_prxc += stats.prxc;
237 total_ptxc += stats.ptxc;
238 total_rxs += stats.traffic_rates.rxs;
239 total_txs += stats.traffic_rates.txs;
240 total_arxs += stats.traffic_rates.arxs;
241 total_atxs += stats.traffic_rates.atxs;
242 total_prxs += stats.traffic_rates.prxs;
243 total_ptxs += stats.traffic_rates.ptxs;
244 total_arxf += stats.incoming_announce_freq();
245 total_atxf += stats.outgoing_announce_freq();
246 total_prxf += stats.incoming_path_request_freq();
247 total_ptxf += stats.outgoing_path_request_freq();
248 }
249
250 let mut ifstats: Vec<(&str, Value)> = vec![
251 ("name", Value::Str(info.name.clone())),
252 ("short_name", Value::Str(info.name.clone())),
253 ("status", Value::Bool(entry.online())),
254 ("mode", Value::UInt(info.mode as u64)),
255 ("rxb", Value::UInt(stats.rxb)),
256 ("txb", Value::UInt(stats.txb)),
257 ("txdrp", Value::UInt(entry.tx_drops())),
258 ("txdrb", Value::UInt(entry.tx_dropped_bytes())),
259 ("txstalled", Value::Bool(entry.tx_stalled())),
260 ("txbuffered", Value::UInt(entry.tx_buffered() as u64)),
261 ("rxs", Value::Float(stats.traffic_rates.rxs)),
262 ("txs", Value::Float(stats.traffic_rates.txs)),
263 ("arxb", Value::UInt(stats.arxb)),
264 ("atxb", Value::UInt(stats.atxb)),
265 ("arxc", Value::UInt(stats.arxc)),
266 ("atxc", Value::UInt(stats.atxc)),
267 ("arxs", Value::Float(stats.traffic_rates.arxs)),
268 ("atxs", Value::Float(stats.traffic_rates.atxs)),
269 ("prxb", Value::UInt(stats.prxb)),
270 ("ptxb", Value::UInt(stats.ptxb)),
271 ("prxc", Value::UInt(stats.prxc)),
272 ("ptxc", Value::UInt(stats.ptxc)),
273 ("prxs", Value::Float(stats.traffic_rates.prxs)),
274 ("ptxs", Value::Float(stats.traffic_rates.ptxs)),
275 (
276 "protocol_violations",
277 Value::UInt(stats.protocol_violations),
278 ),
279 ("ifac_violations", Value::UInt(stats.ifac_violations)),
280 ("packet_filter_hits", Value::UInt(stats.packet_filter_hits)),
281 ];
282 if let Some(br) = info.bitrate {
283 ifstats.push(("bitrate", Value::UInt(br)));
284 } else {
285 ifstats.push(("bitrate", Value::Nil));
286 }
287 ifstats.push((
288 "incoming_announce_freq",
289 Value::Float(stats.incoming_announce_freq()),
290 ));
291 ifstats.push((
292 "outgoing_announce_freq",
293 Value::Float(stats.outgoing_announce_freq()),
294 ));
295 ifstats.push((
296 "held_announces",
297 Value::UInt(engine.held_announce_count(&id) as u64),
298 ));
299
300 ifstats.push(("ifac_signature", Value::Nil));
302 ifstats.push((
303 "ifac_size",
304 if info.bitrate.is_some() {
305 Value::UInt(0)
306 } else {
307 Value::Nil
308 },
309 ));
310 ifstats.push(("ifac_netname", Value::Nil));
311
312 ifstats.push(("clients", Value::Nil));
314 ifstats.push(("announce_queue", Value::Nil));
315
316 let map = ifstats
318 .into_iter()
319 .map(|(k, v)| (Value::Str(k.into()), v))
320 .collect();
321 iface_list.push(Value::Map(map));
322 }
323
324 let mut stats: Vec<(&str, Value)> = vec![
326 ("interfaces", Value::Array(iface_list)),
327 ("rxb", Value::UInt(total_rxb)),
328 ("txb", Value::UInt(total_txb)),
329 ("rxpps", Value::Float(total_rxpps)),
330 ("txpps", Value::Float(total_txpps)),
331 ("rxs", Value::Float(total_rxs)),
332 ("txs", Value::Float(total_txs)),
333 ("arxb", Value::UInt(total_arxb)),
334 ("atxb", Value::UInt(total_atxb)),
335 ("arxc", Value::UInt(total_arxc)),
336 ("atxc", Value::UInt(total_atxc)),
337 ("arxs", Value::Float(total_arxs)),
338 ("atxs", Value::Float(total_atxs)),
339 ("prxb", Value::UInt(total_prxb)),
340 ("ptxb", Value::UInt(total_ptxb)),
341 ("prxc", Value::UInt(total_prxc)),
342 ("ptxc", Value::UInt(total_ptxc)),
343 ("prxs", Value::Float(total_prxs)),
344 ("ptxs", Value::Float(total_ptxs)),
345 ("arxf", Value::Float(total_arxf)),
346 ("atxf", Value::Float(total_atxf)),
347 ("prxf", Value::Float(total_prxf)),
348 ("ptxf", Value::Float(total_ptxf)),
349 ];
350
351 if let Some(identity_hash) = engine.config().identity_hash {
352 stats.push(("transport_id", Value::Bin(identity_hash.to_vec())));
353 stats.push(("transport_uptime", Value::Float(time::now() - started)));
354 }
355 stats.push((
356 "probe_responder",
357 match probe_responder_hash {
358 Some(hash) => Value::Bin(hash.to_vec()),
359 None => Value::Nil,
360 },
361 ));
362 stats.push(("rss", Value::Nil));
363
364 let stats_map = stats
365 .into_iter()
366 .map(|(k, v)| (Value::Str(k.into()), v))
367 .collect();
368
369 let mut response = vec![Value::Map(stats_map)];
371 if include_lstats {
372 let link_count = engine.link_table_count();
373 response.push(Value::UInt(link_count as u64));
374 }
375 if include_profiling {
376 response.push(crate::profiling::results_msgpack());
377 }
378
379 Some(msgpack::pack(&Value::Array(response)))
380}
381
382pub fn handle_path_request(data: &[u8], engine: &TransportEngine) -> Option<Vec<u8>> {
388 let arr = match msgpack::unpack_exact(data) {
389 Ok(Value::Array(arr)) if !arr.is_empty() => arr,
390 _ => return None,
391 };
392
393 let command = match &arr[0] {
394 Value::Str(s) => s.as_str(),
395 _ => return None,
396 };
397
398 let dest_filter: Option<[u8; 16]> = if arr.len() > 1 {
399 match &arr[1] {
400 Value::Bin(b) if b.len() == 16 => {
401 let mut h = [0u8; 16];
402 h.copy_from_slice(b);
403 Some(h)
404 }
405 _ => None,
406 }
407 } else {
408 None
409 };
410
411 let max_hops: Option<u8> = if arr.len() > 2 {
412 arr[2].as_uint().map(|v| v as u8)
413 } else {
414 None
415 };
416
417 match command {
418 "table" => {
419 let paths = engine.get_path_table(max_hops);
420 let mut entries = Vec::new();
421 for p in &paths {
422 if let Some(ref filter) = dest_filter {
423 if p.0 != *filter {
424 continue;
425 }
426 }
427 let entry = vec![
429 (Value::Str("hash".into()), Value::Bin(p.0.to_vec())),
430 (Value::Str("timestamp".into()), Value::Float(p.1)),
431 (Value::Str("via".into()), Value::Bin(p.2.to_vec())),
432 (Value::Str("hops".into()), Value::UInt(p.3 as u64)),
433 (Value::Str("expires".into()), Value::Float(p.4)),
434 (Value::Str("interface".into()), Value::Str(p.5.clone())),
435 ];
436 entries.push(Value::Map(entry));
437 }
438 Some(msgpack::pack(&Value::Array(entries)))
439 }
440 "rates" => {
441 let rates = engine.get_rate_table();
442 let mut entries = Vec::new();
443 for r in &rates {
444 if let Some(ref filter) = dest_filter {
445 if r.0 != *filter {
446 continue;
447 }
448 }
449 let timestamps: Vec<Value> = r.4.iter().map(|t| Value::Float(*t)).collect();
451 let entry = vec![
452 (Value::Str("hash".into()), Value::Bin(r.0.to_vec())),
453 (Value::Str("last".into()), Value::Float(r.1)),
454 (
455 Value::Str("rate_violations".into()),
456 Value::UInt(r.2 as u64),
457 ),
458 (Value::Str("blocked_until".into()), Value::Float(r.3)),
459 (Value::Str("timestamps".into()), Value::Array(timestamps)),
460 ];
461 entries.push(Value::Map(entry));
462 }
463 Some(msgpack::pack(&Value::Array(entries)))
464 }
465 _ => None,
466 }
467}
468
469pub fn handle_blackhole_list_request(engine: &TransportEngine) -> Option<Vec<u8>> {
473 let blackholed = engine.get_blackholed();
474 let mut map_entries = Vec::new();
475 for (hash, created, expires, reason) in &blackholed {
476 let mut entry = vec![
477 (Value::Str("created".into()), Value::Float(*created)),
478 (Value::Str("expires".into()), Value::Float(*expires)),
479 ];
480 if let Some(r) = reason {
481 entry.push((Value::Str("reason".into()), Value::Str(r.clone())));
482 }
483 map_entries.push((Value::Bin(hash.to_vec()), Value::Map(entry)));
484 }
485 Some(msgpack::pack(&Value::Map(map_entries)))
486}
487
488pub fn build_management_announce(
492 identity: &rns_crypto::identity::Identity,
493 rng: &mut dyn rns_crypto::Rng,
494 now: f64,
495 path_response: bool,
496) -> Option<Vec<u8>> {
497 let identity_hash = *identity.hash();
498 let dest_hash = management_dest_hash(&identity_hash);
499 let name_hash = rns_core::destination::name_hash("rnstransport", &["remote", "management"]);
500 let random_hash = announce_random_blob(rng, now);
501
502 let (announce_data, _has_ratchet) = rns_core::announce::AnnounceData::pack(
503 identity,
504 &dest_hash,
505 &name_hash,
506 &random_hash,
507 None, None, )
510 .ok()?;
511
512 let flags = rns_core::packet::PacketFlags {
513 header_type: constants::HEADER_1,
514 context_flag: constants::FLAG_UNSET,
515 transport_type: constants::TRANSPORT_BROADCAST,
516 destination_type: constants::DESTINATION_SINGLE,
517 packet_type: constants::PACKET_TYPE_ANNOUNCE,
518 };
519
520 let packet = rns_core::packet::RawPacket::pack(
521 flags,
522 0,
523 &dest_hash,
524 None,
525 announce_context(path_response),
526 &announce_data,
527 )
528 .ok()?;
529
530 Some(packet.raw)
531}
532
533pub fn build_blackhole_announce(
537 identity: &rns_crypto::identity::Identity,
538 rng: &mut dyn rns_crypto::Rng,
539 now: f64,
540 path_response: bool,
541) -> Option<Vec<u8>> {
542 let identity_hash = *identity.hash();
543 let dest_hash = blackhole_dest_hash(&identity_hash);
544 let name_hash = rns_core::destination::name_hash("rnstransport", &["info", "blackhole"]);
545 let random_hash = announce_random_blob(rng, now);
546
547 let (announce_data, _has_ratchet) = rns_core::announce::AnnounceData::pack(
548 identity,
549 &dest_hash,
550 &name_hash,
551 &random_hash,
552 None,
553 None,
554 )
555 .ok()?;
556
557 let flags = rns_core::packet::PacketFlags {
558 header_type: constants::HEADER_1,
559 context_flag: constants::FLAG_UNSET,
560 transport_type: constants::TRANSPORT_BROADCAST,
561 destination_type: constants::DESTINATION_SINGLE,
562 packet_type: constants::PACKET_TYPE_ANNOUNCE,
563 };
564
565 let packet = rns_core::packet::RawPacket::pack(
566 flags,
567 0,
568 &dest_hash,
569 None,
570 announce_context(path_response),
571 &announce_data,
572 )
573 .ok()?;
574
575 Some(packet.raw)
576}