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