xref: /qemu/net/hub.c (revision 81017645)
1f6c874e3SStefan Hajnoczi /*
2f6c874e3SStefan Hajnoczi  * Hub net client
3f6c874e3SStefan Hajnoczi  *
4f6c874e3SStefan Hajnoczi  * Copyright IBM, Corp. 2012
5f6c874e3SStefan Hajnoczi  *
6f6c874e3SStefan Hajnoczi  * Authors:
7f6c874e3SStefan Hajnoczi  *  Stefan Hajnoczi   <stefanha@linux.vnet.ibm.com>
8f6c874e3SStefan Hajnoczi  *  Zhi Yong Wu       <wuzhy@linux.vnet.ibm.com>
9f6c874e3SStefan Hajnoczi  *
10f6c874e3SStefan Hajnoczi  * This work is licensed under the terms of the GNU LGPL, version 2 or later.
11f6c874e3SStefan Hajnoczi  * See the COPYING.LIB file in the top-level directory.
12f6c874e3SStefan Hajnoczi  *
13f6c874e3SStefan Hajnoczi  */
14f6c874e3SStefan Hajnoczi 
15f6c874e3SStefan Hajnoczi #include "monitor.h"
16f6c874e3SStefan Hajnoczi #include "net.h"
17f6c874e3SStefan Hajnoczi #include "hub.h"
18f6c874e3SStefan Hajnoczi 
19f6c874e3SStefan Hajnoczi /*
20f6c874e3SStefan Hajnoczi  * A hub broadcasts incoming packets to all its ports except the source port.
21f6c874e3SStefan Hajnoczi  * Hubs can be used to provide independent network segments, also confusingly
22f6c874e3SStefan Hajnoczi  * named the QEMU 'vlan' feature.
23f6c874e3SStefan Hajnoczi  */
24f6c874e3SStefan Hajnoczi 
25f6c874e3SStefan Hajnoczi typedef struct NetHub NetHub;
26f6c874e3SStefan Hajnoczi 
27f6c874e3SStefan Hajnoczi typedef struct NetHubPort {
28f6c874e3SStefan Hajnoczi     VLANClientState nc;
29f6c874e3SStefan Hajnoczi     QLIST_ENTRY(NetHubPort) next;
30f6c874e3SStefan Hajnoczi     NetHub *hub;
31f6c874e3SStefan Hajnoczi     int id;
32f6c874e3SStefan Hajnoczi } NetHubPort;
33f6c874e3SStefan Hajnoczi 
34f6c874e3SStefan Hajnoczi struct NetHub {
35f6c874e3SStefan Hajnoczi     int id;
36f6c874e3SStefan Hajnoczi     QLIST_ENTRY(NetHub) next;
37f6c874e3SStefan Hajnoczi     int num_ports;
38f6c874e3SStefan Hajnoczi     QLIST_HEAD(, NetHubPort) ports;
39f6c874e3SStefan Hajnoczi };
40f6c874e3SStefan Hajnoczi 
41f6c874e3SStefan Hajnoczi static QLIST_HEAD(, NetHub) hubs = QLIST_HEAD_INITIALIZER(&hubs);
42f6c874e3SStefan Hajnoczi 
43f6c874e3SStefan Hajnoczi static ssize_t net_hub_receive(NetHub *hub, NetHubPort *source_port,
44f6c874e3SStefan Hajnoczi                                const uint8_t *buf, size_t len)
45f6c874e3SStefan Hajnoczi {
46f6c874e3SStefan Hajnoczi     NetHubPort *port;
47f6c874e3SStefan Hajnoczi 
48f6c874e3SStefan Hajnoczi     QLIST_FOREACH(port, &hub->ports, next) {
49f6c874e3SStefan Hajnoczi         if (port == source_port) {
50f6c874e3SStefan Hajnoczi             continue;
51f6c874e3SStefan Hajnoczi         }
52f6c874e3SStefan Hajnoczi 
53f6c874e3SStefan Hajnoczi         qemu_send_packet(&port->nc, buf, len);
54f6c874e3SStefan Hajnoczi     }
55f6c874e3SStefan Hajnoczi     return len;
56f6c874e3SStefan Hajnoczi }
57f6c874e3SStefan Hajnoczi 
58f6c874e3SStefan Hajnoczi static ssize_t net_hub_receive_iov(NetHub *hub, NetHubPort *source_port,
59f6c874e3SStefan Hajnoczi                                    const struct iovec *iov, int iovcnt)
60f6c874e3SStefan Hajnoczi {
61f6c874e3SStefan Hajnoczi     NetHubPort *port;
62f6c874e3SStefan Hajnoczi     ssize_t ret = 0;
63f6c874e3SStefan Hajnoczi 
64f6c874e3SStefan Hajnoczi     QLIST_FOREACH(port, &hub->ports, next) {
65f6c874e3SStefan Hajnoczi         if (port == source_port) {
66f6c874e3SStefan Hajnoczi             continue;
67f6c874e3SStefan Hajnoczi         }
68f6c874e3SStefan Hajnoczi 
69f6c874e3SStefan Hajnoczi         ret = qemu_sendv_packet(&port->nc, iov, iovcnt);
70f6c874e3SStefan Hajnoczi     }
71f6c874e3SStefan Hajnoczi     return ret;
72f6c874e3SStefan Hajnoczi }
73f6c874e3SStefan Hajnoczi 
74f6c874e3SStefan Hajnoczi static NetHub *net_hub_new(int id)
75f6c874e3SStefan Hajnoczi {
76f6c874e3SStefan Hajnoczi     NetHub *hub;
77f6c874e3SStefan Hajnoczi 
78f6c874e3SStefan Hajnoczi     hub = g_malloc(sizeof(*hub));
79f6c874e3SStefan Hajnoczi     hub->id = id;
80f6c874e3SStefan Hajnoczi     hub->num_ports = 0;
81f6c874e3SStefan Hajnoczi     QLIST_INIT(&hub->ports);
82f6c874e3SStefan Hajnoczi 
83f6c874e3SStefan Hajnoczi     QLIST_INSERT_HEAD(&hubs, hub, next);
84f6c874e3SStefan Hajnoczi 
85f6c874e3SStefan Hajnoczi     return hub;
86f6c874e3SStefan Hajnoczi }
87f6c874e3SStefan Hajnoczi 
88f6c874e3SStefan Hajnoczi static ssize_t net_hub_port_receive(VLANClientState *nc,
89f6c874e3SStefan Hajnoczi                                     const uint8_t *buf, size_t len)
90f6c874e3SStefan Hajnoczi {
91f6c874e3SStefan Hajnoczi     NetHubPort *port = DO_UPCAST(NetHubPort, nc, nc);
92f6c874e3SStefan Hajnoczi 
93f6c874e3SStefan Hajnoczi     return net_hub_receive(port->hub, port, buf, len);
94f6c874e3SStefan Hajnoczi }
95f6c874e3SStefan Hajnoczi 
96f6c874e3SStefan Hajnoczi static ssize_t net_hub_port_receive_iov(VLANClientState *nc,
97f6c874e3SStefan Hajnoczi                                         const struct iovec *iov, int iovcnt)
98f6c874e3SStefan Hajnoczi {
99f6c874e3SStefan Hajnoczi     NetHubPort *port = DO_UPCAST(NetHubPort, nc, nc);
100f6c874e3SStefan Hajnoczi 
101f6c874e3SStefan Hajnoczi     return net_hub_receive_iov(port->hub, port, iov, iovcnt);
102f6c874e3SStefan Hajnoczi }
103f6c874e3SStefan Hajnoczi 
104f6c874e3SStefan Hajnoczi static void net_hub_port_cleanup(VLANClientState *nc)
105f6c874e3SStefan Hajnoczi {
106f6c874e3SStefan Hajnoczi     NetHubPort *port = DO_UPCAST(NetHubPort, nc, nc);
107f6c874e3SStefan Hajnoczi 
108f6c874e3SStefan Hajnoczi     QLIST_REMOVE(port, next);
109f6c874e3SStefan Hajnoczi }
110f6c874e3SStefan Hajnoczi 
111f6c874e3SStefan Hajnoczi static NetClientInfo net_hub_port_info = {
112f6c874e3SStefan Hajnoczi     .type = NET_CLIENT_OPTIONS_KIND_HUBPORT,
113f6c874e3SStefan Hajnoczi     .size = sizeof(NetHubPort),
114f6c874e3SStefan Hajnoczi     .receive = net_hub_port_receive,
115f6c874e3SStefan Hajnoczi     .receive_iov = net_hub_port_receive_iov,
116f6c874e3SStefan Hajnoczi     .cleanup = net_hub_port_cleanup,
117f6c874e3SStefan Hajnoczi };
118f6c874e3SStefan Hajnoczi 
119f6c874e3SStefan Hajnoczi static NetHubPort *net_hub_port_new(NetHub *hub, const char *name)
120f6c874e3SStefan Hajnoczi {
121f6c874e3SStefan Hajnoczi     VLANClientState *nc;
122f6c874e3SStefan Hajnoczi     NetHubPort *port;
123f6c874e3SStefan Hajnoczi     int id = hub->num_ports++;
124f6c874e3SStefan Hajnoczi     char default_name[128];
125f6c874e3SStefan Hajnoczi 
126f6c874e3SStefan Hajnoczi     if (!name) {
127f6c874e3SStefan Hajnoczi         snprintf(default_name, sizeof(default_name),
128f6c874e3SStefan Hajnoczi                  "hub%dport%d", hub->id, id);
129f6c874e3SStefan Hajnoczi         name = default_name;
130f6c874e3SStefan Hajnoczi     }
131f6c874e3SStefan Hajnoczi 
132f6c874e3SStefan Hajnoczi     nc = qemu_new_net_client(&net_hub_port_info, NULL, NULL, "hub", name);
133f6c874e3SStefan Hajnoczi     port = DO_UPCAST(NetHubPort, nc, nc);
134f6c874e3SStefan Hajnoczi     port->id = id;
135f6c874e3SStefan Hajnoczi     port->hub = hub;
136f6c874e3SStefan Hajnoczi 
137f6c874e3SStefan Hajnoczi     QLIST_INSERT_HEAD(&hub->ports, port, next);
138f6c874e3SStefan Hajnoczi 
139f6c874e3SStefan Hajnoczi     return port;
140f6c874e3SStefan Hajnoczi }
141f6c874e3SStefan Hajnoczi 
142f6c874e3SStefan Hajnoczi /**
143f6c874e3SStefan Hajnoczi  * Create a port on a given hub
144f6c874e3SStefan Hajnoczi  * @name: Net client name or NULL for default name.
145f6c874e3SStefan Hajnoczi  *
146f6c874e3SStefan Hajnoczi  * If there is no existing hub with the given id then a new hub is created.
147f6c874e3SStefan Hajnoczi  */
148f6c874e3SStefan Hajnoczi VLANClientState *net_hub_add_port(int hub_id, const char *name)
149f6c874e3SStefan Hajnoczi {
150f6c874e3SStefan Hajnoczi     NetHub *hub;
151f6c874e3SStefan Hajnoczi     NetHubPort *port;
152f6c874e3SStefan Hajnoczi 
153f6c874e3SStefan Hajnoczi     QLIST_FOREACH(hub, &hubs, next) {
154f6c874e3SStefan Hajnoczi         if (hub->id == hub_id) {
155f6c874e3SStefan Hajnoczi             break;
156f6c874e3SStefan Hajnoczi         }
157f6c874e3SStefan Hajnoczi     }
158f6c874e3SStefan Hajnoczi 
159f6c874e3SStefan Hajnoczi     if (!hub) {
160f6c874e3SStefan Hajnoczi         hub = net_hub_new(hub_id);
161f6c874e3SStefan Hajnoczi     }
162f6c874e3SStefan Hajnoczi 
163f6c874e3SStefan Hajnoczi     port = net_hub_port_new(hub, name);
164f6c874e3SStefan Hajnoczi     return &port->nc;
165f6c874e3SStefan Hajnoczi }
166f6c874e3SStefan Hajnoczi 
167f6c874e3SStefan Hajnoczi /**
16890d87a33SStefan Hajnoczi  * Find a specific client on a hub
16990d87a33SStefan Hajnoczi  */
17090d87a33SStefan Hajnoczi VLANClientState *net_hub_find_client_by_name(int hub_id, const char *name)
17190d87a33SStefan Hajnoczi {
17290d87a33SStefan Hajnoczi     NetHub *hub;
17390d87a33SStefan Hajnoczi     NetHubPort *port;
17490d87a33SStefan Hajnoczi     VLANClientState *peer;
17590d87a33SStefan Hajnoczi 
17690d87a33SStefan Hajnoczi     QLIST_FOREACH(hub, &hubs, next) {
17790d87a33SStefan Hajnoczi         if (hub->id == hub_id) {
17890d87a33SStefan Hajnoczi             QLIST_FOREACH(port, &hub->ports, next) {
17990d87a33SStefan Hajnoczi                 peer = port->nc.peer;
18090d87a33SStefan Hajnoczi 
18190d87a33SStefan Hajnoczi                 if (peer && strcmp(peer->name, name) == 0) {
18290d87a33SStefan Hajnoczi                     return peer;
18390d87a33SStefan Hajnoczi                 }
18490d87a33SStefan Hajnoczi             }
18590d87a33SStefan Hajnoczi         }
18690d87a33SStefan Hajnoczi     }
18790d87a33SStefan Hajnoczi     return NULL;
18890d87a33SStefan Hajnoczi }
18990d87a33SStefan Hajnoczi 
19090d87a33SStefan Hajnoczi /**
191f6c874e3SStefan Hajnoczi  * Print hub configuration
192f6c874e3SStefan Hajnoczi  */
193f6c874e3SStefan Hajnoczi void net_hub_info(Monitor *mon)
194f6c874e3SStefan Hajnoczi {
195f6c874e3SStefan Hajnoczi     NetHub *hub;
196f6c874e3SStefan Hajnoczi     NetHubPort *port;
197f6c874e3SStefan Hajnoczi 
198f6c874e3SStefan Hajnoczi     QLIST_FOREACH(hub, &hubs, next) {
199f6c874e3SStefan Hajnoczi         monitor_printf(mon, "hub %d\n", hub->id);
200f6c874e3SStefan Hajnoczi         QLIST_FOREACH(port, &hub->ports, next) {
201f6c874e3SStefan Hajnoczi             monitor_printf(mon, "    port %d peer %s\n", port->id,
202f6c874e3SStefan Hajnoczi                            port->nc.peer ? port->nc.peer->name : "<none>");
203f6c874e3SStefan Hajnoczi         }
204f6c874e3SStefan Hajnoczi     }
205f6c874e3SStefan Hajnoczi }
206f6c874e3SStefan Hajnoczi 
207f6c874e3SStefan Hajnoczi /**
208f6c874e3SStefan Hajnoczi  * Get the hub id that a client is connected to
209f6c874e3SStefan Hajnoczi  *
210f6c874e3SStefan Hajnoczi  * @id              Pointer for hub id output, may be NULL
211f6c874e3SStefan Hajnoczi  */
212f6c874e3SStefan Hajnoczi int net_hub_id_for_client(VLANClientState *nc, int *id)
213f6c874e3SStefan Hajnoczi {
214f6c874e3SStefan Hajnoczi     NetHubPort *port;
215f6c874e3SStefan Hajnoczi 
216f6c874e3SStefan Hajnoczi     if (nc->info->type == NET_CLIENT_OPTIONS_KIND_HUBPORT) {
217f6c874e3SStefan Hajnoczi         port = DO_UPCAST(NetHubPort, nc, nc);
218f6c874e3SStefan Hajnoczi     } else if (nc->peer != NULL && nc->peer->info->type ==
219f6c874e3SStefan Hajnoczi             NET_CLIENT_OPTIONS_KIND_HUBPORT) {
220f6c874e3SStefan Hajnoczi         port = DO_UPCAST(NetHubPort, nc, nc->peer);
221f6c874e3SStefan Hajnoczi     } else {
222f6c874e3SStefan Hajnoczi         return -ENOENT;
223f6c874e3SStefan Hajnoczi     }
224f6c874e3SStefan Hajnoczi 
225f6c874e3SStefan Hajnoczi     if (id) {
226f6c874e3SStefan Hajnoczi         *id = port->hub->id;
227f6c874e3SStefan Hajnoczi     }
228f6c874e3SStefan Hajnoczi     return 0;
229f6c874e3SStefan Hajnoczi }
230f6c874e3SStefan Hajnoczi 
231f6c874e3SStefan Hajnoczi int net_init_hubport(const NetClientOptions *opts, const char *name,
232d33d93b2SStefan Hajnoczi                      VLANClientState *peer)
233f6c874e3SStefan Hajnoczi {
234f6c874e3SStefan Hajnoczi     const NetdevHubPortOptions *hubport;
235f6c874e3SStefan Hajnoczi 
236f6c874e3SStefan Hajnoczi     assert(opts->kind == NET_CLIENT_OPTIONS_KIND_HUBPORT);
237f6c874e3SStefan Hajnoczi     hubport = opts->hubport;
238f6c874e3SStefan Hajnoczi 
239d33d93b2SStefan Hajnoczi     /* Treat hub port like a backend, NIC must be the one to peer */
240d33d93b2SStefan Hajnoczi     if (peer) {
241f6c874e3SStefan Hajnoczi         return -EINVAL;
242f6c874e3SStefan Hajnoczi     }
243f6c874e3SStefan Hajnoczi 
244f6c874e3SStefan Hajnoczi     net_hub_add_port(hubport->hubid, name);
245f6c874e3SStefan Hajnoczi     return 0;
246f6c874e3SStefan Hajnoczi }
247*81017645SStefan Hajnoczi 
248*81017645SStefan Hajnoczi /**
249*81017645SStefan Hajnoczi  * Warn if hub configurations are likely wrong
250*81017645SStefan Hajnoczi  */
251*81017645SStefan Hajnoczi void net_hub_check_clients(void)
252*81017645SStefan Hajnoczi {
253*81017645SStefan Hajnoczi     NetHub *hub;
254*81017645SStefan Hajnoczi     NetHubPort *port;
255*81017645SStefan Hajnoczi     VLANClientState *peer;
256*81017645SStefan Hajnoczi 
257*81017645SStefan Hajnoczi     QLIST_FOREACH(hub, &hubs, next) {
258*81017645SStefan Hajnoczi         int has_nic = 0, has_host_dev = 0;
259*81017645SStefan Hajnoczi 
260*81017645SStefan Hajnoczi         QLIST_FOREACH(port, &hub->ports, next) {
261*81017645SStefan Hajnoczi             peer = port->nc.peer;
262*81017645SStefan Hajnoczi             if (!peer) {
263*81017645SStefan Hajnoczi                 fprintf(stderr, "Warning: hub port %s has no peer\n",
264*81017645SStefan Hajnoczi                         port->nc.name);
265*81017645SStefan Hajnoczi                 continue;
266*81017645SStefan Hajnoczi             }
267*81017645SStefan Hajnoczi 
268*81017645SStefan Hajnoczi             switch (peer->info->type) {
269*81017645SStefan Hajnoczi             case NET_CLIENT_OPTIONS_KIND_NIC:
270*81017645SStefan Hajnoczi                 has_nic = 1;
271*81017645SStefan Hajnoczi                 break;
272*81017645SStefan Hajnoczi             case NET_CLIENT_OPTIONS_KIND_USER:
273*81017645SStefan Hajnoczi             case NET_CLIENT_OPTIONS_KIND_TAP:
274*81017645SStefan Hajnoczi             case NET_CLIENT_OPTIONS_KIND_SOCKET:
275*81017645SStefan Hajnoczi             case NET_CLIENT_OPTIONS_KIND_VDE:
276*81017645SStefan Hajnoczi                 has_host_dev = 1;
277*81017645SStefan Hajnoczi                 break;
278*81017645SStefan Hajnoczi             default:
279*81017645SStefan Hajnoczi                 break;
280*81017645SStefan Hajnoczi             }
281*81017645SStefan Hajnoczi         }
282*81017645SStefan Hajnoczi         if (has_host_dev && !has_nic) {
283*81017645SStefan Hajnoczi             fprintf(stderr, "Warning: vlan %d with no nics\n", hub->id);
284*81017645SStefan Hajnoczi         }
285*81017645SStefan Hajnoczi         if (has_nic && !has_host_dev) {
286*81017645SStefan Hajnoczi             fprintf(stderr,
287*81017645SStefan Hajnoczi                     "Warning: vlan %d is not connected to host network\n",
288*81017645SStefan Hajnoczi                     hub->id);
289*81017645SStefan Hajnoczi         }
290*81017645SStefan Hajnoczi     }
291*81017645SStefan Hajnoczi }
292