Merge remote-tracking branch 'remotes/mdroth/tags/qga-pull-2017-10-26-tag' into staging
[qemu.git] / net / hub.c
blob14b4eec68f9827b90a22e11292d929f2ecded6f3
1 /*
2 * Hub net client
4 * Copyright IBM, Corp. 2012
6 * Authors:
7 * Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
8 * Zhi Yong Wu <wuzhy@linux.vnet.ibm.com>
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.
15 #include "qemu/osdep.h"
16 #include "monitor/monitor.h"
17 #include "net/net.h"
18 #include "clients.h"
19 #include "hub.h"
20 #include "qemu/iov.h"
21 #include "qemu/error-report.h"
24 * A hub broadcasts incoming packets to all its ports except the source port.
25 * Hubs can be used to provide independent network segments, also confusingly
26 * named the QEMU 'vlan' feature.
29 typedef struct NetHub NetHub;
31 typedef struct NetHubPort {
32 NetClientState nc;
33 QLIST_ENTRY(NetHubPort) next;
34 NetHub *hub;
35 int id;
36 } NetHubPort;
38 struct NetHub {
39 int id;
40 QLIST_ENTRY(NetHub) next;
41 int num_ports;
42 QLIST_HEAD(, NetHubPort) ports;
45 static QLIST_HEAD(, NetHub) hubs = QLIST_HEAD_INITIALIZER(&hubs);
47 static ssize_t net_hub_receive(NetHub *hub, NetHubPort *source_port,
48 const uint8_t *buf, size_t len)
50 NetHubPort *port;
52 QLIST_FOREACH(port, &hub->ports, next) {
53 if (port == source_port) {
54 continue;
57 qemu_send_packet(&port->nc, buf, len);
59 return len;
62 static ssize_t net_hub_receive_iov(NetHub *hub, NetHubPort *source_port,
63 const struct iovec *iov, int iovcnt)
65 NetHubPort *port;
66 ssize_t len = iov_size(iov, iovcnt);
68 QLIST_FOREACH(port, &hub->ports, next) {
69 if (port == source_port) {
70 continue;
73 qemu_sendv_packet(&port->nc, iov, iovcnt);
75 return len;
78 static NetHub *net_hub_new(int id)
80 NetHub *hub;
82 hub = g_malloc(sizeof(*hub));
83 hub->id = id;
84 hub->num_ports = 0;
85 QLIST_INIT(&hub->ports);
87 QLIST_INSERT_HEAD(&hubs, hub, next);
89 return hub;
92 static int net_hub_port_can_receive(NetClientState *nc)
94 NetHubPort *port;
95 NetHubPort *src_port = DO_UPCAST(NetHubPort, nc, nc);
96 NetHub *hub = src_port->hub;
98 QLIST_FOREACH(port, &hub->ports, next) {
99 if (port == src_port) {
100 continue;
103 if (qemu_can_send_packet(&port->nc)) {
104 return 1;
108 return 0;
111 static ssize_t net_hub_port_receive(NetClientState *nc,
112 const uint8_t *buf, size_t len)
114 NetHubPort *port = DO_UPCAST(NetHubPort, nc, nc);
116 return net_hub_receive(port->hub, port, buf, len);
119 static ssize_t net_hub_port_receive_iov(NetClientState *nc,
120 const struct iovec *iov, int iovcnt)
122 NetHubPort *port = DO_UPCAST(NetHubPort, nc, nc);
124 return net_hub_receive_iov(port->hub, port, iov, iovcnt);
127 static void net_hub_port_cleanup(NetClientState *nc)
129 NetHubPort *port = DO_UPCAST(NetHubPort, nc, nc);
131 QLIST_REMOVE(port, next);
134 static 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,
143 static NetHubPort *net_hub_port_new(NetHub *hub, const char *name)
145 NetClientState *nc;
146 NetHubPort *port;
147 int id = hub->num_ports++;
148 char default_name[128];
150 if (!name) {
151 snprintf(default_name, sizeof(default_name),
152 "hub%dport%d", hub->id, id);
153 name = default_name;
156 nc = qemu_new_net_client(&net_hub_port_info, NULL, "hub", name);
157 port = DO_UPCAST(NetHubPort, nc, nc);
158 port->id = id;
159 port->hub = hub;
161 QLIST_INSERT_HEAD(&hub->ports, port, next);
163 return port;
167 * Create a port on a given hub
168 * @name: Net client name or NULL for default name.
170 * If there is no existing hub with the given id then a new hub is created.
172 NetClientState *net_hub_add_port(int hub_id, const char *name)
174 NetHub *hub;
175 NetHubPort *port;
177 QLIST_FOREACH(hub, &hubs, next) {
178 if (hub->id == hub_id) {
179 break;
183 if (!hub) {
184 hub = net_hub_new(hub_id);
187 port = net_hub_port_new(hub, name);
188 return &port->nc;
192 * Find a specific client on a hub
194 NetClientState *net_hub_find_client_by_name(int hub_id, const char *name)
196 NetHub *hub;
197 NetHubPort *port;
198 NetClientState *peer;
200 QLIST_FOREACH(hub, &hubs, next) {
201 if (hub->id == hub_id) {
202 QLIST_FOREACH(port, &hub->ports, next) {
203 peer = port->nc.peer;
205 if (peer && strcmp(peer->name, name) == 0) {
206 return peer;
211 return NULL;
215 * Find a available port on a hub; otherwise create one new port
217 NetClientState *net_hub_port_find(int hub_id)
219 NetHub *hub;
220 NetHubPort *port;
221 NetClientState *nc;
223 QLIST_FOREACH(hub, &hubs, next) {
224 if (hub->id == hub_id) {
225 QLIST_FOREACH(port, &hub->ports, next) {
226 nc = port->nc.peer;
227 if (!nc) {
228 return &(port->nc);
231 break;
235 nc = net_hub_add_port(hub_id, NULL);
236 return nc;
240 * Print hub configuration
242 void net_hub_info(Monitor *mon)
244 NetHub *hub;
245 NetHubPort *port;
247 QLIST_FOREACH(hub, &hubs, next) {
248 monitor_printf(mon, "hub %d\n", hub->id);
249 QLIST_FOREACH(port, &hub->ports, next) {
250 monitor_printf(mon, " \\ %s", port->nc.name);
251 if (port->nc.peer) {
252 monitor_printf(mon, ": ");
253 print_net_client(mon, port->nc.peer);
254 } else {
255 monitor_printf(mon, "\n");
262 * Get the hub id that a client is connected to
264 * @id: Pointer for hub id output, may be NULL
266 int net_hub_id_for_client(NetClientState *nc, int *id)
268 NetHubPort *port;
270 if (nc->info->type == NET_CLIENT_DRIVER_HUBPORT) {
271 port = DO_UPCAST(NetHubPort, nc, nc);
272 } else if (nc->peer != NULL && nc->peer->info->type ==
273 NET_CLIENT_DRIVER_HUBPORT) {
274 port = DO_UPCAST(NetHubPort, nc, nc->peer);
275 } else {
276 return -ENOENT;
279 if (id) {
280 *id = port->hub->id;
282 return 0;
285 int net_init_hubport(const Netdev *netdev, const char *name,
286 NetClientState *peer, Error **errp)
288 const NetdevHubPortOptions *hubport;
290 assert(netdev->type == NET_CLIENT_DRIVER_HUBPORT);
291 assert(!peer);
292 hubport = &netdev->u.hubport;
294 net_hub_add_port(hubport->hubid, name);
295 return 0;
299 * Warn if hub configurations are likely wrong
301 void net_hub_check_clients(void)
303 NetHub *hub;
304 NetHubPort *port;
305 NetClientState *peer;
307 QLIST_FOREACH(hub, &hubs, next) {
308 int has_nic = 0, has_host_dev = 0;
310 QLIST_FOREACH(port, &hub->ports, next) {
311 peer = port->nc.peer;
312 if (!peer) {
313 warn_report("hub port %s has no peer", port->nc.name);
314 continue;
317 switch (peer->info->type) {
318 case NET_CLIENT_DRIVER_NIC:
319 has_nic = 1;
320 break;
321 case NET_CLIENT_DRIVER_USER:
322 case NET_CLIENT_DRIVER_TAP:
323 case NET_CLIENT_DRIVER_SOCKET:
324 case NET_CLIENT_DRIVER_VDE:
325 case NET_CLIENT_DRIVER_VHOST_USER:
326 has_host_dev = 1;
327 break;
328 default:
329 break;
332 if (has_host_dev && !has_nic) {
333 warn_report("vlan %d with no nics", hub->id);
335 if (has_nic && !has_host_dev) {
336 warn_report("vlan %d is not connected to host network", hub->id);
341 bool net_hub_flush(NetClientState *nc)
343 NetHubPort *port;
344 NetHubPort *source_port = DO_UPCAST(NetHubPort, nc, nc);
345 int ret = 0;
347 QLIST_FOREACH(port, &source_port->hub->ports, next) {
348 if (port != source_port) {
349 ret += qemu_net_queue_flush(port->nc.incoming_queue);
352 return ret ? true : false;