2008-10-31 22:10:00 +03:00
|
|
|
/*
|
|
|
|
* QEMU System Emulator
|
|
|
|
*
|
|
|
|
* Copyright (c) 2003-2008 Fabrice Bellard
|
|
|
|
*
|
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
|
|
* in the Software without restriction, including without limitation the rights
|
|
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
|
|
* furnished to do so, subject to the following conditions:
|
|
|
|
*
|
|
|
|
* The above copyright notice and this permission notice shall be included in
|
|
|
|
* all copies or substantial portions of the Software.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
|
|
* THE SOFTWARE.
|
|
|
|
*/
|
2009-03-07 19:52:02 +03:00
|
|
|
#include "config-host.h"
|
|
|
|
|
2012-10-24 10:43:34 +04:00
|
|
|
#include "net/net.h"
|
2012-10-24 13:27:28 +04:00
|
|
|
#include "clients.h"
|
|
|
|
#include "hub.h"
|
2012-10-24 10:43:34 +04:00
|
|
|
#include "net/slirp.h"
|
2013-10-21 12:08:44 +04:00
|
|
|
#include "net/eth.h"
|
2012-10-24 13:27:28 +04:00
|
|
|
#include "util.h"
|
2012-09-17 20:43:51 +04:00
|
|
|
|
2012-12-17 21:19:49 +04:00
|
|
|
#include "monitor/monitor.h"
|
2009-11-25 21:48:58 +03:00
|
|
|
#include "qemu-common.h"
|
2015-03-17 19:22:46 +03:00
|
|
|
#include "qapi/qmp/qerror.h"
|
2015-03-17 20:29:20 +03:00
|
|
|
#include "qemu/error-report.h"
|
2012-12-17 21:20:00 +04:00
|
|
|
#include "qemu/sockets.h"
|
|
|
|
#include "qemu/config-file.h"
|
2011-11-23 19:11:55 +04:00
|
|
|
#include "qmp-commands.h"
|
2010-02-25 14:54:43 +03:00
|
|
|
#include "hw/qdev.h"
|
2012-12-17 21:20:00 +04:00
|
|
|
#include "qemu/iov.h"
|
2013-08-21 19:02:47 +04:00
|
|
|
#include "qemu/main-loop.h"
|
2012-07-17 18:17:13 +04:00
|
|
|
#include "qapi-visit.h"
|
|
|
|
#include "qapi/opts-visitor.h"
|
2012-12-17 21:19:43 +04:00
|
|
|
#include "qapi/dealloc-visitor.h"
|
2014-08-26 12:06:17 +04:00
|
|
|
#include "sysemu/sysemu.h"
|
2015-10-07 06:52:14 +03:00
|
|
|
#include "net/filter.h"
|
2009-03-07 18:32:56 +03:00
|
|
|
|
2012-02-04 12:24:46 +04:00
|
|
|
/* Net bridge is currently not supported for W32. */
|
|
|
|
#if !defined(_WIN32)
|
|
|
|
# define CONFIG_NET_BRIDGE
|
|
|
|
#endif
|
|
|
|
|
2014-09-04 12:39:13 +04:00
|
|
|
static VMChangeStateEntry *net_change_state_entry;
|
2012-07-24 19:35:13 +04:00
|
|
|
static QTAILQ_HEAD(, NetClientState) net_clients;
|
2008-10-31 22:10:00 +03:00
|
|
|
|
2014-05-28 02:39:33 +04:00
|
|
|
const char *host_net_devices[] = {
|
|
|
|
"tap",
|
|
|
|
"socket",
|
|
|
|
"dump",
|
|
|
|
#ifdef CONFIG_NET_BRIDGE
|
|
|
|
"bridge",
|
|
|
|
#endif
|
2015-05-27 19:16:48 +03:00
|
|
|
#ifdef CONFIG_NETMAP
|
|
|
|
"netmap",
|
|
|
|
#endif
|
2014-05-28 02:39:33 +04:00
|
|
|
#ifdef CONFIG_SLIRP
|
|
|
|
"user",
|
|
|
|
#endif
|
|
|
|
#ifdef CONFIG_VDE
|
|
|
|
"vde",
|
|
|
|
#endif
|
2014-06-10 14:02:16 +04:00
|
|
|
"vhost-user",
|
2014-05-28 02:39:33 +04:00
|
|
|
NULL,
|
|
|
|
};
|
|
|
|
|
2009-12-08 15:11:47 +03:00
|
|
|
int default_net = 1;
|
|
|
|
|
2008-10-31 22:10:00 +03:00
|
|
|
/***********************************************************/
|
|
|
|
/* network device redirectors */
|
|
|
|
|
2009-11-25 21:48:54 +03:00
|
|
|
#if defined(DEBUG_NET)
|
2008-10-31 22:10:00 +03:00
|
|
|
static void hex_dump(FILE *f, const uint8_t *buf, int size)
|
|
|
|
{
|
|
|
|
int len, i, j, c;
|
|
|
|
|
|
|
|
for(i=0;i<size;i+=16) {
|
|
|
|
len = size - i;
|
|
|
|
if (len > 16)
|
|
|
|
len = 16;
|
|
|
|
fprintf(f, "%08x ", i);
|
|
|
|
for(j=0;j<16;j++) {
|
|
|
|
if (j < len)
|
|
|
|
fprintf(f, " %02x", buf[i+j]);
|
|
|
|
else
|
|
|
|
fprintf(f, " ");
|
|
|
|
}
|
|
|
|
fprintf(f, " ");
|
|
|
|
for(j=0;j<len;j++) {
|
|
|
|
c = buf[i+j];
|
|
|
|
if (c < ' ' || c > '~')
|
|
|
|
c = '.';
|
|
|
|
fprintf(f, "%c", c);
|
|
|
|
}
|
|
|
|
fprintf(f, "\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static int get_str_sep(char *buf, int buf_size, const char **pp, int sep)
|
|
|
|
{
|
|
|
|
const char *p, *p1;
|
|
|
|
int len;
|
|
|
|
p = *pp;
|
|
|
|
p1 = strchr(p, sep);
|
|
|
|
if (!p1)
|
|
|
|
return -1;
|
|
|
|
len = p1 - p;
|
|
|
|
p1++;
|
|
|
|
if (buf_size > 0) {
|
|
|
|
if (len > buf_size - 1)
|
|
|
|
len = buf_size - 1;
|
|
|
|
memcpy(buf, p, len);
|
|
|
|
buf[len] = '\0';
|
|
|
|
}
|
|
|
|
*pp = p1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int parse_host_port(struct sockaddr_in *saddr, const char *str)
|
|
|
|
{
|
|
|
|
char buf[512];
|
|
|
|
struct hostent *he;
|
|
|
|
const char *p, *r;
|
|
|
|
int port;
|
|
|
|
|
|
|
|
p = str;
|
|
|
|
if (get_str_sep(buf, sizeof(buf), &p, ':') < 0)
|
|
|
|
return -1;
|
|
|
|
saddr->sin_family = AF_INET;
|
|
|
|
if (buf[0] == '\0') {
|
|
|
|
saddr->sin_addr.s_addr = 0;
|
|
|
|
} else {
|
2008-11-16 16:53:32 +03:00
|
|
|
if (qemu_isdigit(buf[0])) {
|
2008-10-31 22:10:00 +03:00
|
|
|
if (!inet_aton(buf, &saddr->sin_addr))
|
|
|
|
return -1;
|
|
|
|
} else {
|
|
|
|
if ((he = gethostbyname(buf)) == NULL)
|
|
|
|
return - 1;
|
|
|
|
saddr->sin_addr = *(struct in_addr *)he->h_addr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
port = strtol(p, (char **)&r, 0);
|
|
|
|
if (r == p)
|
|
|
|
return -1;
|
|
|
|
saddr->sin_port = htons(port);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-03-14 07:09:25 +03:00
|
|
|
char *qemu_mac_strdup_printf(const uint8_t *macaddr)
|
|
|
|
{
|
|
|
|
return g_strdup_printf("%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",
|
|
|
|
macaddr[0], macaddr[1], macaddr[2],
|
|
|
|
macaddr[3], macaddr[4], macaddr[5]);
|
|
|
|
}
|
|
|
|
|
2012-07-24 19:35:14 +04:00
|
|
|
void qemu_format_nic_info_str(NetClientState *nc, uint8_t macaddr[6])
|
2009-01-07 20:46:21 +03:00
|
|
|
{
|
2012-07-24 19:35:14 +04:00
|
|
|
snprintf(nc->info_str, sizeof(nc->info_str),
|
2009-01-08 22:01:37 +03:00
|
|
|
"model=%s,macaddr=%02x:%02x:%02x:%02x:%02x:%02x",
|
2012-07-24 19:35:14 +04:00
|
|
|
nc->model,
|
2009-01-07 20:46:21 +03:00
|
|
|
macaddr[0], macaddr[1], macaddr[2],
|
|
|
|
macaddr[3], macaddr[4], macaddr[5]);
|
|
|
|
}
|
|
|
|
|
2015-05-21 12:44:48 +03:00
|
|
|
static int mac_table[256] = {0};
|
|
|
|
|
|
|
|
static void qemu_macaddr_set_used(MACAddr *macaddr)
|
|
|
|
{
|
|
|
|
int index;
|
|
|
|
|
|
|
|
for (index = 0x56; index < 0xFF; index++) {
|
|
|
|
if (macaddr->a[5] == index) {
|
|
|
|
mac_table[index]++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void qemu_macaddr_set_free(MACAddr *macaddr)
|
|
|
|
{
|
|
|
|
int index;
|
|
|
|
static const MACAddr base = { .a = { 0x52, 0x54, 0x00, 0x12, 0x34, 0 } };
|
|
|
|
|
|
|
|
if (memcmp(macaddr->a, &base.a, (sizeof(base.a) - 1)) != 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
for (index = 0x56; index < 0xFF; index++) {
|
|
|
|
if (macaddr->a[5] == index) {
|
|
|
|
mac_table[index]--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int qemu_macaddr_get_free(void)
|
|
|
|
{
|
|
|
|
int index;
|
|
|
|
|
|
|
|
for (index = 0x56; index < 0xFF; index++) {
|
|
|
|
if (mac_table[index] == 0) {
|
|
|
|
return index;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2009-10-21 17:25:22 +04:00
|
|
|
void qemu_macaddr_default_if_unset(MACAddr *macaddr)
|
|
|
|
{
|
|
|
|
static const MACAddr zero = { .a = { 0,0,0,0,0,0 } };
|
2015-05-21 12:44:48 +03:00
|
|
|
static const MACAddr base = { .a = { 0x52, 0x54, 0x00, 0x12, 0x34, 0 } };
|
|
|
|
|
|
|
|
if (memcmp(macaddr, &zero, sizeof(zero)) != 0) {
|
|
|
|
if (memcmp(macaddr->a, &base.a, (sizeof(base.a) - 1)) != 0) {
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
qemu_macaddr_set_used(macaddr);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2009-10-21 17:25:22 +04:00
|
|
|
|
|
|
|
macaddr->a[0] = 0x52;
|
|
|
|
macaddr->a[1] = 0x54;
|
|
|
|
macaddr->a[2] = 0x00;
|
|
|
|
macaddr->a[3] = 0x12;
|
|
|
|
macaddr->a[4] = 0x34;
|
2015-05-21 12:44:48 +03:00
|
|
|
macaddr->a[5] = qemu_macaddr_get_free();
|
|
|
|
qemu_macaddr_set_used(macaddr);
|
2009-10-21 17:25:22 +04:00
|
|
|
}
|
|
|
|
|
2012-07-24 19:35:05 +04:00
|
|
|
/**
|
|
|
|
* Generate a name for net client
|
|
|
|
*
|
net: make network client name unique
assign_name() creates a name MODEL.NUM, where MODEL is the client's model,
and NUM is the number of MODELs that already exist.
Markus added NIC naming for non-VLAN clients in commit 53e51d85.
commit d33d93b2 incorrectly added a judgement of net-hub. It caused
net clients created with -netdev get same names.
eg:
# qemu-upstream -device virtio-net-pci,netdev=h1 -netdev tap,id=h1 \
-device virtio-net-pci,netdev=h2 -netdev tap,id=h2 ..
(qemu) info network
virtio-net-pci.0: index=0,type=nic,model=virtio-net-pci,macaddr=52:54:00:12:34:56
\ h1: index=0,type=tap,ifname=tap0,script=/etc/qemu-ifup,downscript=/etc/qemu-ifdown
virtio-net-pci.0: index=0,type=nic,model=virtio-net-pci,macaddr=52:54:00:12:34:57
\ h2: index=0,type=tap,ifname=tap1,script=/etc/qemu-ifup,downscript=/etc/qemu-ifdown
This patch removed the check of nic-hub, and created unique names for
all net clients that have same model.
v2: update commitlog & comments
Signed-off-by: Amos Kong <akong@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
2013-04-15 14:55:19 +04:00
|
|
|
* Only net clients created with the legacy -net option and NICs need this.
|
2012-07-24 19:35:05 +04:00
|
|
|
*/
|
2012-07-24 19:35:14 +04:00
|
|
|
static char *assign_name(NetClientState *nc1, const char *model)
|
2009-01-07 20:43:44 +03:00
|
|
|
{
|
2012-07-24 19:35:14 +04:00
|
|
|
NetClientState *nc;
|
2009-01-07 20:43:44 +03:00
|
|
|
int id = 0;
|
|
|
|
|
2012-07-24 19:35:14 +04:00
|
|
|
QTAILQ_FOREACH(nc, &net_clients, next) {
|
|
|
|
if (nc == nc1) {
|
2012-07-24 19:35:05 +04:00
|
|
|
continue;
|
2009-10-08 22:58:23 +04:00
|
|
|
}
|
net: make network client name unique
assign_name() creates a name MODEL.NUM, where MODEL is the client's model,
and NUM is the number of MODELs that already exist.
Markus added NIC naming for non-VLAN clients in commit 53e51d85.
commit d33d93b2 incorrectly added a judgement of net-hub. It caused
net clients created with -netdev get same names.
eg:
# qemu-upstream -device virtio-net-pci,netdev=h1 -netdev tap,id=h1 \
-device virtio-net-pci,netdev=h2 -netdev tap,id=h2 ..
(qemu) info network
virtio-net-pci.0: index=0,type=nic,model=virtio-net-pci,macaddr=52:54:00:12:34:56
\ h1: index=0,type=tap,ifname=tap0,script=/etc/qemu-ifup,downscript=/etc/qemu-ifdown
virtio-net-pci.0: index=0,type=nic,model=virtio-net-pci,macaddr=52:54:00:12:34:57
\ h2: index=0,type=tap,ifname=tap1,script=/etc/qemu-ifup,downscript=/etc/qemu-ifdown
This patch removed the check of nic-hub, and created unique names for
all net clients that have same model.
v2: update commitlog & comments
Signed-off-by: Amos Kong <akong@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
2013-04-15 14:55:19 +04:00
|
|
|
if (strcmp(nc->model, model) == 0) {
|
Fix automatically assigned network names for netdev
If a network client doesn't have a name, we make one up, with
assign_name(). assign_name() creates a name MODEL.NUM, where MODEL is
the client's model, and NUM is the number of MODELs that already
exist.
Bug: it misses clients that are not on a VLAN, i.e. netdevs and the
NICs using them:
$ qemu-system-x86_64 -nodefaults -vnc :0 -S -monitor stdio -netdev user,id=hostnet0 -net nic,netdev=hostnet0 -netdev user,id=hostnet1 -net nic,netdev=hostnet1
QEMU 0.14.50 monitor - type 'help' for more information
(qemu) info network
Devices not on any VLAN:
hostnet0: net=10.0.2.0, restricted=n peer=e1000.0
hostnet1: net=10.0.2.0, restricted=n peer=e1000.0
e1000.0: model=e1000,macaddr=52:54:00:12:34:56 peer=hostnet0
e1000.0: model=e1000,macaddr=52:54:00:12:34:57 peer=hostnet1
Fix that.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
2011-06-16 20:45:36 +04:00
|
|
|
id++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-09 22:34:27 +04:00
|
|
|
return g_strdup_printf("%s.%d", model, id);
|
2009-01-07 20:43:44 +03:00
|
|
|
}
|
|
|
|
|
2013-01-30 15:12:27 +04:00
|
|
|
static void qemu_net_client_destructor(NetClientState *nc)
|
|
|
|
{
|
|
|
|
g_free(nc);
|
|
|
|
}
|
|
|
|
|
2013-01-30 15:12:26 +04:00
|
|
|
static void qemu_net_client_setup(NetClientState *nc,
|
|
|
|
NetClientInfo *info,
|
|
|
|
NetClientState *peer,
|
|
|
|
const char *model,
|
2013-01-30 15:12:27 +04:00
|
|
|
const char *name,
|
|
|
|
NetClientDestructor *destructor)
|
2008-10-31 22:10:00 +03:00
|
|
|
{
|
2012-07-24 19:35:14 +04:00
|
|
|
nc->info = info;
|
|
|
|
nc->model = g_strdup(model);
|
2009-11-25 21:49:02 +03:00
|
|
|
if (name) {
|
2012-07-24 19:35:14 +04:00
|
|
|
nc->name = g_strdup(name);
|
2009-11-25 21:49:02 +03:00
|
|
|
} else {
|
2012-07-24 19:35:14 +04:00
|
|
|
nc->name = assign_name(nc, model);
|
2009-11-25 21:49:02 +03:00
|
|
|
}
|
2009-10-08 22:58:23 +04:00
|
|
|
|
2012-07-24 19:35:08 +04:00
|
|
|
if (peer) {
|
|
|
|
assert(!peer->peer);
|
2012-07-24 19:35:14 +04:00
|
|
|
nc->peer = peer;
|
|
|
|
peer->peer = nc;
|
2009-10-08 22:58:24 +04:00
|
|
|
}
|
2012-07-24 19:35:14 +04:00
|
|
|
QTAILQ_INSERT_TAIL(&net_clients, nc, next);
|
2008-10-31 22:10:00 +03:00
|
|
|
|
2015-10-07 06:52:17 +03:00
|
|
|
nc->incoming_queue = qemu_new_net_queue(qemu_deliver_packet_iov, nc);
|
2013-01-30 15:12:27 +04:00
|
|
|
nc->destructor = destructor;
|
2015-10-07 06:52:14 +03:00
|
|
|
QTAILQ_INIT(&nc->filters);
|
2013-01-30 15:12:26 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
NetClientState *qemu_new_net_client(NetClientInfo *info,
|
|
|
|
NetClientState *peer,
|
|
|
|
const char *model,
|
|
|
|
const char *name)
|
|
|
|
{
|
|
|
|
NetClientState *nc;
|
|
|
|
|
|
|
|
assert(info->size >= sizeof(NetClientState));
|
|
|
|
|
|
|
|
nc = g_malloc0(info->size);
|
2013-01-30 15:12:27 +04:00
|
|
|
qemu_net_client_setup(nc, info, peer, model, name,
|
|
|
|
qemu_net_client_destructor);
|
2013-01-30 15:12:26 +04:00
|
|
|
|
2012-07-24 19:35:14 +04:00
|
|
|
return nc;
|
2008-10-31 22:10:00 +03:00
|
|
|
}
|
|
|
|
|
2009-11-25 21:49:10 +03:00
|
|
|
NICState *qemu_new_nic(NetClientInfo *info,
|
|
|
|
NICConf *conf,
|
|
|
|
const char *model,
|
|
|
|
const char *name,
|
|
|
|
void *opaque)
|
|
|
|
{
|
2013-01-30 15:12:28 +04:00
|
|
|
NetClientState **peers = conf->peers.ncs;
|
2009-11-25 21:49:10 +03:00
|
|
|
NICState *nic;
|
2014-05-26 14:04:08 +04:00
|
|
|
int i, queues = MAX(1, conf->peers.queues);
|
2009-11-25 21:49:10 +03:00
|
|
|
|
2012-07-17 18:17:12 +04:00
|
|
|
assert(info->type == NET_CLIENT_OPTIONS_KIND_NIC);
|
2009-11-25 21:49:10 +03:00
|
|
|
assert(info->size >= sizeof(NICState));
|
|
|
|
|
2013-02-22 19:15:06 +04:00
|
|
|
nic = g_malloc0(info->size + sizeof(NetClientState) * queues);
|
|
|
|
nic->ncs = (void *)nic + info->size;
|
2009-11-25 21:49:10 +03:00
|
|
|
nic->conf = conf;
|
|
|
|
nic->opaque = opaque;
|
|
|
|
|
2013-02-22 19:15:06 +04:00
|
|
|
for (i = 0; i < queues; i++) {
|
|
|
|
qemu_net_client_setup(&nic->ncs[i], info, peers[i], model, name,
|
2013-01-30 15:12:28 +04:00
|
|
|
NULL);
|
|
|
|
nic->ncs[i].queue_index = i;
|
|
|
|
}
|
|
|
|
|
2009-11-25 21:49:10 +03:00
|
|
|
return nic;
|
|
|
|
}
|
|
|
|
|
2013-01-30 15:12:28 +04:00
|
|
|
NetClientState *qemu_get_subqueue(NICState *nic, int queue_index)
|
|
|
|
{
|
2013-02-22 19:15:06 +04:00
|
|
|
return nic->ncs + queue_index;
|
2013-01-30 15:12:28 +04:00
|
|
|
}
|
|
|
|
|
2013-01-30 15:12:22 +04:00
|
|
|
NetClientState *qemu_get_queue(NICState *nic)
|
|
|
|
{
|
2013-01-30 15:12:28 +04:00
|
|
|
return qemu_get_subqueue(nic, 0);
|
2013-01-30 15:12:22 +04:00
|
|
|
}
|
|
|
|
|
2013-01-30 15:12:23 +04:00
|
|
|
NICState *qemu_get_nic(NetClientState *nc)
|
|
|
|
{
|
2013-01-30 15:12:28 +04:00
|
|
|
NetClientState *nc0 = nc - nc->queue_index;
|
|
|
|
|
2013-02-22 19:15:06 +04:00
|
|
|
return (NICState *)((void *)nc0 - nc->info->size);
|
2013-01-30 15:12:23 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void *qemu_get_nic_opaque(NetClientState *nc)
|
|
|
|
{
|
|
|
|
NICState *nic = qemu_get_nic(nc);
|
|
|
|
|
|
|
|
return nic->opaque;
|
|
|
|
}
|
|
|
|
|
2012-07-24 19:35:15 +04:00
|
|
|
static void qemu_cleanup_net_client(NetClientState *nc)
|
2008-10-31 22:10:00 +03:00
|
|
|
{
|
2012-07-24 19:35:14 +04:00
|
|
|
QTAILQ_REMOVE(&net_clients, nc, next);
|
2008-10-31 22:10:00 +03:00
|
|
|
|
2013-02-13 02:16:06 +04:00
|
|
|
if (nc->info->cleanup) {
|
|
|
|
nc->info->cleanup(nc);
|
|
|
|
}
|
2010-09-20 20:08:41 +04:00
|
|
|
}
|
2009-10-08 22:58:23 +04:00
|
|
|
|
2012-07-24 19:35:15 +04:00
|
|
|
static void qemu_free_net_client(NetClientState *nc)
|
2010-09-20 20:08:41 +04:00
|
|
|
{
|
2013-08-02 23:47:08 +04:00
|
|
|
if (nc->incoming_queue) {
|
|
|
|
qemu_del_net_queue(nc->incoming_queue);
|
2012-07-24 19:35:11 +04:00
|
|
|
}
|
2012-07-24 19:35:14 +04:00
|
|
|
if (nc->peer) {
|
|
|
|
nc->peer->peer = NULL;
|
2010-09-20 20:08:41 +04:00
|
|
|
}
|
2012-07-24 19:35:14 +04:00
|
|
|
g_free(nc->name);
|
|
|
|
g_free(nc->model);
|
2013-01-30 15:12:27 +04:00
|
|
|
if (nc->destructor) {
|
|
|
|
nc->destructor(nc);
|
|
|
|
}
|
2008-10-31 22:10:00 +03:00
|
|
|
}
|
|
|
|
|
2012-07-24 19:35:15 +04:00
|
|
|
void qemu_del_net_client(NetClientState *nc)
|
2010-09-20 20:08:41 +04:00
|
|
|
{
|
2013-01-30 15:12:28 +04:00
|
|
|
NetClientState *ncs[MAX_QUEUE_NUM];
|
|
|
|
int queues, i;
|
2015-10-07 06:52:14 +03:00
|
|
|
NetFilterState *nf, *next;
|
2013-01-30 15:12:28 +04:00
|
|
|
|
2014-12-23 19:53:20 +03:00
|
|
|
assert(nc->info->type != NET_CLIENT_OPTIONS_KIND_NIC);
|
|
|
|
|
2013-01-30 15:12:28 +04:00
|
|
|
/* If the NetClientState belongs to a multiqueue backend, we will change all
|
|
|
|
* other NetClientStates also.
|
|
|
|
*/
|
|
|
|
queues = qemu_find_net_clients_except(nc->name, ncs,
|
|
|
|
NET_CLIENT_OPTIONS_KIND_NIC,
|
|
|
|
MAX_QUEUE_NUM);
|
|
|
|
assert(queues != 0);
|
|
|
|
|
2015-10-07 06:52:14 +03:00
|
|
|
QTAILQ_FOREACH_SAFE(nf, &nc->filters, next, next) {
|
|
|
|
object_unparent(OBJECT(nf));
|
|
|
|
}
|
|
|
|
|
2010-09-20 20:08:41 +04:00
|
|
|
/* If there is a peer NIC, delete and cleanup client, but do not free. */
|
2012-07-24 19:35:14 +04:00
|
|
|
if (nc->peer && nc->peer->info->type == NET_CLIENT_OPTIONS_KIND_NIC) {
|
2013-01-30 15:12:23 +04:00
|
|
|
NICState *nic = qemu_get_nic(nc->peer);
|
2010-09-20 20:08:41 +04:00
|
|
|
if (nic->peer_deleted) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
nic->peer_deleted = true;
|
2013-01-30 15:12:28 +04:00
|
|
|
|
|
|
|
for (i = 0; i < queues; i++) {
|
|
|
|
ncs[i]->peer->link_down = true;
|
|
|
|
}
|
|
|
|
|
2012-07-24 19:35:14 +04:00
|
|
|
if (nc->peer->info->link_status_changed) {
|
|
|
|
nc->peer->info->link_status_changed(nc->peer);
|
2010-09-20 20:08:41 +04:00
|
|
|
}
|
2013-01-30 15:12:28 +04:00
|
|
|
|
|
|
|
for (i = 0; i < queues; i++) {
|
|
|
|
qemu_cleanup_net_client(ncs[i]);
|
|
|
|
}
|
|
|
|
|
2010-09-20 20:08:41 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-01-30 15:12:28 +04:00
|
|
|
for (i = 0; i < queues; i++) {
|
|
|
|
qemu_cleanup_net_client(ncs[i]);
|
|
|
|
qemu_free_net_client(ncs[i]);
|
|
|
|
}
|
2013-01-30 15:12:24 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void qemu_del_nic(NICState *nic)
|
|
|
|
{
|
2014-05-26 14:04:08 +04:00
|
|
|
int i, queues = MAX(nic->conf->peers.queues, 1);
|
2013-01-30 15:12:28 +04:00
|
|
|
|
2015-05-21 12:44:48 +03:00
|
|
|
qemu_macaddr_set_free(&nic->conf->macaddr);
|
|
|
|
|
2010-09-20 20:08:41 +04:00
|
|
|
/* If this is a peer NIC and peer has already been deleted, free it now. */
|
2013-01-30 15:12:28 +04:00
|
|
|
if (nic->peer_deleted) {
|
|
|
|
for (i = 0; i < queues; i++) {
|
|
|
|
qemu_free_net_client(qemu_get_subqueue(nic, i)->peer);
|
2009-06-24 16:42:31 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-01-30 15:12:28 +04:00
|
|
|
for (i = queues - 1; i >= 0; i--) {
|
|
|
|
NetClientState *nc = qemu_get_subqueue(nic, i);
|
|
|
|
|
|
|
|
qemu_cleanup_net_client(nc);
|
|
|
|
qemu_free_net_client(nc);
|
|
|
|
}
|
2013-02-22 19:15:06 +04:00
|
|
|
|
|
|
|
g_free(nic);
|
2009-06-24 16:42:31 +04:00
|
|
|
}
|
|
|
|
|
2009-11-25 21:49:31 +03:00
|
|
|
void qemu_foreach_nic(qemu_nic_foreach func, void *opaque)
|
|
|
|
{
|
2012-07-24 19:35:13 +04:00
|
|
|
NetClientState *nc;
|
2009-11-25 21:49:31 +03:00
|
|
|
|
2012-07-24 19:35:12 +04:00
|
|
|
QTAILQ_FOREACH(nc, &net_clients, next) {
|
2012-07-17 18:17:12 +04:00
|
|
|
if (nc->info->type == NET_CLIENT_OPTIONS_KIND_NIC) {
|
2013-01-30 15:12:28 +04:00
|
|
|
if (nc->queue_index == 0) {
|
|
|
|
func(qemu_get_nic(nc), opaque);
|
|
|
|
}
|
2009-11-25 21:49:31 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-02-20 15:14:07 +04:00
|
|
|
bool qemu_has_ufo(NetClientState *nc)
|
2014-02-06 20:02:16 +04:00
|
|
|
{
|
2014-02-20 15:14:07 +04:00
|
|
|
if (!nc || !nc->info->has_ufo) {
|
2014-02-06 20:02:16 +04:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2014-02-20 15:14:07 +04:00
|
|
|
return nc->info->has_ufo(nc);
|
2014-02-06 20:02:16 +04:00
|
|
|
}
|
|
|
|
|
2014-02-20 15:14:07 +04:00
|
|
|
bool qemu_has_vnet_hdr(NetClientState *nc)
|
2014-02-06 20:02:16 +04:00
|
|
|
{
|
2014-02-20 15:14:07 +04:00
|
|
|
if (!nc || !nc->info->has_vnet_hdr) {
|
2014-02-06 20:02:16 +04:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2014-02-20 15:14:07 +04:00
|
|
|
return nc->info->has_vnet_hdr(nc);
|
2014-02-06 20:02:16 +04:00
|
|
|
}
|
|
|
|
|
2014-02-20 15:14:07 +04:00
|
|
|
bool qemu_has_vnet_hdr_len(NetClientState *nc, int len)
|
2014-02-06 20:02:16 +04:00
|
|
|
{
|
2014-02-20 15:14:07 +04:00
|
|
|
if (!nc || !nc->info->has_vnet_hdr_len) {
|
2014-02-06 20:02:16 +04:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2014-02-20 15:14:07 +04:00
|
|
|
return nc->info->has_vnet_hdr_len(nc, len);
|
2014-02-06 20:02:16 +04:00
|
|
|
}
|
|
|
|
|
2014-02-20 15:14:07 +04:00
|
|
|
void qemu_using_vnet_hdr(NetClientState *nc, bool enable)
|
2014-02-06 20:02:16 +04:00
|
|
|
{
|
2014-02-20 15:14:07 +04:00
|
|
|
if (!nc || !nc->info->using_vnet_hdr) {
|
2014-02-06 20:02:16 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-02-20 15:14:07 +04:00
|
|
|
nc->info->using_vnet_hdr(nc, enable);
|
2014-02-06 20:02:16 +04:00
|
|
|
}
|
|
|
|
|
2014-02-20 15:14:07 +04:00
|
|
|
void qemu_set_offload(NetClientState *nc, int csum, int tso4, int tso6,
|
2014-02-06 20:02:16 +04:00
|
|
|
int ecn, int ufo)
|
|
|
|
{
|
2014-02-20 15:14:07 +04:00
|
|
|
if (!nc || !nc->info->set_offload) {
|
2014-02-06 20:02:16 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-02-20 15:14:07 +04:00
|
|
|
nc->info->set_offload(nc, csum, tso4, tso6, ecn, ufo);
|
2014-02-06 20:02:16 +04:00
|
|
|
}
|
|
|
|
|
2014-02-20 15:14:07 +04:00
|
|
|
void qemu_set_vnet_hdr_len(NetClientState *nc, int len)
|
2014-02-06 20:02:16 +04:00
|
|
|
{
|
2014-02-20 15:14:07 +04:00
|
|
|
if (!nc || !nc->info->set_vnet_hdr_len) {
|
2014-02-06 20:02:16 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-02-20 15:14:07 +04:00
|
|
|
nc->info->set_vnet_hdr_len(nc, len);
|
2014-02-06 20:02:16 +04:00
|
|
|
}
|
|
|
|
|
2015-06-17 16:23:44 +03:00
|
|
|
int qemu_set_vnet_le(NetClientState *nc, bool is_le)
|
|
|
|
{
|
|
|
|
if (!nc || !nc->info->set_vnet_le) {
|
|
|
|
return -ENOSYS;
|
|
|
|
}
|
|
|
|
|
|
|
|
return nc->info->set_vnet_le(nc, is_le);
|
|
|
|
}
|
|
|
|
|
|
|
|
int qemu_set_vnet_be(NetClientState *nc, bool is_be)
|
|
|
|
{
|
|
|
|
if (!nc || !nc->info->set_vnet_be) {
|
|
|
|
return -ENOSYS;
|
|
|
|
}
|
|
|
|
|
|
|
|
return nc->info->set_vnet_be(nc, is_be);
|
|
|
|
}
|
|
|
|
|
2012-07-24 19:35:13 +04:00
|
|
|
int qemu_can_send_packet(NetClientState *sender)
|
2008-10-31 22:10:00 +03:00
|
|
|
{
|
2014-08-26 12:06:17 +04:00
|
|
|
int vm_running = runstate_is_running();
|
|
|
|
|
|
|
|
if (!vm_running) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-07-24 19:35:11 +04:00
|
|
|
if (!sender->peer) {
|
2009-10-08 22:58:24 +04:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2012-07-24 19:35:11 +04:00
|
|
|
if (sender->peer->receive_disabled) {
|
|
|
|
return 0;
|
|
|
|
} else if (sender->peer->info->can_receive &&
|
|
|
|
!sender->peer->info->can_receive(sender->peer)) {
|
|
|
|
return 0;
|
2008-10-31 22:10:00 +03:00
|
|
|
}
|
net: fix qemu_can_send_packet logic
If any of the clients is not ready to receive (ie it has a can_receive
callback and can_receive() returns false), we don't want to start
sending, else this client may miss/discard the packet.
I got this behaviour with the following setup :
the emulated machine is using an USB-ethernet adapter, it is connected
to the network using SLIRP and I'm dumping the traffic in a .pcap file.
As per the following command line :
-net nic,model=usb,vlan=1 -net user,vlan=1 -net dump,vlan=1,file=/tmp/pkt.pcap
Every time that two packets are coming in a row from the host, the
usb-net code will receive the first one, then returns 0 to can_receive
call since it has a 1 packet long queue. But as the dump code is always
ready to receive, qemu_can_send_packet will return true and the next
packet will discard the previous one in the usb-net code.
Signed-off-by: Vincent Palatin <vpalatin@chromium.org>
Signed-off-by: Blue Swirl <blauwirbel@gmail.com>
2011-03-03 01:25:02 +03:00
|
|
|
return 1;
|
2008-10-31 22:10:00 +03:00
|
|
|
}
|
|
|
|
|
2015-10-07 06:52:15 +03:00
|
|
|
static ssize_t filter_receive_iov(NetClientState *nc,
|
|
|
|
NetFilterDirection direction,
|
|
|
|
NetClientState *sender,
|
|
|
|
unsigned flags,
|
|
|
|
const struct iovec *iov,
|
|
|
|
int iovcnt,
|
|
|
|
NetPacketSent *sent_cb)
|
|
|
|
{
|
|
|
|
ssize_t ret = 0;
|
|
|
|
NetFilterState *nf = NULL;
|
|
|
|
|
|
|
|
QTAILQ_FOREACH(nf, &nc->filters, next) {
|
|
|
|
ret = qemu_netfilter_receive(nf, direction, sender, flags, iov,
|
|
|
|
iovcnt, sent_cb);
|
|
|
|
if (ret) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t filter_receive(NetClientState *nc,
|
|
|
|
NetFilterDirection direction,
|
|
|
|
NetClientState *sender,
|
|
|
|
unsigned flags,
|
|
|
|
const uint8_t *data,
|
|
|
|
size_t size,
|
|
|
|
NetPacketSent *sent_cb)
|
|
|
|
{
|
|
|
|
struct iovec iov = {
|
|
|
|
.iov_base = (void *)data,
|
|
|
|
.iov_len = size
|
|
|
|
};
|
|
|
|
|
|
|
|
return filter_receive_iov(nc, direction, sender, flags, &iov, 1, sent_cb);
|
|
|
|
}
|
|
|
|
|
2012-07-24 19:35:14 +04:00
|
|
|
void qemu_purge_queued_packets(NetClientState *nc)
|
2009-06-18 21:21:29 +04:00
|
|
|
{
|
2012-07-24 19:35:14 +04:00
|
|
|
if (!nc->peer) {
|
2009-10-08 22:58:24 +04:00
|
|
|
return;
|
2009-10-08 22:58:32 +04:00
|
|
|
}
|
2009-10-08 22:58:24 +04:00
|
|
|
|
2013-08-02 23:47:08 +04:00
|
|
|
qemu_net_queue_purge(nc->peer->incoming_queue, nc);
|
2009-06-18 21:21:29 +04:00
|
|
|
}
|
|
|
|
|
2014-09-04 12:39:13 +04:00
|
|
|
static
|
|
|
|
void qemu_flush_or_purge_queued_packets(NetClientState *nc, bool purge)
|
2009-04-29 14:48:12 +04:00
|
|
|
{
|
2012-07-24 19:35:14 +04:00
|
|
|
nc->receive_disabled = 0;
|
2009-10-08 22:58:32 +04:00
|
|
|
|
2013-02-05 20:53:31 +04:00
|
|
|
if (nc->peer && nc->peer->info->type == NET_CLIENT_OPTIONS_KIND_HUBPORT) {
|
|
|
|
if (net_hub_flush(nc->peer)) {
|
|
|
|
qemu_notify_event();
|
|
|
|
}
|
|
|
|
}
|
2013-08-02 23:47:08 +04:00
|
|
|
if (qemu_net_queue_flush(nc->incoming_queue)) {
|
2012-08-09 18:45:55 +04:00
|
|
|
/* We emptied the queue successfully, signal to the IO thread to repoll
|
|
|
|
* the file descriptor (for tap, for example).
|
|
|
|
*/
|
|
|
|
qemu_notify_event();
|
2014-09-04 12:39:13 +04:00
|
|
|
} else if (purge) {
|
|
|
|
/* Unable to empty the queue, purge remaining packets */
|
|
|
|
qemu_net_queue_purge(nc->incoming_queue, nc);
|
2012-08-09 18:45:55 +04:00
|
|
|
}
|
2009-04-29 14:48:12 +04:00
|
|
|
}
|
|
|
|
|
2014-09-04 12:39:13 +04:00
|
|
|
void qemu_flush_queued_packets(NetClientState *nc)
|
|
|
|
{
|
|
|
|
qemu_flush_or_purge_queued_packets(nc, false);
|
|
|
|
}
|
|
|
|
|
2012-07-24 19:35:13 +04:00
|
|
|
static ssize_t qemu_send_packet_async_with_flags(NetClientState *sender,
|
2009-10-22 20:43:41 +04:00
|
|
|
unsigned flags,
|
|
|
|
const uint8_t *buf, int size,
|
|
|
|
NetPacketSent *sent_cb)
|
2009-04-21 23:56:41 +04:00
|
|
|
{
|
2009-10-08 22:58:32 +04:00
|
|
|
NetQueue *queue;
|
2015-10-07 06:52:15 +03:00
|
|
|
int ret;
|
2009-01-08 22:44:06 +03:00
|
|
|
|
2008-10-31 22:10:00 +03:00
|
|
|
#ifdef DEBUG_NET
|
2009-10-08 22:58:24 +04:00
|
|
|
printf("qemu_send_packet_async:\n");
|
2008-10-31 22:10:00 +03:00
|
|
|
hex_dump(stdout, buf, size);
|
|
|
|
#endif
|
2009-04-29 15:15:26 +04:00
|
|
|
|
2012-07-24 19:35:11 +04:00
|
|
|
if (sender->link_down || !sender->peer) {
|
2009-10-08 22:58:32 +04:00
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
2015-10-07 06:52:15 +03:00
|
|
|
/* Let filters handle the packet first */
|
|
|
|
ret = filter_receive(sender, NET_FILTER_DIRECTION_TX,
|
|
|
|
sender, flags, buf, size, sent_cb);
|
|
|
|
if (ret) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = filter_receive(sender->peer, NET_FILTER_DIRECTION_RX,
|
|
|
|
sender, flags, buf, size, sent_cb);
|
|
|
|
if (ret) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-08-02 23:47:08 +04:00
|
|
|
queue = sender->peer->incoming_queue;
|
2009-10-08 22:58:32 +04:00
|
|
|
|
2009-10-22 20:43:41 +04:00
|
|
|
return qemu_net_queue_send(queue, sender, flags, buf, size, sent_cb);
|
|
|
|
}
|
|
|
|
|
2012-07-24 19:35:13 +04:00
|
|
|
ssize_t qemu_send_packet_async(NetClientState *sender,
|
2009-10-22 20:43:41 +04:00
|
|
|
const uint8_t *buf, int size,
|
|
|
|
NetPacketSent *sent_cb)
|
|
|
|
{
|
|
|
|
return qemu_send_packet_async_with_flags(sender, QEMU_NET_PACKET_FLAG_NONE,
|
|
|
|
buf, size, sent_cb);
|
2009-04-29 15:15:26 +04:00
|
|
|
}
|
|
|
|
|
2012-07-24 19:35:14 +04:00
|
|
|
void qemu_send_packet(NetClientState *nc, const uint8_t *buf, int size)
|
2009-04-29 15:15:26 +04:00
|
|
|
{
|
2012-07-24 19:35:14 +04:00
|
|
|
qemu_send_packet_async(nc, buf, size, NULL);
|
2008-10-31 22:10:00 +03:00
|
|
|
}
|
|
|
|
|
2012-07-24 19:35:14 +04:00
|
|
|
ssize_t qemu_send_packet_raw(NetClientState *nc, const uint8_t *buf, int size)
|
2009-10-22 20:43:41 +04:00
|
|
|
{
|
2012-07-24 19:35:14 +04:00
|
|
|
return qemu_send_packet_async_with_flags(nc, QEMU_NET_PACKET_FLAG_RAW,
|
2009-10-22 20:43:41 +04:00
|
|
|
buf, size, NULL);
|
|
|
|
}
|
|
|
|
|
2012-07-24 19:35:14 +04:00
|
|
|
static ssize_t nc_sendv_compat(NetClientState *nc, const struct iovec *iov,
|
2015-10-07 06:52:16 +03:00
|
|
|
int iovcnt, unsigned flags)
|
2008-12-17 22:13:11 +03:00
|
|
|
{
|
2015-10-07 06:52:16 +03:00
|
|
|
uint8_t buf[NET_BUFSIZE];
|
|
|
|
uint8_t *buffer;
|
2011-02-24 03:57:21 +03:00
|
|
|
size_t offset;
|
2008-12-17 22:13:11 +03:00
|
|
|
|
2015-10-07 06:52:16 +03:00
|
|
|
if (iovcnt == 1) {
|
|
|
|
buffer = iov[0].iov_base;
|
|
|
|
offset = iov[0].iov_len;
|
|
|
|
} else {
|
|
|
|
buffer = buf;
|
|
|
|
offset = iov_to_buf(iov, iovcnt, 0, buffer, sizeof(buffer));
|
|
|
|
}
|
2008-12-17 22:13:11 +03:00
|
|
|
|
2015-10-07 06:52:16 +03:00
|
|
|
if (flags & QEMU_NET_PACKET_FLAG_RAW && nc->info->receive_raw) {
|
|
|
|
return nc->info->receive_raw(nc, buffer, offset);
|
|
|
|
} else {
|
|
|
|
return nc->info->receive(nc, buffer, offset);
|
|
|
|
}
|
2008-12-17 22:13:11 +03:00
|
|
|
}
|
|
|
|
|
2012-07-24 19:35:17 +04:00
|
|
|
ssize_t qemu_deliver_packet_iov(NetClientState *sender,
|
|
|
|
unsigned flags,
|
|
|
|
const struct iovec *iov,
|
|
|
|
int iovcnt,
|
|
|
|
void *opaque)
|
2009-10-08 22:58:32 +04:00
|
|
|
{
|
2012-07-24 19:35:14 +04:00
|
|
|
NetClientState *nc = opaque;
|
2012-08-18 00:16:42 +04:00
|
|
|
int ret;
|
2009-10-08 22:58:32 +04:00
|
|
|
|
2012-07-24 19:35:14 +04:00
|
|
|
if (nc->link_down) {
|
2011-02-24 03:57:21 +03:00
|
|
|
return iov_size(iov, iovcnt);
|
2009-10-08 22:58:32 +04:00
|
|
|
}
|
|
|
|
|
2012-08-18 00:16:42 +04:00
|
|
|
if (nc->receive_disabled) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-07-24 19:35:14 +04:00
|
|
|
if (nc->info->receive_iov) {
|
2012-08-18 00:16:42 +04:00
|
|
|
ret = nc->info->receive_iov(nc, iov, iovcnt);
|
2009-10-08 22:58:32 +04:00
|
|
|
} else {
|
2015-10-07 06:52:16 +03:00
|
|
|
ret = nc_sendv_compat(nc, iov, iovcnt, flags);
|
2012-08-18 00:16:42 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (ret == 0) {
|
|
|
|
nc->receive_disabled = 1;
|
2009-04-29 14:48:12 +04:00
|
|
|
}
|
2012-08-18 00:16:42 +04:00
|
|
|
|
|
|
|
return ret;
|
2009-04-29 14:48:12 +04:00
|
|
|
}
|
|
|
|
|
2012-07-24 19:35:13 +04:00
|
|
|
ssize_t qemu_sendv_packet_async(NetClientState *sender,
|
2009-04-29 15:15:26 +04:00
|
|
|
const struct iovec *iov, int iovcnt,
|
|
|
|
NetPacketSent *sent_cb)
|
2009-04-29 14:48:12 +04:00
|
|
|
{
|
2009-10-08 22:58:32 +04:00
|
|
|
NetQueue *queue;
|
2015-10-07 06:52:15 +03:00
|
|
|
int ret;
|
2009-10-08 22:58:32 +04:00
|
|
|
|
2012-07-24 19:35:11 +04:00
|
|
|
if (sender->link_down || !sender->peer) {
|
2011-02-24 03:57:21 +03:00
|
|
|
return iov_size(iov, iovcnt);
|
2009-04-29 14:48:12 +04:00
|
|
|
}
|
|
|
|
|
2015-10-07 06:52:15 +03:00
|
|
|
/* Let filters handle the packet first */
|
|
|
|
ret = filter_receive_iov(sender, NET_FILTER_DIRECTION_TX, sender,
|
|
|
|
QEMU_NET_PACKET_FLAG_NONE, iov, iovcnt, sent_cb);
|
|
|
|
if (ret) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = filter_receive_iov(sender->peer, NET_FILTER_DIRECTION_RX, sender,
|
|
|
|
QEMU_NET_PACKET_FLAG_NONE, iov, iovcnt, sent_cb);
|
|
|
|
if (ret) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-08-02 23:47:08 +04:00
|
|
|
queue = sender->peer->incoming_queue;
|
2009-10-08 22:58:32 +04:00
|
|
|
|
2009-10-22 20:43:40 +04:00
|
|
|
return qemu_net_queue_send_iov(queue, sender,
|
|
|
|
QEMU_NET_PACKET_FLAG_NONE,
|
|
|
|
iov, iovcnt, sent_cb);
|
2008-12-17 22:13:11 +03:00
|
|
|
}
|
|
|
|
|
2009-04-29 15:15:26 +04:00
|
|
|
ssize_t
|
2012-07-24 19:35:14 +04:00
|
|
|
qemu_sendv_packet(NetClientState *nc, const struct iovec *iov, int iovcnt)
|
2009-04-29 15:15:26 +04:00
|
|
|
{
|
2012-07-24 19:35:14 +04:00
|
|
|
return qemu_sendv_packet_async(nc, iov, iovcnt, NULL);
|
2008-10-31 22:10:00 +03:00
|
|
|
}
|
|
|
|
|
2012-07-24 19:35:13 +04:00
|
|
|
NetClientState *qemu_find_netdev(const char *id)
|
2009-10-08 22:58:29 +04:00
|
|
|
{
|
2012-07-24 19:35:14 +04:00
|
|
|
NetClientState *nc;
|
2009-10-08 22:58:29 +04:00
|
|
|
|
2012-07-24 19:35:14 +04:00
|
|
|
QTAILQ_FOREACH(nc, &net_clients, next) {
|
|
|
|
if (nc->info->type == NET_CLIENT_OPTIONS_KIND_NIC)
|
Fix netdev name lookup in -device, device_add, netdev_del
qemu_find_netdev() looks up members of non_vlan_clients by name. It
happily returns the first match. Trouble is the names need not be
unique.
non_vlan_clients contains host parts (netdevs) and guest parts (NICs).
Netdevs have unique names: a netdev's name is a (mandatory)
qemu_netdev_opts ID, and these are unique.
NIC names are not unique. If a NIC has a qdev ID (which is unique),
that's its name. Else, we make up a name. The made-up names are
unique, but they can clash with qdev IDs. Even if NICs had unique
names, they could still clash with netdev names.
Callers of qemu_find_netdev():
* net_init_nic() wants a netdev. It happens to work because it runs
before NICs get added to non_vlan_clients.
* do_netdev_del() wants a netdev. If it gets a NIC, it complains and
fails. Bug: a netdev with the same name that comes later in
non_vlan_clients can't be deleted:
$ qemu-system-x86_64 -nodefaults -vnc :0 -S -monitor stdio -netdev user,id=hostnet0 -device virtio-net-pci,netdev=hostnet0,id=virtio1
[...]
(qemu) netdev_add user,id=virtio1
(qemu) info network
Devices not on any VLAN:
hostnet0: net=10.0.2.0, restricted=n peer=virtio1
virtio1: model=virtio-net-pci,macaddr=52:54:00:12:34:56 peer=hostnet0
virtio1: net=10.0.2.0, restricted=n
(qemu) netdev_del virtio1
Device 'virtio1' not found
* parse_netdev() wants a netdev. If it gets a NIC, it gets confused.
With the test setup above:
(qemu) device_add virtio-net-pci,netdev=virtio1
Property 'virtio-net-pci.netdev' can't take value 'virtio1', it's in use
You can even connect two NICs to each other:
$ qemu-system-x86_64 -nodefaults -vnc :0 -S -monitor stdio -device virtio-net-pci,id=virtio1 -device e1000,netdev=virtio1
[...]
Devices not on any VLAN:
virtio1: model=virtio-net-pci,macaddr=52:54:00:12:34:56 peer=e1000.0
e1000.0: model=e1000,macaddr=52:54:00:12:34:57 peer=virtio1
(qemu) q
Segmentation fault (core dumped)
* do_set_link() works fine for both netdevs and NICs. Whether it
really makes sense for netdevs is debatable, but that's outside this
patch's scope.
Change qemu_find_netdev() to return only netdevs. This fixes the
netdev_del and device_add/-device bugs demonstrated above.
To avoid changing set_link, make do_set_link() search non_vlan_clients
by hand instead of calling qemu_find_netdev().
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
2011-06-16 20:45:37 +04:00
|
|
|
continue;
|
2012-07-24 19:35:14 +04:00
|
|
|
if (!strcmp(nc->name, id)) {
|
|
|
|
return nc;
|
2009-10-08 22:58:29 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2013-01-30 15:12:25 +04:00
|
|
|
int qemu_find_net_clients_except(const char *id, NetClientState **ncs,
|
|
|
|
NetClientOptionsKind type, int max)
|
|
|
|
{
|
|
|
|
NetClientState *nc;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
QTAILQ_FOREACH(nc, &net_clients, next) {
|
|
|
|
if (nc->info->type == type) {
|
|
|
|
continue;
|
|
|
|
}
|
2014-05-08 02:41:30 +04:00
|
|
|
if (!id || !strcmp(nc->name, id)) {
|
2013-01-30 15:12:25 +04:00
|
|
|
if (ret < max) {
|
|
|
|
ncs[ret] = nc;
|
|
|
|
}
|
|
|
|
ret++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2009-02-11 18:20:03 +03:00
|
|
|
static int nic_get_free_idx(void)
|
|
|
|
{
|
|
|
|
int index;
|
|
|
|
|
|
|
|
for (index = 0; index < MAX_NICS; index++)
|
|
|
|
if (!nd_table[index].used)
|
|
|
|
return index;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2009-09-25 05:53:51 +04:00
|
|
|
int qemu_show_nic_models(const char *arg, const char *const *models)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
2012-08-02 16:45:54 +04:00
|
|
|
if (!arg || !is_help_option(arg)) {
|
2009-09-25 05:53:51 +04:00
|
|
|
return 0;
|
2012-08-02 16:45:54 +04:00
|
|
|
}
|
2009-09-25 05:53:51 +04:00
|
|
|
|
|
|
|
fprintf(stderr, "qemu: Supported NIC models: ");
|
|
|
|
for (i = 0 ; models[i]; i++)
|
|
|
|
fprintf(stderr, "%s%c", models[i], models[i+1] ? ',' : '\n');
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2009-01-13 22:03:57 +03:00
|
|
|
void qemu_check_nic_model(NICInfo *nd, const char *model)
|
|
|
|
{
|
|
|
|
const char *models[2];
|
|
|
|
|
|
|
|
models[0] = model;
|
|
|
|
models[1] = NULL;
|
|
|
|
|
2009-09-25 05:53:51 +04:00
|
|
|
if (qemu_show_nic_models(nd->model, models))
|
|
|
|
exit(0);
|
|
|
|
if (qemu_find_nic_model(nd, models, model) < 0)
|
|
|
|
exit(1);
|
2009-01-13 22:03:57 +03:00
|
|
|
}
|
|
|
|
|
2009-09-25 05:53:51 +04:00
|
|
|
int qemu_find_nic_model(NICInfo *nd, const char * const *models,
|
|
|
|
const char *default_model)
|
2009-01-13 22:03:57 +03:00
|
|
|
{
|
2009-09-25 05:53:51 +04:00
|
|
|
int i;
|
2009-01-13 22:03:57 +03:00
|
|
|
|
|
|
|
if (!nd->model)
|
2011-08-21 07:09:37 +04:00
|
|
|
nd->model = g_strdup(default_model);
|
2009-01-13 22:03:57 +03:00
|
|
|
|
2009-09-25 05:53:51 +04:00
|
|
|
for (i = 0 ; models[i]; i++) {
|
|
|
|
if (strcmp(nd->model, models[i]) == 0)
|
|
|
|
return i;
|
2009-01-13 22:03:57 +03:00
|
|
|
}
|
|
|
|
|
2011-06-22 16:03:54 +04:00
|
|
|
error_report("Unsupported NIC model: %s", nd->model);
|
2009-09-25 05:53:51 +04:00
|
|
|
return -1;
|
2009-01-13 22:03:57 +03:00
|
|
|
}
|
|
|
|
|
2012-07-17 18:17:21 +04:00
|
|
|
static int net_init_nic(const NetClientOptions *opts, const char *name,
|
2015-05-15 14:58:50 +03:00
|
|
|
NetClientState *peer, Error **errp)
|
2009-10-06 15:17:06 +04:00
|
|
|
{
|
|
|
|
int idx;
|
|
|
|
NICInfo *nd;
|
2012-07-17 18:17:14 +04:00
|
|
|
const NetLegacyNicOptions *nic;
|
|
|
|
|
|
|
|
assert(opts->kind == NET_CLIENT_OPTIONS_KIND_NIC);
|
|
|
|
nic = opts->nic;
|
2009-10-06 15:17:06 +04:00
|
|
|
|
|
|
|
idx = nic_get_free_idx();
|
|
|
|
if (idx == -1 || nb_nics >= MAX_NICS) {
|
2015-05-15 14:58:51 +03:00
|
|
|
error_setg(errp, "too many NICs");
|
2009-10-06 15:17:06 +04:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
nd = &nd_table[idx];
|
|
|
|
|
|
|
|
memset(nd, 0, sizeof(*nd));
|
|
|
|
|
2012-07-17 18:17:14 +04:00
|
|
|
if (nic->has_netdev) {
|
|
|
|
nd->netdev = qemu_find_netdev(nic->netdev);
|
2009-10-08 22:58:29 +04:00
|
|
|
if (!nd->netdev) {
|
2015-05-15 14:58:51 +03:00
|
|
|
error_setg(errp, "netdev '%s' not found", nic->netdev);
|
2009-10-08 22:58:29 +04:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
} else {
|
2012-07-24 19:35:05 +04:00
|
|
|
assert(peer);
|
|
|
|
nd->netdev = peer;
|
2009-10-08 22:58:29 +04:00
|
|
|
}
|
2013-01-22 14:07:57 +04:00
|
|
|
nd->name = g_strdup(name);
|
2012-07-17 18:17:14 +04:00
|
|
|
if (nic->has_model) {
|
|
|
|
nd->model = g_strdup(nic->model);
|
2009-10-06 15:17:06 +04:00
|
|
|
}
|
2012-07-17 18:17:14 +04:00
|
|
|
if (nic->has_addr) {
|
|
|
|
nd->devaddr = g_strdup(nic->addr);
|
2009-10-06 15:17:06 +04:00
|
|
|
}
|
|
|
|
|
2012-07-17 18:17:14 +04:00
|
|
|
if (nic->has_macaddr &&
|
|
|
|
net_parse_macaddr(nd->macaddr.a, nic->macaddr) < 0) {
|
2015-05-15 14:58:51 +03:00
|
|
|
error_setg(errp, "invalid syntax for ethernet address");
|
2009-10-06 15:17:06 +04:00
|
|
|
return -1;
|
|
|
|
}
|
2013-10-21 12:08:44 +04:00
|
|
|
if (nic->has_macaddr &&
|
|
|
|
is_multicast_ether_addr(nd->macaddr.a)) {
|
2015-05-15 14:58:51 +03:00
|
|
|
error_setg(errp,
|
|
|
|
"NIC cannot have multicast MAC address (odd 1st byte)");
|
2013-10-21 12:08:44 +04:00
|
|
|
return -1;
|
|
|
|
}
|
2011-07-20 14:20:22 +04:00
|
|
|
qemu_macaddr_default_if_unset(&nd->macaddr);
|
2009-10-06 15:17:06 +04:00
|
|
|
|
2012-07-17 18:17:14 +04:00
|
|
|
if (nic->has_vectors) {
|
|
|
|
if (nic->vectors > 0x7ffffff) {
|
2015-05-15 14:58:51 +03:00
|
|
|
error_setg(errp, "invalid # of vectors: %"PRIu32, nic->vectors);
|
2012-07-17 18:17:14 +04:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
nd->nvectors = nic->vectors;
|
|
|
|
} else {
|
|
|
|
nd->nvectors = DEV_NVECTORS_UNSPECIFIED;
|
2009-10-06 15:17:06 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
nd->used = 1;
|
|
|
|
nb_nics++;
|
|
|
|
|
|
|
|
return idx;
|
|
|
|
}
|
|
|
|
|
2012-07-17 18:17:13 +04:00
|
|
|
|
|
|
|
static int (* const net_client_init_fun[NET_CLIENT_OPTIONS_KIND_MAX])(
|
2012-07-17 18:17:21 +04:00
|
|
|
const NetClientOptions *opts,
|
2012-07-17 18:17:13 +04:00
|
|
|
const char *name,
|
2015-05-15 14:58:50 +03:00
|
|
|
NetClientState *peer, Error **errp) = {
|
2012-07-24 19:35:04 +04:00
|
|
|
[NET_CLIENT_OPTIONS_KIND_NIC] = net_init_nic,
|
2009-10-06 15:17:07 +04:00
|
|
|
#ifdef CONFIG_SLIRP
|
2012-07-24 19:35:04 +04:00
|
|
|
[NET_CLIENT_OPTIONS_KIND_USER] = net_init_slirp,
|
2012-02-04 12:24:46 +04:00
|
|
|
#endif
|
2012-07-24 19:35:04 +04:00
|
|
|
[NET_CLIENT_OPTIONS_KIND_TAP] = net_init_tap,
|
|
|
|
[NET_CLIENT_OPTIONS_KIND_SOCKET] = net_init_socket,
|
2009-10-06 15:17:10 +04:00
|
|
|
#ifdef CONFIG_VDE
|
2012-07-24 19:35:04 +04:00
|
|
|
[NET_CLIENT_OPTIONS_KIND_VDE] = net_init_vde,
|
2013-11-06 14:44:06 +04:00
|
|
|
#endif
|
|
|
|
#ifdef CONFIG_NETMAP
|
|
|
|
[NET_CLIENT_OPTIONS_KIND_NETMAP] = net_init_netmap,
|
2009-10-06 15:17:10 +04:00
|
|
|
#endif
|
2012-07-24 19:35:04 +04:00
|
|
|
[NET_CLIENT_OPTIONS_KIND_DUMP] = net_init_dump,
|
2012-02-04 12:24:46 +04:00
|
|
|
#ifdef CONFIG_NET_BRIDGE
|
2012-07-24 19:35:04 +04:00
|
|
|
[NET_CLIENT_OPTIONS_KIND_BRIDGE] = net_init_bridge,
|
2012-07-17 18:17:13 +04:00
|
|
|
#endif
|
2012-07-24 19:35:04 +04:00
|
|
|
[NET_CLIENT_OPTIONS_KIND_HUBPORT] = net_init_hubport,
|
2014-06-10 14:02:16 +04:00
|
|
|
#ifdef CONFIG_VHOST_NET_USED
|
|
|
|
[NET_CLIENT_OPTIONS_KIND_VHOST_USER] = net_init_vhost_user,
|
|
|
|
#endif
|
2014-07-01 16:58:08 +04:00
|
|
|
#ifdef CONFIG_L2TPV3
|
2014-06-20 13:34:41 +04:00
|
|
|
[NET_CLIENT_OPTIONS_KIND_L2TPV3] = net_init_l2tpv3,
|
|
|
|
#endif
|
2009-10-06 15:17:06 +04:00
|
|
|
};
|
|
|
|
|
2012-07-17 18:17:13 +04:00
|
|
|
|
2012-07-17 18:17:21 +04:00
|
|
|
static int net_client_init1(const void *object, int is_netdev, Error **errp)
|
2009-10-06 15:17:06 +04:00
|
|
|
{
|
2012-07-17 18:17:13 +04:00
|
|
|
const NetClientOptions *opts;
|
2009-10-08 22:58:21 +04:00
|
|
|
const char *name;
|
2015-05-27 19:16:51 +03:00
|
|
|
NetClientState *peer = NULL;
|
2009-10-06 15:17:06 +04:00
|
|
|
|
2010-03-25 19:22:38 +03:00
|
|
|
if (is_netdev) {
|
2015-05-27 19:16:52 +03:00
|
|
|
const Netdev *netdev = object;
|
|
|
|
opts = netdev->opts;
|
|
|
|
name = netdev->id;
|
2012-07-17 18:17:13 +04:00
|
|
|
|
2015-05-27 19:16:49 +03:00
|
|
|
if (opts->kind == NET_CLIENT_OPTIONS_KIND_DUMP ||
|
|
|
|
opts->kind == NET_CLIENT_OPTIONS_KIND_NIC ||
|
|
|
|
!net_client_init_fun[opts->kind]) {
|
2015-03-17 13:54:50 +03:00
|
|
|
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "type",
|
|
|
|
"a netdev backend type");
|
2009-10-08 22:58:27 +04:00
|
|
|
return -1;
|
|
|
|
}
|
2012-07-17 18:17:13 +04:00
|
|
|
} else {
|
2015-05-27 19:16:52 +03:00
|
|
|
const NetLegacy *net = object;
|
|
|
|
opts = net->opts;
|
2012-07-17 18:17:13 +04:00
|
|
|
/* missing optional values have been initialized to "all bits zero" */
|
2015-05-27 19:16:52 +03:00
|
|
|
name = net->has_id ? net->id : net->name;
|
2015-05-27 19:16:50 +03:00
|
|
|
|
|
|
|
if (opts->kind == NET_CLIENT_OPTIONS_KIND_NONE) {
|
|
|
|
return 0; /* nothing to do */
|
|
|
|
}
|
2015-05-27 19:16:52 +03:00
|
|
|
if (opts->kind == NET_CLIENT_OPTIONS_KIND_HUBPORT) {
|
|
|
|
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "type",
|
|
|
|
"a net type");
|
|
|
|
return -1;
|
|
|
|
}
|
2015-05-27 19:16:50 +03:00
|
|
|
|
|
|
|
if (!net_client_init_fun[opts->kind]) {
|
|
|
|
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "type",
|
|
|
|
"a net backend type (maybe it is not compiled "
|
|
|
|
"into this binary)");
|
|
|
|
return -1;
|
|
|
|
}
|
2009-10-08 22:58:27 +04:00
|
|
|
|
2015-05-27 19:16:52 +03:00
|
|
|
/* Do not add to a vlan if it's a nic with a netdev= parameter. */
|
|
|
|
if (opts->kind != NET_CLIENT_OPTIONS_KIND_NIC ||
|
|
|
|
!opts->nic->has_netdev) {
|
|
|
|
peer = net_hub_add_port(net->has_vlan ? net->vlan : 0, NULL);
|
|
|
|
}
|
2015-05-27 19:16:51 +03:00
|
|
|
}
|
2012-07-17 18:17:13 +04:00
|
|
|
|
2015-05-27 19:16:51 +03:00
|
|
|
if (net_client_init_fun[opts->kind](opts, name, peer, errp) < 0) {
|
|
|
|
/* FIXME drop when all init functions store an Error */
|
|
|
|
if (errp && !*errp) {
|
|
|
|
error_setg(errp, QERR_DEVICE_INIT_FAILED,
|
|
|
|
NetClientOptionsKind_lookup[opts->kind]);
|
2009-10-08 22:58:27 +04:00
|
|
|
}
|
2015-05-27 19:16:51 +03:00
|
|
|
return -1;
|
2009-10-08 22:58:27 +04:00
|
|
|
}
|
2012-07-17 18:17:13 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-10-08 22:58:27 +04:00
|
|
|
|
2012-07-17 18:17:13 +04:00
|
|
|
static void net_visit(Visitor *v, int is_netdev, void **object, Error **errp)
|
|
|
|
{
|
|
|
|
if (is_netdev) {
|
|
|
|
visit_type_Netdev(v, (Netdev **)object, NULL, errp);
|
|
|
|
} else {
|
|
|
|
visit_type_NetLegacy(v, (NetLegacy **)object, NULL, errp);
|
2009-10-08 22:58:21 +04:00
|
|
|
}
|
2012-07-17 18:17:13 +04:00
|
|
|
}
|
2009-10-08 22:58:21 +04:00
|
|
|
|
2009-10-08 22:58:27 +04:00
|
|
|
|
2012-07-17 18:17:13 +04:00
|
|
|
int net_client_init(QemuOpts *opts, int is_netdev, Error **errp)
|
|
|
|
{
|
|
|
|
void *object = NULL;
|
|
|
|
Error *err = NULL;
|
|
|
|
int ret = -1;
|
2009-10-06 15:17:06 +04:00
|
|
|
|
2012-07-17 18:17:13 +04:00
|
|
|
{
|
|
|
|
OptsVisitor *ov = opts_visitor_new(opts);
|
2009-10-08 22:58:27 +04:00
|
|
|
|
2012-07-17 18:17:13 +04:00
|
|
|
net_visit(opts_get_visitor(ov), is_netdev, &object, &err);
|
|
|
|
opts_visitor_cleanup(ov);
|
2009-10-06 15:17:06 +04:00
|
|
|
}
|
|
|
|
|
2012-07-17 18:17:13 +04:00
|
|
|
if (!err) {
|
2012-07-17 18:17:21 +04:00
|
|
|
ret = net_client_init1(object, is_netdev, &err);
|
2012-07-17 18:17:13 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (object) {
|
|
|
|
QapiDeallocVisitor *dv = qapi_dealloc_visitor_new();
|
|
|
|
|
|
|
|
net_visit(qapi_dealloc_get_visitor(dv), is_netdev, &object, NULL);
|
|
|
|
qapi_dealloc_visitor_cleanup(dv);
|
|
|
|
}
|
|
|
|
|
|
|
|
error_propagate(errp, err);
|
|
|
|
return ret;
|
2009-10-06 15:17:06 +04:00
|
|
|
}
|
|
|
|
|
2012-07-17 18:17:13 +04:00
|
|
|
|
2009-02-11 18:21:54 +03:00
|
|
|
static int net_host_check_device(const char *device)
|
|
|
|
{
|
|
|
|
int i;
|
2014-05-28 02:39:33 +04:00
|
|
|
for (i = 0; host_net_devices[i]; i++) {
|
|
|
|
if (!strncmp(host_net_devices[i], device,
|
|
|
|
strlen(host_net_devices[i]))) {
|
2009-02-11 18:21:54 +03:00
|
|
|
return 1;
|
2014-05-28 02:39:33 +04:00
|
|
|
}
|
2009-02-11 18:21:54 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
hmp: Name HMP command handler functions hmp_COMMAND()
Some are called do_COMMAND() (old ones, usually), some hmp_COMMAND(),
and sometimes COMMAND pointlessly differs in spelling.
Normalize to hmp_COMMAND(), where COMMAND is exactly the command name
with '-' replaced by '_'.
Exceptions:
* do_device_add() and client_migrate_info() *not* renamed to
hmp_device_add(), hmp_client_migrate_info(), because they're also
QMP handlers. They still need to be converted to QAPI.
* do_memory_dump(), do_physical_memory_dump(), do_ioport_read(),
do_ioport_write() renamed do hmp_* instead of hmp_x(), hmp_xp(),
hmp_i(), hmp_o(), because those names are too cryptic for my taste.
* do_info_help() renamed to hmp_info_help() instead of hmp_info(),
because it only covers help.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
2015-02-06 15:55:43 +03:00
|
|
|
void hmp_host_net_add(Monitor *mon, const QDict *qdict)
|
2009-02-11 18:21:54 +03:00
|
|
|
{
|
2009-08-28 22:27:14 +04:00
|
|
|
const char *device = qdict_get_str(qdict, "device");
|
2009-10-06 15:17:13 +04:00
|
|
|
const char *opts_str = qdict_get_try_str(qdict, "opts");
|
2012-04-20 23:50:25 +04:00
|
|
|
Error *local_err = NULL;
|
2009-10-06 15:17:13 +04:00
|
|
|
QemuOpts *opts;
|
2009-08-28 22:27:14 +04:00
|
|
|
|
2009-02-11 18:21:54 +03:00
|
|
|
if (!net_host_check_device(device)) {
|
2009-03-06 02:01:23 +03:00
|
|
|
monitor_printf(mon, "invalid host network device %s\n", device);
|
2009-02-11 18:21:54 +03:00
|
|
|
return;
|
|
|
|
}
|
2009-10-06 15:17:13 +04:00
|
|
|
|
QemuOpts: Wean off qerror_report_err()
qerror_report_err() is a transitional interface to help with
converting existing monitor commands to QMP. It should not be used
elsewhere.
The only remaining user in qemu-option.c is qemu_opts_parse(). Is it
used in QMP context? If not, we can simply replace
qerror_report_err() by error_report_err().
The uses in qemu-img.c, qemu-io.c, qemu-nbd.c and under tests/ are
clearly not in QMP context.
The uses in vl.c aren't either, because the only QMP command handlers
there are qmp_query_status() and qmp_query_machines(), and they don't
call it.
Remaining uses:
* drive_def(): Command line -drive and such, HMP drive_add and pci_add
* hmp_chardev_add(): HMP chardev-add
* monitor_parse_command(): HMP core
* tmp_config_parse(): Command line -tpmdev
* net_host_device_add(): HMP host_net_add
* net_client_parse(): Command line -net and -netdev
* qemu_global_option(): Command line -global
* vnc_parse_func(): Command line -display, -vnc, default display, HMP
change, QMP change. Bummer.
* qemu_pci_hot_add_nic(): HMP pci_add
* usb_net_init(): Command line -usbdevice, HMP usb_add
Propagate errors through qemu_opts_parse(). Create a convenience
function qemu_opts_parse_noisily() that passes errors to
error_report_err(). Switch all non-QMP users outside tests to it.
That leaves vnc_parse_func(). Propagate errors through it. Since I'm
touching it anyway, rename it to vnc_parse().
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Luiz Capitulino <lcapitulino@redhat.com>
2015-02-13 14:50:26 +03:00
|
|
|
opts = qemu_opts_parse_noisily(qemu_find_opts("net"),
|
|
|
|
opts_str ? opts_str : "", false);
|
2009-10-06 15:17:13 +04:00
|
|
|
if (!opts) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-02-12 19:52:20 +03:00
|
|
|
qemu_opt_set(opts, "type", device, &error_abort);
|
2009-10-06 15:17:13 +04:00
|
|
|
|
2012-04-20 23:50:25 +04:00
|
|
|
net_client_init(opts, 0, &local_err);
|
2014-01-30 18:07:28 +04:00
|
|
|
if (local_err) {
|
2015-02-10 17:02:06 +03:00
|
|
|
error_report_err(local_err);
|
2009-04-21 23:56:32 +04:00
|
|
|
monitor_printf(mon, "adding host network device %s failed\n", device);
|
|
|
|
}
|
2009-02-11 18:21:54 +03:00
|
|
|
}
|
|
|
|
|
hmp: Name HMP command handler functions hmp_COMMAND()
Some are called do_COMMAND() (old ones, usually), some hmp_COMMAND(),
and sometimes COMMAND pointlessly differs in spelling.
Normalize to hmp_COMMAND(), where COMMAND is exactly the command name
with '-' replaced by '_'.
Exceptions:
* do_device_add() and client_migrate_info() *not* renamed to
hmp_device_add(), hmp_client_migrate_info(), because they're also
QMP handlers. They still need to be converted to QAPI.
* do_memory_dump(), do_physical_memory_dump(), do_ioport_read(),
do_ioport_write() renamed do hmp_* instead of hmp_x(), hmp_xp(),
hmp_i(), hmp_o(), because those names are too cryptic for my taste.
* do_info_help() renamed to hmp_info_help() instead of hmp_info(),
because it only covers help.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
2015-02-06 15:55:43 +03:00
|
|
|
void hmp_host_net_remove(Monitor *mon, const QDict *qdict)
|
2009-02-11 18:21:54 +03:00
|
|
|
{
|
2012-07-24 19:35:14 +04:00
|
|
|
NetClientState *nc;
|
2009-08-28 22:27:14 +04:00
|
|
|
int vlan_id = qdict_get_int(qdict, "vlan_id");
|
|
|
|
const char *device = qdict_get_str(qdict, "device");
|
2009-02-11 18:21:54 +03:00
|
|
|
|
2012-07-24 19:35:14 +04:00
|
|
|
nc = net_hub_find_client_by_name(vlan_id, device);
|
|
|
|
if (!nc) {
|
2014-04-01 03:05:14 +04:00
|
|
|
error_report("Host network device '%s' on hub '%d' not found",
|
|
|
|
device, vlan_id);
|
2009-02-11 18:21:54 +03:00
|
|
|
return;
|
|
|
|
}
|
2014-12-23 19:53:20 +03:00
|
|
|
if (nc->info->type == NET_CLIENT_OPTIONS_KIND_NIC) {
|
2014-04-01 03:05:14 +04:00
|
|
|
error_report("invalid host network device '%s'", device);
|
2009-04-21 23:56:08 +04:00
|
|
|
return;
|
|
|
|
}
|
2015-02-02 10:06:37 +03:00
|
|
|
|
|
|
|
qemu_del_net_client(nc->peer);
|
2012-07-24 19:35:15 +04:00
|
|
|
qemu_del_net_client(nc);
|
2009-02-11 18:21:54 +03:00
|
|
|
}
|
|
|
|
|
2012-04-19 00:34:15 +04:00
|
|
|
void netdev_add(QemuOpts *opts, Error **errp)
|
|
|
|
{
|
|
|
|
net_client_init(opts, 1, errp);
|
|
|
|
}
|
|
|
|
|
qmp: Wean off qerror_report()
The traditional QMP command handler interface
int qmp_FOO(Monitor *mon, const QDict *params, QObject **ret_data);
doesn't provide for returning an Error object. Instead, the handler
is expected to stash it in the monitor with qerror_report().
When we rebased QMP on top of QAPI, we didn't change this interface.
Instead, commit 776574d introduced "middle mode" as a temporary aid
for converting existing QMP commands to QAPI one by one. More than
three years later, we're still using it.
Middle mode has two effects:
* Instead of the native input marshallers
static void qmp_marshal_input_FOO(QDict *, QObject **, Error **)
it generates input marshallers conforming to the traditional QMP
command handler interface.
* It suppresses generation of code to register them with
qmp_register_command()
This permits giving them internal linkage.
As long as we need qmp-commands.hx, we can't use the registry behind
qmp_register_command(), so the latter has to stay for now.
The former has to go to get rid of qerror_report(). Changing all QMP
commands to fit the QAPI mold in one go was impractical back when we
started, but by now there are just a few stragglers left:
do_qmp_capabilities(), qmp_qom_set(), qmp_qom_get(), qmp_object_add(),
qmp_netdev_add(), do_device_add().
Switch middle mode to generate native input marshallers, and adapt the
stragglers. Simplifies both the monitor code and the stragglers.
Rename do_qmp_capabilities() to qmp_capabilities(), and
do_device_add() to qmp_device_add, because that's how QMP command
handlers are named today.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Luiz Capitulino <lcapitulino@redhat.com>
2015-03-13 19:25:50 +03:00
|
|
|
void qmp_netdev_add(QDict *qdict, QObject **ret, Error **errp)
|
2010-03-25 19:22:40 +03:00
|
|
|
{
|
2012-04-19 00:24:01 +04:00
|
|
|
Error *local_err = NULL;
|
2012-04-19 00:34:15 +04:00
|
|
|
QemuOptsList *opts_list;
|
2010-03-25 19:22:40 +03:00
|
|
|
QemuOpts *opts;
|
|
|
|
|
2012-04-19 00:34:15 +04:00
|
|
|
opts_list = qemu_find_opts_err("netdev", &local_err);
|
2014-01-30 18:07:28 +04:00
|
|
|
if (local_err) {
|
qmp: Wean off qerror_report()
The traditional QMP command handler interface
int qmp_FOO(Monitor *mon, const QDict *params, QObject **ret_data);
doesn't provide for returning an Error object. Instead, the handler
is expected to stash it in the monitor with qerror_report().
When we rebased QMP on top of QAPI, we didn't change this interface.
Instead, commit 776574d introduced "middle mode" as a temporary aid
for converting existing QMP commands to QAPI one by one. More than
three years later, we're still using it.
Middle mode has two effects:
* Instead of the native input marshallers
static void qmp_marshal_input_FOO(QDict *, QObject **, Error **)
it generates input marshallers conforming to the traditional QMP
command handler interface.
* It suppresses generation of code to register them with
qmp_register_command()
This permits giving them internal linkage.
As long as we need qmp-commands.hx, we can't use the registry behind
qmp_register_command(), so the latter has to stay for now.
The former has to go to get rid of qerror_report(). Changing all QMP
commands to fit the QAPI mold in one go was impractical back when we
started, but by now there are just a few stragglers left:
do_qmp_capabilities(), qmp_qom_set(), qmp_qom_get(), qmp_object_add(),
qmp_netdev_add(), do_device_add().
Switch middle mode to generate native input marshallers, and adapt the
stragglers. Simplifies both the monitor code and the stragglers.
Rename do_qmp_capabilities() to qmp_capabilities(), and
do_device_add() to qmp_device_add, because that's how QMP command
handlers are named today.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Luiz Capitulino <lcapitulino@redhat.com>
2015-03-13 19:25:50 +03:00
|
|
|
goto out;
|
2010-03-25 19:22:40 +03:00
|
|
|
}
|
|
|
|
|
2012-04-19 00:34:15 +04:00
|
|
|
opts = qemu_opts_from_qdict(opts_list, qdict, &local_err);
|
2014-01-30 18:07:28 +04:00
|
|
|
if (local_err) {
|
qmp: Wean off qerror_report()
The traditional QMP command handler interface
int qmp_FOO(Monitor *mon, const QDict *params, QObject **ret_data);
doesn't provide for returning an Error object. Instead, the handler
is expected to stash it in the monitor with qerror_report().
When we rebased QMP on top of QAPI, we didn't change this interface.
Instead, commit 776574d introduced "middle mode" as a temporary aid
for converting existing QMP commands to QAPI one by one. More than
three years later, we're still using it.
Middle mode has two effects:
* Instead of the native input marshallers
static void qmp_marshal_input_FOO(QDict *, QObject **, Error **)
it generates input marshallers conforming to the traditional QMP
command handler interface.
* It suppresses generation of code to register them with
qmp_register_command()
This permits giving them internal linkage.
As long as we need qmp-commands.hx, we can't use the registry behind
qmp_register_command(), so the latter has to stay for now.
The former has to go to get rid of qerror_report(). Changing all QMP
commands to fit the QAPI mold in one go was impractical back when we
started, but by now there are just a few stragglers left:
do_qmp_capabilities(), qmp_qom_set(), qmp_qom_get(), qmp_object_add(),
qmp_netdev_add(), do_device_add().
Switch middle mode to generate native input marshallers, and adapt the
stragglers. Simplifies both the monitor code and the stragglers.
Rename do_qmp_capabilities() to qmp_capabilities(), and
do_device_add() to qmp_device_add, because that's how QMP command
handlers are named today.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Luiz Capitulino <lcapitulino@redhat.com>
2015-03-13 19:25:50 +03:00
|
|
|
goto out;
|
2012-04-19 00:34:15 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
netdev_add(opts, &local_err);
|
2014-01-30 18:07:28 +04:00
|
|
|
if (local_err) {
|
2010-06-21 05:41:36 +04:00
|
|
|
qemu_opts_del(opts);
|
qmp: Wean off qerror_report()
The traditional QMP command handler interface
int qmp_FOO(Monitor *mon, const QDict *params, QObject **ret_data);
doesn't provide for returning an Error object. Instead, the handler
is expected to stash it in the monitor with qerror_report().
When we rebased QMP on top of QAPI, we didn't change this interface.
Instead, commit 776574d introduced "middle mode" as a temporary aid
for converting existing QMP commands to QAPI one by one. More than
three years later, we're still using it.
Middle mode has two effects:
* Instead of the native input marshallers
static void qmp_marshal_input_FOO(QDict *, QObject **, Error **)
it generates input marshallers conforming to the traditional QMP
command handler interface.
* It suppresses generation of code to register them with
qmp_register_command()
This permits giving them internal linkage.
As long as we need qmp-commands.hx, we can't use the registry behind
qmp_register_command(), so the latter has to stay for now.
The former has to go to get rid of qerror_report(). Changing all QMP
commands to fit the QAPI mold in one go was impractical back when we
started, but by now there are just a few stragglers left:
do_qmp_capabilities(), qmp_qom_set(), qmp_qom_get(), qmp_object_add(),
qmp_netdev_add(), do_device_add().
Switch middle mode to generate native input marshallers, and adapt the
stragglers. Simplifies both the monitor code and the stragglers.
Rename do_qmp_capabilities() to qmp_capabilities(), and
do_device_add() to qmp_device_add, because that's how QMP command
handlers are named today.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Luiz Capitulino <lcapitulino@redhat.com>
2015-03-13 19:25:50 +03:00
|
|
|
goto out;
|
2010-06-21 05:41:36 +04:00
|
|
|
}
|
|
|
|
|
qmp: Wean off qerror_report()
The traditional QMP command handler interface
int qmp_FOO(Monitor *mon, const QDict *params, QObject **ret_data);
doesn't provide for returning an Error object. Instead, the handler
is expected to stash it in the monitor with qerror_report().
When we rebased QMP on top of QAPI, we didn't change this interface.
Instead, commit 776574d introduced "middle mode" as a temporary aid
for converting existing QMP commands to QAPI one by one. More than
three years later, we're still using it.
Middle mode has two effects:
* Instead of the native input marshallers
static void qmp_marshal_input_FOO(QDict *, QObject **, Error **)
it generates input marshallers conforming to the traditional QMP
command handler interface.
* It suppresses generation of code to register them with
qmp_register_command()
This permits giving them internal linkage.
As long as we need qmp-commands.hx, we can't use the registry behind
qmp_register_command(), so the latter has to stay for now.
The former has to go to get rid of qerror_report(). Changing all QMP
commands to fit the QAPI mold in one go was impractical back when we
started, but by now there are just a few stragglers left:
do_qmp_capabilities(), qmp_qom_set(), qmp_qom_get(), qmp_object_add(),
qmp_netdev_add(), do_device_add().
Switch middle mode to generate native input marshallers, and adapt the
stragglers. Simplifies both the monitor code and the stragglers.
Rename do_qmp_capabilities() to qmp_capabilities(), and
do_device_add() to qmp_device_add, because that's how QMP command
handlers are named today.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Luiz Capitulino <lcapitulino@redhat.com>
2015-03-13 19:25:50 +03:00
|
|
|
out:
|
|
|
|
error_propagate(errp, local_err);
|
2010-03-25 19:22:40 +03:00
|
|
|
}
|
|
|
|
|
2012-04-16 21:36:32 +04:00
|
|
|
void qmp_netdev_del(const char *id, Error **errp)
|
2010-03-25 19:22:40 +03:00
|
|
|
{
|
2012-07-24 19:35:14 +04:00
|
|
|
NetClientState *nc;
|
2012-10-24 16:34:12 +04:00
|
|
|
QemuOpts *opts;
|
2010-03-25 19:22:40 +03:00
|
|
|
|
2012-07-24 19:35:14 +04:00
|
|
|
nc = qemu_find_netdev(id);
|
|
|
|
if (!nc) {
|
2015-03-16 10:57:47 +03:00
|
|
|
error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
|
|
|
|
"Device '%s' not found", id);
|
2012-04-16 21:36:32 +04:00
|
|
|
return;
|
2010-03-25 19:22:40 +03:00
|
|
|
}
|
2012-04-16 21:36:32 +04:00
|
|
|
|
2012-10-24 16:34:12 +04:00
|
|
|
opts = qemu_opts_find(qemu_find_opts_err("netdev", NULL), id);
|
|
|
|
if (!opts) {
|
|
|
|
error_setg(errp, "Device '%s' is not a netdev", id);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-07-24 19:35:15 +04:00
|
|
|
qemu_del_net_client(nc);
|
2012-10-24 16:34:12 +04:00
|
|
|
qemu_opts_del(opts);
|
2010-03-25 19:22:40 +03:00
|
|
|
}
|
|
|
|
|
2012-07-24 19:35:16 +04:00
|
|
|
void print_net_client(Monitor *mon, NetClientState *nc)
|
2011-07-20 14:20:21 +04:00
|
|
|
{
|
2015-10-07 06:52:19 +03:00
|
|
|
NetFilterState *nf;
|
|
|
|
|
2013-01-30 15:12:28 +04:00
|
|
|
monitor_printf(mon, "%s: index=%d,type=%s,%s\n", nc->name,
|
|
|
|
nc->queue_index,
|
|
|
|
NetClientOptionsKind_lookup[nc->info->type],
|
|
|
|
nc->info_str);
|
2015-10-07 06:52:19 +03:00
|
|
|
if (!QTAILQ_EMPTY(&nc->filters)) {
|
|
|
|
monitor_printf(mon, "filters:\n");
|
|
|
|
}
|
|
|
|
QTAILQ_FOREACH(nf, &nc->filters, next) {
|
|
|
|
monitor_printf(mon, " - %s: type=%s%s\n",
|
|
|
|
object_get_canonical_path_component(OBJECT(nf)),
|
|
|
|
object_get_typename(OBJECT(nf)),
|
|
|
|
nf->info_str);
|
|
|
|
}
|
2011-07-20 14:20:21 +04:00
|
|
|
}
|
|
|
|
|
net: add support of mac-programming over macvtap in QEMU side
Currently macvtap based macvlan device is working in promiscuous
mode, we want to implement mac-programming over macvtap through
Libvirt for better performance.
Design:
QEMU notifies Libvirt when rx-filter config is changed in guest,
then Libvirt query the rx-filter information by a monitor command,
and sync the change to macvtap device. Related rx-filter config
of the nic contains main mac, rx-mode items and vlan table.
This patch adds a QMP event to notify management of rx-filter change,
and adds a monitor command for management to query rx-filter
information.
Test:
If we repeatedly add/remove vlan, and change macaddr of vlan
interfaces in guest by a loop script.
Result:
The events will flood the QMP client(management), management takes
too much resource to process the events.
Event_throttle API (set rate to 1 ms) can avoid the events to flood
QMP client, but it could cause an unexpected delay (~1ms), guests
guests normally expect rx-filter updates immediately.
So we use a flag for each nic to avoid events flooding, the event
is emitted once until the query command is executed. The flag
implementation could not introduce unexpected delay.
There maybe exist an uncontrollable delay if we let Libvirt do the
real change, guests normally expect rx-filter updates immediately.
But it's another separate issue, we can investigate it when the
work in Libvirt side is done.
Michael S. Tsirkin: tweaked to enable events on start
Michael S. Tsirkin: fixed not to crash when no id
Michael S. Tsirkin: fold in patch:
"additional fixes for mac-programming feature"
Amos Kong: always notify QMP client if mactable is changed
Amos Kong: return NULL list if no net client supports rx-filter query
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Amos Kong <akong@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
2013-06-14 11:45:52 +04:00
|
|
|
RxFilterInfoList *qmp_query_rx_filter(bool has_name, const char *name,
|
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
NetClientState *nc;
|
|
|
|
RxFilterInfoList *filter_list = NULL, *last_entry = NULL;
|
|
|
|
|
|
|
|
QTAILQ_FOREACH(nc, &net_clients, next) {
|
|
|
|
RxFilterInfoList *entry;
|
|
|
|
RxFilterInfo *info;
|
|
|
|
|
|
|
|
if (has_name && strcmp(nc->name, name) != 0) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* only query rx-filter information of NIC */
|
|
|
|
if (nc->info->type != NET_CLIENT_OPTIONS_KIND_NIC) {
|
|
|
|
if (has_name) {
|
|
|
|
error_setg(errp, "net client(%s) isn't a NIC", name);
|
2014-04-24 17:44:18 +04:00
|
|
|
return NULL;
|
net: add support of mac-programming over macvtap in QEMU side
Currently macvtap based macvlan device is working in promiscuous
mode, we want to implement mac-programming over macvtap through
Libvirt for better performance.
Design:
QEMU notifies Libvirt when rx-filter config is changed in guest,
then Libvirt query the rx-filter information by a monitor command,
and sync the change to macvtap device. Related rx-filter config
of the nic contains main mac, rx-mode items and vlan table.
This patch adds a QMP event to notify management of rx-filter change,
and adds a monitor command for management to query rx-filter
information.
Test:
If we repeatedly add/remove vlan, and change macaddr of vlan
interfaces in guest by a loop script.
Result:
The events will flood the QMP client(management), management takes
too much resource to process the events.
Event_throttle API (set rate to 1 ms) can avoid the events to flood
QMP client, but it could cause an unexpected delay (~1ms), guests
guests normally expect rx-filter updates immediately.
So we use a flag for each nic to avoid events flooding, the event
is emitted once until the query command is executed. The flag
implementation could not introduce unexpected delay.
There maybe exist an uncontrollable delay if we let Libvirt do the
real change, guests normally expect rx-filter updates immediately.
But it's another separate issue, we can investigate it when the
work in Libvirt side is done.
Michael S. Tsirkin: tweaked to enable events on start
Michael S. Tsirkin: fixed not to crash when no id
Michael S. Tsirkin: fold in patch:
"additional fixes for mac-programming feature"
Amos Kong: always notify QMP client if mactable is changed
Amos Kong: return NULL list if no net client supports rx-filter query
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Amos Kong <akong@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
2013-06-14 11:45:52 +04:00
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nc->info->query_rx_filter) {
|
|
|
|
info = nc->info->query_rx_filter(nc);
|
|
|
|
entry = g_malloc0(sizeof(*entry));
|
|
|
|
entry->value = info;
|
|
|
|
|
|
|
|
if (!filter_list) {
|
|
|
|
filter_list = entry;
|
|
|
|
} else {
|
|
|
|
last_entry->next = entry;
|
|
|
|
}
|
|
|
|
last_entry = entry;
|
|
|
|
} else if (has_name) {
|
|
|
|
error_setg(errp, "net client(%s) doesn't support"
|
|
|
|
" rx-filter querying", name);
|
2014-04-24 17:44:18 +04:00
|
|
|
return NULL;
|
net: add support of mac-programming over macvtap in QEMU side
Currently macvtap based macvlan device is working in promiscuous
mode, we want to implement mac-programming over macvtap through
Libvirt for better performance.
Design:
QEMU notifies Libvirt when rx-filter config is changed in guest,
then Libvirt query the rx-filter information by a monitor command,
and sync the change to macvtap device. Related rx-filter config
of the nic contains main mac, rx-mode items and vlan table.
This patch adds a QMP event to notify management of rx-filter change,
and adds a monitor command for management to query rx-filter
information.
Test:
If we repeatedly add/remove vlan, and change macaddr of vlan
interfaces in guest by a loop script.
Result:
The events will flood the QMP client(management), management takes
too much resource to process the events.
Event_throttle API (set rate to 1 ms) can avoid the events to flood
QMP client, but it could cause an unexpected delay (~1ms), guests
guests normally expect rx-filter updates immediately.
So we use a flag for each nic to avoid events flooding, the event
is emitted once until the query command is executed. The flag
implementation could not introduce unexpected delay.
There maybe exist an uncontrollable delay if we let Libvirt do the
real change, guests normally expect rx-filter updates immediately.
But it's another separate issue, we can investigate it when the
work in Libvirt side is done.
Michael S. Tsirkin: tweaked to enable events on start
Michael S. Tsirkin: fixed not to crash when no id
Michael S. Tsirkin: fold in patch:
"additional fixes for mac-programming feature"
Amos Kong: always notify QMP client if mactable is changed
Amos Kong: return NULL list if no net client supports rx-filter query
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Amos Kong <akong@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
2013-06-14 11:45:52 +04:00
|
|
|
}
|
2014-04-24 17:44:17 +04:00
|
|
|
|
|
|
|
if (has_name) {
|
|
|
|
break;
|
|
|
|
}
|
net: add support of mac-programming over macvtap in QEMU side
Currently macvtap based macvlan device is working in promiscuous
mode, we want to implement mac-programming over macvtap through
Libvirt for better performance.
Design:
QEMU notifies Libvirt when rx-filter config is changed in guest,
then Libvirt query the rx-filter information by a monitor command,
and sync the change to macvtap device. Related rx-filter config
of the nic contains main mac, rx-mode items and vlan table.
This patch adds a QMP event to notify management of rx-filter change,
and adds a monitor command for management to query rx-filter
information.
Test:
If we repeatedly add/remove vlan, and change macaddr of vlan
interfaces in guest by a loop script.
Result:
The events will flood the QMP client(management), management takes
too much resource to process the events.
Event_throttle API (set rate to 1 ms) can avoid the events to flood
QMP client, but it could cause an unexpected delay (~1ms), guests
guests normally expect rx-filter updates immediately.
So we use a flag for each nic to avoid events flooding, the event
is emitted once until the query command is executed. The flag
implementation could not introduce unexpected delay.
There maybe exist an uncontrollable delay if we let Libvirt do the
real change, guests normally expect rx-filter updates immediately.
But it's another separate issue, we can investigate it when the
work in Libvirt side is done.
Michael S. Tsirkin: tweaked to enable events on start
Michael S. Tsirkin: fixed not to crash when no id
Michael S. Tsirkin: fold in patch:
"additional fixes for mac-programming feature"
Amos Kong: always notify QMP client if mactable is changed
Amos Kong: return NULL list if no net client supports rx-filter query
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Amos Kong <akong@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
2013-06-14 11:45:52 +04:00
|
|
|
}
|
|
|
|
|
2014-04-24 17:44:18 +04:00
|
|
|
if (filter_list == NULL && has_name) {
|
net: add support of mac-programming over macvtap in QEMU side
Currently macvtap based macvlan device is working in promiscuous
mode, we want to implement mac-programming over macvtap through
Libvirt for better performance.
Design:
QEMU notifies Libvirt when rx-filter config is changed in guest,
then Libvirt query the rx-filter information by a monitor command,
and sync the change to macvtap device. Related rx-filter config
of the nic contains main mac, rx-mode items and vlan table.
This patch adds a QMP event to notify management of rx-filter change,
and adds a monitor command for management to query rx-filter
information.
Test:
If we repeatedly add/remove vlan, and change macaddr of vlan
interfaces in guest by a loop script.
Result:
The events will flood the QMP client(management), management takes
too much resource to process the events.
Event_throttle API (set rate to 1 ms) can avoid the events to flood
QMP client, but it could cause an unexpected delay (~1ms), guests
guests normally expect rx-filter updates immediately.
So we use a flag for each nic to avoid events flooding, the event
is emitted once until the query command is executed. The flag
implementation could not introduce unexpected delay.
There maybe exist an uncontrollable delay if we let Libvirt do the
real change, guests normally expect rx-filter updates immediately.
But it's another separate issue, we can investigate it when the
work in Libvirt side is done.
Michael S. Tsirkin: tweaked to enable events on start
Michael S. Tsirkin: fixed not to crash when no id
Michael S. Tsirkin: fold in patch:
"additional fixes for mac-programming feature"
Amos Kong: always notify QMP client if mactable is changed
Amos Kong: return NULL list if no net client supports rx-filter query
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Amos Kong <akong@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
2013-06-14 11:45:52 +04:00
|
|
|
error_setg(errp, "invalid net client name: %s", name);
|
|
|
|
}
|
|
|
|
|
|
|
|
return filter_list;
|
|
|
|
}
|
|
|
|
|
2015-02-06 16:18:24 +03:00
|
|
|
void hmp_info_network(Monitor *mon, const QDict *qdict)
|
2008-10-31 22:10:00 +03:00
|
|
|
{
|
2012-07-24 19:35:14 +04:00
|
|
|
NetClientState *nc, *peer;
|
2012-07-17 18:17:12 +04:00
|
|
|
NetClientOptionsKind type;
|
2008-10-31 22:10:00 +03:00
|
|
|
|
2012-07-24 19:35:16 +04:00
|
|
|
net_hub_info(mon);
|
|
|
|
|
2012-07-24 19:35:14 +04:00
|
|
|
QTAILQ_FOREACH(nc, &net_clients, next) {
|
|
|
|
peer = nc->peer;
|
|
|
|
type = nc->info->type;
|
2009-10-08 22:58:23 +04:00
|
|
|
|
2012-07-24 19:35:16 +04:00
|
|
|
/* Skip if already printed in hub info */
|
|
|
|
if (net_hub_id_for_client(nc, NULL) == 0) {
|
|
|
|
continue;
|
2009-10-08 22:58:23 +04:00
|
|
|
}
|
2012-07-24 19:35:16 +04:00
|
|
|
|
2012-07-17 18:17:12 +04:00
|
|
|
if (!peer || type == NET_CLIENT_OPTIONS_KIND_NIC) {
|
2012-07-24 19:35:14 +04:00
|
|
|
print_net_client(mon, nc);
|
2011-07-20 14:20:19 +04:00
|
|
|
} /* else it's a netdev connected to a NIC, printed with the NIC */
|
2012-07-17 18:17:12 +04:00
|
|
|
if (peer && type == NET_CLIENT_OPTIONS_KIND_NIC) {
|
2012-07-24 19:35:16 +04:00
|
|
|
monitor_printf(mon, " \\ ");
|
2011-07-20 14:20:21 +04:00
|
|
|
print_net_client(mon, peer);
|
2010-02-11 16:45:01 +03:00
|
|
|
}
|
|
|
|
}
|
2008-10-31 22:10:00 +03:00
|
|
|
}
|
|
|
|
|
2011-11-23 19:11:55 +04:00
|
|
|
void qmp_set_link(const char *name, bool up, Error **errp)
|
2009-01-08 22:44:06 +03:00
|
|
|
{
|
2013-01-30 15:12:28 +04:00
|
|
|
NetClientState *ncs[MAX_QUEUE_NUM];
|
|
|
|
NetClientState *nc;
|
|
|
|
int queues, i;
|
2009-01-08 22:44:06 +03:00
|
|
|
|
2013-01-30 15:12:28 +04:00
|
|
|
queues = qemu_find_net_clients_except(name, ncs,
|
|
|
|
NET_CLIENT_OPTIONS_KIND_MAX,
|
|
|
|
MAX_QUEUE_NUM);
|
|
|
|
|
|
|
|
if (queues == 0) {
|
2015-03-16 10:57:47 +03:00
|
|
|
error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
|
|
|
|
"Device '%s' not found", name);
|
2011-11-23 19:11:55 +04:00
|
|
|
return;
|
2009-01-08 22:44:06 +03:00
|
|
|
}
|
2013-01-30 15:12:28 +04:00
|
|
|
nc = ncs[0];
|
2009-01-08 22:44:06 +03:00
|
|
|
|
2013-01-30 15:12:28 +04:00
|
|
|
for (i = 0; i < queues; i++) {
|
|
|
|
ncs[i]->link_down = !up;
|
|
|
|
}
|
2009-01-08 22:44:06 +03:00
|
|
|
|
2012-07-24 19:35:14 +04:00
|
|
|
if (nc->info->link_status_changed) {
|
|
|
|
nc->info->link_status_changed(nc);
|
2009-11-25 21:49:30 +03:00
|
|
|
}
|
2011-02-09 19:45:04 +03:00
|
|
|
|
net: Update netdev peer on link change
When a link change occurs on a backend (like tap), we currently do
not propage such change to the nic. As a result, when someone turns
off a link on a tap device, for instance, then a guest doesn't see
that change and continues to try to send traffic or run DHCP even
though the lower-layer is disconnected. This is OK when the network
is set up as a HUB since the the guest may be connected to other HUB
ports too, but when it's set up as a netdev, it makes thinkgs worse.
The patch addresses this by setting the peers link down only when the
peer is not a HUBPORT device. With this patch, in the following config
-netdev tap,id=net0 -device e1000,mac=XXXXX,netdev=net0
when net0 link is turned off, the guest e1000 shows lower-layer link
down. This allows guests to boot much faster in such configurations.
With windows guest, it also allows the network to recover properly
since windows will not configure the link-local IPv4 address, and
when the link is turned on, the proper address address is configured.
Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
2013-11-22 06:05:51 +04:00
|
|
|
if (nc->peer) {
|
|
|
|
/* Change peer link only if the peer is NIC and then notify peer.
|
|
|
|
* If the peer is a HUBPORT or a backend, we do not change the
|
|
|
|
* link status.
|
|
|
|
*
|
|
|
|
* This behavior is compatible with qemu vlans where there could be
|
|
|
|
* multiple clients that can still communicate with each other in
|
|
|
|
* disconnected mode. For now maintain this compatibility.
|
|
|
|
*/
|
|
|
|
if (nc->peer->info->type == NET_CLIENT_OPTIONS_KIND_NIC) {
|
|
|
|
for (i = 0; i < queues; i++) {
|
|
|
|
ncs[i]->peer->link_down = !up;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (nc->peer->info->link_status_changed) {
|
|
|
|
nc->peer->info->link_status_changed(nc->peer);
|
|
|
|
}
|
2011-02-09 19:45:04 +03:00
|
|
|
}
|
2009-01-08 22:44:06 +03:00
|
|
|
}
|
|
|
|
|
2014-09-04 12:39:13 +04:00
|
|
|
static void net_vm_change_state_handler(void *opaque, int running,
|
|
|
|
RunState state)
|
|
|
|
{
|
2015-07-07 04:21:07 +03:00
|
|
|
NetClientState *nc;
|
|
|
|
NetClientState *tmp;
|
2014-09-04 12:39:13 +04:00
|
|
|
|
2015-07-07 04:21:07 +03:00
|
|
|
QTAILQ_FOREACH_SAFE(nc, &net_clients, next, tmp) {
|
|
|
|
if (running) {
|
|
|
|
/* Flush queued packets and wake up backends. */
|
|
|
|
if (nc->peer && qemu_can_send_packet(nc)) {
|
|
|
|
qemu_flush_queued_packets(nc->peer);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* Complete all queued packets, to guarantee we don't modify
|
|
|
|
* state later when VM is not running.
|
|
|
|
*/
|
2014-09-04 12:39:13 +04:00
|
|
|
qemu_flush_or_purge_queued_packets(nc, true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-10-31 22:10:00 +03:00
|
|
|
void net_cleanup(void)
|
|
|
|
{
|
2013-01-30 15:12:28 +04:00
|
|
|
NetClientState *nc;
|
2009-10-08 22:58:28 +04:00
|
|
|
|
2013-01-30 15:12:28 +04:00
|
|
|
/* We may del multiple entries during qemu_del_net_client(),
|
|
|
|
* so QTAILQ_FOREACH_SAFE() is also not safe here.
|
|
|
|
*/
|
|
|
|
while (!QTAILQ_EMPTY(&net_clients)) {
|
|
|
|
nc = QTAILQ_FIRST(&net_clients);
|
2013-01-30 15:12:24 +04:00
|
|
|
if (nc->info->type == NET_CLIENT_OPTIONS_KIND_NIC) {
|
|
|
|
qemu_del_nic(qemu_get_nic(nc));
|
|
|
|
} else {
|
|
|
|
qemu_del_net_client(nc);
|
|
|
|
}
|
2009-10-08 22:58:28 +04:00
|
|
|
}
|
2014-09-04 12:39:13 +04:00
|
|
|
|
|
|
|
qemu_del_vm_change_state_handler(net_change_state_entry);
|
2008-10-31 22:10:00 +03:00
|
|
|
}
|
|
|
|
|
2010-02-11 16:44:58 +03:00
|
|
|
void net_check_clients(void)
|
2008-10-31 22:10:00 +03:00
|
|
|
{
|
2012-07-24 19:35:14 +04:00
|
|
|
NetClientState *nc;
|
2011-05-20 19:50:01 +04:00
|
|
|
int i;
|
2008-10-31 22:10:00 +03:00
|
|
|
|
2011-05-20 19:50:00 +04:00
|
|
|
/* Don't warn about the default network setup that you get if
|
|
|
|
* no command line -net or -netdev options are specified. There
|
|
|
|
* are two cases that we would otherwise complain about:
|
|
|
|
* (1) board doesn't support a NIC but the implicit "-net nic"
|
|
|
|
* requested one
|
|
|
|
* (2) CONFIG_SLIRP not set, in which case the implicit "-net nic"
|
|
|
|
* sets up a nic that isn't connected to anything.
|
|
|
|
*/
|
|
|
|
if (default_net) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-07-24 19:35:07 +04:00
|
|
|
net_hub_check_clients();
|
2011-03-15 16:20:54 +03:00
|
|
|
|
2012-07-24 19:35:14 +04:00
|
|
|
QTAILQ_FOREACH(nc, &net_clients, next) {
|
|
|
|
if (!nc->peer) {
|
2010-02-11 16:45:00 +03:00
|
|
|
fprintf(stderr, "Warning: %s %s has no peer\n",
|
2012-07-24 19:35:14 +04:00
|
|
|
nc->info->type == NET_CLIENT_OPTIONS_KIND_NIC ?
|
|
|
|
"nic" : "netdev", nc->name);
|
2010-02-11 16:45:00 +03:00
|
|
|
}
|
|
|
|
}
|
2011-05-20 19:50:01 +04:00
|
|
|
|
|
|
|
/* Check that all NICs requested via -net nic actually got created.
|
|
|
|
* NICs created via -device don't need to be checked here because
|
|
|
|
* they are always instantiated.
|
|
|
|
*/
|
|
|
|
for (i = 0; i < MAX_NICS; i++) {
|
|
|
|
NICInfo *nd = &nd_table[i];
|
|
|
|
if (nd->used && !nd->instantiated) {
|
|
|
|
fprintf(stderr, "Warning: requested NIC (%s, model %s) "
|
|
|
|
"was not created (not supported by this machine?)\n",
|
|
|
|
nd->name ? nd->name : "anonymous",
|
|
|
|
nd->model ? nd->model : "unspecified");
|
|
|
|
}
|
|
|
|
}
|
2008-10-31 22:10:00 +03:00
|
|
|
}
|
2009-10-06 15:17:16 +04:00
|
|
|
|
2015-03-13 15:35:14 +03:00
|
|
|
static int net_init_client(void *dummy, QemuOpts *opts, Error **errp)
|
2009-10-06 15:17:16 +04:00
|
|
|
{
|
2012-04-20 23:50:25 +04:00
|
|
|
Error *local_err = NULL;
|
|
|
|
|
|
|
|
net_client_init(opts, 0, &local_err);
|
2014-01-30 18:07:28 +04:00
|
|
|
if (local_err) {
|
2015-02-10 17:02:06 +03:00
|
|
|
error_report_err(local_err);
|
2009-10-12 12:52:00 +04:00
|
|
|
return -1;
|
2012-04-20 23:50:25 +04:00
|
|
|
}
|
|
|
|
|
2009-10-12 12:52:00 +04:00
|
|
|
return 0;
|
2009-10-08 22:58:27 +04:00
|
|
|
}
|
|
|
|
|
2015-03-13 15:35:14 +03:00
|
|
|
static int net_init_netdev(void *dummy, QemuOpts *opts, Error **errp)
|
2009-10-08 22:58:27 +04:00
|
|
|
{
|
2012-04-20 23:50:25 +04:00
|
|
|
Error *local_err = NULL;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = net_client_init(opts, 1, &local_err);
|
2014-01-30 18:07:28 +04:00
|
|
|
if (local_err) {
|
2015-02-10 17:02:06 +03:00
|
|
|
error_report_err(local_err);
|
2012-04-20 23:50:25 +04:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
2009-10-06 15:17:16 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
int net_init_clients(void)
|
|
|
|
{
|
2010-08-20 15:52:01 +04:00
|
|
|
QemuOptsList *net = qemu_find_opts("net");
|
|
|
|
|
2009-12-08 15:11:47 +03:00
|
|
|
if (default_net) {
|
2009-10-06 15:17:16 +04:00
|
|
|
/* if no clients, we use a default config */
|
2015-02-12 19:07:34 +03:00
|
|
|
qemu_opts_set(net, NULL, "type", "nic", &error_abort);
|
2009-10-06 15:17:16 +04:00
|
|
|
#ifdef CONFIG_SLIRP
|
2015-02-12 19:07:34 +03:00
|
|
|
qemu_opts_set(net, NULL, "type", "user", &error_abort);
|
2009-10-06 15:17:16 +04:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2014-09-04 12:39:13 +04:00
|
|
|
net_change_state_entry =
|
|
|
|
qemu_add_vm_change_state_handler(net_vm_change_state_handler, NULL);
|
|
|
|
|
2012-07-24 19:35:12 +04:00
|
|
|
QTAILQ_INIT(&net_clients);
|
2009-10-08 22:58:23 +04:00
|
|
|
|
2015-03-13 15:35:14 +03:00
|
|
|
if (qemu_opts_foreach(qemu_find_opts("netdev"),
|
|
|
|
net_init_netdev, NULL, NULL)) {
|
2009-10-08 22:58:27 +04:00
|
|
|
return -1;
|
2015-03-13 13:07:24 +03:00
|
|
|
}
|
2009-10-08 22:58:27 +04:00
|
|
|
|
2015-03-13 15:35:14 +03:00
|
|
|
if (qemu_opts_foreach(net, net_init_client, NULL, NULL)) {
|
2009-10-06 15:17:16 +04:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-10-08 22:58:25 +04:00
|
|
|
int net_client_parse(QemuOptsList *opts_list, const char *optarg)
|
2009-10-06 15:17:16 +04:00
|
|
|
{
|
2009-10-08 01:44:15 +04:00
|
|
|
#if defined(CONFIG_SLIRP)
|
2009-11-25 21:48:54 +03:00
|
|
|
int ret;
|
|
|
|
if (net_slirp_parse_legacy(opts_list, optarg, &ret)) {
|
2009-10-06 15:17:16 +04:00
|
|
|
return ret;
|
|
|
|
}
|
2009-10-08 01:44:15 +04:00
|
|
|
#endif
|
2009-11-25 21:48:54 +03:00
|
|
|
|
QemuOpts: Wean off qerror_report_err()
qerror_report_err() is a transitional interface to help with
converting existing monitor commands to QMP. It should not be used
elsewhere.
The only remaining user in qemu-option.c is qemu_opts_parse(). Is it
used in QMP context? If not, we can simply replace
qerror_report_err() by error_report_err().
The uses in qemu-img.c, qemu-io.c, qemu-nbd.c and under tests/ are
clearly not in QMP context.
The uses in vl.c aren't either, because the only QMP command handlers
there are qmp_query_status() and qmp_query_machines(), and they don't
call it.
Remaining uses:
* drive_def(): Command line -drive and such, HMP drive_add and pci_add
* hmp_chardev_add(): HMP chardev-add
* monitor_parse_command(): HMP core
* tmp_config_parse(): Command line -tpmdev
* net_host_device_add(): HMP host_net_add
* net_client_parse(): Command line -net and -netdev
* qemu_global_option(): Command line -global
* vnc_parse_func(): Command line -display, -vnc, default display, HMP
change, QMP change. Bummer.
* qemu_pci_hot_add_nic(): HMP pci_add
* usb_net_init(): Command line -usbdevice, HMP usb_add
Propagate errors through qemu_opts_parse(). Create a convenience
function qemu_opts_parse_noisily() that passes errors to
error_report_err(). Switch all non-QMP users outside tests to it.
That leaves vnc_parse_func(). Propagate errors through it. Since I'm
touching it anyway, rename it to vnc_parse().
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Luiz Capitulino <lcapitulino@redhat.com>
2015-02-13 14:50:26 +03:00
|
|
|
if (!qemu_opts_parse_noisily(opts_list, optarg, true)) {
|
2009-10-06 15:17:16 +04:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2009-12-08 15:11:47 +03:00
|
|
|
default_net = 0;
|
2009-10-06 15:17:16 +04:00
|
|
|
return 0;
|
|
|
|
}
|
2012-03-05 07:08:50 +04:00
|
|
|
|
|
|
|
/* From FreeBSD */
|
|
|
|
/* XXX: optimize */
|
|
|
|
unsigned compute_mcast_idx(const uint8_t *ep)
|
|
|
|
{
|
|
|
|
uint32_t crc;
|
|
|
|
int carry, i, j;
|
|
|
|
uint8_t b;
|
|
|
|
|
|
|
|
crc = 0xffffffff;
|
|
|
|
for (i = 0; i < 6; i++) {
|
|
|
|
b = *ep++;
|
|
|
|
for (j = 0; j < 8; j++) {
|
|
|
|
carry = ((crc & 0x80000000L) ? 1 : 0) ^ (b & 0x01);
|
|
|
|
crc <<= 1;
|
|
|
|
b >>= 1;
|
|
|
|
if (carry) {
|
|
|
|
crc = ((crc ^ POLYNOMIAL) | carry);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return crc >> 26;
|
|
|
|
}
|
2012-11-26 19:03:42 +04:00
|
|
|
|
|
|
|
QemuOptsList qemu_netdev_opts = {
|
|
|
|
.name = "netdev",
|
|
|
|
.implied_opt_name = "type",
|
|
|
|
.head = QTAILQ_HEAD_INITIALIZER(qemu_netdev_opts.head),
|
|
|
|
.desc = {
|
|
|
|
/*
|
|
|
|
* no elements => accept any params
|
|
|
|
* validation will happen later
|
|
|
|
*/
|
|
|
|
{ /* end of list */ }
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
QemuOptsList qemu_net_opts = {
|
|
|
|
.name = "net",
|
|
|
|
.implied_opt_name = "type",
|
|
|
|
.head = QTAILQ_HEAD_INITIALIZER(qemu_net_opts.head),
|
|
|
|
.desc = {
|
|
|
|
/*
|
|
|
|
* no elements => accept any params
|
|
|
|
* validation will happen later
|
|
|
|
*/
|
|
|
|
{ /* end of list */ }
|
|
|
|
},
|
|
|
|
};
|