1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2025-12-13 16:43:52 +01:00

Merge pull request #21608 from elenaf9/gnrc/netapi/event

gnrc, nimble/ble: notify network layer of BLE connection events
This commit is contained in:
benpicco 2025-12-09 18:24:48 +00:00 committed by GitHub
commit 2c01ea02dc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 797 additions and 87 deletions

View File

@ -27,6 +27,7 @@
#include "net/ble.h" #include "net/ble.h"
#include "net/bluetil/addr.h" #include "net/bluetil/addr.h"
#include "net/gnrc/netapi/notify.h"
#include "net/gnrc/netif.h" #include "net/gnrc/netif.h"
#include "net/gnrc/netif/hdr.h" #include "net/gnrc/netif/hdr.h"
#include "net/gnrc/netreg.h" #include "net/gnrc/netreg.h"
@ -39,6 +40,7 @@
#include "host/ble_gap.h" #include "host/ble_gap.h"
#include "host/util/util.h" #include "host/util/util.h"
#include "mem/mem.h" #include "mem/mem.h"
#include <string.h>
#define ENABLE_DEBUG 0 #define ENABLE_DEBUG 0
#include "debug.h" #include "debug.h"
@ -331,6 +333,29 @@ end:
ble_l2cap_recv_ready(event->receive.chan, rxb); 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) static int _on_l2cap_client_evt(struct ble_l2cap_event *event, void *arg)
{ {
int handle = (int)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_L2CAP_CLIENT;
conn->state &= ~NIMBLE_NETIF_CONNECTING; conn->state &= ~NIMBLE_NETIF_CONNECTING;
_notify(handle, NIMBLE_NETIF_CONNECTED_MASTER, conn->addr); _notify(handle, NIMBLE_NETIF_CONNECTED_MASTER, conn->addr);
_dispatch_connection_event(NETAPI_NOTIFY_L2_NEIGH_CONNECTED, conn->addr);
break; break;
case BLE_L2CAP_EVENT_COC_DISCONNECTED: case BLE_L2CAP_EVENT_COC_DISCONNECTED:
assert(conn->state & NIMBLE_NETIF_L2CAP_CLIENT); 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); _notify(handle, NIMBLE_NETIF_CONNECTED_SLAVE, conn->addr);
_dispatch_connection_event(NETAPI_NOTIFY_L2_NEIGH_CONNECTED, conn->addr);
break; break;
case BLE_L2CAP_EVENT_COC_DISCONNECTED: case BLE_L2CAP_EVENT_COC_DISCONNECTED:
conn = nimble_netif_conn_from_gaphandle(event->disconnect.conn_handle); 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); nimble_netif_conn_free(handle, addr);
thread_flags_set(_netif_thread, FLAG_TX_NOTCONN); thread_flags_set(_netif_thread, FLAG_TX_NOTCONN);
_notify(handle, type, addr); _notify(handle, type, addr);
_dispatch_connection_event(NETAPI_NOTIFY_L2_NEIGH_DISCONNECTED, addr);
break; break;
} }
case BLE_GAP_EVENT_CONN_UPDATE: 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); nimble_netif_conn_free(handle, addr);
thread_flags_set(_netif_thread, FLAG_TX_NOTCONN); thread_flags_set(_netif_thread, FLAG_TX_NOTCONN);
_notify(handle, type, addr); _notify(handle, type, addr);
_dispatch_connection_event(NETAPI_NOTIFY_L2_NEIGH_DISCONNECTED, addr);
break; break;
} }
case BLE_GAP_EVENT_CONN_UPDATE: case BLE_GAP_EVENT_CONN_UPDATE:

View File

