net: if: Introduce carrier and dormant management on a network iface

Introduce a new interface state management scheme, according to
RFC 2863. This includes the following changes:

* Introduce a new interface flag: NET_IF_LOWER_UP, along with
  corresponding helper functions. The flag should be set/cleared on an
  interface by a network driver/L2 to signalize physical readiness of an
  interface to transmit data (for example cable plugged in).

* Introduce a new interface flag: NET_IF_DORMANT, along with
  corresponding helper functions. The flag should be set on an
  iterface when the interface is not ready to transmit application data,
  for example still not joined a Wi-Fi network.

* Introduce a new interface flag: NET_IF_RUNNING, indicating that
  interface is ready to transmit application data.

* Update the meaning of the NET_IF_UP flag - it now singnalizes whether
  an interface has been brought up/down by the application (admin
  up/down).

* Introduce operational state of an interface, derived from above. It
  reflects the internal interface state.

The meaning of net_if_is_up() function and NET_EVENT_IF_UP/DOWN events
remains unchanged to retain backward compability - they reflect the
interface readiness to transmit application data.

To verify the administrative up/down state, a new function
net_if_is_admin_up() has been introduced, along with
NET_EVENT_IF_ADMIN_UP/DOWN events.

Signed-off-by: Robert Lubos <robert.lubos@nordicsemi.no>
This commit is contained in:
Robert Lubos 2022-09-27 16:19:44 +02:00 committed by Carles Cufí
parent 04fce39e03
commit efd03a86d7
3 changed files with 331 additions and 22 deletions

View file

