Merge pull request #16824 from benpicco/gnrc_netif_ipv6_wait_for_prefix

gnrc/netif: add gnrc_netif_ipv6_wait_for_global_address()
This commit is contained in:
benpicco 2021-09-17 14:32:53 +02:00 committed by GitHub
commit ea80df1780
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 345 additions and 3 deletions

View File

@ -422,7 +422,7 @@ static inline int gnrc_netif_ipv6_addrs_get(const gnrc_netif_t *netif,
* @return -ENOTSUP, if @p netif doesn't support IPv6.
*/
static inline int gnrc_netif_ipv6_addr_add(const gnrc_netif_t *netif,
ipv6_addr_t *addr, unsigned pfx_len,
const ipv6_addr_t *addr, unsigned pfx_len,
uint8_t flags)
{
assert(netif != NULL);
@ -447,7 +447,7 @@ static inline int gnrc_netif_ipv6_addr_add(const gnrc_netif_t *netif,
* @return -ENOTSUP, if @p netif doesn't support IPv6.
*/
static inline int gnrc_netif_ipv6_addr_remove(const gnrc_netif_t *netif,
ipv6_addr_t *addr)
const ipv6_addr_t *addr)
{
assert(netif != NULL);
assert(addr != NULL);
@ -656,6 +656,23 @@ static inline msg_bus_t* gnrc_netif_get_bus(gnrc_netif_t *netif,
assert(type < GNRC_NETIF_BUS_NUMOF);
return &netif->bus[type];
}
/**
* @brief Wait for a global address to become available.
* This function blocks until a valid global address has been
* configured, e.g. by receiving a router advertisement or via DHCPv6.
*
* Requires the `gnrc_netif_bus` module.
*
* @param netif pointer to the interface
* May be NULL, then this checks for a global address
* on *any* interface.
* @param timeout_ms Time to wait for an address to become available, in ms.
*
* @return true if a global address is configured
*/
bool gnrc_netif_ipv6_wait_for_global_address(gnrc_netif_t *netif,
uint32_t timeout_ms);
#endif /* MODULE_GNRC_NETIF_BUS */
#ifdef __cplusplus

View File

@ -41,7 +41,7 @@
#include "fmt.h"
#include "log.h"
#include "sched.h"
#if (CONFIG_GNRC_NETIF_MIN_WAIT_AFTER_SEND_US > 0U)
#if IS_USED(MODULE_XTIMER) || IS_USED(MODULE_ZTIMER_XTIMER_COMPAT)
#include "xtimer.h"
#endif
@ -1298,6 +1298,105 @@ int gnrc_netif_ipv6_add_prefix(gnrc_netif_t *netif,
out:
return res;
}
#if IS_USED(MODULE_GNRC_NETIF_BUS)
static bool _has_global_addr(gnrc_netif_t *netif)
{
bool has_global = false;
gnrc_netif_acquire(netif);
for (unsigned i = 0; i < CONFIG_GNRC_NETIF_IPV6_ADDRS_NUMOF; i++) {
if (ipv6_addr_is_unspecified(&netif->ipv6.addrs[i])) {
continue;
}
if (!ipv6_addr_is_link_local(&netif->ipv6.addrs[i])) {
has_global = true;
break;
}
}
gnrc_netif_release(netif);
return has_global;
}
static void _netif_bus_attach_and_subscribe_addr_valid(gnrc_netif_t *netif,
msg_bus_entry_t *sub)
{
msg_bus_t *bus = gnrc_netif_get_bus(netif, GNRC_NETIF_BUS_IPV6);
msg_bus_attach(bus, sub);
msg_bus_subscribe(sub, GNRC_IPV6_EVENT_ADDR_VALID);
}
static void _netif_bus_detach(gnrc_netif_t *netif, msg_bus_entry_t *sub)
{
msg_bus_t *bus = gnrc_netif_get_bus(netif, GNRC_NETIF_BUS_IPV6);
msg_bus_detach(bus, sub);
}
bool gnrc_netif_ipv6_wait_for_global_address(gnrc_netif_t *netif,
uint32_t timeout_ms)
{
unsigned netif_numof = gnrc_netif_numof();
/* no interfaces */
if (netif_numof == 0) {
return false;
}
msg_bus_entry_t subs[netif_numof];
bool has_global = false;
if (netif) {
if (_has_global_addr(netif)) {
return true;
}
_netif_bus_attach_and_subscribe_addr_valid(netif, &subs[0]);
} else {
/* subscribe to all interfaces */
for (unsigned count = 0;
(netif = gnrc_netif_iter(netif));
count++) {
if (_has_global_addr(netif)) {
has_global = true;
}
_netif_bus_attach_and_subscribe_addr_valid(netif, &subs[count]);
}
}
/* wait for global address */
msg_t m;
while (!has_global) {
if (xtimer_msg_receive_timeout(&m, timeout_ms * US_PER_MS) < 0) {
DEBUG_PUTS("gnrc_netif: timeout waiting for prefix");
break;
}
if (ipv6_addr_is_link_local(m.content.ptr)) {
DEBUG_PUTS("gnrc_netif: got link-local address");
} else {
DEBUG_PUTS("gnrc_netif: got global address");
has_global = true;
}
}
/* called with a given interface */
if (netif != NULL) {
_netif_bus_detach(netif, &subs[0]);
} else {
/* unsubscribe all */
for (unsigned count = 0;
(netif = gnrc_netif_iter(netif));
count++) {
_netif_bus_detach(netif, &subs[count]);
}
}
return has_global;
}
#endif /* IS_USED(MODULE_GNRC_NETIF_BUS) */
#endif /* IS_USED(MODULE_GNRC_NETIF_IPV6) */
static void _update_l2addr_from_dev(gnrc_netif_t *netif)

View File

@ -0,0 +1,19 @@
include ../Makefile.tests_common
USEMODULE += embunit
USEMODULE += gnrc_netif
USEMODULE += gnrc_netif_bus
USEMODULE += gnrc_ipv6
USEMODULE += netdev_test
USEMODULE += xtimer
# deactivate automatically emitted packets from IPv6 neighbor discovery
CFLAGS += -DCONFIG_GNRC_IPV6_NIB_ARSM=0
CFLAGS += -DCONFIG_GNRC_IPV6_NIB_SLAAC=0
CFLAGS += -DCONFIG_GNRC_IPV6_NIB_NO_RTR_SOL=1
CFLAGS += -DGNRC_NETIF_ADDRS_NUMOF=16
CFLAGS += -DGNRC_NETIF_GROUPS_NUMOF=8
CFLAGS += -DLOG_LEVEL=LOG_NONE
CFLAGS += -DTEST_SUITES
include $(RIOTBASE)/Makefile.include

View File

@ -0,0 +1,28 @@
BOARD_INSUFFICIENT_MEMORY := \
arduino-duemilanove \
arduino-leonardo \
arduino-mega2560 \
arduino-nano \
arduino-uno \
atmega328p \
atmega328p-xplained-mini \
bluepill-stm32f030c8 \
i-nucleo-lrwan1 \
msb-430 \
msb-430h \
nucleo-f030r8 \
nucleo-f031k6 \
nucleo-f042k6 \
nucleo-f303k8 \
nucleo-f334r8 \
nucleo-l011k4 \
nucleo-l031k6 \
nucleo-l053r8 \
samd10-xmini \
slstk3400a \
stk3200 \
stm32f030f4-demo \
stm32f0discovery \
stm32l0538-disco \
waspmote-pro \
#

View File

@ -0,0 +1,161 @@
/*
* Copyright (C) 2021 ML!PA Consulting GmbH
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup tests
* @{
*
* @file
* @brief Tests notification for global address
*
* @author Benjamin Valentin <benjamin.valentin@ml-pa.com>
*
* @}
*/
#include <errno.h>
#include <stdio.h>
#include "embUnit.h"
#include "embUnit/embUnit.h"
#include "net/ipv6.h"
#include "net/gnrc/netif.h"
#include "net/gnrc/netif/raw.h"
#include "net/netdev_test.h"
#include "xtimer.h"
#define TEST_NETIF_NUMOF 2
#define TEST_NETIF_PRIO 3
static netdev_test_t netdev_test[TEST_NETIF_NUMOF];
static gnrc_netif_t netif_test[TEST_NETIF_NUMOF];
static char netif_stack[TEST_NETIF_NUMOF][THREAD_STACKSIZE_DEFAULT];
static char adder_stack[THREAD_STACKSIZE_DEFAULT];
static gnrc_netif_t *_test_netif;
static const ipv6_addr_t _test_addr = {{ 0x20, 0x01, 0x0d, 0xd8,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01 }};
static void tear_down(void)
{
if (_test_netif == NULL) {
return;
}
gnrc_netif_ipv6_addr_remove(_test_netif, &_test_addr);
_test_netif = NULL;
}
static void *_adder_thread(void *netif)
{
xtimer_msleep(10);
gnrc_netif_ipv6_addr_add(netif, &_test_addr, 64,
GNRC_NETIF_IPV6_ADDRS_FLAGS_STATE_VALID);
return NULL;
}
static void _add_delayed_addr(gnrc_netif_t *netif)
{
_test_netif = netif;
memset(adder_stack, 0, sizeof(adder_stack));
thread_create(adder_stack, sizeof(adder_stack),
THREAD_PRIORITY_MAIN - 1,
THREAD_CREATE_STACKTEST,
_adder_thread, netif, "add_addr");
}
static void _assert_wait_blocks(gnrc_netif_t *add_netif,
gnrc_netif_t *wait_netif,
bool success)
{
uint32_t now = xtimer_now_usec();
uint32_t timeout = 20;
_add_delayed_addr(add_netif);
TEST_ASSERT(gnrc_netif_ipv6_wait_for_global_address(wait_netif,
timeout) == success);
if (success) {
TEST_ASSERT(((xtimer_now_usec() - now) / US_PER_MS) < timeout);
} else {
TEST_ASSERT(((xtimer_now_usec() - now) / US_PER_MS) >= timeout);
}
}
static void test_wait_timeout(void)
{
TEST_ASSERT_EQUAL_INT(TEST_NETIF_NUMOF, gnrc_netif_numof());
TEST_ASSERT(!gnrc_netif_ipv6_wait_for_global_address(NULL, 10));
TEST_ASSERT(!gnrc_netif_ipv6_wait_for_global_address(&netif_test[0], 10));
}
static void test_wait_timeout_other_iface(void)
{
TEST_ASSERT_EQUAL_INT(TEST_NETIF_NUMOF, gnrc_netif_numof());
/* no event when adding addr to other interface */
_assert_wait_blocks(&netif_test[1], &netif_test[0], false);
}
static void test_wait_success(void)
{
/* event when adding addr to specified interface */
_assert_wait_blocks(&netif_test[0], &netif_test[0], true);
}
static void test_wait_success_any_iface(void)
{
/* event when adding addr to any interface */
_assert_wait_blocks(&netif_test[0], NULL, true);
}
static Test *embunit_tests_gnrc_netif(void)
{
EMB_UNIT_TESTFIXTURES(fixtures) {
new_TestFixture(test_wait_timeout),
new_TestFixture(test_wait_timeout_other_iface),
new_TestFixture(test_wait_success),
new_TestFixture(test_wait_success_any_iface),
};
EMB_UNIT_TESTCALLER(tests, NULL, tear_down, fixtures);
return (Test *)&tests;
}
static int netdev_get_device_type(netdev_t *dev, void *value, size_t max_len)
{
(void)dev;
(void)max_len;
const uint16_t type = NETDEV_TYPE_SLIP;
memcpy(value, &type, sizeof(type));
return sizeof(uint16_t);
}
static void _setup_mock_netif(netdev_test_t *dev, gnrc_netif_t *netif,
void *stack, size_t stack_size, unsigned prio)
{
netdev_test_setup(dev, NULL);
netdev_test_set_get_cb(dev, NETOPT_DEVICE_TYPE, netdev_get_device_type);
gnrc_netif_raw_create(netif, stack, stack_size, prio,
"netdev_test", &dev->netdev.netdev);
}
int main(void)
{
for (unsigned i = 0; i < TEST_NETIF_NUMOF; ++i) {
_setup_mock_netif(&netdev_test[i], &netif_test[i],
netif_stack[i], sizeof(netif_stack[i]),
TEST_NETIF_PRIO);
}
TESTS_START();
TESTS_RUN(embunit_tests_gnrc_netif());
TESTS_END();
return 0;
}

View File

@ -0,0 +1,18 @@
#!/usr/bin/env python3
# Copyright (C) 2021 Benjamin Valentin <benjamin.valentin@ml-pa.com>
#
# This file is subject to the terms and conditions of the GNU Lesser
# General Public License v2.1. See the file LICENSE in the top level
# directory for more details.
import sys
from testrunner import run
def testfunc(child):
child.expect_exact("OK")
if __name__ == "__main__":
sys.exit(run(testfunc))