// SPDX-License-Identifier: GPL-2.0-or-later #include #include #include #include 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");