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(¶ms.peer6addr, &addr6);
264
265 ret = net_mgmt(
266 NET_REQUEST_VIRTUAL_INTERFACE_SET_PEER_ADDRESS,
267 iface, ¶ms, 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, ¶ms, 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(¶ms.peer4addr, &addr4);
326
327 ret = net_mgmt(
328 NET_REQUEST_VIRTUAL_INTERFACE_SET_PEER_ADDRESS,
329 iface, ¶ms, 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, ¶ms,
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