1 /*
2 * Copyright (c) 2024 Nordic Semiconductor
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/logging/log.h>
8 LOG_MODULE_REGISTER(net_ethernet_vlan, CONFIG_NET_L2_ETHERNET_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/ethernet.h>
15 #include <zephyr/net/ethernet_mgmt.h>
16 #include <zephyr/net/virtual.h>
17 #include <zephyr/random/random.h>
18
19 #include "net_private.h"
20
21 #if defined(CONFIG_NET_VLAN_TXRX_DEBUG)
22 #define DEBUG_TX 1
23 #define DEBUG_RX 1
24 #else
25 #define DEBUG_TX 0
26 #define DEBUG_RX 0
27 #endif
28
29 /* If the VLAN interface count is 0, then only priority tagged (tag 0)
30 * Ethernet frames can be received. In this case we do not need to
31 * allocate any memory for VLAN interfaces.
32 */
33 #if CONFIG_NET_VLAN_COUNT > 0
34
35 #define MAX_VLAN_NAME_LEN MIN(sizeof("VLAN-<#####>"), \
36 CONFIG_NET_INTERFACE_NAME_LEN)
37 #define MAX_VIRT_NAME_LEN MIN(sizeof("<not attached>"), \
38 CONFIG_NET_L2_VIRTUAL_MAX_NAME_LEN)
39
40 static void vlan_iface_init(struct net_if *iface);
41 static int vlan_interface_attach(struct net_if *vlan_iface,
42 struct net_if *iface);
43 static enum net_verdict vlan_interface_recv(struct net_if *iface,
44 struct net_pkt *pkt);
45 static int vlan_interface_send(struct net_if *iface, struct net_pkt *pkt);
46 static int vlan_interface_stop(const struct device *dev);
47 static enum virtual_interface_caps vlan_get_capabilities(struct net_if *iface);
48 static int vlan_interface_start(const struct device *dev);
49 static int virt_dev_init(const struct device *dev);
50
51 static K_MUTEX_DEFINE(lock);
52
53 struct vlan_context {
54 struct net_if *iface;
55 struct net_if *attached_to;
56 uint16_t tag;
57 bool status : 1; /* Is the interface enabled or not */
58 bool is_used : 1; /* Is there active config on this context */
59 bool init_done : 1; /* Is interface init called for this context */
60 };
61
62 static const struct virtual_interface_api vlan_iface_api = {
63 .iface_api.init = vlan_iface_init,
64
65 .get_capabilities = vlan_get_capabilities,
66 .start = vlan_interface_start,
67 .stop = vlan_interface_stop,
68 .send = vlan_interface_send,
69 .recv = vlan_interface_recv,
70 .attach = vlan_interface_attach,
71 };
72
73 #define ETH_DEFINE_VLAN(x, _) \
74 static struct vlan_context vlan_context_data_##x = { \
75 .tag = NET_VLAN_TAG_UNSPEC, \
76 }; \
77 NET_VIRTUAL_INTERFACE_INIT_INSTANCE(vlan_##x, \
78 "VLAN_" #x, \
79 x, \
80 virt_dev_init, \
81 NULL, \
82 &vlan_context_data_##x, \
83 NULL, /* config */ \
84 CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \
85 &vlan_iface_api, \
86 NET_ETH_MTU)
87
88 LISTIFY(CONFIG_NET_VLAN_COUNT, ETH_DEFINE_VLAN, (;), _);
89
90 #define INIT_VLAN_CONTEXT_PTR(x, _) \
91 [x] = &vlan_context_data_##x \
92
93 static struct vlan_context *vlan_ctx[] = {
94 LISTIFY(CONFIG_NET_VLAN_COUNT, INIT_VLAN_CONTEXT_PTR, (,), _)
95 };
96
97 #define INIT_VLAN_CONTEXT_IFACE(x, _) \
98 vlan_context_data_##x.iface = NET_IF_GET(vlan_##x, x)
99
init_context_iface(void)100 static void init_context_iface(void)
101 {
102 static bool init_done;
103
104 if (init_done) {
105 return;
106 }
107
108 init_done = true;
109
110 LISTIFY(CONFIG_NET_VLAN_COUNT, INIT_VLAN_CONTEXT_IFACE, (;), _);
111 }
112
virt_dev_init(const struct device * dev)113 static int virt_dev_init(const struct device *dev)
114 {
115 ARG_UNUSED(dev);
116
117 init_context_iface();
118
119 return 0;
120 }
121
get_vlan_ctx(struct net_if * main_iface,uint16_t vlan_tag,bool any_tag)122 static struct vlan_context *get_vlan_ctx(struct net_if *main_iface,
123 uint16_t vlan_tag,
124 bool any_tag)
125 {
126 struct virtual_interface_context *vctx, *tmp;
127 sys_slist_t *interfaces;
128 struct vlan_context *ctx;
129
130 interfaces = &main_iface->config.virtual_interfaces;
131
132 SYS_SLIST_FOR_EACH_CONTAINER_SAFE(interfaces, vctx, tmp, node) {
133 enum virtual_interface_caps caps;
134
135 if (vctx->virtual_iface == NULL) {
136 continue;
137 }
138
139 caps = net_virtual_get_iface_capabilities(vctx->virtual_iface);
140 if (!(caps & VIRTUAL_INTERFACE_VLAN)) {
141 continue;
142 }
143
144 ctx = net_if_get_device(vctx->virtual_iface)->data;
145
146 if (any_tag) {
147 if (ctx->tag != NET_VLAN_TAG_UNSPEC) {
148 return ctx;
149 }
150 } else {
151 if ((vlan_tag == NET_VLAN_TAG_UNSPEC ||
152 vlan_tag == ctx->tag)) {
153 return ctx;
154 }
155 }
156 }
157
158 return NULL;
159 }
160
get_vlan(struct net_if * iface,uint16_t vlan_tag)161 static struct vlan_context *get_vlan(struct net_if *iface,
162 uint16_t vlan_tag)
163 {
164 struct vlan_context *ctx = NULL;
165
166 k_mutex_lock(&lock, K_FOREVER);
167
168 /* If the interface is NULL, then get the VLAN that has the tag */
169 if (iface == NULL) {
170 ARRAY_FOR_EACH(vlan_ctx, i) {
171 if (vlan_ctx[i] == NULL || !vlan_ctx[i]->is_used) {
172 continue;
173 }
174
175 if (vlan_tag == vlan_ctx[i]->tag) {
176 ctx = vlan_ctx[i];
177 break;
178 }
179 }
180
181 goto out;
182 }
183
184 /* If the interface is the main Ethernet one, then we only need
185 * to go through its attached virtual interfaces.
186 */
187 if (net_if_l2(iface) == &NET_L2_GET_NAME(ETHERNET)) {
188
189 ctx = get_vlan_ctx(iface, vlan_tag, false);
190 goto out;
191
192 }
193
194 if (net_if_l2(iface) != &NET_L2_GET_NAME(VIRTUAL)) {
195 goto out;
196 }
197
198 /* If the interface is virtual, then it should be the VLAN one.
199 * Just get the Ethernet interface it points to get the context.
200 */
201 ctx = get_vlan_ctx(net_virtual_get_iface(iface), vlan_tag, false);
202
203 out:
204 k_mutex_unlock(&lock);
205
206 return ctx;
207 }
208
set_priority(struct net_pkt * pkt)209 static void set_priority(struct net_pkt *pkt)
210 {
211 uint8_t vlan_priority;
212
213 vlan_priority = net_priority2vlan(net_pkt_priority(pkt));
214 net_pkt_set_vlan_priority(pkt, vlan_priority);
215 }
216
net_eth_get_vlan_iface(struct net_if * iface,uint16_t tag)217 struct net_if *net_eth_get_vlan_iface(struct net_if *iface, uint16_t tag)
218 {
219 struct vlan_context *ctx;
220
221 ctx = get_vlan(iface, tag);
222 if (ctx == NULL) {
223 if (tag == NET_VLAN_TAG_PRIORITY) {
224 return iface;
225 }
226
227 return NULL;
228 }
229
230 return ctx->iface;
231 }
232
net_eth_get_vlan_main(struct net_if * iface)233 struct net_if *net_eth_get_vlan_main(struct net_if *iface)
234 {
235 struct vlan_context *ctx;
236
237 ctx = get_vlan(iface, NET_VLAN_TAG_UNSPEC);
238 if (ctx == NULL) {
239 return NULL;
240 }
241
242 return ctx->attached_to;
243 }
244
enable_vlan_iface(struct vlan_context * ctx,struct net_if * iface)245 static bool enable_vlan_iface(struct vlan_context *ctx,
246 struct net_if *iface)
247 {
248 int iface_idx = net_if_get_by_iface(iface);
249 char name[MAX(MAX_VLAN_NAME_LEN, MAX_VIRT_NAME_LEN)];
250 int ret;
251
252 if (iface_idx < 0) {
253 return false;
254 }
255
256 ret = net_virtual_interface_attach(ctx->iface, iface);
257 if (ret < 0) {
258 NET_DBG("Cannot attach iface %d to %d",
259 net_if_get_by_iface(ctx->iface),
260 net_if_get_by_iface(ctx->attached_to));
261 return false;
262 }
263
264 ctx->is_used = true;
265
266 snprintk(name, sizeof(name), "VLAN-%d", ctx->tag);
267 net_if_set_name(ctx->iface, name);
268
269 snprintk(name, sizeof(name), "VLAN to %d",
270 net_if_get_by_iface(ctx->attached_to));
271 net_virtual_set_name(ctx->iface, name);
272
273 return true;
274 }
275
disable_vlan_iface(struct vlan_context * ctx,struct net_if * iface)276 static bool disable_vlan_iface(struct vlan_context *ctx,
277 struct net_if *iface)
278 {
279 int iface_idx = net_if_get_by_iface(iface);
280 char name[MAX(MAX_VLAN_NAME_LEN, MAX_VIRT_NAME_LEN)];
281
282 if (iface_idx < 0) {
283 return false;
284 }
285
286 (void)net_virtual_interface_attach(iface, NULL);
287 ctx->is_used = false;
288
289 snprintk(name, sizeof(name), "VLAN-<free>");
290 net_if_set_name(iface, name);
291
292 snprintk(name, sizeof(name), "<not attached>");
293 net_virtual_set_name(iface, name);
294
295 return true;
296 }
297
is_vlan_enabled_for_iface(struct net_if * iface)298 static bool is_vlan_enabled_for_iface(struct net_if *iface)
299 {
300 int iface_idx = net_if_get_by_iface(iface);
301 struct vlan_context *ctx;
302 bool ret = false;
303
304 if (iface_idx < 0) {
305 return false;
306 }
307
308 k_mutex_lock(&lock, K_FOREVER);
309
310 ctx = get_vlan_ctx(iface, NET_VLAN_TAG_UNSPEC, true);
311 ret = (ctx != NULL);
312
313 k_mutex_unlock(&lock);
314
315 return ret;
316 }
317
net_eth_is_vlan_enabled(struct ethernet_context * ctx,struct net_if * iface)318 bool net_eth_is_vlan_enabled(struct ethernet_context *ctx,
319 struct net_if *iface)
320 {
321 ARG_UNUSED(ctx);
322
323 return is_vlan_enabled_for_iface(iface);
324 }
325
net_eth_get_vlan_tag(struct net_if * iface)326 uint16_t net_eth_get_vlan_tag(struct net_if *iface)
327 {
328 uint16_t tag = NET_VLAN_TAG_UNSPEC;
329
330 k_mutex_lock(&lock, K_FOREVER);
331
332 ARRAY_FOR_EACH(vlan_ctx, i) {
333 if (vlan_ctx[i] == NULL || !vlan_ctx[i]->is_used) {
334 continue;
335 }
336
337 if (vlan_ctx[i]->iface == iface) {
338 tag = vlan_ctx[i]->tag;
339 break;
340 }
341 }
342
343 k_mutex_unlock(&lock);
344
345 return tag;
346 }
347
net_eth_is_vlan_interface(struct net_if * iface)348 bool net_eth_is_vlan_interface(struct net_if *iface)
349 {
350 enum virtual_interface_caps caps;
351
352 if (net_if_l2(iface) != &NET_L2_GET_NAME(VIRTUAL)) {
353 return false;
354 }
355
356 caps = net_virtual_get_iface_capabilities(iface);
357 if (!(caps & VIRTUAL_INTERFACE_VLAN)) {
358 return false;
359 }
360
361 return true;
362 }
363
net_eth_get_vlan_status(struct net_if * iface)364 bool net_eth_get_vlan_status(struct net_if *iface)
365 {
366 bool status = false;
367 struct vlan_context *ctx;
368
369 k_mutex_lock(&lock, K_FOREVER);
370
371 ctx = get_vlan_ctx(iface, NET_VLAN_TAG_UNSPEC, true);
372 if (ctx != NULL) {
373 status = ctx->status;
374 }
375
376 k_mutex_unlock(&lock);
377
378 return status;
379 }
380
setup_link_address(struct vlan_context * ctx)381 static void setup_link_address(struct vlan_context *ctx)
382 {
383 struct net_linkaddr *ll_addr;
384
385 ll_addr = net_if_get_link_addr(ctx->attached_to);
386
387 (void)net_if_set_link_addr(ctx->iface,
388 ll_addr->addr,
389 ll_addr->len,
390 ll_addr->type);
391 }
392
net_eth_vlan_enable(struct net_if * iface,uint16_t tag)393 int net_eth_vlan_enable(struct net_if *iface, uint16_t tag)
394 {
395 struct ethernet_context *ctx = net_if_l2_data(iface);
396 const struct ethernet_api *eth = net_if_get_device(iface)->api;
397 struct vlan_context *vlan;
398 int ret;
399
400 if (!eth) {
401 return -ENOENT;
402 }
403
404 if (net_if_l2(iface) != &NET_L2_GET_NAME(ETHERNET)) {
405 return -EINVAL;
406 }
407
408 if (!(net_eth_get_hw_capabilities(iface) & ETHERNET_HW_VLAN)) {
409 NET_DBG("Interface %d does not support VLAN",
410 net_if_get_by_iface(iface));
411 return -ENOTSUP;
412 }
413
414 if (!ctx->is_init) {
415 return -EPERM;
416 }
417
418 if (tag >= NET_VLAN_TAG_UNSPEC) {
419 return -EBADF;
420 }
421
422 vlan = get_vlan(iface, tag);
423 if (vlan != NULL) {
424 return -EALREADY;
425 }
426
427 /* This will make sure that the tag is not yet in use by some
428 * other interface.
429 */
430 vlan = get_vlan(NULL, tag);
431 if (vlan != NULL) {
432 return -EALREADY;
433 }
434
435 ret = -ENOSPC;
436
437 k_mutex_lock(&lock, K_FOREVER);
438
439 ARRAY_FOR_EACH(vlan_ctx, i) {
440 if (vlan_ctx[i] == NULL || vlan_ctx[i]->is_used) {
441 continue;
442 }
443
444 vlan = vlan_ctx[i];
445 vlan->tag = tag;
446
447 if (!enable_vlan_iface(vlan, iface)) {
448 continue;
449 }
450
451 NET_DBG("[%zd] Adding vlan tag %d to iface %d (%p) attached to %d (%p)",
452 i, vlan->tag, net_if_get_by_iface(vlan->iface), vlan->iface,
453 net_if_get_by_iface(iface), iface);
454
455 /* Use MAC address of the attached Ethernet interface so that
456 * packet reception works without any tweaks.
457 */
458 setup_link_address(vlan);
459
460 if (eth->vlan_setup) {
461 eth->vlan_setup(net_if_get_device(iface),
462 iface, vlan->tag, true);
463 }
464
465 ethernet_mgmt_raise_vlan_enabled_event(vlan->iface, vlan->tag);
466
467 ret = 0;
468 break;
469 }
470
471 k_mutex_unlock(&lock);
472
473 return ret;
474 }
475
net_eth_vlan_disable(struct net_if * iface,uint16_t tag)476 int net_eth_vlan_disable(struct net_if *iface, uint16_t tag)
477 {
478 const struct ethernet_api *eth;
479 struct vlan_context *vlan;
480
481 if (net_if_l2(iface) != &NET_L2_GET_NAME(ETHERNET) &&
482 net_if_l2(iface) != &NET_L2_GET_NAME(VIRTUAL)) {
483 return -EINVAL;
484 }
485
486 if (tag == NET_VLAN_TAG_UNSPEC) {
487 return -EBADF;
488 }
489
490 vlan = get_vlan(iface, tag);
491 if (!vlan) {
492 return -ESRCH;
493 }
494
495 eth = net_if_get_device(vlan->attached_to)->api;
496
497 k_mutex_lock(&lock, K_FOREVER);
498
499 NET_DBG("Removing vlan tag %d from VLAN iface %d (%p) attached to %d (%p)",
500 vlan->tag, net_if_get_by_iface(vlan->iface), vlan->iface,
501 net_if_get_by_iface(vlan->attached_to), vlan->attached_to);
502
503 vlan->tag = NET_VLAN_TAG_UNSPEC;
504
505 if (eth->vlan_setup) {
506 eth->vlan_setup(net_if_get_device(vlan->attached_to),
507 vlan->attached_to, tag, false);
508 }
509
510 ethernet_mgmt_raise_vlan_disabled_event(vlan->iface, tag);
511
512 (void)disable_vlan_iface(vlan, vlan->iface);
513
514 k_mutex_unlock(&lock);
515
516 return 0;
517 }
518
vlan_get_capabilities(struct net_if * iface)519 static enum virtual_interface_caps vlan_get_capabilities(struct net_if *iface)
520 {
521 ARG_UNUSED(iface);
522
523 return VIRTUAL_INTERFACE_VLAN;
524 }
525
vlan_interface_start(const struct device * dev)526 static int vlan_interface_start(const struct device *dev)
527 {
528 struct vlan_context *ctx = dev->data;
529
530 if (!ctx->is_used) {
531 NET_DBG("VLAN interface %d not configured yet.",
532 net_if_get_by_iface(ctx->iface));
533 return -ENOENT;
534 }
535
536 if (ctx->status) {
537 return -EALREADY;
538 }
539
540 ctx->status = true;
541
542 NET_DBG("Starting iface %d", net_if_get_by_iface(ctx->iface));
543
544 /* You can implement here any special action that is needed
545 * when the network interface is coming up.
546 */
547
548 return 0;
549 }
550
vlan_interface_stop(const struct device * dev)551 static int vlan_interface_stop(const struct device *dev)
552 {
553 struct vlan_context *ctx = dev->data;
554
555 if (!ctx->is_used) {
556 NET_DBG("VLAN interface %d not configured yet.",
557 net_if_get_by_iface(ctx->iface));
558 return -ENOENT;
559 }
560
561 if (!ctx->status) {
562 return -EALREADY;
563 }
564
565 ctx->status = false;
566
567 NET_DBG("Stopping iface %d", net_if_get_by_iface(ctx->iface));
568
569 /* You can implement here any special action that is needed
570 * when the network interface is going down.
571 */
572
573 return 0;
574 }
575
vlan_interface_send(struct net_if * iface,struct net_pkt * pkt)576 static int vlan_interface_send(struct net_if *iface, struct net_pkt *pkt)
577 {
578 struct vlan_context *ctx = net_if_get_device(iface)->data;
579
580 if (ctx->attached_to == NULL) {
581 return -ENOENT;
582 }
583
584 net_pkt_set_vlan_tag(pkt, ctx->tag);
585 net_pkt_set_iface(pkt, ctx->attached_to);
586 set_priority(pkt);
587
588 if (DEBUG_TX) {
589 char str[sizeof("TX iface xx (tag xxxx)")];
590
591 snprintk(str, sizeof(str), "TX iface %d (tag %d)",
592 net_if_get_by_iface(net_pkt_iface(pkt)),
593 ctx->tag);
594
595 net_pkt_hexdump(pkt, str);
596 }
597
598 return net_send_data(pkt);
599 }
600
vlan_interface_recv(struct net_if * iface,struct net_pkt * pkt)601 static enum net_verdict vlan_interface_recv(struct net_if *iface,
602 struct net_pkt *pkt)
603 {
604 struct vlan_context *ctx = net_if_get_device(iface)->data;
605
606 if (net_pkt_vlan_tag(pkt) != ctx->tag) {
607 return NET_CONTINUE;
608 }
609
610 if (DEBUG_RX) {
611 char str[sizeof("RX iface xx (tag xxxx)")];
612
613 snprintk(str, sizeof(str), "RX iface %d (tag %d)",
614 net_if_get_by_iface(iface),
615 net_pkt_vlan_tag(pkt));
616
617 net_pkt_hexdump(pkt, str);
618 }
619
620 return NET_OK;
621 }
622
vlan_alloc_buffer(struct net_if * iface,struct net_pkt * pkt,size_t size,uint16_t proto,k_timeout_t timeout)623 int vlan_alloc_buffer(struct net_if *iface, struct net_pkt *pkt,
624 size_t size, uint16_t proto, k_timeout_t timeout)
625 {
626 enum virtual_interface_caps caps;
627 int ret = 0;
628
629 caps = net_virtual_get_iface_capabilities(iface);
630 if (caps & VIRTUAL_INTERFACE_VLAN) {
631 ret = net_pkt_alloc_buffer_with_reserve(pkt, size,
632 sizeof(struct net_eth_vlan_hdr),
633 proto, timeout);
634 }
635
636 return ret;
637 }
638
vlan_interface_attach(struct net_if * vlan_iface,struct net_if * iface)639 static int vlan_interface_attach(struct net_if *vlan_iface,
640 struct net_if *iface)
641 {
642 struct vlan_context *ctx = net_if_get_device(vlan_iface)->data;
643
644 if (iface == NULL) {
645 NET_DBG("VLAN interface %d (%p) detached from %d (%p)",
646 net_if_get_by_iface(vlan_iface), vlan_iface,
647 net_if_get_by_iface(ctx->attached_to), ctx->attached_to);
648 } else {
649 NET_DBG("VLAN interface %d (%p) attached to %d (%p)",
650 net_if_get_by_iface(vlan_iface), vlan_iface,
651 net_if_get_by_iface(iface), iface);
652 }
653
654 ctx->attached_to = iface;
655
656 return 0;
657 }
658
vlan_iface_init(struct net_if * iface)659 static void vlan_iface_init(struct net_if *iface)
660 {
661 struct vlan_context *ctx = net_if_get_device(iface)->data;
662 char name[MAX(MAX_VLAN_NAME_LEN, MAX_VIRT_NAME_LEN)];
663
664 if (ctx->init_done) {
665 return;
666 }
667
668 ctx->iface = iface;
669 net_if_flag_set(iface, NET_IF_NO_AUTO_START);
670
671 snprintk(name, sizeof(name), "VLAN-<free>");
672 net_if_set_name(iface, name);
673
674 snprintk(name, sizeof(name), "not attached");
675 net_virtual_set_name(iface, name);
676
677 (void)net_virtual_set_flags(ctx->iface, NET_L2_MULTICAST);
678
679 ctx->init_done = true;
680 }
681
682 #else /* CONFIG_NET_VLAN_COUNT > 0 */
683
684 /* Dummy functions if VLAN is not really used. This is only needed
685 * if priority tagged frames (tag 0) are supported.
686 */
net_eth_is_vlan_enabled(struct ethernet_context * ctx,struct net_if * iface)687 bool net_eth_is_vlan_enabled(struct ethernet_context *ctx,
688 struct net_if *iface)
689 {
690 ARG_UNUSED(ctx);
691 ARG_UNUSED(iface);
692
693 return true;
694 }
695
net_eth_get_vlan_iface(struct net_if * iface,uint16_t tag)696 struct net_if *net_eth_get_vlan_iface(struct net_if *iface, uint16_t tag)
697 {
698 if (tag == NET_VLAN_TAG_PRIORITY) {
699 return iface;
700 }
701
702 return NULL;
703 }
704 #endif /* CONFIG_NET_VLAN_COUNT > 0 */
705