4 * Copyright IBM, Corp. 2012
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 "qapi/error.h"
17 #include "monitor/monitor.h"
22 #include "qemu/error-report.h"
25 * A hub broadcasts incoming packets to all its ports except the source port.
26 * Hubs can be used to provide independent emulated network segments.
29 typedef struct NetHub NetHub
;
31 typedef struct NetHubPort
{
33 QLIST_ENTRY(NetHubPort
) next
;
40 QLIST_ENTRY(NetHub
) next
;
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
)
52 QLIST_FOREACH(port
, &hub
->ports
, next
) {
53 if (port
== source_port
) {
57 qemu_send_packet(&port
->nc
, buf
, len
);
62 static ssize_t
net_hub_receive_iov(NetHub
*hub
, NetHubPort
*source_port
,
63 const struct iovec
*iov
, int iovcnt
)
66 ssize_t len
= iov_size(iov
, iovcnt
);
68 QLIST_FOREACH(port
, &hub
->ports
, next
) {
69 if (port
== source_port
) {
73 qemu_sendv_packet(&port
->nc
, iov
, iovcnt
);
78 static NetHub
*net_hub_new(int id
)
82 hub
= g_malloc(sizeof(*hub
));
85 QLIST_INIT(&hub
->ports
);
87 QLIST_INSERT_HEAD(&hubs
, hub
, next
);
92 static int net_hub_port_can_receive(NetClientState
*nc
)
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
) {
103 if (qemu_can_send_packet(&port
->nc
)) {
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
,
144 NetClientState
*hubpeer
)
148 int id
= hub
->num_ports
++;
149 char default_name
[128];
152 snprintf(default_name
, sizeof(default_name
),
153 "hub%dport%d", hub
->id
, id
);
157 nc
= qemu_new_net_client(&net_hub_port_info
, hubpeer
, "hub", name
);
158 port
= DO_UPCAST(NetHubPort
, nc
, nc
);
162 QLIST_INSERT_HEAD(&hub
->ports
, port
, next
);
168 * Create a port on a given hub
169 * @hub_id: Number of the hub
170 * @name: Net client name or NULL for default name.
171 * @hubpeer: Peer to use (if "netdev=id" has been specified)
173 * If there is no existing hub with the given id then a new hub is created.
175 NetClientState
*net_hub_add_port(int hub_id
, const char *name
,
176 NetClientState
*hubpeer
)
181 QLIST_FOREACH(hub
, &hubs
, next
) {
182 if (hub
->id
== hub_id
) {
188 hub
= net_hub_new(hub_id
);
191 port
= net_hub_port_new(hub
, name
, hubpeer
);
196 * Find a specific client on a hub
198 NetClientState
*net_hub_find_client_by_name(int hub_id
, const char *name
)
202 NetClientState
*peer
;
204 QLIST_FOREACH(hub
, &hubs
, next
) {
205 if (hub
->id
== hub_id
) {
206 QLIST_FOREACH(port
, &hub
->ports
, next
) {
207 peer
= port
->nc
.peer
;
209 if (peer
&& strcmp(peer
->name
, name
) == 0) {
219 * Find a available port on a hub; otherwise create one new port
221 NetClientState
*net_hub_port_find(int hub_id
)
227 QLIST_FOREACH(hub
, &hubs
, next
) {
228 if (hub
->id
== hub_id
) {
229 QLIST_FOREACH(port
, &hub
->ports
, next
) {
239 nc
= net_hub_add_port(hub_id
, NULL
, NULL
);
244 * Print hub configuration
246 void net_hub_info(Monitor
*mon
)
251 QLIST_FOREACH(hub
, &hubs
, next
) {
252 monitor_printf(mon
, "hub %d\n", hub
->id
);
253 QLIST_FOREACH(port
, &hub
->ports
, next
) {
254 monitor_printf(mon
, " \\ %s", port
->nc
.name
);
256 monitor_printf(mon
, ": ");
257 print_net_client(mon
, port
->nc
.peer
);
259 monitor_printf(mon
, "\n");
266 * Get the hub id that a client is connected to
268 * @id: Pointer for hub id output, may be NULL
270 int net_hub_id_for_client(NetClientState
*nc
, int *id
)
274 if (nc
->info
->type
== NET_CLIENT_DRIVER_HUBPORT
) {
275 port
= DO_UPCAST(NetHubPort
, nc
, nc
);
276 } else if (nc
->peer
!= NULL
&& nc
->peer
->info
->type
==
277 NET_CLIENT_DRIVER_HUBPORT
) {
278 port
= DO_UPCAST(NetHubPort
, nc
, nc
->peer
);
289 int net_init_hubport(const Netdev
*netdev
, const char *name
,
290 NetClientState
*peer
, Error
**errp
)
292 const NetdevHubPortOptions
*hubport
;
293 NetClientState
*hubpeer
= NULL
;
295 assert(netdev
->type
== NET_CLIENT_DRIVER_HUBPORT
);
297 hubport
= &netdev
->u
.hubport
;
299 if (hubport
->has_netdev
) {
300 hubpeer
= qemu_find_netdev(hubport
->netdev
);
302 error_setg(errp
, "netdev '%s' not found", hubport
->netdev
);
307 net_hub_add_port(hubport
->hubid
, name
, hubpeer
);
313 * Warn if hub configurations are likely wrong
315 void net_hub_check_clients(void)
319 NetClientState
*peer
;
321 QLIST_FOREACH(hub
, &hubs
, next
) {
322 int has_nic
= 0, has_host_dev
= 0;
324 QLIST_FOREACH(port
, &hub
->ports
, next
) {
325 peer
= port
->nc
.peer
;
327 warn_report("hub port %s has no peer", port
->nc
.name
);
331 switch (peer
->info
->type
) {
332 case NET_CLIENT_DRIVER_NIC
:
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
:
346 if (has_host_dev
&& !has_nic
) {
347 warn_report("hub %d with no nics", hub
->id
);
349 if (has_nic
&& !has_host_dev
) {
350 warn_report("hub %d is not connected to host network", hub
->id
);
355 bool net_hub_flush(NetClientState
*nc
)
358 NetHubPort
*source_port
= DO_UPCAST(NetHubPort
, nc
, nc
);
361 QLIST_FOREACH(port
, &source_port
->hub
->ports
, next
) {
362 if (port
!= source_port
) {
363 ret
+= qemu_net_queue_flush(port
->nc
.incoming_queue
);
366 return ret
? true : false;