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 monitor_printf(mon, " \\ %s", port->nc.name); 249 if (port->nc.peer) { 250 monitor_printf(mon, ": "); 251 print_net_client(mon, port->nc.peer); 252 } else { 253 monitor_printf(mon, "\n"); 254 } 255 } 256 } 257 } 258 259 /** 260 * Get the hub id that a client is connected to 261 * 262 * @id: Pointer for hub id output, may be NULL 263 */ 264 int net_hub_id_for_client(NetClientState *nc, int *id) 265 { 266 NetHubPort *port; 267 268 if (nc->info->type == NET_CLIENT_OPTIONS_KIND_HUBPORT) { 269 port = DO_UPCAST(NetHubPort, nc, nc); 270 } else if (nc->peer != NULL && nc->peer->info->type == 271 NET_CLIENT_OPTIONS_KIND_HUBPORT) { 272 port = DO_UPCAST(NetHubPort, nc, nc->peer); 273 } else { 274 return -ENOENT; 275 } 276 277 if (id) { 278 *id = port->hub->id; 279 } 280 return 0; 281 } 282 283 int net_init_hubport(const NetClientOptions *opts, const char *name, 284 NetClientState *peer) 285 { 286 const NetdevHubPortOptions *hubport; 287 288 assert(opts->kind == NET_CLIENT_OPTIONS_KIND_HUBPORT); 289 hubport = opts->hubport; 290 291 if (peer) { 292 return -EINVAL; 293 } 294 295 net_hub_add_port(hubport->hubid, name); 296 return 0; 297 } 298 299 /** 300 * Warn if hub configurations are likely wrong 301 */ 302 void net_hub_check_clients(void) 303 { 304 NetHub *hub; 305 NetHubPort *port; 306 NetClientState *peer; 307 308 QLIST_FOREACH(hub, &hubs, next) { 309 int has_nic = 0, has_host_dev = 0; 310 311 QLIST_FOREACH(port, &hub->ports, next) { 312 peer = port->nc.peer; 313 if (!peer) { 314 fprintf(stderr, "Warning: hub port %s has no peer\n", 315 port->nc.name); 316 continue; 317 } 318 319 switch (peer->info->type) { 320 case NET_CLIENT_OPTIONS_KIND_NIC: 321 has_nic = 1; 322 break; 323 case NET_CLIENT_OPTIONS_KIND_USER: 324 case NET_CLIENT_OPTIONS_KIND_TAP: 325 case NET_CLIENT_OPTIONS_KIND_SOCKET: 326 case NET_CLIENT_OPTIONS_KIND_VDE: 327 case NET_CLIENT_OPTIONS_KIND_VHOST_USER: 328 has_host_dev = 1; 329 break; 330 default: 331 break; 332 } 333 } 334 if (has_host_dev && !has_nic) { 335 fprintf(stderr, "Warning: vlan %d with no nics\n", hub->id); 336 } 337 if (has_nic && !has_host_dev) { 338 fprintf(stderr, 339 "Warning: vlan %d is not connected to host network\n", 340 hub->id); 341 } 342 } 343 } 344 345 bool net_hub_flush(NetClientState *nc) 346 { 347 NetHubPort *port; 348 NetHubPort *source_port = DO_UPCAST(NetHubPort, nc, nc); 349 int ret = 0; 350 351 QLIST_FOREACH(port, &source_port->hub->ports, next) { 352 if (port != source_port) { 353 ret += qemu_net_queue_flush(port->nc.incoming_queue); 354 } 355 } 356 return ret ? true : false; 357 } 358