#include "net/vde.h"
#include "net/util.h"
#include "monitor.h"
-#include "sysemu.h"
#include "qemu-common.h"
#include "qemu_socket.h"
+#include "hw/qdev.h"
+#include "iov.h"
static QTAILQ_HEAD(, VLANState) vlans;
static QTAILQ_HEAD(, VLANClientState) non_vlan_clients;
return 0;
}
-int parse_host_src_port(struct sockaddr_in *haddr,
- struct sockaddr_in *saddr,
- const char *input_str)
-{
- char *str = qemu_strdup(input_str);
- char *host_str = str;
- char *src_str;
- const char *src_str2;
- char *ptr;
-
- /*
- * Chop off any extra arguments at the end of the string which
- * would start with a comma, then fill in the src port information
- * if it was provided else use the "any address" and "any port".
- */
- if ((ptr = strchr(str,',')))
- *ptr = '\0';
-
- if ((src_str = strchr(input_str,'@'))) {
- *src_str = '\0';
- src_str++;
- }
-
- if (parse_host_port(haddr, host_str) < 0)
- goto fail;
-
- src_str2 = src_str;
- if (!src_str || *src_str == '\0')
- src_str2 = ":0";
-
- if (parse_host_port(saddr, src_str2) < 0)
- goto fail;
-
- free(str);
- return(0);
-
-fail:
- free(str);
- return -1;
-}
-
int parse_host_port(struct sockaddr_in *saddr, const char *str)
{
char buf[512];
static char *assign_name(VLANClientState *vc1, const char *model)
{
VLANState *vlan;
+ VLANClientState *vc;
char buf[256];
int id = 0;
QTAILQ_FOREACH(vlan, &vlans, next) {
- VLANClientState *vc;
-
QTAILQ_FOREACH(vc, &vlan->clients, next) {
if (vc != vc1 && strcmp(vc->model, model) == 0) {
id++;
}
}
+ QTAILQ_FOREACH(vc, &non_vlan_clients, next) {
+ if (vc != vc1 && strcmp(vc->model, model) == 0) {
+ id++;
+ }
+ }
+
snprintf(buf, sizeof(buf), "%s.%d", model, id);
- return qemu_strdup(buf);
+ return g_strdup(buf);
}
static ssize_t qemu_deliver_packet(VLANClientState *sender,
assert(info->size >= sizeof(VLANClientState));
- vc = qemu_mallocz(info->size);
+ vc = g_malloc0(info->size);
vc->info = info;
- vc->model = qemu_strdup(model);
+ vc->model = g_strdup(model);
if (name) {
- vc->name = qemu_strdup(name);
+ vc->name = g_strdup(name);
} else {
vc->name = assign_name(vc, model);
}
QTAILQ_INSERT_TAIL(&vc->vlan->clients, vc, next);
} else {
if (peer) {
+ assert(!peer->peer);
vc->peer = peer;
peer->peer = vc;
}
return nic;
}
-void qemu_del_vlan_client(VLANClientState *vc)
+static void qemu_cleanup_vlan_client(VLANClientState *vc)
{
if (vc->vlan) {
QTAILQ_REMOVE(&vc->vlan->clients, vc, next);
} else {
+ QTAILQ_REMOVE(&non_vlan_clients, vc, next);
+ }
+
+ if (vc->info->cleanup) {
+ vc->info->cleanup(vc);
+ }
+}
+
+static void qemu_free_vlan_client(VLANClientState *vc)
+{
+ if (!vc->vlan) {
if (vc->send_queue) {
qemu_del_net_queue(vc->send_queue);
}
- QTAILQ_REMOVE(&non_vlan_clients, vc, next);
if (vc->peer) {
vc->peer->peer = NULL;
}
}
+ g_free(vc->name);
+ g_free(vc->model);
+ g_free(vc);
+}
- if (vc->info->cleanup) {
- vc->info->cleanup(vc);
+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_free(vc->name);
- qemu_free(vc->model);
- qemu_free(vc);
+ qemu_cleanup_vlan_client(vc);
+ qemu_free_vlan_client(vc);
}
VLANClientState *
}
/* no can_receive() handler, they can always receive */
- if (!vc->info->can_receive || vc->info->can_receive(vc)) {
- return 1;
+ if (vc->info->can_receive && !vc->info->can_receive(vc)) {
+ return 0;
}
}
- return 0;
+ return 1;
}
static ssize_t qemu_deliver_packet(VLANClientState *sender,
int iovcnt)
{
uint8_t buffer[4096];
- size_t offset = 0;
- int i;
-
- for (i = 0; i < iovcnt; i++) {
- size_t len;
+ size_t offset;
- len = MIN(sizeof(buffer) - offset, iov[i].iov_len);
- memcpy(buffer + offset, iov[i].iov_base, len);
- offset += len;
- }
+ offset = iov_to_buf(iov, iovcnt, buffer, 0, sizeof(buffer));
return vc->info->receive(vc, buffer, offset);
}
-static ssize_t calc_iov_length(const struct iovec *iov, int iovcnt)
-{
- size_t offset = 0;
- int i;
-
- for (i = 0; i < iovcnt; i++)
- offset += iov[i].iov_len;
- return offset;
-}
-
static ssize_t qemu_deliver_packet_iov(VLANClientState *sender,
unsigned flags,
const struct iovec *iov,
VLANClientState *vc = opaque;
if (vc->link_down) {
- return calc_iov_length(iov, iovcnt);
+ return iov_size(iov, iovcnt);
}
if (vc->info->receive_iov) {
}
if (vc->link_down) {
- ret = calc_iov_length(iov, iovcnt);
+ ret = iov_size(iov, iovcnt);
continue;
}
NetQueue *queue;
if (sender->link_down || (!sender->peer && !sender->vlan)) {
- return calc_iov_length(iov, iovcnt);
+ return iov_size(iov, iovcnt);
}
if (sender->peer) {
return NULL;
}
- vlan = qemu_mallocz(sizeof(VLANState));
+ vlan = g_malloc0(sizeof(VLANState));
vlan->id = id;
QTAILQ_INIT(&vlan->clients);
VLANClientState *vc;
QTAILQ_FOREACH(vc, &non_vlan_clients, next) {
+ if (vc->info->type == NET_CLIENT_TYPE_NIC)
+ continue;
if (!strcmp(vc->name, id)) {
return vc;
}
int i;
if (!nd->model)
- nd->model = qemu_strdup(default_model);
+ nd->model = g_strdup(default_model);
for (i = 0 ; models[i]; i++) {
if (strcmp(nd->model, models[i]) == 0)
return i;
}
- qemu_error("qemu: Unsupported NIC model: %s\n", nd->model);
+ error_report("Unsupported NIC model: %s", nd->model);
return -1;
}
int net_handle_fd_param(Monitor *mon, const char *param)
{
- if (!qemu_isdigit(param[0])) {
- int fd;
+ int fd;
+
+ if (!qemu_isdigit(param[0]) && mon) {
fd = monitor_get_fd(mon, param);
if (fd == -1) {
- qemu_error("No file descriptor named %s found", param);
+ error_report("No file descriptor named %s found", param);
return -1;
}
-
- return fd;
} else {
- return strtol(param, NULL, 0);
+ char *endptr = NULL;
+
+ fd = strtol(param, &endptr, 10);
+ if (*endptr || (fd == 0 && param == endptr)) {
+ return -1;
+ }
}
+
+ return fd;
}
static int net_init_nic(QemuOpts *opts,
idx = nic_get_free_idx();
if (idx == -1 || nb_nics >= MAX_NICS) {
- qemu_error("Too Many NICs\n");
+ error_report("Too Many NICs");
return -1;
}
if ((netdev = qemu_opt_get(opts, "netdev"))) {
nd->netdev = qemu_find_netdev(netdev);
if (!nd->netdev) {
- qemu_error("netdev '%s' not found\n", netdev);
+ error_report("netdev '%s' not found", netdev);
return -1;
}
} else {
nd->vlan = vlan;
}
if (name) {
- nd->name = qemu_strdup(name);
+ nd->name = g_strdup(name);
}
if (qemu_opt_get(opts, "model")) {
- nd->model = qemu_strdup(qemu_opt_get(opts, "model"));
+ nd->model = g_strdup(qemu_opt_get(opts, "model"));
}
if (qemu_opt_get(opts, "addr")) {
- nd->devaddr = qemu_strdup(qemu_opt_get(opts, "addr"));
+ nd->devaddr = g_strdup(qemu_opt_get(opts, "addr"));
}
- nd->macaddr[0] = 0x52;
- nd->macaddr[1] = 0x54;
- nd->macaddr[2] = 0x00;
- nd->macaddr[3] = 0x12;
- nd->macaddr[4] = 0x34;
- nd->macaddr[5] = 0x56 + idx;
-
if (qemu_opt_get(opts, "macaddr") &&
- net_parse_macaddr(nd->macaddr, qemu_opt_get(opts, "macaddr")) < 0) {
- qemu_error("invalid syntax for ethernet address\n");
+ net_parse_macaddr(nd->macaddr.a, qemu_opt_get(opts, "macaddr")) < 0) {
+ error_report("invalid syntax for ethernet address");
return -1;
}
+ qemu_macaddr_default_if_unset(&nd->macaddr);
- nd->nvectors = qemu_opt_get_number(opts, "vectors", NIC_NVECTORS_UNSPECIFIED);
- if (nd->nvectors != NIC_NVECTORS_UNSPECIFIED &&
+ nd->nvectors = qemu_opt_get_number(opts, "vectors",
+ DEV_NVECTORS_UNSPECIFIED);
+ if (nd->nvectors != DEV_NVECTORS_UNSPECIFIED &&
(nd->nvectors < 0 || nd->nvectors > 0x7ffffff)) {
- qemu_error("invalid # of vectors: %d\n", nd->nvectors);
+ error_report("invalid # of vectors: %d", nd->nvectors);
return -1;
}
/* magic number, but compiler will warn if too small */
#define NET_MAX_DESC 20
-static struct {
+static const struct {
const char *type;
net_client_init_func init;
QemuOptDesc desc[NET_MAX_DESC];
-} net_client_types[] = {
- {
+} net_client_types[NET_CLIENT_TYPE_MAX] = {
+ [NET_CLIENT_TYPE_NONE] = {
.type = "none",
.desc = {
NET_COMMON_PARAMS_DESC,
{ /* end of list */ }
},
- }, {
+ },
+ [NET_CLIENT_TYPE_NIC] = {
.type = "nic",
.init = net_init_nic,
.desc = {
},
{ /* end of list */ }
},
+ },
#ifdef CONFIG_SLIRP
- }, {
+ [NET_CLIENT_TYPE_USER] = {
.type = "user",
.init = net_init_slirp,
.desc = {
},
{ /* end of list */ }
},
+ },
#endif
- }, {
+ [NET_CLIENT_TYPE_TAP] = {
.type = "tap",
.init = net_init_tap,
.desc = {
.name = "vnet_hdr",
.type = QEMU_OPT_BOOL,
.help = "enable the IFF_VNET_HDR flag on the tap interface"
- },
+ }, {
+ .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",
+ }, {
+ .name = "vhostforce",
+ .type = QEMU_OPT_BOOL,
+ .help = "force vhost on for non-MSIX virtio guests",
+ },
#endif /* _WIN32 */
{ /* end of list */ }
},
- }, {
+ },
+ [NET_CLIENT_TYPE_SOCKET] = {
.type = "socket",
.init = net_init_socket,
.desc = {
.name = "mcast",
.type = QEMU_OPT_STRING,
.help = "UDP multicast address and port number",
+ }, {
+ .name = "localaddr",
+ .type = QEMU_OPT_STRING,
+ .help = "source address for multicast packets",
},
{ /* end of list */ }
},
+ },
#ifdef CONFIG_VDE
- }, {
+ [NET_CLIENT_TYPE_VDE] = {
.type = "vde",
.init = net_init_vde,
.desc = {
},
{ /* end of list */ }
},
+ },
#endif
- }, {
+ [NET_CLIENT_TYPE_DUMP] = {
.type = "dump",
.init = net_init_dump,
.desc = {
{ /* end of list */ }
},
},
- { /* end of list */ }
};
int net_client_init(Monitor *mon, QemuOpts *opts, int is_netdev)
int i;
type = qemu_opt_get(opts, "type");
+ if (!type) {
+ qerror_report(QERR_MISSING_PARAMETER, "type");
+ return -1;
+ }
- if (!is_netdev) {
- if (!type) {
- qemu_error("No type specified for -net\n");
- return -1;
- }
- } else {
- if (!type) {
- qemu_error("No type specified for -netdev\n");
- return -1;
- }
-
+ if (is_netdev) {
if (strcmp(type, "tap") != 0 &&
#ifdef CONFIG_SLIRP
strcmp(type, "user") != 0 &&
strcmp(type, "vde") != 0 &&
#endif
strcmp(type, "socket") != 0) {
- qemu_error("The '%s' network backend type is not valid with -netdev\n",
- type);
+ qerror_report(QERR_INVALID_PARAMETER_VALUE, "type",
+ "a netdev backend type");
return -1;
}
if (qemu_opt_get(opts, "vlan")) {
- qemu_error("The 'vlan' parameter is not valid with -netdev\n");
+ qerror_report(QERR_INVALID_PARAMETER, "vlan");
return -1;
}
if (qemu_opt_get(opts, "name")) {
- qemu_error("The 'name' parameter is not valid with -netdev\n");
+ qerror_report(QERR_INVALID_PARAMETER, "name");
return -1;
}
if (!qemu_opts_id(opts)) {
- qemu_error("The id= parameter is required with -netdev\n");
+ qerror_report(QERR_MISSING_PARAMETER, "id");
return -1;
}
}
name = qemu_opt_get(opts, "name");
}
- for (i = 0; net_client_types[i].type != NULL; i++) {
- if (!strcmp(net_client_types[i].type, type)) {
+ for (i = 0; i < NET_CLIENT_TYPE_MAX; i++) {
+ if (net_client_types[i].type != NULL &&
+ !strcmp(net_client_types[i].type, type)) {
VLANState *vlan = NULL;
+ int ret;
if (qemu_opts_validate(opts, &net_client_types[i].desc[0]) == -1) {
return -1;
vlan = qemu_find_vlan(qemu_opt_get_number(opts, "vlan", 0), 1);
}
+ ret = 0;
if (net_client_types[i].init) {
- return net_client_types[i].init(opts, mon, name, vlan);
- } else {
- return 0;
+ ret = net_client_types[i].init(opts, mon, name, vlan);
+ if (ret < 0) {
+ /* TODO push error reporting into init() methods */
+ qerror_report(QERR_DEVICE_INIT_FAILED, type);
+ return -1;
+ }
}
+ return ret;
}
}
- qemu_error("Invalid -net type '%s'\n", type);
+ qerror_report(QERR_INVALID_PARAMETER_VALUE, "type",
+ "a network client type");
return -1;
}
return;
}
- opts = qemu_opts_parse(&qemu_net_opts, opts_str ? opts_str : "", NULL);
+ opts = qemu_opts_parse(qemu_find_opts("net"), opts_str ? opts_str : "", 0);
if (!opts) {
- monitor_printf(mon, "parsing network options '%s' failed\n",
- opts_str ? opts_str : "");
return;
}
qemu_del_vlan_client(vc);
}
-void net_set_boot_mask(int net_boot_mask)
+int do_netdev_add(Monitor *mon, const QDict *qdict, QObject **ret_data)
{
- int i;
+ QemuOpts *opts;
+ int res;
- /* Only the first four NICs may be bootable */
- net_boot_mask = net_boot_mask & 0xF;
+ opts = qemu_opts_from_qdict(qemu_find_opts("netdev"), qdict);
+ if (!opts) {
+ return -1;
+ }
- for (i = 0; i < nb_nics; i++) {
- if (net_boot_mask & (1 << i)) {
- nd_table[i].bootable = 1;
- net_boot_mask &= ~(1 << i);
- }
+ res = net_client_init(mon, opts, 1);
+ if (res < 0) {
+ qemu_opts_del(opts);
}
- if (net_boot_mask) {
- fprintf(stderr, "Cannot boot from non-existent NIC\n");
- exit(1);
+ return res;
+}
+
+int do_netdev_del(Monitor *mon, const QDict *qdict, QObject **ret_data)
+{
+ const char *id = qdict_get_str(qdict, "id");
+ VLANClientState *vc;
+
+ vc = qemu_find_netdev(id);
+ if (!vc) {
+ qerror_report(QERR_DEVICE_NOT_FOUND, id);
+ return -1;
}
+ qemu_del_vlan_client(vc);
+ qemu_opts_del(qemu_opts_find(qemu_find_opts("netdev"), id));
+ return 0;
+}
+
+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);
}
void do_info_network(Monitor *mon)
{
VLANState *vlan;
- VLANClientState *vc;
+ VLANClientState *vc, *peer;
+ net_client_type type;
QTAILQ_FOREACH(vlan, &vlans, next) {
monitor_printf(mon, "VLAN %d devices:\n", vlan->id);
QTAILQ_FOREACH(vc, &vlan->clients, next) {
- monitor_printf(mon, " %s: %s\n", vc->name, vc->info_str);
+ monitor_printf(mon, " ");
+ print_net_client(mon, vc);
}
}
monitor_printf(mon, "Devices not on any VLAN:\n");
QTAILQ_FOREACH(vc, &non_vlan_clients, next) {
- monitor_printf(mon, " %s: %s", vc->name, vc->info_str);
- if (vc->peer) {
- monitor_printf(mon, " peer=%s", vc->peer->name);
+ peer = vc->peer;
+ type = vc->info->type;
+ if (!peer || type == NET_CLIENT_TYPE_NIC) {
+ monitor_printf(mon, " ");
+ print_net_client(mon, vc);
+ } /* else it's a netdev connected to a NIC, printed with the NIC */
+ if (peer && type == NET_CLIENT_TYPE_NIC) {
+ monitor_printf(mon, " \\ ");
+ print_net_client(mon, peer);
}
- monitor_printf(mon, "\n");
}
}
-void do_set_link(Monitor *mon, const QDict *qdict)
+int do_set_link(Monitor *mon, const QDict *qdict, QObject **ret_data)
{
VLANState *vlan;
VLANClientState *vc = NULL;
const char *name = qdict_get_str(qdict, "name");
- const char *up_or_down = qdict_get_str(qdict, "up_or_down");
+ int up = qdict_get_bool(qdict, "up");
QTAILQ_FOREACH(vlan, &vlans, next) {
QTAILQ_FOREACH(vc, &vlan->clients, next) {
}
}
}
- vc = qemu_find_netdev(name);
+ QTAILQ_FOREACH(vc, &non_vlan_clients, next) {
+ if (!strcmp(vc->name, name)) {
+ goto done;
+ }
+ }
done:
if (!vc) {
- monitor_printf(mon, "could not find network device '%s'\n", name);
- return;
+ qerror_report(QERR_DEVICE_NOT_FOUND, name);
+ return -1;
}
- if (strcmp(up_or_down, "up") == 0)
- vc->link_down = 0;
- else if (strcmp(up_or_down, "down") == 0)
- vc->link_down = 1;
- else
- monitor_printf(mon, "invalid link status '%s'; only 'up' or 'down' "
- "valid\n", up_or_down);
+ vc->link_down = !up;
if (vc->info->link_status_changed) {
vc->info->link_status_changed(vc);
}
+
+ /* 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);
+ }
+ return 0;
}
void net_cleanup(void)
{
VLANState *vlan;
VLANClientState *vc;
- int has_nic, has_host_dev;
+ int i;
+
+ /* 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;
+ }
QTAILQ_FOREACH(vlan, &vlans, next) {
+ int has_nic = 0, has_host_dev = 0;
+
QTAILQ_FOREACH(vc, &vlan->clients, next) {
switch (vc->info->type) {
case NET_CLIENT_TYPE_NIC:
has_nic = 1;
break;
- case NET_CLIENT_TYPE_SLIRP:
+ case NET_CLIENT_TYPE_USER:
case NET_CLIENT_TYPE_TAP:
case NET_CLIENT_TYPE_SOCKET:
case NET_CLIENT_TYPE_VDE:
vc->name);
}
}
+
+ /* 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");
+ }
+ }
}
static int net_init_client(QemuOpts *opts, void *dummy)
int net_init_clients(void)
{
+ QemuOptsList *net = qemu_find_opts("net");
+
if (default_net) {
/* if no clients, we use a default config */
- qemu_opts_set(&qemu_net_opts, NULL, "type", "nic");
+ qemu_opts_set(net, NULL, "type", "nic");
#ifdef CONFIG_SLIRP
- qemu_opts_set(&qemu_net_opts, NULL, "type", "user");
+ qemu_opts_set(net, NULL, "type", "user");
#endif
}
QTAILQ_INIT(&vlans);
QTAILQ_INIT(&non_vlan_clients);
- if (qemu_opts_foreach(&qemu_netdev_opts, net_init_netdev, NULL, 1) == -1)
+ if (qemu_opts_foreach(qemu_find_opts("netdev"), net_init_netdev, NULL, 1) == -1)
return -1;
- if (qemu_opts_foreach(&qemu_net_opts, net_init_client, NULL, 1) == -1) {
+ if (qemu_opts_foreach(net, net_init_client, NULL, 1) == -1) {
return -1;
}
}
#endif
- if (!qemu_opts_parse(opts_list, optarg, "type")) {
+ if (!qemu_opts_parse(opts_list, optarg, 1)) {
return -1;
}