Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

net: conn_mgr: Add IPv4 and IPv6 tracking #72393

Merged
merged 5 commits into from
Jun 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions doc/connectivity/networking/conn_mgr/main.rst
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,17 @@ Afterwards, ifaces can become ready or unready without firing additional events,

glarsennordic marked this conversation as resolved.
Show resolved Hide resolved
When there are no longer any ready ifaces left, the :c:macro:`NET_EVENT_L4_DISCONNECTED` :ref:`network management <net_mgmt_interface>` event is triggered, and IP connectivity is said to be unready.

.. note::

Connection Manager also fires the following more specific ``CONNECTED`` / ``DISCONNECTED`` events:

- :c:macro:`NET_EVENT_L4_IPV4_CONNECTED`
- :c:macro:`NET_EVENT_L4_IPV4_DISCONNECTED`
- :c:macro:`NET_EVENT_L4_IPV6_CONNECTED`
- :c:macro:`NET_EVENT_L4_IPV6_DISCONNECTED`
Comment on lines +80 to +83
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- :c:macro:`NET_EVENT_L4_IPV4_CONNECTED`
- :c:macro:`NET_EVENT_L4_IPV4_DISCONNECTED`
- :c:macro:`NET_EVENT_L4_IPV6_CONNECTED`
- :c:macro:`NET_EVENT_L4_IPV6_DISCONNECTED`
- :c:enumerator:`NET_EVENT_L4_IPV4_CONNECTED`
- :c:enumerator:`NET_EVENT_L4_IPV4_DISCONNECTED`
- :c:enumerator:`NET_EVENT_L4_IPV6_CONNECTED`
- :c:enumerator:`NET_EVENT_L4_IPV6_DISCONNECTED`

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you certain these should be changed to :c:enumerator:? They are defined as macros whose values derive from the actual enumerator values

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Of course! Read too fast and missed the difference between the "CMD" enumerators ans the macros


These are similar to :c:macro:`NET_EVENT_L4_CONNECTED` and :c:macro:`NET_EVENT_L4_DISCONNECTED`, but specifically track whether IPv4- and IPv6-capable ifaces are ready.
kartben marked this conversation as resolved.
Show resolved Hide resolved

.. _conn_mgr_monitoring_usage:

