Thread (5 messages) 5 messages, 3 authors, 2022-03-30

Re: [PATCH net-next] bond: add mac filter option for balance-xor

From: Jay Vosburgh <hidden>
Date: 2022-03-25 00:50:20
Also in: linux-doc, lkml

	Considering this as an RFC given that net-next is closed...
	
Jonathan Toppins [off-list ref] wrote:
Attempt to replicate the OvS SLB Bonding[1] feature by preventing
duplicate frame delivery on a bond whos members are connected to
physically different switches.

Combining this feature with vlan+srcmac hash policy allows a user to
create an access network without the need to use expensive switches that
support features like Cisco's VCP.
	Could you describe this use case / implementation in a bit more
detail?  I.e., how exactly that configuration works.  I don't think this
patch is replicating everything in the OVS SLB documentation.
quoted hunk ↗ jump to hunk
[1] https://docs.openvswitch.org/en/latest/topics/bonding/#slb-bonding

Co-developed-by: Long Xin <redacted>
Signed-off-by: Long Xin <redacted>
Signed-off-by: Jonathan Toppins <redacted>
---
Documentation/networking/bonding.rst  |  19 +++
drivers/net/bonding/Makefile          |   2 +-
drivers/net/bonding/bond_mac_filter.c | 222 ++++++++++++++++++++++++++
drivers/net/bonding/bond_mac_filter.h |  40 +++++
drivers/net/bonding/bond_main.c       |  26 +++
drivers/net/bonding/bond_netlink.c    |  13 ++
drivers/net/bonding/bond_options.c    |  58 ++++++-
drivers/net/bonding/bonding_priv.h    |   1 +
include/net/bond_options.h            |   1 +
include/net/bonding.h                 |   3 +
include/uapi/linux/if_link.h          |   1 +
11 files changed, 384 insertions(+), 2 deletions(-)
create mode 100644 drivers/net/bonding/bond_mac_filter.c
create mode 100644 drivers/net/bonding/bond_mac_filter.h
diff --git a/Documentation/networking/bonding.rst b/Documentation/networking/bonding.rst
index 525e6842dd33..a5a1669d3efe 100644
--- a/Documentation/networking/bonding.rst
+++ b/Documentation/networking/bonding.rst
@@ -550,6 +550,25 @@ lacp_rate
	The default is slow.

+mac_filter
+
+	Tells the bonding device to drop frames received who's source MAC
+	address	matches entries in a filter table. The filter table is
+	populated when the bond transmits frames. This is similar in
+	concept to the MAC learning table implemented in the bridge code.
+
+	This filtering is only enabled for the balance-xor bonding mode.
+
+	off or 0
+		Turns the feature off
+
+	number
+		A number greater than zero turns the feature on and sets
+		the maximum number of MAC addresses to store in the hash
+		table.
+
+	The default is off.
+
max_bonds

	Specifies the number of bonding devices to create for this
diff --git a/drivers/net/bonding/Makefile b/drivers/net/bonding/Makefile
index 30e8ae3da2da..5dbc360a8522 100644
--- a/drivers/net/bonding/Makefile
+++ b/drivers/net/bonding/Makefile
@@ -5,7 +5,7 @@
obj-$(CONFIG_BONDING) += bonding.o

-bonding-objs := bond_main.o bond_3ad.o bond_alb.o bond_sysfs.o bond_sysfs_slave.o bond_debugfs.o bond_netlink.o bond_options.o
+bonding-objs := bond_main.o bond_3ad.o bond_alb.o bond_sysfs.o bond_sysfs_slave.o bond_debugfs.o bond_netlink.o bond_options.o bond_mac_filter.o

proc-$(CONFIG_PROC_FS) += bond_procfs.o
bonding-objs += $(proc-y)
diff --git a/drivers/net/bonding/bond_mac_filter.c b/drivers/net/bonding/bond_mac_filter.c
new file mode 100644
index 000000000000..a16a1a000f05
--- /dev/null
+++ b/drivers/net/bonding/bond_mac_filter.c
@@ -0,0 +1,222 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Filter received frames based on MAC addresses "behind" the bond.
+ */
+
+#include "bonding_priv.h"
+
+/* -------------- Cache Management -------------- */
	I don't think this header adds anything, given that there's not
