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:
parent
04fce39e03
commit
efd03a86d7
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue