Skip to content
27 changes: 27 additions & 0 deletions doc/connectivity/networking/conn_mgr/main.rst
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,14 @@ You can individually set them for each iface.
It is left to connectivity implementations to successfully and accurately implement these two features as described below.
See :ref:`conn_mgr_impl_timeout_persistence` for more details from the connectivity implementation perspective.

The Connection Manager also implements the following optional feature:

* :ref:`Interface idle timeouts <conn_mgr_control_idle_timeout>`

.. note::
The only requirement on the connectivity implementation to implement idle timeouts is to call :c:func:`conn_mgr_if_used` each
time the interface is used.

.. _conn_mgr_control_timeouts:

Connection Timeouts
Expand All @@ -306,6 +314,16 @@ The connection attempt continues indefinitely until it succeeds, unless a timeou
In that case, the connection attempt will be abandoned if the timeout elapses before it succeeds.
If this happens, the :ref:`timeout event<conn_mgr_control_events_timeout>` is raised.

.. _conn_mgr_control_idle_timeout:

Interface Idle Timeout
----------------------

The connection manager enables users to apply an inactivity timeout on an interface (:c:func:`conn_mgr_if_set_idle_timeout`).
Once connected, if the interface goes for the configured number of seconds without any activity, the interface is automatically disconnected.
If this happens, the :ref:`idle timeout event<conn_mgr_control_events_idle_timeout>` is raised.
An idle timeout is considered an unintentional connection loss for the purposes of :ref:`Connection persistence <conn_mgr_control_persistence>`.

.. _conn_mgr_control_persistence:

