1 /*
2 * Copyright (c) 2021 Intel Corporation.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/logging/log.h>
8 LOG_MODULE_REGISTER(net_virtual, CONFIG_NET_L2_VIRTUAL_LOG_LEVEL);
9
10 #include <zephyr/net/net_core.h>
11 #include <zephyr/net/net_l2.h>
12 #include <zephyr/net/net_if.h>
13 #include <zephyr/net/net_mgmt.h>
14 #include <zephyr/net/virtual.h>
15 #include <zephyr/net/virtual_mgmt.h>
16 #include <zephyr/random/random.h>
17
18 #include "net_private.h"
19 #include "net_stats.h"
20
21 #define NET_BUF_TIMEOUT K_MSEC(100)
22
virtual_recv(struct net_if * iface,struct net_pkt * pkt)23 static enum net_verdict virtual_recv(struct net_if *iface,
24 struct net_pkt *pkt)
25 {
26 struct virtual_interface_context *ctx, *tmp;
27 const struct virtual_interface_api *api;
28 struct net_if *filtered_iface = NULL;
29 enum net_verdict verdict;
30 sys_slist_t *interfaces;
31
32 interfaces = &iface->config.virtual_interfaces;
33
34 SYS_SLIST_FOR_EACH_CONTAINER_SAFE(interfaces, ctx, tmp, node) {
35 if (ctx->virtual_iface == NULL) {
36 continue;
37 }
38
39 api = net_if_get_device(ctx->virtual_iface)->api;
40 if (!api || api->recv == NULL) {
41 continue;
42 }
43
44 if (!net_if_is_up(ctx->virtual_iface)) {
45 NET_DBG("Interface %d is down.",
46 net_if_get_by_iface(ctx->virtual_iface));
47 continue;
48 }
49
50 if (IS_ENABLED(CONFIG_NET_PKT_FILTER)) {
51 struct net_if *tmp_iface;
52
53 tmp_iface = net_pkt_iface(pkt);
54 net_pkt_set_iface(pkt, ctx->virtual_iface);
55
56 if (!net_pkt_filter_recv_ok(pkt)) {
57 /* We cannot update the statistics here because
58 * the interface might not be the correct one for
59 * the packet. We would know that only after the
60 * call to api->recv() which is too late. So mark
61 * the interface as filtered and continue and
62 * update the filter statistics out of the loop.
63 */
64 net_pkt_set_iface(pkt, tmp_iface);
65 filtered_iface = tmp_iface;
66 continue;
67 }
68
69 net_pkt_set_iface(pkt, tmp_iface);
70 }
71
72 verdict = api->recv(ctx->virtual_iface, pkt);
73 if (verdict == NET_CONTINUE) {
74 continue;
75 }
76
77 if (IS_ENABLED(CONFIG_NET_STATISTICS)) {
78 size_t pkt_len;
79
80 pkt_len = net_pkt_get_len(pkt);
81
82 NET_DBG("Received pkt %p len %zu", pkt, pkt_len);
83
84 net_stats_update_bytes_recv(ctx->virtual_iface,
85 pkt_len);
86 }
87
88 if (verdict == NET_DROP) {
89 net_stats_update_processing_error(ctx->virtual_iface);
90 }
91
92 return verdict;
93 }
94
95 if (IS_ENABLED(CONFIG_NET_PKT_FILTER) && filtered_iface != NULL) {
96 /* We need to update the statistics for the filtered iface here.
97 */
98 net_stats_update_filter_rx_drop(filtered_iface);
99 goto silent_drop;
100 }
101
102 /* If there are no virtual interfaces attached, then pass the packet
103 * to the actual virtual network interface.
104 */
105 api = net_if_get_device(iface)->api;
106 if (!api || api->recv == NULL) {
107 goto drop;
108 }
109
110 if (!net_if_is_up(iface)) {
111 NET_DBG("Interface %d is down.", net_if_get_by_iface(iface));
112 goto silent_drop;
113 }
114
115 if (!net_pkt_filter_recv_ok(pkt)) {
116 net_stats_update_filter_rx_drop(iface);
117 goto silent_drop;
118 }
119
120 verdict = api->recv(iface, pkt);
121
122 if (IS_ENABLED(CONFIG_NET_STATISTICS)) {
123 size_t pkt_len;
124
125 pkt_len = net_pkt_get_len(pkt);
126
127 NET_DBG("Received pkt %p len %zu", pkt, pkt_len);
128
129 net_stats_update_bytes_recv(iface, pkt_len);
130 }
131
132 if (verdict == NET_DROP) {
133 net_stats_update_processing_error(iface);
134 }
135
136 return verdict;
137
138 drop:
139 NET_DBG("No handler, dropping pkt %p len %zu", pkt, net_pkt_get_len(pkt));
140
141 silent_drop:
142 return NET_DROP;
143 }
144
virtual_send(struct net_if * iface,struct net_pkt * pkt)145 static int virtual_send(struct net_if *iface, struct net_pkt *pkt)
146 {
147 const struct virtual_interface_api *api = net_if_get_device(iface)->api;
148 size_t pkt_len;
149 int ret;
150
151 if (!api) {
152 return -ENOENT;
153 }
154
155 if (!net_if_is_up(iface)) {
156 NET_DBG("Interface %d is down.",
157 net_if_get_by_iface(iface));
158 return -ENETDOWN;
159 }
160
161 if (IS_ENABLED(CONFIG_NET_STATISTICS)) {
162 pkt_len = net_pkt_get_len(pkt);
163 }
164
165 /* As we are just passing data through, the net_pkt is not freed here.
166 */
167 ret = api->send(iface, pkt);
168
169 if (IS_ENABLED(CONFIG_NET_STATISTICS) && ret == 0) {
170 NET_DBG("Sent pkt %p len %zu", pkt, pkt_len);
171 net_stats_update_bytes_sent(iface, pkt_len);
172 }
173
174 return ret;
175 }
176
virtual_enable(struct net_if * iface,bool state)177 static int virtual_enable(struct net_if *iface, bool state)
178 {
179 const struct virtual_interface_api *virt;
180 struct virtual_interface_context *ctx;
181 int ret = 0;
182
183 virt = net_if_get_device(iface)->api;
184 if (!virt) {
185 return -ENOENT;
186 }
187
188 ctx = net_if_l2_data(iface);
189
190 if (state) {
191 /* Take the interfaces below this interface up as
192 * it does not make sense otherwise.
193 */
194
195 while (ctx->iface) {
196 if (net_if_is_up(ctx->iface)) {
197 /* Network interfaces below this must be up too
198 * so we can bail out at this point.
199 */
200 break;
201 }
202
203 if (net_if_l2(ctx->iface) !=
204 &NET_L2_GET_NAME(VIRTUAL)) {
205 net_if_up(ctx->iface);
206 break;
207 }
208
209 NET_DBG("Taking iface %d up", net_if_get_by_iface(ctx->iface));
210
211 net_if_up(ctx->iface);
212 ctx = net_if_l2_data(ctx->iface);
213 }
214
215 if (virt->start) {
216 ret = virt->start(net_if_get_device(iface));
217 }
218
219 return ret;
220 }
221
222 if (virt->stop) {
223 ret = virt->stop(net_if_get_device(iface));
224 }
225
226 return ret;
227 }
228
virtual_flags(struct net_if * iface)229 enum net_l2_flags virtual_flags(struct net_if *iface)
230 {
231 struct virtual_interface_context *ctx = net_if_l2_data(iface);
232
233 return ctx->virtual_l2_flags;
234 }
235
236 #if defined(CONFIG_NET_L2_ETHERNET_RESERVE_HEADER) && defined(CONFIG_NET_VLAN)
237 extern int vlan_alloc_buffer(struct net_if *iface, struct net_pkt *pkt,
238 size_t size, uint16_t proto, k_timeout_t timeout);
239
virtual_l2_alloc(struct net_if * iface,struct net_pkt * pkt,size_t size,enum net_ip_protocol proto,k_timeout_t timeout)240 static int virtual_l2_alloc(struct net_if *iface, struct net_pkt *pkt,
241 size_t size, enum net_ip_protocol proto,
242 k_timeout_t timeout)
243 {
244 return vlan_alloc_buffer(iface, pkt, size, proto, timeout);
245 }
246 #else
247 #define virtual_l2_alloc NULL
248 #endif
249
250 NET_L2_INIT(VIRTUAL_L2, virtual_recv, virtual_send, virtual_enable,
251 virtual_flags, virtual_l2_alloc);
252
random_linkaddr(uint8_t * linkaddr,size_t len)253 static void random_linkaddr(uint8_t *linkaddr, size_t len)
254 {
255 sys_rand_get(linkaddr, len);
256
257 linkaddr[0] |= 0x02; /* force LAA bit */
258 linkaddr[0] &= ~0x01; /* clear multicast bit */
259 }
260
net_virtual_interface_attach(struct net_if * virtual_iface,struct net_if * iface)261 int net_virtual_interface_attach(struct net_if *virtual_iface,
262 struct net_if *iface)
263 {
264 const struct virtual_interface_api *api;
265 struct virtual_interface_context *ctx;
266 bool up = false;
267
268 if (net_if_get_by_iface(virtual_iface) < 0 ||
269 (iface != NULL && net_if_get_by_iface(iface) < 0)) {
270 return -EINVAL;
271 }
272
273 if (virtual_iface == iface) {
274 return -EINVAL;
275 }
276
277 api = net_if_get_device(virtual_iface)->api;
278 if (api->attach == NULL) {
279 return -ENOENT;
280 }
281
282 ctx = net_if_l2_data(virtual_iface);
283
284 if (ctx->iface) {
285 if (iface != NULL) {
286 /* We are already attached */
287 return -EALREADY;
288 }
289
290 /* Detaching, take the interface down */
291 net_if_down(virtual_iface);
292
293 (void)sys_slist_find_and_remove(
294 &ctx->iface->config.virtual_interfaces,
295 &ctx->node);
296
297 NET_DBG("Detaching %d from %d",
298 net_if_get_by_iface(virtual_iface),
299 net_if_get_by_iface(ctx->iface));
300
301 ctx->iface = NULL;
302 } else {
303 if (iface == NULL) {
304 /* We are already detached */
305 return -EALREADY;
306 }
307
308 /* Attaching, take the interface up if auto start is enabled.
309 */
310 ctx->iface = iface;
311 sys_slist_append(&ctx->iface->config.virtual_interfaces,
312 &ctx->node);
313
314 NET_DBG("Attaching %d to %d",
315 net_if_get_by_iface(virtual_iface),
316 net_if_get_by_iface(ctx->iface));
317
318 up = true;
319 }
320
321 /* Figure out the link address for this interface. The actual link
322 * address is randomized. This must be done before attach is called so
323 * that the attach callback can create link local address for the
324 * network interface (if IPv6). The actual link address is typically
325 * not need in tunnels.
326 */
327 if (iface) {
328 random_linkaddr(ctx->lladdr.addr, sizeof(ctx->lladdr.addr));
329
330 ctx->lladdr.len = sizeof(ctx->lladdr.addr);
331 ctx->lladdr.type = NET_LINK_UNKNOWN;
332
333 net_if_set_link_addr(virtual_iface, ctx->lladdr.addr,
334 ctx->lladdr.len, ctx->lladdr.type);
335 }
336
337 api->attach(virtual_iface, iface);
338
339 if (up && !net_if_flag_is_set(virtual_iface,
340 NET_IF_NO_AUTO_START)) {
341 net_if_up(virtual_iface);
342 }
343
344 return 0;
345 }
346
net_virtual_disable(struct net_if * iface)347 void net_virtual_disable(struct net_if *iface)
348 {
349 struct virtual_interface_context *ctx, *tmp;
350 sys_slist_t *interfaces;
351
352 if (net_if_get_by_iface(iface) < 0) {
353 return;
354 }
355
356 interfaces = &iface->config.virtual_interfaces;
357 SYS_SLIST_FOR_EACH_CONTAINER_SAFE(interfaces, ctx, tmp, node) {
358 NET_DBG("Iface %d down, setting virtual iface %d carrier off",
359 net_if_get_by_iface(iface),
360 net_if_get_by_iface(ctx->virtual_iface));
361 net_if_carrier_off(ctx->virtual_iface);
362 }
363 }
364
net_virtual_enable(struct net_if * iface)365 void net_virtual_enable(struct net_if *iface)
366 {
367 struct virtual_interface_context *ctx, *tmp;
368 sys_slist_t *interfaces;
369
370 if (net_if_get_by_iface(iface) < 0) {
371 return;
372 }
373
374 interfaces = &iface->config.virtual_interfaces;
375 SYS_SLIST_FOR_EACH_CONTAINER_SAFE(interfaces, ctx, tmp, node) {
376 NET_DBG("Iface %d up, setting virtual iface %d carrier on",
377 net_if_get_by_iface(iface),
378 net_if_get_by_iface(ctx->virtual_iface));
379 net_if_carrier_on(ctx->virtual_iface);
380 }
381 }
382
net_virtual_get_iface(struct net_if * iface)383 struct net_if *net_virtual_get_iface(struct net_if *iface)
384 {
385 struct virtual_interface_context *ctx;
386
387 if (net_if_get_by_iface(iface) < 0) {
388 return NULL;
389 }
390
391 if (net_if_l2(iface) != &NET_L2_GET_NAME(VIRTUAL)) {
392 return NULL;
393 }
394
395 ctx = net_if_l2_data(iface);
396
397 return ctx->iface;
398 }
399
net_virtual_get_name(struct net_if * iface,char * buf,size_t len)400 char *net_virtual_get_name(struct net_if *iface, char *buf, size_t len)
401 {
402 struct virtual_interface_context *ctx;
403
404 if (net_if_get_by_iface(iface) < 0) {
405 return NULL;
406 }
407
408 if (net_if_l2(iface) != &NET_L2_GET_NAME(VIRTUAL)) {
409 return NULL;
410 }
411
412 ctx = net_if_l2_data(iface);
413
414 strncpy(buf, ctx->name, MIN(len, sizeof(ctx->name)));
415 buf[len - 1] = '\0';
416
417 return buf;
418 }
419
net_virtual_set_name(struct net_if * iface,const char * name)420 void net_virtual_set_name(struct net_if *iface, const char *name)
421 {
422 struct virtual_interface_context *ctx;
423
424 if (net_if_get_by_iface(iface) < 0) {
425 return;
426 }
427
428 if (net_if_l2(iface) != &NET_L2_GET_NAME(VIRTUAL)) {
429 return;
430 }
431
432 ctx = net_if_l2_data(iface);
433
434 strncpy(ctx->name, name, ARRAY_SIZE(ctx->name) - 1);
435 ctx->name[ARRAY_SIZE(ctx->name) - 1] = '\0';
436 }
437
net_virtual_set_flags(struct net_if * iface,enum net_l2_flags flags)438 enum net_l2_flags net_virtual_set_flags(struct net_if *iface,
439 enum net_l2_flags flags)
440 {
441 struct virtual_interface_context *ctx;
442 enum net_l2_flags old_flags;
443
444 if (net_if_get_by_iface(iface) < 0) {
445 return 0;
446 }
447
448 if (net_if_l2(iface) != &NET_L2_GET_NAME(VIRTUAL)) {
449 return 0;
450 }
451
452 ctx = net_if_l2_data(iface);
453 old_flags = ctx->virtual_l2_flags;
454 ctx->virtual_l2_flags = flags;
455
456 return old_flags;
457 }
458
net_virtual_init(struct net_if * iface)459 void net_virtual_init(struct net_if *iface)
460 {
461 struct virtual_interface_context *ctx;
462
463 sys_slist_init(&iface->config.virtual_interfaces);
464
465 if (net_if_l2(iface) != &NET_L2_GET_NAME(VIRTUAL)) {
466 return;
467 }
468
469 ctx = net_if_l2_data(iface);
470 if (ctx->is_init) {
471 return;
472 }
473
474 NET_DBG("Initializing virtual L2 %p for iface %d (%p)", ctx,
475 net_if_get_by_iface(iface), iface);
476
477 ctx->virtual_iface = iface;
478 ctx->virtual_l2_flags = 0;
479 ctx->is_init = true;
480 }
481