diff --git a/sys/include/net/gnrc/ipv6/nib.h b/sys/include/net/gnrc/ipv6/nib.h index 2fbcf9ee67ba..9e89d7f67d9d 100644 --- a/sys/include/net/gnrc/ipv6/nib.h +++ b/sys/include/net/gnrc/ipv6/nib.h @@ -233,16 +233,6 @@ extern "C" { * @note Only handled with @ref CONFIG_GNRC_IPV6_NIB_DNS != 0 */ #define GNRC_IPV6_NIB_RDNSS_TIMEOUT (0x4fd3U) - -/** - * @brief Interface up event - */ -#define GNRC_IPV6_NIB_IFACE_UP (0x4fd4U) - -/** - * @brief Interface down event - */ -#define GNRC_IPV6_NIB_IFACE_DOWN (0x4fd5U) /** @} */ /** diff --git a/sys/include/net/gnrc/netif.h b/sys/include/net/gnrc/netif.h index a6212e608b59..b5ee4d0cff51 100644 --- a/sys/include/net/gnrc/netif.h +++ b/sys/include/net/gnrc/netif.h @@ -103,9 +103,30 @@ typedef enum { GNRC_NETIF_BUS_IPV6, /**< provides @ref gnrc_ipv6_event_t messages to subscribers */ #endif + GNRC_NETIF_BUS_IFACE, /**< provides @ref gnrc_netif_event_t + messages to subscribers */ GNRC_NETIF_BUS_NUMOF } gnrc_netif_bus_t; +/** + * @brief Event types for GNRC_NETIF_BUS_IFACE per-interface message bus + */ +typedef enum { + /** + * @brief Link state changed to UP + * + * The event is generated when the link state on the interface became online. + */ + GNRC_NETIF_EVENT_LINK_STATE_CHANGED_DOWN, + + /** + * @brief Link state changed to DOWN + * + * The event is generated when the link state on the interface become offline. + */ + GNRC_NETIF_EVENT_LINK_STATE_CHANGED_UP, +} gnrc_netif_event_t; + /** * @brief Event types for GNRC_NETIF_BUS_IPV6 per-interface message bus */ @@ -731,6 +752,28 @@ static inline int gnrc_netif_send(gnrc_netif_t *netif, gnrc_pktsnip_t *pkt) return gnrc_netapi_send(netif->pid, pkt); } +/** + * @brief Bring up an interface that has previously been disabled + * + * @param netif pointer to the interface + * @param timeout_ms timeout for confirmation from the interface + * + * @return 0 on success + * negative error + */ +int gnrc_netif_up(gnrc_netif_t *netif, unsigned timeout_ms); + +/** + * @brief Disable a network interface + * + * @param netif pointer to the interface + * @param timeout_ms timeout for confirmation from the interface + * + * @return 0 on success + * negative error + */ +int gnrc_netif_down(gnrc_netif_t *netif, unsigned timeout_ms); + #if defined(MODULE_GNRC_NETIF_BUS) || DOXYGEN /** * @brief Get a message bus of a given @ref gnrc_netif_t interface. diff --git a/sys/include/net/gnrc/netif/internal.h b/sys/include/net/gnrc/netif/internal.h index 353eb8a8412a..0fe8a1bd71a4 100644 --- a/sys/include/net/gnrc/netif/internal.h +++ b/sys/include/net/gnrc/netif/internal.h @@ -304,6 +304,25 @@ static inline void gnrc_netif_ipv6_bus_post(gnrc_netif_t *netif, int type, } #endif /* IS_USED(MODULE_GNRC_NETIF_IPV6) || defined(DOXYGEN) */ +/** + * @brief Posts a message to the event bus of the interface + * + * @param[in] netif Pointer to the interface + * @param[in] type [Type of the event](@ref gnrc_netif_event_t) + * @param[in] ctx The context of the event + */ +static inline void gnrc_netif_event_bus_post(gnrc_netif_t *netif, int type, + const void *ctx) +{ +#ifdef MODULE_GNRC_NETIF_BUS + msg_bus_post(&netif->bus[GNRC_NETIF_BUS_IFACE], type, ctx); +#else + (void) netif; + (void) type; + (void) ctx; +#endif +} + /** * @brief Checks if the interface represents a router according to RFC 4861 * diff --git a/sys/include/net/gnrc/netif/ipv6.h b/sys/include/net/gnrc/netif/ipv6.h index 8bd2ec0ca251..060743270197 100644 --- a/sys/include/net/gnrc/netif/ipv6.h +++ b/sys/include/net/gnrc/netif/ipv6.h @@ -107,6 +107,10 @@ typedef struct { netstats_t stats; #endif #if defined(MODULE_GNRC_IPV6_NIB) || DOXYGEN + /** + * @brief IPv6 interface bus event subscription + */ + msg_bus_entry_t netif_sub; #if IS_ACTIVE(CONFIG_GNRC_IPV6_NIB_ROUTER) || DOXYGEN /** * @brief Route info callback diff --git a/sys/net/gnrc/Makefile.dep b/sys/net/gnrc/Makefile.dep index fe713b446726..d2bf2eb5fbd3 100644 --- a/sys/net/gnrc/Makefile.dep +++ b/sys/net/gnrc/Makefile.dep @@ -392,6 +392,7 @@ ifneq (,$(filter gnrc_ipv6_nib,$(USEMODULE))) USEMODULE += gnrc_ndp USEMODULE += gnrc_netif USEMODULE += gnrc_netif_ipv6 + USEMODULE += gnrc_netif_bus USEMODULE += ipv6_addr USEMODULE += random endif diff --git a/sys/net/gnrc/netif/gnrc_netif.c b/sys/net/gnrc/netif/gnrc_netif.c index bf8df2d6646a..929f8a490efb 100644 --- a/sys/net/gnrc/netif/gnrc_netif.c +++ b/sys/net/gnrc/netif/gnrc_netif.c @@ -311,6 +311,28 @@ int gnrc_netif_get_from_netdev(gnrc_netif_t *netif, gnrc_netapi_opt_t *opt) res = netif->dev->driver->get(netif->dev, opt->opt, opt->data, opt->data_len); } + if (res == -ENOTSUP) { + /* handle default values */ + switch (opt->opt) { + case NETOPT_LINK: { + netopt_state_t state; + res = netif->dev->driver->get(netif->dev, NETOPT_STATE, &state, sizeof(state)); + if (res < 0) { + break; + } + res = sizeof(netopt_enable_t); + if ((state == NETOPT_STATE_SLEEP) || (state == NETOPT_STATE_OFF) || + (state == NETOPT_STATE_STANDBY)) { + *((netopt_enable_t *)opt->data) = NETOPT_DISABLE; + } else { + *((netopt_enable_t *)opt->data) = NETOPT_ENABLE; + } + break; + } + default: + break; + } + } gnrc_netif_release(netif); return res; } @@ -1437,6 +1459,61 @@ bool gnrc_netif_ipv6_wait_for_global_address(gnrc_netif_t *netif, #endif /* IS_USED(MODULE_GNRC_NETIF_BUS) */ #endif /* IS_USED(MODULE_GNRC_NETIF_IPV6) */ +static int _change_state_and_wait(gnrc_netif_t *netif, netopt_state_t state, unsigned timeout_ms) +{ + int res = 0; + + netopt_state_t state_prev; + while (gnrc_netapi_get(netif->pid, NETOPT_STATE, 0, &state_prev, sizeof(state_prev)) == -EBUSY) {} + + if (state == state_prev) { + return 0; + } + +#if IS_USED(MODULE_GNRC_NETIF_BUS) + gnrc_netif_event_t event; + + if ((state == NETOPT_STATE_SLEEP) || (state == NETOPT_STATE_OFF) || + (state == NETOPT_STATE_STANDBY)) { + event = GNRC_NETIF_EVENT_LINK_STATE_CHANGED_DOWN; + } else { + event = GNRC_NETIF_EVENT_LINK_STATE_CHANGED_UP; + } + + msg_bus_entry_t sub; + msg_bus_t *bus = gnrc_netif_get_bus(netif, GNRC_NETIF_BUS_IFACE); + msg_bus_attach(bus, &sub); + msg_bus_subscribe(&sub, event); +#endif + + while (gnrc_netapi_set(netif->pid, NETOPT_STATE, 0, &state, sizeof(state)) == -EBUSY) {} + + if (timeout_ms == 0) { + return 0; + } + +#if IS_USED(MODULE_GNRC_NETIF_BUS) + msg_t m; + if (ztimer_msg_receive_timeout(ZTIMER_MSEC, &m, timeout_ms) < 0) { + DEBUG_PUTS("gnrc_netif: timeout waiting for state change"); + res = -ETIMEDOUT; + } + msg_bus_detach(bus, &sub); +#endif + + return res; +} + +int gnrc_netif_up(gnrc_netif_t *netif, unsigned timeout_ms) +{ + return _change_state_and_wait(netif, NETOPT_STATE_IDLE, timeout_ms); +} + +int gnrc_netif_down(gnrc_netif_t *netif, unsigned timeout_ms) +{ + return _change_state_and_wait(netif, NETOPT_STATE_SLEEP, timeout_ms); +} + static void _update_l2addr_from_dev(gnrc_netif_t *netif) { netdev_t *dev = netif->dev; @@ -2051,18 +2128,14 @@ static void _event_cb(netdev_t *dev, netdev_event_t event) gnrc_pktsnip_t *pkt = NULL; switch (event) { case NETDEV_EVENT_LINK_UP: - if (IS_USED(MODULE_GNRC_IPV6)) { - msg_t msg = { .type = GNRC_IPV6_NIB_IFACE_UP, .content = { .ptr = netif } }; - - msg_send(&msg, gnrc_ipv6_pid); - } + gnrc_netif_event_bus_post(netif, + GNRC_NETIF_EVENT_LINK_STATE_CHANGED_UP, + netif); break; case NETDEV_EVENT_LINK_DOWN: - if (IS_USED(MODULE_GNRC_IPV6)) { - msg_t msg = { .type = GNRC_IPV6_NIB_IFACE_DOWN, .content = { .ptr = netif } }; - - msg_send(&msg, gnrc_ipv6_pid); - } + gnrc_netif_event_bus_post(netif, + GNRC_NETIF_EVENT_LINK_STATE_CHANGED_DOWN, + netif); break; case NETDEV_EVENT_RX_COMPLETE: pkt = netif->ops->recv(netif); diff --git a/sys/net/gnrc/network_layer/ipv6/gnrc_ipv6.c b/sys/net/gnrc/network_layer/ipv6/gnrc_ipv6.c index a70f2e0783df..e7b9cc3d6d70 100644 --- a/sys/net/gnrc/network_layer/ipv6/gnrc_ipv6.c +++ b/sys/net/gnrc/network_layer/ipv6/gnrc_ipv6.c @@ -186,6 +186,22 @@ static void *_event_loop(void *args) /* register interest in all IPv6 packets */ gnrc_netreg_register(GNRC_NETTYPE_IPV6, &me_reg); + DEBUG_PUTS("ipv6: wait for interfaces to be ready"); + while (gnrc_netif_iter(NULL) == NULL) { + ztimer_sleep(ZTIMER_MSEC, 250); + } + + /* subscribe to interface events */ + gnrc_netif_t *netif = NULL; + while ((netif = gnrc_netif_iter(netif))) { + DEBUG("ipv6: subscribe to events on interface %u\n", netif->pid); + + msg_bus_t *bus = gnrc_netif_get_bus(netif, GNRC_NETIF_BUS_IFACE); + msg_bus_attach(bus, &netif->ipv6.netif_sub); + msg_bus_subscribe(&netif->ipv6.netif_sub, GNRC_NETIF_EVENT_LINK_STATE_CHANGED_UP); + msg_bus_subscribe(&netif->ipv6.netif_sub, GNRC_NETIF_EVENT_LINK_STATE_CHANGED_DOWN); + } + /* preinitialize ACK */ reply.type = GNRC_NETAPI_MSG_TYPE_ACK; @@ -194,7 +210,7 @@ static void *_event_loop(void *args) DEBUG("ipv6: waiting for incoming message.\n"); msg_receive(&msg); - switch (msg.type) { + switch (msg_bus_get_type(&msg)) { case GNRC_NETAPI_MSG_TYPE_RCV: DEBUG("ipv6: GNRC_NETAPI_MSG_TYPE_RCV received\n"); _receive(msg.content.ptr); @@ -244,10 +260,10 @@ static void *_event_loop(void *args) DEBUG("ipv6: NIB timer event received\n"); gnrc_ipv6_nib_handle_timer_event(msg.content.ptr, msg.type); break; - case GNRC_IPV6_NIB_IFACE_UP: + case GNRC_NETIF_EVENT_LINK_STATE_CHANGED_UP: gnrc_ipv6_nib_iface_up(msg.content.ptr); break; - case GNRC_IPV6_NIB_IFACE_DOWN: + case GNRC_NETIF_EVENT_LINK_STATE_CHANGED_DOWN: gnrc_ipv6_nib_iface_down(msg.content.ptr, false); break; default: