mvm_runtime/vm/
network.rs1use anyhow::Result;
2
3use crate::config::*;
4use crate::shell::run_in_vm_visible;
5use crate::ui;
6
7pub fn setup() -> Result<()> {
13 ui::info("Setting up network...");
14 run_in_vm_visible(&format!(
15 r#"
16 set -euo pipefail
17
18 # Create TAP device
19 sudo ip link del {tap} 2>/dev/null || true
20 sudo ip tuntap add dev {tap} mode tap
21 sudo ip addr add {tap_ip}{mask} dev {tap}
22 sudo ip link set dev {tap} up
23
24 # Enable IP forwarding
25 sudo sh -c "echo 1 > /proc/sys/net/ipv4/ip_forward"
26 sudo iptables -P FORWARD ACCEPT
27
28 # Determine host network interface
29 HOST_IFACE=$(ip -j route list default | jq -r '.[0].dev')
30
31 # NAT for internet access
32 sudo iptables -t nat -D POSTROUTING -o "$HOST_IFACE" -j MASQUERADE 2>/dev/null || true
33 sudo iptables -t nat -A POSTROUTING -o "$HOST_IFACE" -j MASQUERADE
34
35 echo "[mvm] Network ready (tap={tap}, host=$HOST_IFACE)."
36 "#,
37 tap = TAP_DEV,
38 tap_ip = TAP_IP,
39 mask = MASK_SHORT,
40 ))
41}
42
43pub fn teardown() -> Result<()> {
45 run_in_vm_visible(&format!(
46 r#"
47 sudo ip link del {tap} 2>/dev/null || true
48 HOST_IFACE=$(ip -j route list default 2>/dev/null | jq -r '.[0].dev' 2>/dev/null) || true
49 if [ -n "$HOST_IFACE" ]; then
50 sudo iptables -t nat -D POSTROUTING -o "$HOST_IFACE" -j MASQUERADE 2>/dev/null || true
51 fi
52 "#,
53 tap = TAP_DEV,
54 ))
55}
56
57pub fn bridge_ensure() -> Result<()> {
63 ui::info("Ensuring bridge network...");
64 run_in_vm_visible(&format!(
65 r#"
66 set -euo pipefail
67
68 # Create bridge if missing
69 if ! ip link show {br} >/dev/null 2>&1; then
70 sudo ip link add name {br} type bridge
71 sudo ip addr add {br_cidr} dev {br}
72 sudo ip link set dev {br} up
73 echo "[mvm] Created bridge {br}"
74 else
75 echo "[mvm] Bridge {br} already exists"
76 fi
77
78 # Enable IP forwarding
79 sudo sh -c "echo 1 > /proc/sys/net/ipv4/ip_forward"
80 sudo iptables -P FORWARD ACCEPT
81
82 # NAT: MASQUERADE traffic from the bridge subnet to the internet.
83 # Use -C to check first so we don't duplicate the rule.
84 HOST_IFACE=$(ip -j route list default | jq -r '.[0].dev')
85 if ! sudo iptables -t nat -C POSTROUTING -s 172.16.0.0/24 -o "$HOST_IFACE" -j MASQUERADE 2>/dev/null; then
86 sudo iptables -t nat -A POSTROUTING -s 172.16.0.0/24 -o "$HOST_IFACE" -j MASQUERADE
87 fi
88
89 echo "[mvm] Bridge network ready ({br}=$HOST_IFACE)."
90 "#,
91 br = BRIDGE_DEV,
92 br_cidr = BRIDGE_CIDR,
93 ))
94}
95
96pub fn bridge_teardown() -> Result<()> {
98 run_in_vm_visible(&format!(
99 r#"
100 sudo ip link del {br} 2>/dev/null || true
101 HOST_IFACE=$(ip -j route list default 2>/dev/null | jq -r '.[0].dev' 2>/dev/null) || true
102 if [ -n "$HOST_IFACE" ]; then
103 sudo iptables -t nat -D POSTROUTING -s 172.16.0.0/24 -o "$HOST_IFACE" -j MASQUERADE 2>/dev/null || true
104 fi
105 "#,
106 br = BRIDGE_DEV,
107 ))
108}
109
110pub fn tap_create(slot: &VmSlot) -> Result<()> {
112 ui::info(&format!(
113 "Creating TAP {} for VM '{}'...",
114 slot.tap_dev, slot.name
115 ));
116 run_in_vm_visible(&format!(
117 r#"
118 set -euo pipefail
119 sudo ip link del {tap} 2>/dev/null || true
120 sudo ip tuntap add dev {tap} mode tap
121 sudo ip link set dev {tap} master {br}
122 sudo ip link set dev {tap} up
123 echo "[mvm] TAP {tap} attached to {br}"
124 "#,
125 tap = slot.tap_dev,
126 br = BRIDGE_DEV,
127 ))
128}
129
130pub fn tap_destroy(slot: &VmSlot) -> Result<()> {
132 run_in_vm_visible(&format!(
133 "sudo ip link del {tap} 2>/dev/null || true",
134 tap = slot.tap_dev,
135 ))
136}