/* * Copyright (c) 2021 Intel Corporation * * SPDX-License-Identifier: Apache-2.0 */ #include LOG_MODULE_REGISTER(net_virtual_interface_sample, LOG_LEVEL_DBG); #include #include #include #include #include #include #include /* User data for the interface callback */ struct ud { struct net_if *my_iface; struct net_if *ethernet; struct net_if *ip_tunnel_1; struct net_if *ip_tunnel_2; struct net_if *ipip; }; /* The MTU value here is just an arbitrary number for testing purposes */ #define VIRTUAL_TEST_MTU 1024 #define VIRTUAL_TEST "VIRTUAL_TEST" #define VIRTUAL_TEST2 "VIRTUAL_TEST2" #define VIRTUAL_TEST3 "VIRTUAL_TEST3" static const char *dev_name = VIRTUAL_TEST; static const char *ipip_dev_name = "IP_tunnel"; struct virtual_test_context { struct net_if *iface; struct net_if *attached_to; bool status; bool init_done; }; static void virtual_test_iface_init(struct net_if *iface) { struct virtual_test_context *ctx = net_if_get_device(iface)->data; char name[sizeof("VirtualTest-+##########")]; static int count; if (ctx->init_done) { return; } ctx->iface = iface; net_if_flag_set(iface, NET_IF_NO_AUTO_START); snprintk(name, sizeof(name), "VirtualTest-%d", count + 1); count++; net_virtual_set_name(iface, name); (void)net_virtual_set_flags(iface, NET_L2_POINT_TO_POINT); ctx->init_done = true; } static struct virtual_test_context virtual_test_context_data1 = { }; static struct virtual_test_context virtual_test_context_data2 = { }; static struct virtual_test_context virtual_test_context_data3 = { }; static enum virtual_interface_caps virtual_test_get_capabilities(struct net_if *iface) { ARG_UNUSED(iface); return (enum virtual_interface_caps)0; } static int virtual_test_interface_start(const struct device *dev) { struct virtual_test_context *ctx = dev->data; if (ctx->status) { return -EALREADY; } ctx->status = true; LOG_DBG("Starting iface %d", net_if_get_by_iface(ctx->iface)); /* You can implement here any special action that is needed * when the network interface is coming up. */ return 0; } static int virtual_test_interface_stop(const struct device *dev) { struct virtual_test_context *ctx = dev->data; if (!ctx->status) { return -EALREADY; } ctx->status = false; LOG_DBG("Stopping iface %d", net_if_get_by_iface(ctx->iface)); /* You can implement here any special action that is needed * when the network interface is going down. */ return 0; } static int virtual_test_interface_send(struct net_if *iface, struct net_pkt *pkt) { struct virtual_test_context *ctx = net_if_get_device(iface)->data; if (ctx->attached_to == NULL) { return -ENOENT; } return net_send_data(pkt); } static enum net_verdict virtual_test_interface_recv(struct net_if *iface, struct net_pkt *pkt) { ARG_UNUSED(iface); ARG_UNUSED(pkt); return NET_CONTINUE; } static int virtual_test_interface_attach(struct net_if *virtual_iface, struct net_if *iface) { struct virtual_test_context *ctx = net_if_get_device(virtual_iface)->data; LOG_INF("This interface %d/%p attached to %d/%p", net_if_get_by_iface(virtual_iface), virtual_iface, net_if_get_by_iface(iface), iface); ctx->attached_to = iface; return 0; } static const struct virtual_interface_api virtual_test_iface_api = { .iface_api.init = virtual_test_iface_init, .get_capabilities = virtual_test_get_capabilities, .start = virtual_test_interface_start, .stop = virtual_test_interface_stop, .send = virtual_test_interface_send, .recv = virtual_test_interface_recv, .attach = virtual_test_interface_attach, }; NET_VIRTUAL_INTERFACE_INIT(virtual_test1, VIRTUAL_TEST, NULL, NULL, &virtual_test_context_data1, NULL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &virtual_test_iface_api, VIRTUAL_TEST_MTU); NET_VIRTUAL_INTERFACE_INIT(virtual_test2, VIRTUAL_TEST2, NULL, NULL, &virtual_test_context_data2, NULL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &virtual_test_iface_api, VIRTUAL_TEST_MTU); NET_VIRTUAL_INTERFACE_INIT(virtual_test3, VIRTUAL_TEST3, NULL, NULL, &virtual_test_context_data3, NULL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &virtual_test_iface_api, VIRTUAL_TEST_MTU); static void iface_cb(struct net_if *iface, void *user_data) { struct ud *ud = user_data; if ((net_if_l2(iface) != &NET_L2_GET_NAME(ETHERNET)) && (net_if_l2(iface) != &NET_L2_GET_NAME(VIRTUAL))) { return; } if (!ud->ethernet && net_if_l2(iface) == &NET_L2_GET_NAME(ETHERNET)) { ud->ethernet = iface; return; } if (!ud->ipip && net_if_l2(iface) == &NET_L2_GET_NAME(VIRTUAL) && net_if_get_device(iface) == device_get_binding(ipip_dev_name)) { ud->ipip = iface; return; } if (!ud->my_iface && net_if_l2(iface) == &NET_L2_GET_NAME(VIRTUAL) && net_if_get_device(iface)->data == &virtual_test_context_data1) { ud->my_iface = iface; return; } if (!ud->ip_tunnel_1 && net_if_l2(iface) == &NET_L2_GET_NAME(VIRTUAL)) { ud->ip_tunnel_1 = iface; return; } if (!ud->ip_tunnel_2 && net_if_l2(iface) == &NET_L2_GET_NAME(VIRTUAL)) { ud->ip_tunnel_2 = iface; return; } } static int setup_iface(struct net_if *iface, const char *ipv6_addr, const char *ipv4_addr, const char *peer6addr, const char *peer4addr, const char *netmask) { struct virtual_interface_req_params params = { 0 }; struct net_if_addr *ifaddr; struct in_addr addr4; struct in6_addr addr6; int ret; if (IS_ENABLED(CONFIG_NET_IPV6) && net_if_flag_is_set(iface, NET_IF_IPV6)) { if (net_addr_pton(AF_INET6, ipv6_addr, &addr6)) { LOG_ERR("Invalid address: %s", ipv6_addr); return -EINVAL; } ifaddr = net_if_ipv6_addr_add(iface, &addr6, NET_ADDR_MANUAL, 0); if (!ifaddr) { LOG_ERR("Cannot add %s to interface %p", ipv6_addr, iface); return -EINVAL; } if (!peer6addr || *peer6addr == '\0') { goto try_ipv4; } params.family = AF_INET6; if (net_addr_pton(AF_INET6, peer6addr, &addr6)) { LOG_ERR("Cannot parse peer %s address %s to tunnel", "IPv6", peer6addr); } else { net_ipaddr_copy(¶ms.peer6addr, &addr6); ret = net_mgmt( NET_REQUEST_VIRTUAL_INTERFACE_SET_PEER_ADDRESS, iface, ¶ms, sizeof(params)); if (ret < 0 && ret != -ENOTSUP) { LOG_ERR("Cannot set peer %s address %s to " "interface %d (%d)", "IPv6", peer6addr, net_if_get_by_iface(iface), ret); } } params.mtu = NET_ETH_MTU - sizeof(struct net_ipv6_hdr); ret = net_mgmt(NET_REQUEST_VIRTUAL_INTERFACE_SET_MTU, iface, ¶ms, sizeof(params)); if (ret < 0 && ret != -ENOTSUP) { LOG_ERR("Cannot set interface %d MTU to %d (%d)", net_if_get_by_iface(iface), params.mtu, ret); } } try_ipv4: if (IS_ENABLED(CONFIG_NET_IPV4) && net_if_flag_is_set(iface, NET_IF_IPV4)) { if (net_addr_pton(AF_INET, ipv4_addr, &addr4)) { LOG_ERR("Invalid address: %s", ipv4_addr); return -EINVAL; } ifaddr = net_if_ipv4_addr_add(iface, &addr4, NET_ADDR_MANUAL, 0); if (!ifaddr) { LOG_ERR("Cannot add %s to interface %p", ipv4_addr, iface); return -EINVAL; } if (netmask) { struct in_addr nm; if (net_addr_pton(AF_INET, netmask, &nm)) { LOG_ERR("Invalid netmask: %s", netmask); return -EINVAL; } net_if_ipv4_set_netmask_by_addr(iface, &addr4, &nm); } if (!peer4addr || *peer4addr == '\0') { goto done; } params.family = AF_INET; if (net_addr_pton(AF_INET, peer4addr, &addr4)) { LOG_ERR("Cannot parse peer %s address %s to tunnel", "IPv4", peer4addr); } else { net_ipaddr_copy(¶ms.peer4addr, &addr4); ret = net_mgmt( NET_REQUEST_VIRTUAL_INTERFACE_SET_PEER_ADDRESS, iface, ¶ms, sizeof(params)); if (ret < 0 && ret != -ENOTSUP) { LOG_ERR("Cannot set peer %s address %s to " "interface %d (%d)", "IPv4", peer4addr, net_if_get_by_iface(iface), ret); } } params.mtu = NET_ETH_MTU - sizeof(struct net_ipv4_hdr); ret = net_mgmt(NET_REQUEST_VIRTUAL_INTERFACE_SET_MTU, iface, ¶ms, sizeof(struct virtual_interface_req_params)); if (ret < 0 && ret != -ENOTSUP) { LOG_ERR("Cannot set interface %d MTU to %d (%d)", net_if_get_by_iface(iface), params.mtu, ret); } } done: return 0; } int main(void) { #define MAX_NAME_LEN 32 char buf[MAX_NAME_LEN]; struct ud ud; int ret; LOG_INF("Start application (dev %s/%p)", dev_name, device_get_binding(dev_name)); memset(&ud, 0, sizeof(ud)); net_if_foreach(iface_cb, &ud); LOG_INF("My example tunnel interface %d (%s / %p)", net_if_get_by_iface(ud.my_iface), net_virtual_get_name(ud.my_iface, buf, sizeof(buf)), ud.my_iface); LOG_INF("Tunnel interface %d (%s / %p)", net_if_get_by_iface(ud.ip_tunnel_1), net_virtual_get_name(ud.ip_tunnel_1, buf, sizeof(buf)), ud.ip_tunnel_1); LOG_INF("Tunnel interface %d (%s / %p)", net_if_get_by_iface(ud.ip_tunnel_2), net_virtual_get_name(ud.ip_tunnel_2, buf, sizeof(buf)), ud.ip_tunnel_2); LOG_INF("IPIP interface %d (%p)", net_if_get_by_iface(ud.ipip), ud.ipip); LOG_INF("Ethernet interface %d (%p)", net_if_get_by_iface(ud.ethernet), ud.ethernet); /* Attach the network interfaces on top of the Ethernet interface */ net_virtual_interface_attach(ud.ip_tunnel_1, ud.ethernet); /* Attach our example virtual interface on top of the IPv4 one. * This is just an example how to stack the interface on top of * each other. */ net_virtual_interface_attach(ud.my_iface, ud.ip_tunnel_1); net_virtual_interface_attach(ud.my_iface, ud.ip_tunnel_2); ret = setup_iface(ud.my_iface, CONFIG_NET_SAMPLE_IFACE3_MY_IPV6_ADDR, CONFIG_NET_SAMPLE_IFACE3_MY_IPV4_ADDR, NULL, NULL, CONFIG_NET_SAMPLE_IFACE3_MY_IPV4_NETMASK); if (ret < 0) { LOG_ERR("Cannot set IP address to test interface"); } if (ud.ethernet) { ret = setup_iface(ud.ethernet, CONFIG_NET_CONFIG_MY_IPV6_ADDR, CONFIG_NET_CONFIG_MY_IPV4_ADDR, NULL, NULL, CONFIG_NET_CONFIG_MY_IPV4_NETMASK); if (ret < 0) { LOG_ERR("Cannot set IP address to Ethernet interface"); } } ret = setup_iface(ud.ipip, CONFIG_NET_SAMPLE_IFACE2_MY_IPV6_ADDR, CONFIG_NET_SAMPLE_IFACE2_MY_IPV4_ADDR, CONFIG_NET_CONFIG_PEER_IPV6_ADDR, CONFIG_NET_CONFIG_PEER_IPV4_ADDR, CONFIG_NET_SAMPLE_IFACE2_MY_IPV4_NETMASK); if (ret < 0) { LOG_ERR("Cannot set IP address to IPIP tunnel"); } /* This sample application does nothing itself. You can use * net-shell to send ping or UDP/TCP packets for testing * purposes. */ return 0; }