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