blob: dfe8f8fbb14ee6ec3247569992c59ac0c0e2e3f2 [file] [log] [blame]
/*
* Copyright (c) 2022 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/types.h>
#include <stdbool.h>
#include <stddef.h>
#include <string.h>
#include <errno.h>
#include <zephyr/sys/printk.h>
#include <zephyr/linker/sections.h>
#include <zephyr/ztest.h>
#include <zephyr/net/offloaded_netdev.h>
#include <zephyr/net/net_offload.h>
#include <zephyr/net/net_if.h>
#include <zephyr/net/net_l2.h>
/* Dummy socket creator for socket-offloaded ifaces */
int offload_socket(int family, int type, int proto)
{
return -1;
}
/* Dummy offload API for net-offloaded ifaces */
struct net_offload net_offload_api;
/* Dummy init function for socket-offloaded ifaces */
static void sock_offload_l2_iface_init(struct net_if *iface)
{
/* This must be called, and the passed-in socket creator cannot be NULL,
* or the iface will not be recognized as offloaded
*/
net_if_socket_offload_set(iface, offload_socket);
net_if_flag_set(iface, NET_IF_NO_AUTO_START);
}
/* Dummy init function for net-offloaded ifaces */
static void net_offload_l2_iface_init(struct net_if *iface)
{
/* Reviewers: Is there a better way to do this?
* I couldn't find any actual examples in the source
*/
iface->if_dev->offload = &net_offload_api;
net_if_flag_set(iface, NET_IF_NO_AUTO_START);
}
/* Tracks the total number of ifaces that are up (theoretically). */
atomic_t up_count = ATOMIC_INIT(0);
/* Tracks the total number of times that the offload_impl_enable callback was called. */
atomic_t call_count = ATOMIC_INIT(0);
/* Expected return value from offload_impl_enable */
atomic_t retval = ATOMIC_INIT(0);
/* Functionality under test */
static int offload_impl_enable(const struct net_if *iface, bool enabled)
{
atomic_inc(&call_count);
if (enabled) {
atomic_inc(&up_count);
} else {
atomic_dec(&up_count);
}
return atomic_get(&retval);
}
/* Net-dev APIs for L2s with offloaded sockets, with and without .enable */
static struct offloaded_if_api sock_offloaded_impl_api = {
.iface_api.init = sock_offload_l2_iface_init,
.enable = offload_impl_enable
};
static struct offloaded_if_api sock_offloaded_no_impl_api = {
.iface_api.init = sock_offload_l2_iface_init
};
/* Net-dev APIs for L2s that are net-offloaded, with and without .enable */
static struct offloaded_if_api net_offloaded_impl_api = {
.iface_api.init = net_offload_l2_iface_init,
.enable = offload_impl_enable
};
static struct offloaded_if_api net_offloaded_no_impl_api = {
.iface_api.init = net_offload_l2_iface_init
};
/* Socket-offloaded netdevs, with and without .enable */
NET_DEVICE_OFFLOAD_INIT(sock_offload_test_impl, "sock_offload_test_impl",
NULL, NULL, NULL, NULL,
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,
&sock_offloaded_impl_api, 0);
NET_DEVICE_OFFLOAD_INIT(sock_offload_test_no_impl, "sock_offload_test_no_impl",
NULL, NULL, NULL, NULL,
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,
&sock_offloaded_no_impl_api, 0);
/* Net-offloaded netdevs, with and without .enable */
NET_DEVICE_OFFLOAD_INIT(net_offload_test_impl, "net_offload_test_impl",
NULL, NULL, NULL, NULL,
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,
&net_offloaded_impl_api, 0);
NET_DEVICE_OFFLOAD_INIT(net_offload_test_no_impl, "net_offload_test_no_impl",
NULL, NULL, NULL, NULL,
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,
&net_offloaded_no_impl_api, 0);
static void net_offloaded_netdev_before(void *fixture)
{
ARG_UNUSED(fixture);
/* Default to successful return value */
atomic_set(&retval, 0);
/* Reset all ifaces */
net_if_down(NET_IF_GET(sock_offload_test_impl, 0));
net_if_down(NET_IF_GET(sock_offload_test_no_impl, 0));
net_if_down(NET_IF_GET(net_offload_test_impl, 0));
net_if_down(NET_IF_GET(net_offload_test_impl, 0));
/* Reset counters */
atomic_set(&call_count, 0);
atomic_set(&up_count, 0);
}
ZTEST(net_offloaded_netdev, test_up_down_sock_off_impl)
{
struct net_if *test_iface = NET_IF_GET(sock_offload_test_impl, 0);
/* Verify iface under test is down before test */
zassert_false(net_if_is_admin_up(test_iface),
"Iface under test must be admin-down before test");
/* Bring iface up. */
(void)net_if_up(test_iface);
/* Verify that a single iface went up once (according to the enable callback) */
zassert_equal(atomic_get(&call_count), 1,
"Bad transition-count, offload_impl_enable not called correctly");
zassert_equal(atomic_get(&up_count), 1,
"Bad up-count, offload_impl_enable not called correctly");
zassert_true(net_if_is_admin_up(test_iface),
"Iface under test should be up after net_if_up");
/* Bring iface down */
(void)net_if_down(test_iface);
/* Verify that a single iface went down once (according to the enable callback)*/
zassert_equal(atomic_get(&call_count), 2,
"Bad transition-count, offload_impl_enable not called correctly");
zassert_equal(atomic_get(&up_count), 0,
"Bad up-count, offload_impl_enable not called correctly");
zassert_false(net_if_is_admin_up(test_iface),
"Iface under test should be down after net_if_down");
}
ZTEST(net_offloaded_netdev, test_up_down_sock_off_no_impl)
{
struct net_if *test_iface = NET_IF_GET(sock_offload_test_no_impl, 0);
/* Verify iface under test is down before test */
zassert_false(net_if_is_admin_up(test_iface),
"Iface under test must be admin-down before test");
/* Bring iface up */
(void)net_if_up(test_iface);
/* Verify that the iface went up, but callbacks were not fired*/
zassert_equal(atomic_get(&call_count), 0,
"offload_impl_enable was called unexpectedly");
zassert_equal(atomic_get(&up_count), 0,
"offload_impl_enable was called unexpectedly");
zassert_true(net_if_is_admin_up(test_iface),
"Iface under test should be up after net_if_up");
/* Bring iface down */
(void)net_if_down(test_iface);
/* Verify that the iface went down, but callbacks were not fired*/
zassert_equal(atomic_get(&call_count), 0,
"offload_impl_enable was called unexpectedly");
zassert_equal(atomic_get(&up_count), 0,
"offload_impl_enable was called unexpectedly");
zassert_false(net_if_is_admin_up(test_iface),
"Iface under test should be down after net_if_down");
}
ZTEST(net_offloaded_netdev, test_up_down_net_off_impl)
{
struct net_if *test_iface = NET_IF_GET(net_offload_test_impl, 0);
/* Verify iface under test is down before test */
zassert_false(net_if_is_admin_up(test_iface),
"Iface under test must be admin-down before test");
/* Bring iface up. */
(void)net_if_up(test_iface);
/* Verify that a single iface went up once (according to the enable callback) */
zassert_equal(atomic_get(&call_count), 1,
"Bad transition-count, offload_impl_enable not called correctly");
zassert_equal(atomic_get(&up_count), 1,
"Bad up-count, offload_impl_enable not called correctly");
zassert_true(net_if_is_admin_up(test_iface),
"Iface under test should be up after net_if_up");
/* Bring iface down */
(void)net_if_down(test_iface);
/* Verify that a single iface went down once (according to the enable callback)*/
zassert_equal(atomic_get(&call_count), 2,
"Bad transition-count, offload_impl_enable not called correctly");
zassert_equal(atomic_get(&up_count), 0,
"Bad up-count, offload_impl_enable not called correctly");
zassert_false(net_if_is_admin_up(test_iface),
"Iface under test should be down after net_if_down");
}
ZTEST(net_offloaded_netdev, test_up_down_net_off_no_impl)
{
struct net_if *test_iface = NET_IF_GET(net_offload_test_no_impl, 0);
/* Verify iface under test is down before test */
zassert_false(net_if_is_admin_up(test_iface),
"Iface under test must be admin-down before test");
/* Bring iface up */
(void)net_if_up(test_iface);
/* Verify that the iface went up, but callbacks were not fired*/
zassert_equal(atomic_get(&call_count), 0,
"offload_impl_enable was called unexpectedly");
zassert_equal(atomic_get(&up_count), 0,
"offload_impl_enable was called unexpectedly");
zassert_true(net_if_is_admin_up(test_iface),
"Iface under test should be up after net_if_up");
/* Bring iface down */
(void)net_if_down(test_iface);
/* Verify that the iface went down, but callbacks were not fired*/
zassert_equal(atomic_get(&call_count), 0,
"offload_impl_enable was called unexpectedly");
zassert_equal(atomic_get(&up_count), 0,
"offload_impl_enable was called unexpectedly");
zassert_false(net_if_is_admin_up(test_iface),
"Iface under test should be down after net_if_down");
}
ZTEST(net_offloaded_netdev, test_up_down_sock_off_impl_double)
{
struct net_if *test_iface = NET_IF_GET(sock_offload_test_impl, 0);
/* Verify iface under test is down before test */
zassert_false(net_if_is_admin_up(test_iface),
"Iface under test must be admin-down before test");
/* Bring iface up twice */
(void)net_if_up(test_iface);
(void)net_if_up(test_iface);
/* Verify that a single iface went up once (according to the enable callback)*/
zassert_equal(atomic_get(&call_count), 1,
"Bad transition-count, offload_impl_enable not called correctly");
zassert_equal(atomic_get(&up_count), 1,
"Bad up-count, offload_impl_enable not called correctly");
zassert_true(net_if_is_admin_up(test_iface),
"Iface under test should be up after net_if_up");
/* Verify that a single iface went down once (according to the enable callback)*/
(void)net_if_down(test_iface);
(void)net_if_down(test_iface);
/* Verify appropriate calls were made */
zassert_equal(atomic_get(&call_count), 2,
"Bad transition-count, offload_impl_enable not called correctly");
zassert_equal(atomic_get(&up_count), 0,
"Bad up-count, offload_impl_enable not called correctly");
zassert_false(net_if_is_admin_up(test_iface),
"Iface under test should be down after net_if_down");
}
ZTEST(net_offloaded_netdev, test_up_down_sock_off_impl_fail_up)
{
struct net_if *test_iface = NET_IF_GET(sock_offload_test_impl, 0);
/* Verify iface under test is down before test */
zassert_false(net_if_is_admin_up(test_iface),
"Iface under test must be admin-down before test");
/* Instruct the enable callback to fail */
atomic_set(&retval, -E2BIG);
/* Expect net_if_up to fail accordingly */
zassert_equal(net_if_up(test_iface), -E2BIG,
"net_if_up should forward error returned from offload_impl_enabled");
/* Verify that the iface failed to go up */
zassert_false(net_if_is_admin_up(test_iface),
"Iface under test should have failed to go up");
}
ZTEST(net_offloaded_netdev, test_up_down_sock_off_impl_fail_down)
{
struct net_if *test_iface = NET_IF_GET(sock_offload_test_impl, 0);
/* Bring iface up before test */
(void) net_if_up(test_iface);
/* Instruct the enable callback to fail */
atomic_set(&retval, -EADDRINUSE);
/* Expect net_if_down to fail accordingly */
zassert_equal(net_if_down(test_iface), -EADDRINUSE,
"net_if_down should forward error returned from offload_impl_enabled");
/* Verify that the iface failed to go down */
zassert_true(net_if_is_admin_up(test_iface),
"Iface under test should have failed to go up");
}
ZTEST_SUITE(net_offloaded_netdev, NULL, NULL, net_offloaded_netdev_before, NULL, NULL);