Remove all init functions that do nothing, and provide a `NULL` to *DEVICE*DEFINE* macros. Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no>
428 lines
10 KiB
C
428 lines
10 KiB
C
/*
|
|
* Copyright (c) 2021 Intel Corporation
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <zephyr/logging/log.h>
|
|
LOG_MODULE_REGISTER(net_virtual_interface_sample, LOG_LEVEL_DBG);
|
|
|
|
#include <zephyr/kernel.h>
|
|
#include <zephyr/device.h>
|
|
#include <errno.h>
|
|
|
|
#include <zephyr/net/net_core.h>
|
|
#include <zephyr/net/ethernet.h>
|
|
#include <zephyr/net/virtual.h>
|
|
#include <zephyr/net/virtual_mgmt.h>
|
|
|
|
/* 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) {
|
|
if (net_addr_pton(AF_INET, netmask, &addr4)) {
|
|
LOG_ERR("Invalid netmask: %s", netmask);
|
|
return -EINVAL;
|
|
}
|
|
|
|
net_if_ipv4_set_netmask(iface, &addr4);
|
|
}
|
|
|
|
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;
|
|
}
|