]> Git Repo - qemu.git/blob - net/hub.c
Merge remote-tracking branch 'kwolf/for-anthony' into staging
[qemu.git] / net / hub.c
1 /*
2  * Hub net client
3  *
4  * Copyright IBM, Corp. 2012
5  *
6  * Authors:
7  *  Stefan Hajnoczi   <[email protected]>
8  *  Zhi Yong Wu       <[email protected]>
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 #include "iov.h"
19
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
26 typedef struct NetHub NetHub;
27
28 typedef struct NetHubPort {
29     NetClientState nc;
30     QLIST_ENTRY(NetHubPort) next;
31     NetHub *hub;
32     int id;
33 } NetHubPort;
34
35 struct NetHub {
36     int id;
37     QLIST_ENTRY(NetHub) next;
38     int num_ports;
39     QLIST_HEAD(, NetHubPort) ports;
40 };
41
42 static QLIST_HEAD(, NetHub) hubs = QLIST_HEAD_INITIALIZER(&hubs);
43
44 static 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
59 static ssize_t net_hub_receive_iov(NetHub *hub, NetHubPort *source_port,
60                                    const struct iovec *iov, int iovcnt)
61 {
62     NetHubPort *port;
63     ssize_t len = iov_size(iov, iovcnt);
64
65     QLIST_FOREACH(port, &hub->ports, next) {
66         if (port == source_port) {
67             continue;
68         }
69
70         qemu_sendv_packet(&port->nc, iov, iovcnt);
71     }
72     return len;
73 }
74
75 static 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
89 static 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
100         if (qemu_can_send_packet(&port->nc)) {
101             return 1;
102         }
103     }
104
105     return 0;
106 }
107
108 static ssize_t net_hub_port_receive(NetClientState *nc,
109                                     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
116 static ssize_t net_hub_port_receive_iov(NetClientState *nc,
117                                         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
124 static void net_hub_port_cleanup(NetClientState *nc)
125 {
126     NetHubPort *port = DO_UPCAST(NetHubPort, nc, nc);
127
128     QLIST_REMOVE(port, next);
129 }
130
131 static NetClientInfo net_hub_port_info = {
132     .type = NET_CLIENT_OPTIONS_KIND_HUBPORT,
133     .size = sizeof(NetHubPort),
134     .can_receive = net_hub_port_can_receive,
135     .receive = net_hub_port_receive,
136     .receive_iov = net_hub_port_receive_iov,
137     .cleanup = net_hub_port_cleanup,
138 };
139
140 static NetHubPort *net_hub_port_new(NetHub *hub, const char *name)
141 {
142     NetClientState *nc;
143     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
153     nc = qemu_new_net_client(&net_hub_port_info, NULL, "hub", name);
154     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  */
169 NetClientState *net_hub_add_port(int hub_id, const char *name)
170 {
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 /**
189  * Find a specific client on a hub
190  */
191 NetClientState *net_hub_find_client_by_name(int hub_id, const char *name)
192 {
193     NetHub *hub;
194     NetHubPort *port;
195     NetClientState *peer;
196
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 /**
212  * Find a available port on a hub; otherwise create one new port
213  */
214 NetClientState *net_hub_port_find(int hub_id)
215 {
216     NetHub *hub;
217     NetHubPort *port;
218     NetClientState *nc;
219
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 /**
237  * Print hub configuration
238  */
239 void 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) {
247             if (port->nc.peer) {
248                 monitor_printf(mon, " \\ ");
249                 print_net_client(mon, port->nc.peer);
250             }
251         }
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  */
260 int net_hub_id_for_client(NetClientState *nc, int *id)
261 {
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
279 int net_init_hubport(const NetClientOptions *opts, const char *name,
280                      NetClientState *peer)
281 {
282     const NetdevHubPortOptions *hubport;
283
284     assert(opts->kind == NET_CLIENT_OPTIONS_KIND_HUBPORT);
285     hubport = opts->hubport;
286
287     /* Treat hub port like a backend, NIC must be the one to peer */
288     if (peer) {
289         return -EINVAL;
290     }
291
292     net_hub_add_port(hubport->hubid, name);
293     return 0;
294 }
295
296 /**
297  * Warn if hub configurations are likely wrong
298  */
299 void net_hub_check_clients(void)
300 {
301     NetHub *hub;
302     NetHubPort *port;
303     NetClientState *peer;
304
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 }
This page took 0.070583 seconds and 4 git commands to generate.