gnrc/netif: add gnrc_netif_ipv6_wait_for_global_address()

This adds a function to wait until a global address becomes available.
This commit is contained in:
Benjamin Valentin 2021-09-07 17:31:54 +02:00
parent f93d1276b4
commit 4bd53e70fa
2 changed files with 117 additions and 1 deletions

View File

@ -656,6 +656,23 @@ static inline msg_bus_t* gnrc_netif_get_bus(gnrc_netif_t *netif,
assert(type < GNRC_NETIF_BUS_NUMOF); assert(type < GNRC_NETIF_BUS_NUMOF);
return &netif->bus[type]; 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 */ #endif /* MODULE_GNRC_NETIF_BUS */
#ifdef __cplusplus #ifdef __cplusplus

View File

@ -41,7 +41,7 @@
#include "fmt.h" #include "fmt.h"
#include "log.h" #include "log.h"
#include "sched.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" #include "xtimer.h"
#endif #endif
@ -1298,6 +1298,105 @@ int gnrc_netif_ipv6_add_prefix(gnrc_netif_t *netif,
out: out:
return res; 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) */ #endif /* IS_USED(MODULE_GNRC_NETIF_IPV6) */
static void _update_l2addr_from_dev(gnrc_netif_t *netif) static void _update_l2addr_from_dev(gnrc_netif_t *netif)