Prepare, configure, and manage Firecracker microVMs in seconds!
virtualization
linux
microvm
firecracker
1use crate::{
2 constants::{BRIDGE_IP, MASK_SHORT},
3 types::VmOptions,
4};
5use anyhow::{anyhow, Context, Result};
6use serde_json::Value;
7
8use crate::command::run_command;
9
10fn check_tap_exists(config: &VmOptions) -> bool {
11 run_command("ip", &["link", "show", &config.tap], false)
12 .map(|output| output.status.success())
13 .unwrap_or(false)
14}
15
16fn check_bridge_exists(config: &VmOptions) -> bool {
17 run_command("ip", &["link", "show", &config.bridge], false)
18 .map(|output| output.status.success())
19 .unwrap_or(false)
20}
21
22fn create_new_tap(config: &VmOptions) -> Result<()> {
23 run_command(
24 "ip",
25 &["tuntap", "add", "dev", &config.tap, "mode", "tap"],
26 true,
27 )?;
28 run_command("ip", &["link", "set", "dev", &config.tap, "up"], true)?;
29 run_command(
30 "ip",
31 &["link", "set", &config.tap, "master", &config.bridge],
32 true,
33 )?;
34 Ok(())
35}
36
37pub fn setup_network(config: &VmOptions) -> Result<()> {
38 if check_tap_exists(config) {
39 run_command("ip", &["addr", "flush", "dev", &config.tap], true)?;
40 }
41
42 if check_tap_exists(config) && check_bridge_exists(config) {
43 println!("[✓] Network already configured. Skipping setup.");
44 return Ok(());
45 }
46
47 if !check_bridge_exists(config) {
48 println!("[+] Configuring {}...", config.bridge);
49 run_command(
50 "ip",
51 &["link", "add", "name", &config.bridge, "type", "bridge"],
52 true,
53 )?;
54 run_command("ip", &["link", "set", &config.bridge, "up"], true)?;
55 run_command(
56 "ip",
57 &[
58 "addr",
59 "add",
60 &format!("{}{}", BRIDGE_IP, MASK_SHORT),
61 "dev",
62 &config.bridge,
63 ],
64 true,
65 )?;
66 }
67
68 if !check_tap_exists(config) {
69 println!("[+] Configuring {}...", &config.tap);
70 create_new_tap(config)?;
71 }
72
73 let ip_forward = run_command("cat", &["/proc/sys/net/ipv4/ip_forward"], false)?.stdout;
74 if String::from_utf8_lossy(&ip_forward).trim() != "1" {
75 println!("[+] Enabling IP forwarding...");
76 run_command("sysctl", &["-w", "net.ipv4.ip_forward=1"], true)?;
77 }
78
79 let output = run_command("ip", &["-j", "route", "list", "default"], false)?;
80 let json: Value =
81 serde_json::from_slice(&output.stdout).with_context(|| "Failed to parse route JSON")?;
82 let host_iface = json[0]["dev"]
83 .as_str()
84 .ok_or_else(|| anyhow!("Failed to get host interface"))?;
85
86 println!("[+] Setting up NAT on {}...", host_iface);
87
88 let rule_exists = run_command(
89 "iptables",
90 &[
91 "-t",
92 "nat",
93 "-C",
94 "POSTROUTING",
95 "-o",
96 host_iface,
97 "-j",
98 "MASQUERADE",
99 ],
100 true,
101 )
102 .map(|output| output.status.success())
103 .unwrap_or(false);
104
105 if !rule_exists {
106 run_command(
107 "iptables",
108 &[
109 "-t",
110 "nat",
111 "-A",
112 "POSTROUTING",
113 "-o",
114 host_iface,
115 "-j",
116 "MASQUERADE",
117 ],
118 true,
119 )?;
120 }
121
122 run_command("iptables", &["-P", "FORWARD", "ACCEPT"], true)?;
123
124 Ok(())
125}