shadow_rs/network/router/
mod.rs

1use std::cell::RefCell;
2use std::net::Ipv4Addr;
3
4use self::codel_queue::CoDelQueue;
5use crate::core::worker::Worker;
6use crate::network::PacketDevice;
7use crate::network::packet::PacketRc;
8use crate::utility::{Magic, ObjectCounter};
9mod codel_queue;
10
11use shadow_shim_helper_rs::emulated_time::EmulatedTime;
12
13/// A router assists with moving packets between hosts across the simulated
14/// network.
15pub struct Router {
16    magic: Magic<Self>,
17    _counter: ObjectCounter,
18    address: Ipv4Addr,
19    /// Packets inbound to the host from the simulated network.
20    inbound_packets: RefCell<CoDelQueue>,
21}
22
23impl Router {
24    /// Create a new router for a host that will help route packets between it
25    /// and other hosts. The `address` must uniquely identify this router to the
26    /// host that owns it.
27    pub fn new(address: Ipv4Addr) -> Router {
28        Router {
29            magic: Magic::new(),
30            address,
31            _counter: ObjectCounter::new("Router"),
32            inbound_packets: RefCell::new(CoDelQueue::new()),
33        }
34    }
35
36    fn push_inner(&self, packet: PacketRc, now: EmulatedTime) {
37        self.magic.debug_check();
38        self.inbound_packets.borrow_mut().push(packet, now);
39    }
40
41    fn pop_inner(&self, now: EmulatedTime) -> Option<PacketRc> {
42        self.magic.debug_check();
43        self.inbound_packets.borrow_mut().pop(now)
44    }
45
46    /// Routes the packet from the source host through the virtual internet to
47    /// the destination host.
48    fn route_outgoing_packet(&self, packet: PacketRc) {
49        // TODO: move Worker::send_packet to here?
50        Worker::with_active_host(|src_host| Worker::send_packet(src_host, packet)).unwrap();
51    }
52
53    /// Routes the packet from the virtual internet into our CoDel queue, which
54    /// can then be received by the destiantion host by calling pop().
55    pub fn route_incoming_packet(&self, packet: PacketRc) {
56        self.push_inner(packet, Worker::current_time().unwrap())
57    }
58}
59
60impl PacketDevice for Router {
61    fn get_address(&self) -> Ipv4Addr {
62        self.address
63    }
64
65    fn pop(&self) -> Option<PacketRc> {
66        // When the host calls pop, we provide the next packet from the CoDel queue.
67        self.pop_inner(Worker::current_time().unwrap())
68    }
69
70    fn push(&self, packet: PacketRc) {
71        // When the host calls push, we send to the virtual internet.
72        self.route_outgoing_packet(packet);
73    }
74}
75
76#[cfg(test)]
77mod tests {
78    use super::*;
79    use crate::network::tests::mock_time_millis;
80
81    #[test]
82    fn empty() {
83        let now = mock_time_millis(1000);
84        let router = Router::new(Ipv4Addr::UNSPECIFIED);
85        assert!(router.inbound_packets.borrow().peek().is_none());
86        assert!(router.pop_inner(now).is_none());
87    }
88
89    #[test]
90    // Ignore in miri for use of c::packet* functions.
91    #[cfg_attr(miri, ignore)]
92    fn push_pop_simple() {
93        let now = mock_time_millis(1000);
94        let router = Router::new(Ipv4Addr::UNSPECIFIED);
95
96        const N: usize = 10;
97
98        for _ in 1..=N {
99            router.push_inner(PacketRc::new_ipv4_udp_mock(), now);
100            assert!(router.inbound_packets.borrow().peek().is_some());
101        }
102        for _ in 1..=N {
103            assert!(router.inbound_packets.borrow().peek().is_some());
104            assert!(router.pop_inner(now).is_some());
105        }
106
107        assert!(router.inbound_packets.borrow().peek().is_none());
108        assert!(router.pop_inner(now).is_none());
109    }
110}