X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=net.c;h=12701af253883737df8a7ea7421a3a97aec61690;hb=44e798d3959e6face1adc0b2fd1873b25f66a5e7;hp=e47f727629b6ac542260ec6f1436fc06a480ba25;hpb=27f3f8a362570c5e9eb89b36b6c6d5db7b6e70e3;p=qemu.git diff --git a/net.c b/net.c index e47f72762..12701af25 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; } - 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, @@ -765,7 +748,7 @@ 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; } @@ -776,7 +759,7 @@ static int net_init_nic(QemuOpts *opts, 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 { @@ -802,7 +785,7 @@ static int net_init_nic(QemuOpts *opts, 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"); + error_report("invalid syntax for ethernet address"); return -1; } @@ -810,7 +793,7 @@ static int net_init_nic(QemuOpts *opts, 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; } @@ -847,14 +830,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 +867,9 @@ static const struct { }, { /* end of list */ } }, + }, #ifdef CONFIG_SLIRP - }, { + [NET_CLIENT_TYPE_USER] = { .type = "user", .init = net_init_slirp, .desc = { @@ -944,8 +929,9 @@ static const struct { }, { /* end of list */ } }, + }, #endif - }, { + [NET_CLIENT_TYPE_TAP] = { .type = "tap", .init = net_init_tap, .desc = { @@ -976,11 +962,24 @@ static const struct { .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 = { @@ -1001,11 +1000,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 = { @@ -1029,8 +1033,9 @@ static const struct { }, { /* end of list */ } }, + }, #endif - }, { + [NET_CLIENT_TYPE_DUMP] = { .type = "dump", .init = net_init_dump, .desc = { @@ -1047,7 +1052,6 @@ static const struct { { /* end of list */ } }, }, - { /* end of list */ } }; int net_client_init(Monitor *mon, QemuOpts *opts, int is_netdev) @@ -1057,18 +1061,12 @@ 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 && @@ -1077,21 +1075,21 @@ int net_client_init(Monitor *mon, QemuOpts *opts, int is_netdev) 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; } } @@ -1101,9 +1099,11 @@ 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; if (qemu_opts_validate(opts, &net_client_types[i].desc[0]) == -1) { return -1; @@ -1116,15 +1116,21 @@ int net_client_init(Monitor *mon, QemuOpts *opts, int is_netdev) 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; } @@ -1159,10 +1165,8 @@ void net_host_device_add(Monitor *mon, const QDict *qdict) 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; } @@ -1190,54 +1194,80 @@ void net_host_device_remove(Monitor *mon, const QDict *qdict) 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 || vc->info->type == NET_CLIENT_TYPE_NIC) { + 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) { @@ -1250,21 +1280,27 @@ void do_set_link(Monitor *mon, const QDict *qdict) 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) @@ -1287,15 +1323,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: @@ -1318,6 +1368,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) @@ -1334,21 +1398,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; } @@ -1364,7 +1430,7 @@ int net_client_parse(QemuOptsList *opts_list, const char *optarg) } #endif - if (!qemu_opts_parse(opts_list, optarg, "type")) { + if (!qemu_opts_parse(opts_list, optarg, 1)) { return -1; }