Connection Persistence
Expand Down Expand Up @@ -357,6 +375,15 @@ The :c:macro:`NET_EVENT_CONN_IF_TIMEOUT` event is raised when an :ref:`iface ass

Handlers of this event will be passed a pointer to the iface that timed out attempting to associate.

.. _conn_mgr_control_events_idle_timeout:

Idle Timeout
------------

The :c:macro:`NET_EVENT_CONN_IF_IDLE_TIMEOUT` event is raised when an interface is considered :ref:`inactive <conn_mgr_control_idle_timeout>`.

Handlers of this event will be passed a pointer to the iface that timed out attempting to associate.

.. _conn_mgr_control_events_listening:

Listening for control events
Expand Down
23 changes: 23 additions & 0 deletions drivers/net/nsos_sockets.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ struct nsos_socket {

static sys_dlist_t nsos_polls = SYS_DLIST_STATIC_INIT(&nsos_polls);

/* Forward declaration of the interface */
NET_IF_DECLARE(nsos_socket, 0);

static int socket_family_to_nsos_mid(int family, int *family_mid)
{
switch (family) {
Expand Down Expand Up @@ -693,6 +696,7 @@ static int nsos_connect_blocking(struct nsos_socket *sock,

static int nsos_connect(void *obj, const struct sockaddr *addr, socklen_t addrlen)
{
struct net_if *iface = NET_IF_GET(nsos_socket, 0);
struct nsos_socket *sock = obj;
struct nsos_mid_sockaddr_storage addr_storage_mid;
struct nsos_mid_sockaddr *addr_mid = (struct nsos_mid_sockaddr *)&addr_storage_mid;
Expand All @@ -718,6 +722,7 @@ static int nsos_connect(void *obj, const struct sockaddr *addr, socklen_t addrle
errno = nsi_errno_from_mid(-ret);
return -1;
}
conn_mgr_if_used(iface);

return ret;
}
Expand All @@ -738,6 +743,7 @@ static int nsos_listen(void *obj, int backlog)

static int nsos_accept(void *obj, struct sockaddr *addr, socklen_t *addrlen)
{
struct net_if *iface = NET_IF_GET(nsos_socket, 0);
struct nsos_socket *accept_sock = obj;
struct nsos_mid_sockaddr_storage addr_storage_mid;
struct nsos_mid_sockaddr *addr_mid = (struct nsos_mid_sockaddr *)&addr_storage_mid;
Expand Down Expand Up @@ -782,6 +788,7 @@ static int nsos_accept(void *obj, struct sockaddr *addr, socklen_t *addrlen)

zvfs_finalize_typed_fd(zephyr_fd, conn_sock, &nsos_socket_fd_op_vtable.fd_vtable,
ZVFS_MODE_IFSOCK);
conn_mgr_if_used(iface);

return zephyr_fd;

Expand All @@ -799,6 +806,7 @@ static int nsos_accept(void *obj, struct sockaddr *addr, socklen_t *addrlen)
static ssize_t nsos_sendto(void *obj, const void *buf, size_t len, int flags,
const struct sockaddr *addr, socklen_t addrlen)
{
struct net_if *iface = NET_IF_GET(nsos_socket, 0);
struct nsos_socket *sock = obj;
struct nsos_mid_sockaddr_storage addr_storage_mid;
struct nsos_mid_sockaddr *addr_mid = (struct nsos_mid_sockaddr *)&addr_storage_mid;
Expand Down Expand Up @@ -832,11 +840,13 @@ static ssize_t nsos_sendto(void *obj, const void *buf, size_t len, int flags,
return -1;
}

conn_mgr_if_used(iface);
return ret;
}

static ssize_t nsos_sendmsg(void *obj, const struct msghdr *msg, int flags)
{
struct net_if *iface = NET_IF_GET(nsos_socket, 0);
struct nsos_socket *sock = obj;
struct nsos_mid_sockaddr_storage addr_storage_mid;
struct nsos_mid_sockaddr *addr_mid = (struct nsos_mid_sockaddr *)&addr_storage_mid;
Expand Down Expand Up @@ -893,12 +903,14 @@ static ssize_t nsos_sendmsg(void *obj, const struct msghdr *msg, int flags)
return -1;
}

conn_mgr_if_used(iface);
return ret;
}

static ssize_t nsos_recvfrom(void *obj, void *buf, size_t len, int flags,
struct sockaddr *addr, socklen_t *addrlen)
{
struct net_if *iface = NET_IF_GET(nsos_socket, 0);
struct nsos_socket *sock = obj;
struct nsos_mid_sockaddr_storage addr_storage_mid;
struct nsos_mid_sockaddr *addr_mid = (struct nsos_mid_sockaddr *)&addr_storage_mid;
Expand Down Expand Up @@ -932,6 +944,7 @@ static ssize_t nsos_recvfrom(void *obj, void *buf, size_t len, int flags,
return -1;
}

conn_mgr_if_used(iface);
return ret;
}

Expand Down Expand Up @@ -1492,6 +1505,7 @@ static int nsos_getaddrinfo(const char *node, const char *service,
const struct zsock_addrinfo *hints,
struct zsock_addrinfo **res)
{
struct net_if *iface = NET_IF_GET(nsos_socket, 0);
struct nsos_mid_addrinfo hints_mid;
struct nsos_mid_addrinfo *res_mid;
int system_errno;
Expand Down Expand Up @@ -1525,6 +1539,7 @@ static int nsos_getaddrinfo(const char *node, const char *service,
errno = -ret;
return DNS_EAI_SYSTEM;
}
conn_mgr_if_used(iface);

return ret;
}
Expand Down Expand Up @@ -1651,6 +1666,14 @@ static int nsos_net_if_disconnect(struct conn_mgr_conn_binding *const binding)
LOG_INF("NSOS: dormant");
k_work_cancel_delayable(&data->work);
net_if_dormant_on(binding->iface);

if (conn_mgr_binding_get_flag(binding, CONN_MGR_IF_PERSISTENT) &&
!conn_mgr_binding_get_flag(binding, CONN_MGR_IF_DISCONNECTING)) {
/* Interface marked as persistent, application didn't request the disconnect */
LOG_INF("NSOS: reconnecting");
k_work_reschedule(&data->work, data->connect_delay);
}

return 0;
}

Expand Down
47 changes: 47 additions & 0 deletions include/zephyr/net/conn_mgr_connectivity.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ extern "C" {
enum {
NET_EVENT_CONN_CMD_IF_TIMEOUT_VAL,
NET_EVENT_CONN_CMD_IF_FATAL_ERROR_VAL,
NET_EVENT_CONN_CMD_IF_IDLE_TIMEOUT_VAL,

NET_EVENT_CONN_CMD_MAX
};
Expand All @@ -54,6 +55,7 @@ BUILD_ASSERT(NET_EVENT_CONN_CMD_MAX <= NET_MGMT_MAX_COMMANDS,
enum net_event_conn_cmd {
NET_MGMT_CMD(NET_EVENT_CONN_CMD_IF_TIMEOUT),
NET_MGMT_CMD(NET_EVENT_CONN_CMD_IF_FATAL_ERROR),
NET_MGMT_CMD(NET_EVENT_CONN_CMD_IF_IDLE_TIMEOUT),
};

/** @endcond */
Expand All @@ -70,6 +72,12 @@ enum net_event_conn_cmd {
#define NET_EVENT_CONN_IF_FATAL_ERROR \
(NET_MGMT_CONN_IF_EVENT | NET_EVENT_CONN_CMD_IF_FATAL_ERROR)

/**
* @brief net_mgmt event raised when an interface times out due to inactivity
*/
#define NET_EVENT_CONN_IF_IDLE_TIMEOUT \
(NET_MGMT_CONN_IF_EVENT | NET_EVENT_CONN_CMD_IF_IDLE_TIMEOUT)


/**
* @brief Per-iface connectivity flags
Expand Down Expand Up @@ -264,6 +272,45 @@ int conn_mgr_if_get_timeout(struct net_if *iface);
*/
int conn_mgr_if_set_timeout(struct net_if *iface, int timeout);

/**
* @brief Get the idle timeout for an iface
*
* If the provided iface is bound to a connectivity implementation, retrieves the idle timeout
* setting in seconds for it.
*
* @param iface - Pointer to the iface to check.
* @return int - The connectivity timeout value (in seconds) if it could be retrieved, otherwise
* CONN_MGR_IF_NO_TIMEOUT.
*/
int conn_mgr_if_get_idle_timeout(struct net_if *iface);

/**
* @brief Set the idle timeout for an iface.
*
* If the provided iface is bound to a connectivity implementation, sets the idle timeout setting
* in seconds for it.
*
* @param iface - Pointer to the network interface to modify.
* @param timeout - The timeout value to set (in seconds).
* Pass @ref CONN_MGR_IF_NO_TIMEOUT to disable the timeout.
* @retval 0 on success.
* @retval -ENOTSUP if the provided iface is not bound to a connectivity implementation.
*/
int conn_mgr_if_set_idle_timeout(struct net_if *iface, int timeout);

#if defined(CONFIG_NET_CONNECTION_MANAGER) || defined(__DOXYGEN__)
/**
* @brief Notify connection manager that interface was just used
*
* @note Typically called from network drivers, not application software.
*
* @param iface iface that was just used
*/
void conn_mgr_if_used(struct net_if *iface);
#else
#define conn_mgr_if_used(iface) (void)(iface)
#endif /* defined(CONFIG_NET_CONNECTION_MANAGER) || defined(__DOXYGEN__) */

/**
* @}
*/
Expand Down
13 changes: 13 additions & 0 deletions include/zephyr/net/conn_mgr_connectivity_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -193,9 +193,22 @@ struct conn_mgr_conn_binding {
*/
int timeout;

/**
* Usage timeout (seconds)
*
* Indicates to the connectivity implementation how long the interface can be idle
* for before automatically taking the interface down.
*
* Set to @ref CONN_MGR_IF_NO_TIMEOUT to indicate that no idle timeout should be used.
*/
int idle_timeout;

/** @} */

/** @cond INTERNAL_HIDDEN */
/* Internal-use work item for tracking interface idle timeouts */
struct k_work_delayable idle_worker;

/* Internal-use mutex for protecting access to the binding and API functions. */
struct k_mutex *mutex;
/** @endcond */
Expand Down
10 changes: 10 additions & 0 deletions include/zephyr/net/net_if.h
Original file line number Diff line number Diff line change
Expand Up @@ -3495,6 +3495,16 @@ extern int net_stats_prometheus_scrape(struct prometheus_collector *collector,

/* Network device initialization macros */

/**
* @brief Forward declaration of a network interface
*
* Enables to use of `NET_IF_GET` above the instantiation macro.
*
* @param dev_id Device ID provided to `NET_IF_INIT` or `NET_IF_OFFLOAD_INIT`
*/
#define NET_IF_DECLARE(dev_id, inst) \
static struct net_if NET_IF_GET_NAME(dev_id, inst)[NET_IF_MAX_CONFIGS]

#define Z_NET_DEVICE_INIT_INSTANCE(node_id, dev_id, name, instance, \
init_fn, pm, data, config, prio, \
api, l2, l2_ctx_type, mtu) \
Expand Down
Loading