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