Commit 2c193f2c authored by Jakub Kicinski's avatar Jakub Kicinski Committed by David S. Miller
Browse files

net: kunit: add a test for dev_addr_lists



Add a KUnit test for the dev_addr API.

Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent a387ff8e
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -455,4 +455,9 @@ config ETHTOOL_NETLINK
	  netlink. It provides better extensibility and some new features,
	  e.g. notification messages.

config NETDEV_ADDR_LIST_TEST
	tristate "Unit tests for device address list"
	default KUNIT_ALL_TESTS
	depends on KUNIT

endif   # if NET
+2 −0
Original line number Diff line number Diff line
@@ -13,6 +13,8 @@ obj-y += dev.o dev_addr_lists.o dst.o netevent.o \
			sock_diag.o dev_ioctl.o tso.o sock_reuseport.o \
			fib_notifier.o xdp.o flow_offload.o gro.o

obj-$(CONFIG_NETDEV_ADDR_LIST_TEST) += dev_addr_lists_test.o

obj-y += net-sysfs.o
obj-$(CONFIG_PAGE_POOL) += page_pool.o
obj-$(CONFIG_PROC_FS) += net-procfs.o
+236 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-or-later

#include <kunit/test.h>
#include <linux/etherdevice.h>
#include <linux/netdevice.h>
#include <linux/rtnetlink.h>

static const struct net_device_ops dummy_netdev_ops = {
};

struct dev_addr_test_priv {
	u32 addr_seen;
};

static int dev_addr_test_sync(struct net_device *netdev, const unsigned char *a)
{
	struct dev_addr_test_priv *datp = netdev_priv(netdev);

	if (a[0] < 31 && !memchr_inv(a, a[0], ETH_ALEN))
		datp->addr_seen |= 1 << a[0];
	return 0;
}

static int dev_addr_test_unsync(struct net_device *netdev,
				const unsigned char *a)
{
	struct dev_addr_test_priv *datp = netdev_priv(netdev);

	if (a[0] < 31 && !memchr_inv(a, a[0], ETH_ALEN))
		datp->addr_seen &= ~(1 << a[0]);
	return 0;
}

static int dev_addr_test_init(struct kunit *test)
{
	struct dev_addr_test_priv *datp;
	struct net_device *netdev;
	int err;

	netdev = alloc_etherdev(sizeof(*datp));
	KUNIT_ASSERT_TRUE(test, !!netdev);

	test->priv = netdev;
	netdev->netdev_ops = &dummy_netdev_ops;

	err = register_netdev(netdev);
	if (err) {
		free_netdev(netdev);
		KUNIT_FAIL(test, "Can't register netdev %d", err);
	}

	rtnl_lock();
	return 0;
}

static void dev_addr_test_exit(struct kunit *test)
{
	struct net_device *netdev = test->priv;

	rtnl_unlock();
	unregister_netdev(netdev);
	free_netdev(netdev);
}

static void dev_addr_test_basic(struct kunit *test)
{
	struct net_device *netdev = test->priv;
	u8 addr[ETH_ALEN];

	KUNIT_EXPECT_TRUE(test, !!netdev->dev_addr);

	memset(addr, 2, sizeof(addr));
	eth_hw_addr_set(netdev, addr);
	KUNIT_EXPECT_EQ(test, 0, memcmp(netdev->dev_addr, addr, sizeof(addr)));

	memset(addr, 3, sizeof(addr));
	dev_addr_set(netdev, addr);
	KUNIT_EXPECT_EQ(test, 0, memcmp(netdev->dev_addr, addr, sizeof(addr)));
}

static void dev_addr_test_sync_one(struct kunit *test)
{
	struct net_device *netdev = test->priv;
	struct dev_addr_test_priv *datp;
	u8 addr[ETH_ALEN];

	datp = netdev_priv(netdev);

	memset(addr, 1, sizeof(addr));
	eth_hw_addr_set(netdev, addr);

	__hw_addr_sync_dev(&netdev->dev_addrs, netdev, dev_addr_test_sync,
			   dev_addr_test_unsync);
	KUNIT_EXPECT_EQ(test, 2, datp->addr_seen);

	memset(addr, 2, sizeof(addr));
	eth_hw_addr_set(netdev, addr);

	datp->addr_seen = 0;
	__hw_addr_sync_dev(&netdev->dev_addrs, netdev, dev_addr_test_sync,
			   dev_addr_test_unsync);
	/* It's not going to sync anything because the main address is
	 * considered synced and we overwrite in place.
	 */
	KUNIT_EXPECT_EQ(test, 0, datp->addr_seen);
}