really a lot in the section.
+static struct kmem_cache *bond_mac_cache __read_mostly;
+
+int __init bond_mac_cache_init(void)
+{
+	bond_mac_cache = kmem_cache_create("bond_mac_cache",
+					   sizeof(struct bond_mac_cache_entry),
+					   0, SLAB_HWCACHE_ALIGN, NULL);
+	if (!bond_mac_cache)
+		return -ENOMEM;
+	return 0;
+}
+
+void bond_mac_cache_destroy(void)
+{
+	kmem_cache_destroy(bond_mac_cache);
+}
	There are a lot of the above sort of wrapper functions that are
only ever called once.  Some of these, e.g., mac_delete, below, I agree
with, as the call site is nested fairly deep and the function is
non-trivial; or, mac_delete_rcu, which is used as a callback.

	The above two, though, I don't see a justification for, along
with hold_time and maybe a couple others, below.  In my opinion,
over-abstracting these trivial things with one call site makes the code
harder to follow.
+
+/* -------------- Hash Table Management -------------- */
+
+static const struct rhashtable_params bond_rht_params = {
+	.head_offset         = offsetof(struct bond_mac_cache_entry, rhnode),
+	.key_offset          = offsetof(struct bond_mac_cache_entry, key),
+	.key_len             = sizeof(struct mac_addr),
+	.automatic_shrinking = true,
+};
+
+static inline unsigned long hold_time(const struct bonding *bond)
+{
+	return msecs_to_jiffies(5000);
+}
	This shouldn't be a magic number, and if it's an important
