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_interface_sample, LOG_LEVEL_DBG);
9 
10 #include <zephyr/kernel.h>
11 #include <zephyr/device.h>
12 #include <errno.h>
13 
14 #include <zephyr/net/net_core.h>
15 #include <zephyr/net/ethernet.h>
16 #include <zephyr/net/virtual.h>
17 #include <zephyr/net/virtual_mgmt.h>
18 
19 /* User data for the interface callback */
20 struct ud {
21 	struct net_if *my_iface;
22 	struct net_if *ethernet;
23 	struct net_if *ip_tunnel_1;
24 	struct net_if *ip_tunnel_2;
25 	struct net_if *ipip;
26 };
27 
28 /* The MTU value here is just an arbitrary number for testing purposes */
29 #define VIRTUAL_TEST_MTU 1024
30 
31 #define VIRTUAL_TEST "VIRTUAL_TEST"
32 #define VIRTUAL_TEST2 "VIRTUAL_TEST2"
33 #define VIRTUAL_TEST3 "VIRTUAL_TEST3"
34 static const char *dev_name = VIRTUAL_TEST;
35 static const char *ipip_dev_name = "IP_tunnel";
36 
37 struct virtual_test_context {
38 	struct net_if *iface;
39 	struct net_if *attached_to;
40 	bool status;
41 	bool init_done;
42 };
43 
virtual_test_iface_init(struct net_if * iface)44 static void virtual_test_iface_init(struct net_if *iface)
45 {
46 	struct virtual_test_context *ctx = net_if_get_device(iface)->data;
47 	char name[sizeof("VirtualTest-+##########")];
48 	static int count;
49 
50 	if (ctx->init_done) {
51 		return;
52 	}
53 
54 	ctx->iface = iface;
55 	net_if_flag_set(iface, NET_IF_NO_AUTO_START);
56 
57 	snprintk(name, sizeof(name), "VirtualTest-%d", count + 1);
58 	count++;
59 	net_virtual_set_name(iface, name);
60 	(void)net_virtual_set_flags(iface, NET_L2_POINT_TO_POINT);
61 
62 	ctx->init_done = true;
63 }
64 
65 static struct virtual_test_context virtual_test_context_data1 = {
66 };
67 
68 static struct virtual_test_context virtual_test_context_data2 = {
69 };
70 
71 static struct virtual_test_context virtual_test_context_data3 = {
72 };
73 
74 static enum virtual_interface_caps
virtual_test_get_capabilities(struct net_if * iface)75 virtual_test_get_capabilities(struct net_if *iface)
76 {
77 	ARG_UNUSED(iface);
78 
79 	return (enum virtual_interface_caps)0;
80 }
81 
virtual_test_interface_start(const struct device * dev)82 static int virtual_test_interface_start(const struct device *dev)
83 {
84 	struct virtual_test_context *ctx = dev->data;
85 
86 	if (ctx->status) {
87 		return -EALREADY;
88 	}
89 
90 	ctx->status = true;
91 
92 	LOG_DBG("Starting iface %d", net_if_get_by_iface(ctx->iface));
93 
94 	/* You can implement here any special action that is needed
95 	 * when the network interface is coming up.
96 	 */
97 
98 	return 0;
99 }
100 
virtual_test_interface_stop(const struct device * dev)101 static int virtual_test_interface_stop(const struct device *dev)
102 {
103 	struct virtual_test_context *ctx = dev->data;
104 
105 	if (!ctx->status) {
106 		return -EALREADY;
107 	}
108 
109 	ctx->status = false;
110 
111 	LOG_DBG("Stopping iface %d", net_if_get_by_iface(ctx->iface));
112 
113 	/* You can implement here any special action that is needed
114 	 * when the network interface is going down.
115 	 */
116 
117 	return 0;
118 }
119 
virtual_test_interface_send(struct net_if * iface,struct net_pkt * pkt)120 static int virtual_test_interface_send(struct net_if *iface,
121 				       struct net_pkt *pkt)
122 {
123 	struct virtual_test_context *ctx = net_if_get_device(iface)->data;
124 
125 	if (ctx->attached_to == NULL) {
126 		return -ENOENT;
127 	}
128 
129 	return net_send_data(pkt);
130 }
131 
virtual_test_interface_recv(struct net_if * iface,struct net_pkt * pkt)132 static enum net_verdict virtual_test_interface_recv(struct net_if *iface,
133 						    struct net_pkt *pkt)
134 {
135 	ARG_UNUSED(iface);
136 	ARG_UNUSED(pkt);
137 
138 	return NET_CONTINUE;
139 }
140 
virtual_test_interface_attach(struct net_if * virtual_iface,struct net_if * iface)141 static int virtual_test_interface_attach(struct net_if *virtual_iface,
142 					 struct net_if *iface)
143 {
144 	struct virtual_test_context *ctx = net_if_get_device(virtual_iface)->data;
145 
146 	LOG_INF("This interface %d/%p attached to %d/%p",
147 		net_if_get_by_iface(virtual_iface), virtual_iface,
148 		net_if_get_by_iface(iface), iface);
149 
150 	ctx->attached_to = iface;
151 
152 	return 0;
153 }
154 
155 static const struct virtual_interface_api virtual_test_iface_api = {
156 	.iface_api.init = virtual_test_iface_init,
157 
158 	.get_capabilities = virtual_test_get_capabilities,
159 	.start = virtual_test_interface_start,
160 	.stop = virtual_test_interface_stop,
161 	.send = virtual_test_interface_send,
162 	.recv = virtual_test_interface_recv,
163 	.attach = virtual_test_interface_attach,
164 };
165 
166 NET_VIRTUAL_INTERFACE_INIT(virtual_test1, VIRTUAL_TEST, NULL, NULL,
167 			   &virtual_test_context_data1,
168 			   NULL,
169 			   CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,
170 			   &virtual_test_iface_api,
171 			   VIRTUAL_TEST_MTU);
172 
173 NET_VIRTUAL_INTERFACE_INIT(virtual_test2, VIRTUAL_TEST2, NULL, NULL,
174 			   &virtual_test_context_data2,
175 			   NULL,
176 			   CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,
177 			   &virtual_test_iface_api,
178 			   VIRTUAL_TEST_MTU);
179 
180 NET_VIRTUAL_INTERFACE_INIT(virtual_test3, VIRTUAL_TEST3, NULL, NULL,
181 			   &virtual_test_context_data3,
182 			   NULL,
183 			   CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,
184 			   &virtual_test_iface_api,
185 			   VIRTUAL_TEST_MTU);
186 
iface_cb(struct net_if * iface,void * user_data)187 static void iface_cb(struct net_if *iface, void *user_data)
188 {
189 	struct ud *ud = user_data;
190 
191 	if ((net_if_l2(iface) != &NET_L2_GET_NAME(ETHERNET)) &&
192 	    (net_if_l2(iface) != &NET_L2_GET_NAME(VIRTUAL))) {
193 		return;
194 	}
195 
196 	if (!ud->ethernet && net_if_l2(iface) == &NET_L2_GET_NAME(ETHERNET)) {
197 		ud->ethernet = iface;
198 		return;
199 	}
200 
201 	if (!ud->ipip && net_if_l2(iface) == &NET_L2_GET_NAME(VIRTUAL) &&
202 	    net_if_get_device(iface) == device_get_binding(ipip_dev_name)) {
203 		ud->ipip = iface;
204 		return;
205 	}
206 
207 	if (!ud->my_iface && net_if_l2(iface) == &NET_L2_GET_NAME(VIRTUAL) &&
208 	    net_if_get_device(iface)->data == &virtual_test_context_data1) {
209 		ud->my_iface = iface;
210 		return;
211 	}
212 
213 	if (!ud->ip_tunnel_1 && net_if_l2(iface) == &NET_L2_GET_NAME(VIRTUAL)) {
214 		ud->ip_tunnel_1 = iface;
215 		return;
216 	}
217 
218 	if (!ud->ip_tunnel_2 && net_if_l2(iface) == &NET_L2_GET_NAME(VIRTUAL)) {
219 		ud->ip_tunnel_2 = iface;
220 		return;
221 	}
222 }
223 
setup_iface(struct net_if * iface,const char * ipv6_addr,const char * ipv4_addr,const char * peer6addr,const char * peer4addr,const char * netmask)224 static int setup_iface(struct net_if *iface,
225 		       const char *ipv6_addr,
226 		       const char *ipv4_addr,
227 		       const char *peer6addr,
228 		       const char *peer4addr,
229 		       const char *netmask)
230 {
231 	struct virtual_interface_req_params params = { 0 };
232 	struct net_if_addr *ifaddr;
233 	struct in_addr addr4;
234 	struct in6_addr addr6;
235 	int ret;
236 
237 	if (IS_ENABLED(CONFIG_NET_IPV6) &&
238 	    net_if_flag_is_set(iface, NET_IF_IPV6)) {
239 
240 		if (net_addr_pton(AF_INET6, ipv6_addr, &addr6)) {
241 			LOG_ERR("Invalid address: %s", ipv6_addr);
242 			return -EINVAL;
243 		}
244 
245 		ifaddr = net_if_ipv6_addr_add(iface, &addr6,
246 					      NET_ADDR_MANUAL, 0);
247 		if (!ifaddr) {
248 			LOG_ERR("Cannot add %s to interface %p",
249 				ipv6_addr, iface);
250 			return -EINVAL;
251 		}
252 
253 		if (!peer6addr || *peer6addr == '\0') {
254 			goto try_ipv4;
255 		}
256 
257 		params.family = AF_INET6;
258 
259 		if (net_addr_pton(AF_INET6, peer6addr, &addr6)) {
260 			LOG_ERR("Cannot parse peer %s address %s to tunnel",
261 				"IPv6", peer6addr);
262 		} else {
263 			net_ipaddr_copy(&params.peer6addr, &addr6);
264 
265 			ret = net_mgmt(
266 				NET_REQUEST_VIRTUAL_INTERFACE_SET_PEER_ADDRESS,
267 				iface, &params, sizeof(params));
268 			if (ret < 0 && ret != -ENOTSUP) {
269 				LOG_ERR("Cannot set peer %s address %s to "
270 					"interface %d (%d)",
271 					"IPv6", peer6addr,
272 					net_if_get_by_iface(iface),
273 					ret);
274 			}
275 		}
276 
277 		params.mtu = NET_ETH_MTU - sizeof(struct net_ipv6_hdr);
278 
279 		ret = net_mgmt(NET_REQUEST_VIRTUAL_INTERFACE_SET_MTU,
280 			       iface, &params, sizeof(params));
281 		if (ret < 0 && ret != -ENOTSUP) {
282 			LOG_ERR("Cannot set interface %d MTU to %d (%d)",
283 				net_if_get_by_iface(iface), params.mtu, ret);
284 		}
285 	}
286 
287 try_ipv4:
288 	if (IS_ENABLED(CONFIG_NET_IPV4) &&
289 	    net_if_flag_is_set(iface, NET_IF_IPV4)) {
290 
291 		if (net_addr_pton(AF_INET, ipv4_addr, &addr4)) {
292 			LOG_ERR("Invalid address: %s", ipv4_addr);
293 			return -EINVAL;
294 		}
295 
296 		ifaddr = net_if_ipv4_addr_add(iface, &addr4,
297 					      NET_ADDR_MANUAL, 0);
298 		if (!ifaddr) {
299 			LOG_ERR("Cannot add %s to interface %p",
300 				ipv4_addr, iface);
301 			return -EINVAL;
302 		}
303 
304 		if (netmask) {
305 			struct in_addr nm;
306 
307 			if (net_addr_pton(AF_INET, netmask, &nm)) {
308 				LOG_ERR("Invalid netmask: %s", netmask);
309 				return -EINVAL;
310 			}
311 
312 			net_if_ipv4_set_netmask_by_addr(iface, &addr4, &nm);
313 		}
314 
315 		if (!peer4addr || *peer4addr == '\0') {
316 			goto done;
317 		}
318 
319 		params.family = AF_INET;
320 
321 		if (net_addr_pton(AF_INET, peer4addr, &addr4)) {
322 			LOG_ERR("Cannot parse peer %s address %s to tunnel",
323 				"IPv4", peer4addr);
324 		} else {
325 			net_ipaddr_copy(&params.peer4addr, &addr4);
326 
327 			ret = net_mgmt(
328 				NET_REQUEST_VIRTUAL_INTERFACE_SET_PEER_ADDRESS,
329 				iface, &params, sizeof(params));
330 			if (ret < 0 && ret != -ENOTSUP) {
331 				LOG_ERR("Cannot set peer %s address %s to "
332 					"interface %d (%d)",
333 					"IPv4", peer4addr,
334 					net_if_get_by_iface(iface),
335 					ret);
336 			}
337 		}
338 
339 		params.mtu = NET_ETH_MTU - sizeof(struct net_ipv4_hdr);
340 
341 		ret = net_mgmt(NET_REQUEST_VIRTUAL_INTERFACE_SET_MTU,
342 			       iface, &params,
343 			       sizeof(struct virtual_interface_req_params));
344 		if (ret < 0 && ret != -ENOTSUP) {
345 			LOG_ERR("Cannot set interface %d MTU to %d (%d)",
346 				net_if_get_by_iface(iface), params.mtu, ret);
347 		}
348 	}
349 
350 done:
351 	return 0;
352 }
353 
main(void)354 int main(void)
355 {
356 #define MAX_NAME_LEN 32
357 	char buf[MAX_NAME_LEN];
358 	struct ud ud;
359 	int ret;
360 
361 	LOG_INF("Start application (dev %s/%p)", dev_name,
362 		device_get_binding(dev_name));
363 
364 	memset(&ud, 0, sizeof(ud));
365 	net_if_foreach(iface_cb, &ud);
366 
367 	LOG_INF("My example tunnel interface %d (%s / %p)",
368 		net_if_get_by_iface(ud.my_iface),
369 		net_virtual_get_name(ud.my_iface, buf, sizeof(buf)),
370 		ud.my_iface);
371 	LOG_INF("Tunnel interface %d (%s / %p)",
372 		net_if_get_by_iface(ud.ip_tunnel_1),
373 		net_virtual_get_name(ud.ip_tunnel_1, buf, sizeof(buf)),
374 		ud.ip_tunnel_1);
375 	LOG_INF("Tunnel interface %d (%s / %p)",
376 		net_if_get_by_iface(ud.ip_tunnel_2),
377 		net_virtual_get_name(ud.ip_tunnel_2, buf, sizeof(buf)),
378 		ud.ip_tunnel_2);
379 	LOG_INF("IPIP interface %d (%p)",
380 		net_if_get_by_iface(ud.ipip), ud.ipip);
381 	LOG_INF("Ethernet interface %d (%p)",
382 		net_if_get_by_iface(ud.ethernet), ud.ethernet);
383 
384 	/* Attach the network interfaces on top of the Ethernet interface */
385 	net_virtual_interface_attach(ud.ip_tunnel_1, ud.ethernet);
386 
387 	/* Attach our example virtual interface on top of the IPv4 one.
388 	 * This is just an example how to stack the interface on top of
389 	 * each other.
390 	 */
391 	net_virtual_interface_attach(ud.my_iface, ud.ip_tunnel_1);
392 	net_virtual_interface_attach(ud.my_iface, ud.ip_tunnel_2);
393 
394 	ret = setup_iface(ud.my_iface,
395 			  CONFIG_NET_SAMPLE_IFACE3_MY_IPV6_ADDR,
396 			  CONFIG_NET_SAMPLE_IFACE3_MY_IPV4_ADDR,
397 			  NULL, NULL,
398 			  CONFIG_NET_SAMPLE_IFACE3_MY_IPV4_NETMASK);
399 	if (ret < 0) {
400 		LOG_ERR("Cannot set IP address to test interface");
401 	}
402 
403 	if (ud.ethernet) {
404 		ret = setup_iface(ud.ethernet,
405 				  CONFIG_NET_CONFIG_MY_IPV6_ADDR,
406 				  CONFIG_NET_CONFIG_MY_IPV4_ADDR,
407 				  NULL, NULL,
408 				  CONFIG_NET_CONFIG_MY_IPV4_NETMASK);
409 		if (ret < 0) {
410 			LOG_ERR("Cannot set IP address to Ethernet interface");
411 		}
412 	}
413 
414 	ret = setup_iface(ud.ipip,
415 			  CONFIG_NET_SAMPLE_IFACE2_MY_IPV6_ADDR,
416 			  CONFIG_NET_SAMPLE_IFACE2_MY_IPV4_ADDR,
417 			  CONFIG_NET_CONFIG_PEER_IPV6_ADDR,
418 			  CONFIG_NET_CONFIG_PEER_IPV4_ADDR,
419 			  CONFIG_NET_SAMPLE_IFACE2_MY_IPV4_NETMASK);
420 	if (ret < 0) {
421 		LOG_ERR("Cannot set IP address to IPIP tunnel");
422 	}
423 
424 	/* This sample application does nothing itself. You can use
425 	 * net-shell to send ping or UDP/TCP packets for testing
426 	 * purposes.
427 	 */
428 	return 0;
429 }
430