net: route: multicast routing feature

net: route: Add prefix-based ipv6 multicast forwarding

This adds/reenables the feature of multicast routing/forwarding.
The forwarding decision is based on the added multicast routes
and the new network interface flag:
NET_IF_FORWARD_MULTICASTS.

Signed-off-by: Jan Georgi <jan.georgi@lemonbeat.com>
This commit is contained in:
Jan Georgi 2020-08-17 15:24:46 +02:00 committed by Anas Nashif
parent 92530b5fbb
commit 5931a29979
11 changed files with 869 additions and 11 deletions

View file

@ -181,6 +181,12 @@ enum net_if_flag {
/** Power management specific: interface is being suspended */
NET_IF_SUSPENDED,
/** Flag defines if received multicasts of other interface are
* forwarded on this interface. This activates multicast
* routing / forwarding for this interface.
*/
NET_IF_FORWARD_MULTICASTS,
/** @cond INTERNAL_HIDDEN */
/* Total number of flags - must be at the end of the enum */
NET_IF_NUM_FLAGS

View file

@ -904,13 +904,29 @@ static inline bool net_ipv6_is_addr_mcast_scope(const struct in6_addr *addr,
return (addr->s6_addr[0] == 0xff) && (addr->s6_addr[1] == scope);
}
/**
* @brief Check if the IPv6 addresses have the same multicast scope (FFyx::).
*
* @param addr_1 IPv6 address 1
* @param addr_2 IPv6 address 2
*
* @return True if both addresses have same multicast scope,
* false otherwise.
*/
static inline bool net_ipv6_is_same_mcast_scope(const struct in6_addr *addr_1,
const struct in6_addr *addr_2)
{
return (addr_1->s6_addr[0] == 0xff) && (addr_2->s6_addr[0] == 0xff) &&
(addr_1->s6_addr[1] == addr_2->s6_addr[1]);
}
/**
* @brief Check if the IPv6 address is a global multicast address (FFxE::/16).
*
* @param addr IPv6 address.
*
* @return True if the address is global multicast address, false otherwise.
*/
*/
static inline bool net_ipv6_is_addr_mcast_global(const struct in6_addr *addr)
{
return net_ipv6_is_addr_mcast_scope(addr, 0x0e);
@ -930,6 +946,20 @@ static inline bool net_ipv6_is_addr_mcast_iface(const struct in6_addr *addr)
return net_ipv6_is_addr_mcast_scope(addr, 0x01);
}
/**
* @brief Check if the IPv6 address is a link local scope multicast
* address (FFx2::).
*
* @param addr IPv6 address.
*
* @return True if the address is a link local scope multicast address,
* false otherwise.
*/
static inline bool net_ipv6_is_addr_mcast_link(const struct in6_addr *addr)
{
return net_ipv6_is_addr_mcast_scope(addr, 0x02);
}
/**
* @brief Check if the IPv6 address is a mesh-local scope multicast
* address (FFx3::).

View file

@ -265,8 +265,10 @@ config NET_MAX_NEXTHOPS
This determines how many entries can be stored in nexthop table.
config NET_ROUTE_MCAST
bool
bool "Enable Multicast Routing / Forwarding"
depends on NET_ROUTE
help
Activates multicast routing/forwarding
config NET_MAX_MCAST_ROUTES
int "Max number of multicast routing entries stored."

View file

@ -362,6 +362,31 @@ static inline enum net_verdict ipv6_route_packet(struct net_pkt *pkt,
#endif /* CONFIG_NET_ROUTE */
static enum net_verdict ipv6_forward_mcast_packet(struct net_pkt *pkt,
struct net_ipv6_hdr *hdr)
{
#if defined(CONFIG_NET_ROUTE_MCAST)
int routed;
/* check if routing loop could be created or if the destination is of
* interface local scope or if from link local source
*/
if (net_ipv6_is_addr_mcast(&hdr->src) ||
net_ipv6_is_addr_mcast_iface(&hdr->dst) ||
net_ipv6_is_ll_addr(&hdr->src)) {
return NET_CONTINUE;
}
routed = net_route_mcast_forward_packet(pkt, hdr);
if (routed < 0) {
return NET_DROP;
}
#endif /*CONFIG_NET_ROUTE_MCAST*/
return NET_CONTINUE;
}
enum net_verdict net_ipv6_input(struct net_pkt *pkt, bool is_loopback)
{
NET_PKT_DATA_ACCESS_CONTIGUOUS_DEFINE(ipv6_access, struct net_ipv6_hdr);
@ -443,6 +468,20 @@ enum net_verdict net_ipv6_input(struct net_pkt *pkt, bool is_loopback)
goto drop;
}
if (IS_ENABLED(CONFIG_NET_ROUTE_MCAST) &&
net_ipv6_is_addr_mcast(&hdr->dst)) {
/* If the packet is a multicast packet and multicast routing
* is activated, we give the packet to the routing engine.
*
* But we only drop the packet if an error occurs, otherwise
* it might be eminent to respond on the packet on application
* layer.
*/
if (ipv6_forward_mcast_packet(pkt, hdr) == NET_DROP) {
goto drop;
}
}
/* If we receive a packet with ll source address fe80: and destination
* address is one of ours, and if the packet would cross interface
* boundary, then drop the packet. RFC 4291 ch 2.5.6

View file

@ -641,8 +641,9 @@ static void route_mcast_cb(struct net_route_entry_mcast *entry,
PR("==========================================================="
"%s\n", extra);
PR("IPv6 group : %s\n", net_sprint_ipv6_addr(&entry->group));
PR("Lifetime : %u\n", entry->lifetime);
PR("IPv6 group : %s\n", net_sprint_ipv6_addr(&entry->group));
PR("IPv6 group len : %d\n", entry->prefix_len);
PR("Lifetime : %u\n", entry->lifetime);
}
static void iface_per_mcast_route_cb(struct net_if *iface, void *user_data)

View file

@ -644,6 +644,48 @@ int net_route_foreach(net_route_cb_t cb, void *user_data)
static
struct net_route_entry_mcast route_mcast_entries[CONFIG_NET_MAX_MCAST_ROUTES];
int net_route_mcast_forward_packet(struct net_pkt *pkt,
const struct net_ipv6_hdr *hdr)
{
int i, ret = 0, err = 0;
for (i = 0; i < CONFIG_NET_MAX_MCAST_ROUTES; ++i) {
struct net_route_entry_mcast *route = &route_mcast_entries[i];
struct net_pkt *pkt_cpy = NULL;
if (!route->is_used) {
continue;
}
if (!net_if_flag_is_set(route->iface,
NET_IF_FORWARD_MULTICASTS) ||
!net_ipv6_is_prefix(hdr->dst.s6_addr,
route->group.s6_addr,
route->prefix_len) ||
(pkt->iface == route->iface)) {
continue;
}
pkt_cpy = net_pkt_shallow_clone(pkt, K_NO_WAIT);
if (pkt_cpy == NULL) {
err--;
continue;
}
net_pkt_set_forwarding(pkt_cpy, true);
net_pkt_set_iface(pkt_cpy, route->iface);
if (net_send_data(pkt_cpy) >= 0) {
++ret;
} else {
--err;
}
}
return (err == 0) ? ret : err;
}
int net_route_mcast_foreach(net_route_mcast_cb_t cb,
struct in6_addr *skip,
void *user_data)
@ -654,7 +696,9 @@ int net_route_mcast_foreach(net_route_mcast_cb_t cb,
struct net_route_entry_mcast *route = &route_mcast_entries[i];
if (route->is_used) {
if (skip && net_ipv6_addr_cmp(skip, &route->group)) {
if (skip && net_ipv6_is_prefix(skip->s6_addr,
route->group.s6_addr,
route->prefix_len)) {
continue;
}
@ -668,16 +712,25 @@ int net_route_mcast_foreach(net_route_mcast_cb_t cb,
}
struct net_route_entry_mcast *net_route_mcast_add(struct net_if *iface,
struct in6_addr *group)
struct in6_addr *group,
uint8_t prefix_len)
{
int i;
if ((!net_if_flag_is_set(iface, NET_IF_FORWARD_MULTICASTS)) ||
(!net_ipv6_is_addr_mcast(group)) ||
(net_ipv6_is_addr_mcast_iface(group)) ||
(net_ipv6_is_addr_mcast_link(group))) {
return NULL;
}
for (i = 0; i < CONFIG_NET_MAX_MCAST_ROUTES; i++) {
struct net_route_entry_mcast *route = &route_mcast_entries[i];
if (!route->is_used) {
net_ipaddr_copy(&route->group, group);
route->prefix_len = prefix_len;
route->iface = iface;
route->is_used = true;
@ -713,9 +766,13 @@ net_route_mcast_lookup(struct in6_addr *group)
struct net_route_entry_mcast *route = &route_mcast_entries[i];
if (!route->is_used) {
if (net_ipv6_addr_cmp(group, &route->group)) {
return route;
}
continue;
}
if (net_ipv6_is_prefix(group->s6_addr,
route->group.s6_addr,
route->prefix_len)) {
return route;
}
}

View file

@ -186,13 +186,29 @@ struct net_route_entry_mcast {
/** Routing entry lifetime in seconds. */
uint32_t lifetime;
/** Is this entry in user or not */
/** Is this entry in use or not */
bool is_used;
/** IPv6 multicast group prefix length. */
uint8_t prefix_len;
};
typedef void (*net_route_mcast_cb_t)(struct net_route_entry_mcast *entry,
void *user_data);
/**
* @brief Forwards a multicast packet by checking the local multicast
* routing table
*
* @param pkt The original received ipv6 packet to forward
* @param hdr The IPv6 header of the packet
*
* @return Number of interfaces which forwarded the packet, or a negative
* value in case of an error.
*/
int net_route_mcast_forward_packet(struct net_pkt *pkt,
const struct net_ipv6_hdr *hdr);
/**
* @brief Go through all the multicast routing entries and call callback
* for each entry that is in use.
@ -212,11 +228,13 @@ int net_route_mcast_foreach(net_route_mcast_cb_t cb,
*
* @param iface Network interface to use.
* @param group IPv6 multicast address.
* @param prefix_len Length of the IPv6 group that must match.
*
* @return Multicast routing entry.
*/
struct net_route_entry_mcast *net_route_mcast_add(struct net_if *iface,
struct in6_addr *group);
struct in6_addr *group,
uint8_t prefix_len);
/**
* @brief Delete a multicast routing entry.

View file

@ -0,0 +1,9 @@
# SPDX-License-Identifier: Apache-2.0
cmake_minimum_required(VERSION 3.13.1)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(route_mcast)
target_include_directories(app PRIVATE ${ZEPHYR_BASE}/subsys/net/ip)
FILE(GLOB app_sources src/*.c)
target_sources(app PRIVATE ${app_sources})

View file

@ -0,0 +1,25 @@
CONFIG_NETWORKING=y
CONFIG_NET_TEST=y
CONFIG_NET_IPV6=y
CONFIG_NET_UDP=y
CONFIG_NET_TCP=n
CONFIG_NET_IPV4=n
CONFIG_NET_MAX_CONTEXTS=4
CONFIG_NET_L2_DUMMY=y
CONFIG_NET_LOG=y
CONFIG_ENTROPY_GENERATOR=y
CONFIG_TEST_RANDOM_GENERATOR=y
CONFIG_NET_IPV6_DAD=n
CONFIG_NET_IPV6_MLD=n
CONFIG_NET_PKT_TX_COUNT=10
CONFIG_NET_PKT_RX_COUNT=5
CONFIG_NET_BUF_RX_COUNT=5
CONFIG_NET_BUF_TX_COUNT=5
CONFIG_NET_IF_UNICAST_IPV6_ADDR_COUNT=6
CONFIG_NET_IF_MAX_IPV6_COUNT=10
CONFIG_NET_MAX_ROUTES=4
CONFIG_NET_MAX_NEXTHOPS=8
CONFIG_NET_IPV6_MAX_NEIGHBORS=8
CONFIG_ZTEST=y
CONFIG_NET_MAX_MCAST_ROUTES=10
CONFIG_NET_ROUTE_MCAST=y

View file

@ -0,0 +1,665 @@
/* 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(struct device *dev)
{
return 0;
}
static uint8_t *net_route_mcast_get_mac(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(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, device_pm_control_nop,
&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, device_pm_control_nop,
&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, device_pm_control_nop,
&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);
}

View file

@ -0,0 +1,6 @@
common:
depends_on: netif
tests:
net.route_mcast:
min_ram: 21
tags: net route_mcast