qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

hub.c (8433B)


      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 
     30 typedef struct NetHub NetHub;
     31 
     32 typedef struct NetHubPort {
     33     NetClientState nc;
     34     QLIST_ENTRY(NetHubPort) next;
     35     NetHub *hub;
     36     int id;
     37 } NetHubPort;
     38 
     39 struct NetHub {
     40     int id;
     41     QLIST_ENTRY(NetHub) next;
     42     int num_ports;
     43     QLIST_HEAD(, NetHubPort) ports;
     44 };
     45 
     46 static QLIST_HEAD(, NetHub) hubs = QLIST_HEAD_INITIALIZER(&hubs);
     47 
     48 static 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 
     63 static 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 
     79 static 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 
     93 static bool 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 true;
    106         }
    107     }
    108 
    109     return false;
    110 }
    111 
    112 static 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 
    120 static 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 
    128 static void net_hub_port_cleanup(NetClientState *nc)
    129 {
    130     NetHubPort *port = DO_UPCAST(NetHubPort, nc, nc);
    131 
    132     QLIST_REMOVE(port, next);
    133 }
    134 
    135 static 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 
    144 static 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  */
    176 NetClientState *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 available port on a hub; otherwise create one new port
    198  */
    199 NetClientState *net_hub_port_find(int hub_id)
    200 {
    201     NetHub *hub;
    202     NetHubPort *port;
    203     NetClientState *nc;
    204 
    205     QLIST_FOREACH(hub, &hubs, next) {
    206         if (hub->id == hub_id) {
    207             QLIST_FOREACH(port, &hub->ports, next) {
    208                 nc = port->nc.peer;
    209                 if (!nc) {
    210                     return &(port->nc);
    211                 }
    212             }
    213             break;
    214         }
    215     }
    216 
    217     nc = net_hub_add_port(hub_id, NULL, NULL);
    218     return nc;
    219 }
    220 
    221 /**
    222  * Print hub configuration
    223  */
    224 void net_hub_info(Monitor *mon)
    225 {
    226     NetHub *hub;
    227     NetHubPort *port;
    228 
    229     QLIST_FOREACH(hub, &hubs, next) {
    230         monitor_printf(mon, "hub %d\n", hub->id);
    231         QLIST_FOREACH(port, &hub->ports, next) {
    232             monitor_printf(mon, " \\ %s", port->nc.name);
    233             if (port->nc.peer) {
    234                 monitor_printf(mon, ": ");
    235                 print_net_client(mon, port->nc.peer);
    236             } else {
    237                 monitor_printf(mon, "\n");
    238             }
    239         }
    240     }
    241 }
    242 
    243 /**
    244  * Get the hub id that a client is connected to
    245  *
    246  * @id: Pointer for hub id output, may be NULL
    247  */
    248 int net_hub_id_for_client(NetClientState *nc, int *id)
    249 {
    250     NetHubPort *port;
    251 
    252     if (nc->info->type == NET_CLIENT_DRIVER_HUBPORT) {
    253         port = DO_UPCAST(NetHubPort, nc, nc);
    254     } else if (nc->peer != NULL && nc->peer->info->type ==
    255             NET_CLIENT_DRIVER_HUBPORT) {
    256         port = DO_UPCAST(NetHubPort, nc, nc->peer);
    257     } else {
    258         return -ENOENT;
    259     }
    260 
    261     if (id) {
    262         *id = port->hub->id;
    263     }
    264     return 0;
    265 }
    266 
    267 int net_init_hubport(const Netdev *netdev, const char *name,
    268                      NetClientState *peer, Error **errp)
    269 {
    270     const NetdevHubPortOptions *hubport;
    271     NetClientState *hubpeer = NULL;
    272 
    273     assert(netdev->type == NET_CLIENT_DRIVER_HUBPORT);
    274     assert(!peer);
    275     hubport = &netdev->u.hubport;
    276 
    277     if (hubport->has_netdev) {
    278         hubpeer = qemu_find_netdev(hubport->netdev);
    279         if (!hubpeer) {
    280             error_setg(errp, "netdev '%s' not found", hubport->netdev);
    281             return -1;
    282         }
    283     }
    284 
    285     net_hub_add_port(hubport->hubid, name, hubpeer);
    286 
    287     return 0;
    288 }
    289 
    290 /**
    291  * Warn if hub configurations are likely wrong
    292  */
    293 void net_hub_check_clients(void)
    294 {
    295     NetHub *hub;
    296     NetHubPort *port;
    297     NetClientState *peer;
    298 
    299     QLIST_FOREACH(hub, &hubs, next) {
    300         int has_nic = 0, has_host_dev = 0;
    301 
    302         QLIST_FOREACH(port, &hub->ports, next) {
    303             peer = port->nc.peer;
    304             if (!peer) {
    305                 warn_report("hub port %s has no peer", port->nc.name);
    306                 continue;
    307             }
    308 
    309             switch (peer->info->type) {
    310             case NET_CLIENT_DRIVER_NIC:
    311                 has_nic = 1;
    312                 break;
    313             case NET_CLIENT_DRIVER_USER:
    314             case NET_CLIENT_DRIVER_TAP:
    315             case NET_CLIENT_DRIVER_SOCKET:
    316             case NET_CLIENT_DRIVER_STREAM:
    317             case NET_CLIENT_DRIVER_DGRAM:
    318             case NET_CLIENT_DRIVER_VDE:
    319             case NET_CLIENT_DRIVER_VHOST_USER:
    320                 has_host_dev = 1;
    321                 break;
    322             default:
    323                 break;
    324             }
    325         }
    326         if (has_host_dev && !has_nic) {
    327             warn_report("hub %d with no nics", hub->id);
    328         }
    329         if (has_nic && !has_host_dev && !qtest_enabled()) {
    330             warn_report("hub %d is not connected to host network", hub->id);
    331         }
    332     }
    333 }
    334 
    335 bool net_hub_flush(NetClientState *nc)
    336 {
    337     NetHubPort *port;
    338     NetHubPort *source_port = DO_UPCAST(NetHubPort, nc, nc);
    339     int ret = 0;
    340 
    341     QLIST_FOREACH(port, &source_port->hub->ports, next) {
    342         if (port != source_port) {
    343             ret += qemu_net_queue_flush(port->nc.incoming_queue);
    344         }
    345     }
    346     return ret ? true : false;
    347 }