| /* main.c - Application main entry point */ |
| |
| /* |
| * Copyright (c) 2021 Intel Corporation |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #define NET_LOG_LEVEL CONFIG_NET_L2_VIRTUAL_LOG_LEVEL |
| |
| #include <zephyr/logging/log.h> |
| LOG_MODULE_REGISTER(net_test, NET_LOG_LEVEL); |
| |
| #include <zephyr/types.h> |
| #include <stdbool.h> |
| #include <stddef.h> |
| #include <string.h> |
| #include <errno.h> |
| #include <zephyr/sys/printk.h> |
| #include <zephyr/random/random.h> |
| |
| #include <zephyr/ztest.h> |
| |
| #include <zephyr/net/dummy.h> |
| #include <zephyr/net/buf.h> |
| #include <zephyr/net/net_ip.h> |
| #include <zephyr/net/virtual.h> |
| #include <zephyr/net/virtual_mgmt.h> |
| #include <zephyr/net/ethernet.h> |
| #include <zephyr/net/net_l2.h> |
| |
| #include "ipv4.h" |
| #include "ipv6.h" |
| #include "udp_internal.h" |
| |
| bool arp_add(struct net_if *iface, struct in_addr *src, |
| struct net_eth_addr *hwaddr); |
| |
| #define NET_LOG_ENABLED 1 |
| #include "net_private.h" |
| |
| #if NET_LOG_LEVEL >= LOG_LEVEL_DBG |
| #define DBG(fmt, ...) printk(fmt, ##__VA_ARGS__) |
| #else |
| #define DBG(fmt, ...) |
| #endif |
| |
| #define PKT_ALLOC_TIME K_MSEC(50) |
| #define TEST_PORT 9999 |
| |
| static char *test_data = "Test data to be sent"; |
| |
| /* Interface 1 addresses */ |
| static struct in6_addr my_addr1 = { { { 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, |
| 0, 0, 0, 0, 0, 0, 0, 0x1 } } }; |
| static struct in_addr my_addr = { { { 192, 0, 2, 1 } } }; |
| |
| /* Interface 2 addresses */ |
| static struct in6_addr my_addr2 = { { { 0x20, 0x01, 0x0d, 0xb8, 2, 0, 0, 0, |
| 0, 0, 0, 0, 0, 0, 0, 0x1 } } }; |
| |
| /* Interface 3 addresses */ |
| static struct in6_addr my_addr3 = { { { 0x20, 0x01, 0x0d, 0xb8, 3, 0, 0, 0, |
| 0, 0, 0, 0, 0, 0, 0, 0x1 } } }; |
| |
| /* Extra address is assigned to ll_addr */ |
| static struct in6_addr ll_addr = { { { 0xfe, 0x80, 0x43, 0xb8, 0, 0, 0, 0, |
| 0, 0, 0, 0xf2, 0xaa, 0x29, 0x02, |
| 0x04 } } }; |
| |
| struct sockaddr virtual_addr; |
| struct sockaddr peer_addr; |
| |
| #define MTU 1024 |
| |
| /* Keep track of all virtual interfaces */ |
| static struct net_if *virtual_interfaces[1]; |
| static struct net_if *eth_interfaces[2]; |
| static struct net_if *dummy_interfaces[2]; |
| |
| static struct net_context *udp_ctx; |
| |
| static bool test_failed; |
| static bool test_started; |
| static bool data_received; |
| |
| static K_SEM_DEFINE(wait_data, 0, UINT_MAX); |
| |
| #define WAIT_TIME K_SECONDS(1) |
| |
| struct eth_context { |
| struct net_if *iface; |
| uint8_t mac_addr[6]; |
| }; |
| |
| static struct eth_context eth_context; |
| static uint8_t expecting_outer; |
| static uint8_t expecting_inner; |
| static int header_len; |
| |
| static void eth_iface_init(struct net_if *iface) |
| { |
| const struct device *dev = net_if_get_device(iface); |
| struct eth_context *context = dev->data; |
| |
| net_if_set_link_addr(iface, context->mac_addr, |
| sizeof(context->mac_addr), |
| NET_LINK_ETHERNET); |
| |
| ethernet_init(iface); |
| } |
| |
| static int eth_tx(const struct device *dev, struct net_pkt *pkt) |
| { |
| struct eth_context *context = dev->data; |
| |
| zassert_equal_ptr(ð_context, context, |
| "Context pointers do not match (%p vs %p)", |
| eth_context, context); |
| |
| if (!pkt->buffer) { |
| DBG("No data to send!\n"); |
| return -ENODATA; |
| } |
| |
| if (test_started) { |
| uint8_t outer, inner; |
| int ret; |
| |
| net_pkt_set_overwrite(pkt, true); |
| |
| net_pkt_hexdump(pkt, "pkt"); |
| net_pkt_skip(pkt, sizeof(struct net_eth_hdr)); |
| |
| ret = net_pkt_read_u8(pkt, &outer); |
| zassert_equal(ret, 0, "Cannot read outer protocol type"); |
| zassert_equal(outer, expecting_outer, |
| "Unexpected outer protocol 0x%02x, " |
| "expecting 0x%02x", |
| outer, expecting_outer); |
| net_pkt_skip(pkt, header_len - 1); |
| |
| ret = net_pkt_read_u8(pkt, &inner); |
| zassert_equal(ret, 0, "Cannot read inner protocol type"); |
| zassert_equal(inner, expecting_inner, |
| "Unexpected inner protocol 0x%02x, " |
| "expecting 0x%02x", |
| inner, expecting_inner); |
| |
| k_sem_give(&wait_data); |
| } |
| |
| net_pkt_unref(pkt); |
| |
| return 0; |
| } |
| |
| static enum ethernet_hw_caps eth_capabilities(const struct device *dev) |
| { |
| return 0; |
| } |
| |
| static struct ethernet_api api_funcs = { |
| .iface_api.init = eth_iface_init, |
| |
| .get_capabilities = eth_capabilities, |
| .send = eth_tx, |
| }; |
| |
| static void generate_mac(uint8_t *mac_addr) |
| { |
| /* 00-00-5E-00-53-xx Documentation RFC 7042 */ |
| mac_addr[0] = 0x00; |
| mac_addr[1] = 0x00; |
| mac_addr[2] = 0x5E; |
| mac_addr[3] = 0x00; |
| mac_addr[4] = 0x53; |
| mac_addr[5] = sys_rand32_get(); |
| } |
| |
| static int eth_init(const struct device *dev) |
| { |
| struct eth_context *context = dev->data; |
| |
| generate_mac(context->mac_addr); |
| |
| return 0; |
| } |
| |
| ETH_NET_DEVICE_INIT(eth_test, "eth_test", |
| eth_init, NULL, |
| ð_context, NULL, CONFIG_ETH_INIT_PRIORITY, |
| &api_funcs, NET_ETH_MTU); |
| |
| struct net_if_test { |
| uint8_t idx; /* not used for anything, just a dummy value */ |
| uint8_t mac_addr[sizeof(struct net_eth_addr)]; |
| struct net_linkaddr ll_addr; |
| }; |
| |
| static uint8_t *net_iface_get_mac(const struct device *dev) |
| { |
| struct net_if_test *data = dev->data; |
| |
| if (data->mac_addr[2] == 0x00) { |
| /* 00-00-5E-00-53-xx Documentation RFC 7042 */ |
| data->mac_addr[0] = 0x00; |
| data->mac_addr[1] = 0x00; |
| data->mac_addr[2] = 0x5E; |
| data->mac_addr[3] = 0x00; |
| data->mac_addr[4] = 0x53; |
| data->mac_addr[5] = sys_rand32_get(); |
| } |
| |
| data->ll_addr.addr = data->mac_addr; |
| data->ll_addr.len = 6U; |
| |
| return data->mac_addr; |
| } |
| |
| static void net_iface_init(struct net_if *iface) |
| { |
| uint8_t *mac = net_iface_get_mac(net_if_get_device(iface)); |
| |
| net_if_set_link_addr(iface, mac, sizeof(struct net_eth_addr), |
| NET_LINK_ETHERNET); |
| } |
| |
| static int sender_iface(const struct device *dev, struct net_pkt *pkt) |
| { |
| return 0; |
| } |
| |
| struct net_if_test net_iface1_data; |
| struct net_if_test net_iface2_data; |
| |
| static struct dummy_api net_iface_api = { |
| .iface_api.init = net_iface_init, |
| .send = sender_iface, |
| }; |
| |
| /* For testing purposes, create two dummy network interfaces so we can check |
| * that attaching virtual interface work ok. |
| */ |
| NET_DEVICE_INIT_INSTANCE(eth_test_dummy1, |
| "iface1", |
| iface1, |
| NULL, |
| NULL, |
| &net_iface1_data, |
| NULL, |
| CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, |
| &net_iface_api, |
| DUMMY_L2, |
| NET_L2_GET_CTX_TYPE(DUMMY_L2), |
| 127); |
| |
| NET_DEVICE_INIT_INSTANCE(eth_test_dummy2, |
| "iface2", |
| iface2, |
| NULL, |
| NULL, |
| &net_iface2_data, |
| NULL, |
| CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, |
| &net_iface_api, |
| DUMMY_L2, |
| NET_L2_GET_CTX_TYPE(DUMMY_L2), |
| 127); |
| |
| struct user_data { |
| int eth_if_count; |
| int dummy_if_count; |
| int virtual_if_count; |
| int total_if_count; |
| }; |
| |
| #if NET_LOG_LEVEL >= LOG_LEVEL_DBG |
| static const char *iface2str(struct net_if *iface) |
| { |
| if (net_if_l2(iface) == &NET_L2_GET_NAME(ETHERNET)) { |
| return "Ethernet"; |
| } |
| |
| if (net_if_l2(iface) == &NET_L2_GET_NAME(DUMMY)) { |
| return "Dummy"; |
| } |
| |
| if (net_if_l2(iface) == &NET_L2_GET_NAME(VIRTUAL)) { |
| return "Virtual"; |
| } |
| |
| return "<unknown type>"; |
| } |
| #endif |
| |
| static void iface_cb(struct net_if *iface, void *user_data) |
| { |
| struct user_data *ud = user_data; |
| static int starting_eth_idx = 1; |
| |
| /* |
| * The below code is to only use struct net_if devices defined in this |
| * test as board on which it is run can have its own set of interfaces. |
| * |
| * As a result one will not rely on linker's specific 'net_if_area' |
| * placement. |
| */ |
| if ((iface != net_if_lookup_by_dev(DEVICE_GET(eth_test_dummy1))) && |
| (iface != net_if_lookup_by_dev(DEVICE_GET(eth_test_dummy2))) && |
| (iface != net_if_lookup_by_dev(DEVICE_GET(eth_test))) && |
| (net_if_l2(iface) != &NET_L2_GET_NAME(VIRTUAL))) |
| return; |
| |
| DBG("Interface %p (%s) [%d]\n", iface, iface2str(iface), |
| net_if_get_by_iface(iface)); |
| |
| if (net_if_l2(iface) == &NET_L2_GET_NAME(ETHERNET)) { |
| if (PART_OF_ARRAY(NET_IF_GET_NAME(eth_test, 0), iface)) { |
| if (!eth_interfaces[0]) { |
| /* Just use the first interface */ |
| eth_interfaces[0] = iface; |
| ud->eth_if_count++; |
| } |
| } else { |
| if (ud->eth_if_count > ARRAY_SIZE(eth_interfaces)) { |
| goto out; |
| } |
| |
| eth_interfaces[starting_eth_idx++] = iface; |
| ud->eth_if_count++; |
| } |
| } |
| |
| if (net_if_l2(iface) == &NET_L2_GET_NAME(DUMMY)) { |
| dummy_interfaces[ud->dummy_if_count++] = iface; |
| |
| zassert_true(ud->dummy_if_count <= 2, |
| "Too many dummy interfaces"); |
| } |
| |
| if (net_if_l2(iface) == &NET_L2_GET_NAME(VIRTUAL)) { |
| virtual_interfaces[ud->virtual_if_count++] = iface; |
| |
| zassert_true(ud->virtual_if_count <= 3, |
| "Too many virtual interfaces"); |
| } else { |
| /* By default all interfaces are down initially */ |
| /* Virtual interfaces are down initially */ |
| net_if_down(iface); |
| } |
| |
| out: |
| ud->total_if_count++; |
| } |
| |
| static void test_virtual_setup(void) |
| { |
| struct user_data ud = { 0 }; |
| |
| /* Make sure we have enough virtual interfaces */ |
| net_if_foreach(iface_cb, &ud); |
| |
| zassert_equal(ud.virtual_if_count, ARRAY_SIZE(virtual_interfaces), |
| "Invalid number of virtual interfaces, " |
| "was %d should be %zu", |
| ud.virtual_if_count, ARRAY_SIZE(virtual_interfaces)); |
| |
| zassert_true(ud.eth_if_count <= ARRAY_SIZE(eth_interfaces), |
| "Invalid number of eth interfaces, " |
| "was %d should be %zu", |
| ud.eth_if_count, ARRAY_SIZE(eth_interfaces)); |
| |
| zassert_equal(ud.dummy_if_count, ARRAY_SIZE(dummy_interfaces), |
| "Invalid number of dummy interfaces, " |
| "was %d should be %zu", |
| ud.dummy_if_count, ARRAY_SIZE(dummy_interfaces)); |
| } |
| |
| static void test_address_setup(void) |
| { |
| struct in_addr netmask = {{{ 255, 255, 255, 0 }}}; |
| struct net_if_addr *ifaddr; |
| struct net_if *eth, *virt, *dummy1, *dummy2; |
| int ret; |
| |
| eth = eth_interfaces[0]; |
| virt = virtual_interfaces[0]; |
| dummy1 = dummy_interfaces[0]; |
| dummy2 = dummy_interfaces[1]; |
| |
| zassert_not_null(eth, "Eth Interface"); |
| zassert_not_null(virt, "Virtual Interface"); |
| zassert_not_null(dummy1, "Dummy Interface 1"); |
| zassert_not_null(dummy2, "Dummy Interface 2"); |
| |
| ifaddr = net_if_ipv6_addr_add(eth, &my_addr1, NET_ADDR_MANUAL, 0); |
| if (!ifaddr) { |
| DBG("Cannot add IPv6 address %s\n", |
| net_sprint_ipv6_addr(&my_addr1)); |
| zassert_not_null(ifaddr, "eth addr"); |
| } |
| |
| /* For testing purposes we need to set the addresses preferred */ |
| ifaddr->addr_state = NET_ADDR_PREFERRED; |
| |
| ifaddr = net_if_ipv4_addr_add(eth, &my_addr, NET_ADDR_MANUAL, 0); |
| if (!ifaddr) { |
| DBG("Cannot add IPv4 address %s\n", |
| net_sprint_ipv4_addr(&my_addr)); |
| zassert_not_null(ifaddr, "eth addr"); |
| } |
| |
| ifaddr->addr_state = NET_ADDR_PREFERRED; |
| |
| net_if_ipv4_set_netmask_by_addr(eth, &my_addr, &netmask); |
| |
| ifaddr = net_if_ipv6_addr_add(eth, &ll_addr, NET_ADDR_MANUAL, 0); |
| if (!ifaddr) { |
| DBG("Cannot add IPv6 address %s\n", |
| net_sprint_ipv6_addr(&ll_addr)); |
| zassert_not_null(ifaddr, "ll_addr"); |
| } |
| |
| ifaddr->addr_state = NET_ADDR_PREFERRED; |
| |
| ifaddr = net_if_ipv6_addr_add(virt, &my_addr2, NET_ADDR_MANUAL, 0); |
| if (!ifaddr) { |
| DBG("Cannot add IPv6 address %s\n", |
| net_sprint_ipv6_addr(&my_addr2)); |
| zassert_not_null(ifaddr, "virt addr"); |
| } |
| |
| ifaddr->addr_state = NET_ADDR_PREFERRED; |
| |
| ifaddr = net_if_ipv6_addr_add(dummy1, &my_addr3, NET_ADDR_MANUAL, 0); |
| if (!ifaddr) { |
| DBG("Cannot add IPv6 address %s\n", |
| net_sprint_ipv6_addr(&my_addr3)); |
| zassert_not_null(ifaddr, "dummy1 addr"); |
| } |
| |
| net_if_up(eth); |
| net_if_up(dummy1); |
| net_if_up(dummy2); |
| |
| /* Set the virtual interface addresses */ |
| ret = net_ipaddr_parse(CONFIG_NET_TEST_TUNNEL_MY_ADDR, |
| strlen(CONFIG_NET_TEST_TUNNEL_MY_ADDR), |
| &virtual_addr); |
| zassert_equal(ret, 1, "Cannot parse \"%s\"", |
| CONFIG_NET_TEST_TUNNEL_MY_ADDR); |
| |
| if (virtual_addr.sa_family == AF_INET) { |
| ifaddr = net_if_ipv4_addr_add(virt, |
| &net_sin(&virtual_addr)->sin_addr, |
| NET_ADDR_MANUAL, 0); |
| if (!ifaddr) { |
| DBG("Cannot add IPv4 address %s\n", |
| net_sprint_ipv4_addr( |
| &net_sin(&virtual_addr)->sin_addr)); |
| zassert_not_null(ifaddr, "virt addr"); |
| } |
| |
| net_sin(&virtual_addr)->sin_port = htons(4242); |
| |
| net_if_ipv4_set_netmask_by_addr(virt, |
| &net_sin(&virtual_addr)->sin_addr, |
| &netmask); |
| |
| } else if (virtual_addr.sa_family == AF_INET6) { |
| ifaddr = net_if_ipv6_addr_add(virt, |
| &net_sin6(&virtual_addr)->sin6_addr, |
| NET_ADDR_MANUAL, 0); |
| if (!ifaddr) { |
| DBG("Cannot add IPv6 address %s\n", |
| net_sprint_ipv6_addr( |
| &net_sin6(&virtual_addr)->sin6_addr)); |
| zassert_not_null(ifaddr, "virt addr"); |
| } |
| |
| net_sin6(&virtual_addr)->sin6_port = htons(4242); |
| } else { |
| zassert_not_null(NULL, "Invalid address family (%d)", |
| virtual_addr.sa_family); |
| } |
| |
| ifaddr->addr_state = NET_ADDR_PREFERRED; |
| |
| ret = net_ipaddr_parse(CONFIG_NET_TEST_TUNNEL_PEER_ADDR, |
| strlen(CONFIG_NET_TEST_TUNNEL_PEER_ADDR), |
| &peer_addr); |
| zassert_equal(ret, 1, "Cannot parse \"%s\"", |
| CONFIG_NET_TEST_TUNNEL_PEER_ADDR); |
| |
| /* The interface might receive data which might fail the checks |
| * in the iface sending function, so we need to reset the failure |
| * flag. |
| */ |
| test_failed = false; |
| } |
| |
| static bool add_neighbor(struct net_if *iface, struct in6_addr *addr) |
| { |
| struct net_linkaddr_storage llstorage; |
| struct net_linkaddr lladdr; |
| struct net_nbr *nbr; |
| |
| llstorage.addr[0] = 0x01; |
| llstorage.addr[1] = 0x02; |
| llstorage.addr[2] = 0x33; |
| llstorage.addr[3] = 0x44; |
| llstorage.addr[4] = 0x05; |
| llstorage.addr[5] = 0x06; |
| |
| lladdr.len = 6U; |
| lladdr.addr = llstorage.addr; |
| lladdr.type = NET_LINK_ETHERNET; |
| |
| nbr = net_ipv6_nbr_add(iface, addr, &lladdr, false, |
| NET_IPV6_NBR_STATE_REACHABLE); |
| if (!nbr) { |
| DBG("Cannot add dst %s to neighbor cache\n", |
| net_sprint_ipv6_addr(addr)); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| static bool add_to_arp(struct net_if *iface, struct in_addr *addr) |
| { |
| #if defined(CONFIG_NET_ARP) |
| struct net_eth_addr lladdr; |
| |
| lladdr.addr[0] = sys_rand32_get(); |
| lladdr.addr[1] = 0x08; |
| lladdr.addr[2] = 0x09; |
| lladdr.addr[3] = 0x10; |
| lladdr.addr[4] = 0x11; |
| lladdr.addr[5] = sys_rand32_get(); |
| |
| return arp_add(iface, addr, &lladdr); |
| #else |
| ARG_UNUSED(iface); |
| ARG_UNUSED(addr); |
| |
| return true; |
| #endif |
| } |
| |
| ZTEST(net_virtual, test_virtual_01_attach_and_detach) |
| { |
| struct net_if *iface = virtual_interfaces[0]; |
| int ret; |
| |
| /* Attach virtual interface on top of Ethernet */ |
| |
| ret = net_virtual_interface_attach(iface, eth_interfaces[0]); |
| zassert_equal(ret, 0, "Cannot attach %d on top of %d (%d)", |
| net_if_get_by_iface(iface), |
| net_if_get_by_iface(eth_interfaces[0]), |
| ret); |
| |
| zassert_false(net_if_is_up(iface), |
| "Virtual interface %d should be down", |
| net_if_get_by_iface(iface)); |
| |
| ret = net_if_up(iface); |
| zassert_equal(ret, 0, "Cannot take virtual interface %d up (%d)", |
| net_if_get_by_iface(iface), ret); |
| |
| ret = net_virtual_interface_attach(iface, |
| NULL); |
| zassert_equal(ret, 0, "Cannot deattach %d from %d (%d)", |
| net_if_get_by_iface(iface), |
| net_if_get_by_iface(eth_interfaces[0]), |
| ret); |
| |
| zassert_false(net_if_is_up(iface), "Virtual interface %d is still up", |
| net_if_get_by_iface(iface)); |
| } |
| |
| ZTEST(net_virtual, test_virtual_02_real_iface_down) |
| { |
| struct net_if *iface = virtual_interfaces[0]; |
| int ret; |
| |
| /* Attach virtual interface on top of Ethernet */ |
| |
| ret = net_virtual_interface_attach(iface, eth_interfaces[0]); |
| zassert_equal(ret, 0, "Cannot attach %d on top of %d (%d)", |
| net_if_get_by_iface(iface), |
| net_if_get_by_iface(eth_interfaces[0]), |
| ret); |
| |
| zassert_false(net_if_is_up(iface), |
| "Virtual interface %d should be down", |
| net_if_get_by_iface(iface)); |
| |
| ret = net_if_up(iface); |
| zassert_equal(ret, 0, "Cannot take virtual interface %d up (%d)", |
| net_if_get_by_iface(iface), ret); |
| |
| zassert_true(net_if_is_up(iface), |
| "Virtual interface %d should be up", |
| net_if_get_by_iface(iface)); |
| zassert_true(net_if_is_up(eth_interfaces[0]), |
| "Real interface %d should be up", |
| net_if_get_by_iface(iface)); |
| |
| /* Virtual interface should go down if the underlying iface is down */ |
| ret = net_if_down(eth_interfaces[0]); |
| zassert_equal(ret, 0, "Cannot take real interface %d down (%d)", |
| net_if_get_by_iface(eth_interfaces[0]), ret); |
| |
| zassert_false(net_if_is_up(iface), |
| "Virtual interface %d should be down", |
| net_if_get_by_iface(iface)); |
| zassert_false(net_if_is_carrier_ok(iface), |
| "Virtual interface %d should be in carrier off", |
| net_if_get_by_iface(iface)); |
| zassert_equal(net_if_oper_state(iface), NET_IF_OPER_LOWERLAYERDOWN, |
| "Wrong operational state on %d (%d)", |
| net_if_get_by_iface(iface), net_if_oper_state(iface)); |
| |
| /* Virtual interface should be brought up if the underlying iface is |
| * back up |
| */ |
| ret = net_if_up(eth_interfaces[0]); |
| zassert_equal(ret, 0, "Cannot take real interface %d u (%d)", |
| net_if_get_by_iface(eth_interfaces[0]), ret); |
| |
| zassert_true(net_if_is_up(iface), |
| "Virtual interface %d should be up", |
| net_if_get_by_iface(iface)); |
| zassert_true(net_if_is_carrier_ok(iface), |
| "Virtual interface %d should be in carrier on", |
| net_if_get_by_iface(iface)); |
| |
| ret = net_virtual_interface_attach(iface, |
| NULL); |
| zassert_equal(ret, 0, "Cannot deattach %d from %d (%d)", |
| net_if_get_by_iface(iface), |
| net_if_get_by_iface(eth_interfaces[0]), |
| ret); |
| |
| zassert_false(net_if_is_up(iface), "Virtual interface %d is still up", |
| net_if_get_by_iface(iface)); |
| } |
| |
| |
| ZTEST(net_virtual, test_virtual_03_set_mtu) |
| { |
| struct virtual_interface_req_params params = { 0 }; |
| struct net_if *iface = virtual_interfaces[0]; |
| int ret; |
| |
| ret = net_if_up(iface); |
| zassert_equal(ret, 0, "Cannot take virtual interface %d up (%d)", |
| net_if_get_by_iface(iface), ret); |
| |
| params.mtu = MTU; |
| |
| ret = net_mgmt(NET_REQUEST_VIRTUAL_INTERFACE_SET_MTU, |
| iface, ¶ms, sizeof(params)); |
| zassert_equal(ret, -EACCES, "Could set interface %d MTU to %d (%d)", |
| net_if_get_by_iface(iface), params.mtu, ret); |
| |
| ret = net_if_down(iface); |
| zassert_equal(ret, 0, "Cannot take virtual interface %d down (%d)", |
| net_if_get_by_iface(iface), ret); |
| |
| ret = net_mgmt(NET_REQUEST_VIRTUAL_INTERFACE_SET_MTU, |
| iface, ¶ms, sizeof(params)); |
| zassert_equal(ret, 0, "Cannot set interface %d MTU to %d (%d)", |
| net_if_get_by_iface(iface), params.mtu, ret); |
| } |
| |
| ZTEST(net_virtual, test_virtual_04_get_mtu) |
| { |
| struct virtual_interface_req_params params = { 0 }; |
| struct net_if *iface = virtual_interfaces[0]; |
| int ret; |
| |
| params.mtu = 0; |
| |
| ret = net_mgmt(NET_REQUEST_VIRTUAL_INTERFACE_GET_MTU, |
| iface, ¶ms, sizeof(params)); |
| zassert_equal(ret, 0, "Cannot get interface %d MTU (%d)", |
| net_if_get_by_iface(iface), params.mtu, ret); |
| |
| zassert_equal(params.mtu, MTU, |
| "MTU mismatch from interface %d, got %d should be %d", |
| net_if_get_by_iface(iface), params.mtu, MTU); |
| } |
| |
| ZTEST(net_virtual, test_virtual_05_set_peer) |
| { |
| struct virtual_interface_req_params params = { 0 }; |
| struct net_if *iface = virtual_interfaces[0]; |
| int ret; |
| |
| ret = net_if_up(iface); |
| zassert_equal(ret, 0, "Cannot take virtual interface %d up (%d)", |
| net_if_get_by_iface(iface), ret); |
| |
| params.family = peer_addr.sa_family; |
| if (params.family == AF_INET) { |
| net_ipaddr_copy(¶ms.peer4addr, |
| &net_sin(&peer_addr)->sin_addr); |
| } else if (params.family == AF_INET6) { |
| net_ipaddr_copy(¶ms.peer6addr, |
| &net_sin6(&peer_addr)->sin6_addr); |
| } else { |
| zassert_true(false, "Invalid family (%d)", params.family); |
| } |
| |
| ret = net_mgmt(NET_REQUEST_VIRTUAL_INTERFACE_SET_PEER_ADDRESS, |
| iface, ¶ms, sizeof(params)); |
| zassert_equal(ret, -EACCES, "Could set interface %d peer to %s (%d)", |
| net_if_get_by_iface(iface), |
| CONFIG_NET_TEST_TUNNEL_PEER_ADDR, ret); |
| |
| ret = net_if_down(iface); |
| zassert_equal(ret, 0, "Cannot take virtual interface %d down (%d)", |
| net_if_get_by_iface(iface), ret); |
| |
| ret = net_mgmt(NET_REQUEST_VIRTUAL_INTERFACE_SET_PEER_ADDRESS, |
| iface, ¶ms, sizeof(params)); |
| zassert_equal(ret, 0, "Cannot set interface %d peer to %s (%d)", |
| net_if_get_by_iface(iface), |
| CONFIG_NET_TEST_TUNNEL_PEER_ADDR, ret); |
| |
| /* We should be attached now */ |
| ret = net_virtual_interface_attach(iface, dummy_interfaces[0]); |
| zassert_equal(ret, -EALREADY, "Could attach %d on top of %d (%d)", |
| net_if_get_by_iface(iface), |
| net_if_get_by_iface(dummy_interfaces[0]), |
| ret); |
| |
| ret = net_virtual_interface_attach(iface, NULL); |
| zassert_equal(ret, 0, "Cannot deattach %d from %d (%d)", |
| net_if_get_by_iface(iface), |
| net_if_get_by_iface(eth_interfaces[0]), |
| ret); |
| } |
| |
| ZTEST(net_virtual, test_virtual_06_get_peer) |
| { |
| struct virtual_interface_req_params params = { 0 }; |
| struct net_if *iface = virtual_interfaces[0]; |
| int ret; |
| |
| ret = net_mgmt(NET_REQUEST_VIRTUAL_INTERFACE_GET_PEER_ADDRESS, |
| iface, ¶ms, sizeof(params)); |
| zassert_equal(ret, 0, "Cannot get interface %d peer (%d)", |
| net_if_get_by_iface(iface), ret); |
| |
| zassert_equal(params.family, peer_addr.sa_family, |
| "Invalid family, should be %d was %d", |
| peer_addr.sa_family, params.family); |
| if (params.family == AF_INET) { |
| zassert_mem_equal(¶ms.peer4addr, |
| &net_sin(&peer_addr)->sin_addr, |
| sizeof(struct in_addr), |
| "Peer IPv4 address invalid"); |
| } else if (params.family == AF_INET6) { |
| zassert_mem_equal(¶ms.peer6addr, |
| &net_sin6(&peer_addr)->sin6_addr, |
| sizeof(struct in6_addr), |
| "Peer IPv6 address invalid"); |
| } else { |
| zassert_true(false, "Invalid family (%d)", params.family); |
| } |
| } |
| |
| ZTEST(net_virtual, test_virtual_07_verify_name) |
| { |
| #define NAME "foobar" |
| #define NAME2 "123456789" |
| struct net_if *iface = virtual_interfaces[0]; |
| char *tmp = NAME; |
| char buf[sizeof(NAME2)]; |
| char *name; |
| |
| net_virtual_set_name(iface, NAME); |
| name = net_virtual_get_name(iface, buf, sizeof(buf)); |
| zassert_mem_equal(name, tmp, strlen(name), "Cannot get name"); |
| |
| /* Check that the string is truncated */ |
| tmp = NAME2; |
| net_virtual_set_name(iface, tmp); |
| name = net_virtual_get_name(iface, buf, sizeof(buf)); |
| zassert_mem_equal(name, tmp, strlen(name), "Cannot get name"); |
| zassert_mem_equal(name, tmp, strlen(tmp) - |
| (sizeof(NAME2) - CONFIG_NET_L2_VIRTUAL_MAX_NAME_LEN), |
| "Cannot get name"); |
| } |
| |
| ZTEST(net_virtual, test_virtual_08_detach) |
| { |
| struct net_if *iface = virtual_interfaces[0]; |
| int ret; |
| |
| ret = net_virtual_interface_attach(iface, NULL); |
| zassert_true((ret == 0) || (ret == -EALREADY), |
| "Cannot deattach %d from %d (%d)", |
| net_if_get_by_iface(iface), |
| net_if_get_by_iface(eth_interfaces[0]), |
| ret); |
| } |
| |
| ZTEST(net_virtual, test_virtual_08_send_data_to_tunnel) |
| { |
| struct virtual_interface_req_params params = { 0 }; |
| struct net_if *iface = virtual_interfaces[0]; |
| struct net_if *attached = eth_interfaces[0]; |
| struct sockaddr dst_addr, src_addr; |
| void *addr; |
| int addrlen; |
| int ret; |
| |
| params.family = peer_addr.sa_family; |
| if (params.family == AF_INET) { |
| net_ipaddr_copy(¶ms.peer4addr, |
| &net_sin(&peer_addr)->sin_addr); |
| expecting_outer = 0x45; |
| header_len = sizeof(struct net_ipv4_hdr); |
| |
| ret = add_to_arp(eth_interfaces[0], |
| &net_sin(&peer_addr)->sin_addr); |
| zassert_true(ret, "Cannot add to arp"); |
| } else if (params.family == AF_INET6) { |
| net_ipaddr_copy(¶ms.peer6addr, |
| &net_sin6(&peer_addr)->sin6_addr); |
| expecting_outer = 0x60; |
| header_len = sizeof(struct net_ipv6_hdr); |
| |
| ret = add_neighbor(eth_interfaces[0], |
| &net_sin6(&peer_addr)->sin6_addr); |
| zassert_true(ret, "Cannot add neighbor"); |
| } else { |
| zassert_true(false, "Invalid family (%d)", params.family); |
| } |
| |
| ret = net_mgmt(NET_REQUEST_VIRTUAL_INTERFACE_SET_PEER_ADDRESS, |
| iface, ¶ms, sizeof(params)); |
| zassert_equal(ret, 0, "Cannot set interface %d peer to %s (%d)", |
| net_if_get_by_iface(iface), |
| CONFIG_NET_TEST_TUNNEL_PEER_ADDR, ret); |
| |
| net_virtual_set_name(iface, CONFIG_NET_TEST_TUNNEL_NAME); |
| |
| attached = net_virtual_get_iface(iface); |
| zassert_equal(eth_interfaces[0], attached, |
| "Not attached to Ethernet interface"); |
| |
| ret = net_if_up(iface); |
| zassert_equal(ret, 0, "Cannot take virtual interface %d up (%d)", |
| net_if_get_by_iface(iface), ret); |
| |
| memcpy(&dst_addr, &virtual_addr, sizeof(dst_addr)); |
| memcpy(&src_addr, &virtual_addr, sizeof(src_addr)); |
| |
| if (dst_addr.sa_family == AF_INET) { |
| net_sin(&dst_addr)->sin_addr.s4_addr[3] = 2; |
| |
| addr = &src_addr; |
| addrlen = sizeof(struct sockaddr_in); |
| |
| expecting_inner = 0x45; /* IPv4 */ |
| |
| } else if (dst_addr.sa_family == AF_INET6) { |
| net_sin6(&dst_addr)->sin6_addr.s6_addr[15] = 2; |
| |
| addr = &src_addr; |
| addrlen = sizeof(struct sockaddr_in6); |
| |
| expecting_inner = 0x60; /* IPv6 */ |
| } else { |
| zassert_true(false, "Invalid family (%d)", dst_addr.sa_family); |
| addrlen = 0; |
| } |
| |
| ret = net_context_get(virtual_addr.sa_family, SOCK_DGRAM, IPPROTO_UDP, |
| &udp_ctx); |
| zassert_equal(ret, 0, "Create IP UDP context failed"); |
| |
| ret = net_context_bind(udp_ctx, (struct sockaddr *)addr, addrlen); |
| zassert_equal(ret, 0, "Context bind failure test failed"); |
| |
| test_started = true; |
| |
| ret = net_context_sendto(udp_ctx, test_data, strlen(test_data), |
| &dst_addr, addrlen, |
| NULL, K_NO_WAIT, NULL); |
| zassert_true(ret > 0, "Send UDP pkt failed"); |
| |
| if (k_sem_take(&wait_data, WAIT_TIME)) { |
| DBG("Timeout while waiting interface data\n"); |
| zassert_false(true, "Timeout"); |
| } |
| |
| net_context_unref(udp_ctx); |
| } |
| |
| static struct net_pkt *create_outer(struct net_if *iface, |
| sa_family_t family, |
| enum net_ip_protocol proto, |
| size_t inner_len, |
| size_t outer_len) |
| { |
| return net_pkt_alloc_with_buffer(iface, inner_len + outer_len, |
| family, proto, PKT_ALLOC_TIME); |
| } |
| |
| static struct net_pkt *create_inner(struct net_if *iface, |
| sa_family_t family, |
| enum net_ip_protocol proto, |
| size_t inner_len, |
| size_t data_len) |
| { |
| return net_pkt_alloc_with_buffer(iface, inner_len + data_len, |
| family, proto, PKT_ALLOC_TIME); |
| } |
| |
| static void recv_data(struct net_context *context, |
| struct net_pkt *pkt, |
| union net_ip_header *ip_hdr, |
| union net_proto_header *proto_hdr, |
| int status, |
| void *user_data) |
| { |
| data_received = true; |
| } |
| |
| static void test_virtual_recv_data_from_tunnel(int remote_ip, |
| bool expected_ok) |
| { |
| struct net_if *iface = virtual_interfaces[0]; |
| struct net_if *attached = eth_interfaces[0]; |
| struct sockaddr dst_addr, src_addr, inner_src; |
| struct in_addr *outerv4, *innerv4; |
| struct in6_addr *outerv6, *innerv6; |
| size_t inner_len = sizeof(struct net_udp_hdr) + |
| strlen(test_data); |
| struct net_pkt *outer, *inner; |
| enum net_verdict verdict; |
| uint16_t src_port = 4242, dst_port = 4242; |
| uint8_t next_header; |
| size_t addrlen; |
| int ret; |
| |
| memcpy(&dst_addr, &peer_addr, sizeof(dst_addr)); |
| memcpy(&src_addr, &peer_addr, sizeof(src_addr)); |
| memcpy(&inner_src, &virtual_addr, sizeof(inner_src)); |
| |
| if (peer_addr.sa_family == AF_INET) { |
| net_sin(&dst_addr)->sin_addr.s4_addr[3] = 1; |
| net_sin(&src_addr)->sin_addr.s4_addr[3] = remote_ip; |
| outerv4 = &net_sin(&peer_addr)->sin_addr; |
| } else { |
| net_sin6(&dst_addr)->sin6_addr.s6_addr[15] = 1; |
| net_sin6(&src_addr)->sin6_addr.s6_addr[15] = remote_ip; |
| outerv6 = &net_sin6(&peer_addr)->sin6_addr; |
| } |
| |
| if (virtual_addr.sa_family == AF_INET) { |
| net_sin(&inner_src)->sin_addr.s4_addr[3] = 2; |
| innerv4 = &net_sin(&virtual_addr)->sin_addr; |
| inner_len += sizeof(struct net_ipv4_hdr); |
| } else { |
| net_sin6(&inner_src)->sin6_addr.s6_addr[15] = 2; |
| innerv6 = &net_sin6(&virtual_addr)->sin6_addr; |
| inner_len += sizeof(struct net_ipv6_hdr); |
| } |
| |
| if (peer_addr.sa_family == AF_INET) { |
| outer = create_outer(attached, AF_INET, IPPROTO_IP, |
| sizeof(struct net_ipv4_hdr), 0); |
| zassert_not_null(outer, "Cannot allocate %s pkt", outer); |
| |
| ret = net_ipv4_create(outer, &net_sin(&src_addr)->sin_addr, |
| &net_sin(&dst_addr)->sin_addr); |
| zassert_equal(ret, 0, "Cannot create %s packet (%d)", "IPv4", |
| ret); |
| } else { |
| outer = create_outer(attached, AF_INET6, IPPROTO_IPV6, |
| sizeof(struct net_ipv6_hdr), 0); |
| zassert_not_null(outer, "Cannot allocate %s pkt", outer); |
| |
| ret = net_ipv6_create(outer, &net_sin6(&src_addr)->sin6_addr, |
| &net_sin6(&dst_addr)->sin6_addr); |
| zassert_equal(ret, 0, "Cannot create %s packet (%d)", "IPv6", |
| ret); |
| } |
| |
| if (virtual_addr.sa_family == AF_INET) { |
| inner = create_inner(iface, AF_INET, IPPROTO_IP, |
| sizeof(struct net_ipv4_hdr), |
| sizeof(struct net_udp_hdr) + |
| strlen(test_data)); |
| zassert_not_null(inner, "Cannot allocate %s pkt", inner); |
| |
| ret = net_ipv4_create(inner, &net_sin(&inner_src)->sin_addr, |
| innerv4); |
| zassert_equal(ret, 0, "Cannot create outer %s (%d)", "IPv4", |
| ret); |
| next_header = IPPROTO_IPIP; |
| addrlen = sizeof(struct sockaddr_in); |
| } else { |
| inner = create_inner(iface, AF_INET6, IPPROTO_IPV6, |
| sizeof(struct net_ipv6_hdr), |
| sizeof(struct net_udp_hdr) + |
| strlen(test_data)); |
| zassert_not_null(inner, "Cannot allocate %s pkt", inner); |
| |
| ret = net_ipv6_create(inner, &net_sin6(&inner_src)->sin6_addr, |
| innerv6); |
| zassert_equal(ret, 0, "Cannot create outer %s (%d)", "IPv6", |
| ret); |
| next_header = IPPROTO_IPV6; |
| addrlen = sizeof(struct sockaddr_in6); |
| } |
| |
| ret = net_udp_create(inner, htons(src_port), htons(dst_port)); |
| zassert_equal(ret, 0, "Cannot create UDP (%d)", ret); |
| |
| net_pkt_write(inner, test_data, strlen(test_data)); |
| |
| net_pkt_cursor_init(inner); |
| |
| if (virtual_addr.sa_family == AF_INET) { |
| net_ipv4_finalize(inner, IPPROTO_UDP); |
| } else { |
| net_ipv6_finalize(inner, IPPROTO_UDP); |
| } |
| |
| net_buf_frag_add(outer->buffer, inner->buffer); |
| inner->buffer = NULL; |
| net_pkt_unref(inner); |
| |
| net_pkt_cursor_init(outer); |
| |
| if (peer_addr.sa_family == AF_INET) { |
| net_ipv4_finalize(outer, next_header); |
| } else { |
| net_ipv6_finalize(outer, next_header); |
| } |
| |
| ret = net_context_get(virtual_addr.sa_family, SOCK_DGRAM, IPPROTO_UDP, |
| &udp_ctx); |
| zassert_equal(ret, 0, "Create IP UDP context failed"); |
| |
| net_context_set_iface(udp_ctx, iface); |
| |
| ret = net_context_bind(udp_ctx, (struct sockaddr *)&virtual_addr, |
| addrlen); |
| zassert_equal(ret, 0, "Context bind failure test failed"); |
| |
| test_started = true; |
| data_received = false; |
| |
| ret = net_context_recv(udp_ctx, recv_data, K_NO_WAIT, &wait_data); |
| zassert_equal(ret, 0, "UDP recv failed"); |
| |
| net_pkt_cursor_init(outer); |
| |
| if (peer_addr.sa_family == AF_INET) { |
| verdict = net_ipv4_input(outer, false); |
| } else { |
| verdict = net_ipv6_input(outer, false); |
| } |
| |
| if (expected_ok) { |
| zassert_equal(verdict, NET_OK, |
| "Packet not accepted (%d)", |
| verdict); |
| } else { |
| zassert_equal(verdict, NET_DROP, |
| "Packet not dropped (%d)", |
| verdict); |
| } |
| |
| net_context_put(udp_ctx); |
| } |
| |
| ZTEST(net_virtual, test_virtual_09_recv_data_from_tunnel_ok) |
| { |
| test_virtual_recv_data_from_tunnel(2, true); |
| } |
| |
| ZTEST(net_virtual, test_virtual_10_recv_data_from_tunnel_fail) |
| { |
| test_virtual_recv_data_from_tunnel(3, false); |
| } |
| |
| static void *setup(void) |
| { |
| test_virtual_setup(); |
| test_address_setup(); |
| return NULL; |
| } |
| |
| ZTEST_SUITE(net_virtual, NULL, setup, NULL, NULL, NULL); |