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
use std::ffi::{CString, OsStr};
use std::net::{Ipv4Addr, SocketAddrV4};
use std::os::unix::ffi::OsStrExt;
use std::path::PathBuf;

use shadow_shim_helper_rs::emulated_time::EmulatedTime;
use shadow_shim_helper_rs::HostId;

use crate::core::configuration::QDiscMode;
use crate::core::worker::Worker;
use crate::cshadow as c;
use crate::host::descriptor::socket::inet::InetSocket;
use crate::network::packet::PacketRc;
use crate::network::PacketDevice;
use crate::utility::{self, HostTreePointer};

/// The priority used by the fifo qdisc to choose the next socket to send a packet from.
pub type FifoPacketPriority = u64;

#[derive(Debug, Clone)]
pub struct PcapOptions {
    pub path: PathBuf,
    pub capture_size_bytes: u32,
}

/// Represents a network device that can send and receive packets. All accesses
/// to the internal C implementation should be done through this module.
pub struct NetworkInterface {
    c_ptr: HostTreePointer<c::NetworkInterface>,
    addr: Ipv4Addr,
}

impl NetworkInterface {
    /// Create a new network interface for `host_id` with the assigned `addr`.
    ///
    /// # Safety
    ///
    /// This function will trigger undefined behavior if `addr` is
    /// invalid. The reference count of `addr` will be increased by one using
    /// `address_ref()`, so the caller should call `address_unref()` on it to
    /// drop their reference when they no longer need it.
    pub unsafe fn new(
        host_id: HostId,
        addr: *mut c::Address,
        name: &OsStr,
        pcap_options: Option<PcapOptions>,
        qdisc: QDiscMode,
    ) -> NetworkInterface {
        let maybe_pcap_dir = pcap_options
            .as_ref()
            .map(|x| utility::pathbuf_to_nul_term_cstring(x.path.clone()));
        let pcap_dir_cptr = maybe_pcap_dir
            .as_ref()
            .map_or(std::ptr::null(), |p| p.as_ptr());

        let pcap_capture_size = pcap_options
            .as_ref()
            .map(|x| x.capture_size_bytes)
            .unwrap_or(0);

        let mut name = name.as_bytes().to_vec();
        name.push(0);
        let name = CString::from_vec_with_nul(name).unwrap();

        let c_ptr = unsafe {
            c::networkinterface_new(addr, name.as_ptr(), pcap_dir_cptr, pcap_capture_size, qdisc)
        };

        let ipv4_addr: Ipv4Addr = {
            let addr = unsafe { c::address_toNetworkIP(addr) };
            u32::from_be(addr).into()
        };

        NetworkInterface {
            c_ptr: HostTreePointer::new_for_host(host_id, c_ptr),
            addr: ipv4_addr,
        }
    }

    pub fn associate(
        &self,
        socket_ptr: &InetSocket,
        protocol_type: c::ProtocolType,
        port: u16,
        peer_addr: SocketAddrV4,
    ) {
        let port = port.to_be();
        let peer_ip = u32::from(*peer_addr.ip()).to_be();
        let peer_port = peer_addr.port().to_be();

        unsafe {
            c::networkinterface_associate(
                self.c_ptr.ptr(),
                socket_ptr,
                protocol_type,
                port,
                peer_ip,
                peer_port,
            )
        };
    }

    pub fn disassociate(&self, protocol_type: c::ProtocolType, port: u16, peer_addr: SocketAddrV4) {
        let port = port.to_be();
        let peer_ip = u32::from(*peer_addr.ip()).to_be();
        let peer_port = peer_addr.port().to_be();

        unsafe {
            c::networkinterface_disassociate(
                self.c_ptr.ptr(),
                protocol_type,
                port,
                peer_ip,
                peer_port,
            )
        };
    }

    pub fn is_addr_in_use(&self, protocol: c::ProtocolType, port: u16, peer: SocketAddrV4) -> bool {
        let port = port.to_be();
        let peer_ip = u32::from(*peer.ip()).to_be();
        let peer_port = peer.port().to_be();

        (unsafe {
            c::networkinterface_isAssociated(self.c_ptr.ptr(), protocol, port, peer_ip, peer_port)
        }) != 0
    }

    pub fn add_data_source(&self, socket: &InetSocket) {
        unsafe { c::networkinterface_wantsSend(self.c_ptr.ptr(), socket) };
    }

    /// Disassociate all bound sockets and remove sockets from the sending queue. This should be
    /// called as part of the host's cleanup procedure.
    pub fn remove_all_sockets(&self) {
        unsafe { c::networkinterface_removeAllSockets(self.c_ptr.ptr()) };
    }
}

impl Drop for NetworkInterface {
    fn drop(&mut self) {
        // don't check the active host since we're in the middle of dropping the host
        unsafe { c::networkinterface_free(self.c_ptr.ptr_unchecked()) };
    }
}

impl PacketDevice for NetworkInterface {
    fn get_address(&self) -> Ipv4Addr {
        self.addr
    }

    fn pop(&self) -> Option<PacketRc> {
        let packet_ptr = unsafe { c::networkinterface_pop(self.c_ptr.ptr()) };
        match packet_ptr.is_null() {
            true => None,
            false => Some(PacketRc::from_raw(packet_ptr)),
        }
    }

    fn push(&self, packet: PacketRc) {
        let packet_ptr = packet.into_inner();
        let current_time = Worker::current_time().unwrap();
        unsafe {
            c::networkinterface_push(
                self.c_ptr.ptr(),
                packet_ptr,
                EmulatedTime::to_c_emutime(Some(current_time)),
            )
        };
        unsafe { c::packet_unref(packet_ptr) };
    }
}