@ -222,9 +222,6 @@ static void _on_netif_evt(int handle, nimble_netif_event_t event,
case NIMBLE_NETIF_CONNECTED_MASTER: case NIMBLE_NETIF_CONNECTED_MASTER:
/* parent selected */ /* parent selected */
assert(_current_parent == handle); 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; break;
case NIMBLE_NETIF_CONNECTED_SLAVE: case NIMBLE_NETIF_CONNECTED_SLAVE:
/* child added */ /* child added */

View File

@ -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) static inline unsigned atomic_load_unsigned(const volatile unsigned *var)
{ {
if (sizeof(uint64_t) == sizeof(unsigned)) { 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)) { 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) static inline void atomic_store_unsigned(volatile unsigned *dest, unsigned val)
{ {
if (sizeof(uint64_t) == sizeof(unsigned)) { 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)) { 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 { 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) unsigned summand)
{ {
if (sizeof(unsigned) == sizeof(uint64_t)) { 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)) { 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) unsigned subtrahend)
{ {
if (sizeof(unsigned) == sizeof(uint64_t)) { 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)) { 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) unsigned val)
{ {
if (sizeof(unsigned) == sizeof(uint64_t)) { 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)) { 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) unsigned val)
{ {
if (sizeof(unsigned) == sizeof(uint64_t)) { 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)) { 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) unsigned val)
{ {
if (sizeof(unsigned) == sizeof(uint64_t)) { 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)) { 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) unsigned summand)
{ {
if (sizeof(unsigned) == sizeof(uint64_t)) { 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)) { 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) unsigned subtrahend)
{ {
if (sizeof(unsigned) == sizeof(uint64_t)) { 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)) { 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) unsigned val)
{ {
if (sizeof(unsigned) == sizeof(uint64_t)) { 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)) { 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) unsigned val)
{ {
if (sizeof(unsigned) == sizeof(uint64_t)) { 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)) { 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) unsigned val)
{ {
if (sizeof(unsigned) == sizeof(uint64_t)) { 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)) { 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);
} }
/** @} */ /** @} */

View File

@ -288,6 +288,7 @@
#include "net/netopt.h" #include "net/netopt.h"
#include "net/gnrc/netapi.h" #include "net/gnrc/netapi.h"
#include "net/gnrc/netapi/notify.h"
#include "net/gnrc/netreg.h" #include "net/gnrc/netreg.h"
#include "net/gnrc/nettype.h" #include "net/gnrc/nettype.h"
#include "net/gnrc/netif.h" #include "net/gnrc/netif.h"

View File

@ -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, int gnrc_ipv6_nib_nc_set(const ipv6_addr_t *ipv6, unsigned iface,
const uint8_t *l2addr, size_t l2addr_len); 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 * @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); 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 * @brief Mark neighbor with address @p ipv6 as reachable
* *

View File

@ -60,6 +60,7 @@
#include "thread.h" #include "thread.h"
#include "net/netopt.h" #include "net/netopt.h"
#include "net/gnrc/netapi/notify.h"
#include "net/gnrc/nettype.h" #include "net/gnrc/nettype.h"
#include "net/gnrc/pkt.h" #include "net/gnrc/pkt.h"
@ -88,10 +89,17 @@ extern "C" {
#define GNRC_NETAPI_MSG_TYPE_GET (0x0204) #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) #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) * @brief Data structure to be send for setting (@ref GNRC_NETAPI_MSG_TYPE_SET)
* and getting (@ref GNRC_NETAPI_MSG_TYPE_GET) options * 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); 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 * @brief Shortcut function for sending @ref GNRC_NETAPI_MSG_TYPE_RCV messages
* *

View File

@ -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 <elena.frank@tu-dresden.de>
*/
#include <stdint.h>
#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
/** @} */

View File