timeout, should it be configurable?
+
+static bool has_expired(const struct bonding *bond,
+			struct bond_mac_cache_entry *mac)
+{
+	return time_before_eq(mac->used + hold_time(bond), jiffies);
+}
+
+static void mac_delete_rcu(struct callback_head *head)
+{
+	kmem_cache_free(bond_mac_cache,
+			container_of(head, struct bond_mac_cache_entry, rcu));
+}
+
+static int mac_delete(struct bonding *bond,
+		      struct bond_mac_cache_entry *entry)
+{
+	int ret;
+
+	ret = rhashtable_remove_fast(bond->mac_filter_tbl,
+				     &entry->rhnode,
+				     bond->mac_filter_tbl->p);
+	set_bit(BOND_MAC_DEAD, &entry->flags);
+	call_rcu(&entry->rcu, mac_delete_rcu);
+	return ret;
+}
+
+void bond_mac_hash_release_entries(struct work_struct *work)
+{
+	struct bonding *bond = container_of(work, struct bonding,
+				mac_work.work);
+	struct rhashtable_iter iter;
+	struct bond_mac_cache_entry *entry;
+	unsigned long flags;
+
+	rhashtable_walk_enter(bond->mac_filter_tbl, &iter);
+	rhashtable_walk_start(&iter);
+	while ((entry = rhashtable_walk_next(&iter)) != NULL) {
+		if (IS_ERR(entry))
+			continue;
+
+		spin_lock_irqsave(&entry->lock, flags);
+		if (has_expired(bond, entry))
+			mac_delete(bond, entry);
+		spin_unlock_irqrestore(&entry->lock, flags);
+	}
+	rhashtable_walk_stop(&iter);
+	rhashtable_walk_exit(&iter);
+	queue_delayed_work(bond->wq, &bond->mac_work,
+			   msecs_to_jiffies(5 * 60 * 1000));
+}
+
+int bond_mac_hash_init(struct bonding *bond)
+{
+	int rc;
	As a point of style, (almost) everywhere else in bonding uses
"ret" for a return value.  The exceptions are largely my doing, but,
still, it'd be nice to be mostly consistent in nomenclature.
+
+	netdev_dbg(bond->dev, "mac_filter: alloc memory for hash table\n");
+	bond->mac_filter_tbl = kzalloc(sizeof(*bond->mac_filter_tbl),
+				       GFP_KERNEL);
+	if (!bond->mac_filter_tbl)
+		return -ENOMEM;
+
+	rc = rhashtable_init(bond->mac_filter_tbl, &bond_rht_params);
+	if (rc)
+		kfree(bond->mac_filter_tbl);
+
+	return rc;
+}
+
+static void bond_mac_free_entry(void *entry, void *ctx)
+{
+	kmem_cache_free((struct kmem_cache *)ctx, entry);
+}
+
+void bond_mac_hash_destroy(struct bonding *bond)
+{
+	if (bond->mac_filter_tbl) {
+		rhashtable_free_and_destroy(bond->mac_filter_tbl,
+					    bond_mac_free_entry,
+					    bond_mac_cache);
+		kfree(bond->mac_filter_tbl);
+		bond->mac_filter_tbl = NULL;
+	}
+}
+
+static int mac_create(struct bonding *bond, const u8 *addr)
+{
+	struct bond_mac_cache_entry *entry;
+	int ret;
+
+	entry = kmem_cache_alloc(bond_mac_cache, GFP_ATOMIC);
+	if (!entry)
+		return -ENOMEM;
+	spin_lock_init(&entry->lock);
+	memcpy(&entry->key, addr, sizeof(entry->key));
+	entry->used = jiffies;
+	ret = rhashtable_lookup_insert_fast(bond->mac_filter_tbl,
+					    &entry->rhnode,
+					    bond->mac_filter_tbl->p);
+	if (ret) {
+		kmem_cache_free(bond_mac_cache, entry);
+		entry = NULL;
+		if (ret == -EEXIST)
+			return 0;
+		pr_err_once("Failed to insert mac entry %d\n", ret);
+	}
+	return ret;
+}
+
+static struct bond_mac_cache_entry *mac_find(struct bonding *bond,
+					     const u8 *addr)
+{
+	struct mac_addr key;
+
+	memcpy(&key, addr, sizeof(key));
+	return rhashtable_lookup(bond->mac_filter_tbl, &key,
+				 bond->mac_filter_tbl->p);
+}
+
+inline void mac_update(struct bond_mac_cache_entry *entry)
+{
+	entry->used = jiffies;
+}
+
+int bond_mac_insert(struct bonding *bond, const u8 *addr)
+{
+	struct bond_mac_cache_entry *entry;
+	int rc = 0;
+
+	if (!is_valid_ether_addr(addr))
+		return -EINVAL;
+
+	rcu_read_lock();
+	entry = mac_find(bond, addr);
+	if (entry) {
+		unsigned long flags;
+
+		spin_lock_irqsave(&entry->lock, flags);
+		if (!test_bit(BOND_MAC_DEAD, &entry->flags)) {
+			mac_update(entry);
+			spin_unlock_irqrestore(&entry->lock, flags);
+			goto out;
+		}
+		spin_unlock_irqrestore(&entry->lock, flags);
	This seems really expensive, as it will add a spin_lock_irqsave
round trip for almost every packet transmitted when mac_filter is
enabled (as this will be called by bond_xmit_3ad_xor_slave_get).
+	}
+
+	rc = mac_create(bond, addr);
+
+out:
+	rcu_read_unlock();
+	return rc;
+}
+
+int bond_xor_recv(const struct sk_buff *skb, struct bonding *bond,
+		  struct slave *slave)
+{
+	const struct ethhdr *mac_hdr;
+	struct bond_mac_cache_entry *entry;
+	int rc = RX_HANDLER_PASS;
+
+	mac_hdr = (struct ethhdr *)skb_mac_header(skb);
+	rcu_read_lock();
+	if (is_multicast_ether_addr(mac_hdr->h_dest) &&
+	    slave != rcu_dereference(bond->curr_active_slave)) {
+		rc = RX_HANDLER_CONSUMED;
+		goto out;
+	}
+
+	entry = mac_find(bond, mac_hdr->h_source);
+	if (entry) {
+		unsigned long flags;
+		bool expired;
+
+		spin_lock_irqsave(&entry->lock, flags);
+		expired = has_expired(bond, entry);
+		spin_unlock_irqrestore(&entry->lock, flags);
+		if (!expired)
+			rc = RX_HANDLER_CONSUMED;
+	}
	As above, really expensive, except for incoming packets here
(since this is called as the recv_probe).
quoted hunk ↗ jump to hunk
+
+out:
+	rcu_read_unlock();
+	return rc;
+}
diff --git a/drivers/net/bonding/bond_mac_filter.h b/drivers/net/bonding/bond_mac_filter.h
new file mode 100644
index 000000000000..0cfcc5653e7e
--- /dev/null
+++ b/drivers/net/bonding/bond_mac_filter.h
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: GPL-2.0-only
+ *
+ * Filter received frames based on MAC addresses "behind" the bond.
+ */
+
+#ifndef _BOND_MAC_FILTER_H
+#define _BOND_MAC_FILTER_H
+#include <net/bonding.h>
+#include <linux/spinlock.h>
+#include <linux/rhashtable.h>
+
+enum {
+	BOND_MAC_DEAD,
+	BOND_MAC_LOCKED,
+	BOND_MAC_STATIC,
+};
+
+struct bond_mac_cache_entry {
+	struct rhash_head	rhnode;
+	struct mac_addr		key;
+
+	spinlock_t		lock; /* protects used member */
+	unsigned long		flags;
+	unsigned long		used;
+	struct rcu_head		rcu;
+};
+
+int __init bond_mac_cache_init(void);
+void bond_mac_cache_destroy(void);
+
+void bond_mac_hash_release_entries(struct work_struct *work);
+int bond_mac_hash_init(struct bonding *bond);
+void bond_mac_hash_destroy(struct bonding *bond);
+
+int bond_mac_insert(struct bonding *bond, const u8 *addr);
+int bond_xor_recv(const struct sk_buff *skb,
+		  struct bonding *bond,
+		  struct slave *slave);
+
+#endif
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index 15eddca7b4b6..f5a8a50e9116 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -1549,6 +1549,10 @@ static rx_handler_result_t bond_handle_frame(struct sk_buff **pskb)
		return RX_HANDLER_EXACT;
	}

