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