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 build_probe_announce(
95 identity: &rns_crypto::identity::Identity,
96 rng: &mut dyn rns_crypto::Rng,
97) -> Option<Vec<u8>> {
98 let identity_hash = *identity.hash();
99 let dest_hash = probe_dest_hash(&identity_hash);
100 let name_hash = rns_core::destination::name_hash("rnstransport", &["probe"]);
101 let mut random_hash = [0u8; 10];
102 rng.fill_bytes(&mut random_hash);
103
104 let (announce_data, _has_ratchet) = rns_core::announce::AnnounceData::pack(
105 identity,
106 &dest_hash,
107 &name_hash,
108 &random_hash,
109 None,
110 None,
111 )
112 .ok()?;
113
114 let flags = rns_core::packet::PacketFlags {
115 header_type: constants::HEADER_1,
116 context_flag: constants::FLAG_UNSET,
117 transport_type: constants::TRANSPORT_BROADCAST,
118 destination_type: constants::DESTINATION_SINGLE,
119 packet_type: constants::PACKET_TYPE_ANNOUNCE,
120 };
121
122 let packet = rns_core::packet::RawPacket::pack(
123 flags,
124 0,
125 &dest_hash,
126 None,
127 constants::CONTEXT_NONE,
128 &announce_data,
129 )
130 .ok()?;
131
132 Some(packet.raw)
133}
134
135#[derive(Debug, Clone, Default)]
137pub struct ManagementConfig {
138 pub enable_remote_management: bool,
140 pub remote_management_allowed: Vec<[u8; 16]>,
142 pub publish_blackhole: bool,
144}
145
146pub fn handle_status_request(
152 data: &[u8],
153 engine: &TransportEngine,
154 interfaces: &[&dyn InterfaceStatusView],
155 started: f64,
156 probe_responder_hash: Option<[u8; 16]>,
157) -> Option<Vec<u8>> {
158 let (include_lstats, include_profiling) = match msgpack::unpack_exact(data) {
160 Ok(Value::Array(arr)) if !arr.is_empty() => (
161 arr[0].as_bool().unwrap_or(false),
162 arr.get(1).and_then(Value::as_bool).unwrap_or(false),
163 ),
164 _ => (false, false),
165 };
166
167 let mut iface_list = Vec::new();
169 let mut total_rxb: u64 = 0;
170 let mut total_txb: u64 = 0;
171 let mut total_rxpps = 0.0;
172 let mut total_txpps = 0.0;
173 let mut total_arxb: u64 = 0;
174 let mut total_atxb: u64 = 0;
175 let mut total_arxc: u64 = 0;
176 let mut total_atxc: u64 = 0;
177 let mut total_prxb: u64 = 0;
178 let mut total_ptxb: u64 = 0;
179 let mut total_prxc: u64 = 0;
180 let mut total_ptxc: u64 = 0;
181 let mut total_rxs = 0.0;
182 let mut total_txs = 0.0;
183 let mut total_arxs = 0.0;
184 let mut total_atxs = 0.0;
185 let mut total_prxs = 0.0;
186 let mut total_ptxs = 0.0;
187 let mut total_arxf = 0.0;
188 let mut total_atxf = 0.0;
189 let mut total_prxf = 0.0;
190 let mut total_ptxf = 0.0;
191
192 for entry in interfaces {
193 let id = entry.id();
194 let info = entry.info();
195 let stats = entry.stats();
196
197 if !info.is_local_client {
198 total_rxb += stats.rxb;
199 total_txb += stats.txb;
200 total_rxpps += stats.traffic_rates.rxpps;
201 total_txpps += stats.traffic_rates.txpps;
202 total_arxb += stats.arxb;
203 total_atxb += stats.atxb;
204 total_arxc += stats.arxc;
205 total_atxc += stats.atxc;
206 total_prxb += stats.prxb;
207 total_ptxb += stats.ptxb;
208 total_prxc += stats.prxc;
209 total_ptxc += stats.ptxc;
210 total_rxs += stats.traffic_rates.rxs;
211 total_txs += stats.traffic_rates.txs;
212 total_arxs += stats.traffic_rates.arxs;
213 total_atxs += stats.traffic_rates.atxs;
214 total_prxs += stats.traffic_rates.prxs;
215 total_ptxs += stats.traffic_rates.ptxs;
216 total_arxf += stats.incoming_announce_freq();
217 total_atxf += stats.outgoing_announce_freq();
218 total_prxf += stats.incoming_path_request_freq();
219 total_ptxf += stats.outgoing_path_request_freq();
220 }
221
222 let mut ifstats: Vec<(&str, Value)> = vec![
223 ("name", Value::Str(info.name.clone())),
224 ("short_name", Value::Str(info.name.clone())),
225 ("status", Value::Bool(entry.online())),
226 ("mode", Value::UInt(info.mode as u64)),
227 ("rxb", Value::UInt(stats.rxb)),
228 ("txb", Value::UInt(stats.txb)),
229 ("txdrp", Value::UInt(entry.tx_drops())),
230 ("txdrb", Value::UInt(entry.tx_dropped_bytes())),
231 ("txstalled", Value::Bool(entry.tx_stalled())),
232 ("txbuffered", Value::UInt(entry.tx_buffered() as u64)),
233 ("rxs", Value::Float(stats.traffic_rates.rxs)),
234 ("txs", Value::Float(stats.traffic_rates.txs)),
235 ("arxb", Value::UInt(stats.arxb)),
236 ("atxb", Value::UInt(stats.atxb)),
237 ("arxc", Value::UInt(stats.arxc)),
238 ("atxc", Value::UInt(stats.atxc)),
239 ("arxs", Value::Float(stats.traffic_rates.arxs)),
240 ("atxs", Value::Float(stats.traffic_rates.atxs)),
241 ("prxb", Value::UInt(stats.prxb)),
242 ("ptxb", Value::UInt(stats.ptxb)),
243 ("prxc", Value::UInt(stats.prxc)),
244 ("ptxc", Value::UInt(stats.ptxc)),
245 ("prxs", Value::Float(stats.traffic_rates.prxs)),
246 ("ptxs", Value::Float(stats.traffic_rates.ptxs)),
247 (
248 "protocol_violations",
249 Value::UInt(stats.protocol_violations),
250 ),
251 ("ifac_violations", Value::UInt(stats.ifac_violations)),
252 ("packet_filter_hits", Value::UInt(stats.packet_filter_hits)),
253 ];
254 if let Some(br) = info.bitrate {
255 ifstats.push(("bitrate", Value::UInt(br)));
256 } else {
257 ifstats.push(("bitrate", Value::Nil));
258 }
259 ifstats.push((
260 "incoming_announce_freq",
261 Value::Float(stats.incoming_announce_freq()),
262 ));
263 ifstats.push((
264 "outgoing_announce_freq",
265 Value::Float(stats.outgoing_announce_freq()),
266 ));
267 ifstats.push((
268 "held_announces",
269 Value::UInt(engine.held_announce_count(&id) as u64),
270 ));
271
272 ifstats.push(("ifac_signature", Value::Nil));
274 ifstats.push((
275 "ifac_size",
276 if info.bitrate.is_some() {
277 Value::UInt(0)
278 } else {
279 Value::Nil
280 },
281 ));
282 ifstats.push(("ifac_netname", Value::Nil));
283
284 ifstats.push(("clients", Value::Nil));
286 ifstats.push(("announce_queue", Value::Nil));
287
288 let map = ifstats
290 .into_iter()
291 .map(|(k, v)| (Value::Str(k.into()), v))
292 .collect();
293 iface_list.push(Value::Map(map));
294 }
295
296 let mut stats: Vec<(&str, Value)> = vec![
298 ("interfaces", Value::Array(iface_list)),
299 ("rxb", Value::UInt(total_rxb)),
300 ("txb", Value::UInt(total_txb)),
301 ("rxpps", Value::Float(total_rxpps)),
302 ("txpps", Value::Float(total_txpps)),
303 ("rxs", Value::Float(total_rxs)),
304 ("txs", Value::Float(total_txs)),
305 ("arxb", Value::UInt(total_arxb)),
306 ("atxb", Value::UInt(total_atxb)),
307 ("arxc", Value::UInt(total_arxc)),
308 ("atxc", Value::UInt(total_atxc)),
309 ("arxs", Value::Float(total_arxs)),
310 ("atxs", Value::Float(total_atxs)),
311 ("prxb", Value::UInt(total_prxb)),
312 ("ptxb", Value::UInt(total_ptxb)),
313 ("prxc", Value::UInt(total_prxc)),
314 ("ptxc", Value::UInt(total_ptxc)),
315 ("prxs", Value::Float(total_prxs)),
316 ("ptxs", Value::Float(total_ptxs)),
317 ("arxf", Value::Float(total_arxf)),
318 ("atxf", Value::Float(total_atxf)),
319 ("prxf", Value::Float(total_prxf)),
320 ("ptxf", Value::Float(total_ptxf)),
321 ];
322
323 if let Some(identity_hash) = engine.config().identity_hash {
324 stats.push(("transport_id", Value::Bin(identity_hash.to_vec())));
325 stats.push(("transport_uptime", Value::Float(time::now() - started)));
326 }
327 stats.push((
328 "probe_responder",
329 match probe_responder_hash {
330 Some(hash) => Value::Bin(hash.to_vec()),
331 None => Value::Nil,
332 },
333 ));
334 stats.push(("rss", Value::Nil));
335
336 let stats_map = stats
337 .into_iter()
338 .map(|(k, v)| (Value::Str(k.into()), v))
339 .collect();
340
341 let mut response = vec![Value::Map(stats_map)];
343 if include_lstats {
344 let link_count = engine.link_table_count();
345 response.push(Value::UInt(link_count as u64));
346 }
347 if include_profiling {
348 response.push(crate::profiling::results_msgpack());
349 }
350
351 Some(msgpack::pack(&Value::Array(response)))
352}
353
354pub fn handle_path_request(data: &[u8], engine: &TransportEngine) -> Option<Vec<u8>> {
360 let arr = match msgpack::unpack_exact(data) {
361 Ok(Value::Array(arr)) if !arr.is_empty() => arr,
362 _ => return None,
363 };
364
365 let command = match &arr[0] {
366 Value::Str(s) => s.as_str(),
367 _ => return None,
368 };
369
370 let dest_filter: Option<[u8; 16]> = if arr.len() > 1 {
371 match &arr[1] {
372 Value::Bin(b) if b.len() == 16 => {
373 let mut h = [0u8; 16];
374 h.copy_from_slice(b);
375 Some(h)
376 }
377 _ => None,
378 }
379 } else {
380 None
381 };
382
383 let max_hops: Option<u8> = if arr.len() > 2 {
384 arr[2].as_uint().map(|v| v as u8)
385 } else {
386 None
387 };
388
389 match command {
390 "table" => {
391 let paths = engine.get_path_table(max_hops);
392 let mut entries = Vec::new();
393 for p in &paths {
394 if let Some(ref filter) = dest_filter {
395 if p.0 != *filter {
396 continue;
397 }
398 }
399 let entry = vec![
401 (Value::Str("hash".into()), Value::Bin(p.0.to_vec())),
402 (Value::Str("timestamp".into()), Value::Float(p.1)),
403 (Value::Str("via".into()), Value::Bin(p.2.to_vec())),
404 (Value::Str("hops".into()), Value::UInt(p.3 as u64)),
405 (Value::Str("expires".into()), Value::Float(p.4)),
406 (Value::Str("interface".into()), Value::Str(p.5.clone())),
407 ];
408 entries.push(Value::Map(entry));
409 }
410 Some(msgpack::pack(&Value::Array(entries)))
411 }
412 "rates" => {
413 let rates = engine.get_rate_table();
414 let mut entries = Vec::new();
415 for r in &rates {
416 if let Some(ref filter) = dest_filter {
417 if r.0 != *filter {
418 continue;
419 }
420 }
421 let timestamps: Vec<Value> = r.4.iter().map(|t| Value::Float(*t)).collect();
423 let entry = vec![
424 (Value::Str("hash".into()), Value::Bin(r.0.to_vec())),
425 (Value::Str("last".into()), Value::Float(r.1)),
426 (
427 Value::Str("rate_violations".into()),
428 Value::UInt(r.2 as u64),
429 ),
430 (Value::Str("blocked_until".into()), Value::Float(r.3)),
431 (Value::Str("timestamps".into()), Value::Array(timestamps)),
432 ];
433 entries.push(Value::Map(entry));
434 }
435 Some(msgpack::pack(&Value::Array(entries)))
436 }
437 _ => None,
438 }
439}
440
441pub fn handle_blackhole_list_request(engine: &TransportEngine) -> Option<Vec<u8>> {
445 let blackholed = engine.get_blackholed();
446 let mut map_entries = Vec::new();
447 for (hash, created, expires, reason) in &blackholed {
448 let mut entry = vec![
449 (Value::Str("created".into()), Value::Float(*created)),
450 (Value::Str("expires".into()), Value::Float(*expires)),
451 ];
452 if let Some(r) = reason {
453 entry.push((Value::Str("reason".into()), Value::Str(r.clone())));
454 }
455 map_entries.push((Value::Bin(hash.to_vec()), Value::Map(entry)));
456 }
457 Some(msgpack::pack(&Value::Map(map_entries)))
458}
459
460pub fn build_management_announce(
464 identity: &rns_crypto::identity::Identity,
465 rng: &mut dyn rns_crypto::Rng,
466) -> Option<Vec<u8>> {
467 let identity_hash = *identity.hash();
468 let dest_hash = management_dest_hash(&identity_hash);
469 let name_hash = rns_core::destination::name_hash("rnstransport", &["remote", "management"]);
470 let mut random_hash = [0u8; 10];
471 rng.fill_bytes(&mut random_hash);
472
473 let (announce_data, _has_ratchet) = rns_core::announce::AnnounceData::pack(
474 identity,
475 &dest_hash,
476 &name_hash,
477 &random_hash,
478 None, None, )
481 .ok()?;
482
483 let flags = rns_core::packet::PacketFlags {
484 header_type: constants::HEADER_1,
485 context_flag: constants::FLAG_UNSET,
486 transport_type: constants::TRANSPORT_BROADCAST,
487 destination_type: constants::DESTINATION_SINGLE,
488 packet_type: constants::PACKET_TYPE_ANNOUNCE,
489 };
490
491 let packet = rns_core::packet::RawPacket::pack(
492 flags,
493 0,
494 &dest_hash,
495 None,
496 constants::CONTEXT_NONE,
497 &announce_data,
498 )
499 .ok()?;
500
501 Some(packet.raw)
502}
503
504pub fn build_blackhole_announce(
508 identity: &rns_crypto::identity::Identity,
509 rng: &mut dyn rns_crypto::Rng,
510) -> Option<Vec<u8>> {
511 let identity_hash = *identity.hash();
512 let dest_hash = blackhole_dest_hash(&identity_hash);
513 let name_hash = rns_core::destination::name_hash("rnstransport", &["info", "blackhole"]);
514 let mut random_hash = [0u8; 10];
515 rng.fill_bytes(&mut random_hash);
516
517 let (announce_data, _has_ratchet) = rns_core::announce::AnnounceData::pack(
518 identity,
519 &dest_hash,
520 &name_hash,
521 &random_hash,
522 None,
523 None,
524 )
525 .ok()?;
526
527 let flags = rns_core::packet::PacketFlags {
528 header_type: constants::HEADER_1,
529 context_flag: constants::FLAG_UNSET,
530 transport_type: constants::TRANSPORT_BROADCAST,
531 destination_type: constants::DESTINATION_SINGLE,
532 packet_type: constants::PACKET_TYPE_ANNOUNCE,
533 };
534
535 let packet = rns_core::packet::RawPacket::pack(
536 flags,
537 0,
538 &dest_hash,
539 None,
540 constants::CONTEXT_NONE,
541 &announce_data,
542 )
543 .ok()?;
544
545 Some(packet.raw)
546}