summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock10
-rw-r--r--crosvm_plugin/Cargo.toml2
-rw-r--r--crosvm_plugin/crosvm.h30
-rw-r--r--crosvm_plugin/src/lib.rs56
-rw-r--r--plugin_proto/Cargo.toml2
-rw-r--r--plugin_proto/protos/plugin.proto40
-rw-r--r--src/plugin/mod.rs34
-rw-r--r--src/plugin/process.rs47
-rw-r--r--tests/plugin.policy2
-rw-r--r--tests/plugin_net_config.c85
-rw-r--r--tests/plugins.rs11
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