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