1 /*
2  * Copyright (c) 2021 Intel Corporation.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/logging/log.h>
8 LOG_MODULE_REGISTER(net_virtual, CONFIG_NET_L2_VIRTUAL_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/virtual.h>
15 #include <zephyr/net/virtual_mgmt.h>
16 #include <zephyr/random/random.h>
17 
18 #include "net_private.h"
19 #include "net_stats.h"
20 
21 #define NET_BUF_TIMEOUT K_MSEC(100)
22 
virtual_recv(struct net_if * iface,struct net_pkt * pkt)23 static enum net_verdict virtual_recv(struct net_if *iface,
24 				     struct net_pkt *pkt)
25 {
26 	struct virtual_interface_context *ctx, *tmp;
27 	const struct virtual_interface_api *api;
28 	enum net_verdict verdict;
29 	sys_slist_t *interfaces;
30 
31 	interfaces = &iface->config.virtual_interfaces;
32 
33 	SYS_SLIST_FOR_EACH_CONTAINER_SAFE(interfaces, ctx, tmp, node) {
34 		if (ctx->virtual_iface == NULL) {
35 			continue;
36 		}
37 
38 		api = net_if_get_device(ctx->virtual_iface)->api;
39 		if (!api || api->recv == NULL) {
40 			continue;
41 		}
42 
43 		if (!net_if_is_up(ctx->virtual_iface)) {
44 			NET_DBG("Interface %d is down.",
45 				net_if_get_by_iface(ctx->virtual_iface));
46 			continue;
47 		}
48 
49 		verdict = api->recv(ctx->virtual_iface, pkt);
50 		if (verdict == NET_CONTINUE) {
51 			continue;
52 		}
53 
54 		if (IS_ENABLED(CONFIG_NET_STATISTICS)) {
55 			size_t pkt_len;
56 
57 			pkt_len = net_pkt_get_len(pkt);
58 
59 			NET_DBG("Received pkt %p len %zu", pkt, pkt_len);
60 
61 			net_stats_update_bytes_recv(ctx->virtual_iface,
62 						    pkt_len);
63 		}
64 
65 		if (verdict == NET_DROP) {
66 			net_stats_update_processing_error(ctx->virtual_iface);
67 		}
68 
69 		return verdict;
70 	}
71 
72 	NET_DBG("No handler, dropping pkt %p len %zu", pkt, net_pkt_get_len(pkt));
73 
74 	return NET_DROP;
75 }
76 
virtual_send(struct net_if * iface,struct net_pkt * pkt)77 static int virtual_send(struct net_if *iface, struct net_pkt *pkt)
78 {
79 	const struct virtual_interface_api *api = net_if_get_device(iface)->api;
80 	size_t pkt_len;
81 	int ret;
82 
83 	if (!api) {
84 		return -ENOENT;
85 	}
86 
87 	if (!net_if_is_up(iface)) {
88 		NET_DBG("Interface %d is down.",
89 			net_if_get_by_iface(iface));
90 		return -ENETDOWN;
91 	}
92 
93 	if (IS_ENABLED(CONFIG_NET_STATISTICS)) {
94 		pkt_len = net_pkt_get_len(pkt);
95 	}
96 
97 	/* As we are just passing data through, the net_pkt is not freed here.
98 	 */
99 	ret = api->send(iface, pkt);
100 
101 	if (IS_ENABLED(CONFIG_NET_STATISTICS) && ret == 0) {
102 		NET_DBG("Sent pkt %p len %zu", pkt, pkt_len);
103 		net_stats_update_bytes_sent(iface, pkt_len);
104 	}
105 
106 	return ret;
107 }
108 
virtual_enable(struct net_if * iface,bool state)109 static int virtual_enable(struct net_if *iface, bool state)
110 {
111 	const struct virtual_interface_api *virt;
112 	struct virtual_interface_context *ctx;
113 	int ret = 0;
114 
115 	virt = net_if_get_device(iface)->api;
116 	if (!virt) {
117 		return -ENOENT;
118 	}
119 
120 	ctx = net_if_l2_data(iface);
121 
122 	if (state) {
123 		/* Take the interfaces below this interface up as
124 		 * it does not make sense otherwise.
125 		 */
126 
127 		while (ctx->iface) {
128 			if (net_if_is_up(ctx->iface)) {
129 				/* Network interfaces below this must be up too
130 				 * so we can bail out at this point.
131 				 */
132 				break;
133 			}
134 
135 			if (net_if_l2(ctx->iface) !=
136 						&NET_L2_GET_NAME(VIRTUAL)) {
137 				net_if_up(ctx->iface);
138 				break;
139 			}
140 
141 			NET_DBG("Taking iface %d up", net_if_get_by_iface(ctx->iface));
142 
143 			net_if_up(ctx->iface);
144 			ctx = net_if_l2_data(ctx->iface);
145 		}
146 
147 		if (virt->start) {
148 			ret = virt->start(net_if_get_device(iface));
149 		}
150 
151 		return ret;
152 	}
153 
154 	if (virt->stop) {
155 		ret = virt->stop(net_if_get_device(iface));
156 	}
157 
158 	return ret;
159 }
160 
virtual_flags(struct net_if * iface)161 enum net_l2_flags virtual_flags(struct net_if *iface)
162 {
163 	struct virtual_interface_context *ctx = net_if_l2_data(iface);
164 
165 	return ctx->virtual_l2_flags;
166 }
167 
168 #if defined(CONFIG_NET_L2_ETHERNET_RESERVE_HEADER) && defined(CONFIG_NET_VLAN)
169 extern int vlan_alloc_buffer(struct net_if *iface, struct net_pkt *pkt,
170 			     size_t size, uint16_t proto, k_timeout_t timeout);
171 
virtual_l2_alloc(struct net_if * iface,struct net_pkt * pkt,size_t size,enum net_ip_protocol proto,k_timeout_t timeout)172 static int virtual_l2_alloc(struct net_if *iface, struct net_pkt *pkt,
173 			    size_t size, enum net_ip_protocol proto,
174 			    k_timeout_t timeout)
175 {
176 	return vlan_alloc_buffer(iface, pkt, size, proto, timeout);
177 }
178 #else
179 #define virtual_l2_alloc NULL
180 #endif
181 
182 NET_L2_INIT(VIRTUAL_L2, virtual_recv, virtual_send, virtual_enable,
183 	    virtual_flags, virtual_l2_alloc);
184 
random_linkaddr(uint8_t * linkaddr,size_t len)185 static void random_linkaddr(uint8_t *linkaddr, size_t len)
186 {
187 	sys_rand_get(linkaddr, len);
188 
189 	linkaddr[0] |= 0x02; /* force LAA bit */
190 }
191 
net_virtual_interface_attach(struct net_if * virtual_iface,struct net_if * iface)192 int net_virtual_interface_attach(struct net_if *virtual_iface,
193 				 struct net_if *iface)
194 {
195 	const struct virtual_interface_api *api;
196 	struct virtual_interface_context *ctx;
197 	bool up = false;
198 
199 	if (net_if_get_by_iface(virtual_iface) < 0 ||
200 	    (iface != NULL && net_if_get_by_iface(iface) < 0)) {
201 		return -EINVAL;
202 	}
203 
204 	if (virtual_iface == iface) {
205 		return -EINVAL;
206 	}
207 
208 	api = net_if_get_device(virtual_iface)->api;
209 	if (api->attach == NULL) {
210 		return -ENOENT;
211 	}
212 
213 	ctx = net_if_l2_data(virtual_iface);
214 
215 	if (ctx->iface) {
216 		if (iface != NULL) {
217 			/* We are already attached */
218 			return -EALREADY;
219 		}
220 
221 		/* Detaching, take the interface down */
222 		net_if_down(virtual_iface);
223 
224 		(void)sys_slist_find_and_remove(
225 				&ctx->iface->config.virtual_interfaces,
226 				&ctx->node);
227 
228 		NET_DBG("Detaching %d from %d",
229 			net_if_get_by_iface(virtual_iface),
230 			net_if_get_by_iface(ctx->iface));
231 
232 		ctx->iface = NULL;
233 	} else {
234 		if (iface == NULL) {
235 			/* We are already detached */
236 			return -EALREADY;
237 		}
238 
239 		/* Attaching, take the interface up if auto start is enabled.
240 		 */
241 		ctx->iface = iface;
242 		sys_slist_append(&ctx->iface->config.virtual_interfaces,
243 				 &ctx->node);
244 
245 		NET_DBG("Attaching %d to %d",
246 			net_if_get_by_iface(virtual_iface),
247 			net_if_get_by_iface(ctx->iface));
248 
249 		up = true;
250 	}
251 
252 	/* Figure out the link address for this interface. The actual link
253 	 * address is randomized. This must be done before attach is called so
254 	 * that the attach callback can create link local address for the
255 	 * network interface (if IPv6). The actual link address is typically
256 	 * not need in tunnels.
257 	 */
258 	if (iface) {
259 		random_linkaddr(ctx->lladdr.addr, sizeof(ctx->lladdr.addr));
260 
261 		ctx->lladdr.len = sizeof(ctx->lladdr.addr);
262 		ctx->lladdr.type = NET_LINK_UNKNOWN;
263 
264 		net_if_set_link_addr(virtual_iface, ctx->lladdr.addr,
265 				     ctx->lladdr.len, ctx->lladdr.type);
266 	}
267 
268 	api->attach(virtual_iface, iface);
269 
270 	if (up && !net_if_flag_is_set(virtual_iface,
271 				      NET_IF_NO_AUTO_START)) {
272 		net_if_up(virtual_iface);
273 	}
274 
275 	return 0;
276 }
277 
net_virtual_disable(struct net_if * iface)278 void net_virtual_disable(struct net_if *iface)
279 {
280 	struct virtual_interface_context *ctx, *tmp;
281 	sys_slist_t *interfaces;
282 
283 	if (net_if_get_by_iface(iface) < 0) {
284 		return;
285 	}
286 
287 	interfaces = &iface->config.virtual_interfaces;
288 	SYS_SLIST_FOR_EACH_CONTAINER_SAFE(interfaces, ctx, tmp, node) {
289 		NET_DBG("Iface %d down, setting virtual iface %d carrier off",
290 			net_if_get_by_iface(iface),
291 			net_if_get_by_iface(ctx->virtual_iface));
292 		net_if_carrier_off(ctx->virtual_iface);
293 	}
294 }
295 
net_virtual_enable(struct net_if * iface)296 void net_virtual_enable(struct net_if *iface)
297 {
298 	struct virtual_interface_context *ctx, *tmp;
299 	sys_slist_t *interfaces;
300 
301 	if (net_if_get_by_iface(iface) < 0) {
302 		return;
303 	}
304 
305 	interfaces = &iface->config.virtual_interfaces;
306 	SYS_SLIST_FOR_EACH_CONTAINER_SAFE(interfaces, ctx, tmp, node) {
307 		NET_DBG("Iface %d up, setting virtual iface %d carrier on",
308 			net_if_get_by_iface(iface),
309 			net_if_get_by_iface(ctx->virtual_iface));
310 		net_if_carrier_on(ctx->virtual_iface);
311 	}
312 }
313 
net_virtual_get_iface(struct net_if * iface)314 struct net_if *net_virtual_get_iface(struct net_if *iface)
315 {
316 	struct virtual_interface_context *ctx;
317 
318 	if (net_if_get_by_iface(iface) < 0) {
319 		return NULL;
320 	}
321 
322 	if (net_if_l2(iface) != &NET_L2_GET_NAME(VIRTUAL)) {
323 		return NULL;
324 	}
325 
326 	ctx = net_if_l2_data(iface);
327 
328 	return ctx->iface;
329 }
330 
net_virtual_get_name(struct net_if * iface,char * buf,size_t len)331 char *net_virtual_get_name(struct net_if *iface, char *buf, size_t len)
332 {
333 	struct virtual_interface_context *ctx;
334 
335 	if (net_if_get_by_iface(iface) < 0) {
336 		return NULL;
337 	}
338 
339 	if (net_if_l2(iface) != &NET_L2_GET_NAME(VIRTUAL)) {
340 		return NULL;
341 	}
342 
343 	ctx = net_if_l2_data(iface);
344 
345 	strncpy(buf, ctx->name, MIN(len, sizeof(ctx->name)));
346 	buf[len - 1] = '\0';
347 
348 	return buf;
349 }
350 
net_virtual_set_name(struct net_if * iface,const char * name)351 void net_virtual_set_name(struct net_if *iface, const char *name)
352 {
353 	struct virtual_interface_context *ctx;
354 
355 	if (net_if_get_by_iface(iface) < 0) {
356 		return;
357 	}
358 
359 	if (net_if_l2(iface) != &NET_L2_GET_NAME(VIRTUAL)) {
360 		return;
361 	}
362 
363 	ctx = net_if_l2_data(iface);
364 
365 	strncpy(ctx->name, name, CONFIG_NET_L2_VIRTUAL_MAX_NAME_LEN);
366 	ctx->name[CONFIG_NET_L2_VIRTUAL_MAX_NAME_LEN - 1] = '\0';
367 }
368 
net_virtual_set_flags(struct net_if * iface,enum net_l2_flags flags)369 enum net_l2_flags net_virtual_set_flags(struct net_if *iface,
370 					enum net_l2_flags flags)
371 {
372 	struct virtual_interface_context *ctx;
373 	enum net_l2_flags old_flags;
374 
375 	if (net_if_get_by_iface(iface) < 0) {
376 		return 0;
377 	}
378 
379 	if (net_if_l2(iface) != &NET_L2_GET_NAME(VIRTUAL)) {
380 		return 0;
381 	}
382 
383 	ctx = net_if_l2_data(iface);
384 	old_flags = ctx->virtual_l2_flags;
385 	ctx->virtual_l2_flags = flags;
386 
387 	return old_flags;
388 }
389 
net_virtual_init(struct net_if * iface)390 void net_virtual_init(struct net_if *iface)
391 {
392 	struct virtual_interface_context *ctx;
393 
394 	sys_slist_init(&iface->config.virtual_interfaces);
395 
396 	if (net_if_l2(iface) != &NET_L2_GET_NAME(VIRTUAL)) {
397 		return;
398 	}
399 
400 	ctx = net_if_l2_data(iface);
401 	if (ctx->is_init) {
402 		return;
403 	}
404 
405 	NET_DBG("Initializing virtual L2 %p for iface %d (%p)", ctx,
406 		net_if_get_by_iface(iface), iface);
407 
408 	ctx->virtual_iface = iface;
409 	ctx->virtual_l2_flags = 0;
410 	ctx->is_init = true;
411 }
412