qemu/net/hub.c
<<
>>
Prefs
   1/*
   2 * Hub net client
   3 *
   4 * Copyright IBM, Corp. 2012
   5 *
   6 * Authors:
   7 *  Stefan Hajnoczi   <stefanha@linux.vnet.ibm.com>
   8 *  Zhi Yong Wu       <wuzhy@linux.vnet.ibm.com>
   9 *
  10 * This work is licensed under the terms of the GNU LGPL, version 2 or later.
  11 * See the COPYING.LIB file in the top-level directory.
  12 *
  13 */
  14
  15#include "monitor/monitor.h"
  16#include "net/net.h"
  17#include "clients.h"
  18#include "hub.h"
  19#include "qemu/iov.h"
  20
  21/*
  22 * A hub broadcasts incoming packets to all its ports except the source port.
  23 * Hubs can be used to provide independent network segments, also confusingly
  24 * named the QEMU 'vlan' feature.
  25 */
  26
  27typedef struct NetHub NetHub;
  28
  29typedef struct NetHubPort {
  30    NetClientState nc;
  31    QLIST_ENTRY(NetHubPort) next;
  32    NetHub *hub;
  33    int id;
  34} NetHubPort;
  35
  36struct NetHub {
  37    int id;
  38    QLIST_ENTRY(NetHub) next;
  39    int num_ports;
  40    QLIST_HEAD(, NetHubPort) ports;
  41};
  42
  43static QLIST_HEAD(, NetHub) hubs = QLIST_HEAD_INITIALIZER(&hubs);
  44
  45static ssize_t net_hub_receive(NetHub *hub, NetHubPort *source_port,
  46                               const uint8_t *buf, size_t len)
  47{
  48    NetHubPort *port;
  49
  50    QLIST_FOREACH(port, &hub->ports, next) {
  51        if (port == source_port) {
  52            continue;
  53        }
  54
  55        qemu_send_packet(&port->nc, buf, len);
  56    }
  57    return len;
  58}
  59
  60static ssize_t net_hub_receive_iov(NetHub *hub, NetHubPort *source_port,
  61                                   const struct iovec *iov, int iovcnt)
  62{
  63    NetHubPort *port;
  64    ssize_t len = iov_size(iov, iovcnt);
  65
  66    QLIST_FOREACH(port, &hub->ports, next) {
  67        if (port == source_port) {
  68            continue;
  69        }
  70
  71        qemu_sendv_packet(&port->nc, iov, iovcnt);
  72    }
  73    return len;
  74}
  75
  76static NetHub *net_hub_new(int id)
  77{
  78    NetHub *hub;
  79
  80    hub = g_malloc(sizeof(*hub));
  81    hub->id = id;
  82    hub->num_ports = 0;
  83    QLIST_INIT(&hub->ports);
  84
  85    QLIST_INSERT_HEAD(&hubs, hub, next);
  86
  87    return hub;
  88}
  89
  90static int net_hub_port_can_receive(NetClientState *nc)
  91{
  92    NetHubPort *port;
  93    NetHubPort *src_port = DO_UPCAST(NetHubPort, nc, nc);
  94    NetHub *hub = src_port->hub;
  95
  96    QLIST_FOREACH(port, &hub->ports, next) {
  97        if (port == src_port) {
  98            continue;
  99        }
 100
 101        if (qemu_can_send_packet(&port->nc)) {
 102            return 1;
 103        }
 104    }
 105
 106    return 0;
 107}
 108
 109static ssize_t net_hub_port_receive(NetClientState *nc,
 110                                    const uint8_t *buf, size_t len)
 111{
 112    NetHubPort *port = DO_UPCAST(NetHubPort, nc, nc);
 113
 114    return net_hub_receive(port->hub, port, buf, len);
 115}
 116
 117static ssize_t net_hub_port_receive_iov(NetClientState *nc,
 118                                        const struct iovec *iov, int iovcnt)
 119{
 120    NetHubPort *port = DO_UPCAST(NetHubPort, nc, nc);
 121
 122    return net_hub_receive_iov(port->hub, port, iov, iovcnt);
 123}
 124
 125static void net_hub_port_cleanup(NetClientState *nc)
 126{
 127    NetHubPort *port = DO_UPCAST(NetHubPort, nc, nc);
 128
 129    QLIST_REMOVE(port, next);
 130}
 131
 132static NetClientInfo net_hub_port_info = {
 133    .type = NET_CLIENT_OPTIONS_KIND_HUBPORT,
 134    .size = sizeof(NetHubPort),
 135    .can_receive = net_hub_port_can_receive,
 136    .receive = net_hub_port_receive,
 137    .receive_iov = net_hub_port_receive_iov,
 138    .cleanup = net_hub_port_cleanup,
 139};
 140
 141static NetHubPort *net_hub_port_new(NetHub *hub, const char *name)
 142{
 143    NetClientState *nc;
 144    NetHubPort *port;
 145    int id = hub->num_ports++;
 146    char default_name[128];
 147
 148    if (!name) {
 149        snprintf(default_name, sizeof(default_name),
 150                 "hub%dport%d", hub->id, id);
 151        name = default_name;
 152    }
 153
 154    nc = qemu_new_net_client(&net_hub_port_info, NULL, "hub", name);
 155    port = DO_UPCAST(NetHubPort, nc, nc);
 156    port->id = id;
 157    port->hub = hub;
 158
 159    QLIST_INSERT_HEAD(&hub->ports, port, next);
 160
 161    return port;
 162}
 163
 164/**
 165 * Create a port on a given hub
 166 * @name: Net client name or NULL for default name.
 167 *
 168 * If there is no existing hub with the given id then a new hub is created.
 169 */
 170NetClientState *net_hub_add_port(int hub_id, const char *name)
 171{
 172    NetHub *hub;
 173    NetHubPort *port;
 174
 175    QLIST_FOREACH(hub, &hubs, next) {
 176        if (hub->id == hub_id) {
 177            break;
 178        }
 179    }
 180
 181    if (!hub) {
 182        hub = net_hub_new(hub_id);
 183    }
 184
 185    port = net_hub_port_new(hub, name);
 186    return &port->nc;
 187}
 188
 189/**
 190 * Find a specific client on a hub
 191 */
 192NetClientState *net_hub_find_client_by_name(int hub_id, const char *name)
 193{
 194    NetHub *hub;
 195    NetHubPort *port;
 196    NetClientState *peer;
 197
 198    QLIST_FOREACH(hub, &hubs, next) {
 199        if (hub->id == hub_id) {
 200            QLIST_FOREACH(port, &hub->ports, next) {
 201                peer = port->nc.peer;
 202
 203                if (peer && strcmp(peer->name, name) == 0) {
 204                    return peer;
 205                }
 206            }
 207        }
 208    }
 209    return NULL;
 210}
 211
 212/**
 213 * Find a available port on a hub; otherwise create one new port
 214 */
 215NetClientState *net_hub_port_find(int hub_id)
 216{
 217    NetHub *hub;
 218    NetHubPort *port;
 219    NetClientState *nc;
 220
 221    QLIST_FOREACH(hub, &hubs, next) {
 222        if (hub->id == hub_id) {
 223            QLIST_FOREACH(port, &hub->ports, next) {
 224                nc = port->nc.peer;
 225                if (!nc) {
 226                    return &(port->nc);
 227                }
 228            }
 229            break;
 230        }
 231    }
 232
 233    nc = net_hub_add_port(hub_id, NULL);
 234    return nc;
 235}
 236
 237/**
 238 * Print hub configuration
 239 */
 240void net_hub_info(Monitor *mon)
 241{
 242    NetHub *hub;
 243    NetHubPort *port;
 244
 245    QLIST_FOREACH(hub, &hubs, next) {
 246        monitor_printf(mon, "hub %d\n", hub->id);
 247        QLIST_FOREACH(port, &hub->ports, next) {
 248            if (port->nc.peer) {
 249                monitor_printf(mon, " \\ ");
 250                print_net_client(mon, port->nc.peer);
 251            }
 252        }
 253    }
 254}
 255
 256/**
 257 * Get the hub id that a client is connected to
 258 *
 259 * @id: Pointer for hub id output, may be NULL
 260 */
 261int net_hub_id_for_client(NetClientState *nc, int *id)
 262{
 263    NetHubPort *port;
 264
 265    if (nc->info->type == NET_CLIENT_OPTIONS_KIND_HUBPORT) {
 266        port = DO_UPCAST(NetHubPort, nc, nc);
 267    } else if (nc->peer != NULL && nc->peer->info->type ==
 268            NET_CLIENT_OPTIONS_KIND_HUBPORT) {
 269        port = DO_UPCAST(NetHubPort, nc, nc->peer);
 270    } else {
 271        return -ENOENT;
 272    }
 273
 274    if (id) {
 275        *id = port->hub->id;
 276    }
 277    return 0;
 278}
 279
 280int net_init_hubport(const NetClientOptions *opts, const char *name,
 281                     NetClientState *peer)
 282{
 283    const NetdevHubPortOptions *hubport;
 284
 285    assert(opts->kind == NET_CLIENT_OPTIONS_KIND_HUBPORT);
 286    hubport = opts->hubport;
 287
 288    /* Treat hub port like a backend, NIC must be the one to peer */
 289    if (peer) {
 290        return -EINVAL;
 291    }
 292
 293    net_hub_add_port(hubport->hubid, name);
 294    return 0;
 295}
 296
 297/**
 298 * Warn if hub configurations are likely wrong
 299 */
 300void net_hub_check_clients(void)
 301{
 302    NetHub *hub;
 303    NetHubPort *port;
 304    NetClientState *peer;
 305
 306    QLIST_FOREACH(hub, &hubs, next) {
 307        int has_nic = 0, has_host_dev = 0;
 308
 309        QLIST_FOREACH(port, &hub->ports, next) {
 310            peer = port->nc.peer;
 311            if (!peer) {
 312                fprintf(stderr, "Warning: hub port %s has no peer\n",
 313                        port->nc.name);
 314                continue;
 315            }
 316
 317            switch (peer->info->type) {
 318            case NET_CLIENT_OPTIONS_KIND_NIC:
 319                has_nic = 1;
 320                break;
 321            case NET_CLIENT_OPTIONS_KIND_USER:
 322            case NET_CLIENT_OPTIONS_KIND_TAP:
 323            case NET_CLIENT_OPTIONS_KIND_SOCKET:
 324            case NET_CLIENT_OPTIONS_KIND_VDE:
 325                has_host_dev = 1;
 326                break;
 327            default:
 328                break;
 329            }
 330        }
 331        if (has_host_dev && !has_nic) {
 332            fprintf(stderr, "Warning: vlan %d with no nics\n", hub->id);
 333        }
 334        if (has_nic && !has_host_dev) {
 335            fprintf(stderr,
 336                    "Warning: vlan %d is not connected to host network\n",
 337                    hub->id);
 338        }
 339    }
 340}
 341
 342bool net_hub_flush(NetClientState *nc)
 343{
 344    NetHubPort *port;
 345    NetHubPort *source_port = DO_UPCAST(NetHubPort, nc, nc);
 346    int ret = 0;
 347
 348    QLIST_FOREACH(port, &source_port->hub->ports, next) {
 349        if (port != source_port) {
 350            ret += qemu_net_queue_flush(port->nc.incoming_queue);
 351        }
 352    }
 353    return ret ? true : false;
 354}
 355