blob: 44fa73a79cf512af3db6a63fb662936af9a8f049 [file] [log] [blame]
/* main.c - Application main entry point */
/*
* Copyright (c) 2020 Lemonbeat GmbH
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <logging/log.h>
LOG_MODULE_REGISTER(net_test, CONFIG_NET_ROUTE_LOG_LEVEL);
#include <zephyr/types.h>
#include <ztest.h>
#include <stdbool.h>
#include <stddef.h>
#include <string.h>
#include <errno.h>
#include <sys/printk.h>
#include <linker/sections.h>
#include <random/rand32.h>
#include <tc_util.h>
#include <net/ethernet.h>
#include <net/dummy.h>
#include <net/buf.h>
#include <net/net_ip.h>
#include <net/net_if.h>
#include <net/net_context.h>
#define NET_LOG_ENABLED 1
#include "net_private.h"
#include "icmpv6.h"
#include "ipv6.h"
#include <net/udp.h>
#include "udp_internal.h"
#include "nbr.h"
#include "route.h"
#if defined(CONFIG_NET_ROUTE_LOG_LEVEL_DBG)
#define DBG(fmt, ...) printk(fmt, ##__VA_ARGS__)
#else
#define DBG(fmt, ...)
#endif
static struct in6_addr iface_1_addr = { { { 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0x1 } } };
static struct in6_addr iface_2_addr = { { { 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0,
0, 0, 0, 0, 0x0b, 0x0e, 0x0e, 0x3 } } };
static struct in6_addr iface_3_addr = { { { 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0,
0, 0, 0, 0, 0x0e, 0x0e, 0x0e, 0x4 } } };
/* Extra address is assigned to ll_addr */
static struct in6_addr ll_addr_1 = { { { 0xfe, 0x80, 0x43, 0xb8, 0, 0, 0, 0,
0, 0, 0, 0xf2,
0xaa, 0x29, 0x02, 0x04 } } };
static struct in6_addr ll_addr_2 = { { { 0xfe, 0x80, 0x43, 0xb8, 0, 0, 0, 0,
0, 0, 0, 0xf2,
0xaa, 0x29, 0x05, 0x06 } } };
static struct in6_addr ll_addr_3 = { { { 0xfe, 0x80, 0x43, 0xb8, 0, 0, 0, 0,
0, 0, 0, 0xf2,
0xaa, 0x29, 0x07, 0x08 } } };
static struct in6_addr in6addr_mcast = { { { 0xff, 0x02, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0x1 } } };
static struct net_if *iface_1;
static struct net_if *iface_2;
static struct net_if *iface_3;
#define WAIT_TIME K_MSEC(50)
struct net_route_mcast_iface_cfg {
uint8_t mac_addr[sizeof(struct net_eth_addr)];
struct net_linkaddr ll_addr;
};
#define MAX_MCAST_ROUTES CONFIG_NET_MAX_MCAST_ROUTES
static struct net_route_entry_mcast *test_mcast_routes[MAX_MCAST_ROUTES];
static struct in6_addr mcast_prefix_iflocal = { { {
0xFF, 0x01, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0 } } };
static struct in6_addr mcast_prefix_llocal = { { {
0xFF, 0x02, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0 } } };
static struct in6_addr mcast_prefix_admin = { { {
0xFF, 0x04, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0 } } };
static struct in6_addr mcast_prefix_site_local = { { {
0xFF, 0x05, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0 } } };
static struct in6_addr mcast_prefix_orga = { { {
0xFF, 0x08, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0 } } };
static struct in6_addr mcast_prefix_global = { { {
0xFF, 0x0E, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0 } } };
/*
* full network prefix based address,
* see RFC-3306 for details
* FF3F:40:FD01:101:: \128
* network prefix FD01:101::\64
*/
static struct in6_addr mcast_prefix_nw_based = { { {
0xFF, 0x3F, 0, 0x40,
0xFD, 0x01, 0x01, 0x01,
0, 0, 0, 0,
0, 0, 0, 0 } } };
static uint8_t forwarding_counter;
static bool iface_1_forwarded;
static bool iface_2_forwarded;
static bool iface_3_forwarded;
struct net_route_mcast_scenario_cfg {
struct in6_addr src;
struct in6_addr mcast;
bool is_active;
};
static struct net_route_mcast_scenario_cfg active_scenario;
int net_route_mcast_dev_init(const struct device *dev)
{
return 0;
}
static uint8_t *net_route_mcast_get_mac(const struct device *dev)
{
struct net_route_mcast_iface_cfg *cfg = dev->data;
if (cfg->mac_addr[2] == 0x00) {
/* 00-00-5E-00-53-xx Documentation RFC 7042 */
cfg->mac_addr[0] = 0x00;
cfg->mac_addr[1] = 0x00;
cfg->mac_addr[2] = 0x5E;
cfg->mac_addr[3] = 0x00;
cfg->mac_addr[4] = 0x53;
cfg->mac_addr[5] = sys_rand32_get();
}
cfg->ll_addr.addr = cfg->mac_addr;
cfg->ll_addr.len = 6U;
return cfg->mac_addr;
}
static void net_route_mcast_add_addresses(struct net_if *iface,
struct in6_addr *ipv6, struct in6_addr *ll_addr)
{
struct net_if_mcast_addr *maddr;
struct net_if_addr *ifaddr;
uint8_t *mac = net_route_mcast_get_mac(net_if_get_device(iface));
net_if_set_link_addr(iface, mac, sizeof(struct net_eth_addr),
NET_LINK_ETHERNET);
ifaddr = net_if_ipv6_addr_add(iface, ipv6, NET_ADDR_MANUAL, 0);
zassert_not_null(ifaddr, "Cannot add global IPv6 address");
ifaddr->addr_state = NET_ADDR_PREFERRED;
ifaddr = net_if_ipv6_addr_add(iface, ll_addr, NET_ADDR_MANUAL, 0);
zassert_not_null(ifaddr, "Cannot add ll IPv6 address");
ifaddr->addr_state = NET_ADDR_PREFERRED;
maddr = net_if_ipv6_maddr_add(iface, &in6addr_mcast);
zassert_not_null(maddr, "Cannot add multicast IPv6 address");
}
static void net_route_mcast_iface_init1(struct net_if *iface)
{
iface_1 = iface;
net_route_mcast_add_addresses(iface, &iface_1_addr, &ll_addr_1);
}
static void net_route_mcast_iface_init2(struct net_if *iface)
{
iface_2 = iface;
net_route_mcast_add_addresses(iface, &iface_2_addr, &ll_addr_2);
}
static void net_route_mcast_iface_init3(struct net_if *iface)
{
iface_3 = iface;
net_route_mcast_add_addresses(iface, &iface_3_addr, &ll_addr_3);
}
static bool check_packet_addresses(struct net_pkt *pkt)
{
struct net_ipv6_hdr *ipv6_hdr = NET_IPV6_HDR(pkt);
if ((memcmp(&active_scenario.src,
&ipv6_hdr->src,
sizeof(struct in6_addr)) != 0) ||
(memcmp(&active_scenario.mcast,
ipv6_hdr->dst,
sizeof(struct in6_addr)) != 0)) {
return false;
}
return true;
}
static int iface_send(const struct device *dev, struct net_pkt *pkt)
{
if (!active_scenario.is_active) {
return 0;
}
if (!check_packet_addresses(pkt)) {
return 0;
}
forwarding_counter++;
if (net_pkt_iface(pkt) == iface_1) {
iface_1_forwarded = true;
} else if (net_pkt_iface(pkt) == iface_2) {
iface_2_forwarded = true;
} else if (net_pkt_iface(pkt) == iface_3) {
iface_3_forwarded = true;
}
return 0;
}
struct net_route_mcast_iface_cfg net_route_data_if1;
struct net_route_mcast_iface_cfg net_route_data_if2;
struct net_route_mcast_iface_cfg net_route_data_if3;
static struct dummy_api net_route_mcast_if_api_1 = {
.iface_api.init = net_route_mcast_iface_init1,
.send = iface_send,
};
static struct dummy_api net_route_mcast_if_api_2 = {
.iface_api.init = net_route_mcast_iface_init2,
.send = iface_send,
};
static struct dummy_api net_route_mcast_if_api_3 = {
.iface_api.init = net_route_mcast_iface_init3,
.send = iface_send,
};
#define _ETH_L2_LAYER DUMMY_L2
#define _ETH_L2_CTX_TYPE NET_L2_GET_CTX_TYPE(DUMMY_L2)
NET_DEVICE_INIT_INSTANCE(mcast_iface_1, "mcast_iface_1", iface_1,
net_route_mcast_dev_init, NULL,
&net_route_data_if1, NULL,
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,
&net_route_mcast_if_api_1, _ETH_L2_LAYER,
_ETH_L2_CTX_TYPE, 127);
NET_DEVICE_INIT_INSTANCE(mcast_iface_2, "mcast_iface_2", iface_2,
net_route_mcast_dev_init, NULL,
&net_route_data_if2, NULL,
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,
&net_route_mcast_if_api_2, _ETH_L2_LAYER,
_ETH_L2_CTX_TYPE, 127);
NET_DEVICE_INIT_INSTANCE(mcast_iface_3, "mcast_iface_3", iface_3,
net_route_mcast_dev_init, NULL,
&net_route_data_if3, NULL,
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,
&net_route_mcast_if_api_3, _ETH_L2_LAYER,
_ETH_L2_CTX_TYPE, 127);
static struct net_pkt *setup_ipv6_udp(struct net_if *iface,
struct in6_addr *src_addr,
struct in6_addr *remote_addr,
uint16_t src_port, uint16_t remote_port)
{
static const char payload[] = "foobar";
struct net_pkt *pkt;
int res;
pkt = net_pkt_alloc_with_buffer(iface, strlen(payload), AF_INET6,
IPPROTO_UDP, K_FOREVER);
if (!pkt) {
return NULL;
}
net_pkt_set_ipv6_hop_limit(pkt, 2);
res = net_ipv6_create(pkt, src_addr, remote_addr);
zassert_equal(0, res, "ipv6 create failed");
res = net_udp_create(pkt, htons(src_port), htons(remote_port));
zassert_equal(0, res, "udp create failed");
res = net_pkt_write(pkt, (uint8_t *) payload, strlen(payload));
zassert_equal(0, res, "pkt write failed");
net_pkt_cursor_init(pkt);
net_ipv6_finalize(pkt, IPPROTO_UDP);
net_pkt_cursor_init(pkt);
return pkt;
}
static void test_route_mcast_init(void)
{
zassert_not_null(iface_1, "Interface is NULL");
zassert_not_null(iface_2, "Interface is NULL");
zassert_not_null(iface_3, "Interface is NULL");
net_if_flag_set(iface_1, NET_IF_FORWARD_MULTICASTS);
net_if_flag_set(iface_2, NET_IF_FORWARD_MULTICASTS);
/* iface_3 should not forward multicasts */
}
static void test_route_mcast_route_add(void)
{
struct in6_addr nw_prefix_based_all_nodes;
struct net_route_entry_mcast *entry;
entry = net_route_mcast_add(iface_1, &mcast_prefix_iflocal, 16);
zassert_is_null(entry, "add iface local should fail");
entry = net_route_mcast_add(iface_1, &mcast_prefix_llocal, 16);
zassert_is_null(entry, "add link local should fail");
test_mcast_routes[0] = net_route_mcast_add(iface_1,
&mcast_prefix_admin, 16);
zassert_not_null(test_mcast_routes[0], "mcast route add failed");
test_mcast_routes[1] = net_route_mcast_add(iface_2,
&mcast_prefix_site_local, 16);
zassert_not_null(test_mcast_routes[1], "mcast route add failed");
test_mcast_routes[2] = net_route_mcast_add(iface_1,
&mcast_prefix_orga, 16);
zassert_not_null(test_mcast_routes[2], "mcast route add failed");
test_mcast_routes[3] = net_route_mcast_add(iface_2,
&mcast_prefix_global, 16);
zassert_not_null(test_mcast_routes[3], "mcast route add failed");
/* check if route can be added
* if forwarding flag not set on iface
*/
test_mcast_routes[4] = net_route_mcast_add(iface_3,
&mcast_prefix_global, 16);
zassert_is_null(test_mcast_routes[4], "mcast route add should fail");
test_mcast_routes[4] = net_route_mcast_add(iface_1,
&mcast_prefix_nw_based, 96);
zassert_not_null(test_mcast_routes[4],
"add for nw prefix based failed");
memcpy(&nw_prefix_based_all_nodes, &mcast_prefix_nw_based,
sizeof(struct in6_addr));
nw_prefix_based_all_nodes.s6_addr[15] = 0x01;
test_mcast_routes[5] = net_route_mcast_add(iface_2,
&nw_prefix_based_all_nodes, 128);
zassert_not_null(test_mcast_routes[5],
"add for nw prefix based failed");
}
static void mcast_foreach_cb(struct net_route_entry_mcast *entry,
void *user_data)
{
zassert_equal_ptr(user_data, &mcast_prefix_global,
"foreach failed, wrong user_data");
}
static void test_route_mcast_foreach(void)
{
int executed_first = net_route_mcast_foreach(mcast_foreach_cb,
NULL, &mcast_prefix_global);
int executed_skip = net_route_mcast_foreach(mcast_foreach_cb,
&mcast_prefix_admin, &mcast_prefix_global);
zassert_true(executed_skip == (executed_first - 1),
"mcast foreach skip did not skip");
}
static void test_route_mcast_lookup(void)
{
struct net_route_entry_mcast *route =
net_route_mcast_lookup(&mcast_prefix_admin);
zassert_equal_ptr(test_mcast_routes[0], route,
"mcast lookup failed");
route = net_route_mcast_lookup(&mcast_prefix_site_local);
zassert_equal_ptr(test_mcast_routes[1], route,
"mcast lookup failed");
route = net_route_mcast_lookup(&mcast_prefix_global);
zassert_equal_ptr(test_mcast_routes[3], route,
"mcast lookup failed");
}
static void test_route_mcast_route_del(void)
{
struct net_route_entry_mcast *route;
bool success = net_route_mcast_del(test_mcast_routes[0]);
zassert_true(success, "failed to delete mcast route");
route = net_route_mcast_lookup(&mcast_prefix_admin);
zassert_is_null(route, "lookup found deleted route");
success = net_route_mcast_del(test_mcast_routes[1]);
zassert_true(success, "failed to delete mcast route");
route = net_route_mcast_lookup(&mcast_prefix_site_local);
zassert_is_null(route, "lookup found deleted route");
success = net_route_mcast_del(test_mcast_routes[2]);
zassert_true(success, "failed to delete mcast route");
success = net_route_mcast_del(test_mcast_routes[3]);
zassert_true(success, "failed to delete mcast route");
success = net_route_mcast_del(test_mcast_routes[4]);
zassert_true(success, "failed to delete mcast route");
success = net_route_mcast_del(test_mcast_routes[5]);
zassert_true(success, "failed to delete mcast route");
}
static void reset_counters(void)
{
iface_1_forwarded = false;
iface_2_forwarded = false;
iface_3_forwarded = false;
forwarding_counter = 0;
}
static void test_route_mcast_scenario1(void)
{
/* scenario 1 site local:
* 1. iface_1 receives site local
* only iface_2 forwards
* 2. iface_3 receives site_local
* only iface_2 forwards
*/
reset_counters();
memcpy(&active_scenario.src, &iface_1_addr, sizeof(struct in6_addr));
active_scenario.src.s6_addr[15] = 0x02;
memcpy(&active_scenario.mcast, &mcast_prefix_site_local,
sizeof(struct in6_addr));
active_scenario.mcast.s6_addr[15] = 0x01;
struct net_pkt *pkt1 = setup_ipv6_udp(iface_1, &active_scenario.src,
&active_scenario.mcast, 20015, 20001);
active_scenario.is_active = true;
if (net_recv_data(iface_1, pkt1) < 0) {
net_pkt_unref(pkt1);
zassert_true(0, "failed to receive initial packet!");
}
k_sleep(WAIT_TIME);
net_pkt_unref(pkt1);
zassert_true(iface_2_forwarded, "iface_2 did not forward");
zassert_false(iface_1_forwarded, "iface_1 forwarded");
zassert_false(iface_3_forwarded, "iface_3 forwarded");
zassert_equal(forwarding_counter, 1,
"unexpected forwarded packet count");
reset_counters();
memcpy(&active_scenario.src, &iface_3_addr, sizeof(struct in6_addr));
active_scenario.src.s6_addr[15] = 0x09;
struct net_pkt *pkt2 = setup_ipv6_udp(iface_3, &active_scenario.src,
&active_scenario.mcast, 20015, 20001);
if (net_recv_data(iface_3, pkt2) < 0) {
net_pkt_unref(pkt2);
zassert_true(0, "failed to receive initial packet!");
}
k_sleep(WAIT_TIME);
net_pkt_unref(pkt2);
active_scenario.is_active = false;
zassert_true(iface_2_forwarded, "iface_2 did not forward");
zassert_false(iface_1_forwarded, "iface_1 forwarded");
zassert_false(iface_3_forwarded, "iface_3 forwarded");
zassert_equal(forwarding_counter, 1,
"unexpected forwarded packet count");
reset_counters();
}
static void test_route_mcast_scenario2(void)
{
/*
* scenario 2 admin local:
* 1. iface_1 receives
* iface_2 must not forward due to missing routing entry.
* iface_3 must not forward due to missing
* routing entry and missing flag.
* iface_1 must not forward because itself
* received the packet!
*
* 2. iface_3 receives
* now iface_1 must forward due to routing entry
*/
reset_counters();
memcpy(&active_scenario.src, &iface_1_addr, sizeof(struct in6_addr));
active_scenario.src.s6_addr[15] = 0x08;
memcpy(&active_scenario.mcast, &mcast_prefix_admin,
sizeof(struct in6_addr));
active_scenario.mcast.s6_addr[15] = 0x01;
struct net_pkt *pkt = setup_ipv6_udp(iface_1, &active_scenario.src,
&active_scenario.mcast, 215, 201);
active_scenario.is_active = true;
if (net_recv_data(iface_1, pkt) < 0) {
net_pkt_unref(pkt);
zassert_true(0, "failed to receive initial packet!");
}
k_sleep(WAIT_TIME);
net_pkt_unref(pkt);
zassert_false(iface_1_forwarded, "iface_1 forwarded");
zassert_false(iface_2_forwarded, "iface_2 forwarded");
zassert_false(iface_3_forwarded, "iface_3 forwarded");
zassert_equal(forwarding_counter, 0, "wrong count forwarded packets");
reset_counters();
memcpy(&active_scenario.src, &iface_3_addr, sizeof(struct in6_addr));
active_scenario.src.s6_addr[15] = 0x08;
struct net_pkt *pkt2 = setup_ipv6_udp(iface_3, &active_scenario.src,
&active_scenario.mcast, 215, 201);
if (net_recv_data(iface_3, pkt2) < 0) {
net_pkt_unref(pkt2);
zassert_true(0, "failed to receive initial packet!");
}
k_sleep(WAIT_TIME);
active_scenario.is_active = false;
net_pkt_unref(pkt2);
zassert_true(iface_1_forwarded, "iface_1 did not forward");
zassert_false(iface_2_forwarded, "iface_2 forwarded");
zassert_false(iface_3_forwarded, "iface_3 forwarded");
zassert_equal(forwarding_counter, 1, "wrong count forwarded packets");
}
static void test_route_mcast_scenario3(void)
{
/*
* scenario 3: network prefix based forwarding
* 1. iface 3 receives nw prefix based all nodes
* iface 1 + 2 forwarding because all nodes group
* 2. iface 3 receives nw prefix based custom group
* only iface 1 forwards
* iface 3 route is set to all nodes
* 3. iface 3 receives all nodes group with different prefix
* no iface forwards
*/
reset_counters();
memcpy(&active_scenario.src, &iface_3_addr, sizeof(struct in6_addr));
active_scenario.src.s6_addr[15] = 0x08;
memcpy(&active_scenario.mcast, &mcast_prefix_nw_based,
sizeof(struct in6_addr));
active_scenario.mcast.s6_addr[15] = 0x01;
struct net_pkt *pkt = setup_ipv6_udp(iface_3, &active_scenario.src,
&active_scenario.mcast, 215, 201);
active_scenario.is_active = true;
if (net_recv_data(iface_3, pkt) < 0) {
net_pkt_unref(pkt);
zassert_true(0, "failed to receive initial packet!");
}
k_sleep(WAIT_TIME);
net_pkt_unref(pkt);
active_scenario.is_active = false;
zassert_true(iface_1_forwarded, "iface_1 did not forward");
zassert_true(iface_2_forwarded, "iface_2 did not forward");
zassert_false(iface_3_forwarded, "iface_3 forwarded");
zassert_equal(forwarding_counter, 2, "wrong count forwarded packets");
reset_counters();
/* set to custom group id */
active_scenario.mcast.s6_addr[15] = 0x0F;
struct net_pkt *pkt2 = setup_ipv6_udp(iface_3, &active_scenario.src,
&active_scenario.mcast, 215, 201);
active_scenario.is_active = true;
if (net_recv_data(iface_3, pkt2) < 0) {
net_pkt_unref(pkt2);
zassert_true(0, "failed to receive initial packet!");
}
k_sleep(WAIT_TIME);
net_pkt_unref(pkt2);
active_scenario.is_active = false;
zassert_true(iface_1_forwarded, "iface_1 did not forward");
zassert_false(iface_2_forwarded, "iface_2 forwarded");
zassert_false(iface_3_forwarded, "iface_3 forwarded");
zassert_equal(forwarding_counter, 1, "wrong count forwarded packets");
reset_counters();
/* set to all nodes but different prefix */
active_scenario.mcast.s6_addr[11] = 0x0F;
active_scenario.mcast.s6_addr[15] = 0x01;
struct net_pkt *pkt3 = setup_ipv6_udp(iface_3, &active_scenario.src,
&active_scenario.mcast, 215, 201);
active_scenario.is_active = true;
if (net_recv_data(iface_3, pkt3) < 0) {
net_pkt_unref(pkt3);
zassert_true(0, "failed to receive initial packet!");
}
k_sleep(WAIT_TIME);
net_pkt_unref(pkt3);
active_scenario.is_active = false;
zassert_false(iface_1_forwarded, "iface_1 forwarded");
zassert_false(iface_2_forwarded, "iface_2 forwarded");
zassert_false(iface_3_forwarded, "iface_3 forwarded");
zassert_equal(forwarding_counter, 0, "wrong count forwarded packets");
}
/*test case main entry*/
void test_main(void)
{
ztest_test_suite(test_route_mcast,
ztest_unit_test(test_route_mcast_init),
ztest_unit_test(test_route_mcast_route_add),
ztest_unit_test(test_route_mcast_foreach),
ztest_unit_test(test_route_mcast_scenario1),
ztest_unit_test(test_route_mcast_scenario2),
ztest_unit_test(test_route_mcast_scenario3),
ztest_unit_test(test_route_mcast_lookup),
ztest_unit_test(test_route_mcast_route_del)
);
ztest_run_test_suite(test_route_mcast);
}