| // 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); |
| } |
| |
| return 0; |
| } |
| |
| static void dev_addr_test_exit(struct kunit *test) |
| { |
| struct net_device *netdev = test->priv; |
| |
| 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]; |
| |
| rtnl_lock(); |
| KUNIT_EXPECT_TRUE(test, !!netdev->dev_addr); |
| |
| memset(addr, 2, sizeof(addr)); |
| eth_hw_addr_set(netdev, addr); |
| KUNIT_EXPECT_MEMEQ(test, netdev->dev_addr, addr, sizeof(addr)); |
| |
| memset(addr, 3, sizeof(addr)); |
| dev_addr_set(netdev, addr); |
| KUNIT_EXPECT_MEMEQ(test, netdev->dev_addr, addr, sizeof(addr)); |
| rtnl_unlock(); |
| } |
| |
| 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); |
| |
| rtnl_lock(); |
| 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); |
| rtnl_unlock(); |
| } |
| |
| 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); |
| |
| rtnl_lock(); |
| 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); |
| rtnl_unlock(); |
| } |
| |
| static void dev_addr_test_del_main(struct kunit *test) |
| { |
| struct net_device *netdev = test->priv; |
| u8 addr[ETH_ALEN]; |
| |
| rtnl_lock(); |
| 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)); |
| rtnl_unlock(); |
| } |
| |
| 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); |
| |
| rtnl_lock(); |
| /* 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); |
| rtnl_unlock(); |
| } |
| |
| static void dev_addr_test_add_excl(struct kunit *test) |
| { |
| struct net_device *netdev = test->priv; |
| u8 addr[ETH_ALEN]; |
| int i; |
| |
| rtnl_lock(); |
| 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)); |
| } |
| rtnl_unlock(); |
| } |
| |
| 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_DESCRIPTION("KUnit tests for struct netdev_hw_addr_list"); |
| MODULE_LICENSE("GPL"); |