From patchwork Fri Jul 20 12:01:34 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stefan Hajnoczi X-Patchwork-Id: 172230 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id AE38B2C037B for ; Fri, 20 Jul 2012 22:30:56 +1000 (EST) Received: from localhost ([::1]:58233 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1SsBve-0006bU-T8 for incoming@patchwork.ozlabs.org; Fri, 20 Jul 2012 08:03:30 -0400 Received: from eggs.gnu.org ([208.118.235.92]:44396) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1SsBuW-00047s-27 for qemu-devel@nongnu.org; Fri, 20 Jul 2012 08:02:29 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1SsBuK-0003UM-LO for qemu-devel@nongnu.org; Fri, 20 Jul 2012 08:02:19 -0400 Received: from e06smtp11.uk.ibm.com ([195.75.94.107]:51778) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1SsBuK-0003TP-8l for qemu-devel@nongnu.org; Fri, 20 Jul 2012 08:02:08 -0400 Received: from /spool/local by e06smtp11.uk.ibm.com with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted for from ; Fri, 20 Jul 2012 13:02:04 +0100 Received: from d06nrmr1407.portsmouth.uk.ibm.com (9.149.38.185) by e06smtp11.uk.ibm.com (192.168.101.141) with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted; Fri, 20 Jul 2012 13:02:03 +0100 Received: from d06av09.portsmouth.uk.ibm.com (d06av09.portsmouth.uk.ibm.com [9.149.37.250]) by d06nrmr1407.portsmouth.uk.ibm.com (8.13.8/8.13.8/NCO v10.0) with ESMTP id q6KC225W2527412 for ; Fri, 20 Jul 2012 13:02:02 +0100 Received: from d06av09.portsmouth.uk.ibm.com (loopback [127.0.0.1]) by d06av09.portsmouth.uk.ibm.com (8.14.4/8.13.1/NCO v10.0 AVout) with ESMTP id q6KC21wK003860 for ; Fri, 20 Jul 2012 06:02:02 -0600 Received: from localhost (stefanha-thinkpad.manchester-maybrook.uk.ibm.com [9.174.219.145]) by d06av09.portsmouth.uk.ibm.com (8.14.4/8.13.1/NCO v10.0 AVin) with ESMTP id q6KC20xN003781; Fri, 20 Jul 2012 06:02:00 -0600 From: Stefan Hajnoczi To: Date: Fri, 20 Jul 2012 13:01:34 +0100 Message-Id: <1342785709-3152-2-git-send-email-stefanha@linux.vnet.ibm.com> X-Mailer: git-send-email 1.7.10.4 In-Reply-To: <1342785709-3152-1-git-send-email-stefanha@linux.vnet.ibm.com> References: <1342785709-3152-1-git-send-email-stefanha@linux.vnet.ibm.com> x-cbid: 12072012-5024-0000-0000-00000348510D X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 195.75.94.107 Cc: Paolo Bonzini , Zhi Yong Wu , lersek@redhat.com, Stefan Hajnoczi , Zhi Yong Wu Subject: [Qemu-devel] [PATCH 01/16] net: Add a hub net client X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org The vlan feature can be implemented in terms of hubs. By introducing a hub net client it becomes possible to remove the special case vlan code from net.c and push the vlan feature out of generic networking code. Signed-off-by: Stefan Hajnoczi Signed-off-by: Zhi Yong Wu --- net.c | 17 ++-- net/Makefile.objs | 2 +- net/hub.c | 223 +++++++++++++++++++++++++++++++++++++++++++++++++++++ net/hub.h | 26 +++++++ qapi-schema.json | 30 +++++-- 5 files changed, 282 insertions(+), 16 deletions(-) create mode 100644 net/hub.c create mode 100644 net/hub.h diff --git a/net.c b/net.c index dbca77b..e7a8d81 100644 --- a/net.c +++ b/net.c @@ -30,6 +30,7 @@ #include "net/dump.h" #include "net/slirp.h" #include "net/vde.h" +#include "net/hub.h" #include "net/util.h" #include "monitor.h" #include "qemu-common.h" @@ -816,19 +817,20 @@ static int (* const net_client_init_fun[NET_CLIENT_OPTIONS_KIND_MAX])( const NetClientOptions *opts, const char *name, VLANState *vlan) = { - [NET_CLIENT_OPTIONS_KIND_NIC] = net_init_nic, + [NET_CLIENT_OPTIONS_KIND_NIC] = net_init_nic, #ifdef CONFIG_SLIRP - [NET_CLIENT_OPTIONS_KIND_USER] = net_init_slirp, + [NET_CLIENT_OPTIONS_KIND_USER] = net_init_slirp, #endif - [NET_CLIENT_OPTIONS_KIND_TAP] = net_init_tap, - [NET_CLIENT_OPTIONS_KIND_SOCKET] = net_init_socket, + [NET_CLIENT_OPTIONS_KIND_TAP] = net_init_tap, + [NET_CLIENT_OPTIONS_KIND_SOCKET] = net_init_socket, #ifdef CONFIG_VDE - [NET_CLIENT_OPTIONS_KIND_VDE] = net_init_vde, + [NET_CLIENT_OPTIONS_KIND_VDE] = net_init_vde, #endif - [NET_CLIENT_OPTIONS_KIND_DUMP] = net_init_dump, + [NET_CLIENT_OPTIONS_KIND_DUMP] = net_init_dump, #ifdef CONFIG_NET_BRIDGE - [NET_CLIENT_OPTIONS_KIND_BRIDGE] = net_init_bridge, + [NET_CLIENT_OPTIONS_KIND_BRIDGE] = net_init_bridge, #endif + [NET_CLIENT_OPTIONS_KIND_HUBPORT] = net_init_hubport, }; @@ -858,6 +860,7 @@ static int net_client_init1(const void *object, int is_netdev, Error **errp) #ifdef CONFIG_NET_BRIDGE case NET_CLIENT_OPTIONS_KIND_BRIDGE: #endif + case NET_CLIENT_OPTIONS_KIND_HUBPORT: break; default: diff --git a/net/Makefile.objs b/net/Makefile.objs index 72f50bc..cf04187 100644 --- a/net/Makefile.objs +++ b/net/Makefile.objs @@ -1,4 +1,4 @@ -common-obj-y = queue.o checksum.o util.o +common-obj-y = queue.o checksum.o util.o hub.o common-obj-y += socket.o common-obj-y += dump.o common-obj-$(CONFIG_POSIX) += tap.o diff --git a/net/hub.c b/net/hub.c new file mode 100644 index 0000000..cc58d41 --- /dev/null +++ b/net/hub.c @@ -0,0 +1,223 @@ +/* + * Hub net client + * + * Copyright IBM, Corp. 2012 + * + * Authors: + * Stefan Hajnoczi + * Zhi Yong Wu + * + * This work is licensed under the terms of the GNU LGPL, version 2 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#include "monitor.h" +#include "net.h" +#include "hub.h" + +/* + * A hub broadcasts incoming packets to all its ports except the source port. + * Hubs can be used to provide independent network segments, also confusingly + * named the QEMU 'vlan' feature. + */ + +typedef struct NetHub NetHub; + +typedef struct NetHubPort { + VLANClientState nc; + QLIST_ENTRY(NetHubPort) next; + NetHub *hub; + unsigned int id; +} NetHubPort; + +struct NetHub { + unsigned int id; + QLIST_ENTRY(NetHub) next; + unsigned int num_ports; + QLIST_HEAD(, NetHubPort) ports; +}; + +static QLIST_HEAD(, NetHub) hubs = QLIST_HEAD_INITIALIZER(&hubs); + +static ssize_t net_hub_receive(NetHub *hub, NetHubPort *source_port, + const uint8_t *buf, size_t len) +{ + NetHubPort *port; + + QLIST_FOREACH(port, &hub->ports, next) { + if (port == source_port) { + continue; + } + + qemu_send_packet(&port->nc, buf, len); + } + return len; +} + +static ssize_t net_hub_receive_iov(NetHub *hub, NetHubPort *source_port, + const struct iovec *iov, int iovcnt) +{ + NetHubPort *port; + ssize_t ret = 0; + + QLIST_FOREACH(port, &hub->ports, next) { + if (port == source_port) { + continue; + } + + ret = qemu_sendv_packet(&port->nc, iov, iovcnt); + } + return ret; +} + +static NetHub *net_hub_new(unsigned int id) +{ + NetHub *hub; + + hub = g_malloc(sizeof(*hub)); + hub->id = id; + hub->num_ports = 0; + QLIST_INIT(&hub->ports); + + QLIST_INSERT_HEAD(&hubs, hub, next); + + return hub; +} + +static ssize_t net_hub_port_receive(VLANClientState *nc, + const uint8_t *buf, size_t len) +{ + NetHubPort *port = DO_UPCAST(NetHubPort, nc, nc); + + return net_hub_receive(port->hub, port, buf, len); +} + +static ssize_t net_hub_port_receive_iov(VLANClientState *nc, + const struct iovec *iov, int iovcnt) +{ + NetHubPort *port = DO_UPCAST(NetHubPort, nc, nc); + + return net_hub_receive_iov(port->hub, port, iov, iovcnt); +} + +static void net_hub_port_cleanup(VLANClientState *nc) +{ + NetHubPort *port = DO_UPCAST(NetHubPort, nc, nc); + + QLIST_REMOVE(port, next); +} + +static NetClientInfo net_hub_port_info = { + .type = NET_CLIENT_OPTIONS_KIND_HUBPORT, + .size = sizeof(NetHubPort), + .receive = net_hub_port_receive, + .receive_iov = net_hub_port_receive_iov, + .cleanup = net_hub_port_cleanup, +}; + +static NetHubPort *net_hub_port_new(NetHub *hub, const char *name) +{ + VLANClientState *nc; + NetHubPort *port; + unsigned int id = hub->num_ports++; + char default_name[128]; + + if (!name) { + snprintf(default_name, sizeof(default_name), + "hub%uport%u", hub->id, id); + name = default_name; + } + + nc = qemu_new_net_client(&net_hub_port_info, NULL, NULL, "hub", name); + port = DO_UPCAST(NetHubPort, nc, nc); + port->id = id; + port->hub = hub; + + QLIST_INSERT_HEAD(&hub->ports, port, next); + + return port; +} + +/** + * Create a port on a given hub + * @name: Net client name or NULL for default name. + * + * If there is no existing hub with the given id then a new hub is created. + */ +VLANClientState *net_hub_add_port(unsigned int hub_id, const char *name) +{ + NetHub *hub; + NetHubPort *port; + + QLIST_FOREACH(hub, &hubs, next) { + if (hub->id == hub_id) { + break; + } + } + + if (!hub) { + hub = net_hub_new(hub_id); + } + + port = net_hub_port_new(hub, name); + return &port->nc; +} + +/** + * Print hub configuration + */ +void net_hub_info(Monitor *mon) +{ + NetHub *hub; + NetHubPort *port; + + QLIST_FOREACH(hub, &hubs, next) { + monitor_printf(mon, "hub %u\n", hub->id); + QLIST_FOREACH(port, &hub->ports, next) { + monitor_printf(mon, " port %u peer %s\n", port->id, + port->nc.peer ? port->nc.peer->name : ""); + } + } +} + +/** + * Get the hub id that a client is connected to + * + * @id Pointer for hub id output, may be NULL + */ +int net_hub_id_for_client(VLANClientState *nc, unsigned int *id) +{ + NetHub *hub; + NetHubPort *port; + + QLIST_FOREACH(hub, &hubs, next) { + QLIST_FOREACH(port, &hub->ports, next) { + if (&port->nc == nc || + (port->nc.peer && port->nc.peer == nc)) { + if (id) { + *id = hub->id; + } + return 0; + } + } + } + return -ENOENT; +} + +int net_init_hubport(const NetClientOptions *opts, const char *name, + VLANState *vlan) +{ + const NetdevHubPortOptions *hubport; + + assert(opts->kind == NET_CLIENT_OPTIONS_KIND_HUBPORT); + hubport = opts->hubport; + + /* The hub is a "vlan" so this option makes no sense. */ + if (vlan) { + return -EINVAL; + } + + net_hub_add_port(hubport->hubid, name); + return 0; +} diff --git a/net/hub.h b/net/hub.h new file mode 100644 index 0000000..06939b3 --- /dev/null +++ b/net/hub.h @@ -0,0 +1,26 @@ +/* + * Hub net client + * + * Copyright IBM, Corp. 2012 + * + * Authors: + * Stefan Hajnoczi + * Zhi Yong Wu + * + * This work is licensed under the terms of the GNU LGPL, version 2 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#ifndef NET_HUB_H +#define NET_HUB_H + +#include "qemu-common.h" + +int net_init_hubport(const NetClientOptions *opts, const char *name, + VLANState *vlan); +VLANClientState *net_hub_add_port(unsigned int hub_id, const char *name); +void net_hub_info(Monitor *mon); +int net_hub_id_for_client(VLANClientState *nc, unsigned int *id); + +#endif /* NET_HUB_H */ diff --git a/qapi-schema.json b/qapi-schema.json index bc55ed2..6618eb5 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -2094,6 +2094,19 @@ '*helper': 'str' } } ## +# @NetdevHubPortOptions +# +# Connect two or more net clients through a software hub. +# +# @hubid: hub identifier number +# +# Since 1.2 +## +{ 'type': 'NetdevHubPortOptions', + 'data': { + 'hubid': 'int' } } + +## # @NetClientOptions # # A discriminated record of network device traits. @@ -2102,14 +2115,15 @@ ## { 'union': 'NetClientOptions', 'data': { - 'none': 'NetdevNoneOptions', - 'nic': 'NetLegacyNicOptions', - 'user': 'NetdevUserOptions', - 'tap': 'NetdevTapOptions', - 'socket': 'NetdevSocketOptions', - 'vde': 'NetdevVdeOptions', - 'dump': 'NetdevDumpOptions', - 'bridge': 'NetdevBridgeOptions' } } + 'none': 'NetdevNoneOptions', + 'nic': 'NetLegacyNicOptions', + 'user': 'NetdevUserOptions', + 'tap': 'NetdevTapOptions', + 'socket': 'NetdevSocketOptions', + 'vde': 'NetdevVdeOptions', + 'dump': 'NetdevDumpOptions', + 'bridge': 'NetdevBridgeOptions', + 'hubport': 'NetdevHubPortOptions' } } ## # @NetLegacy