Skip to main content

mvm_runtime/vm/
network.rs

1use anyhow::Result;
2
3use crate::config::*;
4use crate::shell::run_in_vm_visible;
5use crate::ui;
6
7// ============================================================================
8// Legacy dev-mode TAP networking (single VM, used by `mvm start/stop`)
9// ============================================================================
10
11/// Set up TAP networking, IP forwarding, and NAT inside the Lima VM.
12pub 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
43/// Tear down TAP device and iptables rules.
44pub 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
57// ============================================================================
58// Bridge-based networking (multi-VM, used by `mvm run`)
59// ============================================================================
60
61/// Ensure the br-mvm bridge exists with IP and NAT configured.  Idempotent.
62pub 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
96/// Remove the bridge and associated NAT rules.
97pub 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
110/// Create a TAP device for a VM slot and attach it to the bridge.
111pub 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
130/// Remove a TAP device for a VM slot.
131pub 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}