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