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