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