Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: get internet access #39

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ run:
'RUST_BACKTRACE=1 '$CARGO_PATH' run --bin vmm -- cli --memory 512 --cpus 1 \
--kernel tools/kernel/linux-cloud-hypervisor/arch/x86/boot/compressed/vmlinux.bin \
--iface-host-addr 172.29.0.1 --netmask 255.255.0.0 --iface-guest-addr 172.29.0.2 \
--initramfs=tools/rootfs/initramfs.img'
--initramfs=../virt-do/initramfs.img'

build-kernel:
#!/bin/bash
Expand Down
2 changes: 2 additions & 0 deletions src/fs-gen/resources/initfile
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ export RUST_VERSION='1.77.2'

export PATH=$CARGO_HOME/bin:$PATH

ln -s /proc/net/pnp /etc/resolv.conf

/agent

reboot
5 changes: 4 additions & 1 deletion src/vmm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ clap = { version = "4.5.1", features = ["derive", "env"] }
clap-verbosity-flag = "2.2.0"
epoll = "4.3.3"
event-manager = { version = "0.4.0", features = ["remote_endpoint"] }
futures = "0.3.30"
iptables = "0.5.1"
kvm-bindings = { version = "0.7.0", features = ["fam-wrappers"] }
kvm-ioctls = "0.16.0"
libc = "0.2.153"
Expand All @@ -22,7 +24,9 @@ log = "0.4.20"
nix = { version = "0.28.0", features = ["term"] }
openpty = "0.2.0"
prost = "0.11"
rtnetlink = "0.14.1"
tokio = { version = "1.37.0", features = ["full"] }
tokio-stream = "0.1.15"
tonic = "0.9"
tracing = "0.1.40"
tracing-subscriber = "0.3.18"
Expand All @@ -34,7 +38,6 @@ vm-device = "0.1.0"
vm-memory = { version = "0.14.1", features = ["backend-mmap"] }
vm-superio = "0.7.0"
vmm-sys-util = "0.12.1"
tokio-stream = "0.1.15"

[build-dependencies]
tonic-build = "0.9"
137 changes: 137 additions & 0 deletions src/vmm/src/core/devices/virtio/net/bridge.rs
sylvain-pierrot marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
use std::net::{IpAddr, Ipv4Addr};

use futures::stream::TryStreamExt;
use rtnetlink::{new_connection, Error, Handle};

use super::xx_netmask_width;

#[derive(Clone)]
pub struct Bridge {
name: String,
handle: Handle,
}

