]> Git Repo - qemu.git/blame - net/hub.c
linux-user: Use is_error() to avoid warnings and make the code clearer
[qemu.git] / net / hub.c
CommitLineData
f6c874e3
SH
1/*
2 * Hub net client
3 *
4 * Copyright IBM, Corp. 2012
5 *
6 * Authors:
7 * Stefan Hajnoczi <[email protected]>
8 * Zhi Yong Wu <[email protected]>
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
2744d920 15#include "qemu/osdep.h"
18d65d22 16#include "qapi/error.h"
83c9089e 17#include "monitor/monitor.h"
1422e32d 18#include "net/net.h"
a245fc18 19#include "clients.h"
f6c874e3 20#include "hub.h"
1de7afc9 21#include "qemu/iov.h"
2ab4b135 22#include "qemu/error-report.h"
f6c874e3
SH
23
24/*
25 * A hub broadcasts incoming packets to all its ports except the source port.
442da403 26 * Hubs can be used to provide independent emulated network segments.
f6c874e3
SH
27 */
28
29typedef struct NetHub NetHub;
30
31typedef struct NetHubPort {
4e68f7a0 32 NetClientState nc;
f6c874e3
SH
33 QLIST_ENTRY(NetHubPort) next;
34 NetHub *hub;
35 int id;
36} NetHubPort;
37
38struct NetHub {
39 int id;
40 QLIST_ENTRY(NetHub) next;
41 int num_ports;
42 QLIST_HEAD(, NetHubPort) ports;
43};
44
45static QLIST_HEAD(, NetHub) hubs = QLIST_HEAD_INITIALIZER(&hubs);
46
47static ssize_t net_hub_receive(NetHub *hub, NetHubPort *source_port,
48 const uint8_t *buf, size_t len)
49{
50 NetHubPort *port;
51
52 QLIST_FOREACH(port, &hub->ports, next) {
53 if (port == source_port) {
54 continue;
55 }
56
57 qemu_send_packet(&port->nc, buf, len);
58 }
59 return len;
60}
61
62static ssize_t net_hub_receive_iov(NetHub *hub, NetHubPort *source_port,
63 const struct iovec *iov, int iovcnt)
64{
65 NetHubPort *port;
52a3cb86 66 ssize_t len = iov_size(iov, iovcnt);
f6c874e3
SH
67
68 QLIST_FOREACH(port, &hub->ports, next) {
69 if (port == source_port) {
70 continue;
71 }
72
52a3cb86 73 qemu_sendv_packet(&port->nc, iov, iovcnt);
f6c874e3 74 }
52a3cb86 75 return len;
f6c874e3
SH
76}
77
78static NetHub *net_hub_new(int id)
79{
80 NetHub *hub;
81
82 hub = g_malloc(sizeof(*hub));
83 hub->id = id;
84 hub->num_ports = 0;
85 QLIST_INIT(&hub->ports);
86
87 QLIST_INSERT_HEAD(&hubs, hub, next);
88
89 return hub;
90}
91
52a3cb86
ZYW
92static int net_hub_port_can_receive(NetClientState *nc)
93{
94 NetHubPort *port;
95 NetHubPort *src_port = DO_UPCAST(NetHubPort, nc, nc);
96 NetHub *hub = src_port->hub;
97
98 QLIST_FOREACH(port, &hub->ports, next) {
99 if (port == src_port) {
100 continue;
101 }
102
61518a74
SH
103 if (qemu_can_send_packet(&port->nc)) {
104 return 1;
52a3cb86
ZYW
105 }
106 }
107
61518a74 108 return 0;
52a3cb86
ZYW
109}
110
4e68f7a0 111static ssize_t net_hub_port_receive(NetClientState *nc,
f6c874e3
SH
112 const uint8_t *buf, size_t len)
113{
114 NetHubPort *port = DO_UPCAST(NetHubPort, nc, nc);
115
116 return net_hub_receive(port->hub, port, buf, len);
117}
118
4e68f7a0 119static ssize_t net_hub_port_receive_iov(NetClientState *nc,
f6c874e3
SH
120 const struct iovec *iov, int iovcnt)
121{
122 NetHubPort *port = DO_UPCAST(NetHubPort, nc, nc);
123
124 return net_hub_receive_iov(port->hub, port, iov, iovcnt);
125}
126
4e68f7a0 127static void net_hub_port_cleanup(NetClientState *nc)
f6c874e3
SH
128{
129 NetHubPort *port = DO_UPCAST(NetHubPort, nc, nc);
130
131 QLIST_REMOVE(port, next);
132}
133
134static NetClientInfo net_hub_port_info = {
f394b2e2 135 .type = NET_CLIENT_DRIVER_HUBPORT,
f6c874e3 136 .size = sizeof(NetHubPort),
52a3cb86 137 .can_receive = net_hub_port_can_receive,
f6c874e3
SH
138 .receive = net_hub_port_receive,
139 .receive_iov = net_hub_port_receive_iov,
140 .cleanup = net_hub_port_cleanup,
141};
142
18d65d22
TH
143static NetHubPort *net_hub_port_new(NetHub *hub, const char *name,
144 NetClientState *hubpeer)
f6c874e3 145{
4e68f7a0 146 NetClientState *nc;
f6c874e3
SH
147 NetHubPort *port;
148 int id = hub->num_ports++;
149 char default_name[128];
150
151 if (!name) {
152 snprintf(default_name, sizeof(default_name),
153 "hub%dport%d", hub->id, id);
154 name = default_name;
155 }
156
18d65d22 157 nc = qemu_new_net_client(&net_hub_port_info, hubpeer, "hub", name);
f6c874e3
SH
158 port = DO_UPCAST(NetHubPort, nc, nc);
159 port->id = id;
160 port->hub = hub;
161
162 QLIST_INSERT_HEAD(&hub->ports, port, next);
163
164 return port;
165}
166
167/**
168 * Create a port on a given hub
18d65d22 169 * @hub_id: Number of the hub
f6c874e3 170 * @name: Net client name or NULL for default name.
18d65d22 171 * @hubpeer: Peer to use (if "netdev=id" has been specified)
f6c874e3
SH
172 *
173 * If there is no existing hub with the given id then a new hub is created.
174 */
18d65d22
TH
175NetClientState *net_hub_add_port(int hub_id, const char *name,
176 NetClientState *hubpeer)
f6c874e3
SH
177{
178 NetHub *hub;
179 NetHubPort *port;
180
181 QLIST_FOREACH(hub, &hubs, next) {
182 if (hub->id == hub_id) {
183 break;
184 }
185 }
186
187 if (!hub) {
188 hub = net_hub_new(hub_id);
189 }
190
18d65d22 191 port = net_hub_port_new(hub, name, hubpeer);
f6c874e3
SH
192 return &port->nc;
193}
194
90d87a33
SH
195/**
196 * Find a specific client on a hub
197 */
4e68f7a0 198NetClientState *net_hub_find_client_by_name(int hub_id, const char *name)
90d87a33
SH
199{
200 NetHub *hub;
201 NetHubPort *port;
4e68f7a0 202 NetClientState *peer;
90d87a33
SH
203
204 QLIST_FOREACH(hub, &hubs, next) {
205 if (hub->id == hub_id) {
206 QLIST_FOREACH(port, &hub->ports, next) {
207 peer = port->nc.peer;
208
209 if (peer && strcmp(peer->name, name) == 0) {
210 return peer;
211 }
212 }
213 }
214 }
215 return NULL;
216}
217
606c10e2
ZYW
218/**
219 * Find a available port on a hub; otherwise create one new port
220 */
4e68f7a0 221NetClientState *net_hub_port_find(int hub_id)
606c10e2
ZYW
222{
223 NetHub *hub;
224 NetHubPort *port;
4e68f7a0 225 NetClientState *nc;
606c10e2
ZYW
226
227 QLIST_FOREACH(hub, &hubs, next) {
228 if (hub->id == hub_id) {
229 QLIST_FOREACH(port, &hub->ports, next) {
230 nc = port->nc.peer;
231 if (!nc) {
232 return &(port->nc);
233 }
234 }
235 break;
236 }
237 }
238
18d65d22 239 nc = net_hub_add_port(hub_id, NULL, NULL);
606c10e2
ZYW
240 return nc;
241}
242
f6c874e3
SH
243/**
244 * Print hub configuration
245 */
246void net_hub_info(Monitor *mon)
247{
248 NetHub *hub;
249 NetHubPort *port;
250
251 QLIST_FOREACH(hub, &hubs, next) {
252 monitor_printf(mon, "hub %d\n", hub->id);
253 QLIST_FOREACH(port, &hub->ports, next) {
a6efd6ae 254 monitor_printf(mon, " \\ %s", port->nc.name);
1a859593 255 if (port->nc.peer) {
a6efd6ae 256 monitor_printf(mon, ": ");
1a859593 257 print_net_client(mon, port->nc.peer);
a6efd6ae
JW
258 } else {
259 monitor_printf(mon, "\n");
1a859593 260 }
f6c874e3
SH
261 }
262 }
263}
264
265/**
266 * Get the hub id that a client is connected to
267 *
e103129b 268 * @id: Pointer for hub id output, may be NULL
f6c874e3 269 */
4e68f7a0 270int net_hub_id_for_client(NetClientState *nc, int *id)
f6c874e3
SH
271{
272 NetHubPort *port;
273
f394b2e2 274 if (nc->info->type == NET_CLIENT_DRIVER_HUBPORT) {
f6c874e3
SH
275 port = DO_UPCAST(NetHubPort, nc, nc);
276 } else if (nc->peer != NULL && nc->peer->info->type ==
f394b2e2 277 NET_CLIENT_DRIVER_HUBPORT) {
f6c874e3
SH
278 port = DO_UPCAST(NetHubPort, nc, nc->peer);
279 } else {
280 return -ENOENT;
281 }
282
283 if (id) {
284 *id = port->hub->id;
285 }
286 return 0;
287}
288
cebea510 289int net_init_hubport(const Netdev *netdev, const char *name,
a30ecde6 290 NetClientState *peer, Error **errp)
f6c874e3
SH
291{
292 const NetdevHubPortOptions *hubport;
18d65d22 293 NetClientState *hubpeer = NULL;
f6c874e3 294
f394b2e2 295 assert(netdev->type == NET_CLIENT_DRIVER_HUBPORT);
ca7eb184 296 assert(!peer);
f394b2e2 297 hubport = &netdev->u.hubport;
f6c874e3 298
18d65d22
TH
299 if (hubport->has_netdev) {
300 hubpeer = qemu_find_netdev(hubport->netdev);
301 if (!hubpeer) {
302 error_setg(errp, "netdev '%s' not found", hubport->netdev);
303 return -1;
304 }
305 }
306
307 net_hub_add_port(hubport->hubid, name, hubpeer);
308
f6c874e3
SH
309 return 0;
310}
81017645
SH
311
312/**
313 * Warn if hub configurations are likely wrong
314 */
315void net_hub_check_clients(void)
316{
317 NetHub *hub;
318 NetHubPort *port;
4e68f7a0 319 NetClientState *peer;
81017645
SH
320
321 QLIST_FOREACH(hub, &hubs, next) {
322 int has_nic = 0, has_host_dev = 0;
323
324 QLIST_FOREACH(port, &hub->ports, next) {
325 peer = port->nc.peer;
326 if (!peer) {
b62e39b4 327 warn_report("hub port %s has no peer", port->nc.name);
81017645
SH
328 continue;
329 }
330
331 switch (peer->info->type) {
f394b2e2 332 case NET_CLIENT_DRIVER_NIC:
81017645
SH
333 has_nic = 1;
334 break;
f394b2e2
EB
335 case NET_CLIENT_DRIVER_USER:
336 case NET_CLIENT_DRIVER_TAP:
337 case NET_CLIENT_DRIVER_SOCKET:
338 case NET_CLIENT_DRIVER_VDE:
339 case NET_CLIENT_DRIVER_VHOST_USER:
81017645
SH
340 has_host_dev = 1;
341 break;
342 default:
343 break;
344 }
345 }
346 if (has_host_dev && !has_nic) {
442da403 347 warn_report("hub %d with no nics", hub->id);
81017645
SH
348 }
349 if (has_nic && !has_host_dev) {
442da403 350 warn_report("hub %d is not connected to host network", hub->id);
81017645
SH
351 }
352 }
353}
199ee608
LR
354
355bool net_hub_flush(NetClientState *nc)
356{
357 NetHubPort *port;
358 NetHubPort *source_port = DO_UPCAST(NetHubPort, nc, nc);
359 int ret = 0;
360
361 QLIST_FOREACH(port, &source_port->hub->ports, next) {
362 if (port != source_port) {
067404be 363 ret += qemu_net_queue_flush(port->nc.incoming_queue);
199ee608
LR
364 }
365 }
366 return ret ? true : false;
367}
This page took 0.316735 seconds and 4 git commands to generate.