static u64 ram_size;
static u8 image_count;
+static u8 num_net_devices;
static bool virtio_rng;
static const char *kernel_cmdline;
static const char *kernel_filename;
static const char *host_mac;
static const char *script;
static const char *guest_name;
+static struct virtio_net_params *net_params;
static bool single_step;
static bool readonly_image[MAX_DISK_IMAGES];
static bool vnc;
static bool balloon;
static bool using_rootfs;
static bool custom_rootfs;
+static bool no_net;
extern bool ioport_debug;
extern int active_console;
extern int debug_iodelay;
return 0;
}
+static inline void str_to_mac(const char *str, char *mac)
+{
+ sscanf(str, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
+ mac, mac+1, mac+2, mac+3, mac+4, mac+5);
+}
+static int set_net_param(struct virtio_net_params *p, const char *param,
+ const char *val)
+{
+ if (strcmp(param, "guest_mac") == 0) {
+ str_to_mac(val, p->guest_mac);
+ } else if (strcmp(param, "mode") == 0) {
+ if (!strncmp(val, "user", 4)) {
+ int i;
+
+ for (i = 0; i < num_net_devices; i++)
+ if (net_params[i].mode == NET_MODE_USER)
+ die("Only one usermode network device allowed at a time");
+ p->mode = NET_MODE_USER;
+ } else if (!strncmp(val, "tap", 3)) {
+ p->mode = NET_MODE_TAP;
+ } else if (!strncmp(val, "none", 4)) {
+ no_net = 1;
+ return -1;
+ } else
+ die("Unkown network mode %s, please use user, tap or none", network);
+ } else if (strcmp(param, "script") == 0) {
+ p->script = strdup(val);
+ } else if (strcmp(param, "guest_ip") == 0) {
+ p->guest_ip = strdup(val);
+ } else if (strcmp(param, "host_ip") == 0) {
+ p->host_ip = strdup(val);
+ }
+
+ return 0;
+}
+
+static int netdev_parser(const struct option *opt, const char *arg, int unset)
+{
+ struct virtio_net_params p;
+ char *buf = NULL, *cmd = NULL, *cur = NULL;
+ bool on_cmd = true;
+
+ if (arg) {
+ buf = strdup(arg);
+ if (buf == NULL)
+ die("Failed allocating new net buffer");
+ cur = strtok(buf, ",=");
+ }
+
+ p = (struct virtio_net_params) {
+ .guest_ip = DEFAULT_GUEST_ADDR,
+ .host_ip = DEFAULT_HOST_ADDR,
+ .script = DEFAULT_SCRIPT,
+ .mode = NET_MODE_TAP,
+ };
+
+ str_to_mac(DEFAULT_GUEST_MAC, p.guest_mac);
+ p.guest_mac[5] += num_net_devices;
+
+ while (cur) {
+ if (on_cmd) {
+ cmd = cur;
+ } else {
+ if (set_net_param(&p, cmd, cur) < 0)
+ goto done;
+ }
+ on_cmd = !on_cmd;
+
+ cur = strtok(NULL, ",=");
+ };
+
+ num_net_devices++;
+
+ net_params = realloc(net_params, num_net_devices * sizeof(*net_params));
+ if (net_params == NULL)
+ die("Failed adding new network device");
+
+ net_params[num_net_devices - 1] = p;
+
+done:
+ free(buf);
+ return 0;
+}
+
static int shmem_parser(const struct option *opt, const char *arg, int unset)
{
const u64 default_size = SHMEM_DEFAULT_SIZE;
"Kernel command line arguments"),
OPT_GROUP("Networking options:"),
- OPT_STRING('n', "network", &network, "user, tap, none",
- "Network to use"),
- OPT_STRING('\0', "host-ip", &host_ip, "a.b.c.d",
- "Assign this address to the host side networking"),
- OPT_STRING('\0', "guest-ip", &guest_ip, "a.b.c.d",
- "Assign this address to the guest side networking"),
- OPT_STRING('\0', "host-mac", &host_mac, "aa:bb:cc:dd:ee:ff",
- "Assign this address to the host side NIC"),
- OPT_STRING('\0', "guest-mac", &guest_mac, "aa:bb:cc:dd:ee:ff",
- "Assign this address to the guest side NIC"),
- OPT_STRING('\0', "tapscript", &script, "Script path",
- "Assign a script to process created tap device"),
+ OPT_CALLBACK_DEFAULT('n', "network", NULL, "network params",
+ "Create a new guest NIC",
+ netdev_parser, NULL),
OPT_GROUP("BIOS options:"),
OPT_INTEGER('\0', "vidmode", &vidmode,
int kvm_cmd_run(int argc, const char **argv, const char *prefix)
{
- struct virtio_net_parameters net_params;
static char real_cmdline[2048], default_name[20];
struct framebuffer *fb = NULL;
unsigned int nr_online_cpus;
virtio_9p__init(kvm);
- if (strncmp(network, "none", 4)) {
- net_params.guest_ip = guest_ip;
- net_params.host_ip = host_ip;
- net_params.kvm = kvm;
- net_params.script = script;
- sscanf(guest_mac, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
- net_params.guest_mac,
- net_params.guest_mac+1,
- net_params.guest_mac+2,
- net_params.guest_mac+3,
- net_params.guest_mac+4,
- net_params.guest_mac+5);
- sscanf(host_mac, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
- net_params.host_mac,
- net_params.host_mac+1,
- net_params.host_mac+2,
- net_params.host_mac+3,
- net_params.host_mac+4,
- net_params.host_mac+5);
-
- if (!strncmp(network, "user", 4))
- net_params.mode = NET_MODE_USER;
- else if (!strncmp(network, "tap", 3))
- net_params.mode = NET_MODE_TAP;
- else
- die("Unkown network mode %s, please use -network user, tap, none", network);
+ for (i = 0; i < num_net_devices; i++) {
+ net_params[i].kvm = kvm;
+ virtio_net__init(&net_params[i]);
+ }
+
+ if (num_net_devices == 0 && no_net == 0) {
+ struct virtio_net_params net_params;
+
+ net_params = (struct virtio_net_params) {
+ .guest_ip = guest_ip,
+ .host_ip = host_ip,
+ .kvm = kvm,
+ .script = script,
+ .mode = NET_MODE_USER,
+ };
+ str_to_mac(guest_mac, net_params.guest_mac);
+ str_to_mac(host_mac, net_params.host_mac);
+
virtio_net__init(&net_params);
}
struct net_dev {
pthread_mutex_t mutex;
struct virtio_pci vpci;
+ struct list_head list;
struct virt_queue vqs[VIRTIO_NET_NUM_QUEUES];
struct virtio_net_config config;
struct uip_info info;
struct net_dev_operations *ops;
+ struct kvm *kvm;
};
-static struct net_dev ndev = {
- .mutex = PTHREAD_MUTEX_INITIALIZER,
-
- .config = {
- .status = VIRTIO_NET_S_LINK_UP,
- },
- .info = {
- .buf_nr = 20,
- }
-};
+static LIST_HEAD(ndevs);
static void *virtio_net_rx_thread(void *p)
{
struct iovec iov[VIRTIO_NET_QUEUE_SIZE];
struct virt_queue *vq;
struct kvm *kvm;
+ struct net_dev *ndev = p;
u16 out, in;
u16 head;
int len;
- kvm = p;
- vq = &ndev.vqs[VIRTIO_NET_RX_QUEUE];
+ kvm = ndev->kvm;
+ vq = &ndev->vqs[VIRTIO_NET_RX_QUEUE];
while (1) {
- mutex_lock(&ndev.io_rx_lock);
+ mutex_lock(&ndev->io_rx_lock);
if (!virt_queue__available(vq))
- pthread_cond_wait(&ndev.io_rx_cond, &ndev.io_rx_lock);
- mutex_unlock(&ndev.io_rx_lock);
+ pthread_cond_wait(&ndev->io_rx_cond, &ndev->io_rx_lock);
+ mutex_unlock(&ndev->io_rx_lock);
while (virt_queue__available(vq)) {
head = virt_queue__get_iov(vq, iov, &out, &in, kvm);
- len = ndev.ops->rx(iov, in, &ndev);
+ len = ndev->ops->rx(iov, in, ndev);
virt_queue__set_used_elem(vq, head, len);
/* We should interrupt guest right now, otherwise latency is huge. */
- virtio_pci__signal_vq(kvm, &ndev.vpci, VIRTIO_NET_RX_QUEUE);
+ virtio_pci__signal_vq(kvm, &ndev->vpci, VIRTIO_NET_RX_QUEUE);
}
}
struct iovec iov[VIRTIO_NET_QUEUE_SIZE];
struct virt_queue *vq;
struct kvm *kvm;
+ struct net_dev *ndev = p;
u16 out, in;
u16 head;
int len;
- kvm = p;
- vq = &ndev.vqs[VIRTIO_NET_TX_QUEUE];
+ kvm = ndev->kvm;
+ vq = &ndev->vqs[VIRTIO_NET_TX_QUEUE];
while (1) {
- mutex_lock(&ndev.io_tx_lock);
+ mutex_lock(&ndev->io_tx_lock);
if (!virt_queue__available(vq))
- pthread_cond_wait(&ndev.io_tx_cond, &ndev.io_tx_lock);
- mutex_unlock(&ndev.io_tx_lock);
+ pthread_cond_wait(&ndev->io_tx_cond, &ndev->io_tx_lock);
+ mutex_unlock(&ndev->io_tx_lock);
while (virt_queue__available(vq)) {
head = virt_queue__get_iov(vq, iov, &out, &in, kvm);
- len = ndev.ops->tx(iov, out, &ndev);
+ len = ndev->ops->tx(iov, out, ndev);
virt_queue__set_used_elem(vq, head, len);
}
- virtio_pci__signal_vq(kvm, &ndev.vpci, VIRTIO_NET_TX_QUEUE);
+ virtio_pci__signal_vq(kvm, &ndev->vpci, VIRTIO_NET_TX_QUEUE);
}
pthread_exit(NULL);
}
-static void virtio_net_handle_callback(struct kvm *kvm, u16 queue_index)
+static void virtio_net_handle_callback(struct kvm *kvm, struct net_dev *ndev, int queue)
{
- switch (queue_index) {
+ switch (queue) {
case VIRTIO_NET_TX_QUEUE:
- mutex_lock(&ndev.io_tx_lock);
- pthread_cond_signal(&ndev.io_tx_cond);
- mutex_unlock(&ndev.io_tx_lock);
+ mutex_lock(&ndev->io_tx_lock);
+ pthread_cond_signal(&ndev->io_tx_cond);
+ mutex_unlock(&ndev->io_tx_lock);
break;
case VIRTIO_NET_RX_QUEUE:
- mutex_lock(&ndev.io_rx_lock);
- pthread_cond_signal(&ndev.io_rx_cond);
- mutex_unlock(&ndev.io_rx_lock);
+ mutex_lock(&ndev->io_rx_lock);
+ pthread_cond_signal(&ndev->io_rx_cond);
+ mutex_unlock(&ndev->io_rx_lock);
break;
default:
- pr_warning("Unknown queue index %u", queue_index);
+ pr_warning("Unknown queue index %u", queue);
}
}
-static bool virtio_net__tap_init(const struct virtio_net_parameters *params)
+static bool virtio_net__tap_init(const struct virtio_net_params *params,
+ struct net_dev *ndev)
{
int sock = socket(AF_INET, SOCK_STREAM, 0);
int pid, status, offload, hdr_len;
struct sockaddr_in sin = {0};
struct ifreq ifr;
- ndev.tap_fd = open("/dev/net/tun", O_RDWR);
- if (ndev.tap_fd < 0) {
+ ndev->tap_fd = open("/dev/net/tun", O_RDWR);
+ if (ndev->tap_fd < 0) {
pr_warning("Unable to open /dev/net/tun");
goto fail;
}
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_flags = IFF_TAP | IFF_NO_PI | IFF_VNET_HDR;
- if (ioctl(ndev.tap_fd, TUNSETIFF, &ifr) < 0) {
+ if (ioctl(ndev->tap_fd, TUNSETIFF, &ifr) < 0) {
pr_warning("Config tap device error. Are you root?");
goto fail;
}
- strncpy(ndev.tap_name, ifr.ifr_name, sizeof(ndev.tap_name));
+ strncpy(ndev->tap_name, ifr.ifr_name, sizeof(ndev->tap_name));
- if (ioctl(ndev.tap_fd, TUNSETNOCSUM, 1) < 0) {
+ if (ioctl(ndev->tap_fd, TUNSETNOCSUM, 1) < 0) {
pr_warning("Config tap device TUNSETNOCSUM error");
goto fail;
}
hdr_len = sizeof(struct virtio_net_hdr);
- if (ioctl(ndev.tap_fd, TUNSETVNETHDRSZ, &hdr_len) < 0) {
+ if (ioctl(ndev->tap_fd, TUNSETVNETHDRSZ, &hdr_len) < 0)
pr_warning("Config tap device TUNSETVNETHDRSZ error");
- }
offload = TUN_F_CSUM | TUN_F_TSO4 | TUN_F_TSO6 | TUN_F_UFO;
- if (ioctl(ndev.tap_fd, TUNSETOFFLOAD, offload) < 0) {
+ if (ioctl(ndev->tap_fd, TUNSETOFFLOAD, offload) < 0) {
pr_warning("Config tap device TUNSETOFFLOAD error");
goto fail;
}
if (strcmp(params->script, "none")) {
pid = fork();
if (pid == 0) {
- execl(params->script, params->script, ndev.tap_name, NULL);
+ execl(params->script, params->script, ndev->tap_name, NULL);
_exit(1);
} else {
waitpid(pid, &status, 0);
}
} else {
memset(&ifr, 0, sizeof(ifr));
- strncpy(ifr.ifr_name, ndev.tap_name, sizeof(ndev.tap_name));
+ strncpy(ifr.ifr_name, ndev->tap_name, sizeof(ndev->tap_name));
sin.sin_addr.s_addr = inet_addr(params->host_ip);
memcpy(&(ifr.ifr_addr), &sin, sizeof(ifr.ifr_addr));
ifr.ifr_addr.sa_family = AF_INET;
}
memset(&ifr, 0, sizeof(ifr));
- strncpy(ifr.ifr_name, ndev.tap_name, sizeof(ndev.tap_name));
+ strncpy(ifr.ifr_name, ndev->tap_name, sizeof(ndev->tap_name));
ioctl(sock, SIOCGIFFLAGS, &ifr);
ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
if (ioctl(sock, SIOCSIFFLAGS, &ifr) < 0)
fail:
if (sock >= 0)
close(sock);
- if (ndev.tap_fd >= 0)
- close(ndev.tap_fd);
+ if (ndev->tap_fd >= 0)
+ close(ndev->tap_fd);
return 0;
}
-static void virtio_net__io_thread_init(struct kvm *kvm)
+static void virtio_net__io_thread_init(struct kvm *kvm, struct net_dev *ndev)
{
- pthread_mutex_init(&ndev.io_rx_lock, NULL);
- pthread_cond_init(&ndev.io_tx_cond, NULL);
+ pthread_mutex_init(&ndev->io_rx_lock, NULL);
+ pthread_cond_init(&ndev->io_tx_cond, NULL);
- pthread_mutex_init(&ndev.io_rx_lock, NULL);
- pthread_cond_init(&ndev.io_tx_cond, NULL);
+ pthread_mutex_init(&ndev->io_rx_lock, NULL);
+ pthread_cond_init(&ndev->io_tx_cond, NULL);
- pthread_create(&ndev.io_rx_thread, NULL, virtio_net_rx_thread, (void *)kvm);
- pthread_create(&ndev.io_tx_thread, NULL, virtio_net_tx_thread, (void *)kvm);
+ pthread_create(&ndev->io_rx_thread, NULL, virtio_net_rx_thread, ndev);
+ pthread_create(&ndev->io_tx_thread, NULL, virtio_net_tx_thread, ndev);
}
static inline int tap_ops_tx(struct iovec *iov, u16 out, struct net_dev *ndev)
static int notify_vq(struct kvm *kvm, void *dev, u32 vq)
{
- virtio_net_handle_callback(kvm, vq);
+ struct net_dev *ndev = dev;
+
+ virtio_net_handle_callback(kvm, ndev, vq);
return 0;
}
return VIRTIO_NET_QUEUE_SIZE;
}
-void virtio_net__init(const struct virtio_net_parameters *params)
+void virtio_net__init(const struct virtio_net_params *params)
{
int i;
+ struct net_dev *ndev;
+
+ if (!params)
+ return;
+
+ ndev = calloc(1, sizeof(struct net_dev));
+ if (ndev == NULL)
+ die("Failed allocating ndev");
+
+ list_add_tail(&ndev->list, &ndevs);
+
+ ndev->kvm = params->kvm;
+
+ mutex_init(&ndev->mutex);
+ ndev->config.status = VIRTIO_NET_S_LINK_UP;
for (i = 0 ; i < 6 ; i++) {
- ndev.config.mac[i] = params->guest_mac[i];
- ndev.info.guest_mac.addr[i] = params->guest_mac[i];
- ndev.info.host_mac.addr[i] = params->host_mac[i];
+ ndev->config.mac[i] = params->guest_mac[i];
+ ndev->info.guest_mac.addr[i] = params->guest_mac[i];
+ ndev->info.host_mac.addr[i] = params->host_mac[i];
}
- ndev.mode = params->mode;
- if (ndev.mode == NET_MODE_TAP) {
- virtio_net__tap_init(params);
- ndev.ops = &tap_ops;
+ ndev->mode = params->mode;
+ if (ndev->mode == NET_MODE_TAP) {
+ if (!virtio_net__tap_init(params, ndev))
+ die_perror("You have requested a TAP device, but creation of one has"
+ "failed because:");
+ ndev->ops = &tap_ops;
} else {
- ndev.info.host_ip = ntohl(inet_addr(params->host_ip));
- ndev.info.guest_ip = ntohl(inet_addr(params->guest_ip));
- ndev.info.guest_netmask = ntohl(inet_addr("255.255.255.0"));
- uip_init(&ndev.info);
- ndev.ops = &uip_ops;
+ ndev->info.host_ip = ntohl(inet_addr(params->host_ip));
+ ndev->info.guest_ip = ntohl(inet_addr(params->guest_ip));
+ ndev->info.guest_netmask = ntohl(inet_addr("255.255.255.0"));
+ ndev->info.buf_nr = 20,
+ uip_init(&ndev->info);
+ ndev->ops = &uip_ops;
}
- virtio_pci__init(kvm, &ndev.vpci, &ndev, PCI_DEVICE_ID_VIRTIO_NET, VIRTIO_ID_NET);
- ndev.vpci.ops = (struct virtio_pci_ops) {
+ virtio_pci__init(kvm, &ndev->vpci, ndev, PCI_DEVICE_ID_VIRTIO_NET, VIRTIO_ID_NET);
+ ndev->vpci.ops = (struct virtio_pci_ops) {
.set_config = set_config,
.get_config = get_config,
.get_host_features = get_host_features,
.get_size_vq = get_size_vq,
};
- virtio_net__io_thread_init(params->kvm);
+ virtio_net__io_thread_init(params->kvm, ndev);
- ndev.compat_id = compat__add_message("virtio-net device was not detected",
+ ndev->compat_id = compat__add_message("virtio-net device was not detected",
"While you have requested a virtio-net device, "
"the guest kernel didn't seem to detect it.\n"
"Please make sure that the kernel was compiled "