blob: 650a8b4a40188cf60b994f06e8de2980a9b6c3f3 [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
15#include "monitor.h"
16#include "net.h"
17#include "hub.h"
Zhi Yong Wu52a3cb82012-07-24 16:35:19 +010018#include "iov.h"
Stefan Hajnoczif6c874e2012-07-24 16:35:04 +010019
20/*
21 * A hub broadcasts incoming packets to all its ports except the source port.
22 * Hubs can be used to provide independent network segments, also confusingly
23 * named the QEMU 'vlan' feature.
24 */
25
26typedef struct NetHub NetHub;
27
28typedef struct NetHubPort {
Stefan Hajnoczi4e68f7a2012-07-24 16:35:13 +010029 NetClientState nc;
Stefan Hajnoczif6c874e2012-07-24 16:35:04 +010030 QLIST_ENTRY(NetHubPort) next;
31 NetHub *hub;
32 int id;
33} NetHubPort;
34
35struct NetHub {
36 int id;
37 QLIST_ENTRY(NetHub) next;
38 int num_ports;
39 QLIST_HEAD(, NetHubPort) ports;
40};
41
42static QLIST_HEAD(, NetHub) hubs = QLIST_HEAD_INITIALIZER(&hubs);
43
44static ssize_t net_hub_receive(NetHub *hub, NetHubPort *source_port,
45 const uint8_t *buf, size_t len)
46{
47 NetHubPort *port;
48
49 QLIST_FOREACH(port, &hub->ports, next) {
50 if (port == source_port) {
51 continue;
52 }
53
54 qemu_send_packet(&port->nc, buf, len);
55 }
56 return len;
57}
58
59static ssize_t net_hub_receive_iov(NetHub *hub, NetHubPort *source_port,
60 const struct iovec *iov, int iovcnt)
61{
62 NetHubPort *port;
Zhi Yong Wu52a3cb82012-07-24 16:35:19 +010063 ssize_t len = iov_size(iov, iovcnt);
Stefan Hajnoczif6c874e2012-07-24 16:35:04 +010064
65 QLIST_FOREACH(port, &hub->ports, next) {
66 if (port == source_port) {
67 continue;
68 }
69
Zhi Yong Wu52a3cb82012-07-24 16:35:19 +010070 qemu_sendv_packet(&port->nc, iov, iovcnt);
Stefan Hajnoczif6c874e2012-07-24 16:35:04 +010071 }
Zhi Yong Wu52a3cb82012-07-24 16:35:19 +010072 return len;
Stefan Hajnoczif6c874e2012-07-24 16:35:04 +010073}
74
75static NetHub *net_hub_new(int id)
76{
77 NetHub *hub;
78
79 hub = g_malloc(sizeof(*hub));
80 hub->id = id;
81 hub->num_ports = 0;
82 QLIST_INIT(&hub->ports);
83
84 QLIST_INSERT_HEAD(&hubs, hub, next);
85
86 return hub;
87}
88
Zhi Yong Wu52a3cb82012-07-24 16:35:19 +010089static int net_hub_port_can_receive(NetClientState *nc)
90{
91 NetHubPort *port;
92 NetHubPort *src_port = DO_UPCAST(NetHubPort, nc, nc);
93 NetHub *hub = src_port->hub;
94
95 QLIST_FOREACH(port, &hub->ports, next) {
96 if (port == src_port) {
97 continue;
98 }
99
Stefan Hajnoczi61518a72012-08-24 13:50:30 +0100100 if (qemu_can_send_packet(&port->nc)) {
101 return 1;
Zhi Yong Wu52a3cb82012-07-24 16:35:19 +0100102 }
103 }
104
Stefan Hajnoczi61518a72012-08-24 13:50:30 +0100105 return 0;
Zhi Yong Wu52a3cb82012-07-24 16:35:19 +0100106}
107
Stefan Hajnoczi4e68f7a2012-07-24 16:35:13 +0100108static ssize_t net_hub_port_receive(NetClientState *nc,
Stefan Hajnoczif6c874e2012-07-24 16:35:04 +0100109 const uint8_t *buf, size_t len)
110{
111 NetHubPort *port = DO_UPCAST(NetHubPort, nc, nc);
112
113 return net_hub_receive(port->hub, port, buf, len);
114}
115
Stefan Hajnoczi4e68f7a2012-07-24 16:35:13 +0100116static ssize_t net_hub_port_receive_iov(NetClientState *nc,
Stefan Hajnoczif6c874e2012-07-24 16:35:04 +0100117 const struct iovec *iov, int iovcnt)
118{
119 NetHubPort *port = DO_UPCAST(NetHubPort, nc, nc);
120
121 return net_hub_receive_iov(port->hub, port, iov, iovcnt);
122}
123
Stefan Hajnoczi4e68f7a2012-07-24 16:35:13 +0100124static void net_hub_port_cleanup(NetClientState *nc)
Stefan Hajnoczif6c874e2012-07-24 16:35:04 +0100125{
126 NetHubPort *port = DO_UPCAST(NetHubPort, nc, nc);
127
128 QLIST_REMOVE(port, next);
129}
130
131static NetClientInfo net_hub_port_info = {
132 .type = NET_CLIENT_OPTIONS_KIND_HUBPORT,
133 .size = sizeof(NetHubPort),
Zhi Yong Wu52a3cb82012-07-24 16:35:19 +0100134 .can_receive = net_hub_port_can_receive,
Stefan Hajnoczif6c874e2012-07-24 16:35:04 +0100135 .receive = net_hub_port_receive,
136 .receive_iov = net_hub_port_receive_iov,
137 .cleanup = net_hub_port_cleanup,
138};
139
140static NetHubPort *net_hub_port_new(NetHub *hub, const char *name)
141{
Stefan Hajnoczi4e68f7a2012-07-24 16:35:13 +0100142 NetClientState *nc;
Stefan Hajnoczif6c874e2012-07-24 16:35:04 +0100143 NetHubPort *port;
144 int id = hub->num_ports++;
145 char default_name[128];
146
147 if (!name) {
148 snprintf(default_name, sizeof(default_name),
149 "hub%dport%d", hub->id, id);
150 name = default_name;
151 }
152
Stefan Hajnocziab5f3f82012-07-24 16:35:08 +0100153 nc = qemu_new_net_client(&net_hub_port_info, NULL, "hub", name);
Stefan Hajnoczif6c874e2012-07-24 16:35:04 +0100154 port = DO_UPCAST(NetHubPort, nc, nc);
155 port->id = id;
156 port->hub = hub;
157
158 QLIST_INSERT_HEAD(&hub->ports, port, next);
159
160 return port;
161}
162
163/**
164 * Create a port on a given hub
165 * @name: Net client name or NULL for default name.
166 *
167 * If there is no existing hub with the given id then a new hub is created.
168 */
Stefan Hajnoczi4e68f7a2012-07-24 16:35:13 +0100169NetClientState *net_hub_add_port(int hub_id, const char *name)
Stefan Hajnoczif6c874e2012-07-24 16:35:04 +0100170{
171 NetHub *hub;
172 NetHubPort *port;
173
174 QLIST_FOREACH(hub, &hubs, next) {
175 if (hub->id == hub_id) {
176 break;
177 }
178 }
179
180 if (!hub) {
181 hub = net_hub_new(hub_id);
182 }
183
184 port = net_hub_port_new(hub, name);
185 return &port->nc;
186}
187
188/**
Stefan Hajnoczi90d87a32012-07-24 16:35:06 +0100189 * Find a specific client on a hub
190 */
Stefan Hajnoczi4e68f7a2012-07-24 16:35:13 +0100191NetClientState *net_hub_find_client_by_name(int hub_id, const char *name)
Stefan Hajnoczi90d87a32012-07-24 16:35:06 +0100192{
193 NetHub *hub;
194 NetHubPort *port;
Stefan Hajnoczi4e68f7a2012-07-24 16:35:13 +0100195 NetClientState *peer;
Stefan Hajnoczi90d87a32012-07-24 16:35:06 +0100196
197 QLIST_FOREACH(hub, &hubs, next) {
198 if (hub->id == hub_id) {
199 QLIST_FOREACH(port, &hub->ports, next) {
200 peer = port->nc.peer;
201
202 if (peer && strcmp(peer->name, name) == 0) {
203 return peer;
204 }
205 }
206 }
207 }
208 return NULL;
209}
210
211/**
Zhi Yong Wu606c10e2012-07-24 16:35:09 +0100212 * Find a available port on a hub; otherwise create one new port
213 */
Stefan Hajnoczi4e68f7a2012-07-24 16:35:13 +0100214NetClientState *net_hub_port_find(int hub_id)
Zhi Yong Wu606c10e2012-07-24 16:35:09 +0100215{
216 NetHub *hub;
217 NetHubPort *port;
Stefan Hajnoczi4e68f7a2012-07-24 16:35:13 +0100218 NetClientState *nc;
Zhi Yong Wu606c10e2012-07-24 16:35:09 +0100219
220 QLIST_FOREACH(hub, &hubs, next) {
221 if (hub->id == hub_id) {
222 QLIST_FOREACH(port, &hub->ports, next) {
223 nc = port->nc.peer;
224 if (!nc) {
225 return &(port->nc);
226 }
227 }
228 break;
229 }
230 }
231
232 nc = net_hub_add_port(hub_id, NULL);
233 return nc;
234}
235
236/**
Stefan Hajnoczif6c874e2012-07-24 16:35:04 +0100237 * Print hub configuration
238 */
239void net_hub_info(Monitor *mon)
240{
241 NetHub *hub;
242 NetHubPort *port;
243
244 QLIST_FOREACH(hub, &hubs, next) {
245 monitor_printf(mon, "hub %d\n", hub->id);
246 QLIST_FOREACH(port, &hub->ports, next) {
Zhi Yong Wu1a859592012-07-24 16:35:16 +0100247 if (port->nc.peer) {
248 monitor_printf(mon, " \\ ");
249 print_net_client(mon, port->nc.peer);
250 }
Stefan Hajnoczif6c874e2012-07-24 16:35:04 +0100251 }
252 }
253}
254
255/**
256 * Get the hub id that a client is connected to
257 *
258 * @id Pointer for hub id output, may be NULL
259 */
Stefan Hajnoczi4e68f7a2012-07-24 16:35:13 +0100260int net_hub_id_for_client(NetClientState *nc, int *id)
Stefan Hajnoczif6c874e2012-07-24 16:35:04 +0100261{
262 NetHubPort *port;
263
264 if (nc->info->type == NET_CLIENT_OPTIONS_KIND_HUBPORT) {
265 port = DO_UPCAST(NetHubPort, nc, nc);
266 } else if (nc->peer != NULL && nc->peer->info->type ==
267 NET_CLIENT_OPTIONS_KIND_HUBPORT) {
268 port = DO_UPCAST(NetHubPort, nc, nc->peer);
269 } else {
270 return -ENOENT;
271 }
272
273 if (id) {
274 *id = port->hub->id;
275 }
276 return 0;
277}
278
279int net_init_hubport(const NetClientOptions *opts, const char *name,
Stefan Hajnoczi4e68f7a2012-07-24 16:35:13 +0100280 NetClientState *peer)
Stefan Hajnoczif6c874e2012-07-24 16:35:04 +0100281{
282 const NetdevHubPortOptions *hubport;
283
284 assert(opts->kind == NET_CLIENT_OPTIONS_KIND_HUBPORT);
285 hubport = opts->hubport;
286
Stefan Hajnoczid33d93b2012-07-24 16:35:05 +0100287 /* Treat hub port like a backend, NIC must be the one to peer */
288 if (peer) {
Stefan Hajnoczif6c874e2012-07-24 16:35:04 +0100289 return -EINVAL;
290 }
291
292 net_hub_add_port(hubport->hubid, name);
293 return 0;
294}
Stefan Hajnoczi81017642012-07-24 16:35:07 +0100295
296/**
297 * Warn if hub configurations are likely wrong
298 */
299void net_hub_check_clients(void)
300{
301 NetHub *hub;
302 NetHubPort *port;
Stefan Hajnoczi4e68f7a2012-07-24 16:35:13 +0100303 NetClientState *peer;
Stefan Hajnoczi81017642012-07-24 16:35:07 +0100304
305 QLIST_FOREACH(hub, &hubs, next) {
306 int has_nic = 0, has_host_dev = 0;
307
308 QLIST_FOREACH(port, &hub->ports, next) {
309 peer = port->nc.peer;
310 if (!peer) {
311 fprintf(stderr, "Warning: hub port %s has no peer\n",
312 port->nc.name);
313 continue;
314 }
315
316 switch (peer->info->type) {
317 case NET_CLIENT_OPTIONS_KIND_NIC:
318 has_nic = 1;
319 break;
320 case NET_CLIENT_OPTIONS_KIND_USER:
321 case NET_CLIENT_OPTIONS_KIND_TAP:
322 case NET_CLIENT_OPTIONS_KIND_SOCKET:
323 case NET_CLIENT_OPTIONS_KIND_VDE:
324 has_host_dev = 1;
325 break;
326 default:
327 break;
328 }
329 }
330 if (has_host_dev && !has_nic) {
331 fprintf(stderr, "Warning: vlan %d with no nics\n", hub->id);
332 }
333 if (has_nic && !has_host_dev) {
334 fprintf(stderr,
335 "Warning: vlan %d is not connected to host network\n",
336 hub->id);
337 }
338 }
339}