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/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 */ 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); } /** @} */ 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/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/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..d495727270 --- /dev/null +++ b/sys/include/net/gnrc/netapi/notify.h @@ -0,0 +1,104 @@ +/* + * 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 { + 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; + +/** + * @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 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. + * + * @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..342ea57164 100644 --- a/sys/include/net/gnrc/nettype.h +++ b/sys/include/net/gnrc/nettype.h @@ -120,6 +120,36 @@ 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. + * } + */ + + /** + * @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, + /** + * @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, + /** @} */ + /** * @{ * @name Testing 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/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..626174b1a5 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 @@ -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 @@ -446,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 @@ -460,6 +466,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..2e1fe08b94 --- /dev/null +++ b/sys/net/gnrc/netapi/notify/gnrc_netapi_notify.c @@ -0,0 +1,79 @@ +/* + * 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 + +#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 _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; + + 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; + 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; + } + + /* Acknowledge the read data */ + gnrc_netapi_notify_ack(¬ify->ack); + + return res; +} + +/** @} */ diff --git a/sys/net/gnrc/network_layer/ipv6/gnrc_ipv6.c b/sys/net/gnrc/network_layer/ipv6/gnrc_ipv6.c index 5fbabfb625..33517d80dd 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,125 @@ 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; + + 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)); + } +} + +/** + * @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; + + 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); +#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 +299,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 +333,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; 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; } 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) {