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