Usage
Expand Down
21 changes: 21 additions & 0 deletions include/zephyr/net/net_event.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,10 @@ enum net_event_ipv4_cmd {
enum net_event_l4_cmd {
NET_EVENT_L4_CMD_CONNECTED = 1,
NET_EVENT_L4_CMD_DISCONNECTED,
NET_EVENT_L4_CMD_IPV4_CONNECTED,
NET_EVENT_L4_CMD_IPV4_DISCONNECTED,
NET_EVENT_L4_CMD_IPV6_CONNECTED,
NET_EVENT_L4_CMD_IPV6_DISCONNECTED,
NET_EVENT_L4_CMD_DNS_SERVER_ADD,
NET_EVENT_L4_CMD_DNS_SERVER_DEL,
NET_EVENT_L4_CMD_HOSTNAME_CHANGED,
Expand Down Expand Up @@ -289,6 +293,23 @@ enum net_event_l4_cmd {
#define NET_EVENT_L4_DISCONNECTED \
(_NET_EVENT_L4_BASE | NET_EVENT_L4_CMD_DISCONNECTED)


/** Event raised when IPv4 network connectivity is available. */
#define NET_EVENT_L4_IPV4_CONNECTED \
glarsennordic marked this conversation as resolved.
Show resolved Hide resolved
(_NET_EVENT_L4_BASE | NET_EVENT_L4_CMD_IPV4_CONNECTED)

/** Event emitted when IPv4 network connectivity is lost. */
#define NET_EVENT_L4_IPV4_DISCONNECTED \
(_NET_EVENT_L4_BASE | NET_EVENT_L4_CMD_IPV4_DISCONNECTED)

/** Event emitted when IPv6 network connectivity is available. */
#define NET_EVENT_L4_IPV6_CONNECTED \
(_NET_EVENT_L4_BASE | NET_EVENT_L4_CMD_IPV6_CONNECTED)

/** Event emitted when IPv6 network connectivity is lost. */
#define NET_EVENT_L4_IPV6_DISCONNECTED \
(_NET_EVENT_L4_BASE | NET_EVENT_L4_CMD_IPV6_DISCONNECTED)

/** Event emitted when a DNS server is added to the system. */
#define NET_EVENT_DNS_SERVER_ADD \
(_NET_EVENT_L4_BASE | NET_EVENT_L4_CMD_DNS_SERVER_ADD)
Expand Down
162 changes: 117 additions & 45 deletions subsys/net/conn_mgr/conn_mgr_monitor.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,15 @@ static struct k_thread conn_mgr_mon_thread;
*/
uint16_t iface_states[CONN_MGR_IFACE_MAX];

/* Tracks the total number of L4-ready ifaces */
static uint16_t ready_count;
/* Tracks the most recent total quantity of L4-ready ifaces (any, IPv4, IPv6) */
static uint16_t last_ready_count;
static uint16_t last_ready_count_ipv4;
static uint16_t last_ready_count_ipv6;

/* Tracks the last ifaces to change state in each respective direction */
static struct net_if *last_iface_down;
static struct net_if *last_iface_up;
/* Tracks the last ifaces to cause a major state change (any, IPv4, IPv6) */
static struct net_if *last_blame;
static struct net_if *last_blame_ipv4;
static struct net_if *last_blame_ipv6;

/* Used to signal when modifications have been made that need to be responded to */
K_SEM_DEFINE(conn_mgr_mon_updated, 1, 1);
Expand Down Expand Up @@ -70,81 +73,142 @@ static int conn_mgr_get_index_for_if(struct net_if *iface)
}

/**
* @brief Marks an iface as ready or unready and updates all associated state tracking.
* @brief Conveniently update iface readiness state
*
* @param idx - index (in iface_states) of the iface to mark ready or unready
* @param readiness - true if the iface should be considered ready, otherwise false
* @param ready - true if the iface should be considered ready, otherwise false
* @param ready_ipv4 - true if the iface is ready with IPv4, otherwise false
* @param ready_ipv6 - true if the iface is ready with IPv6, otherwise false
*/
static void conn_mgr_mon_set_ready(int idx, bool readiness)
static void conn_mgr_mon_set_ready(int idx, bool ready, bool ready_ipv4, bool ready_ipv6)
{
/* Clear and then update the L4-readiness bit */
iface_states[idx] &= ~CONN_MGR_IF_READY;
iface_states[idx] &= ~CONN_MGR_IF_READY_IPV4;
iface_states[idx] &= ~CONN_MGR_IF_READY_IPV6;

if (readiness) {
if (ready) {
iface_states[idx] |= CONN_MGR_IF_READY;
}

ready_count += 1;
last_iface_up = conn_mgr_mon_get_if_by_index(idx);
} else {
ready_count -= 1;
last_iface_down = conn_mgr_mon_get_if_by_index(idx);
if (ready_ipv4) {
iface_states[idx] |= CONN_MGR_IF_READY_IPV4;
}

if (ready_ipv6) {
iface_states[idx] |= CONN_MGR_IF_READY_IPV6;
}
}

static void conn_mgr_mon_handle_update(void)
{
int idx;
int original_ready_count;
bool is_ip_ready;
bool has_ip;
bool has_ipv6;
bool has_ipv4;
bool is_l4_ready;
bool is_ipv6_ready;
bool is_ipv4_ready;
bool is_l4_ready;
bool is_oper_up;
bool was_l4_ready;
bool was_ipv6_ready;
bool was_ipv4_ready;
bool is_ignored;
int ready_count = 0;
int ready_count_ipv4 = 0;
int ready_count_ipv6 = 0;
struct net_if *blame = NULL;
struct net_if *blame_ipv4 = NULL;
struct net_if *blame_ipv6 = NULL;

k_mutex_lock(&conn_mgr_mon_lock, K_FOREVER);

original_ready_count = ready_count;
for (idx = 0; idx < ARRAY_SIZE(iface_states); idx++) {
if (iface_states[idx] == 0) {
/* This interface is not used */
continue;
}

if (!(iface_states[idx] & CONN_MGR_IF_CHANGED)) {
/* No changes on this iface */
continue;
}

/* Clear the state-change flag */
iface_states[idx] &= ~CONN_MGR_IF_CHANGED;

/* Detect whether the iface is currently or was L4 ready */
/* Detect whether iface was previously considered ready */
was_l4_ready = iface_states[idx] & CONN_MGR_IF_READY;
is_ipv6_ready = iface_states[idx] & CONN_MGR_IF_IPV6_SET;
is_ipv4_ready = iface_states[idx] & CONN_MGR_IF_IPV4_SET;
was_ipv6_ready = iface_states[idx] & CONN_MGR_IF_READY_IPV6;
was_ipv4_ready = iface_states[idx] & CONN_MGR_IF_READY_IPV4;

/* Collect iface readiness requirements */
has_ipv6 = iface_states[idx] & CONN_MGR_IF_IPV6_SET;
has_ipv4 = iface_states[idx] & CONN_MGR_IF_IPV4_SET;
has_ip = has_ipv6 || has_ipv4;
is_oper_up = iface_states[idx] & CONN_MGR_IF_UP;
is_ignored = iface_states[idx] & CONN_MGR_IF_IGNORED;
is_ip_ready = is_ipv6_ready || is_ipv4_ready;
is_l4_ready = is_oper_up && is_ip_ready && !is_ignored;

/* Respond to changes to iface readiness */
/* Determine whether iface is currently considered ready */
is_l4_ready = is_oper_up && has_ip && !is_ignored;
is_ipv6_ready = is_oper_up && has_ipv6 && !is_ignored;
is_ipv4_ready = is_oper_up && has_ipv4 && !is_ignored;

/* Track ready iface count */
if (is_l4_ready) {
ready_count += 1;
}
if (is_ipv6_ready) {
ready_count_ipv6 += 1;
}
if (is_ipv4_ready) {
ready_count_ipv4 += 1;
}

/* If any states changed, track blame for possibly triggered events */
if (was_l4_ready != is_l4_ready) {
/* Track the iface readiness change */
conn_mgr_mon_set_ready(idx, is_l4_ready);
blame = conn_mgr_mon_get_if_by_index(idx);
}
if (was_ipv6_ready != is_ipv6_ready) {
blame_ipv6 = conn_mgr_mon_get_if_by_index(idx);
}
if (was_ipv4_ready != is_ipv4_ready) {
blame_ipv4 = conn_mgr_mon_get_if_by_index(idx);
}

/* Update readiness state flags with the (possibly) new values */
conn_mgr_mon_set_ready(idx, is_l4_ready, is_ipv4_ready, is_ipv6_ready);
}

/* If the total number of ready ifaces changed, possibly send an event */
if (ready_count != original_ready_count) {
if (ready_count != last_ready_count) {
if (ready_count == 0) {
/* We just lost connectivity */
net_mgmt_event_notify(NET_EVENT_L4_DISCONNECTED, last_iface_down);
} else if (original_ready_count == 0) {
net_mgmt_event_notify(NET_EVENT_L4_DISCONNECTED, blame);
} else if (last_ready_count == 0) {
/* We just gained connectivity */
net_mgmt_event_notify(NET_EVENT_L4_CONNECTED, last_iface_up);
net_mgmt_event_notify(NET_EVENT_L4_CONNECTED, blame);
}
last_ready_count = ready_count;
last_blame = blame;
}

/* Same, but specifically for IPv4 */
if (ready_count_ipv4 != last_ready_count_ipv4) {
if (ready_count_ipv4 == 0) {
/* We just lost IPv4 connectivity */
net_mgmt_event_notify(NET_EVENT_L4_IPV4_DISCONNECTED, blame_ipv4);
} else if (last_ready_count_ipv4 == 0) {
/* We just gained IPv4 connectivity */
net_mgmt_event_notify(NET_EVENT_L4_IPV4_CONNECTED, blame_ipv4);
}
last_ready_count_ipv4 = ready_count_ipv4;
last_blame_ipv4 = blame_ipv4;
}

/* Same, but specifically for IPv6 */
if (ready_count_ipv6 != last_ready_count_ipv6) {
if (ready_count_ipv6 == 0) {
/* We just lost IPv6 connectivity */
net_mgmt_event_notify(NET_EVENT_L4_IPV6_DISCONNECTED, blame_ipv6);
} else if (last_ready_count_ipv6 == 0) {
/* We just gained IPv6 connectivity */
net_mgmt_event_notify(NET_EVENT_L4_IPV6_CONNECTED, blame_ipv6);
}
last_ready_count_ipv6 = ready_count_ipv6;
last_blame_ipv6 = blame_ipv6;
}

k_mutex_unlock(&conn_mgr_mon_lock);
Expand Down Expand Up @@ -181,8 +245,6 @@ static void conn_mgr_mon_initial_state(struct net_if *iface)

}

iface_states[idx] |= CONN_MGR_IF_CHANGED;

k_mutex_unlock(&conn_mgr_mon_lock);
}

Expand Down Expand Up @@ -224,10 +286,22 @@ void conn_mgr_mon_resend_status(void)
{
k_mutex_lock(&conn_mgr_mon_lock, K_FOREVER);

if (ready_count == 0) {
net_mgmt_event_notify(NET_EVENT_L4_DISCONNECTED, last_iface_down);
if (last_ready_count == 0) {
net_mgmt_event_notify(NET_EVENT_L4_DISCONNECTED, last_blame);
} else {
net_mgmt_event_notify(NET_EVENT_L4_CONNECTED, last_blame);
}

if (last_ready_count_ipv6 == 0) {
net_mgmt_event_notify(NET_EVENT_L4_IPV6_DISCONNECTED, last_blame_ipv6);
} else {
net_mgmt_event_notify(NET_EVENT_L4_IPV6_CONNECTED, last_blame_ipv6);
}

if (last_ready_count_ipv4 == 0) {
net_mgmt_event_notify(NET_EVENT_L4_IPV4_DISCONNECTED, last_blame_ipv4);
} else {
net_mgmt_event_notify(NET_EVENT_L4_CONNECTED, last_iface_up);
net_mgmt_event_notify(NET_EVENT_L4_IPV4_CONNECTED, last_blame_ipv4);
}

k_mutex_unlock(&conn_mgr_mon_lock);
Expand All @@ -242,7 +316,6 @@ void conn_mgr_ignore_iface(struct net_if *iface)
if (!(iface_states[idx] & CONN_MGR_IF_IGNORED)) {
/* Set ignored flag and mark state as changed */
iface_states[idx] |= CONN_MGR_IF_IGNORED;
iface_states[idx] |= CONN_MGR_IF_CHANGED;
k_sem_give(&conn_mgr_mon_updated);
}

Expand All @@ -258,7 +331,6 @@ void conn_mgr_watch_iface(struct net_if *iface)
if (iface_states[idx] & CONN_MGR_IF_IGNORED) {
/* Clear ignored flag and mark state as changed */
iface_states[idx] &= ~CONN_MGR_IF_IGNORED;
iface_states[idx] |= CONN_MGR_IF_CHANGED;
k_sem_give(&conn_mgr_mon_updated);
}

Expand Down
7 changes: 3 additions & 4 deletions subsys/net/conn_mgr/conn_mgr_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,9 @@
#define CONN_MGR_IF_IGNORED BIT(7)

/* Internal state flags */
#define CONN_MGR_IF_READY BIT(14)

/* Event flags */
#define CONN_MGR_IF_CHANGED BIT(15)
#define CONN_MGR_IF_READY BIT(13)
#define CONN_MGR_IF_READY_IPV4 BIT(14)
#define CONN_MGR_IF_READY_IPV6 BIT(15)

/* NET_MGMT event masks */
#define CONN_MGR_IFACE_EVENTS_MASK (NET_EVENT_IF_DOWN | \
Expand Down
4 changes: 0 additions & 4 deletions subsys/net/conn_mgr/events_handler.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,6 @@ static void conn_mgr_iface_events_handler(struct net_mgmt_event_callback *cb,
default:
goto done;
}

iface_states[idx] |= CONN_MGR_IF_CHANGED;
k_sem_give(&conn_mgr_mon_updated);

done:
Expand Down Expand Up @@ -95,7 +93,6 @@ static void conn_mgr_ipv6_events_handler(struct net_mgmt_event_callback *cb,
goto done;
}

iface_states[idx] |= CONN_MGR_IF_CHANGED;
k_sem_give(&conn_mgr_mon_updated);

done:
Expand Down Expand Up @@ -148,7 +145,6 @@ static void conn_mgr_ipv4_events_handler(struct net_mgmt_event_callback *cb,
goto done;
}

iface_states[idx] |= CONN_MGR_IF_CHANGED;
k_sem_give(&conn_mgr_mon_updated);

done:
Expand Down
12 changes: 12 additions & 0 deletions subsys/net/lib/shell/events.c
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,18 @@ static const char *get_l4_desc(uint32_t event)
case NET_EVENT_L4_DISCONNECTED:
desc = "disconnected";
break;
case NET_EVENT_L4_IPV4_CONNECTED:
desc = "IPv4 connectivity available";
break;
case NET_EVENT_L4_IPV4_DISCONNECTED:
desc = "IPv4 connectivity lost";
break;
case NET_EVENT_L4_IPV6_CONNECTED:
desc = "IPv6 connectivity available";
break;
case NET_EVENT_L4_IPV6_DISCONNECTED:
desc = "IPv6 connectivity lost";
break;
case NET_EVENT_DNS_SERVER_ADD:
desc = "DNS server add";
break;
Expand Down
Loading
Loading