Add qemu 2.4.0
[kvmfornfv.git] / qemu / net / hub.c
diff --git a/qemu/net/hub.c b/qemu/net/hub.c
new file mode 100644 (file)
index 0000000..3047f12
--- /dev/null
@@ -0,0 +1,354 @@
+/*
+ * Hub net client
+ *
+ * Copyright IBM, Corp. 2012
+ *
+ * Authors:
+ *  Stefan Hajnoczi   <stefanha@linux.vnet.ibm.com>
+ *  Zhi Yong Wu       <wuzhy@linux.vnet.ibm.com>
+ *
+ * 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/monitor.h"
+#include "net/net.h"
+#include "clients.h"
+#include "hub.h"
+#include "qemu/iov.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 {
+    NetClientState nc;
+    QLIST_ENTRY(NetHubPort) next;
+    NetHub *hub;
+    int id;
+} NetHubPort;
+
+struct NetHub {
+    int id;
+    QLIST_ENTRY(NetHub) next;
+    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 len = iov_size(iov, iovcnt);
+
+    QLIST_FOREACH(port, &hub->ports, next) {
+        if (port == source_port) {
+            continue;
+        }
+
+        qemu_sendv_packet(&port->nc, iov, iovcnt);
+    }
+    return len;
+}
+
+static NetHub *net_hub_new(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 int net_hub_port_can_receive(NetClientState *nc)
+{
+    NetHubPort *port;
+    NetHubPort *src_port = DO_UPCAST(NetHubPort, nc, nc);
+    NetHub *hub = src_port->hub;
+
+    QLIST_FOREACH(port, &hub->ports, next) {
+        if (port == src_port) {
+            continue;
+        }
+
+        if (qemu_can_send_packet(&port->nc)) {
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
+static ssize_t net_hub_port_receive(NetClientState *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(NetClientState *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(NetClientState *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),
+    .can_receive = net_hub_port_can_receive,
+    .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)
+{
+    NetClientState *nc;
+    NetHubPort *port;
+    int id = hub->num_ports++;
+    char default_name[128];
+
+    if (!name) {
+        snprintf(default_name, sizeof(default_name),
+                 "hub%dport%d", hub->id, id);
+        name = default_name;
+    }
+
+    nc = qemu_new_net_client(&net_hub_port_info, 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.
+ */
+NetClientState *net_hub_add_port(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;
+}
+
+/**
+ * Find a specific client on a hub
+ */
+NetClientState *net_hub_find_client_by_name(int hub_id, const char *name)
+{
+    NetHub *hub;
+    NetHubPort *port;
+    NetClientState *peer;
+
+    QLIST_FOREACH(hub, &hubs, next) {
+        if (hub->id == hub_id) {
+            QLIST_FOREACH(port, &hub->ports, next) {
+                peer = port->nc.peer;
+
+                if (peer && strcmp(peer->name, name) == 0) {
+                    return peer;
+                }
+            }
+        }
+    }
+    return NULL;
+}
+
+/**
+ * Find a available port on a hub; otherwise create one new port
+ */
+NetClientState *net_hub_port_find(int hub_id)
+{
+    NetHub *hub;
+    NetHubPort *port;
+    NetClientState *nc;
+
+    QLIST_FOREACH(hub, &hubs, next) {
+        if (hub->id == hub_id) {
+            QLIST_FOREACH(port, &hub->ports, next) {
+                nc = port->nc.peer;
+                if (!nc) {
+                    return &(port->nc);
+                }
+            }
+            break;
+        }
+    }
+
+    nc = net_hub_add_port(hub_id, NULL);
+    return nc;
+}
+
+/**
+ * Print hub configuration
+ */
+void net_hub_info(Monitor *mon)
+{
+    NetHub *hub;
+    NetHubPort *port;
+
+    QLIST_FOREACH(hub, &hubs, next) {
+        monitor_printf(mon, "hub %d\n", hub->id);
+        QLIST_FOREACH(port, &hub->ports, next) {
+            monitor_printf(mon, " \\ %s", port->nc.name);
+            if (port->nc.peer) {
+                monitor_printf(mon, ": ");
+                print_net_client(mon, port->nc.peer);
+            } else {
+                monitor_printf(mon, "\n");
+            }
+        }
+    }
+}
+
+/**
+ * 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(NetClientState *nc, int *id)
+{
+    NetHubPort *port;
+
+    if (nc->info->type == NET_CLIENT_OPTIONS_KIND_HUBPORT) {
+        port = DO_UPCAST(NetHubPort, nc, nc);
+    } else if (nc->peer != NULL && nc->peer->info->type ==
+            NET_CLIENT_OPTIONS_KIND_HUBPORT) {
+        port = DO_UPCAST(NetHubPort, nc, nc->peer);
+    } else {
+        return -ENOENT;
+    }
+
+    if (id) {
+        *id = port->hub->id;
+    }
+    return 0;
+}
+
+int net_init_hubport(const NetClientOptions *opts, const char *name,
+                     NetClientState *peer, Error **errp)
+{
+    const NetdevHubPortOptions *hubport;
+
+    assert(opts->kind == NET_CLIENT_OPTIONS_KIND_HUBPORT);
+    assert(!peer);
+    hubport = opts->hubport;
+
+    net_hub_add_port(hubport->hubid, name);
+    return 0;
+}
+
+/**
+ * Warn if hub configurations are likely wrong
+ */
+void net_hub_check_clients(void)
+{
+    NetHub *hub;
+    NetHubPort *port;
+    NetClientState *peer;
+
+    QLIST_FOREACH(hub, &hubs, next) {
+        int has_nic = 0, has_host_dev = 0;
+
+        QLIST_FOREACH(port, &hub->ports, next) {
+            peer = port->nc.peer;
+            if (!peer) {
+                fprintf(stderr, "Warning: hub port %s has no peer\n",
+                        port->nc.name);
+                continue;
+            }
+
+            switch (peer->info->type) {
+            case NET_CLIENT_OPTIONS_KIND_NIC:
+                has_nic = 1;
+                break;
+            case NET_CLIENT_OPTIONS_KIND_USER:
+            case NET_CLIENT_OPTIONS_KIND_TAP:
+            case NET_CLIENT_OPTIONS_KIND_SOCKET:
+            case NET_CLIENT_OPTIONS_KIND_VDE:
+            case NET_CLIENT_OPTIONS_KIND_VHOST_USER:
+                has_host_dev = 1;
+                break;
+            default:
+                break;
+            }
+        }
+        if (has_host_dev && !has_nic) {
+            fprintf(stderr, "Warning: vlan %d with no nics\n", hub->id);
+        }
+        if (has_nic && !has_host_dev) {
+            fprintf(stderr,
+                    "Warning: vlan %d is not connected to host network\n",
+                    hub->id);
+        }
+    }
+}
+
+bool net_hub_flush(NetClientState *nc)
+{
+    NetHubPort *port;
+    NetHubPort *source_port = DO_UPCAST(NetHubPort, nc, nc);
+    int ret = 0;
+
+    QLIST_FOREACH(port, &source_port->hub->ports, next) {
+        if (port != source_port) {
+            ret += qemu_net_queue_flush(port->nc.incoming_queue);
+        }
+    }
+    return ret ? true : false;
+}