@ -36,6 +36,9 @@ extern "C" {
enum net_event_if_cmd {
NET_EVENT_IF_CMD_DOWN = 1,
NET_EVENT_IF_CMD_UP,
NET_EVENT_IF_CMD_ADMIN_DOWN,
NET_EVENT_IF_CMD_ADMIN_UP,
};
#define NET_EVENT_IF_DOWN \
@ -44,6 +47,13 @@ enum net_event_if_cmd {
#define NET_EVENT_IF_UP \
(_NET_EVENT_IF_BASE | NET_EVENT_IF_CMD_UP)
#define NET_EVENT_IF_ADMIN_DOWN \
(_NET_EVENT_IF_BASE | NET_EVENT_IF_CMD_ADMIN_DOWN)
#define NET_EVENT_IF_ADMIN_UP \
(_NET_EVENT_IF_BASE | NET_EVENT_IF_CMD_ADMIN_UP)
/* IPv6 Events */
#define _NET_IPV6_LAYER NET_MGMT_LAYER_L3
#define _NET_IPV6_CORE_CODE 0x060

View file

@ -160,8 +160,9 @@ struct net_if_router {
uint8_t _unused : 5;
};
/** Network interface flags. */
enum net_if_flag {
/** Interface is up/ready to receive and transmit */
/** Interface is admin up. */
NET_IF_UP,
/** Interface is pointopoint */
@ -193,12 +194,32 @@ enum net_if_flag {
/** Interface supports IPv6 */
NET_IF_IPV6,
/** Interface up and running (ready to receive and transmit). */
NET_IF_RUNNING,
/** Driver signals L1 is up. */
NET_IF_LOWER_UP,
/** Driver signals dormant. */
NET_IF_DORMANT,
/** @cond INTERNAL_HIDDEN */
/* Total number of flags - must be at the end of the enum */
NET_IF_NUM_FLAGS
/** @endcond */
};
/** Network interface operational status (RFC 2863). */
enum net_if_oper_state {
NET_IF_OPER_UNKNOWN,
NET_IF_OPER_NOTPRESENT,
NET_IF_OPER_DOWN,
NET_IF_OPER_LOWERLAYERDOWN,
NET_IF_OPER_TESTING,
NET_IF_OPER_DORMANT,
NET_IF_OPER_UP,
} __packed;
#if defined(CONFIG_NET_OFFLOAD)
struct net_offload;
#endif /* CONFIG_NET_OFFLOAD */
@ -468,6 +489,9 @@ struct net_if_dev {
*/
net_socket_create_t socket_offload;
#endif /* CONFIG_NET_SOCKETS_OFFLOAD */
/** RFC 2863 operational status */
enum net_if_oper_state oper_state;
};
/**
@ -576,6 +600,40 @@ static inline bool net_if_flag_is_set(struct net_if *iface,
return atomic_test_bit(iface->if_dev->flags, value);
}
/**
* @brief Set an operational state on an interface
*
* @param iface Pointer to network interface
* @param oper_state Operational state to set
*
* @return The new operational state of an interface
*/
static inline enum net_if_oper_state net_if_oper_state_set(
struct net_if *iface, enum net_if_oper_state oper_state)
{
NET_ASSERT(iface);
if (oper_state >= NET_IF_OPER_UNKNOWN && oper_state <= NET_IF_OPER_UP) {
iface->if_dev->oper_state = oper_state;
}
return iface->if_dev->oper_state;
}
/**
* @brief Get an operational state of an interface
*
* @param iface Pointer to network interface
*
* @return Operational state of an interface
*/
static inline enum net_if_oper_state net_if_oper_state(struct net_if *iface)
{
NET_ASSERT(iface);
return iface->if_dev->oper_state;
}
/**
* @brief Send a packet through a net iface
*
@ -2091,7 +2149,7 @@ void net_if_foreach(net_if_cb_t cb, void *user_data);
int net_if_up(struct net_if *iface);
/**
* @brief Check if interface is up.
* @brief Check if interface is is up and running.
*
* @param iface Pointer to network interface
*
@ -2101,7 +2159,8 @@ static inline bool net_if_is_up(struct net_if *iface)
{
NET_ASSERT(iface);
return net_if_flag_is_set(iface, NET_IF_UP);
return net_if_flag_is_set(iface, NET_IF_UP) &&
net_if_flag_is_set(iface, NET_IF_RUNNING);
}
/**
@ -2113,6 +2172,90 @@ static inline bool net_if_is_up(struct net_if *iface)
*/
int net_if_down(struct net_if *iface);
/**
* @brief Check if interface was brought up by the administrator.
*
* @param iface Pointer to network interface
*
* @return True if interface is admin up, false otherwise.
*/
static inline bool net_if_is_admin_up(struct net_if *iface)
{
NET_ASSERT(iface);
return net_if_flag_is_set(iface, NET_IF_UP);
}
/**
* @brief Underlying network device has detected the carrier (cable connected).
*
* @details The function should be used by the respective network device driver
* or L2 implementation to update its state on a network interface.
*
* @param iface Pointer to network interface
*/
void net_if_carrier_on(struct net_if *iface);
/**
* @brief Underlying network device has lost the carrier (cable disconnected).
*
* @details The function should be used by the respective network device driver
* or L2 implementation to update its state on a network interface.
*
* @param iface Pointer to network interface
*/
void net_if_carrier_off(struct net_if *iface);
/**
* @brief Check if carrier is present on network device.
*
* @param iface Pointer to network interface
*
* @return True if carrier is present, false otherwise.
*/
static inline bool net_if_is_carrier_ok(struct net_if *iface)
{
NET_ASSERT(iface);
return net_if_flag_is_set(iface, NET_IF_LOWER_UP);
}
/**
* @brief Mark interface as dormant. Dormant state indicates that the interface
* is not ready to pass packets yet, but is waiting for some event
* (for example Wi-Fi network association).
*
* @details The function should be used by the respective network device driver
* or L2 implementation to update its state on a network interface.
*
* @param iface Pointer to network interface
*/
void net_if_dormant_on(struct net_if *iface);
/**
* @brief Mark interface as not dormant.
*
* @details The function should be used by the respective network device driver
* or L2 implementation to update its state on a network interface.
*
* @param iface Pointer to network interface
*/
void net_if_dormant_off(struct net_if *iface);
/**
* @brief Check if the interface is dormant.
*
* @param iface Pointer to network interface
*
* @return True if interface is dormant, false otherwise.
*/
static inline bool net_if_is_dormant(struct net_if *iface)
{
NET_ASSERT(iface);
return net_if_flag_is_set(iface, NET_IF_DORMANT);
}
#if defined(CONFIG_NET_PKT_TIMESTAMP) && defined(CONFIG_NET_NATIVE)
/**
* @typedef net_if_timestamp_callback_t

View file

@ -422,6 +422,9 @@ static inline void init_iface(struct net_if *iface)
#if defined(CONFIG_NET_NATIVE_IPV6)
net_if_flag_set(iface, NET_IF_IPV6);
#endif
net_if_flag_test_and_set(iface, NET_IF_LOWER_UP);
net_virtual_init(iface);
NET_DBG("On iface %p", iface);
@ -4023,6 +4026,115 @@ static inline bool is_iface_offloaded(struct net_if *iface)
net_if_is_socket_offloaded(iface));
}
static void notify_iface_up(struct net_if *iface)
{
net_if_flag_set(iface, NET_IF_RUNNING);
net_mgmt_event_notify(NET_EVENT_IF_UP, iface);
/* If the interface is only having point-to-point traffic then we do
* not need to run DAD etc for it.
*/
if (!is_iface_offloaded(iface) &&
!(l2_flags_get(iface) & NET_L2_POINT_TO_POINT)) {
iface_ipv6_start(iface);
net_ipv4_autoconf_start(iface);
}
}
static void notify_iface_down(struct net_if *iface)
{
net_if_flag_clear(iface, NET_IF_RUNNING);
net_mgmt_event_notify(NET_EVENT_IF_DOWN, iface);
if (!is_iface_offloaded(iface) &&
!(l2_flags_get(iface) & NET_L2_POINT_TO_POINT)) {
net_ipv4_autoconf_reset(iface);
}
}
static inline const char *net_if_oper_state2str(enum net_if_oper_state state)
{
#if CONFIG_NET_IF_LOG_LEVEL >= LOG_LEVEL_DBG
switch (state) {
case NET_IF_OPER_UNKNOWN:
return "UNKNOWN";
case NET_IF_OPER_NOTPRESENT:
return "NOTPRESENT";
case NET_IF_OPER_DOWN:
return "DOWN";
case NET_IF_OPER_LOWERLAYERDOWN:
return "LOWERLAYERDOWN";
case NET_IF_OPER_TESTING:
return "TESTING";
case NET_IF_OPER_DORMANT:
return "DORMANT";
case NET_IF_OPER_UP:
return "UP";
default:
break;
}
return "<invalid>";
#else
ARG_UNUSED(state);
return "";
#endif /* CONFIG_NET_IF_LOG_LEVEL >= LOG_LEVEL_DBG */
}
static void update_operational_state(struct net_if *iface)
{
enum net_if_oper_state prev_state = iface->if_dev->oper_state;
enum net_if_oper_state new_state = NET_IF_OPER_UNKNOWN;
if (!net_if_is_admin_up(iface)) {
new_state = NET_IF_OPER_DOWN;
goto exit;
}
if (!net_if_is_carrier_ok(iface)) {
#if defined(CONFIG_NET_L2_VIRTUAL)
if (net_if_l2(iface) == &NET_L2_GET_NAME(VIRTUAL)) {
new_state = NET_IF_OPER_LOWERLAYERDOWN;
} else
#endif /* CONFIG_NET_L2_VIRTUAL */
{
new_state = NET_IF_OPER_DOWN;
}
goto exit;
}
if (net_if_is_dormant(iface)) {
new_state = NET_IF_OPER_DORMANT;
goto exit;
}
new_state = NET_IF_OPER_UP;
exit:
if (net_if_oper_state_set(iface, new_state) != new_state) {
NET_ERR("Failed to update oper state to %d", new_state);
return;
}
NET_DBG("iface %p, oper state %s admin %s carrier %s dormant %s",
iface, net_if_oper_state2str(net_if_oper_state(iface)),
net_if_is_admin_up(iface) ? "UP" : "DOWN",
net_if_is_carrier_ok(iface) ? "ON" : "OFF",
net_if_is_dormant(iface) ? "ON" : "OFF");
if (net_if_oper_state(iface) == NET_IF_OPER_UP) {
if (prev_state != NET_IF_OPER_UP) {
notify_iface_up(iface);
}
} else {
if (prev_state == NET_IF_OPER_UP) {
notify_iface_down(iface);
}
}
}
int net_if_up(struct net_if *iface)
{
int status = 0;
@ -4037,8 +4149,7 @@ int net_if_up(struct net_if *iface)
}
if (is_iface_offloaded(iface)) {
net_if_flag_set(iface, NET_IF_UP);
goto notify;
goto done;
}
/* If the L2 does not support enable just set the flag */
@ -4052,7 +4163,6 @@ int net_if_up(struct net_if *iface)
goto out;
}
done:
/* In many places it's assumed that link address was set with
* net_if_set_link_addr(). Better check that now.
*/
@ -4066,19 +4176,10 @@ done:
NET_ASSERT(net_if_get_link_addr(iface)->addr != NULL);
}
done:
net_if_flag_set(iface, NET_IF_UP);
/* If the interface is only having point-to-point traffic then we do
* not need to run DAD etc for it.
*/
if (!(l2_flags_get(iface) & NET_L2_POINT_TO_POINT)) {
iface_ipv6_start(iface);
net_ipv4_autoconf_start(iface);
}
notify:
net_mgmt_event_notify(NET_EVENT_IF_UP, iface);
net_mgmt_event_notify(NET_EVENT_IF_ADMIN_UP, iface);
update_operational_state(iface);
out:
k_mutex_unlock(&lock);
@ -4094,8 +4195,6 @@ void net_if_carrier_down(struct net_if *iface)
net_if_flag_clear(iface, NET_IF_UP);
net_ipv4_autoconf_reset(iface);
net_mgmt_event_notify(NET_EVENT_IF_DOWN, iface);
k_mutex_unlock(&lock);
@ -4109,6 +4208,11 @@ int net_if_down(struct net_if *iface)
k_mutex_lock(&lock, K_FOREVER);
if (!net_if_flag_is_set(iface, NET_IF_UP)) {
status = -EALREADY;
goto out;
}
leave_mcast_all(iface);
leave_ipv4_mcast_all(iface);
@ -4131,8 +4235,8 @@ int net_if_down(struct net_if *iface)
done:
net_if_flag_clear(iface, NET_IF_UP);
net_mgmt_event_notify(NET_EVENT_IF_DOWN, iface);
net_mgmt_event_notify(NET_EVENT_IF_ADMIN_DOWN, iface);
update_operational_state(iface);
out:
k_mutex_unlock(&lock);
@ -4140,6 +4244,58 @@ out:
return status;
}
void net_if_carrier_on(struct net_if *iface)
{
NET_ASSERT(iface);
k_mutex_lock(&lock, K_FOREVER);
if (!net_if_flag_test_and_set(iface, NET_IF_LOWER_UP)) {
update_operational_state(iface);
}
k_mutex_unlock(&lock);
}
void net_if_carrier_off(struct net_if *iface)
{
NET_ASSERT(iface);
k_mutex_lock(&lock, K_FOREVER);
if (net_if_flag_test_and_clear(iface, NET_IF_LOWER_UP)) {
update_operational_state(iface);
}
k_mutex_unlock(&lock);
}
void net_if_dormant_on(struct net_if *iface)
{
NET_ASSERT(iface);
k_mutex_lock(&lock, K_FOREVER);
if (!net_if_flag_test_and_set(iface, NET_IF_DORMANT)) {
update_operational_state(iface);
}
k_mutex_unlock(&lock);
}
void net_if_dormant_off(struct net_if *iface)
{
NET_ASSERT(iface);
k_mutex_lock(&lock, K_FOREVER);
if (net_if_flag_test_and_clear(iface, NET_IF_DORMANT)) {
update_operational_state(iface);
}
k_mutex_unlock(&lock);
}
static int promisc_mode_set(struct net_if *iface, bool enable)
{
enum net_l2_flags l2_flags = 0;