impl Bridge {
pub fn new(name: String) -> Self {
let (connection, handle, _) = new_connection().unwrap();
tokio::spawn(connection);

let br = Self { name, handle };
br.create_bridge_if_not_exist();

br
}

fn create_bridge_if_not_exist(&self) {
futures::executor::block_on(async {
let mut bridge_names = self
.handle
.link()
.get()
.match_name(self.name.clone())
.execute();

let _ = match bridge_names.try_next().await {
Ok(_) => Ok(()),
Err(_) => self
.handle
.link()
.add()
.bridge(self.name.clone())
.execute()
.await
.map_err(|_| Error::RequestFailed),
};
});
}

pub fn set_addr(&self, addr: Ipv4Addr, netmask: Ipv4Addr) {
futures::executor::block_on(async {
let mut bridge_names = self
.handle
.link()
.get()
.match_name(self.name.clone())
.execute();

let bridge_index = match bridge_names.try_next().await {
Ok(Some(link)) => link.header.index,
Ok(None) => panic!(),
Err(_) => panic!(),
};

let prefix_len = xx_netmask_width(netmask.octets());

let _ = self
.handle
.address()
.add(bridge_index, IpAddr::V4(addr), prefix_len)
.execute()
.await
.map_err(|_| Error::RequestFailed);
});
}

pub fn set_up(&self) {
futures::executor::block_on(async {
let mut bridge_names = self
.handle
.link()
.get()
.match_name(self.name.clone())
.execute();

let bridge_index = match bridge_names.try_next().await {
Ok(Some(link)) => link.header.index,
Ok(None) => panic!(),
Err(_) => panic!(),
};

let _ = self
.handle
.link()
.set(bridge_index)
.up()
.execute()
.await
.map_err(|_| Error::RequestFailed);
});
}

pub fn attach_link(&self, link_name: String) {
futures::executor::block_on(async {
let mut link_names = self
.handle
.link()
.get()
.match_name(link_name.clone())
.execute();
let mut master_names = self
.handle
.link()
.get()
.match_name(self.name.clone())
.execute();

let link_index = match link_names.try_next().await {
Ok(Some(link)) => link.header.index,
Ok(None) => panic!(),
Err(_) => panic!(),
};
let master_index = match master_names.try_next().await {
Ok(Some(link)) => link.header.index,
Ok(None) => panic!(),
Err(_) => panic!(),
};

let _ = self
.handle
.link()
.set(link_index)
.controller(master_index)
.execute()
.await
.map_err(|_| Error::RequestFailed);
});
}
}
24 changes: 18 additions & 6 deletions src/vmm/src/core/devices/virtio/net/device.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use super::bridge::Bridge;
use super::iptables::iptables_ip_masq;
use super::queue_handler::QueueHandler;
use super::{
simple_handler::SimpleHandler, tuntap::tap::Tap, Error, Result, NET_DEVICE_ID,
Expand Down Expand Up @@ -34,6 +36,7 @@ pub struct Net {
mem: Arc<GuestMemoryMmap>,
pub config: Config,
tap: Arc<Mutex<Tap>>,
_bridge: Bridge,
}

impl Net {
Expand All @@ -42,7 +45,7 @@ impl Net {
mem: Arc<GuestMemoryMmap>,
device_mgr: Arc<Mutex<IoManager>>,
mmio_cfg: MmioConfig,
tap_addr: Ipv4Addr,
iface_host_addr: Ipv4Addr,
netmask: Ipv4Addr,
iface_guest_addr: Ipv4Addr,
irq: u32,
Expand Down Expand Up @@ -74,25 +77,34 @@ impl Net {

// Set offload flags to match the relevant virtio features of the device (for now,
// statically set in the constructor.
let tap = open_tap(None, Some(tap_addr), Some(netmask), &mut None, None, None)
.map_err(Error::TunTap)?;
let tap = open_tap(None, None, None, &mut None, None, None).map_err(Error::TunTap)?;

// The layout of the header is specified in the standard and is 12 bytes in size. We
// should define this somewhere.
tap.set_vnet_hdr_size(VIRTIO_NET_HDR_SIZE as i32)
.map_err(Error::Tap)?;

let bridge_name = "br0".to_string();
let bridge = Bridge::new(bridge_name.clone());
bridge.set_addr(iface_host_addr, netmask);
bridge.attach_link(tap.get_name().map_err(Error::Tap)?);
bridge.set_up();

// Get internet access
iptables_ip_masq(iface_host_addr & netmask, netmask, bridge_name);

let net = Arc::new(Mutex::new(Net {
mem,
config: cfg,
tap: Arc::new(Mutex::new(tap)),
tap: Arc::new(Mutex::new(tap.clone())),
_bridge: bridge,
}));

let vmmio_param = register_mmio_device(mmio_cfg, device_mgr, irq, None, net.clone())
.map_err(Error::Virtio)?;
let ip_pnp_param: String = format!(
"ip={}::{}:{}::eth0:off",
iface_guest_addr, tap_addr, netmask
"ip={}::{}:{}::eth0:off:1.1.1.1",
iface_guest_addr, iface_host_addr, netmask
);

cmdline_extra_parameters.push(vmmio_param);
Expand Down
16 changes: 16 additions & 0 deletions src/vmm/src/core/devices/virtio/net/iptables.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
use std::net::Ipv4Addr;

use super::xx_netmask_width;

pub fn iptables_ip_masq(network: Ipv4Addr, netmask: Ipv4Addr, link_name: String) {
let prefix_len = xx_netmask_width(netmask.octets());
let source = format!("{}/{}", network, prefix_len);

let ipt = iptables::new(false).unwrap();
let rule = format!("-s {} ! -o {} -j MASQUERADE", source, link_name);

let exists = ipt.exists("nat", "POSTROUTING", rule.as_str()).unwrap();
if !exists {
let _ = ipt.insert_unique("nat", "POSTROUTING", rule.as_str(), 1);
}
}
6 changes: 6 additions & 0 deletions src/vmm/src/core/devices/virtio/net/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
mod bridge;
pub mod device;
pub mod iptables;
mod queue_handler;
mod simple_handler;
pub mod tuntap;
Expand All @@ -20,3 +22,7 @@ pub enum Error {
}

pub type Result<T> = std::result::Result<T, Error>;

pub fn xx_netmask_width<const SZ: usize>(netmask: [u8; SZ]) -> u8 {
netmask.iter().map(|x| x.count_ones() as u8).sum()
}
6 changes: 6 additions & 0 deletions src/vmm/src/core/devices/virtio/net/tuntap/tap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,12 @@ impl Tap {
unsafe { Self::ioctl_with_ref(&sock, net_gen::sockios::SIOCSIFHWADDR as c_ulong, &ifreq) }
}

/// Get tap name
pub fn get_name(&self) -> Result<String> {
let name = String::from_utf8(self.if_name.clone()).map_err(|_| Error::InvalidIfname)?;
Ok(name)
}

/// Get mac addr for tap interface.
pub fn get_mac_addr(&self) -> Result<MacAddr> {
let sock = create_unix_socket().map_err(Error::NetUtil)?;
Expand Down
12 changes: 8 additions & 4 deletions src/vmm/src/core/vmm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ pub struct VMM {
event_mgr: EventMgr,
vcpus: Vec<Vcpu>,

tap_addr: Ipv4Addr,
iface_host_addr: Ipv4Addr,
netmask: Ipv4Addr,
iface_guest_addr: Ipv4Addr,
net_devices: Vec<Arc<Mutex<Net>>>,
Expand All @@ -69,7 +69,11 @@ pub struct VMM {

impl VMM {
/// Create a new VMM.
pub fn new(tap_addr: Ipv4Addr, netmask: Ipv4Addr, iface_guest_addr: Ipv4Addr) -> Result<Self> {
pub fn new(
iface_host_addr: Ipv4Addr,
netmask: Ipv4Addr,
iface_guest_addr: Ipv4Addr,
) -> Result<Self> {
// Open /dev/kvm and get a file descriptor to it.
let kvm = Kvm::new().map_err(Error::KvmIoctl)?;

Expand Down Expand Up @@ -111,7 +115,7 @@ impl VMM {
)),
slip_pty: Arc::new(Mutex::new(slip_pty)),
epoll,
tap_addr,
iface_host_addr,
netmask,
iface_guest_addr,
net_devices: Vec::new(),
Expand Down Expand Up @@ -382,7 +386,7 @@ impl VMM {
mem,
self.device_mgr.clone(),
mmio_cfg,
self.tap_addr,
self.iface_host_addr,
self.netmask,
self.iface_guest_addr,
irq,
Expand Down