net: kunit: add a test for dev_addr_lists
authorJakub Kicinski <kuba@kernel.org>
Fri, 19 Nov 2021 14:21:55 +0000 (06:21 -0800)
committerDavid S. Miller <davem@davemloft.net>
Sat, 20 Nov 2021 12:25:57 +0000 (12:25 +0000)
Add a KUnit test for the dev_addr API.

Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/Kconfig
net/core/Makefile
net/core/dev_addr_lists_test.c [new file with mode: 0644]

index 074472d..8a1f9d0 100644 (file)
@@ -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
index 6bdcb2c..a8e4f73 100644 (file)
@@ -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
diff --git a/net/core/dev_addr_lists_test.c b/net/core/dev_addr_lists_test.c
new file mode 100644 (file)
index 0000000..049cfbc
--- /dev/null
@@ -0,0 +1,236 @@
+// 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");