kratanet/
backend.rs

1use crate::autonet::NetworkMetadata;
2use crate::chandev::ChannelDevice;
3use crate::nat::Nat;
4use crate::proxynat::ProxyNatHandlerFactory;
5use crate::raw_socket::{AsyncRawSocketChannel, RawSocketHandle, RawSocketProtocol};
6use crate::vbridge::{BridgeJoinHandle, VirtualBridge};
7use crate::EXTRA_MTU;
8use anyhow::{anyhow, Result};
9use bytes::BytesMut;
10use futures::TryStreamExt;
11use log::{info, trace, warn};
12use smoltcp::iface::{Config, Interface, SocketSet};
13use smoltcp::phy::Medium;
14use smoltcp::time::Instant;
15use smoltcp::wire::{HardwareAddress, IpCidr};
16use tokio::select;
17use tokio::sync::mpsc::{channel, Receiver};
18use tokio::task::JoinHandle;
19
20const TX_CHANNEL_BUFFER_LEN: usize = 3000;
21
22#[derive(Clone)]
23pub struct NetworkBackend {
24    metadata: NetworkMetadata,
25    bridge: VirtualBridge,
26}
27
28#[derive(Debug)]
29enum NetworkStackSelect {
30    Receive(Option<BytesMut>),
31    Send(Option<BytesMut>),
32}
33
34struct NetworkStack<'a> {
35    tx: Receiver<BytesMut>,
36    kdev: AsyncRawSocketChannel,
37    udev: ChannelDevice,
38    interface: Interface,
39    sockets: SocketSet<'a>,
40    nat: Nat,
41    bridge: BridgeJoinHandle,
42}
43
44impl NetworkStack<'_> {
45    async fn poll(&mut self) -> Result<bool> {
46        let what = select! {
47            biased;
48            x = self.kdev.receiver.recv() => NetworkStackSelect::Receive(x),
49            x = self.tx.recv() => NetworkStackSelect::Send(x),
50            x = self.bridge.from_bridge_receiver.recv() => NetworkStackSelect::Send(x),
51            x = self.bridge.from_broadcast_receiver.recv() => NetworkStackSelect::Send(x.ok()),
52        };
53
54        match what {
55            NetworkStackSelect::Receive(Some(packet)) => {
56                if let Err(error) = self.bridge.to_bridge_sender.try_send(packet.clone()) {
57                    trace!("failed to send zone packet to bridge: {}", error);
58                }
59
60                if let Err(error) = self.nat.receive_sender.try_send(packet.clone()) {
61                    trace!("failed to send zone packet to nat: {}", error);
62                }
63
64                self.udev.rx = Some(packet);
65                self.interface
66                    .poll(Instant::now(), &mut self.udev, &mut self.sockets);
67            }
68
69            NetworkStackSelect::Send(Some(packet)) => {
70                if let Err(error) = self.kdev.sender.try_send(packet) {
71                    warn!("failed to transmit packet to interface: {}", error);
72                }
73            }
74
75            NetworkStackSelect::Receive(None) | NetworkStackSelect::Send(None) => {
76                return Ok(false);
77            }
78        }
79
80        Ok(true)
81    }
82}
83
84impl NetworkBackend {
85    pub fn new(metadata: NetworkMetadata, bridge: VirtualBridge) -> Result<Self> {
86        Ok(Self { metadata, bridge })
87    }
88
89    pub async fn init(&mut self) -> Result<()> {
90        let interface = self.metadata.interface();
91        let (connection, handle, _) = rtnetlink::new_connection()?;
92        tokio::spawn(connection);
93
94        let mut links = handle.link().get().match_name(interface.clone()).execute();
95        let link = links.try_next().await?;
96        if link.is_none() {
97            return Err(anyhow!(
98                "unable to find network interface named {}",
99                interface
100            ));
101        }
102        let link = link.unwrap();
103        handle.link().set(link.header.index).up().execute().await?;
104        Ok(())
105    }
106
107    pub async fn run(&self) -> Result<()> {
108        let mut stack = self.create_network_stack().await?;
109        loop {
110            if !stack.poll().await? {
111                break;
112            }
113        }
114        Ok(())
115    }
116
117    async fn create_network_stack(&self) -> Result<NetworkStack> {
118        let interface = self.metadata.interface();
119        let proxy = Box::new(ProxyNatHandlerFactory::new());
120        let addresses: Vec<IpCidr> = vec![
121            self.metadata.gateway.ipv4.into(),
122            self.metadata.gateway.ipv6.into(),
123        ];
124        let mut kdev =
125            RawSocketHandle::bound_to_interface(&interface, RawSocketProtocol::Ethernet)?;
126        let mtu = kdev.mtu_of_interface(&interface)? + EXTRA_MTU;
127        let (tx_sender, tx_receiver) = channel::<BytesMut>(TX_CHANNEL_BUFFER_LEN);
128        let mut udev = ChannelDevice::new(mtu, Medium::Ethernet, tx_sender.clone());
129        let mac = self.metadata.gateway.mac;
130        let local_cidrs = addresses.clone();
131        let nat = Nat::new(mtu, proxy, mac, local_cidrs, tx_sender.clone())?;
132        let hardware_addr = HardwareAddress::Ethernet(mac);
133        let config = Config::new(hardware_addr);
134        let mut iface = Interface::new(config, &mut udev, Instant::now());
135        iface.update_ip_addrs(|addrs| {
136            addrs
137                .extend_from_slice(&addresses)
138                .expect("failed to set ip addresses");
139        });
140        let sockets = SocketSet::new(vec![]);
141        let handle = self.bridge.join(self.metadata.zone.mac).await?;
142        let kdev = AsyncRawSocketChannel::new(mtu, kdev)?;
143        Ok(NetworkStack {
144            tx: tx_receiver,
145            kdev,
146            udev,
147            interface: iface,
148            sockets,
149            nat,
150            bridge: handle,
151        })
152    }
153
154    pub async fn launch(self) -> Result<JoinHandle<()>> {
155        Ok(tokio::task::spawn(async move {
156            info!(
157                "launched network backend for krata zone {}",
158                self.metadata.uuid
159            );
160            if let Err(error) = self.run().await {
161                warn!(
162                    "network backend for krata zone {} failed: {}",
163                    self.metadata.uuid, error
164                );
165            }
166        }))
167    }
168}
169
170impl Drop for NetworkBackend {
171    fn drop(&mut self) {
172        info!(
173            "destroyed network backend for krata zone {}",
174            self.metadata.uuid
175        );
176    }
177}