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