diff options
-rw-r--r-- | Cargo.lock | 10 | ||||
-rw-r--r-- | crosvm_plugin/Cargo.toml | 2 | ||||
-rw-r--r-- | crosvm_plugin/crosvm.h | 30 | ||||
-rw-r--r-- | crosvm_plugin/src/lib.rs | 56 | ||||
-rw-r--r-- | plugin_proto/Cargo.toml | 2 | ||||
-rw-r--r-- | plugin_proto/protos/plugin.proto | 40 | ||||
-rw-r--r-- | src/plugin/mod.rs | 34 | ||||
-rw-r--r-- | src/plugin/process.rs | 47 | ||||
-rw-r--r-- | tests/plugin.policy | 2 | ||||
-rw-r--r-- | tests/plugin_net_config.c | 85 | ||||
-rw-r--r-- | tests/plugins.rs | 11 |
11 files changed, 292 insertions, 27 deletions
diff --git a/Cargo.lock b/Cargo.lock index fc43a11..9e15b85 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -18,7 +18,7 @@ name = "crosvm" version = "0.1.0" dependencies = [ "byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "crosvm_plugin 0.11.0", + "crosvm_plugin 0.12.0", "data_model 0.1.0", "device_manager 0.1.0", "devices 0.1.0", @@ -29,7 +29,7 @@ dependencies = [ "kvm_sys 0.1.0", "libc 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)", "net_util 0.1.0", - "plugin_proto 0.11.0", + "plugin_proto 0.12.0", "protobuf 1.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "qcow 0.1.0", "qcow_utils 0.1.0", @@ -42,12 +42,12 @@ dependencies = [ [[package]] name = "crosvm_plugin" -version = "0.11.0" +version = "0.12.0" dependencies = [ "kvm 0.1.0", "kvm_sys 0.1.0", "libc 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)", - "plugin_proto 0.11.0", + "plugin_proto 0.12.0", "protobuf 1.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "sys_util 0.1.0", ] @@ -173,7 +173,7 @@ dependencies = [ [[package]] name = "plugin_proto" -version = "0.11.0" +version = "0.12.0" dependencies = [ "gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)", "kvm_sys 0.1.0", diff --git a/crosvm_plugin/Cargo.toml b/crosvm_plugin/Cargo.toml index d34753a..42516ad 100644 --- a/crosvm_plugin/Cargo.toml +++ b/crosvm_plugin/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "crosvm_plugin" -version = "0.11.0" +version = "0.12.0" authors = ["The Chromium OS Authors"] [lib] diff --git a/crosvm_plugin/crosvm.h b/crosvm_plugin/crosvm.h index 0dc2a60..30b32c1 100644 --- a/crosvm_plugin/crosvm.h +++ b/crosvm_plugin/crosvm.h @@ -47,7 +47,7 @@ extern "C" { * do not indicate anything about what version of crosvm is running. */ #define CROSVM_API_MAJOR 0 -#define CROSVM_API_MINOR 11 +#define CROSVM_API_MINOR 12 #define CROSVM_API_PATCH 0 enum crosvm_address_space { @@ -130,6 +130,34 @@ int crosvm_get_emulated_cpuid(struct crosvm*, uint32_t __entry_count, uint32_t *__out_count); /* + * The network configuration for a crosvm instance. + */ +struct crosvm_net_config { + /* + * The tap device fd. This fd is owned by the caller, and should be closed + * by the caller when it is no longer in use. + */ + int tap_fd; + /* The IPv4 address of the tap interface, in network (big-endian) format. */ + uint32_t host_ip; + /* The netmask of the tap interface subnet, in network (big-endian) format. */ + uint32_t netmask; + /* The mac address of the host side of the tap interface. */ + uint8_t host_mac_address[6]; + uint8_t _padding[2]; +}; + +#ifdef static_assert +static_assert(sizeof(struct crosvm_net_config) == 20, + "extra padding in struct crosvm_net_config"); +#endif + +/* + * Gets the network configuration. + */ +int crosvm_net_get_config(struct crosvm*, struct crosvm_net_config*); + +/* * Registers a range in the given address space that, when accessed, will block * and wait for a crosvm_vcpu_resume call. * diff --git a/crosvm_plugin/src/lib.rs b/crosvm_plugin/src/lib.rs index 34d9d91..4bb763f 100644 --- a/crosvm_plugin/src/lib.rs +++ b/crosvm_plugin/src/lib.rs @@ -61,6 +61,16 @@ const CROSVM_VCPU_EVENT_KIND_PAUSED: u32 = 2; #[repr(C)] #[derive(Copy, Clone)] +pub struct crosvm_net_config { + tap_fd: c_int, + host_ipv4_address: u32, + netmask: u32, + host_mac_address: [u8; 6], + _reserved: [u8; 2], +} + +#[repr(C)] +#[derive(Copy, Clone)] pub struct anon_irqchip { irqchip: u32, pin: u32, @@ -376,6 +386,38 @@ impl crosvm { Err(ENOENT) } } + + fn get_net_config(&mut self) -> result::Result<crosvm_net_config, c_int> { + let mut r = MainRequest::new(); + r.mut_get_net_config(); + + let (response, mut files) = self.main_transaction(&r, &[])?; + if !response.has_get_net_config() { + return Err(EPROTO); + } + let config = response.get_get_net_config(); + + match files.pop() { + Some(f) => { + let mut net_config = crosvm_net_config { + tap_fd: f.into_raw_fd(), + host_ipv4_address: config.host_ipv4_address, + netmask: config.netmask, + host_mac_address: [0; 6], + _reserved: [0; 2], + }; + + let mac_addr = config.get_host_mac_address(); + if mac_addr.len() != net_config.host_mac_address.len() { + return Err(EPROTO) + } + net_config.host_mac_address.copy_from_slice(mac_addr); + + Ok(net_config) + }, + None => Err(EPROTO), + } + } } /// This helper macro implements the C API's constructor/destructor for a given type. Because they @@ -918,6 +960,20 @@ fn crosvm_get_emulated_cpuid(this: *mut crosvm, } #[no_mangle] +pub unsafe extern "C" fn crosvm_net_get_config(self_: *mut crosvm, + config: *mut crosvm_net_config) + -> c_int { + let self_ = &mut (*self_); + let ret = self_.get_net_config(); + + if let Ok(c) = ret { + *config = c; + } + + to_crosvm_rc(ret) +} + +#[no_mangle] pub unsafe extern "C" fn crosvm_reserve_range(self_: *mut crosvm, space: u32, start: u64, diff --git a/plugin_proto/Cargo.toml b/plugin_proto/Cargo.toml index 9ddf1a4..534358d 100644 --- a/plugin_proto/Cargo.toml +++ b/plugin_proto/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "plugin_proto" -version = "0.11.0" +version = "0.12.0" authors = ["The Chromium OS Authors"] build = "build.rs" diff --git a/plugin_proto/protos/plugin.proto b/plugin_proto/protos/plugin.proto index 0e6f883..c67872c 100644 --- a/plugin_proto/protos/plugin.proto +++ b/plugin_proto/protos/plugin.proto @@ -75,6 +75,8 @@ message MainRequest { message CpuidRequest { } + message GetNetConfig {} + message ReserveRange { AddressSpace space = 1; uint64 start = 2; @@ -136,13 +138,14 @@ message MainRequest { CheckExtension check_extension = 5; CpuidRequest get_supported_cpuid = 6; CpuidRequest get_emulated_cpuid = 7; - ReserveRange reserve_range = 8; - SetIrq set_irq = 9; - SetIrqRouting set_irq_routing = 10; - SetIdentityMapAddr set_identity_map_addr = 11; - PauseVcpus pause_vcpus = 12; - GetVcpus get_vcpus = 13; - Start start = 14; + GetNetConfig get_net_config = 8; + ReserveRange reserve_range = 9; + SetIrq set_irq = 10; + SetIrqRouting set_irq_routing = 11; + SetIdentityMapAddr set_identity_map_addr = 12; + PauseVcpus pause_vcpus = 13; + GetVcpus get_vcpus = 14; + Start start = 15; // Method for a Memory type object for retrieving the dirty bitmap. Only valid if the memory // object was created with dirty_log set. MemoryDirtyLog dirty_log = 101; @@ -164,6 +167,14 @@ message MainResponse { message CpuidResponse { repeated CpuidEntry entries = 1; } + + // GetNetConfig messages also return a file descriptor for the tap device. + message GetNetConfig { + bytes host_mac_address = 1; + fixed32 host_ipv4_address = 2; + fixed32 netmask = 3; + } + message ReserveRange {} message SetIrq {} message SetIrqRouting {} @@ -189,13 +200,14 @@ message MainResponse { CheckExtension check_extension = 6; CpuidResponse get_supported_cpuid = 7; CpuidResponse get_emulated_cpuid = 8; - ReserveRange reserve_range = 9; - SetIrq set_irq = 10; - SetIrqRouting set_irq_routing = 11; - SetIdentityMapAddr set_identity_map_addr = 12; - PauseVcpus pause_vcpus = 13; - GetVcpus get_vcpus = 14; - Start start = 15; + GetNetConfig get_net_config = 9; + ReserveRange reserve_range = 10; + SetIrq set_irq = 11; + SetIrqRouting set_irq_routing = 12; + SetIdentityMapAddr set_identity_map_addr = 13; + PauseVcpus pause_vcpus = 14; + GetVcpus get_vcpus = 15; + Start start = 16; MemoryDirtyLog dirty_log = 101; } } diff --git a/src/plugin/mod.rs b/src/plugin/mod.rs index 4528df8..cd88bbf 100644 --- a/src/plugin/mod.rs +++ b/src/plugin/mod.rs @@ -18,12 +18,13 @@ use std::thread; use std::time::{Duration, Instant}; use libc::{socketpair, ioctl, c_ulong, AF_UNIX, SOCK_SEQPACKET, FIOCLEX, EAGAIN, EINTR, EINVAL, - ENOENT, EPERM, EDEADLK, ENOTTY, EEXIST, EBADF, EOVERFLOW, SIGCHLD, MS_NOSUID, MS_NODEV}; + ENOENT, EPERM, EDEADLK, EEXIST, EBADF, EOVERFLOW, SIGCHLD, MS_NOSUID, MS_NODEV}; use protobuf::ProtobufError; use io_jail::{self, Minijail}; use kvm::{Kvm, Vm, Vcpu, VcpuExit, IoeventAddress, NoDatamatch}; +use net_util::{Error as TapError, Tap, TapT}; use sys_util::{EventFd, MmapError, Killable, SignalFd, SignalFdError, Poller, Pollable, GuestMemory, Result as SysResult, Error as SysError, register_signal_handler, block_signal, clear_signal, SIGRTMIN, @@ -83,6 +84,11 @@ pub enum Error { }, SignalFd(SignalFdError), SpawnVcpu(io::Error), + TapOpen(TapError), + TapSetIp(TapError), + TapSetNetmask(TapError), + TapSetMacAddress(TapError), + TapEnable(TapError), } impl fmt::Display for Error { @@ -149,7 +155,11 @@ impl fmt::Display for Error { } Error::SignalFd(ref e) => write!(f, "failed to read signal fd: {:?}", e), Error::SpawnVcpu(ref e) => write!(f, "error spawning vcpu thread: {}", e), - + Error::TapOpen(ref e) => write!(f, "error opening tap device: {:?}", e), + Error::TapSetIp(ref e) => write!(f, "error setting tap ip: {:?}", e), + Error::TapSetNetmask(ref e) => write!(f, "error setting tap netmask: {:?}", e), + Error::TapSetMacAddress(ref e) => write!(f, "error setting tap mac address: {:?}", e), + Error::TapEnable(ref e) => write!(f, "error enabling tap device: {:?}", e), } } } @@ -413,6 +423,23 @@ pub fn run_config(cfg: Config) -> Result<()> { None }; + let mut tap_opt: Option<Tap> = None; + if let Some(host_ip) = cfg.host_ip { + if let Some(netmask) = cfg.netmask { + if let Some(mac_address) = cfg.mac_address { + let tap = Tap::new(false).map_err(Error::TapOpen)?; + tap.set_ip_addr(host_ip).map_err(Error::TapSetIp)?; + tap.set_netmask(netmask) + .map_err(Error::TapSetNetmask)?; + tap.set_mac_address(mac_address) + .map_err(Error::TapSetMacAddress)?; + + tap.enable().map_err(Error::TapEnable)?; + tap_opt = Some(tap); + } + } + } + let plugin_args: Vec<&str> = cfg.params.iter().map(|s| &s[..]).collect(); let plugin_path = cfg.plugin.as_ref().unwrap().as_path(); @@ -421,7 +448,8 @@ pub fn run_config(cfg: Config) -> Result<()> { let kvm = Kvm::new().map_err(Error::CreateKvm)?; let mut vm = Vm::new(&kvm, mem).map_err(Error::CreateVm)?; vm.create_irq_chip().map_err(Error::CreateIrqChip)?; - let mut plugin = Process::new(vcpu_count, &kvm, &mut vm, plugin_path, &plugin_args, jail)?; + let mut plugin = Process::new(vcpu_count, &kvm, &mut vm, + plugin_path, &plugin_args, jail, tap_opt)?; let mut res = Ok(()); // If Some, we will exit after enough time is passed to shutdown cleanly. diff --git a/src/plugin/process.rs b/src/plugin/process.rs index d40e5f2..7450fe4 100644 --- a/src/plugin/process.rs +++ b/src/plugin/process.rs @@ -14,7 +14,10 @@ use std::process::Command; use std::sync::{Arc, Mutex, RwLock}; use std::thread::JoinHandle; -use libc::{waitpid, pid_t, EINVAL, WNOHANG, WIFEXITED, WEXITSTATUS, WTERMSIG}; +use net_util; +use net_util::Error as NetError; + +use libc::{waitpid, pid_t, EINVAL, ENODATA, ENOTTY, WNOHANG, WIFEXITED, WEXITSTATUS, WTERMSIG}; use protobuf; use protobuf::Message; @@ -55,6 +58,7 @@ pub struct Process { // Resource to sent to plugin kill_evt: EventFd, vcpu_sockets: Vec<(UnixDatagram, UnixDatagram)>, + tap: Option<net_util::Tap>, // Socket Transmission scm: Scm, @@ -77,7 +81,8 @@ impl Process { vm: &mut Vm, cmd: &Path, args: &[&str], - jail: Option<Minijail>) + jail: Option<Minijail>, + tap: Option<net_util::Tap>) -> Result<Process> { let (request_socket, child_socket) = new_seqpacket_pair().map_err(Error::CreateMainSocket)?; @@ -120,6 +125,7 @@ impl Process { per_vcpu_states, kill_evt: EventFd::new().map_err(Error::CreateEventFd)?, vcpu_sockets, + tap, scm: Scm::new(1), request_buffer: vec![0; MAX_DATAGRAM_SIZE], datagram_files: Vec::new(), @@ -382,6 +388,31 @@ impl Process { } } + fn handle_get_net_config(tap: &net_util::Tap, + config: &mut MainResponse_GetNetConfig) -> SysResult<()> { + // Log any NetError so that the cause can be found later, but extract and return the + // underlying errno for the client as well. + fn map_net_error(s: &str, e: NetError) -> SysError { + error!("failed to get {}: {:?}", s, e); + e.sys_error() + } + + let ip_addr = tap.ip_addr().map_err(|e| map_net_error("IP address", e))?; + config.set_host_ipv4_address(u32::from(ip_addr)); + + let netmask = tap.netmask().map_err(|e| map_net_error("netmask", e))?; + config.set_netmask(u32::from(netmask)); + + let result_mac_addr = config.mut_host_mac_address(); + let mac_addr_octets = tap.mac_address() + .map_err(|e| map_net_error("mac address", e))? + .octets(); + result_mac_addr.resize(mac_addr_octets.len(), 0); + result_mac_addr.clone_from_slice(&mac_addr_octets); + + Ok(()) + } + /// Handles a request on a readable socket identified by its index in the slice returned by /// `sockets`. /// @@ -525,6 +556,18 @@ impl Process { self.started = true; Ok(()) } + } else if request.has_get_net_config() { + match self.tap { + Some(ref tap) => match Self::handle_get_net_config(tap, + response.mut_get_net_config()) { + Ok(_) => { + response_fds.push(tap.as_raw_fd()); + Ok(()) + }, + Err(e) => Err(e), + }, + None => Err(SysError::new(-ENODATA)), + } } else if request.has_dirty_log() { let dirty_log_response = response.mut_dirty_log(); match self.objects.get(&request.get_dirty_log().id) { diff --git a/tests/plugin.policy b/tests/plugin.policy index 960c8e5..773ea24 100644 --- a/tests/plugin.policy +++ b/tests/plugin.policy @@ -33,6 +33,8 @@ fstat: 1 ftruncate: 1 getcwd: 1 getrlimit: 1 +# TUNGETFEATURES +ioctl: arg1 == 0x800454CF madvise: 1 memfd_create: 1 mmap: 1 diff --git a/tests/plugin_net_config.c b/tests/plugin_net_config.c new file mode 100644 index 0000000..f11fa10 --- /dev/null +++ b/tests/plugin_net_config.c @@ -0,0 +1,85 @@ +/* + * Copyright 2018 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include <arpa/inet.h> +#include <linux/if_tun.h> +#include <sys/ioctl.h> + +#include <errno.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include "crosvm.h" + +/* + * These must match the network arguments supplied to the plugin in plugins.rs. + * IPv4 addresses here are in host-native byte order. + */ +const uint32_t expected_ip = 0x64735c05; // 100.115.92.5 +const uint32_t expected_netmask = 0xfffffffc; // 255.255.255.252 +const uint8_t expected_mac[] = {0xde, 0x21, 0xe8, 0x47, 0x6b, 0x6a}; + +int main(int argc, char** argv) { + struct crosvm *crosvm; + struct crosvm_net_config net_config; + int ret = crosvm_connect(&crosvm); + + if (ret) { + fprintf(stderr, "failed to connect to crosvm: %d\n", ret); + return 1; + } + + ret = crosvm_net_get_config(crosvm, &net_config); + if (ret) { + fprintf(stderr, "failed to get crosvm net config: %d\n", ret); + return 1; + } + + if (net_config.tap_fd < 0) { + fprintf(stderr, "fd %d is < 0\n", net_config.tap_fd); + return 1; + } + + unsigned int features; + if (ioctl(net_config.tap_fd, TUNGETFEATURES, &features) < 0) { + fprintf(stderr, + "failed to read tap features: %s\n", + strerror(errno)); + return 1; + } + + if (net_config.host_ip != htonl(expected_ip)) { + char ip_addr[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, &net_config.host_ip, ip_addr, sizeof(ip_addr)); + fprintf(stderr, "ip %s != 100.115.92.5\n", ip_addr); + return 1; + } + + if (net_config.netmask != htonl(expected_netmask)) { + char netmask[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, &net_config.netmask, netmask, sizeof(netmask)); + fprintf(stderr, "netmask %s != 255.255.255.252\n", netmask); + return 1; + } + + if (memcmp(net_config.host_mac_address, + expected_mac, + sizeof(expected_mac)) != 0) { + fprintf(stderr, + "mac %02X:%02X:%02X:%02X:%02X:%02X != de:21:e8:47:6b:6a\n", + net_config.host_mac_address[0], + net_config.host_mac_address[1], + net_config.host_mac_address[2], + net_config.host_mac_address[3], + net_config.host_mac_address[4], + net_config.host_mac_address[5]); + return 1; + } + + return 0; +} diff --git a/tests/plugins.rs b/tests/plugins.rs index d036e97..e858c9a 100644 --- a/tests/plugins.rs +++ b/tests/plugins.rs @@ -83,6 +83,12 @@ fn run_plugin(bin_path: &Path, with_sandbox: bool) { cmd.args(&["run", "-c", "1", + "--host_ip", + "100.115.92.5", + "--netmask", + "255.255.255.252", + "--mac", + "de:21:e8:47:6b:6a", "--seccomp-policy-dir", "tests", "--plugin"]) @@ -241,6 +247,11 @@ fn test_vcpu_pause() { } #[test] +fn test_net_config() { + test_plugin(include_str!("plugin_net_config.c")); +} + +#[test] fn test_debugregs() { let mini_plugin = MiniPlugin { assembly_src: "org 0x1000 |