+	/* this function should not rely on the recv_probe to set ret
+	 * correctly
+	 */
+	ret = RX_HANDLER_ANOTHER;
	This change is overriding the return from a recv_probe added by
this patch (bond_xor_recv can return RX_HANDLER_PASS).  Why?

	Also, I don't agree with the comment; the recv_probe return
value by design affects the return value from bond_handle_frame.
quoted hunk ↗ jump to hunk
	skb->dev = bond->dev;

	if (BOND_MODE(bond) == BOND_MODE_ALB &&
@@ -4117,6 +4121,7 @@ void bond_work_init_all(struct bonding *bond)
	INIT_DELAYED_WORK(&bond->arp_work, bond_arp_monitor);
	INIT_DELAYED_WORK(&bond->ad_work, bond_3ad_state_machine_handler);
	INIT_DELAYED_WORK(&bond->slave_arr_work, bond_slave_arr_handler);
+	INIT_DELAYED_WORK(&bond->mac_work, bond_mac_hash_release_entries);
}

static void bond_work_cancel_all(struct bonding *bond)
@@ -4127,6 +4132,7 @@ static void bond_work_cancel_all(struct bonding *bond)
	cancel_delayed_work_sync(&bond->ad_work);
	cancel_delayed_work_sync(&bond->mcast_work);
	cancel_delayed_work_sync(&bond->slave_arr_work);
+	cancel_delayed_work_sync(&bond->mac_work);
}

