diff --git a/include/zephyr/net/conn_mgr_connectivity.h b/include/zephyr/net/conn_mgr_connectivity.h index 34d8e04e153..ba7041a038b 100644 --- a/include/zephyr/net/conn_mgr_connectivity.h +++ b/include/zephyr/net/conn_mgr_connectivity.h @@ -423,6 +423,57 @@ int conn_mgr_if_set_timeout(struct net_if *iface, int timeout); */ void conn_mgr_conn_init(void); +/** + * @brief Convenience function that takes all available ifaces into the admin-up state. + * + * Essentially a wrapper for net_if_up. + * + * @param skip_ignored - If true, only affect ifaces that aren't ignored by conn_mgr. + * Otherwise, affect all ifaces. + * @return 0 if all net_if_up calls returned 0, otherwise the first nonzero value + * returned by a net_if_up call. + */ +int conn_mgr_all_if_up(bool skip_ignored); + + +/** + * @brief Convenience function that takes all available ifaces into the admin-down state. + * + * Essentially a wrapper for net_if_down. + * + * @param skip_ignored - If true, only affect ifaces that aren't ignored by conn_mgr. + * Otherwise, affect all ifaces. + * @return 0 if all net_if_down calls returned 0, otherwise the first nonzero value + * returned by a net_if_down call. + */ +int conn_mgr_all_if_down(bool skip_ignored); + +/** + * @brief Convenience function that takes all available ifaces into the admin-up state, and + * connects those that support connectivity. + * + * Essentially a wrapper for net_if_up and conn_mgr_if_connect. + * + * @param skip_ignored - If true, only affect ifaces that aren't ignored by conn_mgr. + * Otherwise, affect all ifaces. + * @return 0 if all net_if_up and conn_mgr_if_connect calls returned 0, otherwise the first nonzero + * value returned by either net_if_up or conn_mgr_if_connect. + */ +int conn_mgr_all_if_connect(bool skip_ignored); + +/** + * @brief Convenience function that disconnects all available ifaces that support connectivity + * without putting them into admin-down state (unless auto-down is enabled for the iface). + * + * Essentially a wrapper for net_if_down. + * + * @param skip_ignored - If true, only affect ifaces that aren't ignored by conn_mgr. + * Otherwise, affect all ifaces. + * @return 0 if all net_if_up and conn_mgr_if_connect calls returned 0, otherwise the first nonzero + * value returned by either net_if_up or conn_mgr_if_connect. + */ +int conn_mgr_all_if_disconnect(bool skip_ignored); + /** * @} */ diff --git a/subsys/net/conn_mgr/conn_mgr_connectivity.c b/subsys/net/conn_mgr/conn_mgr_connectivity.c index ebdd41dc5ae..e50ac0614f6 100644 --- a/subsys/net/conn_mgr/conn_mgr_connectivity.c +++ b/subsys/net/conn_mgr/conn_mgr_connectivity.c @@ -8,8 +8,10 @@ LOG_MODULE_REGISTER(conn_mgr_conn, CONFIG_NET_CONNECTION_MANAGER_LOG_LEVEL); #include -#include #include +#include + +#include #include "conn_mgr_private.h" @@ -422,3 +424,144 @@ void conn_mgr_conn_init(void) } } } + +enum conn_mgr_conn_all_if_oper { + ALL_IF_UP, + ALL_IF_DOWN, + ALL_IF_CONNECT, + ALL_IF_DISCONNECT +}; + +struct conn_mgr_conn_all_if_ctx { + bool skip_ignored; + enum conn_mgr_conn_all_if_oper operation; + int status; +}; + +/* Per-iface callback for conn_mgr_conn_all_if_up */ +static void conn_mgr_conn_all_if_cb(struct net_if *iface, void *user_data) +{ + int status = 0; + struct conn_mgr_conn_all_if_ctx *context = (struct conn_mgr_conn_all_if_ctx *)user_data; + + /* Skip ignored ifaces if so desired */ + if (context->skip_ignored && conn_mgr_is_iface_ignored(iface)) { + return; + } + + /* Perform the requested operation */ + switch (context->operation) { + case ALL_IF_UP: + /* Do not take iface admin up if it already is. */ + if (net_if_is_admin_up(iface)) { + return; + } + + status = net_if_up(iface); + break; + case ALL_IF_DOWN: + /* Do not take iface admin down if it already is. */ + if (!net_if_is_admin_up(iface)) { + return; + } + + status = net_if_down(iface); + break; + case ALL_IF_CONNECT: + /* Connect operation only supported if iface is bound */ + if (!conn_mgr_if_is_bound(iface)) { + return; + } + + status = conn_mgr_if_connect(iface); + break; + case ALL_IF_DISCONNECT: + /* Disconnect operation only supported if iface is bound */ + if (!conn_mgr_if_is_bound(iface)) { + return; + } + + status = conn_mgr_if_disconnect(iface); + break; + } + + if (status == 0) { + return; + } + + if (context->status == 0) { + context->status = status; + } + + NET_ERR("%s failed for iface %d (%p). Error: %d", + context->operation == ALL_IF_UP ? "net_if_up" : + context->operation == ALL_IF_DOWN ? "net_if_down" : + context->operation == ALL_IF_CONNECT ? "conn_mgr_if_connect" : + context->operation == ALL_IF_DISCONNECT ? "conn_mgr_if_disconnect" : + "invalid", + net_if_get_by_iface(iface), iface, status + ); +} + +int conn_mgr_all_if_up(bool skip_ignored) +{ + struct conn_mgr_conn_all_if_ctx context = { + .operation = ALL_IF_UP, + .skip_ignored = skip_ignored, + .status = 0 + }; + + net_if_foreach(conn_mgr_conn_all_if_cb, &context); + + return context.status; +} + +int conn_mgr_all_if_down(bool skip_ignored) +{ + struct conn_mgr_conn_all_if_ctx context = { + .operation = ALL_IF_DOWN, + .skip_ignored = skip_ignored, + .status = 0 + }; + + net_if_foreach(conn_mgr_conn_all_if_cb, &context); + + return context.status; +} + +int conn_mgr_all_if_connect(bool skip_ignored) +{ + /* First, take all ifaces up. + * All bound ifaces will do this automatically when connect is called, but non-bound ifaces + * won't, so we must request it explicitly. + */ + struct conn_mgr_conn_all_if_ctx context = { + .operation = ALL_IF_UP, + .skip_ignored = skip_ignored, + .status = 0 + }; + + net_if_foreach(conn_mgr_conn_all_if_cb, &context); + + /* Now connect all ifaces. + * We are delibarately not resetting context.status between these two calls so that + * the first nonzero status code encountered between the two of them is what is returned. + */ + context.operation = ALL_IF_CONNECT; + net_if_foreach(conn_mgr_conn_all_if_cb, &context); + + return context.status; +} + +int conn_mgr_all_if_disconnect(bool skip_ignored) +{ + struct conn_mgr_conn_all_if_ctx context = { + .operation = ALL_IF_DISCONNECT, + .skip_ignored = skip_ignored, + .status = 0 + }; + + net_if_foreach(conn_mgr_conn_all_if_cb, &context); + + return context.status; +} diff --git a/tests/net/conn_mgr_conn/prj.conf b/tests/net/conn_mgr_conn/prj.conf index 5ffa0ba9d9f..ec6b62f6be3 100644 --- a/tests/net/conn_mgr_conn/prj.conf +++ b/tests/net/conn_mgr_conn/prj.conf @@ -24,3 +24,7 @@ CONFIG_ZTEST_NEW_API=y CONFIG_NET_IF_MAX_IPV4_COUNT=6 CONFIG_NET_IF_MAX_IPV6_COUNT=6 CONFIG_TEST_USERSPACE=y + +# Increased net event queue size needed since this test performs simultaneous events on a +# large number of ifaces. +CONFIG_NET_MGMT_EVENT_QUEUE_SIZE=16 diff --git a/tests/net/conn_mgr_conn/src/main.c b/tests/net/conn_mgr_conn/src/main.c index e7154329d5a..9d36688ae87 100644 --- a/tests/net/conn_mgr_conn/src/main.c +++ b/tests/net/conn_mgr_conn/src/main.c @@ -15,6 +15,7 @@ #include #include #include +#include #include "conn_mgr_private.h" #include "test_conn_impl.h" #include "test_ifaces.h" @@ -54,6 +55,9 @@ static void reset_test_iface_state(struct net_if *iface) struct conn_mgr_conn_binding *iface_binding = conn_mgr_if_get_binding(iface); struct test_conn_data *iface_data = conn_mgr_if_get_data(iface); + /* Some tests mark ifaces as ignored, this must be reset between each test. */ + conn_mgr_watch_iface(iface); + if (iface_binding) { /* Reset all flags and settings for the binding */ iface_binding->flags = 0; @@ -1139,5 +1143,440 @@ ZTEST(conn_mgr_conn, test_auto_down_fatal) "Auto-down should not trigger on fatal error if it is disabled."); } +/* Verify that all_if_up brings all ifaces up, but only if they are not ignored or + * skip_ignored is false + */ +ZTEST(conn_mgr_conn, test_all_if_up) +{ + /* Ignore an iface */ + conn_mgr_ignore_iface(ifa1); + + /* Take all ifaces up (do not skip ignored) */ + zassert_equal(conn_mgr_all_if_up(false), 0, "conn_mgr_all_if_up should succeed."); + k_sleep(K_MSEC(1)); + + /* Verify all ifaces are up */ + zassert_true(net_if_is_admin_up(ifa1), "All ifaces should be admin-up."); + zassert_true(net_if_is_admin_up(ifa2), "All ifaces should be admin-up."); + zassert_true(net_if_is_admin_up(ifb), "All ifaces should be admin-up."); + zassert_true(net_if_is_admin_up(ifni), "All ifaces should be admin-up."); + zassert_true(net_if_is_admin_up(ifnull), "All ifaces should be admin-up."); + zassert_true(net_if_is_admin_up(ifnone), "All ifaces should be admin-up."); + + + /* Manually take all ifaces down */ + zassert_equal(net_if_down(ifa1), 0, "net_if_down should succeed for all ifaces."); + zassert_equal(net_if_down(ifa2), 0, "net_if_down should succeed for all ifaces."); + zassert_equal(net_if_down(ifb), 0, "net_if_down should succeed for all ifaces."); + zassert_equal(net_if_down(ifni), 0, "net_if_down should succeed for all ifaces."); + zassert_equal(net_if_down(ifnull), 0, "net_if_down should succeed for all ifaces."); + zassert_equal(net_if_down(ifnone), 0, "net_if_down should succeed for all ifaces."); + k_sleep(K_MSEC(1)); + + /* Take all ifaces up (skip ignored) */ + zassert_equal(conn_mgr_all_if_up(true), 0, "conn_mgr_all_if_up should succeed."); + k_sleep(K_MSEC(1)); + + /* Verify all except ignored are up */ + zassert_true(net_if_is_admin_up(ifa2), "All non-ignored ifaces should be admin-up."); + zassert_true(net_if_is_admin_up(ifb), "All non-ignored ifaces should be admin-up."); + zassert_true(net_if_is_admin_up(ifni), "All non-ignored ifaces should be admin-up."); + zassert_true(net_if_is_admin_up(ifnull), "All non-ignored ifaces should be admin-up."); + zassert_true(net_if_is_admin_up(ifnone), "All non-ignored ifaces should be admin-up."); + + zassert_false(net_if_is_admin_up(ifa1), "Ignored iface should not be admin-up."); +} + +/* Verify that all_if_connect brings all ifaces up, and connects all bound ifaces, but only those + * that are not ignored, or all of them if skip_ignored is false + */ +ZTEST(conn_mgr_conn, test_all_if_connect) +{ + /* Ignore a bound and an unbound iface */ + conn_mgr_ignore_iface(ifa1); + conn_mgr_ignore_iface(ifnone); + + /* Connect all ifaces (do not skip ignored) */ + zassert_equal(conn_mgr_all_if_connect(false), 0, "conn_mgr_all_if_connect should succeed."); + k_sleep(K_MSEC(1)); + + /* Verify all ifaces are up */ + zassert_true(net_if_is_admin_up(ifa1), "All ifaces should be admin-up."); + zassert_true(net_if_is_admin_up(ifa2), "All ifaces should be admin-up."); + zassert_true(net_if_is_admin_up(ifb), "All ifaces should be admin-up."); + zassert_true(net_if_is_admin_up(ifni), "All ifaces should be admin-up."); + zassert_true(net_if_is_admin_up(ifnull), "All ifaces should be admin-up."); + zassert_true(net_if_is_admin_up(ifnone), "All ifaces should be admin-up."); + + /* Verify bound ifaces are connected */ + zassert_true(net_if_is_up(ifa1), "All bound ifaces should be connected."); + zassert_true(net_if_is_up(ifa2), "All bound ifaces should be connected."); + zassert_true(net_if_is_up(ifb), "All bound ifaces should be connected."); + zassert_true(net_if_is_up(ifni), "All bound ifaces should be connected."); + + /* Manually take all ifaces down */ + zassert_equal(conn_mgr_if_disconnect(ifa1), 0, "net_if_disconnect should succeed."); + zassert_equal(conn_mgr_if_disconnect(ifa2), 0, "net_if_disconnect should succeed."); + zassert_equal(conn_mgr_if_disconnect(ifb), 0, "net_if_disconnect should succeed."); + zassert_equal(conn_mgr_if_disconnect(ifni), 0, "net_if_disconnect should succeed."); + + zassert_equal(net_if_down(ifa1), 0, "net_if_down should succeed for all ifaces."); + zassert_equal(net_if_down(ifa2), 0, "net_if_down should succeed for all ifaces."); + zassert_equal(net_if_down(ifb), 0, "net_if_down should succeed for all ifaces."); + zassert_equal(net_if_down(ifni), 0, "net_if_down should succeed for all ifaces."); + zassert_equal(net_if_down(ifnull), 0, "net_if_down should succeed for all ifaces."); + zassert_equal(net_if_down(ifnone), 0, "net_if_down should succeed for all ifaces."); + k_sleep(K_MSEC(1)); + + /* Connect all ifaces (skip ignored) */ + zassert_equal(conn_mgr_all_if_connect(true), 0, "conn_mgr_all_if_connect should succeed."); + k_sleep(K_MSEC(1)); + + /* Verify all except ignored are up */ + zassert_true(net_if_is_admin_up(ifa2), "All non-ignored ifaces should be admin-up."); + zassert_true(net_if_is_admin_up(ifb), "All non-ignored ifaces should be admin-up."); + zassert_true(net_if_is_admin_up(ifni), "All non-ignored ifaces should be admin-up."); + zassert_true(net_if_is_admin_up(ifnull), "All non-ignored ifaces should be admin-up."); + + zassert_false(net_if_is_admin_up(ifa1), "All ignored ifaces should be admin-down."); + zassert_false(net_if_is_admin_up(ifnone), "All ignored ifaces should be admin-down."); + + /* Verify bound ifaces are connected, except for ignored */ + zassert_true(net_if_is_up(ifa2), "All non-ignored bound ifaces should be connected."); + zassert_true(net_if_is_up(ifb), "All non-ignored bound ifaces should be connected."); + zassert_true(net_if_is_up(ifni), "All non-ignored bound ifaces should be connected."); + + zassert_false(net_if_is_up(ifa1), "Ignored iface should not be connected."); +} + +/* Verify that all_if_down takes all ifaces down, but only if they are not ignored, + * or skip_ignored is false + */ +ZTEST(conn_mgr_conn, test_all_if_down) +{ + /* Ignore an iface */ + conn_mgr_ignore_iface(ifa1); + + /* Manually take all ifaces up */ + zassert_equal(net_if_up(ifa1), 0, "net_if_up should succeed for all ifaces."); + zassert_equal(net_if_up(ifa2), 0, "net_if_up should succeed for all ifaces."); + zassert_equal(net_if_up(ifb), 0, "net_if_up should succeed for all ifaces."); + zassert_equal(net_if_up(ifni), 0, "net_if_up should succeed for all ifaces."); + zassert_equal(net_if_up(ifnull), 0, "net_if_up should succeed for all ifaces."); + zassert_equal(net_if_up(ifnone), 0, "net_if_up should succeed for all ifaces."); + k_sleep(K_MSEC(1)); + + /* Take all ifaces down (do not skip ignored) */ + zassert_equal(conn_mgr_all_if_down(false), 0, "conn_mgr_all_if_down should succeed."); + k_sleep(K_MSEC(1)); + + /* Verify all ifaces are down */ + zassert_false(net_if_is_admin_up(ifa1), "All ifaces should be admin-down."); + zassert_false(net_if_is_admin_up(ifa2), "All ifaces should be admin-down."); + zassert_false(net_if_is_admin_up(ifb), "All ifaces should be admin-down."); + zassert_false(net_if_is_admin_up(ifni), "All ifaces should be admin-down."); + zassert_false(net_if_is_admin_up(ifnull), "All ifaces should be admin-down."); + zassert_false(net_if_is_admin_up(ifnone), "All ifaces should be admin-down."); + + /* Manually take all ifaces up */ + zassert_equal(net_if_up(ifa1), 0, "net_if_up should succeed for all ifaces."); + zassert_equal(net_if_up(ifa2), 0, "net_if_up should succeed for all ifaces."); + zassert_equal(net_if_up(ifb), 0, "net_if_up should succeed for all ifaces."); + zassert_equal(net_if_up(ifni), 0, "net_if_up should succeed for all ifaces."); + zassert_equal(net_if_up(ifnull), 0, "net_if_up should succeed for all ifaces."); + zassert_equal(net_if_up(ifnone), 0, "net_if_up should succeed for all ifaces."); + k_sleep(K_MSEC(1)); + + /* Take all ifaces down (skip ignored) */ + zassert_equal(conn_mgr_all_if_down(true), 0, "conn_mgr_all_if_down should succeed."); + k_sleep(K_MSEC(1)); + + /* Verify that all except the ignored iface is down */ + zassert_false(net_if_is_admin_up(ifa2), "All non-ignored ifaces should be admin-down."); + zassert_false(net_if_is_admin_up(ifb), "All non-ignored ifaces should be admin-down."); + zassert_false(net_if_is_admin_up(ifni), "All non-ignored ifaces should be admin-down."); + zassert_false(net_if_is_admin_up(ifnull), "All non-ignored ifaces should be admin-down."); + zassert_false(net_if_is_admin_up(ifnone), "All non-ignored ifaces should be admin-down."); + + zassert_true(net_if_is_admin_up(ifa1), "Ignored iface should be admin-up."); +} + +/* Verify that all_if_disconnect disconnects all bound ifaces, but only if they are not ignored, + * or skip_ignored is false + */ +ZTEST(conn_mgr_conn, test_all_if_disconnect) +{ + /* Ignore a bound iface */ + conn_mgr_ignore_iface(ifa1); + + /* Manually take all ifaces up */ + zassert_equal(net_if_up(ifa1), 0, "net_if_up should succeed for all ifaces."); + zassert_equal(net_if_up(ifa2), 0, "net_if_up should succeed for all ifaces."); + zassert_equal(net_if_up(ifb), 0, "net_if_up should succeed for all ifaces."); + zassert_equal(net_if_up(ifni), 0, "net_if_up should succeed for all ifaces."); + zassert_equal(net_if_up(ifnull), 0, "net_if_up should succeed for all ifaces."); + zassert_equal(net_if_up(ifnone), 0, "net_if_up should succeed for all ifaces."); + k_sleep(K_MSEC(1)); + + /* Manually connect all bound ifaces */ + zassert_equal(conn_mgr_if_connect(ifa1), 0, "conn_mgr_if_connect should succeed."); + zassert_equal(conn_mgr_if_connect(ifa2), 0, "conn_mgr_if_connect should succeed."); + zassert_equal(conn_mgr_if_connect(ifb), 0, "conn_mgr_if_connect should succeed."); + zassert_equal(conn_mgr_if_connect(ifni), 0, "conn_mgr_if_connect should succeed."); + k_sleep(K_MSEC(1)); + + /* Disconnect all ifaces (do not skip ignored) */ + zassert_equal(conn_mgr_all_if_disconnect(false), 0, + "conn_mgr_all_if_disconnect should succeed."); + k_sleep(K_MSEC(1)); + + /* Verify that all bound ifaces are disconnected */ + zassert_false(net_if_is_up(ifa1), "All bound ifaces should be disconnected."); + zassert_false(net_if_is_up(ifa2), "All bound ifaces should be disconnected."); + zassert_false(net_if_is_up(ifb), "All bound ifaces should be disconnected."); + zassert_false(net_if_is_up(ifni), "All bound ifaces should be disconnected."); + + /* Verify that all ifaces are still up, even if disconnected */ + zassert_true(net_if_is_admin_up(ifa1), "All ifaces should be admin-up."); + zassert_true(net_if_is_admin_up(ifa2), "All ifaces should be admin-up."); + zassert_true(net_if_is_admin_up(ifb), "All ifaces should be admin-up."); + zassert_true(net_if_is_admin_up(ifni), "All ifaces should be admin-up."); + zassert_true(net_if_is_admin_up(ifnull), "All ifaces should be admin-up."); + zassert_true(net_if_is_admin_up(ifnone), "All ifaces should be admin-up."); + + /* Manually reconnect bound ifaces */ + zassert_equal(conn_mgr_if_connect(ifa1), 0, "conn_mgr_if_connect should succeed."); + zassert_equal(conn_mgr_if_connect(ifa2), 0, "conn_mgr_if_connect should succeed."); + zassert_equal(conn_mgr_if_connect(ifb), 0, "conn_mgr_if_connect should succeed."); + zassert_equal(conn_mgr_if_connect(ifni), 0, "conn_mgr_if_connect should succeed."); + k_sleep(K_MSEC(1)); + + /* Disconnect all ifaces (skip ignored) */ + zassert_equal(conn_mgr_all_if_disconnect(true), 0, + "conn_mgr_all_if_disconnect should succeed."); + k_sleep(K_MSEC(1)); + + /* Verify that all bound ifaces are disconnected, except the ignored iface */ + zassert_false(net_if_is_up(ifa2), "All non-ignored bound ifaces should be disconnected."); + zassert_false(net_if_is_up(ifb), "All non-ignored bound ifaces should be disconnected."); + zassert_false(net_if_is_up(ifni), "All non-ignored bound ifaces should be disconnected."); + + zassert_true(net_if_is_up(ifa1), "Ignored iface should still be connected"); +} + + +/* Verify that double calls to all_if_up do not raise errors */ +ZTEST(conn_mgr_conn, test_all_if_up_double) +{ + /* Take all ifaces up twice in a row */ + zassert_equal(conn_mgr_all_if_up(false), 0, + "conn_mgr_all_if_up should succeed."); + zassert_equal(conn_mgr_all_if_up(false), 0, + "conn_mgr_all_if_up should succeed twice in a row."); + + /* One more time, after a delay, to be sure */ + k_sleep(K_MSEC(1)); + zassert_equal(conn_mgr_all_if_up(false), 0, + "conn_mgr_all_if_up should succeed twice in a row."); + k_sleep(K_MSEC(1)); + + /* Verify all ifaces are up */ + zassert_true(net_if_is_admin_up(ifa1), "All ifaces should be admin-up."); + zassert_true(net_if_is_admin_up(ifa2), "All ifaces should be admin-up."); + zassert_true(net_if_is_admin_up(ifb), "All ifaces should be admin-up."); + zassert_true(net_if_is_admin_up(ifni), "All ifaces should be admin-up."); + zassert_true(net_if_is_admin_up(ifnull), "All ifaces should be admin-up."); + zassert_true(net_if_is_admin_up(ifnone), "All ifaces should be admin-up."); +} + +/* Verify that double calls to all_if_down do not raise errors */ +ZTEST(conn_mgr_conn, test_all_if_down_double) +{ + /* Manually take all ifaces up */ + zassert_equal(net_if_up(ifa1), 0, "net_if_up should succeed for all ifaces."); + zassert_equal(net_if_up(ifa2), 0, "net_if_up should succeed for all ifaces."); + zassert_equal(net_if_up(ifb), 0, "net_if_up should succeed for all ifaces."); + zassert_equal(net_if_up(ifni), 0, "net_if_up should succeed for all ifaces."); + zassert_equal(net_if_up(ifnull), 0, "net_if_up should succeed for all ifaces."); + zassert_equal(net_if_up(ifnone), 0, "net_if_up should succeed for all ifaces."); + k_sleep(K_MSEC(1)); + + /* Take all ifaces down twice in a row */ + zassert_equal(conn_mgr_all_if_down(false), 0, + "conn_mgr_all_if_down should succeed."); + zassert_equal(conn_mgr_all_if_down(false), 0, + "conn_mgr_all_if_down should succeed twice in a row."); + + /* One more time, after a delay, to be sure */ + k_sleep(K_MSEC(1)); + zassert_equal(conn_mgr_all_if_down(false), 0, + "conn_mgr_all_if_down should succeed twice in a row."); + k_sleep(K_MSEC(1)); + + /* Verify all ifaces are down */ + zassert_false(net_if_is_admin_up(ifa1), "All ifaces should be admin-down."); + zassert_false(net_if_is_admin_up(ifa2), "All ifaces should be admin-down."); + zassert_false(net_if_is_admin_up(ifb), "All ifaces should be admin-down."); + zassert_false(net_if_is_admin_up(ifni), "All ifaces should be admin-down."); + zassert_false(net_if_is_admin_up(ifnull), "All ifaces should be admin-down."); + zassert_false(net_if_is_admin_up(ifnone), "All ifaces should be admin-down."); +} + +/* Verify that double calls to all_if_connect do not raise errors */ +ZTEST(conn_mgr_conn, test_all_if_connect_double) +{ + /* Connect all ifaces twice in a row */ + zassert_equal(conn_mgr_all_if_connect(false), 0, + "conn_mgr_all_if_connect should succeed."); + zassert_equal(conn_mgr_all_if_connect(false), 0, + "conn_mgr_all_if_connect should succeed twice in a row."); + + /* One more time, after a delay, to be sure */ + k_sleep(K_MSEC(1)); + zassert_equal(conn_mgr_all_if_connect(false), 0, + "conn_mgr_all_if_connect should succeed twice in a row."); + k_sleep(K_MSEC(1)); + + /* Verify all ifaces are up */ + zassert_true(net_if_is_admin_up(ifa1), "All ifaces should be admin-up."); + zassert_true(net_if_is_admin_up(ifa2), "All ifaces should be admin-up."); + zassert_true(net_if_is_admin_up(ifb), "All ifaces should be admin-up."); + zassert_true(net_if_is_admin_up(ifni), "All ifaces should be admin-up."); + zassert_true(net_if_is_admin_up(ifnull), "All ifaces should be admin-up."); + zassert_true(net_if_is_admin_up(ifnone), "All ifaces should be admin-up."); + + /* Verify all bound ifaces are connected */ +} + +/* Verify that double calls to all_if_disconnect do not raise errors */ +ZTEST(conn_mgr_conn, test_all_if_disconnect_double) +{ + /* Manually take all ifaces up */ + zassert_equal(net_if_up(ifa1), 0, "net_if_up should succeed for all ifaces."); + zassert_equal(net_if_up(ifa2), 0, "net_if_up should succeed for all ifaces."); + zassert_equal(net_if_up(ifb), 0, "net_if_up should succeed for all ifaces."); + zassert_equal(net_if_up(ifni), 0, "net_if_up should succeed for all ifaces."); + zassert_equal(net_if_up(ifnull), 0, "net_if_up should succeed for all ifaces."); + zassert_equal(net_if_up(ifnone), 0, "net_if_up should succeed for all ifaces."); + k_sleep(K_MSEC(1)); + + /* Manually connect all bound ifaces */ + zassert_equal(conn_mgr_if_connect(ifa1), 0, "conn_mgr_if_connect should succeed."); + zassert_equal(conn_mgr_if_connect(ifa2), 0, "conn_mgr_if_connect should succeed."); + zassert_equal(conn_mgr_if_connect(ifb), 0, "conn_mgr_if_connect should succeed."); + zassert_equal(conn_mgr_if_connect(ifni), 0, "conn_mgr_if_connect should succeed."); + k_sleep(K_MSEC(1)); + + /* Connect all ifaces twice in a row */ + zassert_equal(conn_mgr_all_if_disconnect(false), 0, + "conn_mgr_all_if_disconnect should succeed."); + zassert_equal(conn_mgr_all_if_disconnect(false), 0, + "conn_mgr_all_if_disconnect should succeed twice in a row."); + + /* One more time, after a delay, to be sure */ + k_sleep(K_MSEC(1)); + zassert_equal(conn_mgr_all_if_disconnect(false), 0, + "conn_mgr_all_if_disconnect should succeed twice in a row."); + k_sleep(K_MSEC(1)); + + /* Verify all bound ifaces are disconnected */ + zassert_false(net_if_is_up(ifa1), "All bound ifaces should be disconnected."); + zassert_false(net_if_is_up(ifa2), "All bound ifaces should be disconnected."); + zassert_false(net_if_is_up(ifb), "All bound ifaces should be disconnected."); + zassert_false(net_if_is_up(ifni), "All bound ifaces should be disconnected."); + + /* Verify all ifaces are up */ + zassert_true(net_if_is_admin_up(ifa1), "All ifaces should be admin-up."); + zassert_true(net_if_is_admin_up(ifa2), "All ifaces should be admin-up."); + zassert_true(net_if_is_admin_up(ifb), "All ifaces should be admin-up."); + zassert_true(net_if_is_admin_up(ifni), "All ifaces should be admin-up."); + zassert_true(net_if_is_admin_up(ifnull), "All ifaces should be admin-up."); + zassert_true(net_if_is_admin_up(ifnone), "All ifaces should be admin-up."); +} + + + +/* Testing error passing for all_if_up/all_if_down is not possible without using an L2 other than + * Dummy, since the dummy L2 is not capable of erroring in response to either of these. + * + * However, since all bulk convenience functions share a single implementation, testing + * connect and disconnect is sufficient to gain acceptable coverage of this behavior for all of + * them. + */ + +/* Verify that all_if_connect successfully forwards errors encountered on individual ifaces */ +ZTEST(conn_mgr_conn, test_all_if_connect_err) +{ + struct test_conn_data *ifa1_data = conn_mgr_if_get_data(ifa1); + + /* Schedule a connect error on one of the ifaces */ + ifa1_data->api_err = -ECHILD; + + /* Verify that this error is passed to all_if_connect */ + zassert_equal(conn_mgr_all_if_connect(false), -ECHILD, + "conn_mgr_all_if_connect should fail with the requested error."); + k_sleep(K_MSEC(1)); + + /* Verify that all ifaces went admin-up */ + zassert_true(net_if_is_admin_up(ifa1), "All ifaces should be admin-up."); + zassert_true(net_if_is_admin_up(ifa2), "All ifaces should be admin-up."); + zassert_true(net_if_is_admin_up(ifb), "All ifaces should be admin-up."); + zassert_true(net_if_is_admin_up(ifni), "All ifaces should be admin-up."); + zassert_true(net_if_is_admin_up(ifnull), "All ifaces should be admin-up."); + zassert_true(net_if_is_admin_up(ifnone), "All ifaces should be admin-up."); + + /* Verify that all the non-error ifaces are connected */ + zassert_true(net_if_is_up(ifa2), "All non-failing ifaces should be connected."); + zassert_true(net_if_is_up(ifb), "All non-failing ifaces should be connected."); + zassert_true(net_if_is_up(ifni), "All non-failing ifaces should be connected."); + + /* Verify that the error iface is not connected */ + zassert_false(net_if_is_up(ifa1), "The failing iface should not be connected."); +} + +/* Verify that all_if_disconnect successfully forwards errors encountered on individual ifaces */ +ZTEST(conn_mgr_conn, test_all_if_disconnect_err) +{ + struct test_conn_data *ifa1_data = conn_mgr_if_get_data(ifa1); + + /* Manually take all ifaces up */ + zassert_equal(net_if_up(ifa1), 0, "net_if_up should succeed for all ifaces."); + zassert_equal(net_if_up(ifa2), 0, "net_if_up should succeed for all ifaces."); + zassert_equal(net_if_up(ifb), 0, "net_if_up should succeed for all ifaces."); + zassert_equal(net_if_up(ifni), 0, "net_if_up should succeed for all ifaces."); + zassert_equal(net_if_up(ifnull), 0, "net_if_up should succeed for all ifaces."); + zassert_equal(net_if_up(ifnone), 0, "net_if_up should succeed for all ifaces."); + k_sleep(K_MSEC(1)); + + /* Manually connect all bound ifaces */ + zassert_equal(conn_mgr_if_connect(ifa1), 0, "conn_mgr_if_connect should succeed."); + zassert_equal(conn_mgr_if_connect(ifa2), 0, "conn_mgr_if_connect should succeed."); + zassert_equal(conn_mgr_if_connect(ifb), 0, "conn_mgr_if_connect should succeed."); + zassert_equal(conn_mgr_if_connect(ifni), 0, "conn_mgr_if_connect should succeed."); + k_sleep(K_MSEC(1)); + + /* Schedule a disconnect error on one of the ifaces */ + ifa1_data->api_err = -ECHILD; + + /* Verify that this error is passed to all_if_disconnect */ + zassert_equal(conn_mgr_all_if_disconnect(false), -ECHILD, + "conn_mgr_all_if_disconnect should fail with the requested error."); + + /* Verify that all ifaces are still admin-up */ + zassert_true(net_if_is_admin_up(ifa1), "All ifaces should be admin-up."); + zassert_true(net_if_is_admin_up(ifa2), "All ifaces should be admin-up."); + zassert_true(net_if_is_admin_up(ifb), "All ifaces should be admin-up."); + zassert_true(net_if_is_admin_up(ifni), "All ifaces should be admin-up."); + zassert_true(net_if_is_admin_up(ifnull), "All ifaces should be admin-up."); + zassert_true(net_if_is_admin_up(ifnone), "All ifaces should be admin-up."); + + /* Verify that all the non-error ifaces are disconnected */ + zassert_false(net_if_is_up(ifa2), "All non-failing ifaces should be disconnected."); + zassert_false(net_if_is_up(ifb), "All non-failing ifaces should be disconnected."); + zassert_false(net_if_is_up(ifni), "All non-failing ifaces should be disconnected."); + + /* Verify that the error iface is not connected */ + zassert_true(net_if_is_up(ifa1), "The failing iface should not be disconnected."); +} ZTEST_SUITE(conn_mgr_conn, NULL, conn_mgr_conn_setup, conn_mgr_conn_before, NULL, NULL);