From 19a4ba04d1bf8ec8c8022a85b230a156323f02c4 Mon Sep 17 00:00:00 2001 From: Elena Frank Date: Tue, 9 Dec 2025 14:17:48 +0100 Subject: [PATCH 1/9] gnrc/netapi: add `notify` message API The netapi notify API enables protocol-independent, cross-layer notification events. Lower layers in the network stack can use this to inform upper layers of network events. --- sys/include/net/gnrc.h | 1 + sys/include/net/gnrc/netapi.h | 28 +++- sys/include/net/gnrc/netapi/notify.h | 91 +++++++++++++ sys/include/net/gnrc/netreg.h | 3 +- sys/include/net/gnrc/nettype.h | 14 ++ sys/net/gnrc/Makefile | 3 + sys/net/gnrc/Makefile.dep | 6 +- sys/net/gnrc/netapi/gnrc_netapi.c | 120 +++++++++++++----- sys/net/gnrc/netapi/notify/Makefile | 3 + .../gnrc/netapi/notify/gnrc_netapi_notify.c | 38 ++++++ 10 files changed, 269 insertions(+), 38 deletions(-) create mode 100644 sys/include/net/gnrc/netapi/notify.h create mode 100644 sys/net/gnrc/netapi/notify/Makefile create mode 100644 sys/net/gnrc/netapi/notify/gnrc_netapi_notify.c diff --git a/sys/include/net/gnrc.h b/sys/include/net/gnrc.h index c970eb9638..99b3985c21 100644 --- a/sys/include/net/gnrc.h +++ b/sys/include/net/gnrc.h @@ -288,6 +288,7 @@ #include "net/netopt.h" #include "net/gnrc/netapi.h" +#include "net/gnrc/netapi/notify.h" #include "net/gnrc/netreg.h" #include "net/gnrc/nettype.h" #include "net/gnrc/netif.h" diff --git a/sys/include/net/gnrc/netapi.h b/sys/include/net/gnrc/netapi.h index ea8157e408..6c68b39368 100644 --- a/sys/include/net/gnrc/netapi.h +++ b/sys/include/net/gnrc/netapi.h @@ -60,6 +60,7 @@ #include "thread.h" #include "net/netopt.h" +#include "net/gnrc/netapi/notify.h" #include "net/gnrc/nettype.h" #include "net/gnrc/pkt.h" @@ -88,10 +89,17 @@ extern "C" { #define GNRC_NETAPI_MSG_TYPE_GET (0x0204) /** - * @brief @ref core_msg type for replying to get and set option messages + * @brief @ref core_msg type for replying to get/set option, and notify messages */ #define GNRC_NETAPI_MSG_TYPE_ACK (0x0205) +/** + * @brief @ref core_msg type for sending a general event notifications to one or more subscribers + * + * This sends back a @ref GNRC_NETAPI_MSG_TYPE_ACK, so it should be used with `msg_send_receive()`. + */ +#define GNRC_NETAPI_MSG_TYPE_NOTIFY (0x0207) + /** * @brief Data structure to be send for setting (@ref GNRC_NETAPI_MSG_TYPE_SET) * and getting (@ref GNRC_NETAPI_MSG_TYPE_GET) options @@ -183,6 +191,24 @@ static inline int gnrc_netapi_dispatch_send(gnrc_nettype_t type, uint32_t demux_ return gnrc_netapi_dispatch(type, demux_ctx, GNRC_NETAPI_MSG_TYPE_SND, pkt); } +/** + * @brief Sends a @ref GNRC_NETAPI_MSG_TYPE_NOTIFY command to all subscribers to + * (@p type, @p demux_ctx). + * + * @note This blocks the sender until all registered subscribers have ack'ed the notification + * or sending has failed. The @p data can be freed after the function returned. + * + * @param[in] type Type of the targeted network module. + * @param[in] demux_ctx Demultiplexing context for @p type. + * @param[in] event Notification event type. + * @param[in] data Data associated with the event. + * @param[in] data_len Size of @p data. + * + * @return Number of subscribers to (@p type, @p demux_ctx). + */ +int gnrc_netapi_notify(gnrc_nettype_t type, uint32_t demux_ctx, netapi_notify_t event, + void *data, size_t data_len); + /** * @brief Shortcut function for sending @ref GNRC_NETAPI_MSG_TYPE_RCV messages * diff --git a/sys/include/net/gnrc/netapi/notify.h b/sys/include/net/gnrc/netapi/notify.h new file mode 100644 index 0000000000..c7c6c1e1d0 --- /dev/null +++ b/sys/include/net/gnrc/netapi/notify.h @@ -0,0 +1,91 @@ +/* + * SPDX-FileCopyrightText: 2025 TU Dresden + * SPDX-License-Identifier: LGPL-2.1-only + */ + +#pragma once + +/** + * @defgroup net_gnrc_netapi_notify Notification events for network APIs. + * @ingroup net_gnrc_netapi + * @brief Network event notification types. + * @{ + * + * @file + * @brief Network event notification type definitions. + * + * @details The purpose of this API is to allow lower layers in the network + * stack to inform higher layers of general, protocol-independent + * network events. + * It can be used through the @ref net_gnrc_netapi to notify any + * other network interfaces that have registered themselves through + * @ref net_gnrc_netreg for the corresponding @ref net_gnrc_nettype. + * + * @note Notification events are associated with type-dependent event data. + * Receivers should not access the data directly, but instead use the + * the dedicated API functions to copy the data from the sender's. + * If the data is read manually, the receiver is responsible for + * calling @ref gnrc_netapi_notify_ack to unblock the sender. + * + * @author Elena Frank + */ + +#include +#ifdef __cplusplus +extern "C" { +#endif + +#include "net/gnrc/netif/conf.h" +#include "sema_inv.h" + +/** + * @brief Definition of notification event types in the network stack. + * + * @note Expand at will. + * If event data is associated with the type, a helper function must also + * be added for parsing the data and calling @ref gnrc_netapi_notify_ack. + */ +typedef enum { + PLACEHOLDER +} netapi_notify_t; + +/** + * @brief Data structure to be sent for netapi notification events. + */ +typedef struct { + netapi_notify_t event; /**< the type of event */ + void *_data; /**< associated event data. */ + uint16_t _data_len; /**< size of the event data */ + sema_inv_t ack; /**< inverse semaphore for collecting ack's */ +} gnrc_netapi_notify_t; + +/** + * @brief Acknowledge that a notify event was received and its data read. + * + * @param[in] ack Pointer to semaphore that is used to collect ack's. + */ +static inline void gnrc_netapi_notify_ack(sema_inv_t *ack) +{ + sema_inv_post(ack); +} + +/** + * @brief Copy the connection event data associated with a @ref gnrc_netapi_notify_t event. + * + * @note This will call @ref gnrc_netapi_notify_ack. + * + * @param[in] notify Pointer to the received notify event. + * @param[in] data_len Size of the expected data type. + * @param[out] data Connection data received in the @p notify event. + * + * @retval Size of the data type on success. + * @retval -EINVAL if the data in @p notify is invalid or doesn't match the expected + * data length. + */ +int gnrc_netapi_notify_copy_event_data(gnrc_netapi_notify_t *notify, uint8_t data_len, void *data); + +#ifdef __cplusplus +} +#endif + +/** @} */ diff --git a/sys/include/net/gnrc/netreg.h b/sys/include/net/gnrc/netreg.h index ad48958850..58a41a7ec6 100644 --- a/sys/include/net/gnrc/netreg.h +++ b/sys/include/net/gnrc/netreg.h @@ -330,7 +330,8 @@ static inline void gnrc_netreg_entry_init_cb(gnrc_netreg_entry_t *entry, * @brief Registers a thread to the registry. * * @details The semantics are: Thread gnrc_netreg_entry_t::pid is interested in - * packets of protocol @p type with context gnrc_netreg_entry_t::demux_ctx. + * packets or network events of protocol @p type with context + * gnrc_netreg_entry_t::demux_ctx. * * @param[in] type Type of the protocol. Must not be < GNRC_NETTYPE_UNDEF or * >= GNRC_NETTYPE_NUMOF. diff --git a/sys/include/net/gnrc/nettype.h b/sys/include/net/gnrc/nettype.h index 85ca4fccb1..b054a0dace 100644 --- a/sys/include/net/gnrc/nettype.h +++ b/sys/include/net/gnrc/nettype.h @@ -120,6 +120,20 @@ typedef enum { #endif /** @} */ + /** + * @{ + * @name Cross-layer network events. + * + * @brief Subscription types for protocol-independent events. + * They are used by lower-layers in the network stack to published + * information about the network state. + * + * @details See @ref net_gnrc_netapi_notify. + * } + */ + + /** @} */ + /** * @{ * @name Testing diff --git a/sys/net/gnrc/Makefile b/sys/net/gnrc/Makefile index ee7772b21d..182a8a1566 100644 --- a/sys/net/gnrc/Makefile +++ b/sys/net/gnrc/Makefile @@ -43,6 +43,9 @@ endif ifneq (,$(filter gnrc_netapi,$(USEMODULE))) DIRS += netapi endif +ifneq (,$(filter gnrc_netapi_notify,$(USEMODULE))) + DIRS += netapi/notify +endif ifneq (,$(filter gnrc_netif gnrc_netif_%,$(USEMODULE))) DIRS += netif endif diff --git a/sys/net/gnrc/Makefile.dep b/sys/net/gnrc/Makefile.dep index 09c4fccbdd..8b306dc9c8 100644 --- a/sys/net/gnrc/Makefile.dep +++ b/sys/net/gnrc/Makefile.dep @@ -55,7 +55,7 @@ ifneq (,$(filter gnrc_uhcpc,$(USEMODULE))) USEMODULE += fmt endif -ifneq (,$(filter gnrc_%,$(filter-out gnrc_lorawan gnrc_lorawan_1_1 gnrc_netapi gnrc_netreg gnrc_netif% gnrc_pkt%,$(USEMODULE)))) +ifneq (,$(filter gnrc_%,$(filter-out gnrc_lorawan gnrc_lorawan_1_1 gnrc_netapi gnrc_netapi_notify gnrc_netreg gnrc_netif% gnrc_pkt%,$(USEMODULE)))) USEMODULE += gnrc endif @@ -460,6 +460,10 @@ ifneq (,$(filter gnrc,$(USEMODULE))) endif endif +ifneq (,$(filter gnrc_netapi_notify,$(USEMODULE))) + USEMODULE += sema_inv +endif + ifneq (,$(filter gnrc_pktbuf, $(USEMODULE))) ifeq (,$(filter gnrc_pktbuf_%, $(USEMODULE))) USEMODULE += gnrc_pktbuf_static diff --git a/sys/net/gnrc/netapi/gnrc_netapi.c b/sys/net/gnrc/netapi/gnrc_netapi.c index a8cecb285b..680712e32e 100644 --- a/sys/net/gnrc/netapi/gnrc_netapi.c +++ b/sys/net/gnrc/netapi/gnrc_netapi.c @@ -24,8 +24,10 @@ #include "mbox.h" #include "msg.h" #include "net/gnrc/netreg.h" +#include "net/gnrc/netapi/notify.h" #include "net/gnrc/pktbuf.h" #include "net/gnrc/netapi.h" +#include "sema_inv.h" #define ENABLE_DEBUG 0 #include "debug.h" @@ -82,6 +84,42 @@ static inline int _snd_rcv_mbox(mbox_t *mbox, uint16_t type, gnrc_pktsnip_t *pkt } #endif +static int _dispatch_single(gnrc_netreg_entry_t *sendto, uint16_t cmd, void *data) +{ + int status = 0; + +#if defined(MODULE_GNRC_NETAPI_MBOX) || defined(MODULE_GNRC_NETAPI_CALLBACKS) + switch (sendto->type) { + case GNRC_NETREG_TYPE_DEFAULT: + if (_gnrc_netapi_send_recv(sendto->target.pid, data, cmd) < 1) { + /* unable to dispatch packet */ + status = -EIO; + } + break; +# ifdef MODULE_GNRC_NETAPI_MBOX + case GNRC_NETREG_TYPE_MBOX: + if (_snd_rcv_mbox(sendto->target.mbox, cmd, data) < 1) { + /* unable to dispatch packet */ + status = -EIO; + } + break; +# endif +# ifdef MODULE_GNRC_NETAPI_CALLBACKS + case GNRC_NETREG_TYPE_CB: + sendto->target.cbd->cb(cmd, data, sendto->target.cbd->ctx); + break; +# endif + default: + /* unknown dispatch type */ + status = -ECANCELED; + break; + } +#else + status = _gnrc_netapi_send_recv(sendto->target.pid, data, cmd); +#endif + return status; +} + int gnrc_netapi_dispatch(gnrc_nettype_t type, uint32_t demux_ctx, uint16_t cmd, gnrc_pktsnip_t *pkt) { @@ -96,43 +134,10 @@ int gnrc_netapi_dispatch(gnrc_nettype_t type, uint32_t demux_ctx, gnrc_pktbuf_hold(pkt, numof - 1); while (sendto) { -#if defined(MODULE_GNRC_NETAPI_MBOX) || defined(MODULE_GNRC_NETAPI_CALLBACKS) - uint32_t status = 0; - switch (sendto->type) { - case GNRC_NETREG_TYPE_DEFAULT: - if (_gnrc_netapi_send_recv(sendto->target.pid, pkt, - cmd) < 1) { - /* unable to dispatch packet */ - status = EIO; - } - break; -#ifdef MODULE_GNRC_NETAPI_MBOX - case GNRC_NETREG_TYPE_MBOX: - if (_snd_rcv_mbox(sendto->target.mbox, cmd, pkt) < 1) { - /* unable to dispatch packet */ - status = EIO; - } - break; -#endif -#ifdef MODULE_GNRC_NETAPI_CALLBACKS - case GNRC_NETREG_TYPE_CB: - sendto->target.cbd->cb(cmd, pkt, sendto->target.cbd->ctx); - break; -#endif - default: - /* unknown dispatch type */ - status = ECANCELED; - break; - } - if (status != 0) { + int status = _dispatch_single(sendto, cmd, pkt); + if (status < 0) { gnrc_pktbuf_release_error(pkt, status); } -#else - if (_gnrc_netapi_send_recv(sendto->target.pid, pkt, cmd) < 1) { - /* unable to dispatch packet */ - gnrc_pktbuf_release_error(pkt, EIO); - } -#endif sendto = gnrc_netreg_getnext(sendto); } } @@ -141,3 +146,48 @@ int gnrc_netapi_dispatch(gnrc_nettype_t type, uint32_t demux_ctx, return numof; } + +int gnrc_netapi_notify(gnrc_nettype_t type, uint32_t demux_ctx, netapi_notify_t event, + void *data, size_t data_len) +{ + gnrc_netreg_acquire_shared(); + + int numof = gnrc_netreg_num(type, demux_ctx); + if (numof <= 0) { + gnrc_netreg_release_shared(); + return numof; + } + + gnrc_netapi_notify_t notify = { + .event = event, + ._data = data, + ._data_len = data_len, + }; + + /* Use inverse semaphore to count the acknowledgments from the receivers. */ + sema_inv_init(¬ify.ack, numof); + + /* Look up the registered threads for this message type. */ + gnrc_netreg_entry_t *sendto = gnrc_netreg_lookup(type, demux_ctx); + + /* Dispatch to all registered threads. */ + while (sendto) { + int status = _dispatch_single(sendto, GNRC_NETAPI_MSG_TYPE_NOTIFY, ¬ify); + if (status < 0) { + numof--; + /* Decrease semaphore counter manually when sending failed. */ + sema_inv_post(¬ify.ack); + } + sendto = gnrc_netreg_getnext(sendto); + } + + gnrc_netreg_release_shared(); + + if (data != NULL) { + /* Wait for ACK from all receivers to ensure that the data was read and + * that there won't be any dangling pointers after the function returned. */ + sema_inv_wait(¬ify.ack); + } + + return numof; +} diff --git a/sys/net/gnrc/netapi/notify/Makefile b/sys/net/gnrc/netapi/notify/Makefile new file mode 100644 index 0000000000..7b4541bc39 --- /dev/null +++ b/sys/net/gnrc/netapi/notify/Makefile @@ -0,0 +1,3 @@ +MODULE = gnrc_netapi_notify + +include $(RIOTBASE)/Makefile.base diff --git a/sys/net/gnrc/netapi/notify/gnrc_netapi_notify.c b/sys/net/gnrc/netapi/notify/gnrc_netapi_notify.c new file mode 100644 index 0000000000..a9a21b9c4d --- /dev/null +++ b/sys/net/gnrc/netapi/notify/gnrc_netapi_notify.c @@ -0,0 +1,38 @@ +/* + * SPDX-FileCopyrightText: 2025 TU Dresden + * SPDX-License-Identifier: LGPL-2.1-only + */ + +/** + * @{ + * @ingroup net_gnrc_netapi_notify + * @file + * @brief Helper functions to extract data from netapi notify events. + * + * @author Elena Frank + */ + +#include + +#include "net/gnrc/netapi/notify.h" + +int gnrc_netapi_notify_copy_event_data(gnrc_netapi_notify_t *notify, uint8_t data_len, void *data) +{ + (void) data_len; + (void) data; + + int res; + + switch (notify->event) { + default: + res = -EINVAL; + break; + } + + /* Acknowledge the read data */ + gnrc_netapi_notify_ack(¬ify->ack); + + return res; +} + +/** @} */ From 72c65e3a816d91c40104328de3dd1d7ca08816ee Mon Sep 17 00:00:00 2001 From: Elena Frank Date: Tue, 9 Dec 2025 14:29:28 +0100 Subject: [PATCH 2/9] nimble/netif: notify on BLE connection events --- pkg/nimble/netif/nimble_netif.c | 29 +++++++++++++++++ sys/include/net/gnrc/netapi/notify.h | 13 +++++++- sys/include/net/gnrc/nettype.h | 7 +++++ .../gnrc/netapi/notify/gnrc_netapi_notify.c | 31 +++++++++++++++++-- 4 files changed, 76 insertions(+), 4 deletions(-) diff --git a/pkg/nimble/netif/nimble_netif.c b/pkg/nimble/netif/nimble_netif.c index e133f6455a..694042b831 100644 --- a/pkg/nimble/netif/nimble_netif.c +++ b/pkg/nimble/netif/nimble_netif.c @@ -27,6 +27,7 @@ #include "net/ble.h" #include "net/bluetil/addr.h" +#include "net/gnrc/netapi/notify.h" #include "net/gnrc/netif.h" #include "net/gnrc/netif/hdr.h" #include "net/gnrc/netreg.h" @@ -39,6 +40,7 @@ #include "host/ble_gap.h" #include "host/util/util.h" #include "mem/mem.h" +#include #define ENABLE_DEBUG 0 #include "debug.h" @@ -331,6 +333,29 @@ end: ble_l2cap_recv_ready(event->receive.chan, rxb); } +/** + * @brief Sends a netapi notification for a connection event. + * + * @param[in] notify The type of notification event. + * @param[in] addr BLE address of the node that (dis-)connected. + */ +static inline void _dispatch_connection_event(netapi_notify_t notify, const void *addr) +{ + if (!IS_USED(MODULE_GNRC_NETAPI_NOTIFY)) { + return; + } + + netapi_notify_l2_connection_t event = { + .l2addr_len = BLE_ADDR_LEN, + .if_pid = _netif.pid, + }; + + memcpy(event.l2addr, addr, BLE_ADDR_LEN); + + gnrc_netapi_notify(GNRC_NETTYPE_L2_DISCOVERY, GNRC_NETREG_DEMUX_CTX_ALL, + notify, &event, sizeof(netapi_notify_l2_connection_t)); +} + static int _on_l2cap_client_evt(struct ble_l2cap_event *event, void *arg) { int handle = (int)arg; @@ -349,6 +374,7 @@ static int _on_l2cap_client_evt(struct ble_l2cap_event *event, void *arg) conn->state |= NIMBLE_NETIF_L2CAP_CLIENT; conn->state &= ~NIMBLE_NETIF_CONNECTING; _notify(handle, NIMBLE_NETIF_CONNECTED_MASTER, conn->addr); + _dispatch_connection_event(NETAPI_NOTIFY_L2_NEIGH_CONNECTED, conn->addr); break; case BLE_L2CAP_EVENT_COC_DISCONNECTED: assert(conn->state & NIMBLE_NETIF_L2CAP_CLIENT); @@ -404,6 +430,7 @@ static int _on_l2cap_server_evt(struct ble_l2cap_event *event, void *arg) } _notify(handle, NIMBLE_NETIF_CONNECTED_SLAVE, conn->addr); + _dispatch_connection_event(NETAPI_NOTIFY_L2_NEIGH_CONNECTED, conn->addr); break; case BLE_L2CAP_EVENT_COC_DISCONNECTED: conn = nimble_netif_conn_from_gaphandle(event->disconnect.conn_handle); @@ -498,6 +525,7 @@ static int _on_gap_master_evt(struct ble_gap_event *event, void *arg) nimble_netif_conn_free(handle, addr); thread_flags_set(_netif_thread, FLAG_TX_NOTCONN); _notify(handle, type, addr); + _dispatch_connection_event(NETAPI_NOTIFY_L2_NEIGH_DISCONNECTED, addr); break; } case BLE_GAP_EVENT_CONN_UPDATE: @@ -542,6 +570,7 @@ static int _on_gap_slave_evt(struct ble_gap_event *event, void *arg) nimble_netif_conn_free(handle, addr); thread_flags_set(_netif_thread, FLAG_TX_NOTCONN); _notify(handle, type, addr); + _dispatch_connection_event(NETAPI_NOTIFY_L2_NEIGH_DISCONNECTED, addr); break; } case BLE_GAP_EVENT_CONN_UPDATE: diff --git a/sys/include/net/gnrc/netapi/notify.h b/sys/include/net/gnrc/netapi/notify.h index c7c6c1e1d0..8e82adedca 100644 --- a/sys/include/net/gnrc/netapi/notify.h +++ b/sys/include/net/gnrc/netapi/notify.h @@ -46,7 +46,8 @@ extern "C" { * be added for parsing the data and calling @ref gnrc_netapi_notify_ack. */ typedef enum { - PLACEHOLDER + NETAPI_NOTIFY_L2_NEIGH_CONNECTED, /**< Connection established on layer 2. */ + NETAPI_NOTIFY_L2_NEIGH_DISCONNECTED, /**< Connection closed on layer 2. */ } netapi_notify_t; /** @@ -59,6 +60,16 @@ typedef struct { sema_inv_t ack; /**< inverse semaphore for collecting ack's */ } gnrc_netapi_notify_t; +/** + * @brief L2 connection event data associated with @ref NETAPI_NOTIFY_L2_NEIGH_CONNECTED or + * @ref NETAPI_NOTIFY_L2_NEIGH_DISCONNECTED events. + */ +typedef struct { + uint8_t l2addr[GNRC_NETIF_L2ADDR_MAXLEN]; /**< L2 address of the node */ + uint8_t l2addr_len; /**< length of L2 address in byte */ + kernel_pid_t if_pid; /**< PID of network interface */ +} netapi_notify_l2_connection_t; + /** * @brief Acknowledge that a notify event was received and its data read. * diff --git a/sys/include/net/gnrc/nettype.h b/sys/include/net/gnrc/nettype.h index b054a0dace..8ce21f4633 100644 --- a/sys/include/net/gnrc/nettype.h +++ b/sys/include/net/gnrc/nettype.h @@ -132,6 +132,13 @@ typedef enum { * } */ + /** + * @brief Reports the reachability of link-layer neighbors. + * Connection-oriented links such as BLE can use this to inform + * the IPv6-layer, e.g., about new connected or disconnected nodes + * and their l2 address. + */ + GNRC_NETTYPE_L2_DISCOVERY, /** @} */ /** diff --git a/sys/net/gnrc/netapi/notify/gnrc_netapi_notify.c b/sys/net/gnrc/netapi/notify/gnrc_netapi_notify.c index a9a21b9c4d..4f8c4b806a 100644 --- a/sys/net/gnrc/netapi/notify/gnrc_netapi_notify.c +++ b/sys/net/gnrc/netapi/notify/gnrc_netapi_notify.c @@ -12,18 +12,43 @@ * @author Elena Frank */ +#include #include #include "net/gnrc/netapi/notify.h" +#include "net/ipv6/addr.h" + +int _copy_l2_connection_data(gnrc_netapi_notify_t *notify, netapi_notify_l2_connection_t *data) +{ + assert(notify->_data_len == sizeof(netapi_notify_l2_connection_t)); + + /* Parse event data */ + netapi_notify_l2_connection_t *recv_data = notify->_data; + + if (recv_data->l2addr_len > GNRC_NETIF_L2ADDR_MAXLEN) { + return -EINVAL; + } + memcpy(data->l2addr, recv_data->l2addr, recv_data->l2addr_len); + + data->l2addr_len = recv_data->l2addr_len; + data->if_pid = recv_data->if_pid; + + return sizeof(netapi_notify_l2_connection_t); +} int gnrc_netapi_notify_copy_event_data(gnrc_netapi_notify_t *notify, uint8_t data_len, void *data) { - (void) data_len; - (void) data; - int res; switch (notify->event) { + case NETAPI_NOTIFY_L2_NEIGH_CONNECTED: + case NETAPI_NOTIFY_L2_NEIGH_DISCONNECTED: + if (data_len != sizeof(netapi_notify_l2_connection_t)) { + res = -EINVAL; + break; + } + res = _copy_l2_connection_data(notify, data); + break; default: res = -EINVAL; break; From 8746281d9d698dc9f469fb5aad0a0b5aa0852421 Mon Sep 17 00:00:00 2001 From: Elena Frank Date: Tue, 9 Dec 2025 14:41:52 +0100 Subject: [PATCH 3/9] gnrc/ipv6: modify nib nc on l2 connection event If a new L2 connection was established, the node should be inserted in the neighbor cache. For 6LN nodes, NIB will never start the usual neighbor discovery process because it can directly resolve the link-layer address from the IPv6 address. Thus we have to manually add such nodes to the NC by building the IPv6 address from the L2 address. If a L2 connection closed, the node is removed again from the NC. --- sys/include/net/gnrc/ipv6/nib/nc.h | 47 +++++++ sys/net/gnrc/network_layer/ipv6/gnrc_ipv6.c | 117 +++++++++++++++++- .../gnrc/network_layer/ipv6/nib/_nib-6ln.c | 27 ++++ .../gnrc/network_layer/ipv6/nib/_nib-6ln.h | 22 ++++ sys/net/gnrc/network_layer/ipv6/nib/nib_nc.c | 52 ++++++++ 5 files changed, 260 insertions(+), 5 deletions(-) diff --git a/sys/include/net/gnrc/ipv6/nib/nc.h b/sys/include/net/gnrc/ipv6/nib/nc.h index 5ac6a11926..9556a70f89 100644 --- a/sys/include/net/gnrc/ipv6/nib/nc.h +++ b/sys/include/net/gnrc/ipv6/nib/nc.h @@ -233,6 +233,36 @@ static inline unsigned gnrc_ipv6_nib_nc_get_ar_state(const gnrc_ipv6_nib_nc_t *e int gnrc_ipv6_nib_nc_set(const ipv6_addr_t *ipv6, unsigned iface, const uint8_t *l2addr, size_t l2addr_len); +#if IS_ACTIVE(CONFIG_GNRC_IPV6_NIB_6LN) || defined(DOXYGEN) +/** + * @brief Adds an unmanaged neighbor entry to the NIB if the interface represents a 6LN node + * and the IPv6 address can be constructed from the L2 address @p l2addr. + * + * @param[in] iface The interface to the neighbor. + * @param[in] l2addr The neighbor's layer 2 address. + * @param[in] l2addr_len Length of @p l2addr. + * + * @retval 0 on success. + * @retval -ENOTSUP if the interface does not represent a 6LN or when + * gnrc_netif_t::device_type of the iface does not support IID conversion. + * @retval -EINVAL when @p addr_len is invalid for the + * gnrc_netif_t::device_type of @p netif. + * @retval -ENOMEM if no space is left in neighbor cache. + */ +int gnrc_ipv6_nib_nc_set_6ln(unsigned iface, const uint8_t *l2addr, + size_t l2addr_len); +#else /* CONFIG_GNRC_IPV6_NIB_6LN */ +static inline int gnrc_ipv6_nib_nc_set_6ln(unsigned iface, const uint8_t *l2addr, + size_t l2addr_len) +{ + (void)iface; + (void)l2addr; + (void)l2addr_len; + + return -ENOTSUP; +} +#endif /* CONFIG_GNRC_IPV6_NIB_6LN */ + /** * @brief Deletes neighbor with address @p ipv6 from NIB * @@ -245,6 +275,23 @@ int gnrc_ipv6_nib_nc_set(const ipv6_addr_t *ipv6, unsigned iface, */ void gnrc_ipv6_nib_nc_del(const ipv6_addr_t *ipv6, unsigned iface); +#if IS_ACTIVE(CONFIG_GNRC_IPV6_NIB_ARSM) || defined(DOXYGEN) +/** + * @brief Deletes neighbor with link-layer address @p l2addr from NIB. + * + * @param[in] iface The interface to the neighbor. + * @param[in] l2addr The neighbor's l2addr address. + * @param[in] l2addr_len Length of @p l2addr. + * + * + * If the @p l2addr can't be found for a neighbor in the NIB nothing happens. + * + * @retval True if a neighbor with @p l2addr existed. + * @retval False otherwise. + */ +bool gnrc_ipv6_nib_nc_del_l2(unsigned iface, const uint8_t *l2addr, size_t l2addr_len); +#endif /* CONFIG_GNRC_IPV6_NIB_ARSM */ + /** * @brief Mark neighbor with address @p ipv6 as reachable * diff --git a/sys/net/gnrc/network_layer/ipv6/gnrc_ipv6.c b/sys/net/gnrc/network_layer/ipv6/gnrc_ipv6.c index 5fbabfb625..b53cbb7e59 100644 --- a/sys/net/gnrc/network_layer/ipv6/gnrc_ipv6.c +++ b/sys/net/gnrc/network_layer/ipv6/gnrc_ipv6.c @@ -29,6 +29,7 @@ #include "utlist.h" #include "net/gnrc/ipv6/nib.h" +#include "net/gnrc/netapi/notify.h" #include "net/gnrc/netif/internal.h" #include "net/gnrc/ipv6/whitelist.h" #include "net/gnrc/ipv6/blacklist.h" @@ -171,11 +172,108 @@ static void _dispatch_next_header(gnrc_pktsnip_t *pkt, unsigned nh, } } +/** + * @brief Find entry in NIB neighbor cache. + * + * @param[in] l2addr Layer 2 address of the neighbor. + * @param[in] l2addr_len Length of @p l2addr. + * @param[out] ipv6 IPv6 address of the neighbor or NULL. + * + * @retval True if a neighbor with @p l2addr was found in the nc. + * @retval False otherwise. + */ +static inline bool _find_entry_in_nc(uint8_t *l2addr, uint8_t l2addr_len, ipv6_addr_t *ipv6) +{ + void *state = NULL; + gnrc_ipv6_nib_nc_t nce; + + while (gnrc_ipv6_nib_nc_iter(0, &state, &nce)) { + if (l2util_addr_equal(l2addr, l2addr_len, nce.l2addr, nce.l2addr_len)) { + *ipv6 = nce.ipv6; + return true; + } + } + return false; +} + +/** + * @brief Handle a link-layer connection-established event. + * + * @param[in] if_pid Network interface of the connection. + * @param[in] l2addr L2 address of the node on the connection. + * @param[in] l2addr_len Length of @p l2addr. + */ +static inline void _on_l2_connected(kernel_pid_t if_pid, uint8_t *l2addr, uint8_t l2addr_len) +{ + (void)if_pid; + +#if IS_ACTIVE(CONFIG_GNRC_IPV6_NIB_6LN) + /* Add neighbor to neighbor cache if interface represents a 6LN. */ + gnrc_ipv6_nib_nc_set_6ln(if_pid, l2addr, l2addr_len); +#endif /* CONFIG_GNRC_IPV6_NIB_6LN */ +} + +/** + * @brief Handle a link-layer connection-closed event. + * + * @param[in] if_pid Network interface of the connection. + * @param[in] l2addr L2 address of the node on the connection. + * @param[in] l2addr_len Length of @p l2addr. + */ +static inline void _on_l2_disconnected(kernel_pid_t if_pid, uint8_t *l2addr, uint8_t l2addr_len) +{ + (void)if_pid; + +#if IS_ACTIVE(CONFIG_GNRC_IPV6_NIB_ARSM) + /* Remove from neighbor cache. */ + gnrc_ipv6_nib_nc_del_l2(if_pid, l2addr, l2addr_len); +#endif /* CONFIG_GNRC_IPV6_NIB_ARSM */ +} + +/** + * @brief Handles a netapi event notification. + * + * @param[in] notify The type of notification. + */ +static inline void _netapi_notify_event(gnrc_netapi_notify_t *notify) +{ + if (!IS_USED(MODULE_GNRC_NETAPI_NOTIFY)) { + return; + } + + netapi_notify_l2_connection_t data; + netapi_notify_t type = notify->event; + + if (gnrc_netapi_notify_copy_event_data(notify, sizeof(netapi_notify_l2_connection_t), + &data) <= 0) { + DEBUG("ipv6: invalid data on netapi notify event.\n"); + return; + } + + switch (type) { + case NETAPI_NOTIFY_L2_NEIGH_CONNECTED: + _on_l2_connected(data.if_pid, data.l2addr, data.l2addr_len); + break; + case NETAPI_NOTIFY_L2_NEIGH_DISCONNECTED: + _on_l2_disconnected(data.if_pid, data.l2addr, data.l2addr_len); + break; + default: + break; + } +} + static void *_event_loop(void *args) { msg_t msg, reply; - gnrc_netreg_entry_t me_reg = GNRC_NETREG_ENTRY_INIT_PID(GNRC_NETREG_DEMUX_CTX_ALL, - thread_getpid()); + + /* Register entry for messages in IPv6 context. */ + gnrc_netreg_entry_t me_ipv6_reg = GNRC_NETREG_ENTRY_INIT_PID(GNRC_NETREG_DEMUX_CTX_ALL, + thread_getpid()); +#ifdef MODULE_GNRC_NETAPI_NOTIFY + /* Register entry for messages in L2 discovery context. */ + gnrc_netreg_entry_t me_discovery_reg = GNRC_NETREG_ENTRY_INIT_PID(GNRC_NETREG_DEMUX_CTX_ALL, + thread_getpid()); +#endif /* MODULE_GNRC_NETAPI_NOTIFY */ (void)args; msg_init_queue(_msg_q, GNRC_IPV6_MSG_QUEUE_SIZE); @@ -184,8 +282,14 @@ static void *_event_loop(void *args) #ifdef MODULE_GNRC_IPV6_EXT_FRAG gnrc_ipv6_ext_frag_init(); #endif /* MODULE_GNRC_IPV6_EXT_FRAG */ - /* register interest in all IPv6 packets */ - gnrc_netreg_register(GNRC_NETTYPE_IPV6, &me_reg); + + /* Register interest in all IPv6 packets. */ + gnrc_netreg_register(GNRC_NETTYPE_IPV6, &me_ipv6_reg); + +#ifdef MODULE_GNRC_NETAPI_NOTIFY + /* Register interest in L2 neighbor discovery info. */ + gnrc_netreg_register(GNRC_NETTYPE_L2_DISCOVERY, &me_discovery_reg); +#endif /* MODULE_GNRC_NETAPI_NOTIFY */ /* preinitialize ACK */ reply.type = GNRC_NETAPI_MSG_TYPE_ACK; @@ -212,7 +316,10 @@ static void *_event_loop(void *args) reply.content.value = -ENOTSUP; msg_reply(&msg, &reply); break; - + case GNRC_NETAPI_MSG_TYPE_NOTIFY: + DEBUG("ipv6: GNRC_NETAPI_MSG_TYPE_NOTIFY received\n"); + _netapi_notify_event(msg.content.ptr); + break; #ifdef MODULE_GNRC_IPV6_EXT_FRAG case GNRC_IPV6_EXT_FRAG_RBUF_GC: gnrc_ipv6_ext_frag_rbuf_gc(); diff --git a/sys/net/gnrc/network_layer/ipv6/nib/_nib-6ln.c b/sys/net/gnrc/network_layer/ipv6/nib/_nib-6ln.c index 8219f701c1..759078e61e 100644 --- a/sys/net/gnrc/network_layer/ipv6/nib/_nib-6ln.c +++ b/sys/net/gnrc/network_layer/ipv6/nib/_nib-6ln.c @@ -73,6 +73,33 @@ bool _resolve_addr_from_ipv6(const ipv6_addr_t *dst, gnrc_netif_t *netif, return res; } +int _build_ll_ipv6_from_addr(gnrc_netif_t *netif, const uint8_t *l2addr, uint8_t l2addr_len, + ipv6_addr_t *ipv6addr) +{ + /* Reverse of _resolve_addr_from_ipv6. */ + + if (netif == NULL) { + return -ENOENT; + } + + /* Only supported for 6LN nodes. */ + if (!gnrc_netif_is_6ln(netif)) { + return -ENOTSUP; + } + + /* Set IPv6 link-local prefix. */ + *ipv6addr = ipv6_addr_link_local_prefix; + + /* Build interface identifier based on l2 address. */ + int res = gnrc_netif_ipv6_iid_from_addr(netif, l2addr, l2addr_len, + (eui64_t *)&ipv6addr->u64[1]); + if (res < 0) { + return res; + } + + return sizeof(ipv6_addr_t); +} + uint8_t _handle_aro(gnrc_netif_t *netif, const ipv6_hdr_t *ipv6, const icmpv6_hdr_t *icmpv6, const sixlowpan_nd_opt_ar_t *aro, const ndp_opt_t *sl2ao, diff --git a/sys/net/gnrc/network_layer/ipv6/nib/_nib-6ln.h b/sys/net/gnrc/network_layer/ipv6/nib/_nib-6ln.h index 92a5245234..eb5769acc5 100644 --- a/sys/net/gnrc/network_layer/ipv6/nib/_nib-6ln.h +++ b/sys/net/gnrc/network_layer/ipv6/nib/_nib-6ln.h @@ -70,6 +70,27 @@ extern "C" { bool _resolve_addr_from_ipv6(const ipv6_addr_t *dst, gnrc_netif_t *netif, gnrc_ipv6_nib_nc_t *nce); +/** + * @brief Build a link-local ipv6 address statically from the L2 address by + * translating the hardware address to an EUI-64 and adding the link-local + * IPv6 prefix. + * + * @note Reverse of _resolve_addr_from_ipv6. + * + * @param[in] netif The interface to @p l2addr. + * @param[in] l2addr Layer 2 address. + * @param[in] l2addr_len Length of the layer 2 address. + * @param[out] ipv6addr The resulting IPv6 address. + * + * @retval sizeof(ipv6_addr_t) on success. + * @retval `-ENOTSUP` if the interface does not represent a 6LN or when + * gnrc_netif_t::device_type of @p netif does not support IID conversion. + * @retval `-EINVAL` when @p addr_len is invalid for the + * gnrc_netif_t::device_type of @p netif. + */ +int _build_ll_ipv6_from_addr(gnrc_netif_t *netif, const uint8_t *l2addr, + uint8_t l2addr_len, ipv6_addr_t *ipv6addr); + /** * @brief Calculates exponential backoff for RS retransmissions * @@ -137,6 +158,7 @@ uint32_t _handle_6co(const icmpv6_hdr_t *icmpv6, #endif /* CONFIG_GNRC_IPV6_NIB_MULTIHOP_P6C || defined(DOXYGEN) */ #else /* CONFIG_GNRC_IPV6_NIB_6LN || defined(DOXYGEN) */ #define _resolve_addr_from_ipv6(dst, netif, nce) (false) +#define _build_ll_ipv6_from_addr(netif, l2addr, l2addr_len, ipv6addr) (-ENOTSUP) /* _handle_aro() doesn't make sense without 6LR so don't even use it * => throw error in case it is compiled in => don't define it here as NOP macro */ diff --git a/sys/net/gnrc/network_layer/ipv6/nib/nib_nc.c b/sys/net/gnrc/network_layer/ipv6/nib/nib_nc.c index ddb1ea5d28..e6012bca70 100644 --- a/sys/net/gnrc/network_layer/ipv6/nib/nib_nc.c +++ b/sys/net/gnrc/network_layer/ipv6/nib/nib_nc.c @@ -24,6 +24,9 @@ #include "net/gnrc/ipv6/nib/nc.h" #include "_nib-internal.h" +#if IS_ACTIVE(CONFIG_GNRC_IPV6_NIB_6LN) +# include "_nib-6ln.h" +#endif /* CONFIG_GNRC_IPV6_NIB_6LN */ int gnrc_ipv6_nib_nc_set(const ipv6_addr_t *ipv6, unsigned iface, const uint8_t *l2addr, size_t l2addr_len) @@ -59,6 +62,32 @@ int gnrc_ipv6_nib_nc_set(const ipv6_addr_t *ipv6, unsigned iface, return 0; } +#if IS_ACTIVE(CONFIG_GNRC_IPV6_NIB_6LN) +int gnrc_ipv6_nib_nc_set_6ln(unsigned iface, const uint8_t *l2addr, size_t l2addr_len) +{ + int res; + ipv6_addr_t ipv6addr; + + gnrc_netif_t *netif = gnrc_netif_get_by_pid(iface); + + if (netif == NULL) { + return -ENOENT; + } + /* Build link-local IPv6 address based on L2-address. */ + res = _build_ll_ipv6_from_addr(netif, l2addr, l2addr_len, &ipv6addr); + if (res < 0) { + return res; + } + /* Add as unmanaged entry to neighbor cache. */ + res = gnrc_ipv6_nib_nc_set(&ipv6addr, netif->pid, l2addr, l2addr_len); + if (res < 0) { + return res; + } + + return 0; +} +#endif + void gnrc_ipv6_nib_nc_del(const ipv6_addr_t *ipv6, unsigned iface) { _nib_onl_entry_t *node = NULL; @@ -74,6 +103,29 @@ void gnrc_ipv6_nib_nc_del(const ipv6_addr_t *ipv6, unsigned iface) _nib_release(); } +#if IS_ACTIVE(CONFIG_GNRC_IPV6_NIB_ARSM) +bool gnrc_ipv6_nib_nc_del_l2(unsigned iface, const uint8_t *l2addr, size_t l2addr_len) +{ + _nib_onl_entry_t *node = NULL; + bool res = false; + + _nib_acquire(); + + /* Find and remove entry with matching l2 address.*/ + while ((node = _nib_onl_iter(node)) != NULL) { + if ((_nib_onl_get_if(node) == iface) && + l2util_addr_equal(l2addr, l2addr_len, node->l2addr, node->l2addr_len)) { + _nib_nc_remove(node); + res = true; + break; + } + } + _nib_release(); + + return res; +} +#endif /* CONFIG_GNRC_IPV6_NIB_ARSM */ + void gnrc_ipv6_nib_nc_mark_reachable(const ipv6_addr_t *ipv6) { _nib_onl_entry_t *node = NULL; From 352f63de75a9fee846d96bdf11e9cf4cfe7582e2 Mon Sep 17 00:00:00 2001 From: Elena Frank Date: Tue, 9 Dec 2025 14:44:52 +0100 Subject: [PATCH 4/9] gnrc/ipv6: notify L3_ROUTING of discovered/ unreachable nodes --- sys/include/net/gnrc/netapi/notify.h | 2 ++ sys/include/net/gnrc/nettype.h | 9 +++++++++ sys/net/gnrc/netapi/notify/gnrc_netapi_notify.c | 16 ++++++++++++++++ sys/net/gnrc/network_layer/ipv6/gnrc_ipv6.c | 17 +++++++++++++++++ 4 files changed, 44 insertions(+) diff --git a/sys/include/net/gnrc/netapi/notify.h b/sys/include/net/gnrc/netapi/notify.h index 8e82adedca..d495727270 100644 --- a/sys/include/net/gnrc/netapi/notify.h +++ b/sys/include/net/gnrc/netapi/notify.h @@ -48,6 +48,8 @@ extern "C" { typedef enum { NETAPI_NOTIFY_L2_NEIGH_CONNECTED, /**< Connection established on layer 2. */ NETAPI_NOTIFY_L2_NEIGH_DISCONNECTED, /**< Connection closed on layer 2. */ + NETAPI_NOTIFY_L3_DISCOVERED, /**< Discovered node on the network layer. */ + NETAPI_NOTIFY_L3_UNREACHABLE, /**< Node became unreachable on the network layer. */ } netapi_notify_t; /** diff --git a/sys/include/net/gnrc/nettype.h b/sys/include/net/gnrc/nettype.h index 8ce21f4633..342ea57164 100644 --- a/sys/include/net/gnrc/nettype.h +++ b/sys/include/net/gnrc/nettype.h @@ -139,6 +139,15 @@ typedef enum { * and their l2 address. */ GNRC_NETTYPE_L2_DISCOVERY, + /** + * @brief Reports routing-relevant information, e.g., discovered or + * unreachable nodes, from the IPv6-layer to routing protocol like + * RPL. + * + * @note Compared to @ref GNRC_NETTYPE_L2_DISCOVERY, events published to + * this type include the IPv6 address of the node. + */ + GNRC_NETTYPE_L3_ROUTING, /** @} */ /** diff --git a/sys/net/gnrc/netapi/notify/gnrc_netapi_notify.c b/sys/net/gnrc/netapi/notify/gnrc_netapi_notify.c index 4f8c4b806a..2e1fe08b94 100644 --- a/sys/net/gnrc/netapi/notify/gnrc_netapi_notify.c +++ b/sys/net/gnrc/netapi/notify/gnrc_netapi_notify.c @@ -36,6 +36,14 @@ int _copy_l2_connection_data(gnrc_netapi_notify_t *notify, netapi_notify_l2_conn return sizeof(netapi_notify_l2_connection_t); } +int _copy_l3_address(gnrc_netapi_notify_t *notify, ipv6_addr_t *addr) +{ + assert(notify->_data_len == sizeof(ipv6_addr_t)); + + memcpy(addr, notify->_data, sizeof(ipv6_addr_t)); + return sizeof(ipv6_addr_t); +} + int gnrc_netapi_notify_copy_event_data(gnrc_netapi_notify_t *notify, uint8_t data_len, void *data) { int res; @@ -49,6 +57,14 @@ int gnrc_netapi_notify_copy_event_data(gnrc_netapi_notify_t *notify, uint8_t dat } res = _copy_l2_connection_data(notify, data); break; + case NETAPI_NOTIFY_L3_DISCOVERED: + case NETAPI_NOTIFY_L3_UNREACHABLE: + if (data_len != sizeof(ipv6_addr_t)) { + res = -EINVAL; + break; + } + res = _copy_l3_address(notify, data); + break; default: res = -EINVAL; break; diff --git a/sys/net/gnrc/network_layer/ipv6/gnrc_ipv6.c b/sys/net/gnrc/network_layer/ipv6/gnrc_ipv6.c index b53cbb7e59..33517d80dd 100644 --- a/sys/net/gnrc/network_layer/ipv6/gnrc_ipv6.c +++ b/sys/net/gnrc/network_layer/ipv6/gnrc_ipv6.c @@ -207,10 +207,18 @@ static inline void _on_l2_connected(kernel_pid_t if_pid, uint8_t *l2addr, uint8_ { (void)if_pid; + ipv6_addr_t ipv6; + #if IS_ACTIVE(CONFIG_GNRC_IPV6_NIB_6LN) /* Add neighbor to neighbor cache if interface represents a 6LN. */ gnrc_ipv6_nib_nc_set_6ln(if_pid, l2addr, l2addr_len); #endif /* CONFIG_GNRC_IPV6_NIB_6LN */ + + /* Inform routing layer of new reachable neighbor. */ + if (_find_entry_in_nc(l2addr, l2addr_len, &ipv6)) { + gnrc_netapi_notify(GNRC_NETTYPE_L3_ROUTING, GNRC_NETREG_DEMUX_CTX_ALL, + NETAPI_NOTIFY_L3_DISCOVERED, &ipv6, sizeof(ipv6_addr_t)); + } } /** @@ -224,6 +232,15 @@ static inline void _on_l2_disconnected(kernel_pid_t if_pid, uint8_t *l2addr, uin { (void)if_pid; + ipv6_addr_t ipv6; + + /* Inform routing layer of unreachable neighbor. This must be done *before* removing + the neighbor from the neighbor cache. */ + if (_find_entry_in_nc(l2addr, l2addr_len, &ipv6)) { + gnrc_netapi_notify(GNRC_NETTYPE_L3_ROUTING, GNRC_NETREG_DEMUX_CTX_ALL, + NETAPI_NOTIFY_L3_UNREACHABLE, &ipv6, sizeof(ipv6_addr_t)); + } + #if IS_ACTIVE(CONFIG_GNRC_IPV6_NIB_ARSM) /* Remove from neighbor cache. */ gnrc_ipv6_nib_nc_del_l2(if_pid, l2addr, l2addr_len); From eb2c0016b2ec6fdb8a0d02124058321ea85045c1 Mon Sep 17 00:00:00 2001 From: Elena Frank Date: Tue, 9 Dec 2025 14:46:44 +0100 Subject: [PATCH 5/9] gnrc/rpl: iterate through parents that match addr Iterate through all parents whose address match `addr`. In most cases there will only be a single parent, but if `GNRC_RPL_INSTANCES_NUMOF > 1` then one node can be parent in multiple DODAGs. --- sys/include/net/gnrc/rpl/dodag.h | 12 ++++++++++++ sys/net/gnrc/routing/rpl/gnrc_rpl_dodag.c | 13 +++++++++++++ 2 files changed, 25 insertions(+) diff --git a/sys/include/net/gnrc/rpl/dodag.h b/sys/include/net/gnrc/rpl/dodag.h index e23c543748..680b14787b 100644 --- a/sys/include/net/gnrc/rpl/dodag.h +++ b/sys/include/net/gnrc/rpl/dodag.h @@ -133,6 +133,18 @@ void gnrc_rpl_dodag_remove_all_parents(gnrc_rpl_dodag_t *dodag); bool gnrc_rpl_parent_add_by_addr(gnrc_rpl_dodag_t *dodag, ipv6_addr_t *addr, gnrc_rpl_parent_t **parent); +/** + * @brief Iterate over all parents in all DODAGs with @p IPv6 address. + * + * @param[in] addr IPV6 address of the parent. + * @param[out] parent Pointer to the parent if one was found. Otherwise NULL. + * @param[in] idx Index to start searching from. + * + * @retval Index > 0 to continue next search from, if parent was found. + * @retval -ENONENT if not found + */ +int gnrc_rpl_parent_iter_by_addr(const ipv6_addr_t *addr, gnrc_rpl_parent_t **parent, int idx); + /** * @brief Remove the @p parent from its DODAG. * diff --git a/sys/net/gnrc/routing/rpl/gnrc_rpl_dodag.c b/sys/net/gnrc/routing/rpl/gnrc_rpl_dodag.c index e0bf6ad396..aaa3dca363 100644 --- a/sys/net/gnrc/routing/rpl/gnrc_rpl_dodag.c +++ b/sys/net/gnrc/routing/rpl/gnrc_rpl_dodag.c @@ -201,6 +201,19 @@ void gnrc_rpl_dodag_remove_all_parents(gnrc_rpl_dodag_t *dodag) dodag->my_rank = GNRC_RPL_INFINITE_RANK; } +int gnrc_rpl_parent_iter_by_addr(const ipv6_addr_t *addr, gnrc_rpl_parent_t **parent, int idx) +{ + *parent = NULL; + for (uint8_t i = idx; i < GNRC_RPL_PARENTS_NUMOF; ++i) { + if ((gnrc_rpl_parents[i].state != 0) && ipv6_addr_equal(&gnrc_rpl_parents[i].addr, addr)) { + *parent = &gnrc_rpl_parents[i]; + /* Index to continue search from. */ + return i + 1; + } + } + return -ENOENT; +} + bool gnrc_rpl_parent_add_by_addr(gnrc_rpl_dodag_t *dodag, ipv6_addr_t *addr, gnrc_rpl_parent_t **parent) { From 108cfb9ca48807e94a69760f4135acd36b417098 Mon Sep 17 00:00:00 2001 From: Elena Frank Date: Tue, 9 Dec 2025 14:48:39 +0100 Subject: [PATCH 6/9] gnrc/rpl: handle L3 routing events from netapi Handle netapi notification for discovered and unreachable neighbors. For newly discovered neighbors, send a DIS message to the node. For unreachable nodes trigger a timeout for RPL parents that match the address. --- sys/net/gnrc/Makefile.dep | 1 + sys/net/gnrc/routing/rpl/gnrc_rpl.c | 90 +++++++++++++++++++++++++++-- 2 files changed, 86 insertions(+), 5 deletions(-) diff --git a/sys/net/gnrc/Makefile.dep b/sys/net/gnrc/Makefile.dep index 8b306dc9c8..afea132093 100644 --- a/sys/net/gnrc/Makefile.dep +++ b/sys/net/gnrc/Makefile.dep @@ -94,6 +94,7 @@ ifneq (,$(filter gnrc_rpl_p2p,$(USEMODULE))) endif ifneq (,$(filter gnrc_rpl,$(USEMODULE))) + USEMODULE += gnrc_ipv6 USEMODULE += gnrc_icmpv6 USEMODULE += gnrc_ipv6_nib USEMODULE += trickle diff --git a/sys/net/gnrc/routing/rpl/gnrc_rpl.c b/sys/net/gnrc/routing/rpl/gnrc_rpl.c index 19e6b761c7..a0ef1a2151 100644 --- a/sys/net/gnrc/routing/rpl/gnrc_rpl.c +++ b/sys/net/gnrc/routing/rpl/gnrc_rpl.c @@ -15,12 +15,14 @@ * @author Cenk Gündoğan */ +#include "net/ipv6/addr.h" #include #include #include "kernel_defines.h" #include "net/icmpv6.h" #include "net/ipv6.h" +#include "net/gnrc/netapi/notify.h" #include "net/gnrc/netif/internal.h" #include "net/gnrc.h" #include "mutex.h" @@ -58,7 +60,12 @@ static xtimer_t _lt_timer; static msg_t _lt_msg = { .type = GNRC_RPL_MSG_TYPE_LIFETIME_UPDATE }; #endif static msg_t _msg_q[GNRC_RPL_MSG_QUEUE_SIZE]; -static gnrc_netreg_entry_t _me_reg; + +static gnrc_netreg_entry_t _me_icmpv6_reg; +#ifdef MODULE_GNRC_NETAPI_NOTIFY +static gnrc_netreg_entry_t _me_routing_reg; +#endif /* MODULE_GNRC_NETAPI_NOTIFY*/ + static mutex_t _inst_id_mutex = MUTEX_INIT; static uint8_t _instance_id; @@ -100,10 +107,17 @@ kernel_pid_t gnrc_rpl_init(kernel_pid_t if_pid) * queue, and registration with netreg can commence. */ mutex_lock(&eventloop_startup); - _me_reg.demux_ctx = ICMPV6_RPL_CTRL; - _me_reg.target.pid = gnrc_rpl_pid; - /* register interest in all ICMPv6 packets */ - gnrc_netreg_register(GNRC_NETTYPE_ICMPV6, &_me_reg); + /* Register interest in all ICMPv6 packets. */ + _me_icmpv6_reg.demux_ctx = ICMPV6_RPL_CTRL; + _me_icmpv6_reg.target.pid = gnrc_rpl_pid; + gnrc_netreg_register(GNRC_NETTYPE_ICMPV6, &_me_icmpv6_reg); + +#ifdef MODULE_GNRC_NETAPI_NOTIFY + /* Register interest in L3 routing info. */ + _me_routing_reg.demux_ctx = GNRC_NETREG_DEMUX_CTX_ALL; + _me_routing_reg.target.pid = gnrc_rpl_pid; + gnrc_netreg_register(GNRC_NETTYPE_L3_ROUTING, &_me_routing_reg); +#endif /* MODULE_GNRC_NETAPI_NOTIFY*/ gnrc_rpl_of_manager_init(); evtimer_init_msg(&gnrc_rpl_evtimer); @@ -273,6 +287,68 @@ static void _parent_timeout(gnrc_rpl_parent_t *parent) evtimer_add_msg(&gnrc_rpl_evtimer, &parent->timeout_event, gnrc_rpl_pid); } +/** + * @brief Handles the event that a new neighbor was discovered and is reachable. + * + * @param[in] addr The address of the neighbor. + */ +static inline void _handle_discovered_neighbor(ipv6_addr_t *addr) +{ + /* Send DODAG soliciation message to node. */ + gnrc_rpl_send_DIS(NULL, addr, NULL, 0); +} + +/** + * @brief Handles the event that a neighbor became unreachable. + * + * @param[in] addr The address of the neighbor. + */ +static inline void _handle_unreachable_neighbor(ipv6_addr_t *addr) +{ + gnrc_rpl_parent_t *parent; + int idx = 0; + + /* Iterate through all parents and timeout the ones with matching address. + * There can be multiple parents with the same address because the same node + * can be a parent in multiple different DODAGs. */ + while ((idx = gnrc_rpl_parent_iter_by_addr(addr, &parent, idx)) >= 0) { + _parent_timeout(parent); + } +} + +/** + * @brief Handles a netapi event notification. + * + * @param[in] notify The type of notification. + */ +static inline void _netapi_notify_event(gnrc_netapi_notify_t *notify) +{ + if (!IS_USED(MODULE_GNRC_NETAPI_NOTIFY)) { + return; + } + + ipv6_addr_t neigh_addr; + netapi_notify_t type = notify->event; + + int res = gnrc_netapi_notify_copy_event_data(notify, sizeof(ipv6_addr_t), &neigh_addr); + + if (res != sizeof(ipv6_addr_t)) { + DEBUG("RPL: Received invalid data for netapi notify event.\n"); + return; + } + + switch (type) { + case NETAPI_NOTIFY_L3_DISCOVERED: + _handle_discovered_neighbor(&neigh_addr); + break; + case NETAPI_NOTIFY_L3_UNREACHABLE: + _handle_unreachable_neighbor(&neigh_addr); + break; + default: + break; + } +} + static void *_event_loop(void *args) { msg_t msg, reply; @@ -342,6 +418,10 @@ static void *_event_loop(void *args) reply.content.value = -ENOTSUP; msg_reply(&msg, &reply); break; + case GNRC_NETAPI_MSG_TYPE_NOTIFY: + DEBUG("RPL: GNRC_NETAPI_MSG_TYPE_NOTIFY received\n"); + _netapi_notify_event(msg.content.ptr); + break; default: break; } From 81ed9c8295730df0a1dda3151eb0707bd7967103 Mon Sep 17 00:00:00 2001 From: Elena Frank Date: Tue, 9 Dec 2025 14:50:06 +0100 Subject: [PATCH 7/9] nimble/rpble: don't send DIS on connection event `gnrc_rpl` now receives a notification if a new node is reachable (i.e., connected) and sends a DIS. No need anymore to also do it in rpble. --- pkg/nimble/rpble/nimble_rpble.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/pkg/nimble/rpble/nimble_rpble.c b/pkg/nimble/rpble/nimble_rpble.c index da56a56e6e..815fff7284 100644 --- a/pkg/nimble/rpble/nimble_rpble.c +++ b/pkg/nimble/rpble/nimble_rpble.c @@ -222,9 +222,6 @@ static void _on_netif_evt(int handle, nimble_netif_event_t event, case NIMBLE_NETIF_CONNECTED_MASTER: /* parent selected */ assert(_current_parent == handle); - /* send a DIS once connected to a (new) parent) */ - gnrc_rpl_send_DIS(NULL, (ipv6_addr_t *) &ipv6_addr_all_rpl_nodes, - NULL, 0); break; case NIMBLE_NETIF_CONNECTED_SLAVE: /* child added */ From 4a326d6c351d66c308a032f8c66504eb134c1f42 Mon Sep 17 00:00:00 2001 From: Elena Frank Date: Tue, 9 Dec 2025 14:53:53 +0100 Subject: [PATCH 8/9] gnrc: include netapi_notify module in deps --- sys/net/gnrc/Makefile.dep | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sys/net/gnrc/Makefile.dep b/sys/net/gnrc/Makefile.dep index afea132093..626174b1a5 100644 --- a/sys/net/gnrc/Makefile.dep +++ b/sys/net/gnrc/Makefile.dep @@ -447,6 +447,11 @@ ifneq (,$(filter gnrc,$(USEMODULE))) USEMODULE += gnrc_netif USEMODULE += gnrc_netif_hdr USEMODULE += gnrc_pktbuf + ifneq (,$(filter gnrc_ipv6,$(USEMODULE))) + ifneq (,$(filter nimble_netif,$(USEMODULE))) + USEMODULE += gnrc_netapi_notify + endif + endif ifneq (,$(filter sock_async, $(USEMODULE))) USEMODULE += gnrc_sock_async endif From 1f3daf33352f4558983d98b324a654da52be953d Mon Sep 17 00:00:00 2001 From: Marian Buschsieweke Date: Tue, 9 Dec 2025 19:11:09 +0100 Subject: [PATCH 9/9] sys/atomic_utils: fix compilation with C++ compiler --- sys/include/atomic_utils.h | 72 +++++++++++++++++++------------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/sys/include/atomic_utils.h b/sys/include/atomic_utils.h index 97b3aaaaa9..2c685f4066 100644 --- a/sys/include/atomic_utils.h +++ b/sys/include/atomic_utils.h @@ -274,14 +274,14 @@ static inline uint64_t atomic_load_u64(const volatile uint64_t *var); static inline unsigned atomic_load_unsigned(const volatile unsigned *var) { if (sizeof(uint64_t) == sizeof(unsigned)) { - return atomic_load_u64((volatile void *)var); + return atomic_load_u64((const volatile uint64_t *)(uintptr_t)var); } if (sizeof(uint32_t) == sizeof(unsigned)) { - return atomic_load_u32((volatile void *)var); + return atomic_load_u32((const volatile uint32_t *)(uintptr_t)var); } - return atomic_load_u16((volatile void *)var); + return atomic_load_u16((const volatile uint16_t *)(uintptr_t)var); } /** @@ -362,13 +362,13 @@ static inline void atomic_store_u64(volatile uint64_t *dest, uint64_t val); static inline void atomic_store_unsigned(volatile unsigned *dest, unsigned val) { if (sizeof(uint64_t) == sizeof(unsigned)) { - atomic_store_u64((volatile void *)dest, val); + atomic_store_u64((volatile uint64_t *)(uintptr_t)dest, val); } else if (sizeof(uint32_t) == sizeof(unsigned)) { - atomic_store_u32((volatile void *)dest, val); + atomic_store_u32((volatile uint32_t *)(uintptr_t)dest, val); } else { - atomic_store_u16((volatile void *)dest, val); + atomic_store_u16((volatile uint16_t *)(uintptr_t)dest, val); } } @@ -462,14 +462,14 @@ static inline unsigned atomic_fetch_add_unsigned(volatile unsigned *dest, unsigned summand) { if (sizeof(unsigned) == sizeof(uint64_t)) { - return atomic_fetch_add_u64((volatile void *)dest, summand); + return atomic_fetch_add_u64((volatile uint64_t *)(uintptr_t)dest, summand); } if (sizeof(unsigned) == sizeof(uint32_t)) { - return atomic_fetch_add_u32((volatile void *)dest, summand); + return atomic_fetch_add_u32((volatile uint32_t *)(uintptr_t)dest, summand); } - return atomic_fetch_add_u16((volatile void *)dest, summand); + return atomic_fetch_add_u16((volatile uint16_t *)(uintptr_t)dest, summand); } /** @} */ @@ -528,14 +528,14 @@ static inline unsigned atomic_fetch_sub_unsigned(volatile unsigned *dest, unsigned subtrahend) { if (sizeof(unsigned) == sizeof(uint64_t)) { - return atomic_fetch_sub_u64((volatile void *)dest, subtrahend); + return atomic_fetch_sub_u64((volatile uint64_t *)(uintptr_t)dest, subtrahend); } if (sizeof(unsigned) == sizeof(uint32_t)) { - return atomic_fetch_sub_u32((volatile void *)dest, subtrahend); + return atomic_fetch_sub_u32((volatile uint32_t *)(uintptr_t)dest, subtrahend); } - return atomic_fetch_sub_u16((volatile void *)dest, subtrahend); + return atomic_fetch_sub_u16((volatile uint16_t *)(uintptr_t)dest, subtrahend); } /** @} */ @@ -593,14 +593,14 @@ static inline unsigned atomic_fetch_or_unsigned(volatile unsigned *dest, unsigned val) { if (sizeof(unsigned) == sizeof(uint64_t)) { - return atomic_fetch_or_u64((volatile void *)dest, val); + return atomic_fetch_or_u64((volatile uint64_t *)(uintptr_t)dest, val); } if (sizeof(unsigned) == sizeof(uint32_t)) { - return atomic_fetch_or_u32((volatile void *)dest, val); + return atomic_fetch_or_u32((volatile uint32_t *)(uintptr_t)dest, val); } - return atomic_fetch_or_u16((volatile void *)dest, val); + return atomic_fetch_or_u16((volatile uint16_t *)(uintptr_t)dest, val); } /** @} */ @@ -658,14 +658,14 @@ static inline unsigned atomic_fetch_xor_unsigned(volatile unsigned *dest, unsigned val) { if (sizeof(unsigned) == sizeof(uint64_t)) { - return atomic_fetch_xor_u64((volatile void *)dest, val); + return atomic_fetch_xor_u64((volatile uint64_t *)(uintptr_t)dest, val); } if (sizeof(unsigned) == sizeof(uint32_t)) { - return atomic_fetch_xor_u32((volatile void *)dest, val); + return atomic_fetch_xor_u32((volatile uint32_t *)(uintptr_t)dest, val); } - return atomic_fetch_xor_u16((volatile void *)dest, val); + return atomic_fetch_xor_u16((volatile uint16_t *)(uintptr_t)dest, val); } /** @} */ @@ -723,14 +723,14 @@ static inline unsigned atomic_fetch_and_unsigned(volatile unsigned *dest, unsigned val) { if (sizeof(unsigned) == sizeof(uint64_t)) { - return atomic_fetch_and_u64((volatile void *)dest, val); + return atomic_fetch_and_u64((volatile uint64_t *)(uintptr_t)dest, val); } if (sizeof(unsigned) == sizeof(uint32_t)) { - return atomic_fetch_and_u32((volatile void *)dest, val); + return atomic_fetch_and_u32((volatile uint32_t *)(uintptr_t)dest, val); } - return atomic_fetch_and_u16((volatile void *)dest, val); + return atomic_fetch_and_u16((volatile uint16_t *)(uintptr_t)dest, val); } /** @} */ @@ -935,14 +935,14 @@ static inline unsigned semi_atomic_fetch_add_unsigned(volatile unsigned *dest, unsigned summand) { if (sizeof(unsigned) == sizeof(uint64_t)) { - return semi_atomic_fetch_add_u64((volatile void *)dest, summand); + return semi_atomic_fetch_add_u64((volatile uint64_t *)(uintptr_t)dest, summand); } if (sizeof(unsigned) == sizeof(uint32_t)) { - return semi_atomic_fetch_add_u32((volatile void *)dest, summand); + return semi_atomic_fetch_add_u32((volatile uint32_t *)(uintptr_t)dest, summand); } - return semi_atomic_fetch_add_u16((volatile void *)dest, summand); + return semi_atomic_fetch_add_u16((volatile uint16_t *)(uintptr_t)dest, summand); } /** @} */ @@ -1001,14 +1001,14 @@ static inline unsigned semi_atomic_fetch_sub_unsigned(volatile unsigned *dest, unsigned subtrahend) { if (sizeof(unsigned) == sizeof(uint64_t)) { - return semi_atomic_fetch_sub_u64((volatile void *)dest, subtrahend); + return semi_atomic_fetch_sub_u64((volatile uint64_t *)(uintptr_t)dest, subtrahend); } if (sizeof(unsigned) == sizeof(uint32_t)) { - return semi_atomic_fetch_sub_u32((volatile void *)dest, subtrahend); + return semi_atomic_fetch_sub_u32((volatile uint32_t *)(uintptr_t)dest, subtrahend); } - return semi_atomic_fetch_sub_u16((volatile void *)dest, subtrahend); + return semi_atomic_fetch_sub_u16((volatile uint16_t *)(uintptr_t)dest, subtrahend); } /** @} */ @@ -1066,14 +1066,14 @@ static inline unsigned semi_atomic_fetch_or_unsigned(volatile unsigned *dest, unsigned val) { if (sizeof(unsigned) == sizeof(uint64_t)) { - return semi_atomic_fetch_or_u64((volatile void *)dest, val); + return semi_atomic_fetch_or_u64((volatile uint64_t *)(uintptr_t)dest, val); } if (sizeof(unsigned) == sizeof(uint32_t)) { - return semi_atomic_fetch_or_u32((volatile void *)dest, val); + return semi_atomic_fetch_or_u32((volatile uint32_t *)(uintptr_t)dest, val); } - return semi_atomic_fetch_or_u16((volatile void *)dest, val); + return semi_atomic_fetch_or_u16((volatile uint16_t *)(uintptr_t)dest, val); } /** @} */ @@ -1132,14 +1132,14 @@ static inline unsigned semi_atomic_fetch_xor_unsigned(volatile unsigned *dest, unsigned val) { if (sizeof(unsigned) == sizeof(uint64_t)) { - return semi_atomic_fetch_xor_u64((volatile void *)dest, val); + return semi_atomic_fetch_xor_u64((volatile uint64_t *)(uintptr_t)dest, val); } if (sizeof(unsigned) == sizeof(uint32_t)) { - return semi_atomic_fetch_xor_u32((volatile void *)dest, val); + return semi_atomic_fetch_xor_u32((volatile uint32_t *)(uintptr_t)dest, val); } - return semi_atomic_fetch_xor_u16((volatile void *)dest, val); + return semi_atomic_fetch_xor_u16((volatile uint16_t *)(uintptr_t)dest, val); } /** @} */ @@ -1198,14 +1198,14 @@ static inline unsigned semi_atomic_fetch_and_unsigned(volatile unsigned *dest, unsigned val) { if (sizeof(unsigned) == sizeof(uint64_t)) { - return semi_atomic_fetch_and_u64((volatile void *)dest, val); + return semi_atomic_fetch_and_u64((volatile uint64_t *)(uintptr_t)dest, val); } if (sizeof(unsigned) == sizeof(uint32_t)) { - return semi_atomic_fetch_and_u32((volatile void *)dest, val); + return semi_atomic_fetch_and_u32((volatile uint32_t *)(uintptr_t)dest, val); } - return semi_atomic_fetch_and_u16((volatile void *)dest, val); + return semi_atomic_fetch_and_u16((volatile uint16_t *)(uintptr_t)dest, val); } /** @} */