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:
parent
92530b5fbb
commit
5931a29979
|
@ -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
|
||||
|
|
|
@ -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::).
|
||||
|
|
|
@ -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."
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
9
tests/net/route_mcast/CMakeLists.txt
Normal file
9
tests/net/route_mcast/CMakeLists.txt
Normal 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})
|
25
tests/net/route_mcast/prj.conf
Normal file
25
tests/net/route_mcast/prj.conf
Normal 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
|
665
tests/net/route_mcast/src/main.c
Normal file
665
tests/net/route_mcast/src/main.c
Normal 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);
|
||||
}
|
6
tests/net/route_mcast/testcase.yaml
Normal file
6
tests/net/route_mcast/testcase.yaml
Normal file
|
@ -0,0 +1,6 @@
|
|||
common:
|
||||
depends_on: netif
|
||||
tests:
|
||||
net.route_mcast:
|
||||
min_ram: 21
|
||||
tags: net route_mcast
|
Loading…
Reference in a new issue