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 	struct net_if *filtered_iface = NULL;
29 	enum net_verdict verdict;
30 	sys_slist_t *interfaces;
31 
32 	interfaces = &iface->config.virtual_interfaces;
33 
34 	SYS_SLIST_FOR_EACH_CONTAINER_SAFE(interfaces, ctx, tmp, node) {
35 		if (ctx->virtual_iface == NULL) {
36 			continue;
37 		}
38 
39 		api = net_if_get_device(ctx->virtual_iface)->api;
40 		if (!api || api->recv == NULL) {
41 			continue;
42 		}
43 
44 		if (!net_if_is_up(ctx->virtual_iface)) {
45 			NET_DBG("Interface %d is down.",
46 				net_if_get_by_iface(ctx->virtual_iface));
47 			continue;
48 		}
49 
50 		if (IS_ENABLED(CONFIG_NET_PKT_FILTER)) {
51 			struct net_if *tmp_iface;
52 
53 			tmp_iface = net_pkt_iface(pkt);
54 			net_pkt_set_iface(pkt, ctx->virtual_iface);
55 
56 			if (!net_pkt_filter_recv_ok(pkt)) {
57 				/* We cannot update the statistics here because
58 				 * the interface might not be the correct one for
59 				 * the packet. We would know that only after the
60 				 * call to api->recv() which is too late. So mark
61 				 * the interface as filtered and continue and
62 				 * update the filter statistics out of the loop.
63 				 */
64 				net_pkt_set_iface(pkt, tmp_iface);
65 				filtered_iface = tmp_iface;
66 				continue;
67 			}
68 
69 			net_pkt_set_iface(pkt, tmp_iface);
70 		}
71 
72 		verdict = api->recv(ctx->virtual_iface, pkt);
73 		if (verdict == NET_CONTINUE) {
74 			continue;
75 		}
76 
77 		if (IS_ENABLED(CONFIG_NET_STATISTICS)) {
78 			size_t pkt_len;
79 
80 			pkt_len = net_pkt_get_len(pkt);
81 
82 			NET_DBG("Received pkt %p len %zu", pkt, pkt_len);
83 
84 			net_stats_update_bytes_recv(ctx->virtual_iface,
85 						    pkt_len);
86 		}
87 
88 		if (verdict == NET_DROP) {
89 			net_stats_update_processing_error(ctx->virtual_iface);
90 		}
91 
92 		return verdict;
93 	}
94 
95 	if (IS_ENABLED(CONFIG_NET_PKT_FILTER) && filtered_iface != NULL) {
96 		/* We need to update the statistics for the filtered iface here.
97 		 */
98 		net_stats_update_filter_rx_drop(filtered_iface);
99 		goto silent_drop;
100 	}
101 
102 	/* If there are no virtual interfaces attached, then pass the packet
103 	 * to the actual virtual network interface.
104 	 */
105 	api = net_if_get_device(iface)->api;
106 	if (!api || api->recv == NULL) {
107 		goto drop;
108 	}
109 
110 	if (!net_if_is_up(iface)) {
111 		NET_DBG("Interface %d is down.", net_if_get_by_iface(iface));
112 		goto silent_drop;
113 	}
114 
115 	if (!net_pkt_filter_recv_ok(pkt)) {
116 		net_stats_update_filter_rx_drop(iface);
117 		goto silent_drop;
118 	}
119 
120 	verdict = api->recv(iface, pkt);
121 
122 	if (IS_ENABLED(CONFIG_NET_STATISTICS)) {
123 		size_t pkt_len;
124 
125 		pkt_len = net_pkt_get_len(pkt);
126 
127 		NET_DBG("Received pkt %p len %zu", pkt, pkt_len);
128 
129 		net_stats_update_bytes_recv(iface, pkt_len);
130 	}
131 
132 	if (verdict == NET_DROP) {
133 		net_stats_update_processing_error(iface);
134 	}
135 
136 	return verdict;
137 
138 drop:
139 	NET_DBG("No handler, dropping pkt %p len %zu", pkt, net_pkt_get_len(pkt));
140 
141 silent_drop:
142 	return NET_DROP;
143 }
144 
virtual_send(struct net_if * iface,struct net_pkt * pkt)145 static int virtual_send(struct net_if *iface, struct net_pkt *pkt)
146 {
147 	const struct virtual_interface_api *api = net_if_get_device(iface)->api;
148 	size_t pkt_len;
149 	int ret;
150 
151 	if (!api) {
152 		return -ENOENT;
153 	}
154 
155 	if (!net_if_is_up(iface)) {
156 		NET_DBG("Interface %d is down.",
157 			net_if_get_by_iface(iface));
158 		return -ENETDOWN;
159 	}
160 
161 	if (IS_ENABLED(CONFIG_NET_STATISTICS)) {
162 		pkt_len = net_pkt_get_len(pkt);
163 	}
164 
165 	/* As we are just passing data through, the net_pkt is not freed here.
166 	 */
167 	ret = api->send(iface, pkt);
168 
169 	if (IS_ENABLED(CONFIG_NET_STATISTICS) && ret == 0) {
170 		NET_DBG("Sent pkt %p len %zu", pkt, pkt_len);
171 		net_stats_update_bytes_sent(iface, pkt_len);
172 	}
173 
174 	return ret;
175 }
176 
virtual_enable(struct net_if * iface,bool state)177 static int virtual_enable(struct net_if *iface, bool state)
178 {
179 	const struct virtual_interface_api *virt;
180 	struct virtual_interface_context *ctx;
181 	int ret = 0;
182 
183 	virt = net_if_get_device(iface)->api;
184 	if (!virt) {
185 		return -ENOENT;
186 	}
187 
188 	ctx = net_if_l2_data(iface);
189 
190 	if (state) {
191 		/* Take the interfaces below this interface up as
192 		 * it does not make sense otherwise.
193 		 */
194 
195 		while (ctx->iface) {
196 			if (net_if_is_up(ctx->iface)) {
197 				/* Network interfaces below this must be up too
198 				 * so we can bail out at this point.
199 				 */
200 				break;
201 			}
202 
203 			if (net_if_l2(ctx->iface) !=
204 						&NET_L2_GET_NAME(VIRTUAL)) {
205 				net_if_up(ctx->iface);
206 				break;
207 			}
208 
209 			NET_DBG("Taking iface %d up", net_if_get_by_iface(ctx->iface));
210 
211 			net_if_up(ctx->iface);
212 			ctx = net_if_l2_data(ctx->iface);
213 		}
214 
215 		if (virt->start) {
216 			ret = virt->start(net_if_get_device(iface));
217 		}
218 
219 		return ret;
220 	}
221 
222 	if (virt->stop) {
223 		ret = virt->stop(net_if_get_device(iface));
224 	}
225 
226 	return ret;
227 }
228 
virtual_flags(struct net_if * iface)229 enum net_l2_flags virtual_flags(struct net_if *iface)
230 {
231 	struct virtual_interface_context *ctx = net_if_l2_data(iface);
232 
233 	return ctx->virtual_l2_flags;
234 }
235 
236 #if defined(CONFIG_NET_L2_ETHERNET_RESERVE_HEADER) && defined(CONFIG_NET_VLAN)
237 extern int vlan_alloc_buffer(struct net_if *iface, struct net_pkt *pkt,
238 			     size_t size, uint16_t proto, k_timeout_t timeout);
239 
virtual_l2_alloc(struct net_if * iface,struct net_pkt * pkt,size_t size,enum net_ip_protocol proto,k_timeout_t timeout)240 static int virtual_l2_alloc(struct net_if *iface, struct net_pkt *pkt,
241 			    size_t size, enum net_ip_protocol proto,
242 			    k_timeout_t timeout)
243 {
244 	return vlan_alloc_buffer(iface, pkt, size, proto, timeout);
245 }
246 #else
247 #define virtual_l2_alloc NULL
248 #endif
249 
250 NET_L2_INIT(VIRTUAL_L2, virtual_recv, virtual_send, virtual_enable,
251 	    virtual_flags, virtual_l2_alloc);
252 
random_linkaddr(uint8_t * linkaddr,size_t len)253 static void random_linkaddr(uint8_t *linkaddr, size_t len)
254 {
255 	sys_rand_get(linkaddr, len);
256 
257 	linkaddr[0] |= 0x02;  /* force LAA bit */
258 	linkaddr[0] &= ~0x01; /* clear multicast bit */
259 }
260 
net_virtual_interface_attach(struct net_if * virtual_iface,struct net_if * iface)261 int net_virtual_interface_attach(struct net_if *virtual_iface,
262 				 struct net_if *iface)
263 {
264 	const struct virtual_interface_api *api;
265 	struct virtual_interface_context *ctx;
266 	bool up = false;
267 
268 	if (net_if_get_by_iface(virtual_iface) < 0 ||
269 	    (iface != NULL && net_if_get_by_iface(iface) < 0)) {
270 		return -EINVAL;
271 	}
272 
273 	if (virtual_iface == iface) {
274 		return -EINVAL;
275 	}
276 
277 	api = net_if_get_device(virtual_iface)->api;
278 	if (api->attach == NULL) {
279 		return -ENOENT;
280 	}
281 
282 	ctx = net_if_l2_data(virtual_iface);
283 
284 	if (ctx->iface) {
285 		if (iface != NULL) {
286 			/* We are already attached */
287 			return -EALREADY;
288 		}
289 
290 		/* Detaching, take the interface down */
291 		net_if_down(virtual_iface);
292 
293 		(void)sys_slist_find_and_remove(
294 				&ctx->iface->config.virtual_interfaces,
295 				&ctx->node);
296 
297 		NET_DBG("Detaching %d from %d",
298 			net_if_get_by_iface(virtual_iface),
299 			net_if_get_by_iface(ctx->iface));
300 
301 		ctx->iface = NULL;
302 	} else {
303 		if (iface == NULL) {
304 			/* We are already detached */
305 			return -EALREADY;
306 		}
307 
308 		/* Attaching, take the interface up if auto start is enabled.
309 		 */
310 		ctx->iface = iface;
311 		sys_slist_append(&ctx->iface->config.virtual_interfaces,
312 				 &ctx->node);
313 
314 		NET_DBG("Attaching %d to %d",
315 			net_if_get_by_iface(virtual_iface),
316 			net_if_get_by_iface(ctx->iface));
317 
318 		up = true;
319 	}
320 
321 	/* Figure out the link address for this interface. The actual link
322 	 * address is randomized. This must be done before attach is called so
323 	 * that the attach callback can create link local address for the
324 	 * network interface (if IPv6). The actual link address is typically
325 	 * not need in tunnels.
326 	 */
327 	if (iface) {
328 		random_linkaddr(ctx->lladdr.addr, sizeof(ctx->lladdr.addr));
329 
330 		ctx->lladdr.len = sizeof(ctx->lladdr.addr);
331 		ctx->lladdr.type = NET_LINK_UNKNOWN;
332 
333 		net_if_set_link_addr(virtual_iface, ctx->lladdr.addr,
334 				     ctx->lladdr.len, ctx->lladdr.type);
335 	}
336 
337 	api->attach(virtual_iface, iface);
338 
339 	if (up && !net_if_flag_is_set(virtual_iface,
340 				      NET_IF_NO_AUTO_START)) {
341 		net_if_up(virtual_iface);
342 	}
343 
344 	return 0;
345 }
346 
net_virtual_disable(struct net_if * iface)347 void net_virtual_disable(struct net_if *iface)
348 {
349 	struct virtual_interface_context *ctx, *tmp;
350 	sys_slist_t *interfaces;
351 
352 	if (net_if_get_by_iface(iface) < 0) {
353 		return;
354 	}
355 
356 	interfaces = &iface->config.virtual_interfaces;
357 	SYS_SLIST_FOR_EACH_CONTAINER_SAFE(interfaces, ctx, tmp, node) {
358 		NET_DBG("Iface %d down, setting virtual iface %d carrier off",
359 			net_if_get_by_iface(iface),
360 			net_if_get_by_iface(ctx->virtual_iface));
361 		net_if_carrier_off(ctx->virtual_iface);
362 	}
363 }
364 
net_virtual_enable(struct net_if * iface)365 void net_virtual_enable(struct net_if *iface)
366 {
367 	struct virtual_interface_context *ctx, *tmp;
368 	sys_slist_t *interfaces;
369 
370 	if (net_if_get_by_iface(iface) < 0) {
371 		return;
372 	}
373 
374 	interfaces = &iface->config.virtual_interfaces;
375 	SYS_SLIST_FOR_EACH_CONTAINER_SAFE(interfaces, ctx, tmp, node) {
376 		NET_DBG("Iface %d up, setting virtual iface %d carrier on",
377 			net_if_get_by_iface(iface),
378 			net_if_get_by_iface(ctx->virtual_iface));
379 		net_if_carrier_on(ctx->virtual_iface);
380 	}
381 }
382 
net_virtual_get_iface(struct net_if * iface)383 struct net_if *net_virtual_get_iface(struct net_if *iface)
384 {
385 	struct virtual_interface_context *ctx;
386 
387 	if (net_if_get_by_iface(iface) < 0) {
388 		return NULL;
389 	}
390 
391 	if (net_if_l2(iface) != &NET_L2_GET_NAME(VIRTUAL)) {
392 		return NULL;
393 	}
394 
395 	ctx = net_if_l2_data(iface);
396 
397 	return ctx->iface;
398 }
399 
net_virtual_get_name(struct net_if * iface,char * buf,size_t len)400 char *net_virtual_get_name(struct net_if *iface, char *buf, size_t len)
401 {
402 	struct virtual_interface_context *ctx;
403 
404 	if (net_if_get_by_iface(iface) < 0) {
405 		return NULL;
406 	}
407 
408 	if (net_if_l2(iface) != &NET_L2_GET_NAME(VIRTUAL)) {
409 		return NULL;
410 	}
411 
412 	ctx = net_if_l2_data(iface);
413 
414 	strncpy(buf, ctx->name, MIN(len, sizeof(ctx->name)));
415 	buf[len - 1] = '\0';
416 
417 	return buf;
418 }
419 
net_virtual_set_name(struct net_if * iface,const char * name)420 void net_virtual_set_name(struct net_if *iface, const char *name)
421 {
422 	struct virtual_interface_context *ctx;
423 
424 	if (net_if_get_by_iface(iface) < 0) {
425 		return;
426 	}
427 
428 	if (net_if_l2(iface) != &NET_L2_GET_NAME(VIRTUAL)) {
429 		return;
430 	}
431 
432 	ctx = net_if_l2_data(iface);
433 
434 	strncpy(ctx->name, name, ARRAY_SIZE(ctx->name) - 1);
435 	ctx->name[ARRAY_SIZE(ctx->name) - 1] = '\0';
436 }
437 
net_virtual_set_flags(struct net_if * iface,enum net_l2_flags flags)438 enum net_l2_flags net_virtual_set_flags(struct net_if *iface,
439 					enum net_l2_flags flags)
440 {
441 	struct virtual_interface_context *ctx;
442 	enum net_l2_flags old_flags;
443 
444 	if (net_if_get_by_iface(iface) < 0) {
445 		return 0;
446 	}
447 
448 	if (net_if_l2(iface) != &NET_L2_GET_NAME(VIRTUAL)) {
449 		return 0;
450 	}
451 
452 	ctx = net_if_l2_data(iface);
453 	old_flags = ctx->virtual_l2_flags;
454 	ctx->virtual_l2_flags = flags;
455 
456 	return old_flags;
457 }
458 
net_virtual_init(struct net_if * iface)459 void net_virtual_init(struct net_if *iface)
460 {
461 	struct virtual_interface_context *ctx;
462 
463 	sys_slist_init(&iface->config.virtual_interfaces);
464 
465 	if (net_if_l2(iface) != &NET_L2_GET_NAME(VIRTUAL)) {
466 		return;
467 	}
468 
469 	ctx = net_if_l2_data(iface);
470 	if (ctx->is_init) {
471 		return;
472 	}
473 
474 	NET_DBG("Initializing virtual L2 %p for iface %d (%p)", ctx,
475 		net_if_get_by_iface(iface), iface);
476 
477 	ctx->virtual_iface = iface;
478 	ctx->virtual_l2_flags = 0;
479 	ctx->is_init = true;
480 }
481