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 "monitor/monitor.h"
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.
28 typedef struct NetHub NetHub
;
30 typedef struct NetHubPort
{
32 QLIST_ENTRY(NetHubPort
) next
;
39 QLIST_ENTRY(NetHub
) next
;
41 QLIST_HEAD(, NetHubPort
) ports
;
44 static QLIST_HEAD(, NetHub
) hubs
= QLIST_HEAD_INITIALIZER(&hubs
);
46 static ssize_t
net_hub_receive(NetHub
*hub
, NetHubPort
*source_port
,
47 const uint8_t *buf
, size_t len
)
51 QLIST_FOREACH(port
, &hub
->ports
, next
) {
52 if (port
== source_port
) {
56 qemu_send_packet(&port
->nc
, buf
, len
);
61 static ssize_t
net_hub_receive_iov(NetHub
*hub
, NetHubPort
*source_port
,
62 const struct iovec
*iov
, int iovcnt
)
65 ssize_t len
= iov_size(iov
, iovcnt
);
67 QLIST_FOREACH(port
, &hub
->ports
, next
) {
68 if (port
== source_port
) {
72 qemu_sendv_packet(&port
->nc
, iov
, iovcnt
);
77 static NetHub
*net_hub_new(int id
)
81 hub
= g_malloc(sizeof(*hub
));
84 QLIST_INIT(&hub
->ports
);
86 QLIST_INSERT_HEAD(&hubs
, hub
, next
);
91 static int net_hub_port_can_receive(NetClientState
*nc
)
94 NetHubPort
*src_port
= DO_UPCAST(NetHubPort
, nc
, nc
);
95 NetHub
*hub
= src_port
->hub
;
97 QLIST_FOREACH(port
, &hub
->ports
, next
) {
98 if (port
== src_port
) {
102 if (qemu_can_send_packet(&port
->nc
)) {
110 static ssize_t
net_hub_port_receive(NetClientState
*nc
,
111 const uint8_t *buf
, size_t len
)
113 NetHubPort
*port
= DO_UPCAST(NetHubPort
, nc
, nc
);
115 return net_hub_receive(port
->hub
, port
, buf
, len
);
118 static ssize_t
net_hub_port_receive_iov(NetClientState
*nc
,
119 const struct iovec
*iov
, int iovcnt
)
121 NetHubPort
*port
= DO_UPCAST(NetHubPort
, nc
, nc
);
123 return net_hub_receive_iov(port
->hub
, port
, iov
, iovcnt
);
126 static void net_hub_port_cleanup(NetClientState
*nc
)
128 NetHubPort
*port
= DO_UPCAST(NetHubPort
, nc
, nc
);
130 QLIST_REMOVE(port
, next
);
133 static NetClientInfo net_hub_port_info
= {
134 .type
= NET_CLIENT_OPTIONS_KIND_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
,
142 static NetHubPort
*net_hub_port_new(NetHub
*hub
, const char *name
)
146 int id
= hub
->num_ports
++;
147 char default_name
[128];
150 snprintf(default_name
, sizeof(default_name
),
151 "hub%dport%d", hub
->id
, id
);
155 nc
= qemu_new_net_client(&net_hub_port_info
, NULL
, "hub", name
);
156 port
= DO_UPCAST(NetHubPort
, nc
, nc
);
160 QLIST_INSERT_HEAD(&hub
->ports
, port
, next
);
166 * Create a port on a given hub
167 * @name: Net client name or NULL for default name.
169 * If there is no existing hub with the given id then a new hub is created.
171 NetClientState
*net_hub_add_port(int hub_id
, const char *name
)
176 QLIST_FOREACH(hub
, &hubs
, next
) {
177 if (hub
->id
== hub_id
) {
183 hub
= net_hub_new(hub_id
);
186 port
= net_hub_port_new(hub
, name
);
191 * Find a specific client on a hub
193 NetClientState
*net_hub_find_client_by_name(int hub_id
, const char *name
)
197 NetClientState
*peer
;
199 QLIST_FOREACH(hub
, &hubs
, next
) {
200 if (hub
->id
== hub_id
) {
201 QLIST_FOREACH(port
, &hub
->ports
, next
) {
202 peer
= port
->nc
.peer
;
204 if (peer
&& strcmp(peer
->name
, name
) == 0) {
214 * Find a available port on a hub; otherwise create one new port
216 NetClientState
*net_hub_port_find(int hub_id
)
222 QLIST_FOREACH(hub
, &hubs
, next
) {
223 if (hub
->id
== hub_id
) {
224 QLIST_FOREACH(port
, &hub
->ports
, next
) {
234 nc
= net_hub_add_port(hub_id
, NULL
);
239 * Print hub configuration
241 void net_hub_info(Monitor
*mon
)
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
);
251 monitor_printf(mon
, ": ");
252 print_net_client(mon
, port
->nc
.peer
);
254 monitor_printf(mon
, "\n");
261 * Get the hub id that a client is connected to
263 * @id: Pointer for hub id output, may be NULL
265 int net_hub_id_for_client(NetClientState
*nc
, int *id
)
269 if (nc
->info
->type
== NET_CLIENT_OPTIONS_KIND_HUBPORT
) {
270 port
= DO_UPCAST(NetHubPort
, nc
, nc
);
271 } else if (nc
->peer
!= NULL
&& nc
->peer
->info
->type
==
272 NET_CLIENT_OPTIONS_KIND_HUBPORT
) {
273 port
= DO_UPCAST(NetHubPort
, nc
, nc
->peer
);
284 int net_init_hubport(const NetClientOptions
*opts
, const char *name
,
285 NetClientState
*peer
, Error
**errp
)
287 const NetdevHubPortOptions
*hubport
;
289 assert(opts
->type
== NET_CLIENT_OPTIONS_KIND_HUBPORT
);
291 hubport
= opts
->u
.hubport
;
293 net_hub_add_port(hubport
->hubid
, name
);
298 * Warn if hub configurations are likely wrong
300 void net_hub_check_clients(void)
304 NetClientState
*peer
;
306 QLIST_FOREACH(hub
, &hubs
, next
) {
307 int has_nic
= 0, has_host_dev
= 0;
309 QLIST_FOREACH(port
, &hub
->ports
, next
) {
310 peer
= port
->nc
.peer
;
312 fprintf(stderr
, "Warning: hub port %s has no peer\n",
317 switch (peer
->info
->type
) {
318 case NET_CLIENT_OPTIONS_KIND_NIC
:
321 case NET_CLIENT_OPTIONS_KIND_USER
:
322 case NET_CLIENT_OPTIONS_KIND_TAP
:
323 case NET_CLIENT_OPTIONS_KIND_SOCKET
:
324 case NET_CLIENT_OPTIONS_KIND_VDE
:
325 case NET_CLIENT_OPTIONS_KIND_VHOST_USER
:
332 if (has_host_dev
&& !has_nic
) {
333 fprintf(stderr
, "Warning: vlan %d with no nics\n", hub
->id
);
335 if (has_nic
&& !has_host_dev
) {
337 "Warning: vlan %d is not connected to host network\n",
343 bool net_hub_flush(NetClientState
*nc
)
346 NetHubPort
*source_port
= DO_UPCAST(NetHubPort
, nc
, nc
);
349 QLIST_FOREACH(port
, &source_port
->hub
->ports
, next
) {
350 if (port
!= source_port
) {
351 ret
+= qemu_net_queue_flush(port
->nc
.incoming_queue
);
354 return ret
? true : false;