X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=net.c;h=31c23389c835f8e7fe2c31190c0063a06e16cbe4;hb=7095e715765e0b5ca57799142a712f0c761e98ee;hp=8ddf872a6f1b896c5db2f49a07f950c6a2c89d93;hpb=02d0ba1420803562109185f47be6f7430bfdefae;p=qemu.git diff --git a/net.c b/net.c index 8ddf872a6..31c23389c 100644 --- a/net.c +++ b/net.c @@ -32,10 +32,10 @@ #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; @@ -93,47 +93,6 @@ static int get_str_sep(char *buf, int buf_size, const char **pp, int sep) 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]; @@ -281,29 +240,64 @@ NICState *qemu_new_nic(NetClientInfo *info, 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 { - 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; - } } 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); + } + if (vc->peer) { + vc->peer->peer = NULL; + } + } qemu_free(vc->name); qemu_free(vc->model); qemu_free(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_cleanup_vlan_client(vc); + qemu_free_vlan_client(vc); +} + VLANClientState * qemu_find_vlan_client_by_name(Monitor *mon, int vlan_id, const char *client_str) @@ -376,11 +370,11 @@ int qemu_can_send_packet(VLANClientState *sender) } /* 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, @@ -537,30 +531,13 @@ static ssize_t vc_sendv_compat(VLANClientState *vc, const struct iovec *iov, int iovcnt) { uint8_t buffer[4096]; - size_t offset = 0; - int i; + size_t offset; - for (i = 0; i < iovcnt; i++) { - size_t len; - - 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, @@ -570,7 +547,7 @@ static ssize_t qemu_deliver_packet_iov(VLANClientState *sender, VLANClientState *vc = opaque; if (vc->link_down) { - return calc_iov_length(iov, iovcnt); + return iov_size(iov, iovcnt); } if (vc->info->receive_iov) { @@ -598,7 +575,7 @@ static ssize_t qemu_vlan_deliver_packet_iov(VLANClientState *sender, } if (vc->link_down) { - ret = calc_iov_length(iov, iovcnt); + ret = iov_size(iov, iovcnt); continue; } @@ -623,7 +600,7 @@ ssize_t qemu_sendv_packet_async(VLANClientState *sender, NetQueue *queue; if (sender->link_down || (!sender->peer && !sender->vlan)) { - return calc_iov_length(iov, iovcnt); + return iov_size(iov, iovcnt); } if (sender->peer) { @@ -733,25 +710,31 @@ int qemu_find_nic_model(NICInfo *nd, const char * const *models, return i; } - error_report("qemu: Unsupported NIC model: %s", 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) { 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, @@ -793,18 +776,12 @@ static int net_init_nic(QemuOpts *opts, nd->devaddr = qemu_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) { + 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", DEV_NVECTORS_UNSPECIFIED); @@ -847,14 +824,15 @@ 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 = { @@ -883,8 +861,9 @@ static const struct { }, { /* end of list */ } }, + }, #ifdef CONFIG_SLIRP - }, { + [NET_CLIENT_TYPE_USER] = { .type = "user", .init = net_init_slirp, .desc = { @@ -944,8 +923,9 @@ static const struct { }, { /* end of list */ } }, + }, #endif - }, { + [NET_CLIENT_TYPE_TAP] = { .type = "tap", .init = net_init_tap, .desc = { @@ -984,11 +964,16 @@ static const struct { .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 = { @@ -1009,11 +994,16 @@ static const struct { .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 = { @@ -1037,8 +1027,9 @@ static const struct { }, { /* end of list */ } }, + }, #endif - }, { + [NET_CLIENT_TYPE_DUMP] = { .type = "dump", .init = net_init_dump, .desc = { @@ -1055,7 +1046,6 @@ static const struct { { /* end of list */ } }, }, - { /* end of list */ } }; int net_client_init(Monitor *mon, QemuOpts *opts, int is_netdev) @@ -1103,8 +1093,9 @@ int net_client_init(Monitor *mon, QemuOpts *opts, int is_netdev) 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; @@ -1168,7 +1159,7 @@ void net_host_device_add(Monitor *mon, const QDict *qdict) return; } - opts = qemu_opts_parse(&qemu_net_opts, opts_str ? opts_str : "", 0); + opts = qemu_opts_parse(qemu_find_opts("net"), opts_str ? opts_str : "", 0); if (!opts) { return; } @@ -1202,7 +1193,7 @@ int do_netdev_add(Monitor *mon, const QDict *qdict, QObject **ret_data) QemuOpts *opts; int res; - opts = qemu_opts_from_qdict(&qemu_netdev_opts, qdict); + opts = qemu_opts_from_qdict(qemu_find_opts("netdev"), qdict); if (!opts) { return -1; } @@ -1226,29 +1217,42 @@ int do_netdev_del(Monitor *mon, const QDict *qdict, QObject **ret_data) return -1; } qemu_del_vlan_client(vc); - qemu_opts_del(qemu_opts_find(&qemu_netdev_opts, id)); + 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"); } } @@ -1279,6 +1283,17 @@ done: 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; } @@ -1302,15 +1317,29 @@ void net_check_clients(void) { VLANState *vlan; VLANClientState *vc; - int has_nic = 0, has_host_dev = 0; + 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: @@ -1333,6 +1362,20 @@ void net_check_clients(void) 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) @@ -1349,21 +1392,23 @@ static int net_init_netdev(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; }