2008-10-31 20:10:00 +01: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-11-25 19:48:58 +01:00
|
|
|
#include "net.h"
|
2008-10-31 20:10:00 +01:00
|
|
|
|
2009-03-07 17:52:02 +01:00
|
|
|
#include "config-host.h"
|
|
|
|
|
2009-10-22 18:49:05 +02:00
|
|
|
#include "net/tap.h"
|
2009-11-25 19:48:56 +01:00
|
|
|
#include "net/socket.h"
|
2009-11-25 19:48:57 +01:00
|
|
|
#include "net/dump.h"
|
2009-11-25 19:48:54 +01:00
|
|
|
#include "net/slirp.h"
|
2009-11-25 19:48:55 +01:00
|
|
|
#include "net/vde.h"
|
2009-11-25 19:49:27 +01:00
|
|
|
#include "net/util.h"
|
2009-03-07 16:32:56 +01:00
|
|
|
#include "monitor.h"
|
2009-11-25 19:48:58 +01:00
|
|
|
#include "qemu-common.h"
|
2009-03-07 16:32:56 +01:00
|
|
|
#include "qemu_socket.h"
|
2011-11-23 16:11:55 +01:00
|
|
|
#include "qmp-commands.h"
|
2010-02-25 12:54:43 +01:00
|
|
|
#include "hw/qdev.h"
|
2011-02-24 01:57:21 +01:00
|
|
|
#include "iov.h"
|
2009-03-07 16:32:56 +01:00
|
|
|
|
2012-02-04 09:24:46 +01:00
|
|
|
/* Net bridge is currently not supported for W32. */
|
|
|
|
#if !defined(_WIN32)
|
|
|
|
# define CONFIG_NET_BRIDGE
|
|
|
|
#endif
|
|
|
|
|
2009-10-08 20:58:23 +02:00
|
|
|
static QTAILQ_HEAD(, VLANState) vlans;
|
2009-10-08 20:58:28 +02:00
|
|
|
static QTAILQ_HEAD(, VLANClientState) non_vlan_clients;
|
2008-10-31 20:10:00 +01:00
|
|
|
|
2009-12-08 13:11:47 +01:00
|
|
|
int default_net = 1;
|
|
|
|
|
2008-10-31 20:10:00 +01:00
|
|
|
/***********************************************************/
|
|
|
|
/* network device redirectors */
|
|
|
|
|
2009-11-25 19:48:54 +01:00
|
|
|
#if defined(DEBUG_NET)
|
2008-10-31 20:10:00 +01: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 14:53:32 +01:00
|
|
|
if (qemu_isdigit(buf[0])) {
|
2008-10-31 20:10:00 +01: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;
|
|
|
|
}
|
|
|
|
|
2009-01-07 18:46:21 +01:00
|
|
|
void qemu_format_nic_info_str(VLANClientState *vc, uint8_t macaddr[6])
|
|
|
|
{
|
|
|
|
snprintf(vc->info_str, sizeof(vc->info_str),
|
2009-01-08 20:01:37 +01:00
|
|
|
"model=%s,macaddr=%02x:%02x:%02x:%02x:%02x:%02x",
|
|
|
|
vc->model,
|
2009-01-07 18:46:21 +01:00
|
|
|
macaddr[0], macaddr[1], macaddr[2],
|
|
|
|
macaddr[3], macaddr[4], macaddr[5]);
|
|
|
|
}
|
|
|
|
|
2009-10-21 15:25:22 +02:00
|
|
|
void qemu_macaddr_default_if_unset(MACAddr *macaddr)
|
|
|
|
{
|
|
|
|
static int index = 0;
|
|
|
|
static const MACAddr zero = { .a = { 0,0,0,0,0,0 } };
|
|
|
|
|
|
|
|
if (memcmp(macaddr, &zero, sizeof(zero)) != 0)
|
|
|
|
return;
|
|
|
|
macaddr->a[0] = 0x52;
|
|
|
|
macaddr->a[1] = 0x54;
|
|
|
|
macaddr->a[2] = 0x00;
|
|
|
|
macaddr->a[3] = 0x12;
|
|
|
|
macaddr->a[4] = 0x34;
|
|
|
|
macaddr->a[5] = 0x56 + index++;
|
|
|
|
}
|
|
|
|
|
2009-01-07 18:43:44 +01:00
|
|
|
static char *assign_name(VLANClientState *vc1, const char *model)
|
|
|
|
{
|
|
|
|
VLANState *vlan;
|
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 18:45:36 +02:00
|
|
|
VLANClientState *vc;
|
2009-01-07 18:43:44 +01:00
|
|
|
char buf[256];
|
|
|
|
int id = 0;
|
|
|
|
|
2009-10-08 20:58:23 +02:00
|
|
|
QTAILQ_FOREACH(vlan, &vlans, next) {
|
|
|
|
QTAILQ_FOREACH(vc, &vlan->clients, next) {
|
|
|
|
if (vc != vc1 && strcmp(vc->model, model) == 0) {
|
2009-01-07 18:43:44 +01:00
|
|
|
id++;
|
2009-10-08 20:58:23 +02:00
|
|
|
}
|
|
|
|
}
|
2009-01-07 18:43:44 +01:00
|
|
|
}
|
|
|
|
|
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 18:45:36 +02:00
|
|
|
QTAILQ_FOREACH(vc, &non_vlan_clients, next) {
|
|
|
|
if (vc != vc1 && strcmp(vc->model, model) == 0) {
|
|
|
|
id++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-01-07 18:43:44 +01:00
|
|
|
snprintf(buf, sizeof(buf), "%s.%d", model, id);
|
|
|
|
|
2011-08-21 05:09:37 +02:00
|
|
|
return g_strdup(buf);
|
2009-01-07 18:43:44 +01:00
|
|
|
}
|
|
|
|
|
2009-10-08 20:58:32 +02:00
|
|
|
static ssize_t qemu_deliver_packet(VLANClientState *sender,
|
2009-10-22 18:43:40 +02:00
|
|
|
unsigned flags,
|
2009-10-08 20:58:32 +02:00
|
|
|
const uint8_t *data,
|
|
|
|
size_t size,
|
|
|
|
void *opaque);
|
|
|
|
static ssize_t qemu_deliver_packet_iov(VLANClientState *sender,
|
2009-10-22 18:43:40 +02:00
|
|
|
unsigned flags,
|
2009-10-08 20:58:32 +02:00
|
|
|
const struct iovec *iov,
|
|
|
|
int iovcnt,
|
|
|
|
void *opaque);
|
|
|
|
|
2009-11-25 19:49:02 +01:00
|
|
|
VLANClientState *qemu_new_net_client(NetClientInfo *info,
|
|
|
|
VLANState *vlan,
|
|
|
|
VLANClientState *peer,
|
|
|
|
const char *model,
|
|
|
|
const char *name)
|
2008-10-31 20:10:00 +01:00
|
|
|
{
|
2009-10-08 20:58:23 +02:00
|
|
|
VLANClientState *vc;
|
|
|
|
|
2009-11-25 19:49:02 +01:00
|
|
|
assert(info->size >= sizeof(VLANClientState));
|
|
|
|
|
2011-08-21 05:09:37 +02:00
|
|
|
vc = g_malloc0(info->size);
|
2009-10-08 20:58:23 +02:00
|
|
|
|
2009-11-25 19:49:30 +01:00
|
|
|
vc->info = info;
|
2011-08-21 05:09:37 +02:00
|
|
|
vc->model = g_strdup(model);
|
2009-11-25 19:49:02 +01:00
|
|
|
if (name) {
|
2011-08-21 05:09:37 +02:00
|
|
|
vc->name = g_strdup(name);
|
2009-11-25 19:49:02 +01:00
|
|
|
} else {
|
2009-01-07 18:48:51 +01:00
|
|
|
vc->name = assign_name(vc, model);
|
2009-11-25 19:49:02 +01:00
|
|
|
}
|
2009-10-08 20:58:23 +02:00
|
|
|
|
2009-10-08 20:58:24 +02:00
|
|
|
if (vlan) {
|
2009-10-08 20:58:30 +02:00
|
|
|
assert(!peer);
|
2009-10-08 20:58:24 +02:00
|
|
|
vc->vlan = vlan;
|
|
|
|
QTAILQ_INSERT_TAIL(&vc->vlan->clients, vc, next);
|
2009-10-08 20:58:28 +02:00
|
|
|
} else {
|
2009-10-08 20:58:30 +02:00
|
|
|
if (peer) {
|
2010-02-26 15:50:51 +01:00
|
|
|
assert(!peer->peer);
|
2009-10-08 20:58:30 +02:00
|
|
|
vc->peer = peer;
|
|
|
|
peer->peer = vc;
|
|
|
|
}
|
2009-10-08 20:58:28 +02:00
|
|
|
QTAILQ_INSERT_TAIL(&non_vlan_clients, vc, next);
|
2009-10-08 20:58:32 +02:00
|
|
|
|
|
|
|
vc->send_queue = qemu_new_net_queue(qemu_deliver_packet,
|
|
|
|
qemu_deliver_packet_iov,
|
|
|
|
vc);
|
2009-10-08 20:58:24 +02:00
|
|
|
}
|
2008-10-31 20:10:00 +01:00
|
|
|
|
|
|
|
return vc;
|
|
|
|
}
|
|
|
|
|
2009-11-25 19:49:10 +01:00
|
|
|
NICState *qemu_new_nic(NetClientInfo *info,
|
|
|
|
NICConf *conf,
|
|
|
|
const char *model,
|
|
|
|
const char *name,
|
|
|
|
void *opaque)
|
|
|
|
{
|
|
|
|
VLANClientState *nc;
|
|
|
|
NICState *nic;
|
|
|
|
|
|
|
|
assert(info->type == NET_CLIENT_TYPE_NIC);
|
|
|
|
assert(info->size >= sizeof(NICState));
|
|
|
|
|
|
|
|
nc = qemu_new_net_client(info, conf->vlan, conf->peer, model, name);
|
|
|
|
|
|
|
|
nic = DO_UPCAST(NICState, nc, nc);
|
|
|
|
nic->conf = conf;
|
|
|
|
nic->opaque = opaque;
|
|
|
|
|
|
|
|
return nic;
|
|
|
|
}
|
|
|
|
|
2010-09-20 18:08:41 +02:00
|
|
|
static void qemu_cleanup_vlan_client(VLANClientState *vc)
|
2008-10-31 20:10:00 +01:00
|
|
|
{
|
2009-10-08 20:58:24 +02:00
|
|
|
if (vc->vlan) {
|
|
|
|
QTAILQ_REMOVE(&vc->vlan->clients, vc, next);
|
2009-10-08 20:58:28 +02:00
|
|
|
} else {
|
|
|
|
QTAILQ_REMOVE(&non_vlan_clients, vc, next);
|
2009-10-08 20:58:24 +02:00
|
|
|
}
|
2008-10-31 20:10:00 +01:00
|
|
|
|
2009-11-25 19:49:30 +01:00
|
|
|
if (vc->info->cleanup) {
|
|
|
|
vc->info->cleanup(vc);
|
2009-10-08 20:58:23 +02:00
|
|
|
}
|
2010-09-20 18:08:41 +02:00
|
|
|
}
|
2009-10-08 20:58:23 +02:00
|
|
|
|
2010-09-20 18:08:41 +02:00
|
|
|
static void qemu_free_vlan_client(VLANClientState *vc)
|
|
|
|
{
|
|
|
|
if (!vc->vlan) {
|
|
|
|
if (vc->send_queue) {
|
|
|
|
qemu_del_net_queue(vc->send_queue);
|
|
|
|
}
|
|
|
|
if (vc->peer) {
|
|
|
|
vc->peer->peer = NULL;
|
|
|
|
}
|
|
|
|
}
|
2011-08-21 05:09:37 +02:00
|
|
|
g_free(vc->name);
|
|
|
|
g_free(vc->model);
|
|
|
|
g_free(vc);
|
2008-10-31 20:10:00 +01:00
|
|
|
}
|
|
|
|
|
2010-09-20 18:08:41 +02:00
|
|
|
void qemu_del_vlan_client(VLANClientState *vc)
|
|
|
|
{
|
|
|
|
/* If there is a peer NIC, delete and cleanup client, but do not free. */
|
|
|
|
if (!vc->vlan && vc->peer && vc->peer->info->type == NET_CLIENT_TYPE_NIC) {
|
|
|
|
NICState *nic = DO_UPCAST(NICState, nc, vc->peer);
|
|
|
|
if (nic->peer_deleted) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
nic->peer_deleted = true;
|
|
|
|
/* Let NIC know peer is gone. */
|
|
|
|
vc->peer->link_down = true;
|
|
|
|
if (vc->peer->info->link_status_changed) {
|
|
|
|
vc->peer->info->link_status_changed(vc->peer);
|
|
|
|
}
|
|
|
|
qemu_cleanup_vlan_client(vc);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If this is a peer NIC and peer has already been deleted, free it now. */
|
|
|
|
if (!vc->vlan && vc->peer && vc->info->type == NET_CLIENT_TYPE_NIC) {
|
|
|
|
NICState *nic = DO_UPCAST(NICState, nc, vc);
|
|
|
|
if (nic->peer_deleted) {
|
|
|
|
qemu_free_vlan_client(vc->peer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
qemu_cleanup_vlan_client(vc);
|
|
|
|
qemu_free_vlan_client(vc);
|
|
|
|
}
|
|
|
|
|
2009-11-25 19:48:54 +01:00
|
|
|
VLANClientState *
|
2009-06-24 14:42:31 +02:00
|
|
|
qemu_find_vlan_client_by_name(Monitor *mon, int vlan_id,
|
|
|
|
const char *client_str)
|
|
|
|
{
|
|
|
|
VLANState *vlan;
|
|
|
|
VLANClientState *vc;
|
|
|
|
|
|
|
|
vlan = qemu_find_vlan(vlan_id, 0);
|
|
|
|
if (!vlan) {
|
|
|
|
monitor_printf(mon, "unknown VLAN %d\n", vlan_id);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2009-10-08 20:58:23 +02:00
|
|
|
QTAILQ_FOREACH(vc, &vlan->clients, next) {
|
2009-06-24 14:42:31 +02:00
|
|
|
if (!strcmp(vc->name, client_str)) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!vc) {
|
|
|
|
monitor_printf(mon, "can't find device %s on VLAN %d\n",
|
|
|
|
client_str, vlan_id);
|
|
|
|
}
|
|
|
|
|
|
|
|
return vc;
|
|
|
|
}
|
|
|
|
|
2009-11-25 19:49:31 +01:00
|
|
|
void qemu_foreach_nic(qemu_nic_foreach func, void *opaque)
|
|
|
|
{
|
|
|
|
VLANClientState *nc;
|
|
|
|
VLANState *vlan;
|
|
|
|
|
|
|
|
QTAILQ_FOREACH(nc, &non_vlan_clients, next) {
|
|
|
|
if (nc->info->type == NET_CLIENT_TYPE_NIC) {
|
|
|
|
func(DO_UPCAST(NICState, nc, nc), opaque);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
QTAILQ_FOREACH(vlan, &vlans, next) {
|
|
|
|
QTAILQ_FOREACH(nc, &vlan->clients, next) {
|
|
|
|
if (nc->info->type == NET_CLIENT_TYPE_NIC) {
|
|
|
|
func(DO_UPCAST(NICState, nc, nc), opaque);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-04-29 10:36:43 +02:00
|
|
|
int qemu_can_send_packet(VLANClientState *sender)
|
2008-10-31 20:10:00 +01:00
|
|
|
{
|
2009-04-29 10:36:43 +02:00
|
|
|
VLANState *vlan = sender->vlan;
|
2008-10-31 20:10:00 +01:00
|
|
|
VLANClientState *vc;
|
|
|
|
|
2009-10-08 20:58:32 +02:00
|
|
|
if (sender->peer) {
|
2009-10-27 19:16:36 +01:00
|
|
|
if (sender->peer->receive_disabled) {
|
|
|
|
return 0;
|
2009-11-25 19:49:30 +01:00
|
|
|
} else if (sender->peer->info->can_receive &&
|
|
|
|
!sender->peer->info->can_receive(sender->peer)) {
|
2009-10-08 20:58:32 +02:00
|
|
|
return 0;
|
2009-10-27 19:16:36 +01:00
|
|
|
} else {
|
|
|
|
return 1;
|
2009-10-08 20:58:32 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-10-08 20:58:24 +02:00
|
|
|
if (!sender->vlan) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2009-10-08 20:58:23 +02:00
|
|
|
QTAILQ_FOREACH(vc, &vlan->clients, next) {
|
2009-04-29 10:36:43 +02:00
|
|
|
if (vc == sender) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2009-05-18 14:13:16 +02:00
|
|
|
/* no can_receive() handler, they can always receive */
|
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-02 23:25:02 +01:00
|
|
|
if (vc->info->can_receive && !vc->info->can_receive(vc)) {
|
|
|
|
return 0;
|
2008-10-31 20:10:00 +01: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-02 23:25:02 +01:00
|
|
|
return 1;
|
2008-10-31 20:10:00 +01:00
|
|
|
}
|
|
|
|
|
2009-10-08 20:58:32 +02:00
|
|
|
static ssize_t qemu_deliver_packet(VLANClientState *sender,
|
2009-10-22 18:43:40 +02:00
|
|
|
unsigned flags,
|
2009-10-08 20:58:32 +02:00
|
|
|
const uint8_t *data,
|
|
|
|
size_t size,
|
|
|
|
void *opaque)
|
|
|
|
{
|
|
|
|
VLANClientState *vc = opaque;
|
2009-10-27 19:16:36 +01:00
|
|
|
ssize_t ret;
|
2009-10-08 20:58:32 +02:00
|
|
|
|
|
|
|
if (vc->link_down) {
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
2009-10-27 19:16:36 +01:00
|
|
|
if (vc->receive_disabled) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-11-25 19:49:30 +01:00
|
|
|
if (flags & QEMU_NET_PACKET_FLAG_RAW && vc->info->receive_raw) {
|
|
|
|
ret = vc->info->receive_raw(vc, data, size);
|
2009-10-27 19:16:36 +01:00
|
|
|
} else {
|
2009-11-25 19:49:30 +01:00
|
|
|
ret = vc->info->receive(vc, data, size);
|
2009-10-27 19:16:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (ret == 0) {
|
|
|
|
vc->receive_disabled = 1;
|
|
|
|
};
|
|
|
|
|
|
|
|
return ret;
|
2009-10-08 20:58:32 +02:00
|
|
|
}
|
|
|
|
|
2009-10-08 20:58:31 +02:00
|
|
|
static ssize_t qemu_vlan_deliver_packet(VLANClientState *sender,
|
2009-10-22 18:43:40 +02:00
|
|
|
unsigned flags,
|
2009-10-08 20:58:31 +02:00
|
|
|
const uint8_t *buf,
|
|
|
|
size_t size,
|
|
|
|
void *opaque)
|
2008-10-31 20:10:00 +01:00
|
|
|
{
|
2009-10-08 20:58:31 +02:00
|
|
|
VLANState *vlan = opaque;
|
2008-10-31 20:10:00 +01:00
|
|
|
VLANClientState *vc;
|
2009-10-27 19:16:36 +01:00
|
|
|
ssize_t ret = -1;
|
2008-10-31 20:10:00 +01:00
|
|
|
|
2009-10-08 20:58:31 +02:00
|
|
|
QTAILQ_FOREACH(vc, &vlan->clients, next) {
|
2009-04-29 12:34:52 +02:00
|
|
|
ssize_t len;
|
|
|
|
|
|
|
|
if (vc == sender) {
|
|
|
|
continue;
|
2009-04-21 21:56:41 +02:00
|
|
|
}
|
2009-04-29 12:34:52 +02:00
|
|
|
|
|
|
|
if (vc->link_down) {
|
|
|
|
ret = size;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2009-10-27 19:16:36 +01:00
|
|
|
if (vc->receive_disabled) {
|
|
|
|
ret = 0;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2009-11-25 19:49:30 +01:00
|
|
|
if (flags & QEMU_NET_PACKET_FLAG_RAW && vc->info->receive_raw) {
|
|
|
|
len = vc->info->receive_raw(vc, buf, size);
|
2009-10-27 19:16:36 +01:00
|
|
|
} else {
|
2009-11-25 19:49:30 +01:00
|
|
|
len = vc->info->receive(vc, buf, size);
|
2009-10-27 19:16:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (len == 0) {
|
|
|
|
vc->receive_disabled = 1;
|
|
|
|
}
|
2009-04-29 12:34:52 +02:00
|
|
|
|
|
|
|
ret = (ret >= 0) ? ret : len;
|
2009-10-27 19:16:36 +01:00
|
|
|
|
2009-04-21 21:56:41 +02:00
|
|
|
}
|
2009-04-29 12:34:52 +02:00
|
|
|
|
|
|
|
return ret;
|
2009-04-21 21:56:41 +02:00
|
|
|
}
|
|
|
|
|
2009-06-18 19:21:29 +02:00
|
|
|
void qemu_purge_queued_packets(VLANClientState *vc)
|
|
|
|
{
|
2009-10-08 20:58:32 +02:00
|
|
|
NetQueue *queue;
|
|
|
|
|
|
|
|
if (!vc->peer && !vc->vlan) {
|
2009-10-08 20:58:24 +02:00
|
|
|
return;
|
2009-10-08 20:58:32 +02:00
|
|
|
}
|
2009-10-08 20:58:24 +02:00
|
|
|
|
2009-10-08 20:58:32 +02:00
|
|
|
if (vc->peer) {
|
|
|
|
queue = vc->peer->send_queue;
|
|
|
|
} else {
|
|
|
|
queue = vc->vlan->send_queue;
|
|
|
|
}
|
|
|
|
|
|
|
|
qemu_net_queue_purge(queue, vc);
|
2009-06-18 19:21:29 +02:00
|
|
|
}
|
|
|
|
|
2009-04-29 13:15:26 +02:00
|
|
|
void qemu_flush_queued_packets(VLANClientState *vc)
|
2009-04-29 12:48:12 +02:00
|
|
|
{
|
2009-10-08 20:58:32 +02:00
|
|
|
NetQueue *queue;
|
|
|
|
|
2009-10-27 19:16:36 +01:00
|
|
|
vc->receive_disabled = 0;
|
|
|
|
|
2009-10-08 20:58:32 +02:00
|
|
|
if (vc->vlan) {
|
|
|
|
queue = vc->vlan->send_queue;
|
|
|
|
} else {
|
|
|
|
queue = vc->send_queue;
|
|
|
|
}
|
2009-10-08 20:58:24 +02:00
|
|
|
|
2009-10-08 20:58:32 +02:00
|
|
|
qemu_net_queue_flush(queue);
|
2009-04-29 12:48:12 +02:00
|
|
|
}
|
|
|
|
|
2009-10-22 18:43:41 +02:00
|
|
|
static ssize_t qemu_send_packet_async_with_flags(VLANClientState *sender,
|
|
|
|
unsigned flags,
|
|
|
|
const uint8_t *buf, int size,
|
|
|
|
NetPacketSent *sent_cb)
|
2009-04-21 21:56:41 +02:00
|
|
|
{
|
2009-10-08 20:58:32 +02:00
|
|
|
NetQueue *queue;
|
2009-01-08 20:44:06 +01:00
|
|
|
|
2008-10-31 20:10:00 +01:00
|
|
|
#ifdef DEBUG_NET
|
2009-10-08 20:58:24 +02:00
|
|
|
printf("qemu_send_packet_async:\n");
|
2008-10-31 20:10:00 +01:00
|
|
|
hex_dump(stdout, buf, size);
|
|
|
|
#endif
|
2009-04-29 13:15:26 +02:00
|
|
|
|
2009-10-08 20:58:32 +02:00
|
|
|
if (sender->link_down || (!sender->peer && !sender->vlan)) {
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sender->peer) {
|
|
|
|
queue = sender->peer->send_queue;
|
|
|
|
} else {
|
|
|
|
queue = sender->vlan->send_queue;
|
|
|
|
}
|
|
|
|
|
2009-10-22 18:43:41 +02:00
|
|
|
return qemu_net_queue_send(queue, sender, flags, buf, size, sent_cb);
|
|
|
|
}
|
|
|
|
|
|
|
|
ssize_t qemu_send_packet_async(VLANClientState *sender,
|
|
|
|
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 13:15:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void qemu_send_packet(VLANClientState *vc, const uint8_t *buf, int size)
|
|
|
|
{
|
|
|
|
qemu_send_packet_async(vc, buf, size, NULL);
|
2008-10-31 20:10:00 +01:00
|
|
|
}
|
|
|
|
|
2009-10-22 18:43:41 +02:00
|
|
|
ssize_t qemu_send_packet_raw(VLANClientState *vc, const uint8_t *buf, int size)
|
|
|
|
{
|
|
|
|
return qemu_send_packet_async_with_flags(vc, QEMU_NET_PACKET_FLAG_RAW,
|
|
|
|
buf, size, NULL);
|
|
|
|
}
|
|
|
|
|
2008-12-17 20:13:11 +01:00
|
|
|
static ssize_t vc_sendv_compat(VLANClientState *vc, const struct iovec *iov,
|
|
|
|
int iovcnt)
|
|
|
|
{
|
|
|
|
uint8_t buffer[4096];
|
2011-02-24 01:57:21 +01:00
|
|
|
size_t offset;
|
2008-12-17 20:13:11 +01:00
|
|
|
|
2011-02-24 01:57:21 +01:00
|
|
|
offset = iov_to_buf(iov, iovcnt, buffer, 0, sizeof(buffer));
|
2008-12-17 20:13:11 +01:00
|
|
|
|
2009-11-25 19:49:30 +01:00
|
|
|
return vc->info->receive(vc, buffer, offset);
|
2008-12-17 20:13:11 +01:00
|
|
|
}
|
|
|
|
|
2009-10-08 20:58:32 +02:00
|
|
|
static ssize_t qemu_deliver_packet_iov(VLANClientState *sender,
|
2009-10-22 18:43:40 +02:00
|
|
|
unsigned flags,
|
2009-10-08 20:58:32 +02:00
|
|
|
const struct iovec *iov,
|
|
|
|
int iovcnt,
|
|
|
|
void *opaque)
|
|
|
|
{
|
|
|
|
VLANClientState *vc = opaque;
|
|
|
|
|
|
|
|
if (vc->link_down) {
|
2011-02-24 01:57:21 +01:00
|
|
|
return iov_size(iov, iovcnt);
|
2009-10-08 20:58:32 +02:00
|
|
|
}
|
|
|
|
|
2009-11-25 19:49:30 +01:00
|
|
|
if (vc->info->receive_iov) {
|
|
|
|
return vc->info->receive_iov(vc, iov, iovcnt);
|
2009-10-08 20:58:32 +02:00
|
|
|
} else {
|
|
|
|
return vc_sendv_compat(vc, iov, iovcnt);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-10-08 20:58:31 +02:00
|
|
|
static ssize_t qemu_vlan_deliver_packet_iov(VLANClientState *sender,
|
2009-10-22 18:43:40 +02:00
|
|
|
unsigned flags,
|
2009-10-08 20:58:31 +02:00
|
|
|
const struct iovec *iov,
|
|
|
|
int iovcnt,
|
|
|
|
void *opaque)
|
2008-12-17 20:13:11 +01:00
|
|
|
{
|
2009-10-08 20:58:31 +02:00
|
|
|
VLANState *vlan = opaque;
|
2008-12-17 20:13:11 +01:00
|
|
|
VLANClientState *vc;
|
2009-10-08 20:58:31 +02:00
|
|
|
ssize_t ret = -1;
|
2009-04-29 12:48:12 +02:00
|
|
|
|
2009-10-08 20:58:31 +02:00
|
|
|
QTAILQ_FOREACH(vc, &vlan->clients, next) {
|
2009-04-29 12:48:12 +02:00
|
|
|
ssize_t len;
|
|
|
|
|
|
|
|
if (vc == sender) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (vc->link_down) {
|
2011-02-24 01:57:21 +01:00
|
|
|
ret = iov_size(iov, iovcnt);
|
2009-04-29 12:48:12 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2009-10-22 18:43:41 +02:00
|
|
|
assert(!(flags & QEMU_NET_PACKET_FLAG_RAW));
|
|
|
|
|
2009-11-25 19:49:30 +01:00
|
|
|
if (vc->info->receive_iov) {
|
|
|
|
len = vc->info->receive_iov(vc, iov, iovcnt);
|
2009-04-29 12:48:12 +02:00
|
|
|
} else {
|
|
|
|
len = vc_sendv_compat(vc, iov, iovcnt);
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = (ret >= 0) ? ret : len;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2009-04-29 13:15:26 +02:00
|
|
|
ssize_t qemu_sendv_packet_async(VLANClientState *sender,
|
|
|
|
const struct iovec *iov, int iovcnt,
|
|
|
|
NetPacketSent *sent_cb)
|
2009-04-29 12:48:12 +02:00
|
|
|
{
|
2009-10-08 20:58:32 +02:00
|
|
|
NetQueue *queue;
|
|
|
|
|
|
|
|
if (sender->link_down || (!sender->peer && !sender->vlan)) {
|
2011-02-24 01:57:21 +01:00
|
|
|
return iov_size(iov, iovcnt);
|
2009-04-29 12:48:12 +02:00
|
|
|
}
|
|
|
|
|
2009-10-08 20:58:32 +02:00
|
|
|
if (sender->peer) {
|
|
|
|
queue = sender->peer->send_queue;
|
|
|
|
} else {
|
|
|
|
queue = sender->vlan->send_queue;
|
|
|
|
}
|
|
|
|
|
2009-10-22 18:43:40 +02:00
|
|
|
return qemu_net_queue_send_iov(queue, sender,
|
|
|
|
QEMU_NET_PACKET_FLAG_NONE,
|
|
|
|
iov, iovcnt, sent_cb);
|
2008-12-17 20:13:11 +01:00
|
|
|
}
|
|
|
|
|
2009-04-29 13:15:26 +02:00
|
|
|
ssize_t
|
|
|
|
qemu_sendv_packet(VLANClientState *vc, const struct iovec *iov, int iovcnt)
|
|
|
|
{
|
|
|
|
return qemu_sendv_packet_async(vc, iov, iovcnt, NULL);
|
|
|
|
}
|
|
|
|
|
2008-10-31 20:10:00 +01:00
|
|
|
/* find or alloc a new VLAN */
|
2009-06-24 14:42:31 +02:00
|
|
|
VLANState *qemu_find_vlan(int id, int allocate)
|
2008-10-31 20:10:00 +01:00
|
|
|
{
|
2009-10-08 20:58:23 +02:00
|
|
|
VLANState *vlan;
|
|
|
|
|
|
|
|
QTAILQ_FOREACH(vlan, &vlans, next) {
|
|
|
|
if (vlan->id == id) {
|
2008-10-31 20:10:00 +01:00
|
|
|
return vlan;
|
2009-10-08 20:58:23 +02:00
|
|
|
}
|
2008-10-31 20:10:00 +01:00
|
|
|
}
|
2009-10-08 20:58:23 +02:00
|
|
|
|
2009-06-24 14:42:31 +02:00
|
|
|
if (!allocate) {
|
|
|
|
return NULL;
|
|
|
|
}
|
2009-10-08 20:58:23 +02:00
|
|
|
|
2011-08-21 05:09:37 +02:00
|
|
|
vlan = g_malloc0(sizeof(VLANState));
|
2008-10-31 20:10:00 +01:00
|
|
|
vlan->id = id;
|
2009-10-08 20:58:23 +02:00
|
|
|
QTAILQ_INIT(&vlan->clients);
|
2009-10-08 20:58:31 +02:00
|
|
|
|
|
|
|
vlan->send_queue = qemu_new_net_queue(qemu_vlan_deliver_packet,
|
|
|
|
qemu_vlan_deliver_packet_iov,
|
|
|
|
vlan);
|
2009-10-08 20:58:23 +02:00
|
|
|
|
|
|
|
QTAILQ_INSERT_TAIL(&vlans, vlan, next);
|
|
|
|
|
2008-10-31 20:10:00 +01:00
|
|
|
return vlan;
|
|
|
|
}
|
|
|
|
|
2009-10-21 15:25:24 +02:00
|
|
|
VLANClientState *qemu_find_netdev(const char *id)
|
2009-10-08 20:58:29 +02:00
|
|
|
{
|
|
|
|
VLANClientState *vc;
|
|
|
|
|
|
|
|
QTAILQ_FOREACH(vc, &non_vlan_clients, next) {
|
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 18:45:37 +02:00
|
|
|
if (vc->info->type == NET_CLIENT_TYPE_NIC)
|
|
|
|
continue;
|
2009-10-08 20:58:29 +02:00
|
|
|
if (!strcmp(vc->name, id)) {
|
|
|
|
return vc;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2009-02-11 16:20:03 +01: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 03:53:51 +02:00
|
|
|
int qemu_show_nic_models(const char *arg, const char *const *models)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (!arg || strcmp(arg, "?"))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
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 20:03:57 +01:00
|
|
|
void qemu_check_nic_model(NICInfo *nd, const char *model)
|
|
|
|
{
|
|
|
|
const char *models[2];
|
|
|
|
|
|
|
|
models[0] = model;
|
|
|
|
models[1] = NULL;
|
|
|
|
|
2009-09-25 03:53:51 +02: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 20:03:57 +01:00
|
|
|
}
|
|
|
|
|
2009-09-25 03:53:51 +02:00
|
|
|
int qemu_find_nic_model(NICInfo *nd, const char * const *models,
|
|
|
|
const char *default_model)
|
2009-01-13 20:03:57 +01:00
|
|
|
{
|
2009-09-25 03:53:51 +02:00
|
|
|
int i;
|
2009-01-13 20:03:57 +01:00
|
|
|
|
|
|
|
if (!nd->model)
|
2011-08-21 05:09:37 +02:00
|
|
|
nd->model = g_strdup(default_model);
|
2009-01-13 20:03:57 +01:00
|
|
|
|
2009-09-25 03:53:51 +02:00
|
|
|
for (i = 0 ; models[i]; i++) {
|
|
|
|
if (strcmp(nd->model, models[i]) == 0)
|
|
|
|
return i;
|
2009-01-13 20:03:57 +01:00
|
|
|
}
|
|
|
|
|
2011-06-22 14:03:54 +02:00
|
|
|
error_report("Unsupported NIC model: %s", nd->model);
|
2009-09-25 03:53:51 +02:00
|
|
|
return -1;
|
2009-01-13 20:03:57 +01:00
|
|
|
}
|
|
|
|
|
2009-10-22 18:49:07 +02:00
|
|
|
int net_handle_fd_param(Monitor *mon, const char *param)
|
2009-07-22 10:11:42 +02:00
|
|
|
{
|
2010-10-25 07:39:59 +02:00
|
|
|
int fd;
|
|
|
|
|
|
|
|
if (!qemu_isdigit(param[0]) && mon) {
|
2009-07-22 10:11:42 +02:00
|
|
|
|
|
|
|
fd = monitor_get_fd(mon, param);
|
|
|
|
if (fd == -1) {
|
2010-02-18 17:25:24 +01:00
|
|
|
error_report("No file descriptor named %s found", param);
|
2009-07-22 10:11:42 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
} else {
|
2011-09-28 12:41:32 +02:00
|
|
|
fd = qemu_parse_fd(param);
|
2009-07-22 10:11:42 +02:00
|
|
|
}
|
2010-10-25 07:39:59 +02:00
|
|
|
|
|
|
|
return fd;
|
2009-07-22 10:11:42 +02:00
|
|
|
}
|
|
|
|
|
2012-04-12 18:20:40 +02:00
|
|
|
static int net_init_nic(QemuOpts *opts, const char *name, VLANState *vlan)
|
2009-10-06 13:17:06 +02:00
|
|
|
{
|
|
|
|
int idx;
|
|
|
|
NICInfo *nd;
|
2009-10-08 20:58:29 +02:00
|
|
|
const char *netdev;
|
2009-10-06 13:17:06 +02:00
|
|
|
|
|
|
|
idx = nic_get_free_idx();
|
|
|
|
if (idx == -1 || nb_nics >= MAX_NICS) {
|
2010-02-18 17:25:24 +01:00
|
|
|
error_report("Too Many NICs");
|
2009-10-06 13:17:06 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
nd = &nd_table[idx];
|
|
|
|
|
|
|
|
memset(nd, 0, sizeof(*nd));
|
|
|
|
|
2009-10-08 20:58:29 +02:00
|
|
|
if ((netdev = qemu_opt_get(opts, "netdev"))) {
|
|
|
|
nd->netdev = qemu_find_netdev(netdev);
|
|
|
|
if (!nd->netdev) {
|
2010-02-18 17:25:24 +01:00
|
|
|
error_report("netdev '%s' not found", netdev);
|
2009-10-08 20:58:29 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
assert(vlan);
|
|
|
|
nd->vlan = vlan;
|
|
|
|
}
|
2009-10-08 20:58:21 +02:00
|
|
|
if (name) {
|
2011-08-21 05:09:37 +02:00
|
|
|
nd->name = g_strdup(name);
|
2009-10-08 20:58:21 +02:00
|
|
|
}
|
2009-10-06 13:17:06 +02:00
|
|
|
if (qemu_opt_get(opts, "model")) {
|
2011-08-21 05:09:37 +02:00
|
|
|
nd->model = g_strdup(qemu_opt_get(opts, "model"));
|
2009-10-06 13:17:06 +02:00
|
|
|
}
|
|
|
|
if (qemu_opt_get(opts, "addr")) {
|
2011-08-21 05:09:37 +02:00
|
|
|
nd->devaddr = g_strdup(qemu_opt_get(opts, "addr"));
|
2009-10-06 13:17:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (qemu_opt_get(opts, "macaddr") &&
|
2011-07-20 12:20:22 +02:00
|
|
|
net_parse_macaddr(nd->macaddr.a, qemu_opt_get(opts, "macaddr")) < 0) {
|
2010-02-18 17:25:24 +01:00
|
|
|
error_report("invalid syntax for ethernet address");
|
2009-10-06 13:17:06 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2011-07-20 12:20:22 +02:00
|
|
|
qemu_macaddr_default_if_unset(&nd->macaddr);
|
2009-10-06 13:17:06 +02:00
|
|
|
|
2010-02-25 12:54:43 +01:00
|
|
|
nd->nvectors = qemu_opt_get_number(opts, "vectors",
|
|
|
|
DEV_NVECTORS_UNSPECIFIED);
|
|
|
|
if (nd->nvectors != DEV_NVECTORS_UNSPECIFIED &&
|
2009-10-06 13:17:06 +02:00
|
|
|
(nd->nvectors < 0 || nd->nvectors > 0x7ffffff)) {
|
2010-02-18 17:25:24 +01:00
|
|
|
error_report("invalid # of vectors: %d", nd->nvectors);
|
2009-10-06 13:17:06 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
nd->used = 1;
|
|
|
|
nb_nics++;
|
|
|
|
|
|
|
|
return idx;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define NET_COMMON_PARAMS_DESC \
|
|
|
|
{ \
|
|
|
|
.name = "type", \
|
|
|
|
.type = QEMU_OPT_STRING, \
|
|
|
|
.help = "net client type (nic, tap etc.)", \
|
|
|
|
}, { \
|
|
|
|
.name = "vlan", \
|
|
|
|
.type = QEMU_OPT_NUMBER, \
|
|
|
|
.help = "vlan number", \
|
|
|
|
}, { \
|
|
|
|
.name = "name", \
|
|
|
|
.type = QEMU_OPT_STRING, \
|
|
|
|
.help = "identifier for monitor commands", \
|
|
|
|
}
|
|
|
|
|
2009-10-08 20:58:21 +02:00
|
|
|
typedef int (*net_client_init_func)(QemuOpts *opts,
|
2009-10-08 20:58:27 +02:00
|
|
|
const char *name,
|
|
|
|
VLANState *vlan);
|
2009-10-06 13:17:06 +02:00
|
|
|
|
|
|
|
/* magic number, but compiler will warn if too small */
|
|
|
|
#define NET_MAX_DESC 20
|
|
|
|
|
2010-02-21 17:01:30 +01:00
|
|
|
static const struct {
|
2009-10-06 13:17:06 +02:00
|
|
|
const char *type;
|
|
|
|
net_client_init_func init;
|
|
|
|
QemuOptDesc desc[NET_MAX_DESC];
|
2011-07-20 12:20:20 +02:00
|
|
|
} net_client_types[NET_CLIENT_TYPE_MAX] = {
|
|
|
|
[NET_CLIENT_TYPE_NONE] = {
|
2009-10-06 13:17:06 +02:00
|
|
|
.type = "none",
|
|
|
|
.desc = {
|
|
|
|
NET_COMMON_PARAMS_DESC,
|
|
|
|
{ /* end of list */ }
|
|
|
|
},
|
2011-07-20 12:20:20 +02:00
|
|
|
},
|
|
|
|
[NET_CLIENT_TYPE_NIC] = {
|
2009-10-06 13:17:06 +02:00
|
|
|
.type = "nic",
|
|
|
|
.init = net_init_nic,
|
|
|
|
.desc = {
|
|
|
|
NET_COMMON_PARAMS_DESC,
|
2009-10-08 20:58:29 +02:00
|
|
|
{
|
|
|
|
.name = "netdev",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "id of -netdev to connect to",
|
|
|
|
},
|
2009-10-06 13:17:06 +02:00
|
|
|
{
|
|
|
|
.name = "macaddr",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "MAC address",
|
|
|
|
}, {
|
|
|
|
.name = "model",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "device model (e1000, rtl8139, virtio etc.)",
|
|
|
|
}, {
|
|
|
|
.name = "addr",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "PCI device address",
|
|
|
|
}, {
|
|
|
|
.name = "vectors",
|
|
|
|
.type = QEMU_OPT_NUMBER,
|
|
|
|
.help = "number of MSI-x vectors, 0 to disable MSI-X",
|
|
|
|
},
|
|
|
|
{ /* end of list */ }
|
|
|
|
},
|
2011-07-20 12:20:20 +02:00
|
|
|
},
|
2009-10-06 13:17:07 +02:00
|
|
|
#ifdef CONFIG_SLIRP
|
2011-07-20 12:20:20 +02:00
|
|
|
[NET_CLIENT_TYPE_USER] = {
|
2009-10-06 13:17:07 +02:00
|
|
|
.type = "user",
|
|
|
|
.init = net_init_slirp,
|
|
|
|
.desc = {
|
|
|
|
NET_COMMON_PARAMS_DESC,
|
|
|
|
{
|
|
|
|
.name = "hostname",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "client hostname reported by the builtin DHCP server",
|
|
|
|
}, {
|
|
|
|
.name = "restrict",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "isolate the guest from the host (y|yes|n|no)",
|
|
|
|
}, {
|
|
|
|
.name = "ip",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "legacy parameter, use net= instead",
|
|
|
|
}, {
|
|
|
|
.name = "net",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "IP address and optional netmask",
|
|
|
|
}, {
|
|
|
|
.name = "host",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "guest-visible address of the host",
|
|
|
|
}, {
|
|
|
|
.name = "tftp",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "root directory of the built-in TFTP server",
|
|
|
|
}, {
|
|
|
|
.name = "bootfile",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "BOOTP filename, for use with tftp=",
|
|
|
|
}, {
|
|
|
|
.name = "dhcpstart",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "the first of the 16 IPs the built-in DHCP server can assign",
|
|
|
|
}, {
|
|
|
|
.name = "dns",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "guest-visible address of the virtual nameserver",
|
|
|
|
}, {
|
|
|
|
.name = "smb",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "root directory of the built-in SMB server",
|
|
|
|
}, {
|
|
|
|
.name = "smbserver",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "IP address of the built-in SMB server",
|
|
|
|
}, {
|
|
|
|
.name = "hostfwd",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "guest port number to forward incoming TCP or UDP connections",
|
|
|
|
}, {
|
|
|
|
.name = "guestfwd",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "IP address and port to forward guest TCP connections",
|
|
|
|
},
|
|
|
|
{ /* end of list */ }
|
|
|
|
},
|
2011-07-20 12:20:20 +02:00
|
|
|
},
|
2009-10-06 13:17:08 +02:00
|
|
|
#endif
|
2011-07-20 12:20:20 +02:00
|
|
|
[NET_CLIENT_TYPE_TAP] = {
|
2009-10-06 13:17:08 +02:00
|
|
|
.type = "tap",
|
2009-10-22 18:49:05 +02:00
|
|
|
.init = net_init_tap,
|
2009-10-06 13:17:08 +02:00
|
|
|
.desc = {
|
|
|
|
NET_COMMON_PARAMS_DESC,
|
|
|
|
{
|
|
|
|
.name = "ifname",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "interface name",
|
|
|
|
},
|
2009-10-22 18:49:05 +02:00
|
|
|
#ifndef _WIN32
|
2009-10-06 13:17:08 +02:00
|
|
|
{
|
|
|
|
.name = "fd",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "file descriptor of an already opened tap",
|
|
|
|
}, {
|
|
|
|
.name = "script",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "script to initialize the interface",
|
|
|
|
}, {
|
|
|
|
.name = "downscript",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "script to shut down the interface",
|
Add support for net bridge
The most common use of -net tap is to connect a tap device to a bridge. This
requires the use of a script and running qemu as root in order to allocate a
tap device to pass to the script.
This model is great for portability and flexibility but it's incredibly
difficult to eliminate the need to run qemu as root. The only really viable
mechanism is to use tunctl to create a tap device, attach it to a bridge as
root, and then hand that tap device to qemu. The problem with this mechanism
is that it requires administrator intervention whenever a user wants to create
a guest.
By essentially writing a helper that implements the most common qemu-ifup
script that can be safely given cap_net_admin, we can dramatically simplify
things for non-privileged users. We still support existing -net tap options
as a mechanism for advanced users and backwards compatibility.
Currently, this is very Linux centric but there's really no reason why it
couldn't be extended for other Unixes.
A typical invocation would be similar to one of the following:
qemu linux.img -net bridge -net nic,model=virtio
qemu linux.img -net tap,helper="/usr/local/libexec/qemu-bridge-helper"
-net nic,model=virtio
qemu linux.img -netdev bridge,id=hn0
-device virtio-net-pci,netdev=hn0,id=nic1
qemu linux.img -netdev tap,helper="/usr/local/libexec/qemu-bridge-helper",id=hn0
-device virtio-net-pci,netdev=hn0,id=nic1
The default bridge that we attach to is br0. The thinking is that a distro
could preconfigure such an interface to allow out-of-the-box bridged networking.
Alternatively, if a user wants to use a different bridge, a typical invocation
would be simliar to one of the following:
qemu linux.img -net bridge,br=qemubr0 -net nic,model=virtio
qemu linux.img -net tap,helper="/usr/local/libexec/qemu-bridge-helper --br=qemubr0"
-net nic,model=virtio
qemu linux.img -netdev bridge,br=qemubr0,id=hn0
-device virtio-net-pci,netdev=hn0,id=nic1
qemu linux.img -netdev tap,helper="/usr/local/libexec/qemu-bridge-helper --br=qemubr0",id=hn0
-device virtio-net-pci,netdev=hn0,id=nic1
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
Signed-off-by: Richa Marwaha <rmarwah@linux.vnet.ibm.com>
Signed-off-by: Corey Bryant <coreyb@linux.vnet.ibm.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2012-01-26 15:42:27 +01:00
|
|
|
}, {
|
2012-02-04 09:24:46 +01:00
|
|
|
#ifdef CONFIG_NET_BRIDGE
|
Add support for net bridge
The most common use of -net tap is to connect a tap device to a bridge. This
requires the use of a script and running qemu as root in order to allocate a
tap device to pass to the script.
This model is great for portability and flexibility but it's incredibly
difficult to eliminate the need to run qemu as root. The only really viable
mechanism is to use tunctl to create a tap device, attach it to a bridge as
root, and then hand that tap device to qemu. The problem with this mechanism
is that it requires administrator intervention whenever a user wants to create
a guest.
By essentially writing a helper that implements the most common qemu-ifup
script that can be safely given cap_net_admin, we can dramatically simplify
things for non-privileged users. We still support existing -net tap options
as a mechanism for advanced users and backwards compatibility.
Currently, this is very Linux centric but there's really no reason why it
couldn't be extended for other Unixes.
A typical invocation would be similar to one of the following:
qemu linux.img -net bridge -net nic,model=virtio
qemu linux.img -net tap,helper="/usr/local/libexec/qemu-bridge-helper"
-net nic,model=virtio
qemu linux.img -netdev bridge,id=hn0
-device virtio-net-pci,netdev=hn0,id=nic1
qemu linux.img -netdev tap,helper="/usr/local/libexec/qemu-bridge-helper",id=hn0
-device virtio-net-pci,netdev=hn0,id=nic1
The default bridge that we attach to is br0. The thinking is that a distro
could preconfigure such an interface to allow out-of-the-box bridged networking.
Alternatively, if a user wants to use a different bridge, a typical invocation
would be simliar to one of the following:
qemu linux.img -net bridge,br=qemubr0 -net nic,model=virtio
qemu linux.img -net tap,helper="/usr/local/libexec/qemu-bridge-helper --br=qemubr0"
-net nic,model=virtio
qemu linux.img -netdev bridge,br=qemubr0,id=hn0
-device virtio-net-pci,netdev=hn0,id=nic1
qemu linux.img -netdev tap,helper="/usr/local/libexec/qemu-bridge-helper --br=qemubr0",id=hn0
-device virtio-net-pci,netdev=hn0,id=nic1
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
Signed-off-by: Richa Marwaha <rmarwah@linux.vnet.ibm.com>
Signed-off-by: Corey Bryant <coreyb@linux.vnet.ibm.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2012-01-26 15:42:27 +01:00
|
|
|
.name = "helper",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "command to execute to configure bridge",
|
2009-10-06 13:17:08 +02:00
|
|
|
}, {
|
2012-02-04 09:24:46 +01:00
|
|
|
#endif
|
2009-10-06 13:17:08 +02:00
|
|
|
.name = "sndbuf",
|
|
|
|
.type = QEMU_OPT_SIZE,
|
|
|
|
.help = "send buffer limit"
|
2009-10-22 18:43:37 +02:00
|
|
|
}, {
|
|
|
|
.name = "vnet_hdr",
|
|
|
|
.type = QEMU_OPT_BOOL,
|
|
|
|
.help = "enable the IFF_VNET_HDR flag on the tap interface"
|
2010-03-17 12:08:24 +01:00
|
|
|
}, {
|
|
|
|
.name = "vhost",
|
|
|
|
.type = QEMU_OPT_BOOL,
|
|
|
|
.help = "enable vhost-net network accelerator",
|
|
|
|
}, {
|
|
|
|
.name = "vhostfd",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "file descriptor of an already opened vhost net device",
|
2011-02-25 09:11:27 +01:00
|
|
|
}, {
|
|
|
|
.name = "vhostforce",
|
|
|
|
.type = QEMU_OPT_BOOL,
|
|
|
|
.help = "force vhost on for non-MSIX virtio guests",
|
|
|
|
},
|
2009-10-22 18:49:05 +02:00
|
|
|
#endif /* _WIN32 */
|
2009-10-06 13:17:08 +02:00
|
|
|
{ /* end of list */ }
|
|
|
|
},
|
2011-07-20 12:20:20 +02:00
|
|
|
},
|
|
|
|
[NET_CLIENT_TYPE_SOCKET] = {
|
2009-10-06 13:17:09 +02:00
|
|
|
.type = "socket",
|
|
|
|
.init = net_init_socket,
|
|
|
|
.desc = {
|
|
|
|
NET_COMMON_PARAMS_DESC,
|
|
|
|
{
|
|
|
|
.name = "fd",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "file descriptor of an already opened socket",
|
|
|
|
}, {
|
|
|
|
.name = "listen",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "port number, and optional hostname, to listen on",
|
|
|
|
}, {
|
|
|
|
.name = "connect",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "port number, and optional hostname, to connect to",
|
|
|
|
}, {
|
|
|
|
.name = "mcast",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "UDP multicast address and port number",
|
2010-12-01 20:16:47 +01:00
|
|
|
}, {
|
|
|
|
.name = "localaddr",
|
|
|
|
.type = QEMU_OPT_STRING,
|
2012-01-11 01:20:54 +01:00
|
|
|
.help = "source address and port for multicast and udp packets",
|
|
|
|
}, {
|
|
|
|
.name = "udp",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "UDP unicast address and port number",
|
2009-10-06 13:17:09 +02:00
|
|
|
},
|
|
|
|
{ /* end of list */ }
|
|
|
|
},
|
2011-07-20 12:20:20 +02:00
|
|
|
},
|
2009-10-06 13:17:10 +02:00
|
|
|
#ifdef CONFIG_VDE
|
2011-07-20 12:20:20 +02:00
|
|
|
[NET_CLIENT_TYPE_VDE] = {
|
2009-10-06 13:17:10 +02:00
|
|
|
.type = "vde",
|
|
|
|
.init = net_init_vde,
|
|
|
|
.desc = {
|
|
|
|
NET_COMMON_PARAMS_DESC,
|
|
|
|
{
|
|
|
|
.name = "sock",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "socket path",
|
|
|
|
}, {
|
|
|
|
.name = "port",
|
|
|
|
.type = QEMU_OPT_NUMBER,
|
|
|
|
.help = "port number",
|
|
|
|
}, {
|
|
|
|
.name = "group",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "group owner of socket",
|
|
|
|
}, {
|
|
|
|
.name = "mode",
|
|
|
|
.type = QEMU_OPT_NUMBER,
|
|
|
|
.help = "permissions for socket",
|
|
|
|
},
|
|
|
|
{ /* end of list */ }
|
|
|
|
},
|
2011-07-20 12:20:20 +02:00
|
|
|
},
|
2009-10-06 13:17:10 +02:00
|
|
|
#endif
|
2011-07-20 12:20:20 +02:00
|
|
|
[NET_CLIENT_TYPE_DUMP] = {
|
2009-10-06 13:17:11 +02:00
|
|
|
.type = "dump",
|
|
|
|
.init = net_init_dump,
|
|
|
|
.desc = {
|
|
|
|
NET_COMMON_PARAMS_DESC,
|
|
|
|
{
|
|
|
|
.name = "len",
|
|
|
|
.type = QEMU_OPT_SIZE,
|
|
|
|
.help = "per-packet size limit (64k default)",
|
|
|
|
}, {
|
|
|
|
.name = "file",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "dump file path (default is qemu-vlan0.pcap)",
|
|
|
|
},
|
|
|
|
{ /* end of list */ }
|
|
|
|
},
|
2009-10-06 13:17:06 +02:00
|
|
|
},
|
2012-02-04 09:24:46 +01:00
|
|
|
#ifdef CONFIG_NET_BRIDGE
|
Add support for net bridge
The most common use of -net tap is to connect a tap device to a bridge. This
requires the use of a script and running qemu as root in order to allocate a
tap device to pass to the script.
This model is great for portability and flexibility but it's incredibly
difficult to eliminate the need to run qemu as root. The only really viable
mechanism is to use tunctl to create a tap device, attach it to a bridge as
root, and then hand that tap device to qemu. The problem with this mechanism
is that it requires administrator intervention whenever a user wants to create
a guest.
By essentially writing a helper that implements the most common qemu-ifup
script that can be safely given cap_net_admin, we can dramatically simplify
things for non-privileged users. We still support existing -net tap options
as a mechanism for advanced users and backwards compatibility.
Currently, this is very Linux centric but there's really no reason why it
couldn't be extended for other Unixes.
A typical invocation would be similar to one of the following:
qemu linux.img -net bridge -net nic,model=virtio
qemu linux.img -net tap,helper="/usr/local/libexec/qemu-bridge-helper"
-net nic,model=virtio
qemu linux.img -netdev bridge,id=hn0
-device virtio-net-pci,netdev=hn0,id=nic1
qemu linux.img -netdev tap,helper="/usr/local/libexec/qemu-bridge-helper",id=hn0
-device virtio-net-pci,netdev=hn0,id=nic1
The default bridge that we attach to is br0. The thinking is that a distro
could preconfigure such an interface to allow out-of-the-box bridged networking.
Alternatively, if a user wants to use a different bridge, a typical invocation
would be simliar to one of the following:
qemu linux.img -net bridge,br=qemubr0 -net nic,model=virtio
qemu linux.img -net tap,helper="/usr/local/libexec/qemu-bridge-helper --br=qemubr0"
-net nic,model=virtio
qemu linux.img -netdev bridge,br=qemubr0,id=hn0
-device virtio-net-pci,netdev=hn0,id=nic1
qemu linux.img -netdev tap,helper="/usr/local/libexec/qemu-bridge-helper --br=qemubr0",id=hn0
-device virtio-net-pci,netdev=hn0,id=nic1
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
Signed-off-by: Richa Marwaha <rmarwah@linux.vnet.ibm.com>
Signed-off-by: Corey Bryant <coreyb@linux.vnet.ibm.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2012-01-26 15:42:27 +01:00
|
|
|
[NET_CLIENT_TYPE_BRIDGE] = {
|
|
|
|
.type = "bridge",
|
|
|
|
.init = net_init_bridge,
|
|
|
|
.desc = {
|
|
|
|
NET_COMMON_PARAMS_DESC,
|
|
|
|
{
|
|
|
|
.name = "br",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "bridge name",
|
|
|
|
}, {
|
|
|
|
.name = "helper",
|
|
|
|
.type = QEMU_OPT_STRING,
|
|
|
|
.help = "command to execute to configure bridge",
|
|
|
|
},
|
|
|
|
{ /* end of list */ }
|
|
|
|
},
|
|
|
|
},
|
2012-02-04 09:24:46 +01:00
|
|
|
#endif /* CONFIG_NET_BRIDGE */
|
2009-10-06 13:17:06 +02:00
|
|
|
};
|
|
|
|
|
2012-04-20 21:50:25 +02:00
|
|
|
int net_client_init(QemuOpts *opts, int is_netdev, Error **errp)
|
2009-10-06 13:17:06 +02:00
|
|
|
{
|
2009-10-08 20:58:21 +02:00
|
|
|
const char *name;
|
2009-10-06 13:17:06 +02:00
|
|
|
const char *type;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
type = qemu_opt_get(opts, "type");
|
2010-03-25 17:22:38 +01:00
|
|
|
if (!type) {
|
2012-04-20 21:50:25 +02:00
|
|
|
error_set(errp, QERR_MISSING_PARAMETER, "type");
|
2010-03-25 17:22:38 +01:00
|
|
|
return -1;
|
|
|
|
}
|
2009-10-06 13:17:06 +02:00
|
|
|
|
2010-03-25 17:22:38 +01:00
|
|
|
if (is_netdev) {
|
2009-10-08 20:58:27 +02:00
|
|
|
if (strcmp(type, "tap") != 0 &&
|
2012-02-04 09:24:46 +01:00
|
|
|
#ifdef CONFIG_NET_BRIDGE
|
|
|
|
strcmp(type, "bridge") != 0 &&
|
|
|
|
#endif
|
2009-10-08 20:58:27 +02:00
|
|
|
#ifdef CONFIG_SLIRP
|
|
|
|
strcmp(type, "user") != 0 &&
|
|
|
|
#endif
|
|
|
|
#ifdef CONFIG_VDE
|
|
|
|
strcmp(type, "vde") != 0 &&
|
|
|
|
#endif
|
2012-02-04 09:24:46 +01:00
|
|
|
strcmp(type, "socket") != 0) {
|
2012-04-20 21:50:25 +02:00
|
|
|
error_set(errp, QERR_INVALID_PARAMETER_VALUE, "type",
|
|
|
|
"a netdev backend type");
|
2009-10-08 20:58:27 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (qemu_opt_get(opts, "vlan")) {
|
2012-04-20 21:50:25 +02:00
|
|
|
error_set(errp, QERR_INVALID_PARAMETER, "vlan");
|
2009-10-08 20:58:27 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (qemu_opt_get(opts, "name")) {
|
2012-04-20 21:50:25 +02:00
|
|
|
error_set(errp, QERR_INVALID_PARAMETER, "name");
|
2009-10-08 20:58:27 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (!qemu_opts_id(opts)) {
|
2012-04-20 21:50:25 +02:00
|
|
|
error_set(errp, QERR_MISSING_PARAMETER, "id");
|
2009-10-08 20:58:27 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-10-08 20:58:21 +02:00
|
|
|
name = qemu_opts_id(opts);
|
|
|
|
if (!name) {
|
|
|
|
name = qemu_opt_get(opts, "name");
|
|
|
|
}
|
|
|
|
|
2011-07-20 12:20:20 +02:00
|
|
|
for (i = 0; i < NET_CLIENT_TYPE_MAX; i++) {
|
|
|
|
if (net_client_types[i].type != NULL &&
|
|
|
|
!strcmp(net_client_types[i].type, type)) {
|
2012-04-12 16:58:57 +02:00
|
|
|
Error *local_err = NULL;
|
2009-10-08 20:58:27 +02:00
|
|
|
VLANState *vlan = NULL;
|
2010-06-08 17:43:58 +02:00
|
|
|
int ret;
|
2009-10-08 20:58:27 +02:00
|
|
|
|
2012-04-12 16:58:57 +02:00
|
|
|
qemu_opts_validate(opts, &net_client_types[i].desc[0], &local_err);
|
|
|
|
if (error_is_set(&local_err)) {
|
2012-04-20 21:50:25 +02:00
|
|
|
error_propagate(errp, local_err);
|
2009-10-06 13:17:06 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2009-10-08 20:58:29 +02:00
|
|
|
/* Do not add to a vlan if it's a -netdev or a nic with a
|
|
|
|
* netdev= parameter. */
|
|
|
|
if (!(is_netdev ||
|
|
|
|
(strcmp(type, "nic") == 0 && qemu_opt_get(opts, "netdev")))) {
|
2009-10-08 20:58:27 +02:00
|
|
|
vlan = qemu_find_vlan(qemu_opt_get_number(opts, "vlan", 0), 1);
|
|
|
|
}
|
|
|
|
|
2010-06-15 10:00:39 +02:00
|
|
|
ret = 0;
|
2009-10-06 13:17:06 +02:00
|
|
|
if (net_client_types[i].init) {
|
2012-04-12 18:20:40 +02:00
|
|
|
ret = net_client_types[i].init(opts, name, vlan);
|
2010-06-08 17:43:58 +02:00
|
|
|
if (ret < 0) {
|
2010-03-25 17:22:38 +01:00
|
|
|
/* TODO push error reporting into init() methods */
|
2012-04-20 21:50:25 +02:00
|
|
|
error_set(errp, QERR_DEVICE_INIT_FAILED, type);
|
2010-03-25 17:22:38 +01:00
|
|
|
return -1;
|
|
|
|
}
|
2009-10-06 13:17:06 +02:00
|
|
|
}
|
2010-06-08 17:43:58 +02:00
|
|
|
return ret;
|
2009-10-06 13:17:06 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-04-20 21:50:25 +02:00
|
|
|
error_set(errp, QERR_INVALID_PARAMETER_VALUE, "type",
|
|
|
|
"a network client type");
|
2009-10-06 13:17:06 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2009-02-11 16:21:54 +01:00
|
|
|
static int net_host_check_device(const char *device)
|
|
|
|
{
|
|
|
|
int i;
|
2009-04-21 21:56:28 +02:00
|
|
|
const char *valid_param_list[] = { "tap", "socket", "dump"
|
2012-02-04 09:24:46 +01:00
|
|
|
#ifdef CONFIG_NET_BRIDGE
|
|
|
|
, "bridge"
|
|
|
|
#endif
|
2009-02-11 16:21:54 +01:00
|
|
|
#ifdef CONFIG_SLIRP
|
|
|
|
,"user"
|
|
|
|
#endif
|
|
|
|
#ifdef CONFIG_VDE
|
|
|
|
,"vde"
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
for (i = 0; i < sizeof(valid_param_list) / sizeof(char *); i++) {
|
|
|
|
if (!strncmp(valid_param_list[i], device,
|
|
|
|
strlen(valid_param_list[i])))
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-08-28 20:27:14 +02:00
|
|
|
void net_host_device_add(Monitor *mon, const QDict *qdict)
|
2009-02-11 16:21:54 +01:00
|
|
|
{
|
2009-08-28 20:27:14 +02:00
|
|
|
const char *device = qdict_get_str(qdict, "device");
|
2009-10-06 13:17:13 +02:00
|
|
|
const char *opts_str = qdict_get_try_str(qdict, "opts");
|
2012-04-20 21:50:25 +02:00
|
|
|
Error *local_err = NULL;
|
2009-10-06 13:17:13 +02:00
|
|
|
QemuOpts *opts;
|
2009-08-28 20:27:14 +02:00
|
|
|
|
2009-02-11 16:21:54 +01:00
|
|
|
if (!net_host_check_device(device)) {
|
2009-03-06 00:01:23 +01:00
|
|
|
monitor_printf(mon, "invalid host network device %s\n", device);
|
2009-02-11 16:21:54 +01:00
|
|
|
return;
|
|
|
|
}
|
2009-10-06 13:17:13 +02:00
|
|
|
|
2010-08-20 13:52:01 +02:00
|
|
|
opts = qemu_opts_parse(qemu_find_opts("net"), opts_str ? opts_str : "", 0);
|
2009-10-06 13:17:13 +02:00
|
|
|
if (!opts) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
qemu_opt_set(opts, "type", device);
|
|
|
|
|
2012-04-20 21:50:25 +02:00
|
|
|
net_client_init(opts, 0, &local_err);
|
|
|
|
if (error_is_set(&local_err)) {
|
|
|
|
qerror_report_err(local_err);
|
|
|
|
error_free(local_err);
|
2009-04-21 21:56:32 +02:00
|
|
|
monitor_printf(mon, "adding host network device %s failed\n", device);
|
|
|
|
}
|
2009-02-11 16:21:54 +01:00
|
|
|
}
|
|
|
|
|
2009-08-28 20:27:14 +02:00
|
|
|
void net_host_device_remove(Monitor *mon, const QDict *qdict)
|
2009-02-11 16:21:54 +01:00
|
|
|
{
|
|
|
|
VLANClientState *vc;
|
2009-08-28 20:27:14 +02:00
|
|
|
int vlan_id = qdict_get_int(qdict, "vlan_id");
|
|
|
|
const char *device = qdict_get_str(qdict, "device");
|
2009-02-11 16:21:54 +01:00
|
|
|
|
2009-06-24 14:42:31 +02:00
|
|
|
vc = qemu_find_vlan_client_by_name(mon, vlan_id, device);
|
2009-02-11 16:21:54 +01:00
|
|
|
if (!vc) {
|
|
|
|
return;
|
|
|
|
}
|
2009-04-21 21:56:08 +02:00
|
|
|
if (!net_host_check_device(vc->model)) {
|
|
|
|
monitor_printf(mon, "invalid host network device %s\n", device);
|
|
|
|
return;
|
|
|
|
}
|
2009-02-11 16:21:54 +01:00
|
|
|
qemu_del_vlan_client(vc);
|
|
|
|
}
|
|
|
|
|
2012-04-18 22:34:15 +02:00
|
|
|
void netdev_add(QemuOpts *opts, Error **errp)
|
|
|
|
{
|
|
|
|
net_client_init(opts, 1, errp);
|
|
|
|
}
|
|
|
|
|
|
|
|
int qmp_netdev_add(Monitor *mon, const QDict *qdict, QObject **ret)
|
2010-03-25 17:22:40 +01:00
|
|
|
{
|
2012-04-18 22:24:01 +02:00
|
|
|
Error *local_err = NULL;
|
2012-04-18 22:34:15 +02:00
|
|
|
QemuOptsList *opts_list;
|
2010-03-25 17:22:40 +01:00
|
|
|
QemuOpts *opts;
|
|
|
|
|
2012-04-18 22:34:15 +02:00
|
|
|
opts_list = qemu_find_opts_err("netdev", &local_err);
|
|
|
|
if (error_is_set(&local_err)) {
|
|
|
|
goto exit_err;
|
2010-03-25 17:22:40 +01:00
|
|
|
}
|
|
|
|
|
2012-04-18 22:34:15 +02:00
|
|
|
opts = qemu_opts_from_qdict(opts_list, qdict, &local_err);
|
|
|
|
if (error_is_set(&local_err)) {
|
|
|
|
goto exit_err;
|
|
|
|
}
|
|
|
|
|
|
|
|
netdev_add(opts, &local_err);
|
|
|
|
if (error_is_set(&local_err)) {
|
2010-06-21 03:41:36 +02:00
|
|
|
qemu_opts_del(opts);
|
2012-04-18 22:34:15 +02:00
|
|
|
goto exit_err;
|
2010-06-21 03:41:36 +02:00
|
|
|
}
|
|
|
|
|
2012-04-18 22:34:15 +02:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
exit_err:
|
|
|
|
qerror_report_err(local_err);
|
|
|
|
error_free(local_err);
|
|
|
|
return -1;
|
2010-03-25 17:22:40 +01:00
|
|
|
}
|
|
|
|
|
2012-04-16 19:36:32 +02:00
|
|
|
void qmp_netdev_del(const char *id, Error **errp)
|
2010-03-25 17:22:40 +01:00
|
|
|
{
|
|
|
|
VLANClientState *vc;
|
|
|
|
|
|
|
|
vc = qemu_find_netdev(id);
|
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 18:45:37 +02:00
|
|
|
if (!vc) {
|
2012-04-16 19:36:32 +02:00
|
|
|
error_set(errp, QERR_DEVICE_NOT_FOUND, id);
|
|
|
|
return;
|
2010-03-25 17:22:40 +01:00
|
|
|
}
|
2012-04-16 19:36:32 +02:00
|
|
|
|
2010-03-25 17:22:40 +01:00
|
|
|
qemu_del_vlan_client(vc);
|
2012-04-16 19:36:32 +02:00
|
|
|
qemu_opts_del(qemu_opts_find(qemu_find_opts_err("netdev", errp), id));
|
2010-03-25 17:22:40 +01:00
|
|
|
}
|
|
|
|
|
2011-07-20 12:20:21 +02:00
|
|
|
static void print_net_client(Monitor *mon, VLANClientState *vc)
|
|
|
|
{
|
|
|
|
monitor_printf(mon, "%s: type=%s,%s\n", vc->name,
|
|
|
|
net_client_types[vc->info->type].type, vc->info_str);
|
|
|
|
}
|
|
|
|
|
2009-03-06 00:01:23 +01:00
|
|
|
void do_info_network(Monitor *mon)
|
2008-10-31 20:10:00 +01:00
|
|
|
{
|
|
|
|
VLANState *vlan;
|
2011-07-20 12:20:19 +02:00
|
|
|
VLANClientState *vc, *peer;
|
|
|
|
net_client_type type;
|
2008-10-31 20:10:00 +01:00
|
|
|
|
2009-10-08 20:58:23 +02:00
|
|
|
QTAILQ_FOREACH(vlan, &vlans, next) {
|
2009-03-06 00:01:23 +01:00
|
|
|
monitor_printf(mon, "VLAN %d devices:\n", vlan->id);
|
2009-10-08 20:58:23 +02:00
|
|
|
|
|
|
|
QTAILQ_FOREACH(vc, &vlan->clients, next) {
|
2011-07-20 12:20:21 +02:00
|
|
|
monitor_printf(mon, " ");
|
|
|
|
print_net_client(mon, vc);
|
2009-10-08 20:58:23 +02:00
|
|
|
}
|
2008-10-31 20:10:00 +01:00
|
|
|
}
|
2010-02-11 14:45:01 +01:00
|
|
|
monitor_printf(mon, "Devices not on any VLAN:\n");
|
|
|
|
QTAILQ_FOREACH(vc, &non_vlan_clients, next) {
|
2011-07-20 12:20:19 +02:00
|
|
|
peer = vc->peer;
|
|
|
|
type = vc->info->type;
|
|
|
|
if (!peer || type == NET_CLIENT_TYPE_NIC) {
|
2011-07-20 12:20:21 +02:00
|
|
|
monitor_printf(mon, " ");
|
|
|
|
print_net_client(mon, vc);
|
2011-07-20 12:20:19 +02:00
|
|
|
} /* else it's a netdev connected to a NIC, printed with the NIC */
|
|
|
|
if (peer && type == NET_CLIENT_TYPE_NIC) {
|
2011-07-20 12:20:21 +02:00
|
|
|
monitor_printf(mon, " \\ ");
|
|
|
|
print_net_client(mon, peer);
|
2010-02-11 14:45:01 +01:00
|
|
|
}
|
|
|
|
}
|
2008-10-31 20:10:00 +01:00
|
|
|
}
|
|
|
|
|
2011-11-23 16:11:55 +01:00
|
|
|
void qmp_set_link(const char *name, bool up, Error **errp)
|
2009-01-08 20:44:06 +01:00
|
|
|
{
|
|
|
|
VLANState *vlan;
|
|
|
|
VLANClientState *vc = NULL;
|
|
|
|
|
2009-10-08 20:58:23 +02:00
|
|
|
QTAILQ_FOREACH(vlan, &vlans, next) {
|
|
|
|
QTAILQ_FOREACH(vc, &vlan->clients, next) {
|
|
|
|
if (strcmp(vc->name, name) == 0) {
|
2009-01-09 01:48:28 +01:00
|
|
|
goto done;
|
2009-10-08 20:58:23 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
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 18:45:37 +02:00
|
|
|
QTAILQ_FOREACH(vc, &non_vlan_clients, next) {
|
|
|
|
if (!strcmp(vc->name, name)) {
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
}
|
2009-01-09 01:48:28 +01:00
|
|
|
done:
|
2009-01-08 20:44:06 +01:00
|
|
|
|
|
|
|
if (!vc) {
|
2011-11-23 16:11:55 +01:00
|
|
|
error_set(errp, QERR_DEVICE_NOT_FOUND, name);
|
|
|
|
return;
|
2009-01-08 20:44:06 +01:00
|
|
|
}
|
|
|
|
|
2010-03-26 09:07:10 +01:00
|
|
|
vc->link_down = !up;
|
2009-01-08 20:44:06 +01:00
|
|
|
|
2009-11-25 19:49:30 +01:00
|
|
|
if (vc->info->link_status_changed) {
|
|
|
|
vc->info->link_status_changed(vc);
|
|
|
|
}
|
2011-02-09 17:45:04 +01:00
|
|
|
|
|
|
|
/* Notify peer. Don't update peer link status: this makes it possible to
|
|
|
|
* disconnect from host network without notifying the guest.
|
|
|
|
* FIXME: is disconnected link status change operation useful?
|
|
|
|
*
|
|
|
|
* Current behaviour 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 (vc->peer && vc->peer->info->link_status_changed) {
|
|
|
|
vc->peer->info->link_status_changed(vc->peer);
|
|
|
|
}
|
2009-01-08 20:44:06 +01:00
|
|
|
}
|
|
|
|
|
2008-10-31 20:10:00 +01:00
|
|
|
void net_cleanup(void)
|
|
|
|
{
|
|
|
|
VLANState *vlan;
|
2009-10-08 20:58:28 +02:00
|
|
|
VLANClientState *vc, *next_vc;
|
2008-10-31 20:10:00 +01:00
|
|
|
|
2009-10-08 20:58:23 +02:00
|
|
|
QTAILQ_FOREACH(vlan, &vlans, next) {
|
|
|
|
QTAILQ_FOREACH_SAFE(vc, &vlan->clients, next, next_vc) {
|
2009-04-17 19:11:08 +02:00
|
|
|
qemu_del_vlan_client(vc);
|
2008-10-31 20:10:00 +01:00
|
|
|
}
|
|
|
|
}
|
2009-10-08 20:58:28 +02:00
|
|
|
|
|
|
|
QTAILQ_FOREACH_SAFE(vc, &non_vlan_clients, next, next_vc) {
|
|
|
|
qemu_del_vlan_client(vc);
|
|
|
|
}
|
2008-10-31 20:10:00 +01:00
|
|
|
}
|
|
|
|
|
2010-02-11 14:44:58 +01:00
|
|
|
void net_check_clients(void)
|
2008-10-31 20:10:00 +01:00
|
|
|
{
|
|
|
|
VLANState *vlan;
|
2010-02-11 14:44:59 +01:00
|
|
|
VLANClientState *vc;
|
2011-05-20 17:50:01 +02:00
|
|
|
int i;
|
2008-10-31 20:10:00 +01:00
|
|
|
|
2011-05-20 17:50:00 +02: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;
|
|
|
|
}
|
|
|
|
|
2009-10-08 20:58:23 +02:00
|
|
|
QTAILQ_FOREACH(vlan, &vlans, next) {
|
2011-03-15 14:20:54 +01:00
|
|
|
int has_nic = 0, has_host_dev = 0;
|
|
|
|
|
2010-02-11 14:44:59 +01:00
|
|
|
QTAILQ_FOREACH(vc, &vlan->clients, next) {
|
|
|
|
switch (vc->info->type) {
|
|
|
|
case NET_CLIENT_TYPE_NIC:
|
|
|
|
has_nic = 1;
|
|
|
|
break;
|
2011-07-20 12:20:20 +02:00
|
|
|
case NET_CLIENT_TYPE_USER:
|
2010-02-11 14:44:59 +01:00
|
|
|
case NET_CLIENT_TYPE_TAP:
|
|
|
|
case NET_CLIENT_TYPE_SOCKET:
|
|
|
|
case NET_CLIENT_TYPE_VDE:
|
|
|
|
has_host_dev = 1;
|
|
|
|
break;
|
|
|
|
default: ;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (has_host_dev && !has_nic)
|
2008-10-31 20:10:00 +01:00
|
|
|
fprintf(stderr, "Warning: vlan %d with no nics\n", vlan->id);
|
2010-02-11 14:44:59 +01:00
|
|
|
if (has_nic && !has_host_dev)
|
2008-10-31 20:10:00 +01:00
|
|
|
fprintf(stderr,
|
|
|
|
"Warning: vlan %d is not connected to host network\n",
|
|
|
|
vlan->id);
|
|
|
|
}
|
2010-02-11 14:45:00 +01:00
|
|
|
QTAILQ_FOREACH(vc, &non_vlan_clients, next) {
|
|
|
|
if (!vc->peer) {
|
|
|
|
fprintf(stderr, "Warning: %s %s has no peer\n",
|
|
|
|
vc->info->type == NET_CLIENT_TYPE_NIC ? "nic" : "netdev",
|
|
|
|
vc->name);
|
|
|
|
}
|
|
|
|
}
|
2011-05-20 17:50:01 +02: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 20:10:00 +01:00
|
|
|
}
|
2009-10-06 13:17:16 +02:00
|
|
|
|
|
|
|
static int net_init_client(QemuOpts *opts, void *dummy)
|
|
|
|
{
|
2012-04-20 21:50:25 +02:00
|
|
|
Error *local_err = NULL;
|
|
|
|
|
|
|
|
net_client_init(opts, 0, &local_err);
|
|
|
|
if (error_is_set(&local_err)) {
|
|
|
|
qerror_report_err(local_err);
|
|
|
|
error_free(local_err);
|
2009-10-12 10:52:00 +02:00
|
|
|
return -1;
|
2012-04-20 21:50:25 +02:00
|
|
|
}
|
|
|
|
|
2009-10-12 10:52:00 +02:00
|
|
|
return 0;
|
2009-10-08 20:58:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static int net_init_netdev(QemuOpts *opts, void *dummy)
|
|
|
|
{
|
2012-04-20 21:50:25 +02:00
|
|
|
Error *local_err = NULL;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = net_client_init(opts, 1, &local_err);
|
|
|
|
if (error_is_set(&local_err)) {
|
|
|
|
qerror_report_err(local_err);
|
|
|
|
error_free(local_err);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
2009-10-06 13:17:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int net_init_clients(void)
|
|
|
|
{
|
2010-08-20 13:52:01 +02:00
|
|
|
QemuOptsList *net = qemu_find_opts("net");
|
|
|
|
|
2009-12-08 13:11:47 +01:00
|
|
|
if (default_net) {
|
2009-10-06 13:17:16 +02:00
|
|
|
/* if no clients, we use a default config */
|
2010-08-20 13:52:01 +02:00
|
|
|
qemu_opts_set(net, NULL, "type", "nic");
|
2009-10-06 13:17:16 +02:00
|
|
|
#ifdef CONFIG_SLIRP
|
2010-08-20 13:52:01 +02:00
|
|
|
qemu_opts_set(net, NULL, "type", "user");
|
2009-10-06 13:17:16 +02:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2009-10-08 20:58:23 +02:00
|
|
|
QTAILQ_INIT(&vlans);
|
2009-10-08 20:58:28 +02:00
|
|
|
QTAILQ_INIT(&non_vlan_clients);
|
2009-10-08 20:58:23 +02:00
|
|
|
|
2010-08-20 13:52:01 +02:00
|
|
|
if (qemu_opts_foreach(qemu_find_opts("netdev"), net_init_netdev, NULL, 1) == -1)
|
2009-10-08 20:58:27 +02:00
|
|
|
return -1;
|
|
|
|
|
2010-08-20 13:52:01 +02:00
|
|
|
if (qemu_opts_foreach(net, net_init_client, NULL, 1) == -1) {
|
2009-10-06 13:17:16 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-10-08 20:58:25 +02:00
|
|
|
int net_client_parse(QemuOptsList *opts_list, const char *optarg)
|
2009-10-06 13:17:16 +02:00
|
|
|
{
|
2009-10-07 23:44:15 +02:00
|
|
|
#if defined(CONFIG_SLIRP)
|
2009-11-25 19:48:54 +01:00
|
|
|
int ret;
|
|
|
|
if (net_slirp_parse_legacy(opts_list, optarg, &ret)) {
|
2009-10-06 13:17:16 +02:00
|
|
|
return ret;
|
|
|
|
}
|
2009-10-07 23:44:15 +02:00
|
|
|
#endif
|
2009-11-25 19:48:54 +01:00
|
|
|
|
2010-02-10 19:52:18 +01:00
|
|
|
if (!qemu_opts_parse(opts_list, optarg, 1)) {
|
2009-10-06 13:17:16 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2009-12-08 13:11:47 +01:00
|
|
|
default_net = 0;
|
2009-10-06 13:17:16 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2012-03-05 04:08:50 +01: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;
|
|
|
|
}
|