1 /*
2  * Copyright (c) 2018 Intel Corporation.
3  * Copyright (c) 2025 Nordic Semiconductor
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #include <zephyr/logging/log.h>
9 LOG_MODULE_DECLARE(net_samples_common, LOG_LEVEL_DBG);
10 
11 #include <stdlib.h>
12 #include <zephyr/kernel.h>
13 #include <zephyr/net/ethernet.h>
14 
15 /* User data for the interface callback */
16 struct ud {
17 	struct net_if *first;
18 	struct net_if *second;
19 	struct net_if *eth;
20 };
21 
iface_cb(struct net_if * iface,void * user_data)22 static void iface_cb(struct net_if *iface, void *user_data)
23 {
24 	struct ud *ud = user_data;
25 
26 	if (net_if_l2(iface) == &NET_L2_GET_NAME(ETHERNET) && ud->eth == NULL) {
27 		ud->eth = iface;
28 		return;
29 	}
30 
31 	if (net_if_l2(iface) != &NET_L2_GET_NAME(VIRTUAL)) {
32 		return;
33 	}
34 
35 	if (!ud->first) {
36 		ud->first = iface;
37 		return;
38 	}
39 
40 	if (!ud->second) {
41 		ud->second = iface;
42 		return;
43 	}
44 }
45 
setup_iface(struct net_if * eth_iface,struct net_if * vlan_iface,const char * option)46 static int setup_iface(struct net_if *eth_iface,
47 		       struct net_if *vlan_iface,
48 		       const char *option)
49 {
50 	struct net_sockaddr_storage addr = { 0 };
51 	struct net_sockaddr *paddr = (struct net_sockaddr *)&addr;
52 	const char *addr_str, *next;
53 	struct net_if_addr *ifaddr;
54 	uint8_t mask_len = 0;
55 	unsigned long value;
56 	uint16_t vlan_tag;
57 	char *endptr;
58 	bool status;
59 	int ret;
60 
61 	if (option[0] == '\0') {
62 		return 0;
63 	}
64 
65 	next = strstr(option, ";");
66 	if (next == NULL) {
67 		LOG_ERR("VLAN tag not found, invalid option \"%s\"", option);
68 		return -EINVAL;
69 	}
70 
71 	value = strtoul(option, &endptr, 10);
72 	if (*endptr != '\0' && endptr != next) {
73 		LOG_ERR("Invalid VLAN tag \"%s\"", option);
74 		return -EINVAL;
75 	}
76 
77 	vlan_tag = (uint16_t)value;
78 	addr_str = ++next;
79 
80 	do {
81 		char my_addr[NET_INET6_ADDRSTRLEN] = { 'N', 'o', ' ', 'I', 'P', '\0'};
82 
83 		next = net_ipaddr_parse_mask(addr_str, strlen(addr_str),
84 					     paddr, &mask_len);
85 		if (next == NULL) {
86 			LOG_ERR("Cannot parse IP address \"%s\"", addr_str);
87 			return -EINVAL;
88 		}
89 
90 		net_addr_ntop(paddr->sa_family, net_sin(paddr)->sin_addr.s4_addr,
91 			      my_addr, sizeof(my_addr));
92 
93 		if (paddr->sa_family == NET_AF_INET) {
94 			struct net_sockaddr_in *addr4 = (struct net_sockaddr_in *)paddr;
95 			struct net_sockaddr_in mask;
96 
97 			ifaddr = net_if_ipv4_addr_add(vlan_iface, &addr4->sin_addr,
98 						      NET_ADDR_MANUAL, 0);
99 
100 			ret = net_mask_len_to_netmask(NET_AF_INET, mask_len,
101 						      (struct net_sockaddr *)&mask);
102 			if (ret < 0) {
103 				LOG_ERR("Invalid network mask length (%d)", ret);
104 				return ret;
105 			}
106 
107 			status = net_if_ipv4_set_netmask_by_addr(vlan_iface,
108 								 &addr4->sin_addr,
109 								 &mask.sin_addr);
110 
111 		} else if (paddr->sa_family == NET_AF_INET6) {
112 			struct net_sockaddr_in6 *addr6 = (struct net_sockaddr_in6 *)paddr;
113 			struct net_in6_addr netaddr6;
114 
115 			ifaddr = net_if_ipv6_addr_add(vlan_iface, &addr6->sin6_addr,
116 						      NET_ADDR_MANUAL, 0);
117 
118 			net_ipv6_addr_prefix_mask((uint8_t *)&addr6->sin6_addr,
119 						  (uint8_t *)&netaddr6,
120 						  mask_len);
121 
122 			if (!net_if_ipv6_prefix_add(vlan_iface, &netaddr6, mask_len,
123 						    (uint32_t)0xffffffff)) {
124 				LOG_ERR("Cannot add %s to interface %d", my_addr,
125 					net_if_get_by_iface(vlan_iface));
126 				return -EINVAL;
127 			}
128 
129 		} else {
130 			LOG_ERR("Cannot parse IP address \"%s\"", my_addr);
131 			return -EAFNOSUPPORT;
132 		}
133 
134 		if (ifaddr == NULL) {
135 			LOG_ERR("Cannot add IP address \"%s\" to interface %d",
136 				my_addr, net_if_get_by_iface(vlan_iface));
137 			return -ENOENT;
138 		}
139 
140 		addr_str = next;
141 	} while (addr_str != NULL && *addr_str != '\0');
142 
143 	ret = net_eth_vlan_enable(eth_iface, vlan_tag);
144 	if (ret < 0) {
145 		LOG_ERR("Cannot enable VLAN for tag %d (%d)", vlan_tag, ret);
146 	}
147 
148 	LOG_DBG("Interface %d VLAN tag %d setup done.",
149 		net_if_get_by_iface(vlan_iface), vlan_tag);
150 
151 	/* Take the interface up if the setup was ok */
152 	net_if_up(vlan_iface);
153 
154 	return 0;
155 }
156 
init_vlan(void)157 int init_vlan(void)
158 {
159 	struct ud user_data;
160 	int ret;
161 
162 	if (CONFIG_NET_VLAN_COUNT == 0) {
163 		LOG_DBG("No VLAN interfaces defined.");
164 		return 0;
165 	}
166 
167 	memset(&user_data, 0, sizeof(user_data));
168 
169 	net_if_foreach(iface_cb, &user_data);
170 
171 	/* This sample has two VLANs. For the second one we need to manually
172 	 * create IP address for this test. But first the VLAN needs to be
173 	 * added to the interface so that IPv6 DAD can work properly.
174 	 */
175 	ret = setup_iface(user_data.eth, user_data.first,
176 			  CONFIG_NET_SAMPLE_COMMON_VLAN_SETUP_1);
177 	if (ret < 0) {
178 		return ret;
179 	}
180 
181 	ret = setup_iface(user_data.eth, user_data.second,
182 			  CONFIG_NET_SAMPLE_COMMON_VLAN_SETUP_2);
183 	if (ret < 0) {
184 		return ret;
185 	}
186 
187 	return 0;
188 }
189