@ -330,7 +330,8 @@ static inline void gnrc_netreg_entry_init_cb(gnrc_netreg_entry_t *entry,
* @brief Registers a thread to the registry. * @brief Registers a thread to the registry.
* *
* @details The semantics are: Thread gnrc_netreg_entry_t::pid is interested in * @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 * @param[in] type Type of the protocol. Must not be < GNRC_NETTYPE_UNDEF or
* >= GNRC_NETTYPE_NUMOF. * >= GNRC_NETTYPE_NUMOF.

View File

@ -120,6 +120,36 @@ typedef enum {
#endif #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 * @name Testing

View File

@ -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, bool gnrc_rpl_parent_add_by_addr(gnrc_rpl_dodag_t *dodag, ipv6_addr_t *addr,
gnrc_rpl_parent_t **parent); 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. * @brief Remove the @p parent from its DODAG.
* *

View File

@ -43,6 +43,9 @@ endif
ifneq (,$(filter gnrc_netapi,$(USEMODULE))) ifneq (,$(filter gnrc_netapi,$(USEMODULE)))
DIRS += netapi DIRS += netapi
endif endif
ifneq (,$(filter gnrc_netapi_notify,$(USEMODULE)))
DIRS += netapi/notify
endif
ifneq (,$(filter gnrc_netif gnrc_netif_%,$(USEMODULE))) ifneq (,$(filter gnrc_netif gnrc_netif_%,$(USEMODULE)))
DIRS += netif DIRS += netif
endif endif

View File

@ -55,7 +55,7 @@ ifneq (,$(filter gnrc_uhcpc,$(USEMODULE)))
USEMODULE += fmt USEMODULE += fmt
endif 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 USEMODULE += gnrc
endif endif
@ -94,6 +94,7 @@ ifneq (,$(filter gnrc_rpl_p2p,$(USEMODULE)))
endif endif
ifneq (,$(filter gnrc_rpl,$(USEMODULE))) ifneq (,$(filter gnrc_rpl,$(USEMODULE)))
USEMODULE += gnrc_ipv6
USEMODULE += gnrc_icmpv6 USEMODULE += gnrc_icmpv6
USEMODULE += gnrc_ipv6_nib USEMODULE += gnrc_ipv6_nib
USEMODULE += trickle USEMODULE += trickle
@ -446,6 +447,11 @@ ifneq (,$(filter gnrc,$(USEMODULE)))
USEMODULE += gnrc_netif USEMODULE += gnrc_netif
USEMODULE += gnrc_netif_hdr USEMODULE += gnrc_netif_hdr
USEMODULE += gnrc_pktbuf USEMODULE += gnrc_pktbuf
ifneq (,$(filter gnrc_ipv6,$(USEMODULE)))
ifneq (,$(filter nimble_netif,$(USEMODULE)))
USEMODULE += gnrc_netapi_notify
endif
endif
ifneq (,$(filter sock_async, $(USEMODULE))) ifneq (,$(filter sock_async, $(USEMODULE)))
USEMODULE += gnrc_sock_async USEMODULE += gnrc_sock_async
endif endif
@ -460,6 +466,10 @@ ifneq (,$(filter gnrc,$(USEMODULE)))
endif endif
endif endif
ifneq (,$(filter gnrc_netapi_notify,$(USEMODULE)))
USEMODULE += sema_inv
endif
ifneq (,$(filter gnrc_pktbuf, $(USEMODULE))) ifneq (,$(filter gnrc_pktbuf, $(USEMODULE)))
ifeq (,$(filter gnrc_pktbuf_%, $(USEMODULE))) ifeq (,$(filter gnrc_pktbuf_%, $(USEMODULE)))
USEMODULE += gnrc_pktbuf_static USEMODULE += gnrc_pktbuf_static

View File

@ -24,8 +24,10 @@
#include "mbox.h" #include "mbox.h"
#include "msg.h" #include "msg.h"
#include "net/gnrc/netreg.h" #include "net/gnrc/netreg.h"
#include "net/gnrc/netapi/notify.h"
#include "net/gnrc/pktbuf.h" #include "net/gnrc/pktbuf.h"
#include "net/gnrc/netapi.h" #include "net/gnrc/netapi.h"
#include "sema_inv.h"
#define ENABLE_DEBUG 0 #define ENABLE_DEBUG 0
#include "debug.h" #include "debug.h"
@ -82,6 +84,42 @@ static inline int _snd_rcv_mbox(mbox_t *mbox, uint16_t type, gnrc_pktsnip_t *pkt
} }
#endif #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, int gnrc_netapi_dispatch(gnrc_nettype_t type, uint32_t demux_ctx,
uint16_t cmd, gnrc_pktsnip_t *pkt) 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); gnrc_pktbuf_hold(pkt, numof - 1);
while (sendto) { while (sendto) {
#if defined(MODULE_GNRC_NETAPI_MBOX) || defined(MODULE_GNRC_NETAPI_CALLBACKS) int status = _dispatch_single(sendto, cmd, pkt);
uint32_t status = 0; if (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) {
gnrc_pktbuf_release_error(pkt, status); 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); sendto = gnrc_netreg_getnext(sendto);
} }
} }
@ -141,3 +146,48 @@ int gnrc_netapi_dispatch(gnrc_nettype_t type, uint32_t demux_ctx,
return numof; 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(&notify.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, &notify);
if (status < 0) {
numof--;
/* Decrease semaphore counter manually when sending failed. */
sema_inv_post(&notify.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(&notify.ack);
}
return numof;
}

View File

@ -0,0 +1,3 @@
MODULE = gnrc_netapi_notify
include $(RIOTBASE)/Makefile.base

View File

@ -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 <elena.frank@tu-dresden.de>
*/
#include <assert.h>
#include <errno.h>
#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(&notify->ack);
return res;
}
/** @} */

