blob: 33a99c99ef1eac9baf5f0f6d65f62d62bcff5c5b [file] [log] [blame]
Stefan Hajnoczif6c874e2012-07-24 16:35:04 +01001/*
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
Paolo Bonzini83c90892012-12-17 18:19:49 +010015#include "monitor/monitor.h"
Paolo Bonzini1422e322012-10-24 08:43:34 +020016#include "net/net.h"
Paolo Bonzinia245fc12012-09-17 18:43:51 +020017#include "clients.h"
Stefan Hajnoczif6c874e2012-07-24 16:35:04 +010018#include "hub.h"
Paolo Bonzini1de7afc2012-12-17 18:20:00 +010019#include "qemu/iov.h"
Stefan Hajnoczif6c874e2012-07-24 16:35:04 +010020
21/*
22 * A hub broadcasts incoming packets to all its ports except the source port.
23 * Hubs can be used to provide independent network segments, also confusingly
24 * named the QEMU 'vlan' feature.
25 */
26
27typedef struct NetHub NetHub;
28
29typedef struct NetHubPort {
Stefan Hajnoczi4e68f7a2012-07-24 16:35:13 +010030 NetClientState nc;
Stefan Hajnoczif6c874e2012-07-24 16:35:04 +010031 QLIST_ENTRY(NetHubPort) next;
32 NetHub *hub;
33 int id;
34} NetHubPort;
35
36struct NetHub {
37 int id;
38 QLIST_ENTRY(NetHub) next;
39 int num_ports;
40 QLIST_HEAD(, NetHubPort) ports;
41};
42
43static QLIST_HEAD(, NetHub) hubs = QLIST_HEAD_INITIALIZER(&hubs);
44
45static ssize_t net_hub_receive(NetHub *hub, NetHubPort *source_port,
46 const uint8_t *buf, size_t len)
47{
48 NetHubPort *port;
49
50 QLIST_FOREACH(port, &hub->ports, next) {
51 if (port == source_port) {
52 continue;
53 }
54
55 qemu_send_packet(&port->nc, buf, len);
56 }
57 return len;
58}
59
60static ssize_t net_hub_receive_iov(NetHub *hub, NetHubPort *source_port,
61 const struct iovec *iov, int iovcnt)
62{
63 NetHubPort *port;
Zhi Yong Wu52a3cb82012-07-24 16:35:19 +010064 ssize_t len = iov_size(iov, iovcnt);
Stefan Hajnoczif6c874e2012-07-24 16:35:04 +010065
66 QLIST_FOREACH(port, &hub->ports, next) {
67 if (port == source_port) {
68 continue;
69 }
70
Zhi Yong Wu52a3cb82012-07-24 16:35:19 +010071 qemu_sendv_packet(&port->nc, iov, iovcnt);
Stefan Hajnoczif6c874e2012-07-24 16:35:04 +010072 }
Zhi Yong Wu52a3cb82012-07-24 16:35:19 +010073 return len;
Stefan Hajnoczif6c874e2012-07-24 16:35:04 +010074}
75
76static NetHub *net_hub_new(int id)
77{
78 NetHub *hub;
79
80 hub = g_malloc(sizeof(*hub));
81 hub->id = id;
82 hub->num_ports = 0;
83 QLIST_INIT(&hub->ports);
84
85 QLIST_INSERT_HEAD(&hubs, hub, next);
86
87 return hub;
88}
89
Zhi Yong Wu52a3cb82012-07-24 16:35:19 +010090static int net_hub_port_can_receive(NetClientState *nc)
91{
92 NetHubPort *port;
93 NetHubPort *src_port = DO_UPCAST(NetHubPort, nc, nc);
94 NetHub *hub = src_port->hub;
95
96 QLIST_FOREACH(port, &hub->ports, next) {
97 if (port == src_port) {
98 continue;
99 }
100
Stefan Hajnoczi61518a72012-08-24 13:50:30 +0100101 if (qemu_can_send_packet(&port->nc)) {
102 return 1;
Zhi Yong Wu52a3cb82012-07-24 16:35:19 +0100103 }
104 }
105
Stefan Hajnoczi61518a72012-08-24 13:50:30 +0100106 return 0;
Zhi Yong Wu52a3cb82012-07-24 16:35:19 +0100107}
108
Stefan Hajnoczi4e68f7a2012-07-24 16:35:13 +0100109static ssize_t net_hub_port_receive(NetClientState *nc,
Stefan Hajnoczif6c874e2012-07-24 16:35:04 +0100110 const uint8_t *buf, size_t len)
111{
112 NetHubPort *port = DO_UPCAST(NetHubPort, nc, nc);
113
114 return net_hub_receive(port->hub, port, buf, len);
115}
116
Stefan Hajnoczi4e68f7a2012-07-24 16:35:13 +0100117static ssize_t net_hub_port_receive_iov(NetClientState *nc,
Stefan Hajnoczif6c874e2012-07-24 16:35:04 +0100118 const struct iovec *iov, int iovcnt)
119{
120 NetHubPort *port = DO_UPCAST(NetHubPort, nc, nc);
121
122 return net_hub_receive_iov(port->hub, port, iov, iovcnt);
123}
124
Stefan Hajnoczi4e68f7a2012-07-24 16:35:13 +0100125static void net_hub_port_cleanup(NetClientState *nc)
Stefan Hajnoczif6c874e2012-07-24 16:35:04 +0100126{
127 NetHubPort *port = DO_UPCAST(NetHubPort, nc, nc);
128
129 QLIST_REMOVE(port, next);
130}
131
132static NetClientInfo net_hub_port_info = {
133 .type = NET_CLIENT_OPTIONS_KIND_HUBPORT,
134 .size = sizeof(NetHubPort),
Zhi Yong Wu52a3cb82012-07-24 16:35:19 +0100135 .can_receive = net_hub_port_can_receive,
Stefan Hajnoczif6c874e2012-07-24 16:35:04 +0100136 .receive = net_hub_port_receive,
137 .receive_iov = net_hub_port_receive_iov,
138 .cleanup = net_hub_port_cleanup,
139};
140
141static NetHubPort *net_hub_port_new(NetHub *hub, const char *name)
142{
Stefan Hajnoczi4e68f7a2012-07-24 16:35:13 +0100143 NetClientState *nc;
Stefan Hajnoczif6c874e2012-07-24 16:35:04 +0100144 NetHubPort *port;
145 int id = hub->num_ports++;
146 char default_name[128];
147
148 if (!name) {
149 snprintf(default_name, sizeof(default_name),
150 "hub%dport%d", hub->id, id);
151 name = default_name;
152 }
153
Stefan Hajnocziab5f3f82012-07-24 16:35:08 +0100154 nc = qemu_new_net_client(&net_hub_port_info, NULL, "hub", name);
Stefan Hajnoczif6c874e2012-07-24 16:35:04 +0100155 port = DO_UPCAST(NetHubPort, nc, nc);
156 port->id = id;
157 port->hub = hub;
158
159 QLIST_INSERT_HEAD(&hub->ports, port, next);
160
161 return port;
162}
163
164/**
165 * Create a port on a given hub
166 * @name: Net client name or NULL for default name.
167 *
168 * If there is no existing hub with the given id then a new hub is created.
169 */
Stefan Hajnoczi4e68f7a2012-07-24 16:35:13 +0100170NetClientState *net_hub_add_port(int hub_id, const char *name)
Stefan Hajnoczif6c874e2012-07-24 16:35:04 +0100171{
172 NetHub *hub;
173 NetHubPort *port;
174
175 QLIST_FOREACH(hub, &hubs, next) {
176 if (hub->id == hub_id) {
177 break;
178 }
179 }
180
181 if (!hub) {
182 hub = net_hub_new(hub_id);
183 }
184
185 port = net_hub_port_new(hub, name);
186 return &port->nc;
187}
188
189/**
Stefan Hajnoczi90d87a32012-07-24 16:35:06 +0100190 * Find a specific client on a hub
191 */
Stefan Hajnoczi4e68f7a2012-07-24 16:35:13 +0100192NetClientState *net_hub_find_client_by_name(int hub_id, const char *name)
Stefan Hajnoczi90d87a32012-07-24 16:35:06 +0100193{
194 NetHub *hub;
195 NetHubPort *port;
Stefan Hajnoczi4e68f7a2012-07-24 16:35:13 +0100196 NetClientState *peer;
Stefan Hajnoczi90d87a32012-07-24 16:35:06 +0100197
198 QLIST_FOREACH(hub, &hubs, next) {
199 if (hub->id == hub_id) {
200 QLIST_FOREACH(port, &hub->ports, next) {
201 peer = port->nc.peer;
202
203 if (peer && strcmp(peer->name, name) == 0) {
204 return peer;
205 }
206 }
207 }
208 }
209 return NULL;
210}
211
212/**
Zhi Yong Wu606c10e2012-07-24 16:35:09 +0100213 * Find a available port on a hub; otherwise create one new port
214 */
Stefan Hajnoczi4e68f7a2012-07-24 16:35:13 +0100215NetClientState *net_hub_port_find(int hub_id)
Zhi Yong Wu606c10e2012-07-24 16:35:09 +0100216{
217 NetHub *hub;
218 NetHubPort *port;
Stefan Hajnoczi4e68f7a2012-07-24 16:35:13 +0100219 NetClientState *nc;
Zhi Yong Wu606c10e2012-07-24 16:35:09 +0100220
221 QLIST_FOREACH(hub, &hubs, next) {
222 if (hub->id == hub_id) {
223 QLIST_FOREACH(port, &hub->ports, next) {
224 nc = port->nc.peer;
225 if (!nc) {
226 return &(port->nc);
227 }
228 }
229 break;
230 }
231 }
232
233 nc = net_hub_add_port(hub_id, NULL);
234 return nc;
235}
236
237/**
Stefan Hajnoczif6c874e2012-07-24 16:35:04 +0100238 * Print hub configuration
239 */
240void net_hub_info(Monitor *mon)
241{
242 NetHub *hub;
243 NetHubPort *port;
244
245 QLIST_FOREACH(hub, &hubs, next) {
246 monitor_printf(mon, "hub %d\n", hub->id);
247 QLIST_FOREACH(port, &hub->ports, next) {
Zhi Yong Wu1a859592012-07-24 16:35:16 +0100248 if (port->nc.peer) {
249 monitor_printf(mon, " \\ ");
250 print_net_client(mon, port->nc.peer);
251 }
Stefan Hajnoczif6c874e2012-07-24 16:35:04 +0100252 }
253 }
254}
255
256/**
257 * Get the hub id that a client is connected to
258 *
Zhi Yong Wue1031292012-12-07 09:43:18 +0800259 * @id: Pointer for hub id output, may be NULL
Stefan Hajnoczif6c874e2012-07-24 16:35:04 +0100260 */
Stefan Hajnoczi4e68f7a2012-07-24 16:35:13 +0100261int net_hub_id_for_client(NetClientState *nc, int *id)
Stefan Hajnoczif6c874e2012-07-24 16:35:04 +0100262{
263 NetHubPort *port;
264
265 if (nc->info->type == NET_CLIENT_OPTIONS_KIND_HUBPORT) {
266 port = DO_UPCAST(NetHubPort, nc, nc);
267 } else if (nc->peer != NULL && nc->peer->info->type ==
268 NET_CLIENT_OPTIONS_KIND_HUBPORT) {
269 port = DO_UPCAST(NetHubPort, nc, nc->peer);
270 } else {
271 return -ENOENT;
272 }
273
274 if (id) {
275 *id = port->hub->id;
276 }
277 return 0;
278}
279
280int net_init_hubport(const NetClientOptions *opts, const char *name,
Stefan Hajnoczi4e68f7a2012-07-24 16:35:13 +0100281 NetClientState *peer)
Stefan Hajnoczif6c874e2012-07-24 16:35:04 +0100282{
283 const NetdevHubPortOptions *hubport;
284
285 assert(opts->kind == NET_CLIENT_OPTIONS_KIND_HUBPORT);
286 hubport = opts->hubport;
287
Stefan Hajnoczid33d93b2012-07-24 16:35:05 +0100288 /* Treat hub port like a backend, NIC must be the one to peer */
289 if (peer) {
Stefan Hajnoczif6c874e2012-07-24 16:35:04 +0100290 return -EINVAL;
291 }
292
293 net_hub_add_port(hubport->hubid, name);
294 return 0;
295}
Stefan Hajnoczi81017642012-07-24 16:35:07 +0100296
297/**
298 * Warn if hub configurations are likely wrong
299 */
300void net_hub_check_clients(void)
301{
302 NetHub *hub;
303 NetHubPort *port;
Stefan Hajnoczi4e68f7a2012-07-24 16:35:13 +0100304 NetClientState *peer;
Stefan Hajnoczi81017642012-07-24 16:35:07 +0100305
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_OPTIONS_KIND_NIC:
319 has_nic = 1;
320 break;
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 has_host_dev = 1;
326 break;
327 default:
328 break;
329 }
330 }
331 if (has_host_dev && !has_nic) {
332 fprintf(stderr, "Warning: vlan %d with no nics\n", hub->id);
333 }
334 if (has_nic && !has_host_dev) {
335 fprintf(stderr,
336 "Warning: vlan %d is not connected to host network\n",
337 hub->id);
338 }
339 }
340}
Luigi Rizzo199ee602013-02-05 17:53:31 +0100341
342bool net_hub_flush(NetClientState *nc)
343{
344 NetHubPort *port;
345 NetHubPort *source_port = DO_UPCAST(NetHubPort, nc, nc);
346 int ret = 0;
347
348 QLIST_FOREACH(port, &source_port->hub->ports, next) {
349 if (port != source_port) {
Jan Kiszka067404b2013-08-02 21:47:08 +0200350 ret += qemu_net_queue_flush(port->nc.incoming_queue);
Luigi Rizzo199ee602013-02-05 17:53:31 +0100351 }
352 }
353 return ret ? true : false;
354}