static void dev_addr_test_add_del(struct kunit *test)
{
	struct net_device *netdev = test->priv;
	struct dev_addr_test_priv *datp;
	u8 addr[ETH_ALEN];
	int i;

	datp = netdev_priv(netdev);

	for (i = 1; i < 4; i++) {
		memset(addr, i, sizeof(addr));
		KUNIT_EXPECT_EQ(test, 0, dev_addr_add(netdev, addr,
						      NETDEV_HW_ADDR_T_LAN));
	}
	/* Add 3 again */
	KUNIT_EXPECT_EQ(test, 0, dev_addr_add(netdev, addr,
					      NETDEV_HW_ADDR_T_LAN));

	__hw_addr_sync_dev(&netdev->dev_addrs, netdev, dev_addr_test_sync,
			   dev_addr_test_unsync);
	KUNIT_EXPECT_EQ(test, 0xf, datp->addr_seen);

	KUNIT_EXPECT_EQ(test, 0, dev_addr_del(netdev, addr,
					      NETDEV_HW_ADDR_T_LAN));

	__hw_addr_sync_dev(&netdev->dev_addrs, netdev, dev_addr_test_sync,
			   dev_addr_test_unsync);
	KUNIT_EXPECT_EQ(test, 0xf, datp->addr_seen);

	for (i = 1; i < 4; i++) {
		memset(addr, i, sizeof(addr));
		KUNIT_EXPECT_EQ(test, 0, dev_addr_del(netdev, addr,
						      NETDEV_HW_ADDR_T_LAN));
	}

	__hw_addr_sync_dev(&netdev->dev_addrs, netdev, dev_addr_test_sync,
			   dev_addr_test_unsync);
	KUNIT_EXPECT_EQ(test, 1, datp->addr_seen);
}

static void dev_addr_test_del_main(struct kunit *test)
{
	struct net_device *netdev = test->priv;
	u8 addr[ETH_ALEN];

	memset(addr, 1, sizeof(addr));
	eth_hw_addr_set(netdev, addr);

	KUNIT_EXPECT_EQ(test, -ENOENT, dev_addr_del(netdev, addr,
						    NETDEV_HW_ADDR_T_LAN));
	KUNIT_EXPECT_EQ(test, 0, dev_addr_add(netdev, addr,
					      NETDEV_HW_ADDR_T_LAN));
	KUNIT_EXPECT_EQ(test, 0, dev_addr_del(netdev, addr,
					      NETDEV_HW_ADDR_T_LAN));
	KUNIT_EXPECT_EQ(test, -ENOENT, dev_addr_del(netdev, addr,
						    NETDEV_HW_ADDR_T_LAN));
}

static void dev_addr_test_add_set(struct kunit *test)
{
	struct net_device *netdev = test->priv;
	struct dev_addr_test_priv *datp;
	u8 addr[ETH_ALEN];
	int i;

	datp = netdev_priv(netdev);

	/* There is no external API like dev_addr_add_excl(),
	 * so shuffle the tree a little bit and exploit aliasing.
	 */
	for (i = 1; i < 16; i++) {
		memset(addr, i, sizeof(addr));
		KUNIT_EXPECT_EQ(test, 0, dev_addr_add(netdev, addr,
						      NETDEV_HW_ADDR_T_LAN));
	}

	memset(addr, i, sizeof(addr));
	eth_hw_addr_set(netdev, addr);
	KUNIT_EXPECT_EQ(test, 0, dev_addr_add(netdev, addr,
					      NETDEV_HW_ADDR_T_LAN));
	memset(addr, 0, sizeof(addr));
	eth_hw_addr_set(netdev, addr);

	__hw_addr_sync_dev(&netdev->dev_addrs, netdev, dev_addr_test_sync,
			   dev_addr_test_unsync);
	KUNIT_EXPECT_EQ(test, 0xffff, datp->addr_seen);
}

static void dev_addr_test_add_excl(struct kunit *test)
{
	struct net_device *netdev = test->priv;
	u8 addr[ETH_ALEN];
	int i;

	for (i = 0; i < 10; i++) {
		memset(addr, i, sizeof(addr));
		KUNIT_EXPECT_EQ(test, 0, dev_uc_add_excl(netdev, addr));
	}
	KUNIT_EXPECT_EQ(test, -EEXIST, dev_uc_add_excl(netdev, addr));

	for (i = 0; i < 10; i += 2) {
		memset(addr, i, sizeof(addr));
		KUNIT_EXPECT_EQ(test, 0, dev_uc_del(netdev, addr));
	}
	for (i = 1; i < 10; i += 2) {
		memset(addr, i, sizeof(addr));
		KUNIT_EXPECT_EQ(test, -EEXIST, dev_uc_add_excl(netdev, addr));
	}
}

static struct kunit_case dev_addr_test_cases[] = {
	KUNIT_CASE(dev_addr_test_basic),
	KUNIT_CASE(dev_addr_test_sync_one),
	KUNIT_CASE(dev_addr_test_add_del),
	KUNIT_CASE(dev_addr_test_del_main),
	KUNIT_CASE(dev_addr_test_add_set),
	KUNIT_CASE(dev_addr_test_add_excl),
	{}
};

static struct kunit_suite dev_addr_test_suite = {
	.name = "dev-addr-list-test",
	.test_cases = dev_addr_test_cases,
	.init = dev_addr_test_init,
	.exit = dev_addr_test_exit,
};
kunit_test_suite(dev_addr_test_suite);

MODULE_LICENSE("GPL");