static int bond_open(struct net_device *bond_dev)
@@ -4174,6 +4180,11 @@ static int bond_open(struct net_device *bond_dev)
		bond_3ad_initiate_agg_selection(bond, 1);
	}

+	if (bond->params.mac_filter) {
+		bond->recv_probe = bond_xor_recv;
+		queue_delayed_work(bond->wq, &bond->mac_work, 0);
+	}
+
	if (bond_mode_can_use_xmit_hash(bond))
		bond_update_slave_arr(bond, NULL);
@@ -5043,6 +5054,13 @@ static struct slave *bond_xmit_3ad_xor_slave_get(struct bonding *bond,
	if (unlikely(!count))
		return NULL;

+	if (bond->params.mac_filter) {
+		const struct ethhdr *mac_hdr;
+
+		mac_hdr = (struct ethhdr *)skb_mac_header(skb);
+		if (bond_mac_insert(bond, mac_hdr->h_source))
+			return NULL;
+	}
	slave = slaves->arr[hash % count];
	return slave;
}
@@ -5660,6 +5678,8 @@ static void bond_destructor(struct net_device *bond_dev)
	if (bond->rr_tx_counter)
		free_percpu(bond->rr_tx_counter);
+
+	bond_mac_hash_destroy(bond);
}

void bond_setup(struct net_device *bond_dev)
@@ -6115,6 +6135,7 @@ static int bond_check_params(struct bond_params *params)
	params->downdelay = downdelay;
	params->peer_notif_delay = 0;
	params->use_carrier = use_carrier;
+	params->mac_filter = 0;
	params->lacp_active = 1;
	params->lacp_fast = lacp_fast;
	params->primary[0] = 0;
@@ -6317,6 +6338,10 @@ static int __init bonding_init(void)
			goto err;
	}

+	res = bond_mac_cache_init();
+	if (res)
+		goto err;
+
	skb_flow_dissector_init(&flow_keys_bonding,
				flow_keys_bonding_keys,
				ARRAY_SIZE(flow_keys_bonding_keys));
@@ -6346,6 +6371,7 @@ static void __exit bonding_exit(void)
	/* Make sure we don't have an imbalance on our netpoll blocking */
	WARN_ON(atomic_read(&netpoll_block_tx));
#endif
+	bond_mac_cache_destroy();
}

module_init(bonding_init);
diff --git a/drivers/net/bonding/bond_netlink.c b/drivers/net/bonding/bond_netlink.c
index f427fa1737c7..249d79b6e21a 100644
--- a/drivers/net/bonding/bond_netlink.c
+++ b/drivers/net/bonding/bond_netlink.c
@@ -113,6 +113,7 @@ static const struct nla_policy bond_policy[IFLA_BOND_MAX + 1] = {
	[IFLA_BOND_PEER_NOTIF_DELAY]    = { .type = NLA_U32 },
	[IFLA_BOND_MISSED_MAX]		= { .type = NLA_U8 },
	[IFLA_BOND_NS_IP6_TARGET]	= { .type = NLA_NESTED },
+	[IFLA_BOND_MAC_FILTER]		= { .type = NLA_U8 },
};

static const struct nla_policy bond_slave_policy[IFLA_BOND_SLAVE_MAX + 1] = {
@@ -498,6 +499,14 @@ static int bond_changelink(struct net_device *bond_dev, struct nlattr *tb[],
		if (err)
			return err;
	}
+	if (data[IFLA_BOND_MAC_FILTER]) {
+		u8 mac_filter = nla_get_u8(data[IFLA_BOND_MAC_FILTER]);
+
+		bond_opt_initval(&newval, mac_filter);
+		err = __bond_opt_set(bond, BOND_OPT_MAC_FILTER, &newval);
+		if (err)
+			return err;
+	}

	return 0;
}
@@ -565,6 +574,7 @@ static size_t bond_get_size(const struct net_device *bond_dev)
						/* IFLA_BOND_NS_IP6_TARGET */
		nla_total_size(sizeof(struct nlattr)) +
		nla_total_size(sizeof(struct in6_addr)) * BOND_MAX_NS_TARGETS +
