| /* 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)); |
| } |