1f6c874e3SStefan Hajnoczi /* 2f6c874e3SStefan Hajnoczi * Hub net client 3f6c874e3SStefan Hajnoczi * 4f6c874e3SStefan Hajnoczi * Copyright IBM, Corp. 2012 5f6c874e3SStefan Hajnoczi * 6f6c874e3SStefan Hajnoczi * Authors: 7f6c874e3SStefan Hajnoczi * Stefan Hajnoczi <stefanha@linux.vnet.ibm.com> 8f6c874e3SStefan Hajnoczi * Zhi Yong Wu <wuzhy@linux.vnet.ibm.com> 9f6c874e3SStefan Hajnoczi * 10f6c874e3SStefan Hajnoczi * This work is licensed under the terms of the GNU LGPL, version 2 or later. 11f6c874e3SStefan Hajnoczi * See the COPYING.LIB file in the top-level directory. 12f6c874e3SStefan Hajnoczi * 13f6c874e3SStefan Hajnoczi */ 14f6c874e3SStefan Hajnoczi 15f6c874e3SStefan Hajnoczi #include "monitor.h" 16f6c874e3SStefan Hajnoczi #include "net.h" 17f6c874e3SStefan Hajnoczi #include "hub.h" 18f6c874e3SStefan Hajnoczi 19f6c874e3SStefan Hajnoczi /* 20f6c874e3SStefan Hajnoczi * A hub broadcasts incoming packets to all its ports except the source port. 21f6c874e3SStefan Hajnoczi * Hubs can be used to provide independent network segments, also confusingly 22f6c874e3SStefan Hajnoczi * named the QEMU 'vlan' feature. 23f6c874e3SStefan Hajnoczi */ 24f6c874e3SStefan Hajnoczi 25f6c874e3SStefan Hajnoczi typedef struct NetHub NetHub; 26f6c874e3SStefan Hajnoczi 27f6c874e3SStefan Hajnoczi typedef struct NetHubPort { 28f6c874e3SStefan Hajnoczi VLANClientState nc; 29f6c874e3SStefan Hajnoczi QLIST_ENTRY(NetHubPort) next; 30f6c874e3SStefan Hajnoczi NetHub *hub; 31f6c874e3SStefan Hajnoczi int id; 32f6c874e3SStefan Hajnoczi } NetHubPort; 33f6c874e3SStefan Hajnoczi 34f6c874e3SStefan Hajnoczi struct NetHub { 35f6c874e3SStefan Hajnoczi int id; 36f6c874e3SStefan Hajnoczi QLIST_ENTRY(NetHub) next; 37f6c874e3SStefan Hajnoczi int num_ports; 38f6c874e3SStefan Hajnoczi QLIST_HEAD(, NetHubPort) ports; 39f6c874e3SStefan Hajnoczi }; 40f6c874e3SStefan Hajnoczi 41f6c874e3SStefan Hajnoczi static QLIST_HEAD(, NetHub) hubs = QLIST_HEAD_INITIALIZER(&hubs); 42f6c874e3SStefan Hajnoczi 43f6c874e3SStefan Hajnoczi static ssize_t net_hub_receive(NetHub *hub, NetHubPort *source_port, 44f6c874e3SStefan Hajnoczi const uint8_t *buf, size_t len) 45f6c874e3SStefan Hajnoczi { 46f6c874e3SStefan Hajnoczi NetHubPort *port; 47f6c874e3SStefan Hajnoczi 48f6c874e3SStefan Hajnoczi QLIST_FOREACH(port, &hub->ports, next) { 49f6c874e3SStefan Hajnoczi if (port == source_port) { 50f6c874e3SStefan Hajnoczi continue; 51f6c874e3SStefan Hajnoczi } 52f6c874e3SStefan Hajnoczi 53f6c874e3SStefan Hajnoczi qemu_send_packet(&port->nc, buf, len); 54f6c874e3SStefan Hajnoczi } 55f6c874e3SStefan Hajnoczi return len; 56f6c874e3SStefan Hajnoczi } 57f6c874e3SStefan Hajnoczi 58f6c874e3SStefan Hajnoczi static ssize_t net_hub_receive_iov(NetHub *hub, NetHubPort *source_port, 59f6c874e3SStefan Hajnoczi const struct iovec *iov, int iovcnt) 60f6c874e3SStefan Hajnoczi { 61f6c874e3SStefan Hajnoczi NetHubPort *port; 62f6c874e3SStefan Hajnoczi ssize_t ret = 0; 63f6c874e3SStefan Hajnoczi 64f6c874e3SStefan Hajnoczi QLIST_FOREACH(port, &hub->ports, next) { 65f6c874e3SStefan Hajnoczi if (port == source_port) { 66f6c874e3SStefan Hajnoczi continue; 67f6c874e3SStefan Hajnoczi } 68f6c874e3SStefan Hajnoczi 69f6c874e3SStefan Hajnoczi ret = qemu_sendv_packet(&port->nc, iov, iovcnt); 70f6c874e3SStefan Hajnoczi } 71f6c874e3SStefan Hajnoczi return ret; 72f6c874e3SStefan Hajnoczi } 73f6c874e3SStefan Hajnoczi 74f6c874e3SStefan Hajnoczi static NetHub *net_hub_new(int id) 75f6c874e3SStefan Hajnoczi { 76f6c874e3SStefan Hajnoczi NetHub *hub; 77f6c874e3SStefan Hajnoczi 78f6c874e3SStefan Hajnoczi hub = g_malloc(sizeof(*hub)); 79f6c874e3SStefan Hajnoczi hub->id = id; 80f6c874e3SStefan Hajnoczi hub->num_ports = 0; 81f6c874e3SStefan Hajnoczi QLIST_INIT(&hub->ports); 82f6c874e3SStefan Hajnoczi 83f6c874e3SStefan Hajnoczi QLIST_INSERT_HEAD(&hubs, hub, next); 84f6c874e3SStefan Hajnoczi 85f6c874e3SStefan Hajnoczi return hub; 86f6c874e3SStefan Hajnoczi } 87f6c874e3SStefan Hajnoczi 88f6c874e3SStefan Hajnoczi static ssize_t net_hub_port_receive(VLANClientState *nc, 89f6c874e3SStefan Hajnoczi const uint8_t *buf, size_t len) 90f6c874e3SStefan Hajnoczi { 91f6c874e3SStefan Hajnoczi NetHubPort *port = DO_UPCAST(NetHubPort, nc, nc); 92f6c874e3SStefan Hajnoczi 93f6c874e3SStefan Hajnoczi return net_hub_receive(port->hub, port, buf, len); 94f6c874e3SStefan Hajnoczi } 95f6c874e3SStefan Hajnoczi 96f6c874e3SStefan Hajnoczi static ssize_t net_hub_port_receive_iov(VLANClientState *nc, 97f6c874e3SStefan Hajnoczi const struct iovec *iov, int iovcnt) 98f6c874e3SStefan Hajnoczi { 99f6c874e3SStefan Hajnoczi NetHubPort *port = DO_UPCAST(NetHubPort, nc, nc); 100f6c874e3SStefan Hajnoczi 101f6c874e3SStefan Hajnoczi return net_hub_receive_iov(port->hub, port, iov, iovcnt); 102f6c874e3SStefan Hajnoczi } 103f6c874e3SStefan Hajnoczi 104f6c874e3SStefan Hajnoczi static void net_hub_port_cleanup(VLANClientState *nc) 105f6c874e3SStefan Hajnoczi { 106f6c874e3SStefan Hajnoczi NetHubPort *port = DO_UPCAST(NetHubPort, nc, nc); 107f6c874e3SStefan Hajnoczi 108f6c874e3SStefan Hajnoczi QLIST_REMOVE(port, next); 109f6c874e3SStefan Hajnoczi } 110f6c874e3SStefan Hajnoczi 111f6c874e3SStefan Hajnoczi static NetClientInfo net_hub_port_info = { 112f6c874e3SStefan Hajnoczi .type = NET_CLIENT_OPTIONS_KIND_HUBPORT, 113f6c874e3SStefan Hajnoczi .size = sizeof(NetHubPort), 114f6c874e3SStefan Hajnoczi .receive = net_hub_port_receive, 115f6c874e3SStefan Hajnoczi .receive_iov = net_hub_port_receive_iov, 116f6c874e3SStefan Hajnoczi .cleanup = net_hub_port_cleanup, 117f6c874e3SStefan Hajnoczi }; 118f6c874e3SStefan Hajnoczi 119f6c874e3SStefan Hajnoczi static NetHubPort *net_hub_port_new(NetHub *hub, const char *name) 120f6c874e3SStefan Hajnoczi { 121f6c874e3SStefan Hajnoczi VLANClientState *nc; 122f6c874e3SStefan Hajnoczi NetHubPort *port; 123f6c874e3SStefan Hajnoczi int id = hub->num_ports++; 124f6c874e3SStefan Hajnoczi char default_name[128]; 125f6c874e3SStefan Hajnoczi 126f6c874e3SStefan Hajnoczi if (!name) { 127f6c874e3SStefan Hajnoczi snprintf(default_name, sizeof(default_name), 128f6c874e3SStefan Hajnoczi "hub%dport%d", hub->id, id); 129f6c874e3SStefan Hajnoczi name = default_name; 130f6c874e3SStefan Hajnoczi } 131f6c874e3SStefan Hajnoczi 132f6c874e3SStefan Hajnoczi nc = qemu_new_net_client(&net_hub_port_info, NULL, NULL, "hub", name); 133f6c874e3SStefan Hajnoczi port = DO_UPCAST(NetHubPort, nc, nc); 134f6c874e3SStefan Hajnoczi port->id = id; 135f6c874e3SStefan Hajnoczi port->hub = hub; 136f6c874e3SStefan Hajnoczi 137f6c874e3SStefan Hajnoczi QLIST_INSERT_HEAD(&hub->ports, port, next); 138f6c874e3SStefan Hajnoczi 139f6c874e3SStefan Hajnoczi return port; 140f6c874e3SStefan Hajnoczi } 141f6c874e3SStefan Hajnoczi 142f6c874e3SStefan Hajnoczi /** 143f6c874e3SStefan Hajnoczi * Create a port on a given hub 144f6c874e3SStefan Hajnoczi * @name: Net client name or NULL for default name. 145f6c874e3SStefan Hajnoczi * 146f6c874e3SStefan Hajnoczi * If there is no existing hub with the given id then a new hub is created. 147f6c874e3SStefan Hajnoczi */ 148f6c874e3SStefan Hajnoczi VLANClientState *net_hub_add_port(int hub_id, const char *name) 149f6c874e3SStefan Hajnoczi { 150f6c874e3SStefan Hajnoczi NetHub *hub; 151f6c874e3SStefan Hajnoczi NetHubPort *port; 152f6c874e3SStefan Hajnoczi 153f6c874e3SStefan Hajnoczi QLIST_FOREACH(hub, &hubs, next) { 154f6c874e3SStefan Hajnoczi if (hub->id == hub_id) { 155f6c874e3SStefan Hajnoczi break; 156f6c874e3SStefan Hajnoczi } 157f6c874e3SStefan Hajnoczi } 158f6c874e3SStefan Hajnoczi 159f6c874e3SStefan Hajnoczi if (!hub) { 160f6c874e3SStefan Hajnoczi hub = net_hub_new(hub_id); 161f6c874e3SStefan Hajnoczi } 162f6c874e3SStefan Hajnoczi 163f6c874e3SStefan Hajnoczi port = net_hub_port_new(hub, name); 164f6c874e3SStefan Hajnoczi return &port->nc; 165f6c874e3SStefan Hajnoczi } 166f6c874e3SStefan Hajnoczi 167f6c874e3SStefan Hajnoczi /** 16890d87a33SStefan Hajnoczi * Find a specific client on a hub 16990d87a33SStefan Hajnoczi */ 17090d87a33SStefan Hajnoczi VLANClientState *net_hub_find_client_by_name(int hub_id, const char *name) 17190d87a33SStefan Hajnoczi { 17290d87a33SStefan Hajnoczi NetHub *hub; 17390d87a33SStefan Hajnoczi NetHubPort *port; 17490d87a33SStefan Hajnoczi VLANClientState *peer; 17590d87a33SStefan Hajnoczi 17690d87a33SStefan Hajnoczi QLIST_FOREACH(hub, &hubs, next) { 17790d87a33SStefan Hajnoczi if (hub->id == hub_id) { 17890d87a33SStefan Hajnoczi QLIST_FOREACH(port, &hub->ports, next) { 17990d87a33SStefan Hajnoczi peer = port->nc.peer; 18090d87a33SStefan Hajnoczi 18190d87a33SStefan Hajnoczi if (peer && strcmp(peer->name, name) == 0) { 18290d87a33SStefan Hajnoczi return peer; 18390d87a33SStefan Hajnoczi } 18490d87a33SStefan Hajnoczi } 18590d87a33SStefan Hajnoczi } 18690d87a33SStefan Hajnoczi } 18790d87a33SStefan Hajnoczi return NULL; 18890d87a33SStefan Hajnoczi } 18990d87a33SStefan Hajnoczi 19090d87a33SStefan Hajnoczi /** 191f6c874e3SStefan Hajnoczi * Print hub configuration 192f6c874e3SStefan Hajnoczi */ 193f6c874e3SStefan Hajnoczi void net_hub_info(Monitor *mon) 194f6c874e3SStefan Hajnoczi { 195f6c874e3SStefan Hajnoczi NetHub *hub; 196f6c874e3SStefan Hajnoczi NetHubPort *port; 197f6c874e3SStefan Hajnoczi 198f6c874e3SStefan Hajnoczi QLIST_FOREACH(hub, &hubs, next) { 199f6c874e3SStefan Hajnoczi monitor_printf(mon, "hub %d\n", hub->id); 200f6c874e3SStefan Hajnoczi QLIST_FOREACH(port, &hub->ports, next) { 201f6c874e3SStefan Hajnoczi monitor_printf(mon, " port %d peer %s\n", port->id, 202f6c874e3SStefan Hajnoczi port->nc.peer ? port->nc.peer->name : "<none>"); 203f6c874e3SStefan Hajnoczi } 204f6c874e3SStefan Hajnoczi } 205f6c874e3SStefan Hajnoczi } 206f6c874e3SStefan Hajnoczi 207f6c874e3SStefan Hajnoczi /** 208f6c874e3SStefan Hajnoczi * Get the hub id that a client is connected to 209f6c874e3SStefan Hajnoczi * 210f6c874e3SStefan Hajnoczi * @id Pointer for hub id output, may be NULL 211f6c874e3SStefan Hajnoczi */ 212f6c874e3SStefan Hajnoczi int net_hub_id_for_client(VLANClientState *nc, int *id) 213f6c874e3SStefan Hajnoczi { 214f6c874e3SStefan Hajnoczi NetHubPort *port; 215f6c874e3SStefan Hajnoczi 216f6c874e3SStefan Hajnoczi if (nc->info->type == NET_CLIENT_OPTIONS_KIND_HUBPORT) { 217f6c874e3SStefan Hajnoczi port = DO_UPCAST(NetHubPort, nc, nc); 218f6c874e3SStefan Hajnoczi } else if (nc->peer != NULL && nc->peer->info->type == 219f6c874e3SStefan Hajnoczi NET_CLIENT_OPTIONS_KIND_HUBPORT) { 220f6c874e3SStefan Hajnoczi port = DO_UPCAST(NetHubPort, nc, nc->peer); 221f6c874e3SStefan Hajnoczi } else { 222f6c874e3SStefan Hajnoczi return -ENOENT; 223f6c874e3SStefan Hajnoczi } 224f6c874e3SStefan Hajnoczi 225f6c874e3SStefan Hajnoczi if (id) { 226f6c874e3SStefan Hajnoczi *id = port->hub->id; 227f6c874e3SStefan Hajnoczi } 228f6c874e3SStefan Hajnoczi return 0; 229f6c874e3SStefan Hajnoczi } 230f6c874e3SStefan Hajnoczi 231f6c874e3SStefan Hajnoczi int net_init_hubport(const NetClientOptions *opts, const char *name, 232d33d93b2SStefan Hajnoczi VLANClientState *peer) 233f6c874e3SStefan Hajnoczi { 234f6c874e3SStefan Hajnoczi const NetdevHubPortOptions *hubport; 235f6c874e3SStefan Hajnoczi 236f6c874e3SStefan Hajnoczi assert(opts->kind == NET_CLIENT_OPTIONS_KIND_HUBPORT); 237f6c874e3SStefan Hajnoczi hubport = opts->hubport; 238f6c874e3SStefan Hajnoczi 239d33d93b2SStefan Hajnoczi /* Treat hub port like a backend, NIC must be the one to peer */ 240d33d93b2SStefan Hajnoczi if (peer) { 241f6c874e3SStefan Hajnoczi return -EINVAL; 242f6c874e3SStefan Hajnoczi } 243f6c874e3SStefan Hajnoczi 244f6c874e3SStefan Hajnoczi net_hub_add_port(hubport->hubid, name); 245f6c874e3SStefan Hajnoczi return 0; 246f6c874e3SStefan Hajnoczi } 247*81017645SStefan Hajnoczi 248*81017645SStefan Hajnoczi /** 249*81017645SStefan Hajnoczi * Warn if hub configurations are likely wrong 250*81017645SStefan Hajnoczi */ 251*81017645SStefan Hajnoczi void net_hub_check_clients(void) 252*81017645SStefan Hajnoczi { 253*81017645SStefan Hajnoczi NetHub *hub; 254*81017645SStefan Hajnoczi NetHubPort *port; 255*81017645SStefan Hajnoczi VLANClientState *peer; 256*81017645SStefan Hajnoczi 257*81017645SStefan Hajnoczi QLIST_FOREACH(hub, &hubs, next) { 258*81017645SStefan Hajnoczi int has_nic = 0, has_host_dev = 0; 259*81017645SStefan Hajnoczi 260*81017645SStefan Hajnoczi QLIST_FOREACH(port, &hub->ports, next) { 261*81017645SStefan Hajnoczi peer = port->nc.peer; 262*81017645SStefan Hajnoczi if (!peer) { 263*81017645SStefan Hajnoczi fprintf(stderr, "Warning: hub port %s has no peer\n", 264*81017645SStefan Hajnoczi port->nc.name); 265*81017645SStefan Hajnoczi continue; 266*81017645SStefan Hajnoczi } 267*81017645SStefan Hajnoczi 268*81017645SStefan Hajnoczi switch (peer->info->type) { 269*81017645SStefan Hajnoczi case NET_CLIENT_OPTIONS_KIND_NIC: 270*81017645SStefan Hajnoczi has_nic = 1; 271*81017645SStefan Hajnoczi break; 272*81017645SStefan Hajnoczi case NET_CLIENT_OPTIONS_KIND_USER: 273*81017645SStefan Hajnoczi case NET_CLIENT_OPTIONS_KIND_TAP: 274*81017645SStefan Hajnoczi case NET_CLIENT_OPTIONS_KIND_SOCKET: 275*81017645SStefan Hajnoczi case NET_CLIENT_OPTIONS_KIND_VDE: 276*81017645SStefan Hajnoczi has_host_dev = 1; 277*81017645SStefan Hajnoczi break; 278*81017645SStefan Hajnoczi default: 279*81017645SStefan Hajnoczi break; 280*81017645SStefan Hajnoczi } 281*81017645SStefan Hajnoczi } 282*81017645SStefan Hajnoczi if (has_host_dev && !has_nic) { 283*81017645SStefan Hajnoczi fprintf(stderr, "Warning: vlan %d with no nics\n", hub->id); 284*81017645SStefan Hajnoczi } 285*81017645SStefan Hajnoczi if (has_nic && !has_host_dev) { 286*81017645SStefan Hajnoczi fprintf(stderr, 287*81017645SStefan Hajnoczi "Warning: vlan %d is not connected to host network\n", 288*81017645SStefan Hajnoczi hub->id); 289*81017645SStefan Hajnoczi } 290*81017645SStefan Hajnoczi } 291*81017645SStefan Hajnoczi } 292