blob: 522fe990a2faae9c2fff5ad18d760b41e4b2dfb0 [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"
18
19/*
20 * A hub broadcasts incoming packets to all its ports except the source port.
21 * Hubs can be used to provide independent network segments, also confusingly
22 * named the QEMU 'vlan' feature.
23 */
24
25typedef struct NetHub NetHub;
26
27typedef struct NetHubPort {
Stefan Hajnoczi4e68f7a2012-07-24 16:35:13 +010028 NetClientState nc;
Stefan Hajnoczif6c874e2012-07-24 16:35:04 +010029 QLIST_ENTRY(NetHubPort) next;
30 NetHub *hub;
31 int id;
32} NetHubPort;
33
34struct NetHub {
35 int id;
36 QLIST_ENTRY(NetHub) next;
37 int num_ports;
38 QLIST_HEAD(, NetHubPort) ports;
39};
40
41static QLIST_HEAD(, NetHub) hubs = QLIST_HEAD_INITIALIZER(&hubs);
42
43static ssize_t net_hub_receive(NetHub *hub, NetHubPort *source_port,
44 const uint8_t *buf, size_t len)
45{
46 NetHubPort *port;
47
48 QLIST_FOREACH(port, &hub->ports, next) {
49 if (port == source_port) {
50 continue;
51 }
52
53 qemu_send_packet(&port->nc, buf, len);
54 }
55 return len;
56}
57
58static ssize_t net_hub_receive_iov(NetHub *hub, NetHubPort *source_port,
59 const struct iovec *iov, int iovcnt)
60{
61 NetHubPort *port;
62 ssize_t ret = 0;
63
64 QLIST_FOREACH(port, &hub->ports, next) {
65 if (port == source_port) {
66 continue;
67 }
68
69 ret = qemu_sendv_packet(&port->nc, iov, iovcnt);
70 }
71 return ret;
72}
73
74static NetHub *net_hub_new(int id)
75{
76 NetHub *hub;
77
78 hub = g_malloc(sizeof(*hub));
79 hub->id = id;
80 hub->num_ports = 0;
81 QLIST_INIT(&hub->ports);
82
83 QLIST_INSERT_HEAD(&hubs, hub, next);
84
85 return hub;
86}
87
Stefan Hajnoczi4e68f7a2012-07-24 16:35:13 +010088static ssize_t net_hub_port_receive(NetClientState *nc,
Stefan Hajnoczif6c874e2012-07-24 16:35:04 +010089 const uint8_t *buf, size_t len)
90{
91 NetHubPort *port = DO_UPCAST(NetHubPort, nc, nc);
92
93 return net_hub_receive(port->hub, port, buf, len);
94}
95
Stefan Hajnoczi4e68f7a2012-07-24 16:35:13 +010096static ssize_t net_hub_port_receive_iov(NetClientState *nc,
Stefan Hajnoczif6c874e2012-07-24 16:35:04 +010097 const struct iovec *iov, int iovcnt)
98{
99 NetHubPort *port = DO_UPCAST(NetHubPort, nc, nc);
100
101 return net_hub_receive_iov(port->hub, port, iov, iovcnt);
102}
103
Stefan Hajnoczi4e68f7a2012-07-24 16:35:13 +0100104static void net_hub_port_cleanup(NetClientState *nc)
Stefan Hajnoczif6c874e2012-07-24 16:35:04 +0100105{
106 NetHubPort *port = DO_UPCAST(NetHubPort, nc, nc);
107
108 QLIST_REMOVE(port, next);
109}
110
111static NetClientInfo net_hub_port_info = {
112 .type = NET_CLIENT_OPTIONS_KIND_HUBPORT,
113 .size = sizeof(NetHubPort),
114 .receive = net_hub_port_receive,
115 .receive_iov = net_hub_port_receive_iov,
116 .cleanup = net_hub_port_cleanup,
117};
118
119static NetHubPort *net_hub_port_new(NetHub *hub, const char *name)
120{
Stefan Hajnoczi4e68f7a2012-07-24 16:35:13 +0100121 NetClientState *nc;
Stefan Hajnoczif6c874e2012-07-24 16:35:04 +0100122 NetHubPort *port;
123 int id = hub->num_ports++;
124 char default_name[128];
125
126 if (!name) {
127 snprintf(default_name, sizeof(default_name),
128 "hub%dport%d", hub->id, id);
129 name = default_name;
130 }
131
Stefan Hajnocziab5f3f82012-07-24 16:35:08 +0100132 nc = qemu_new_net_client(&net_hub_port_info, NULL, "hub", name);
Stefan Hajnoczif6c874e2012-07-24 16:35:04 +0100133 port = DO_UPCAST(NetHubPort, nc, nc);
134 port->id = id;
135 port->hub = hub;
136
137 QLIST_INSERT_HEAD(&hub->ports, port, next);
138
139 return port;
140}
141
142/**
143 * Create a port on a given hub
144 * @name: Net client name or NULL for default name.
145 *
146 * If there is no existing hub with the given id then a new hub is created.
147 */
Stefan Hajnoczi4e68f7a2012-07-24 16:35:13 +0100148NetClientState *net_hub_add_port(int hub_id, const char *name)
Stefan Hajnoczif6c874e2012-07-24 16:35:04 +0100149{
150 NetHub *hub;
151 NetHubPort *port;
152
153 QLIST_FOREACH(hub, &hubs, next) {
154 if (hub->id == hub_id) {
155 break;
156 }
157 }
158
159 if (!hub) {
160 hub = net_hub_new(hub_id);
161 }
162
163 port = net_hub_port_new(hub, name);
164 return &port->nc;
165}
166
167/**
Stefan Hajnoczi90d87a32012-07-24 16:35:06 +0100168 * Find a specific client on a hub
169 */
Stefan Hajnoczi4e68f7a2012-07-24 16:35:13 +0100170NetClientState *net_hub_find_client_by_name(int hub_id, const char *name)
Stefan Hajnoczi90d87a32012-07-24 16:35:06 +0100171{
172 NetHub *hub;
173 NetHubPort *port;
Stefan Hajnoczi4e68f7a2012-07-24 16:35:13 +0100174 NetClientState *peer;
Stefan Hajnoczi90d87a32012-07-24 16:35:06 +0100175
176 QLIST_FOREACH(hub, &hubs, next) {
177 if (hub->id == hub_id) {
178 QLIST_FOREACH(port, &hub->ports, next) {
179 peer = port->nc.peer;
180
181 if (peer && strcmp(peer->name, name) == 0) {
182 return peer;
183 }
184 }
185 }
186 }
187 return NULL;
188}
189
190/**
Zhi Yong Wu606c10e2012-07-24 16:35:09 +0100191 * Find a available port on a hub; otherwise create one new port
192 */
Stefan Hajnoczi4e68f7a2012-07-24 16:35:13 +0100193NetClientState *net_hub_port_find(int hub_id)
Zhi Yong Wu606c10e2012-07-24 16:35:09 +0100194{
195 NetHub *hub;
196 NetHubPort *port;
Stefan Hajnoczi4e68f7a2012-07-24 16:35:13 +0100197 NetClientState *nc;
Zhi Yong Wu606c10e2012-07-24 16:35:09 +0100198
199 QLIST_FOREACH(hub, &hubs, next) {
200 if (hub->id == hub_id) {
201 QLIST_FOREACH(port, &hub->ports, next) {
202 nc = port->nc.peer;
203 if (!nc) {
204 return &(port->nc);
205 }
206 }
207 break;
208 }
209 }
210
211 nc = net_hub_add_port(hub_id, NULL);
212 return nc;
213}
214
215/**
Stefan Hajnoczif6c874e2012-07-24 16:35:04 +0100216 * Print hub configuration
217 */
218void net_hub_info(Monitor *mon)
219{
220 NetHub *hub;
221 NetHubPort *port;
222
223 QLIST_FOREACH(hub, &hubs, next) {
224 monitor_printf(mon, "hub %d\n", hub->id);
225 QLIST_FOREACH(port, &hub->ports, next) {
Zhi Yong Wu1a859592012-07-24 16:35:16 +0100226 if (port->nc.peer) {
227 monitor_printf(mon, " \\ ");
228 print_net_client(mon, port->nc.peer);
229 }
Stefan Hajnoczif6c874e2012-07-24 16:35:04 +0100230 }
231 }
232}
233
234/**
235 * Get the hub id that a client is connected to
236 *
237 * @id Pointer for hub id output, may be NULL
238 */
Stefan Hajnoczi4e68f7a2012-07-24 16:35:13 +0100239int net_hub_id_for_client(NetClientState *nc, int *id)
Stefan Hajnoczif6c874e2012-07-24 16:35:04 +0100240{
241 NetHubPort *port;
242
243 if (nc->info->type == NET_CLIENT_OPTIONS_KIND_HUBPORT) {
244 port = DO_UPCAST(NetHubPort, nc, nc);
245 } else if (nc->peer != NULL && nc->peer->info->type ==
246 NET_CLIENT_OPTIONS_KIND_HUBPORT) {
247 port = DO_UPCAST(NetHubPort, nc, nc->peer);
248 } else {
249 return -ENOENT;
250 }
251
252 if (id) {
253 *id = port->hub->id;
254 }
255 return 0;
256}
257
258int net_init_hubport(const NetClientOptions *opts, const char *name,
Stefan Hajnoczi4e68f7a2012-07-24 16:35:13 +0100259 NetClientState *peer)
Stefan Hajnoczif6c874e2012-07-24 16:35:04 +0100260{
261 const NetdevHubPortOptions *hubport;
262
263 assert(opts->kind == NET_CLIENT_OPTIONS_KIND_HUBPORT);
264 hubport = opts->hubport;
265
Stefan Hajnoczid33d93b2012-07-24 16:35:05 +0100266 /* Treat hub port like a backend, NIC must be the one to peer */
267 if (peer) {
Stefan Hajnoczif6c874e2012-07-24 16:35:04 +0100268 return -EINVAL;
269 }
270
271 net_hub_add_port(hubport->hubid, name);
272 return 0;
273}
Stefan Hajnoczi81017642012-07-24 16:35:07 +0100274
275/**
276 * Warn if hub configurations are likely wrong
277 */
278void net_hub_check_clients(void)
279{
280 NetHub *hub;
281 NetHubPort *port;
Stefan Hajnoczi4e68f7a2012-07-24 16:35:13 +0100282 NetClientState *peer;
Stefan Hajnoczi81017642012-07-24 16:35:07 +0100283
284 QLIST_FOREACH(hub, &hubs, next) {
285 int has_nic = 0, has_host_dev = 0;
286
287 QLIST_FOREACH(port, &hub->ports, next) {
288 peer = port->nc.peer;
289 if (!peer) {
290 fprintf(stderr, "Warning: hub port %s has no peer\n",
291 port->nc.name);
292 continue;
293 }
294
295 switch (peer->info->type) {
296 case NET_CLIENT_OPTIONS_KIND_NIC:
297 has_nic = 1;
298 break;
299 case NET_CLIENT_OPTIONS_KIND_USER:
300 case NET_CLIENT_OPTIONS_KIND_TAP:
301 case NET_CLIENT_OPTIONS_KIND_SOCKET:
302 case NET_CLIENT_OPTIONS_KIND_VDE:
303 has_host_dev = 1;
304 break;
305 default:
306 break;
307 }
308 }
309 if (has_host_dev && !has_nic) {
310 fprintf(stderr, "Warning: vlan %d with no nics\n", hub->id);
311 }
312 if (has_nic && !has_host_dev) {
313 fprintf(stderr,
314 "Warning: vlan %d is not connected to host network\n",
315 hub->id);
316 }
317 }
318}