blob: 6a9d6dac8d18fd6c0db22560e9574d4b4ffb133e [file] [log] [blame]
/* main.c - Application main entry point */
/*
* Copyright (c) 2016 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/types.h>
#include <stdbool.h>
#include <stddef.h>
#include <string.h>
#include <errno.h>
#include <misc/printk.h>
#include <linker/sections.h>
#include <tc_util.h>
#include <net/ethernet.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 "ipv6.h"
#include "icmpv6.h"
#include "nbr.h"
#include "rpl.h"
#if defined(CONFIG_NET_DEBUG_RPL)
#define DBG(fmt, ...) printk(fmt, ##__VA_ARGS__)
#else
#define DBG(fmt, ...)
#endif
static struct net_context *udp_ctx;
static struct in6_addr in6addr_my = { { { 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0x1 } } };
static struct in6_addr peer_addr = { { { 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0x2 } } };
static struct in6_addr in6addr_ll = { { { 0xfe, 0x80, 0x43, 0xb8, 0, 0, 0, 0,
0, 0, 0, 0xf2, 0xaa, 0x29, 0x02,
0x04 } } };
static struct in6_addr in6addr_mcast = { { { 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0x1 } } };
static struct net_linkaddr_storage lladdr_src_storage = {
.addr = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 },
.len = NET_LINK_ADDR_MAX_LENGTH
};
static struct net_linkaddr lladdr_src = {
.addr = lladdr_src_storage.addr,
.len = NET_LINK_ADDR_MAX_LENGTH
};
static bool test_failed;
static bool data_failure;
static bool feed_data; /* feed data back to IP stack */
static int msg_sending;
static int expected_icmpv6 = NET_ICMPV6_RPL;
static struct k_sem wait_data;
static struct net_if_link_cb link_cb;
static bool link_cb_called;
#define WAIT_TIME 250
struct net_rpl_test {
u8_t mac_addr[sizeof(struct net_eth_addr)];
struct net_linkaddr ll_addr;
};
int net_rpl_dev_init(struct device *dev)
{
return 0;
}
static u8_t *net_rpl_get_mac(struct device *dev)
{
struct net_rpl_test *rpl = dev->driver_data;
if (rpl->mac_addr[2] == 0x00) {
/* 00-00-5E-00-53-xx Documentation RFC 7042 */
rpl->mac_addr[0] = 0x00;
rpl->mac_addr[1] = 0x00;
rpl->mac_addr[2] = 0x5E;
rpl->mac_addr[3] = 0x00;
rpl->mac_addr[4] = 0x53;
rpl->mac_addr[5] = sys_rand32_get();
}
return rpl->mac_addr;
}
static void net_rpl_iface_init(struct net_if *iface)
{
u8_t *mac = net_rpl_get_mac(net_if_get_device(iface));
net_if_set_link_addr(iface, mac, sizeof(struct net_eth_addr),
NET_LINK_ETHERNET);
}
static void set_pkt_ll_addr(struct device *dev, struct net_pkt *pkt)
{
struct net_rpl_test *rpl = dev->driver_data;
struct net_linkaddr *src = net_pkt_ll_src(pkt);
struct net_linkaddr *dst = net_pkt_ll_dst(pkt);
dst->len = lladdr_src.len;
dst->addr = lladdr_src.addr;
src->len = sizeof(rpl->mac_addr);
src->addr = rpl->mac_addr;
}
#define NET_ICMP_HDR(pkt) ((struct net_icmp_hdr *)net_pkt_icmp_data(pkt))
static int tester_send(struct net_if *iface, struct net_pkt *pkt)
{
if (!pkt->frags) {
TC_ERROR("No data to send!\n");
return -ENODATA;
}
set_pkt_ll_addr(iface->dev, pkt);
/* By default we assume that the test is ok */
data_failure = false;
if (feed_data) {
net_pkt_ll_swap(pkt);
if (net_recv_data(iface, pkt) < 0) {
TC_ERROR("Data receive failed.");
net_pkt_unref(pkt);
test_failed = true;
}
k_sem_give(&wait_data);
return 0;
}
DBG("pkt %p to be sent len %lu\n", pkt, net_pkt_get_len(pkt));
#if defined(CONFIG_NET_DEBUG_RPL)
net_hexdump_frags("recv", pkt, false);
#endif
if (NET_ICMP_HDR(pkt)->type != expected_icmpv6) {
DBG("ICMPv6 type %d, expected %d\n",
NET_ICMP_HDR(pkt)->type, expected_icmpv6);
data_failure = true;
}
/* If we are not sending what is expected, then mark it as a failure
*/
if (msg_sending) {
if (msg_sending != NET_ICMP_HDR(pkt)->code) {
DBG("Received code %d, expected %d\n",
NET_ICMP_HDR(pkt)->code, msg_sending);
data_failure = true;
} else {
/* Pass sent DIO message back to us */
if (msg_sending == NET_RPL_DODAG_INFO_OBJ) {
net_pkt_ll_swap(pkt);
if (!net_recv_data(iface, pkt)) {
/* We must not unref the msg,
* as it should be unfreed by
* the upper stack.
*/
goto out;
}
}
}
}
net_pkt_unref(pkt);
out:
if (data_failure) {
test_failed = true;
}
msg_sending = 0;
k_sem_give(&wait_data);
return 0;
}
struct net_rpl_test net_rpl_data;
static struct net_if_api net_rpl_if_api = {
.init = net_rpl_iface_init,
.send = tester_send,
};
#define _ETH_L2_LAYER DUMMY_L2
#define _ETH_L2_CTX_TYPE NET_L2_GET_CTX_TYPE(DUMMY_L2)
NET_DEVICE_INIT(net_rpl_test, "net_rpl_test",
net_rpl_dev_init, &net_rpl_data, NULL,
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,
&net_rpl_if_api, _ETH_L2_LAYER,
_ETH_L2_CTX_TYPE, 127);
static void send_link_cb(struct net_if *iface, struct net_linkaddr *lladdr,
int status)
{
link_cb_called = true;
}
static bool test_init(void)
{
struct net_if_addr *ifaddr;
struct net_if_mcast_addr *maddr;
struct net_if *iface = net_if_get_default();
struct net_rpl_dag *dag;
if (!iface) {
TC_ERROR("Interface is NULL\n");
return false;
}
ifaddr = net_if_ipv6_addr_add(iface, &in6addr_my,
NET_ADDR_MANUAL, 0);
if (!ifaddr) {
TC_ERROR("Cannot add IPv6 address %s\n",
net_sprint_ipv6_addr(&in6addr_my));
return false;
}
/* For testing purposes we need to set the adddresses preferred */
ifaddr->addr_state = NET_ADDR_PREFERRED;
ifaddr = net_if_ipv6_addr_add(iface, &in6addr_ll,
NET_ADDR_MANUAL, 0);
if (!ifaddr) {
TC_ERROR("Cannot add IPv6 address %s\n",
net_sprint_ipv6_addr(&in6addr_ll));
return false;
}
ifaddr->addr_state = NET_ADDR_PREFERRED;
net_ipv6_addr_create(&in6addr_mcast, 0xff02, 0, 0, 0, 0, 0, 0, 0x0001);
maddr = net_if_ipv6_maddr_add(iface, &in6addr_mcast);
if (!maddr) {
TC_ERROR("Cannot add multicast IPv6 address %s\n",
net_sprint_ipv6_addr(&in6addr_mcast));
return false;
}
/* The semaphore is there to wait the data to be received. */
k_sem_init(&wait_data, 0, UINT_MAX);
net_if_register_link_cb(&link_cb, send_link_cb);
/* Creating a new RPL DAG */
net_rpl_set_root(iface, NET_RPL_DEFAULT_INSTANCE, &in6addr_my);
dag = net_rpl_get_any_dag();
net_rpl_set_prefix(iface, dag, &in6addr_my, 64);
return true;
}
static bool net_ctx_create(void)
{
int ret;
ret = net_context_get(AF_INET6, SOCK_DGRAM, IPPROTO_UDP,
&udp_ctx);
if (ret != 0) {
TC_ERROR("Context create IPv6 UDP test failed (%d vs %d)\n",
ret, 0);
return false;
}
return true;
}
static bool test_rpl_mcast_addr(void)
{
struct in6_addr rpl_mcast = { { { 0xff, 0x02, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0x1a } } };
struct in6_addr addr;
bool ret;
ret = net_rpl_is_ipv6_addr_mcast(&rpl_mcast);
if (!ret) {
TC_ERROR("RPL multicast address check failed.\n");
return false;
}
net_rpl_create_mcast_address(&addr);
ret = net_rpl_is_ipv6_addr_mcast(&addr);
if (!ret) {
TC_ERROR("Generated RPL multicast address check failed.\n");
return false;
}
return true;
}
static bool test_dio_dummy_input(void)
{
struct net_pkt *pkt;
struct net_buf *frag;
int ret;
pkt = net_pkt_get_tx(udp_ctx, K_FOREVER);
frag = net_pkt_get_data(udp_ctx, K_FOREVER);
net_pkt_frag_add(pkt, frag);
msg_sending = NET_RPL_DODAG_INFO_OBJ;
set_pkt_ll_addr(net_if_get_default()->dev, pkt);
ret = net_icmpv6_input(pkt, NET_ICMPV6_RPL, msg_sending);
if (!ret) {
TC_ERROR("%d: Callback in %s not called properly\n", __LINE__,
__func__);
return false;
}
data_failure = false;
k_sem_take(&wait_data, WAIT_TIME);
if (data_failure) {
TC_ERROR("%d: Unexpected ICMPv6 code received\n", __LINE__);
return false;
}
data_failure = false;
return true;
}
static bool test_dis_sending(void)
{
struct net_if *iface;
int ret;
iface = net_if_get_default();
msg_sending = NET_RPL_DODAG_SOLICIT;
ret = net_rpl_dis_send(NULL, iface);
if (ret) {
TC_ERROR("%d: Cannot send DIS (%d)\n", __LINE__, ret);
return false;
}
k_sem_take(&wait_data, WAIT_TIME);
if (data_failure) {
data_failure = false;
TC_ERROR("%d: Unexpected ICMPv6 code received\n", __LINE__);
return false;
}
data_failure = false;
return true;
}
static bool test_dao_sending_fail(void)
{
struct net_if *iface = NULL, *iface_def;
struct in6_addr *prefix, *prefix2;
struct net_rpl_instance instance = {
.instance_id = 42,
};
struct net_rpl_dag dag = {
.dag_id = { { { 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0x2 } } },
.instance = &instance,
};
struct net_rpl_parent parent = {
.dag = &dag,
};
int ret;
iface_def = net_if_get_default();
prefix2 = net_if_ipv6_get_global_addr(&iface_def);
prefix = net_if_ipv6_get_global_addr(&iface);
if (!prefix) {
TC_ERROR("Will not send DAO as no global address was found.");
return false;
}
if (iface != iface_def) {
TC_ERROR("Network interface mismatch (%p vs %p)\n",
iface, iface_def);
return false;
}
if (prefix != prefix2) {
TC_ERROR("Network interface mismatch or not set (%p vs %p)\n",
prefix, prefix2);
return false;
}
msg_sending = NET_RPL_DEST_ADV_OBJ;
/* The sending should fail at this point because the neighbor
* is not suppose to be found in neighbor cache.
*/
ret = net_rpl_dao_send(iface, &parent, prefix, 100);
if (!ret) {
TC_ERROR("DAO send succeed but should not have\n");
return false;
}
return true;
}
static bool net_test_send_ns(void)
{
struct net_if *iface = net_if_get_default();
struct net_nbr *nbr;
int ret;
/* As we are sending a node reachability NS (RFC 4861 ch 4.3),
* we need to add the neighbor to the cache, otherwise we cannot
* send a NS with unicast destination address.
*/
nbr = net_ipv6_nbr_add(iface,
&in6addr_my,
&iface->link_addr,
false,
NET_IPV6_NBR_STATE_REACHABLE);
if (!nbr) {
TC_ERROR("Cannot add to neighbor cache\n");
return false;
}
ret = net_ipv6_send_ns(iface,
NULL,
&peer_addr,
&in6addr_my,
&in6addr_my,
false);
if (ret < 0) {
TC_ERROR("Cannot send NS (%d)\n", ret);
return false;
}
return true;
}
static bool net_test_nbr_lookup_ok(void)
{
struct net_linkaddr_storage *llstorage;
struct net_nbr *nbr;
nbr = net_ipv6_nbr_lookup(net_if_get_default(),
&peer_addr);
if (!nbr) {
TC_ERROR("Neighbor %s not found in cache\n",
net_sprint_ipv6_addr(&peer_addr));
return false;
}
/* Set the ll address in the neighbor so that following
* tests work ok.
*/
llstorage = net_nbr_get_lladdr(nbr->idx);
memcpy(llstorage->addr, lladdr_src.addr, lladdr_src.len);
llstorage->len = lladdr_src.len;
DBG("[%d] Neighbor %s lladdr %s\n", nbr->idx,
net_sprint_ipv6_addr(&peer_addr),
net_sprint_ll_addr(llstorage->addr, llstorage->len));
net_ipv6_nbr_data(nbr)->state = NET_IPV6_NBR_STATE_REACHABLE;
return true;
}
static bool populate_nbr_cache(void)
{
struct net_nbr *nbr;
msg_sending = NET_ICMPV6_NS;
feed_data = true;
data_failure = false;
if (!net_test_send_ns()) {
return false;
}
k_sem_take(&wait_data, WAIT_TIME);
feed_data = false;
if (data_failure) {
data_failure = false;
return false;
}
data_failure = false;
nbr = net_ipv6_nbr_add(net_if_get_default(),
&peer_addr,
&lladdr_src,
false,
NET_IPV6_NBR_STATE_REACHABLE);
if (!nbr) {
TC_ERROR("Cannot add peer to neighbor cache\n");
return false;
}
if (!net_test_nbr_lookup_ok()) {
return false;
}
return true;
}
#if 0
/* This test is currently disabled as it needs more TLC */
static bool test_dao_sending_ok(void)
{
struct net_if *iface = NULL, *iface_def;
struct in6_addr *prefix, *prefix2;
struct net_rpl_dag *dag;
int ret;
iface_def = net_if_get_default();
prefix2 = net_if_ipv6_get_global_addr(&iface_def);
prefix = net_if_ipv6_get_global_addr(&iface);
if (!prefix) {
TC_ERROR("Will not send DAO as no global address was found.");
return false;
}
if (iface != iface_def) {
TC_ERROR("Network interface mismatch (%p vs %p)\n",
iface, iface_def);
return false;
}
if (prefix != prefix2) {
TC_ERROR("Network interface mismatch or not set (%p vs %p)\n",
prefix, prefix2);
return false;
}
msg_sending = NET_RPL_DEST_ADV_OBJ;
net_rpl_set_root(iface_def, NET_RPL_DEFAULT_INSTANCE, prefix);
dag = net_rpl_get_any_dag();
if (!net_rpl_set_prefix(iface, dag, &prefix, 64)) {
TC_ERROR("Failed to create a new RPL DAG\n");
return false;
}
ret = net_rpl_dao_send(iface, dag->preferred_parent, prefix, 100);
if (ret) {
TC_ERROR("%d: Cannot send DAO (%d)\n", __LINE__, ret);
return false;
}
k_sem_take(&wait_data, WAIT_TIME);
if (data_failure) {
data_failure = false;
TC_ERROR("%d: Unexpected ICMPv6 code received\n", __LINE__);
return false;
}
data_failure = false;
return true;
}
/* This test fails currently, it needs more TLC */
static bool test_link_cb(void)
{
link_cb_called = false;
msg_sending = 0;
expected_icmpv6 = NET_ICMPV6_NS;
net_test_send_ns();
k_sem_take(&wait_data, WAIT_TIME);
/* Restore earlier expected value, by default we only accept
* RPL ICMPv6 messages.
*/
expected_icmpv6 = NET_ICMPV6_RPL;
if (!link_cb_called) {
TC_ERROR("%d: Link cb not called\n", __LINE__);
return false;
}
return true;
}
#endif
static bool test_dio_receive_dest(void)
{
struct net_if *iface = NULL, *iface_def;
struct in6_addr *prefix, *prefix2;
struct net_rpl_instance instance = {
.instance_id = CONFIG_NET_RPL_DEFAULT_INSTANCE,
.mop = NET_RPL_MOP_STORING_NO_MULTICAST,
.min_hop_rank_inc = 100,
.ocp = 1, /* MRH OF */
};
struct net_rpl_dag dag = {
.dag_id = { { { 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0x1 } } },
.instance = &instance,
.prefix_info = {
.prefix = { { { 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0 } } },
.length = 64,
},
.version = 1,
.rank = 2,
};
int ret;
instance.current_dag = &dag;
iface_def = net_if_get_default();
prefix2 = net_if_ipv6_get_global_addr(&iface_def);
prefix = net_if_ipv6_get_global_addr(&iface);
if (!prefix) {
TC_ERROR("Will not send DAO as no global address was found.");
return false;
}
if (iface != iface_def) {
TC_ERROR("Network interface mismatch (%p vs %p)\n",
iface, iface_def);
return false;
}
if (prefix != prefix2) {
TC_ERROR("Network interface mismatch or not set (%p vs %p)\n",
prefix, prefix2);
return false;
}
msg_sending = NET_RPL_DODAG_INFO_OBJ;
ret = net_rpl_dio_send(iface, &instance, &peer_addr, &in6addr_my);
if (ret) {
TC_ERROR("%d: Cannot send DIO (%d)\n", __LINE__, ret);
return false;
}
k_sem_take(&wait_data, WAIT_TIME);
if (data_failure) {
data_failure = false;
TC_ERROR("%d: Unexpected ICMPv6 code received\n", __LINE__);
return false;
}
data_failure = false;
return true;
}
static const struct {
const char *name;
bool (*func)(void);
} tests[] = {
{ "test init", test_init },
{ "test ctx create", net_ctx_create },
{ "RPL multicast address test", test_rpl_mcast_addr },
{ "DIO input handler test", test_dio_dummy_input },
{ "DIS sending", test_dis_sending },
{ "DAO sending fail", test_dao_sending_fail },
{ "Populate neighbor cache", populate_nbr_cache },
{ "DIO receive dest set", test_dio_receive_dest },
#if 0
{ "Link cb test", test_link_cb },
{ "DAO sending ok", test_dao_sending_ok },
{ "DIO receive dest not set", test_dio_receive },
#endif
};
void main(void)
{
int count, pass;
for (count = 0, pass = 0; count < ARRAY_SIZE(tests); count++) {
TC_START(tests[count].name);
test_failed = false;
if (!tests[count].func() || test_failed) {
TC_END(FAIL, "failed\n");
} else {
TC_END(PASS, "passed\n");
pass++;
}
}
TC_END_REPORT(((pass != ARRAY_SIZE(tests)) ? TC_FAIL : TC_PASS));
}