blob: 8e1dba825e946f12c1f256c450cb753c25b0598f [file] [log] [blame]
// 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");