View File

@ -29,6 +29,7 @@
#include "utlist.h" #include "utlist.h"
#include "net/gnrc/ipv6/nib.h" #include "net/gnrc/ipv6/nib.h"
#include "net/gnrc/netapi/notify.h"
#include "net/gnrc/netif/internal.h" #include "net/gnrc/netif/internal.h"
#include "net/gnrc/ipv6/whitelist.h" #include "net/gnrc/ipv6/whitelist.h"
#include "net/gnrc/ipv6/blacklist.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) static void *_event_loop(void *args)
{ {
msg_t msg, reply; 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; (void)args;
msg_init_queue(_msg_q, GNRC_IPV6_MSG_QUEUE_SIZE); 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 #ifdef MODULE_GNRC_IPV6_EXT_FRAG
gnrc_ipv6_ext_frag_init(); gnrc_ipv6_ext_frag_init();
#endif /* MODULE_GNRC_IPV6_EXT_FRAG */ #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 */ /* preinitialize ACK */
reply.type = GNRC_NETAPI_MSG_TYPE_ACK; reply.type = GNRC_NETAPI_MSG_TYPE_ACK;
@ -212,7 +333,10 @@ static void *_event_loop(void *args)
reply.content.value = -ENOTSUP; reply.content.value = -ENOTSUP;
msg_reply(&msg, &reply); msg_reply(&msg, &reply);
break; 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 #ifdef MODULE_GNRC_IPV6_EXT_FRAG
case GNRC_IPV6_EXT_FRAG_RBUF_GC: case GNRC_IPV6_EXT_FRAG_RBUF_GC:
gnrc_ipv6_ext_frag_rbuf_gc(); gnrc_ipv6_ext_frag_rbuf_gc();

View File

@ -73,6 +73,33 @@ bool _resolve_addr_from_ipv6(const ipv6_addr_t *dst, gnrc_netif_t *netif,
return res; 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, uint8_t _handle_aro(gnrc_netif_t *netif, const ipv6_hdr_t *ipv6,
const icmpv6_hdr_t *icmpv6, const icmpv6_hdr_t *icmpv6,
const sixlowpan_nd_opt_ar_t *aro, const ndp_opt_t *sl2ao, const sixlowpan_nd_opt_ar_t *aro, const ndp_opt_t *sl2ao,

View File

@ -70,6 +70,27 @@ extern "C" {
bool _resolve_addr_from_ipv6(const ipv6_addr_t *dst, gnrc_netif_t *netif, bool _resolve_addr_from_ipv6(const ipv6_addr_t *dst, gnrc_netif_t *netif,
gnrc_ipv6_nib_nc_t *nce); 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 * @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) */ #endif /* CONFIG_GNRC_IPV6_NIB_MULTIHOP_P6C || defined(DOXYGEN) */
#else /* CONFIG_GNRC_IPV6_NIB_6LN || defined(DOXYGEN) */ #else /* CONFIG_GNRC_IPV6_NIB_6LN || defined(DOXYGEN) */
#define _resolve_addr_from_ipv6(dst, netif, nce) (false) #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 /* _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 * => throw error in case it is compiled in => don't define it here as NOP macro
*/ */

View File

@ -24,6 +24,9 @@
#include "net/gnrc/ipv6/nib/nc.h" #include "net/gnrc/ipv6/nib/nc.h"
#include "_nib-internal.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, int gnrc_ipv6_nib_nc_set(const ipv6_addr_t *ipv6, unsigned iface,
const uint8_t *l2addr, size_t l2addr_len) 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; 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) void gnrc_ipv6_nib_nc_del(const ipv6_addr_t *ipv6, unsigned iface)
{ {
_nib_onl_entry_t *node = NULL; _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(); _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) void gnrc_ipv6_nib_nc_mark_reachable(const ipv6_addr_t *ipv6)
{ {
_nib_onl_entry_t *node = NULL; _nib_onl_entry_t *node = NULL;

View File

@ -15,12 +15,14 @@
* @author Cenk Gündoğan <cenk.guendogan@haw-hamburg.de> * @author Cenk Gündoğan <cenk.guendogan@haw-hamburg.de>
*/ */
#include "net/ipv6/addr.h"
#include <assert.h> #include <assert.h>
#include <string.h> #include <string.h>
#include "kernel_defines.h" #include "kernel_defines.h"
#include "net/icmpv6.h" #include "net/icmpv6.h"
#include "net/ipv6.h" #include "net/ipv6.h"
#include "net/gnrc/netapi/notify.h"
#include "net/gnrc/netif/internal.h" #include "net/gnrc/netif/internal.h"
#include "net/gnrc.h" #include "net/gnrc.h"
#include "mutex.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 }; static msg_t _lt_msg = { .type = GNRC_RPL_MSG_TYPE_LIFETIME_UPDATE };
#endif #endif
static msg_t _msg_q[GNRC_RPL_MSG_QUEUE_SIZE]; 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 mutex_t _inst_id_mutex = MUTEX_INIT;
static uint8_t _instance_id; 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. */ * queue, and registration with netreg can commence. */
mutex_lock(&eventloop_startup); mutex_lock(&eventloop_startup);
_me_reg.demux_ctx = ICMPV6_RPL_CTRL; /* Register interest in all ICMPv6 packets. */
_me_reg.target.pid = gnrc_rpl_pid; _me_icmpv6_reg.demux_ctx = ICMPV6_RPL_CTRL;
/* register interest in all ICMPv6 packets */ _me_icmpv6_reg.target.pid = gnrc_rpl_pid;
gnrc_netreg_register(GNRC_NETTYPE_ICMPV6, &_me_reg); 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(); gnrc_rpl_of_manager_init();
evtimer_init_msg(&gnrc_rpl_evtimer); 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); 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) static void *_event_loop(void *args)
{ {
msg_t msg, reply; msg_t msg, reply;
@ -342,6 +418,10 @@ static void *_event_loop(void *args)
reply.content.value = -ENOTSUP; reply.content.value = -ENOTSUP;
msg_reply(&msg, &reply); msg_reply(&msg, &reply);
break; break;
case GNRC_NETAPI_MSG_TYPE_NOTIFY:
DEBUG("RPL: GNRC_NETAPI_MSG_TYPE_NOTIFY received\n");
_netapi_notify_event(msg.content.ptr);
break;
default: default:
break; break;
} }

View File

@ -201,6 +201,19 @@ void gnrc_rpl_dodag_remove_all_parents(gnrc_rpl_dodag_t *dodag)
dodag->my_rank = GNRC_RPL_INFINITE_RANK; 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, bool gnrc_rpl_parent_add_by_addr(gnrc_rpl_dodag_t *dodag, ipv6_addr_t *addr,
gnrc_rpl_parent_t **parent) gnrc_rpl_parent_t **parent)
{ {