scratch repo for intrusive networking

Compare changes

Choose any two refs to compare.

Changed files
+749
crates
ergot
src
interface_manager
profiles
bridge_router
+745
crates/ergot/src/interface_manager/profiles/bridge_router/mod.rs
···
··· 1 + //! The Direct Router profile 2 + //! 3 + //! This is an early and simple router profile that can manage multiple directly connected 4 + //! edge devices. It can route messages from one directly connected edge device to another, 5 + //! as well as messages to/from itself and an edge device. It does not currently handle 6 + //! multi-hop routing. 7 + 8 + use core::time::Duration; 9 + use std::{ 10 + collections::{BTreeMap, HashMap}, 11 + time::Instant, 12 + }; 13 + 14 + use log::{debug, info, trace, warn}; 15 + use rand::Rng; 16 + 17 + use crate::{ 18 + Header, ProtocolError, 19 + interface_manager::{ 20 + DeregisterError, Interface, InterfaceSendError, InterfaceState, Profile, 21 + SeedAssignmentError, SeedNetAssignment, SeedRefreshError, SetStateError, 22 + profiles::direct_edge::CENTRAL_NODE_ID, 23 + }, 24 + net_stack::NetStackHandle, 25 + wire_frames::de_frame, 26 + }; 27 + 28 + use super::direct_edge::EDGE_NODE_ID; 29 + 30 + // pub mod tokio_tcp; 31 + 32 + // #[cfg(feature = "nusb-v0_1")] 33 + // pub mod nusb_0_1; 34 + 35 + // #[cfg(feature = "tokio-serial-v5")] 36 + // pub mod tokio_serial_5; 37 + 38 + struct Node<I: Interface> { 39 + // TODO: can we JUST use an interface here, NOT a profile? 40 + edge: edge_interface_plus::EdgeInterfacePlus<I>, 41 + net_id: u16, 42 + ident: u64, 43 + } 44 + 45 + /// The "kind" of route, currently only "directly connected and assigned by us", 46 + /// coming soon: remotely assigned net ids 47 + #[derive(Clone)] 48 + enum RouteKind { 49 + /// This route is associated with a node we are DIRECTLY connected to 50 + DirectAssigned, 51 + /// This route was assigned by us as a seed router 52 + SeedAssigned { 53 + source_net_id: u16, 54 + expiration_time: Instant, 55 + refresh_token: u64, 56 + }, 57 + /// This route is inactive 58 + Tombstone { clear_time: Instant }, 59 + } 60 + 61 + /// Route information 62 + #[derive(Clone)] 63 + struct Route { 64 + /// The interface identifier for this route 65 + ident: u64, 66 + /// The kind of this route 67 + kind: RouteKind, 68 + } 69 + 70 + pub struct DirectRouter<I: Interface> { 71 + /// Monotonic interface counter 72 + interface_ctr: u64, 73 + /// Map of (Network ID => Route), where Route contains the interface ident 74 + routes: BTreeMap<u16, Route>, 75 + /// Map of (Interface Ident => Ident) 76 + direct_links: HashMap<u64, Node<I>>, 77 + } 78 + 79 + /// The timeout duration in seconds for an initial net_id assignment as a seed router 80 + const INITIAL_SEED_ASSIGN_TIMEOUT: u16 = 30; 81 + /// The max timeout duration in seconds for a net_id assignment 82 + const MAX_SEED_ASSIGN_TIMEOUT: u16 = 120; 83 + /// There must be LESS than this many seconds left until expiration to allow for a refresh 84 + const MIN_SEED_REFRESH: u16 = 62; 85 + 86 + impl<I: Interface> Profile for DirectRouter<I> { 87 + type InterfaceIdent = u64; 88 + 89 + fn send<T: serde::Serialize>( 90 + &mut self, 91 + hdr: &crate::Header, 92 + data: &T, 93 + ) -> Result<(), InterfaceSendError> { 94 + let mut hdr = hdr.clone(); 95 + // Make sure we still have ttl juice 96 + if hdr.decrement_ttl().is_err() { 97 + return Err(InterfaceSendError::NoRouteToDest); 98 + } 99 + 100 + if hdr.dst.port_id == 255 { 101 + if hdr.any_all.is_none() { 102 + return Err(InterfaceSendError::AnyPortMissingKey); 103 + } 104 + 105 + let mut any_good = false; 106 + for (_ident, p) in self.direct_links.iter_mut() { 107 + // Don't send back to the origin 108 + if hdr.dst.network_id == p.net_id { 109 + continue; 110 + } 111 + let mut hdr = hdr.clone(); 112 + hdr.dst.network_id = p.net_id; 113 + hdr.dst.node_id = EDGE_NODE_ID; 114 + any_good |= p.edge.send(&hdr, data).is_ok(); 115 + } 116 + if any_good { 117 + Ok(()) 118 + } else { 119 + Err(InterfaceSendError::NoRouteToDest) 120 + } 121 + } else { 122 + let intfc = self.find(&hdr, None)?; 123 + intfc.send(&hdr, data) 124 + } 125 + } 126 + 127 + fn send_err( 128 + &mut self, 129 + hdr: &crate::Header, 130 + err: ProtocolError, 131 + source: Option<Self::InterfaceIdent>, 132 + ) -> Result<(), InterfaceSendError> { 133 + let mut hdr = hdr.clone(); 134 + // Make sure we still have ttl juice 135 + if hdr.decrement_ttl().is_err() { 136 + return Err(InterfaceSendError::NoRouteToDest); 137 + } 138 + 139 + let intfc = self.find(&hdr, source)?; 140 + intfc.send_err(&hdr, err) 141 + } 142 + 143 + fn send_raw( 144 + &mut self, 145 + hdr: &crate::HeaderSeq, 146 + data: &[u8], 147 + source: Self::InterfaceIdent, 148 + ) -> Result<(), InterfaceSendError> { 149 + let mut hdr = hdr.clone(); 150 + // Make sure we still have ttl juice 151 + if hdr.decrement_ttl().is_err() { 152 + return Err(InterfaceSendError::NoRouteToDest); 153 + } 154 + 155 + if hdr.dst.port_id == 255 { 156 + if hdr.any_all.is_none() { 157 + return Err(InterfaceSendError::AnyPortMissingKey); 158 + } 159 + let mut any_good = false; 160 + for (_ident, p) in self.direct_links.iter_mut() { 161 + // Don't send back to the origin 162 + if source == p.ident { 163 + continue; 164 + } 165 + 166 + // For broadcast messages, rewrite the destination address 167 + // to the address of the next hop. 168 + hdr.dst.network_id = p.net_id; 169 + hdr.dst.node_id = EDGE_NODE_ID; 170 + any_good |= p.edge.send_raw(&hdr, data).is_ok(); 171 + } 172 + if any_good { 173 + Ok(()) 174 + } else { 175 + Err(InterfaceSendError::NoRouteToDest) 176 + } 177 + } else { 178 + let nshdr = hdr.clone().into(); 179 + let intfc = self.find(&nshdr, Some(source))?; 180 + intfc.send_raw(&hdr, data) 181 + } 182 + } 183 + 184 + fn interface_state(&mut self, ident: Self::InterfaceIdent) -> Option<InterfaceState> { 185 + let node = self.direct_links.get_mut(&ident)?; 186 + node.edge.interface_state(()) 187 + } 188 + 189 + fn set_interface_state( 190 + &mut self, 191 + ident: Self::InterfaceIdent, 192 + state: InterfaceState, 193 + ) -> Result<(), SetStateError> { 194 + let Some(node) = self.direct_links.get_mut(&ident) else { 195 + return Err(SetStateError::InterfaceNotFound); 196 + }; 197 + node.edge.set_interface_state((), state) 198 + } 199 + 200 + fn request_seed_net_assign( 201 + &mut self, 202 + source_net: u16, 203 + ) -> Result<SeedNetAssignment, SeedAssignmentError> { 204 + // Get the route for the source net 205 + let Some(rte) = self.routes.get(&source_net) else { 206 + return Err(SeedAssignmentError::UnknownSource); 207 + }; 208 + let rte = rte.clone(); 209 + // Get a new net id 210 + let Some(new_net_id) = self.find_free_net_id() else { 211 + return Err(SeedAssignmentError::NetIdsExhausted); 212 + }; 213 + // Pick a random refresh token 214 + let refresh_token = rand::rng().random(); 215 + 216 + // Insert this route, initially with a low refresh time, allowing the 217 + // remote device to immediately refresh, acting as an "acknowledgement" 218 + // of the assignment 219 + self.routes.insert( 220 + new_net_id, 221 + Route { 222 + ident: rte.ident, 223 + kind: RouteKind::SeedAssigned { 224 + source_net_id: source_net, 225 + expiration_time: Instant::now() 226 + + Duration::from_secs(INITIAL_SEED_ASSIGN_TIMEOUT.into()), 227 + refresh_token, 228 + }, 229 + }, 230 + ); 231 + 232 + Ok(SeedNetAssignment { 233 + net_id: new_net_id, 234 + expires_seconds: INITIAL_SEED_ASSIGN_TIMEOUT, 235 + max_refresh_seconds: MAX_SEED_ASSIGN_TIMEOUT, 236 + min_refresh_seconds: MIN_SEED_REFRESH, 237 + refresh_token: refresh_token.to_le_bytes(), 238 + }) 239 + } 240 + 241 + fn refresh_seed_net_assignment( 242 + &mut self, 243 + req_source_net: u16, 244 + req_refresh_net: u16, 245 + req_refresh_token: [u8; 8], 246 + ) -> Result<SeedNetAssignment, SeedRefreshError> { 247 + let Some(rte) = self.routes.get_mut(&req_refresh_net) else { 248 + return Err(SeedRefreshError::UnknownNetId); 249 + }; 250 + let req_refresh_token_u64 = u64::from_le_bytes(req_refresh_token); 251 + match &mut rte.kind { 252 + RouteKind::DirectAssigned => Err(SeedRefreshError::NotAssigned), 253 + RouteKind::Tombstone { clear_time: _ } => Err(SeedRefreshError::AlreadyExpired), 254 + RouteKind::SeedAssigned { 255 + source_net_id, 256 + expiration_time, 257 + refresh_token, 258 + } => { 259 + let bad_net = *source_net_id != req_source_net; 260 + let bad_tok = *refresh_token != req_refresh_token_u64; 261 + if bad_net || bad_tok { 262 + return Err(SeedRefreshError::BadRequest); 263 + } 264 + let now = Instant::now(); 265 + // Are we ALREADY expired? 266 + if *expiration_time <= now { 267 + warn!("Tombstoning net_id: {req_refresh_net}"); 268 + rte.kind = RouteKind::Tombstone { 269 + clear_time: now + Duration::from_secs(30), 270 + }; 271 + return Err(SeedRefreshError::AlreadyExpired); 272 + } 273 + // Are we TOO SOON for a refresh? 274 + // Note: we already checked if the expiration time is in the past 275 + let until_expired = *expiration_time - now; 276 + if until_expired > Duration::from_secs(MIN_SEED_REFRESH.into()) { 277 + return Err(SeedRefreshError::TooSoon); 278 + } 279 + 280 + // Looks good: update the expiration time 281 + *expiration_time = now + Duration::from_secs(MAX_SEED_ASSIGN_TIMEOUT.into()); 282 + Ok(SeedNetAssignment { 283 + net_id: req_refresh_net, 284 + expires_seconds: MAX_SEED_ASSIGN_TIMEOUT, 285 + max_refresh_seconds: MAX_SEED_ASSIGN_TIMEOUT, 286 + min_refresh_seconds: MIN_SEED_REFRESH, 287 + refresh_token: req_refresh_token, 288 + }) 289 + } 290 + } 291 + } 292 + } 293 + 294 + impl<I: Interface> DirectRouter<I> { 295 + pub fn new() -> Self { 296 + Self { 297 + interface_ctr: 0, 298 + direct_links: HashMap::new(), 299 + routes: BTreeMap::new(), 300 + } 301 + } 302 + 303 + pub fn get_nets(&mut self) -> Vec<u16> { 304 + self.direct_links 305 + .iter_mut() 306 + .filter_map(|(_ident, n)| match n.edge.interface_state(())? { 307 + InterfaceState::Down => None, 308 + InterfaceState::Inactive => None, 309 + InterfaceState::ActiveLocal { .. } => None, 310 + InterfaceState::Active { net_id, node_id: _ } => Some(net_id), 311 + }) 312 + .collect() 313 + } 314 + 315 + fn find<'b>( 316 + &'b mut self, 317 + hdr: &Header, 318 + source: Option<<Self as Profile>::InterfaceIdent>, 319 + ) -> Result<&'b mut edge_interface_plus::EdgeInterfacePlus<I>, InterfaceSendError> { 320 + // todo: make this state impossible? enum of dst w/ or w/o key? 321 + if hdr.dst.port_id == 0 && hdr.any_all.is_none() { 322 + return Err(InterfaceSendError::AnyPortMissingKey); 323 + } 324 + 325 + // Find destination by net_id 326 + let Some(rte) = self.routes.get_mut(&hdr.dst.network_id) else { 327 + return Err(InterfaceSendError::NoRouteToDest); 328 + }; 329 + 330 + // Do an expiration check for the given route 331 + match rte.kind { 332 + RouteKind::DirectAssigned => {} 333 + RouteKind::SeedAssigned { 334 + expiration_time, .. 335 + } => { 336 + let now = Instant::now(); 337 + if expiration_time <= now { 338 + warn!("Tombstoning net_id: {}", hdr.dst.network_id); 339 + rte.kind = RouteKind::Tombstone { 340 + clear_time: now + Duration::from_secs(30), 341 + }; 342 + return Err(InterfaceSendError::NoRouteToDest); 343 + } 344 + } 345 + RouteKind::Tombstone { clear_time } => { 346 + let now = Instant::now(); 347 + if clear_time <= now { 348 + // times up, get gone. 349 + self.routes.remove(&hdr.dst.network_id); 350 + } 351 + return Err(InterfaceSendError::NoRouteToDest); 352 + } 353 + } 354 + 355 + // Cool, get the interface based on that ident 356 + let Some(intfc) = self.direct_links.get_mut(&rte.ident) else { 357 + // This is not cool. We have a route with no live direct link 358 + // associated with it. Remove the route, return no route. 359 + warn!( 360 + "Stale route with net_id: {}, ident: {}, removing", 361 + hdr.dst.network_id, rte.ident 362 + ); 363 + self.routes.remove(&hdr.dst.network_id); 364 + return Err(InterfaceSendError::NoRouteToDest); 365 + }; 366 + 367 + // Is this actually for us? 368 + if (hdr.dst.network_id == intfc.net_id) && (hdr.dst.node_id == CENTRAL_NODE_ID) { 369 + return Err(InterfaceSendError::DestinationLocal); 370 + } 371 + 372 + // Is this NOT for us but the source and destination are the same? 373 + // 374 + // If the dest IS one of our interfaces, but NOT for us, and we received it, 375 + // then the only thing to do would be to send it back on the same interface 376 + // it came in on. That's a routing loop: don't do that! 377 + if let Some(src) = source 378 + && intfc.ident == src 379 + { 380 + return Err(InterfaceSendError::RoutingLoop); 381 + } 382 + 383 + Ok(&mut intfc.edge) 384 + } 385 + 386 + fn find_free_net_id(&mut self) -> Option<u16> { 387 + if self.routes.is_empty() { 388 + assert!(self.direct_links.is_empty()); 389 + Some(1) 390 + } else if self.routes.len() == 65534 { 391 + warn!("Out of netids!"); 392 + None 393 + } else { 394 + let mut new_net_id = 1; 395 + 396 + let mut to_evict = None; 397 + let now = Instant::now(); 398 + for (net_id, rte) in self.routes.iter_mut() { 399 + match rte.kind { 400 + RouteKind::DirectAssigned => { 401 + // We don't need to care about timeouts for directly assigned routes 402 + } 403 + RouteKind::SeedAssigned { 404 + expiration_time, .. 405 + } => { 406 + // If a route has expired, mark it tombstoned to avoid re-using it for a bit 407 + if expiration_time <= now { 408 + warn!("Tombstoning net_id: {net_id}"); 409 + rte.kind = RouteKind::Tombstone { 410 + clear_time: now + Duration::from_secs(30), 411 + }; 412 + } 413 + } 414 + RouteKind::Tombstone { clear_time } => { 415 + // If we've cleared the tombstone time, then re-use this net id 416 + if clear_time <= now { 417 + info!("Reclaiming tombstoned net_id: {net_id}"); 418 + to_evict = Some(*net_id); 419 + break; 420 + } 421 + } 422 + } 423 + if *net_id > new_net_id { 424 + trace!("Found gap: {net_id}"); 425 + break; 426 + } 427 + debug_assert!(*net_id == new_net_id); 428 + new_net_id += 1; 429 + } 430 + if let Some(evicted) = to_evict { 431 + self.routes.remove(&evicted); 432 + Some(evicted) 433 + } else { 434 + // EITHER: We've found a gap that we can use, OR we've iterated all 435 + // interfaces, which means that we had contiguous allocations but we 436 + // have not exhausted the range. 437 + debug_assert!(new_net_id > 0 && new_net_id != u16::MAX); 438 + Some(new_net_id) 439 + } 440 + } 441 + } 442 + 443 + pub fn register_interface(&mut self, sink: I::Sink) -> Option<u64> { 444 + let net_id = self.find_free_net_id()?; 445 + 446 + let intfc_id = self.interface_ctr; 447 + self.interface_ctr += 1; 448 + 449 + self.direct_links.insert( 450 + intfc_id, 451 + Node { 452 + edge: edge_interface_plus::EdgeInterfacePlus::new_controller( 453 + sink, 454 + InterfaceState::Active { 455 + net_id, 456 + node_id: CENTRAL_NODE_ID, 457 + }, 458 + ), 459 + net_id, 460 + ident: intfc_id, 461 + }, 462 + ); 463 + self.routes.insert( 464 + net_id, 465 + Route { 466 + ident: intfc_id, 467 + kind: RouteKind::DirectAssigned, 468 + }, 469 + ); 470 + Some(intfc_id) 471 + } 472 + 473 + pub fn deregister_interface(&mut self, ident: u64) -> Result<(), DeregisterError> { 474 + let Some(node) = self.direct_links.remove(&ident) else { 475 + return Err(DeregisterError::NoSuchInterface); 476 + }; 477 + if let Some(rte) = self.routes.remove(&node.net_id) { 478 + debug!( 479 + "removing interface with net_id: {}, ident: {ident:?}", 480 + node.net_id 481 + ); 482 + assert!(matches!(rte.kind, RouteKind::DirectAssigned)); 483 + } else { 484 + unreachable!("Why doesn't this interface have a direct route?") 485 + } 486 + // Also remove any routes that rely on this interface 487 + self.routes.retain(|net_id, rte| { 488 + let keep = rte.ident != ident; 489 + if !keep { 490 + debug!("removing indirect route with net_id: {net_id}, ident: {ident:?}") 491 + } 492 + keep 493 + }); 494 + 495 + // re-insert route as a tombstone 496 + self.routes.insert( 497 + node.net_id, 498 + Route { 499 + ident, 500 + kind: RouteKind::Tombstone { 501 + clear_time: Instant::now() + Duration::from_secs(30), 502 + }, 503 + }, 504 + ); 505 + 506 + Ok(()) 507 + } 508 + } 509 + 510 + impl<I: Interface> Default for DirectRouter<I> { 511 + fn default() -> Self { 512 + Self::new() 513 + } 514 + } 515 + 516 + pub fn process_frame<N>( 517 + net_id: u16, 518 + data: &[u8], 519 + nsh: &N, 520 + ident: <<N as NetStackHandle>::Profile as Profile>::InterfaceIdent, 521 + ) where 522 + N: NetStackHandle, 523 + { 524 + // Successfully received a packet, now we need to 525 + // do something with it. 526 + if let Some(mut frame) = de_frame(data) { 527 + trace!("{} got frame from {ident:?}", frame.hdr); 528 + // If the message comes in and has a src net_id of zero, 529 + // we should rewrite it so it isn't later understood as a 530 + // local packet. 531 + if frame.hdr.src.network_id == 0 { 532 + match frame.hdr.src.node_id { 533 + 0 => { 534 + log::warn!("{}: device is sending us frames without a node id, ignoring", frame.hdr); 535 + return; 536 + } 537 + CENTRAL_NODE_ID => { 538 + log::warn!("{}: device is sending us frames as us, ignoring", frame.hdr); 539 + return; 540 + } 541 + EDGE_NODE_ID => {} 542 + _ => { 543 + log::warn!("{}: device is sending us frames with a bad node id, ignoring", frame.hdr); 544 + return; 545 + } 546 + } 547 + 548 + frame.hdr.src.network_id = net_id; 549 + } 550 + // TODO: if the destination IS self.net_id, we could rewrite the 551 + // dest net_id as zero to avoid a pass through the interface manager. 552 + // 553 + // If the dest is 0, should we rewrite the dest as self.net_id? This 554 + // is the opposite as above, but I dunno how that will work with responses 555 + let hdr = frame.hdr.clone(); 556 + let nshdr: Header = hdr.clone().into(); 557 + 558 + let res = match frame.body { 559 + Ok(body) => nsh.stack().send_raw(&hdr, body, ident), 560 + Err(e) => nsh.stack().send_err(&nshdr, e, Some(ident)), 561 + }; 562 + match res { 563 + Ok(()) => {} 564 + Err(e) => { 565 + // TODO: match on error, potentially try to send NAK? 566 + warn!("{} recv->send error: {e:?}", frame.hdr); 567 + } 568 + } 569 + } else { 570 + warn!("Decode error! Ignoring frame on net_id {}", net_id); 571 + } 572 + } 573 + 574 + mod edge_interface_plus { 575 + use log::trace; 576 + use serde::Serialize; 577 + 578 + use crate::{ 579 + Header, HeaderSeq, ProtocolError, 580 + interface_manager::{ 581 + Interface, InterfaceSendError, InterfaceSink, InterfaceState, SetStateError, 582 + profiles::direct_edge::{CENTRAL_NODE_ID, EDGE_NODE_ID}, 583 + }, 584 + }; 585 + 586 + // TODO: call this something like "point to point edge" 587 + pub struct EdgeInterfacePlus<I: Interface> { 588 + sink: I::Sink, 589 + seq_no: u16, 590 + state: InterfaceState, 591 + own_node_id: u8, 592 + other_node_id: u8, 593 + } 594 + 595 + impl<I: Interface> EdgeInterfacePlus<I> { 596 + pub const fn new_controller(sink: I::Sink, state: InterfaceState) -> Self { 597 + Self { 598 + sink, 599 + seq_no: 0, 600 + state, 601 + own_node_id: CENTRAL_NODE_ID, 602 + other_node_id: EDGE_NODE_ID, 603 + } 604 + } 605 + } 606 + 607 + impl<I: Interface> EdgeInterfacePlus<I> { 608 + fn common_send<'b>( 609 + &'b mut self, 610 + hdr: &Header, 611 + ) -> Result<(&'b mut I::Sink, HeaderSeq), InterfaceSendError> { 612 + let net_id = match &self.state { 613 + InterfaceState::Down | InterfaceState::Inactive => { 614 + return Err(InterfaceSendError::NoRouteToDest); 615 + } 616 + InterfaceState::ActiveLocal { .. } => { 617 + // TODO: maybe also handle this? 618 + return Err(InterfaceSendError::NoRouteToDest); 619 + } 620 + InterfaceState::Active { net_id, node_id: _ } => *net_id, 621 + }; 622 + 623 + trace!("{hdr} common_send"); 624 + 625 + // TODO: when this WAS a real Profile, we did a lot of these things, but 626 + // now they should be done by the router. For now, we just have asserts, 627 + // eventually we should relax this to debug_asserts? 628 + assert!(net_id != 0); 629 + let for_us = hdr.dst.network_id == net_id && hdr.dst.node_id == self.own_node_id; 630 + assert!(!for_us); 631 + 632 + let mut hdr = hdr.clone(); 633 + 634 + // If the source is local, rewrite the source using this interface's 635 + // information so responses can find their way back here 636 + if hdr.src.net_node_any() { 637 + // todo: if we know the destination is EXACTLY this network, 638 + // we could leave the network_id local to allow for shorter 639 + // addresses 640 + hdr.src.network_id = net_id; 641 + hdr.src.node_id = self.own_node_id; 642 + } 643 + 644 + // If this is a broadcast message, update the destination, ignoring 645 + // whatever was there before 646 + if hdr.dst.port_id == 255 { 647 + hdr.dst.network_id = net_id; 648 + hdr.dst.node_id = self.other_node_id; 649 + } 650 + 651 + // If this message has no seq_no, assign it one 652 + let header = hdr.to_headerseq_or_with_seq(|| { 653 + let seq_no = self.seq_no; 654 + self.seq_no = self.seq_no.wrapping_add(1); 655 + seq_no 656 + }); 657 + if [0, 255].contains(&hdr.dst.port_id) && hdr.any_all.is_none() { 658 + return Err(InterfaceSendError::AnyPortMissingKey); 659 + } 660 + 661 + Ok((&mut self.sink, header)) 662 + } 663 + } 664 + 665 + /// NOTE: this LOOKS like a profile impl, because it was, but it's actually not, because 666 + /// this version of DirectEdge only serves DirectRouter 667 + impl<I: Interface> EdgeInterfacePlus<I> { 668 + pub(super) fn send<T: Serialize>( 669 + &mut self, 670 + hdr: &Header, 671 + data: &T, 672 + ) -> Result<(), InterfaceSendError> { 673 + let (intfc, header) = self.common_send(hdr)?; 674 + 675 + let res = intfc.send_ty(&header, data); 676 + 677 + match res { 678 + Ok(()) => Ok(()), 679 + Err(()) => Err(InterfaceSendError::InterfaceFull), 680 + } 681 + } 682 + 683 + pub(super) fn send_err( 684 + &mut self, 685 + hdr: &Header, 686 + err: ProtocolError, 687 + ) -> Result<(), InterfaceSendError> { 688 + let (intfc, header) = self.common_send(hdr)?; 689 + 690 + let res = intfc.send_err(&header, err); 691 + 692 + match res { 693 + Ok(()) => Ok(()), 694 + Err(()) => Err(InterfaceSendError::InterfaceFull), 695 + } 696 + } 697 + 698 + pub(super) fn send_raw( 699 + &mut self, 700 + hdr: &HeaderSeq, 701 + data: &[u8], 702 + ) -> Result<(), InterfaceSendError> { 703 + let nshdr: Header = hdr.clone().into(); 704 + let (intfc, header) = self.common_send(&nshdr)?; 705 + let res = intfc.send_raw(&header, data); 706 + 707 + match res { 708 + Ok(()) => Ok(()), 709 + Err(()) => Err(InterfaceSendError::InterfaceFull), 710 + } 711 + } 712 + 713 + pub(super) fn interface_state(&mut self, _ident: ()) -> Option<InterfaceState> { 714 + Some(self.state) 715 + } 716 + 717 + pub(super) fn set_interface_state( 718 + &mut self, 719 + _ident: (), 720 + state: InterfaceState, 721 + ) -> Result<(), SetStateError> { 722 + match state { 723 + InterfaceState::Down => { 724 + self.state = InterfaceState::Down; 725 + } 726 + InterfaceState::Inactive => { 727 + self.state = InterfaceState::Inactive; 728 + } 729 + InterfaceState::ActiveLocal { node_id } => { 730 + if node_id != self.own_node_id { 731 + return Err(SetStateError::InvalidNodeId); 732 + } 733 + self.state = InterfaceState::ActiveLocal { node_id }; 734 + } 735 + InterfaceState::Active { net_id, node_id } => { 736 + if node_id != self.own_node_id { 737 + return Err(SetStateError::InvalidNodeId); 738 + } 739 + self.state = InterfaceState::Active { net_id, node_id }; 740 + } 741 + } 742 + Ok(()) 743 + } 744 + } 745 + }
+4
crates/ergot/src/interface_manager/profiles/mod.rs
··· 8 9 #[cfg(feature = "tokio-std")] 10 pub mod direct_router;
··· 8 9 #[cfg(feature = "tokio-std")] 10 pub mod direct_router; 11 + 12 + // todo: nostd bridge router? 13 + #[cfg(feature = "tokio-std")] 14 + pub mod bridge_router;