+		nla_total_size(sizeof(u8)) +	/* IFLA_BOND_MAC_FILTER */
		0;
}
@@ -723,6 +733,9 @@ static int bond_fill_info(struct sk_buff *skb,
	if (nla_put_u8(skb, IFLA_BOND_MISSED_MAX,
		       bond->params.missed_max))
		goto nla_put_failure;
+	if (nla_put_u8(skb, IFLA_BOND_MAC_FILTER,
+		       bond->params.mac_filter))
+		goto nla_put_failure;

	if (BOND_MODE(bond) == BOND_MODE_8023AD) {
		struct ad_info info;
diff --git a/drivers/net/bonding/bond_options.c b/drivers/net/bonding/bond_options.c
index 64f7db2627ce..0f6036ff7b86 100644
--- a/drivers/net/bonding/bond_options.c
+++ b/drivers/net/bonding/bond_options.c
@@ -15,6 +15,7 @@
#include <linux/sched/signal.h>

#include <net/bonding.h>
+#include "bonding_priv.h"

static int bond_option_active_slave_set(struct bonding *bond,
					const struct bond_opt_value *newval);
@@ -84,6 +85,8 @@ static int bond_option_ad_user_port_key_set(struct bonding *bond,
					    const struct bond_opt_value *newval);
static int bond_option_missed_max_set(struct bonding *bond,
				      const struct bond_opt_value *newval);
+static int bond_option_mac_filter_set(struct bonding *bond,
+				      const struct bond_opt_value *newval);


static const struct bond_opt_value bond_mode_tbl[] = {
@@ -226,6 +229,12 @@ static const struct bond_opt_value bond_missed_max_tbl[] = {
	{ NULL,		-1,	0},
};

+static const struct bond_opt_value bond_mac_filter_tbl[] = {
+	{ "off",	0,	BOND_VALFLAG_MIN | BOND_VALFLAG_DEFAULT},
+	{ "maxval",	18,	BOND_VALFLAG_MAX},
	What's the magic number 18?
quoted hunk ↗ jump to hunk
+	{ NULL,		-1,	0}
+};
+
static const struct bond_option bond_opts[BOND_OPT_LAST] = {
	[BOND_OPT_MODE] = {
		.id = BOND_OPT_MODE,
@@ -482,7 +491,16 @@ static const struct bond_option bond_opts[BOND_OPT_LAST] = {
		.desc = "Delay between each peer notification on failover event, in milliseconds",
		.values = bond_intmax_tbl,
		.set = bond_option_peer_notif_delay_set
-	}
+	},
+	[BOND_OPT_MAC_FILTER] = {
+		.id = BOND_OPT_MAC_FILTER,
+		.name = "mac_filter",
+		.unsuppmodes = BOND_MODE_ALL_EX(BIT(BOND_MODE_XOR)),
+		.desc = "filter received frames based on MAC addresses that have transmitted from the bond, number of MAC addresses to track",
+		.flags = BOND_OPTFLAG_NOSLAVES | BOND_OPTFLAG_IFDOWN,
+		.values = bond_mac_filter_tbl,
+		.set = bond_option_mac_filter_set
+	},
};

/* Searches for an option by name */
@@ -1035,6 +1053,44 @@ static int bond_option_use_carrier_set(struct bonding *bond,
	return 0;
}

+static int bond_option_mac_filter_set(struct bonding *bond,
+				      const struct bond_opt_value *newval)
+{
+	int rc = 0;
+	u8 prev = bond->params.mac_filter;
+
+	if (newval->value && bond->params.arp_interval) {
+		netdev_err(bond->dev, "ARP monitoring cannot be used with MAC Filtering.\n");
+		rc = -EPERM;
+		goto err;
+	}
	What happens if a user (a) switches to ARP monitor with
arp_validate in balance-xor mode after mac_filter is enabled, or, (b)
changes the mode to something other than balance-xor with mac_filter
enabled (both by changing the configuration in real time)?

	-J
quoted hunk ↗ jump to hunk
+
+	netdev_dbg(bond->dev, "Setting mac_filter to %llu\n",
+		   newval->value);
+	bond->params.mac_filter = newval->value;
+
+	if (prev == 0 && bond->params.mac_filter > 0) {
+		rc = bond_mac_hash_init(bond);
+		if (rc)
+			goto err;
+	} else if (prev > 0 && bond->params.mac_filter == 0)
+		bond_mac_hash_destroy(bond);
+
+	if (bond->mac_filter_tbl) {
+		bond->mac_filter_tbl->p.max_size =
+			1 << bond->params.mac_filter;
+		netdev_dbg(bond->dev, "mac_filter hash table size: %d\n",
+			   bond->mac_filter_tbl->p.max_size);
+	}
+
+out:
+	return rc;
+
+err:
+	bond->params.mac_filter = 0;
+	goto out;
+}
+
/* There are two tricky bits here.  First, if ARP monitoring is activated, then
 * we must disable MII monitoring.  Second, if the ARP timer isn't running,
 * we must start it.
diff --git a/drivers/net/bonding/bonding_priv.h b/drivers/net/bonding/bonding_priv.h
index 48cdf3a49a7d..0299f8bcb5fd 100644
--- a/drivers/net/bonding/bonding_priv.h
+++ b/drivers/net/bonding/bonding_priv.h
@@ -15,6 +15,7 @@
#ifndef _BONDING_PRIV_H
#define _BONDING_PRIV_H
#include <generated/utsrelease.h>
+#include "bond_mac_filter.h"

#define DRV_NAME	"bonding"
#define DRV_DESCRIPTION	"Ethernet Channel Bonding Driver"
diff --git a/include/net/bond_options.h b/include/net/bond_options.h
index 61b49063791c..42e3e676b9c2 100644
--- a/include/net/bond_options.h
+++ b/include/net/bond_options.h
@@ -67,6 +67,7 @@ enum {
	BOND_OPT_LACP_ACTIVE,
	BOND_OPT_MISSED_MAX,
	BOND_OPT_NS_TARGETS,
+	BOND_OPT_MAC_FILTER,
	BOND_OPT_LAST
};
diff --git a/include/net/bonding.h b/include/net/bonding.h
index b14f4c0b4e9e..5bc3e7b5a2af 100644
--- a/include/net/bonding.h
+++ b/include/net/bonding.h
@@ -125,6 +125,7 @@ struct bond_params {
	int miimon;
	u8 num_peer_notif;
	u8 missed_max;
+	u8 mac_filter;
	int arp_interval;
	int arp_validate;
	int arp_all_targets;
@@ -248,6 +249,7 @@ struct bonding {
	struct   delayed_work alb_work;
	struct   delayed_work ad_work;
	struct   delayed_work mcast_work;
+	struct   delayed_work mac_work;
	struct   delayed_work slave_arr_work;
#ifdef CONFIG_DEBUG_FS
	/* debugging support via debugfs */
@@ -260,6 +262,7 @@ struct bonding {
	spinlock_t ipsec_lock;
#endif /* CONFIG_XFRM_OFFLOAD */
	struct bpf_prog *xdp_prog;
+	struct rhashtable *mac_filter_tbl;
};

#define bond_slave_get_rcu(dev) \
diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h
index cc284c048e69..9291df89581f 100644
--- a/include/uapi/linux/if_link.h
+++ b/include/uapi/linux/if_link.h
@@ -929,6 +929,7 @@ enum {
	IFLA_BOND_AD_LACP_ACTIVE,
	IFLA_BOND_MISSED_MAX,
	IFLA_BOND_NS_IP6_TARGET,
+	IFLA_BOND_MAC_FILTER,
	__IFLA_BOND_MAX,
};

-- 
2.27.0
---
	-Jay Vosburgh, jay.vosburgh@canonical.com
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help