netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH net-next v10 0/7] bonding: Extend arp_ip_target format to allow for a list of vlan tags.
@ 2025-09-04 22:18 David Wilder
  2025-09-04 22:18 ` [PATCH net-next v10 1/7] bonding: Adding struct bond_arp_target David Wilder
                   ` (7 more replies)
  0 siblings, 8 replies; 22+ messages in thread
From: David Wilder @ 2025-09-04 22:18 UTC (permalink / raw)
  To: netdev
  Cc: jv, wilder, pradeeps, pradeep, i.maximets, amorenoz, haliu,
	stephen, horms

The current implementation of the arp monitor builds a list of vlan-tags by
following the chain of net_devices above the bond. See bond_verify_device_path().
Unfortunately, with some configurations, this is not possible. One example is
when an ovs switch is configured above the bond.

This change extends the "arp_ip_target" parameter format to allow for a list of
vlan tags to be included for each arp target. This new list of tags is optional
and may be omitted to preserve the current format and process of discovering
vlans.

The new format for arp_ip_target is:
arp_ip_target ipv4-address[vlan-tag\...],...

For example:
arp_ip_target 10.0.0.1[10/20]
arp_ip_target 10.0.0.1[] (used to disable vlan discovery)

Change since V9
Fix kdoc build error.

Changes since V8:
Moved the #define BOND_MAX_VLAN_TAGS from patch 6 to patch 3.
Thanks Simon for catching the bisection break.

Changes since V7:
These changes should eliminate the CI failures I have been seeing.
1) patch 2, changed type of bond_opt_value.extra_len to size_t.
2) Patch 4, added bond_validate_tags() to validate the array of bond_vlan_tag provided by
 the user.

Changes since V6:
1) I made a number of changes to fix the failure seen in the
kernel CI.  I am still unable to reproduce the this failure, hopefully I
have fixed it.  These change are in patch #4 to functions:
bond_option_arp_ip_targets_clear() and
bond_option_arp_ip_targets_set()

Changes since V5: Only the last 2 patches have changed since V5.
1) Fixed sparse warning in bond_fill_info().
2) Also in bond_fill_info() I resolved data.addr uninitialized when if condition is not met.
Thank you Simon for catching this. Note: The change is different that what I shared earlier.
3) Fixed shellcheck warnings in test script: Blocked source warning, Ignored specific unassigned
references and exported ALL_TESTS to resolve a reference warning.

Changes since V4:
1)Dropped changes to proc and sysfs APIs to bonding.  These APIs 
do not need to be updated to support new functionality.  Netlink
and iproute2 have been updated to do the right thing, but the
other APIs are more or less frozen in the past.

2)Jakub reported a warning triggered in bond_info_seq_show() during
testing.  I was unable to reproduce this warning or identify
it with code inspection.  However, all my changes to bond_info_seq_show()
have been dropped as unnecessary (see above).
Hopefully this will resolve the issue. 

3)Selftest script has been updated based on the results of shellcheck.
Two unresolved references that are not possible to resolve are all
that remain.

4)A patch was added updating bond_info_fill()
to support "ip -d show <bond-device>" command.

The inclusion of a list of vlan tags is optional. The new logic
preserves both forward and backward compatibility with the kernel
and iproute2 versions.

Changes since V3:
1) Moved the parsing of the extended arp_ip_target out of the kernel and into
   userspace (ip command). A separate patch to iproute2 to follow shortly.
2) Split up the patch set to make review easier.

Please see iproute changes in a separate posting.

Thank you for your time and reviews.

Signed-off-by: David Wilder <wilder@us.ibm.com>

David Wilder (7):
  bonding: Adding struct bond_arp_target
  bonding: Adding extra_len field to struct bond_opt_value.
  bonding: arp_ip_target helpers.
  bonding: Processing extended arp_ip_target from user space.
  bonding: Update to bond_arp_send_all() to use supplied vlan tags
  bonding: Update for extended arp_ip_target format.
  bonding: Selftest and documentation for the arp_ip_target parameter.

 Documentation/networking/bonding.rst          |  11 ++
 drivers/net/bonding/bond_main.c               |  47 +++--
 drivers/net/bonding/bond_netlink.c            |  35 +++-
 drivers/net/bonding/bond_options.c            | 135 +++++++++----
 drivers/net/bonding/bond_procfs.c             |   4 +-
 drivers/net/bonding/bond_sysfs.c              |   4 +-
 include/net/bond_options.h                    |  29 ++-
 include/net/bonding.h                         |  61 +++++-
 .../selftests/drivers/net/bonding/Makefile    |   3 +-
 .../drivers/net/bonding/bond-arp-ip-target.sh | 180 ++++++++++++++++++
 .../selftests/drivers/net/bonding/config      |   1 +
 11 files changed, 433 insertions(+), 77 deletions(-)
 create mode 100755 tools/testing/selftests/drivers/net/bonding/bond-arp-ip-target.sh

-- 
2.50.1


^ permalink raw reply	[flat|nested] 22+ messages in thread

* [PATCH net-next v10 1/7] bonding: Adding struct bond_arp_target
  2025-09-04 22:18 [PATCH net-next v10 0/7] bonding: Extend arp_ip_target format to allow for a list of vlan tags David Wilder
@ 2025-09-04 22:18 ` David Wilder
  2025-09-04 22:18 ` [PATCH net-next v10 2/7] bonding: Adding extra_len field to struct bond_opt_value David Wilder
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 22+ messages in thread
From: David Wilder @ 2025-09-04 22:18 UTC (permalink / raw)
  To: netdev
  Cc: jv, wilder, pradeeps, pradeep, i.maximets, amorenoz, haliu,
	stephen, horms

Replacing the definition of bond_params.arp_targets (__be32 arp_targets[])
with:

struct bond_arp_target {
	__be32			target_ip;
	struct bond_vlan_tag	*tags;
	u32			flags;
};

To provide storage for a list of vlan tags for each target.

All references to arp_target are change to use the new structure.

Signed-off-by: David Wilder <wilder@us.ibm.com>
---
 drivers/net/bonding/bond_main.c    | 29 ++++++++++++++++-------------
 drivers/net/bonding/bond_netlink.c |  4 ++--
 drivers/net/bonding/bond_options.c | 18 +++++++++---------
 drivers/net/bonding/bond_procfs.c  |  4 ++--
 drivers/net/bonding/bond_sysfs.c   |  4 ++--
 include/net/bond_options.h         | 20 ++++++++++++++++++++
 include/net/bonding.h              | 15 +++++----------
 7 files changed, 56 insertions(+), 38 deletions(-)

diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index f25c2d2c9181..7548119ca0f3 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -3065,26 +3065,29 @@ static void bond_arp_send_all(struct bonding *bond, struct slave *slave)
 {
 	struct rtable *rt;
 	struct bond_vlan_tag *tags;
-	__be32 *targets = bond->params.arp_targets, addr;
+	struct bond_arp_target *targets = bond->params.arp_targets;
+	__be32 target_ip, addr;
 	int i;
 
-	for (i = 0; i < BOND_MAX_ARP_TARGETS && targets[i]; i++) {
+	for (i = 0; i < BOND_MAX_ARP_TARGETS && targets[i].target_ip; i++) {
+		target_ip = targets[i].target_ip;
+		tags = targets[i].tags;
+
 		slave_dbg(bond->dev, slave->dev, "%s: target %pI4\n",
-			  __func__, &targets[i]);
-		tags = NULL;
+			  __func__, &target_ip);
 
 		/* Find out through which dev should the packet go */
-		rt = ip_route_output(dev_net(bond->dev), targets[i], 0, 0, 0,
+		rt = ip_route_output(dev_net(bond->dev), target_ip, 0, 0, 0,
 				     RT_SCOPE_LINK);
 		if (IS_ERR(rt)) {
-			/* there's no route to target - try to send arp
+			/* there's no route to target_ip - try to send arp
 			 * probe to generate any traffic (arp_validate=0)
 			 */
 			if (bond->params.arp_validate)
 				pr_warn_once("%s: no route to arp_ip_target %pI4 and arp_validate is set\n",
 					     bond->dev->name,
-					     &targets[i]);
-			bond_arp_send(slave, ARPOP_REQUEST, targets[i],
+					     &target_ip);
+			bond_arp_send(slave, ARPOP_REQUEST, target_ip,
 				      0, tags);
 			continue;
 		}
@@ -3102,15 +3105,15 @@ static void bond_arp_send_all(struct bonding *bond, struct slave *slave)
 
 		/* Not our device - skip */
 		slave_dbg(bond->dev, slave->dev, "no path to arp_ip_target %pI4 via rt.dev %s\n",
-			   &targets[i], rt->dst.dev ? rt->dst.dev->name : "NULL");
+			   &target_ip, rt->dst.dev ? rt->dst.dev->name : "NULL");
 
 		ip_rt_put(rt);
 		continue;
 
 found:
-		addr = bond_confirm_addr(rt->dst.dev, targets[i], 0);
+		addr = bond_confirm_addr(rt->dst.dev, target_ip, 0);
 		ip_rt_put(rt);
-		bond_arp_send(slave, ARPOP_REQUEST, targets[i], addr, tags);
+		bond_arp_send(slave, ARPOP_REQUEST, target_ip, addr, tags);
 		kfree(tags);
 	}
 }
@@ -6066,7 +6069,7 @@ static int __init bond_check_params(struct bond_params *params)
 	int arp_all_targets_value = 0;
 	u16 ad_actor_sys_prio = 0;
 	u16 ad_user_port_key = 0;
-	__be32 arp_target[BOND_MAX_ARP_TARGETS] = { 0 };
+	struct bond_arp_target arp_target[BOND_MAX_ARP_TARGETS] = { 0 };
 	int arp_ip_count;
 	int bond_mode	= BOND_MODE_ROUNDROBIN;
 	int xmit_hashtype = BOND_XMIT_POLICY_LAYER2;
@@ -6260,7 +6263,7 @@ static int __init bond_check_params(struct bond_params *params)
 			arp_interval = 0;
 		} else {
 			if (bond_get_targets_ip(arp_target, ip) == -1)
-				arp_target[arp_ip_count++] = ip;
+				arp_target[arp_ip_count++].target_ip = ip;
 			else
 				pr_warn("Warning: duplicate address %pI4 in arp_ip_target, skipping\n",
 					&ip);
diff --git a/drivers/net/bonding/bond_netlink.c b/drivers/net/bonding/bond_netlink.c
index e573b34a1bbc..a5887254ff23 100644
--- a/drivers/net/bonding/bond_netlink.c
+++ b/drivers/net/bonding/bond_netlink.c
@@ -698,8 +698,8 @@ static int bond_fill_info(struct sk_buff *skb,
 
 	targets_added = 0;
 	for (i = 0; i < BOND_MAX_ARP_TARGETS; i++) {
-		if (bond->params.arp_targets[i]) {
-			if (nla_put_be32(skb, i, bond->params.arp_targets[i]))
+		if (bond->params.arp_targets[i].target_ip) {
+			if (nla_put_be32(skb, i, bond->params.arp_targets[i].target_ip))
 				goto nla_put_failure;
 			targets_added = 1;
 		}
diff --git a/drivers/net/bonding/bond_options.c b/drivers/net/bonding/bond_options.c
index c0a5eb8766b5..cf4cb301a738 100644
--- a/drivers/net/bonding/bond_options.c
+++ b/drivers/net/bonding/bond_options.c
@@ -1108,7 +1108,7 @@ static int bond_option_arp_interval_set(struct bonding *bond,
 			netdev_dbg(bond->dev, "ARP monitoring cannot be used with MII monitoring. Disabling MII monitoring\n");
 			bond->params.miimon = 0;
 		}
-		if (!bond->params.arp_targets[0])
+		if (!bond->params.arp_targets[0].target_ip)
 			netdev_dbg(bond->dev, "ARP monitoring has been set up, but no ARP targets have been specified\n");
 	}
 	if (bond->dev->flags & IFF_UP) {
@@ -1136,20 +1136,20 @@ static void _bond_options_arp_ip_target_set(struct bonding *bond, int slot,
 					    __be32 target,
 					    unsigned long last_rx)
 {
-	__be32 *targets = bond->params.arp_targets;
+	struct bond_arp_target *targets = bond->params.arp_targets;
 	struct list_head *iter;
 	struct slave *slave;
 
 	if (slot >= 0 && slot < BOND_MAX_ARP_TARGETS) {
 		bond_for_each_slave(bond, slave, iter)
 			slave->target_last_arp_rx[slot] = last_rx;
-		targets[slot] = target;
+		targets[slot].target_ip = target;
 	}
 }
 
 static int _bond_option_arp_ip_target_add(struct bonding *bond, __be32 target)
 {
-	__be32 *targets = bond->params.arp_targets;
+	struct bond_arp_target *targets = bond->params.arp_targets;
 	int ind;
 
 	if (!bond_is_ip_target_ok(target)) {
@@ -1184,7 +1184,7 @@ static int bond_option_arp_ip_target_add(struct bonding *bond, __be32 target)
 
 static int bond_option_arp_ip_target_rem(struct bonding *bond, __be32 target)
 {
-	__be32 *targets = bond->params.arp_targets;
+	struct bond_arp_target *targets = bond->params.arp_targets;
 	struct list_head *iter;
 	struct slave *slave;
 	unsigned long *targets_rx;
@@ -1203,20 +1203,20 @@ static int bond_option_arp_ip_target_rem(struct bonding *bond, __be32 target)
 		return -EINVAL;
 	}
 
-	if (ind == 0 && !targets[1] && bond->params.arp_interval)
+	if (ind == 0 && !targets[1].target_ip && bond->params.arp_interval)
 		netdev_warn(bond->dev, "Removing last arp target with arp_interval on\n");
 
 	netdev_dbg(bond->dev, "Removing ARP target %pI4\n", &target);
 
 	bond_for_each_slave(bond, slave, iter) {
 		targets_rx = slave->target_last_arp_rx;
-		for (i = ind; (i < BOND_MAX_ARP_TARGETS-1) && targets[i+1]; i++)
+		for (i = ind; (i < BOND_MAX_ARP_TARGETS - 1) && targets[i + 1].target_ip; i++)
 			targets_rx[i] = targets_rx[i+1];
 		targets_rx[i] = 0;
 	}
-	for (i = ind; (i < BOND_MAX_ARP_TARGETS-1) && targets[i+1]; i++)
+	for (i = ind; (i < BOND_MAX_ARP_TARGETS - 1) && targets[i + 1].target_ip; i++)
 		targets[i] = targets[i+1];
-	targets[i] = 0;
+	targets[i].target_ip = 0;
 
 	return 0;
 }
diff --git a/drivers/net/bonding/bond_procfs.c b/drivers/net/bonding/bond_procfs.c
index 7edf72ec816a..94e6fd7041ee 100644
--- a/drivers/net/bonding/bond_procfs.c
+++ b/drivers/net/bonding/bond_procfs.c
@@ -121,11 +121,11 @@ static void bond_info_show_master(struct seq_file *seq)
 		seq_printf(seq, "ARP IP target/s (n.n.n.n form):");
 
 		for (i = 0; (i < BOND_MAX_ARP_TARGETS); i++) {
-			if (!bond->params.arp_targets[i])
+			if (!bond->params.arp_targets[i].target_ip)
 				break;
 			if (printed)
 				seq_printf(seq, ",");
-			seq_printf(seq, " %pI4", &bond->params.arp_targets[i]);
+			seq_printf(seq, " %pI4", &bond->params.arp_targets[i].target_ip);
 			printed = 1;
 		}
 		seq_printf(seq, "\n");
diff --git a/drivers/net/bonding/bond_sysfs.c b/drivers/net/bonding/bond_sysfs.c
index 9a75ad3181ab..7114bd4d7735 100644
--- a/drivers/net/bonding/bond_sysfs.c
+++ b/drivers/net/bonding/bond_sysfs.c
@@ -290,9 +290,9 @@ static ssize_t bonding_show_arp_targets(struct device *d,
 	int i, res = 0;
 
 	for (i = 0; i < BOND_MAX_ARP_TARGETS; i++) {
-		if (bond->params.arp_targets[i])
+		if (bond->params.arp_targets[i].target_ip)
 			res += sysfs_emit_at(buf, res, "%pI4 ",
-					     &bond->params.arp_targets[i]);
+					     &bond->params.arp_targets[i].target_ip);
 	}
 	if (res)
 		buf[res-1] = '\n'; /* eat the leftover space */
diff --git a/include/net/bond_options.h b/include/net/bond_options.h
index 022b122a9fb6..b7f275bc33a1 100644
--- a/include/net/bond_options.h
+++ b/include/net/bond_options.h
@@ -120,6 +120,26 @@ struct bond_option {
 	int (*set)(struct bonding *bond, const struct bond_opt_value *val);
 };
 
+struct bond_vlan_tag {
+	__be16		vlan_proto;
+	unsigned short	vlan_id;
+};
+
+/* Value type flags:
+ *  BOND_TARGET_DONTFREE - never free the tags
+ *  BOND_TARGET_USERTAGS - tags have been supplied by the user
+ */
+enum {
+	BOND_TARGET_DONTFREE = BIT(0),
+	BOND_TARGET_USERTAGS = BIT(1),
+};
+
+struct bond_arp_target {
+	__be32			target_ip;
+	struct bond_vlan_tag	*tags;
+	u32			flags;
+};
+
 int __bond_opt_set(struct bonding *bond, unsigned int option,
 		   struct bond_opt_value *val,
 		   struct nlattr *bad_attr, struct netlink_ext_ack *extack);
diff --git a/include/net/bonding.h b/include/net/bonding.h
index 37335f62f579..a0eae209315f 100644
--- a/include/net/bonding.h
+++ b/include/net/bonding.h
@@ -136,7 +136,7 @@ struct bond_params {
 	int ad_select;
 	char primary[IFNAMSIZ];
 	int primary_reselect;
-	__be32 arp_targets[BOND_MAX_ARP_TARGETS];
+	struct bond_arp_target arp_targets[BOND_MAX_ARP_TARGETS];
 	int tx_queues;
 	int all_slaves_active;
 	int resend_igmp;
@@ -276,11 +276,6 @@ struct bonding {
 void bond_queue_slave_event(struct slave *slave);
 void bond_lower_state_changed(struct slave *slave);
 
-struct bond_vlan_tag {
-	__be16		vlan_proto;
-	unsigned short	vlan_id;
-};
-
 /*
  * Returns NULL if the net_device does not belong to any of the bond's slaves
  *
@@ -524,7 +519,7 @@ static inline unsigned long slave_oldest_target_arp_rx(struct bonding *bond,
 	int i = 1;
 	unsigned long ret = slave->target_last_arp_rx[0];
 
-	for (; (i < BOND_MAX_ARP_TARGETS) && bond->params.arp_targets[i]; i++)
+	for (; (i < BOND_MAX_ARP_TARGETS) && bond->params.arp_targets[i].target_ip; i++)
 		if (time_before(slave->target_last_arp_rx[i], ret))
 			ret = slave->target_last_arp_rx[i];
 
@@ -762,14 +757,14 @@ static inline bool bond_slave_has_mac_rcu(struct bonding *bond, const u8 *mac)
 /* Check if the ip is present in arp ip list, or first free slot if ip == 0
  * Returns -1 if not found, index if found
  */
-static inline int bond_get_targets_ip(__be32 *targets, __be32 ip)
+static inline int bond_get_targets_ip(struct bond_arp_target *targets, __be32 ip)
 {
 	int i;
 
 	for (i = 0; i < BOND_MAX_ARP_TARGETS; i++)
-		if (targets[i] == ip)
+		if (targets[i].target_ip == ip)
 			return i;
-		else if (targets[i] == 0)
+		else if (targets[i].target_ip == 0)
 			break;
 
 	return -1;
-- 
2.50.1


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH net-next v10 2/7] bonding: Adding extra_len field to struct bond_opt_value.
  2025-09-04 22:18 [PATCH net-next v10 0/7] bonding: Extend arp_ip_target format to allow for a list of vlan tags David Wilder
  2025-09-04 22:18 ` [PATCH net-next v10 1/7] bonding: Adding struct bond_arp_target David Wilder
@ 2025-09-04 22:18 ` David Wilder
  2025-09-04 22:18 ` [PATCH net-next v10 3/7] bonding: arp_ip_target helpers David Wilder
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 22+ messages in thread
From: David Wilder @ 2025-09-04 22:18 UTC (permalink / raw)
  To: netdev
  Cc: jv, wilder, pradeeps, pradeep, i.maximets, amorenoz, haliu,
	stephen, horms

Used to record the size of the extra array.

__bond_opt_init() is updated to set extra_len.
BOND_OPT_EXTRA_MAXLEN is increased from 16 to 64.

This is needed for the extended  arp_ip_target option.
The ip command will now pass a variable length value when
setting arp_ip_target.

Signed-off-by: David Wilder <wilder@us.ibm.com>
---
 include/net/bond_options.h | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/include/net/bond_options.h b/include/net/bond_options.h
index b7f275bc33a1..7d22bbe67121 100644
--- a/include/net/bond_options.h
+++ b/include/net/bond_options.h
@@ -86,14 +86,15 @@ enum {
  * - if value != ULLONG_MAX -> parse value
  * - if string != NULL -> parse string
  * - if the opt is RAW data and length less than maxlen,
- *   copy the data to extra storage
+ *   copy the data to extra storage, extra_len is set to the size of data copied.
  */
 
-#define BOND_OPT_EXTRA_MAXLEN 16
+#define BOND_OPT_EXTRA_MAXLEN 64
 struct bond_opt_value {
 	char *string;
 	u64 value;
 	u32 flags;
+	size_t extra_len;
 	union {
 		char extra[BOND_OPT_EXTRA_MAXLEN];
 		struct net_device *slave_dev;
@@ -168,8 +169,10 @@ static inline void __bond_opt_init(struct bond_opt_value *optval,
 	else if (string)
 		optval->string = string;
 
-	if (extra && extra_len <= BOND_OPT_EXTRA_MAXLEN)
+	if (extra && extra_len <= BOND_OPT_EXTRA_MAXLEN) {
 		memcpy(optval->extra, extra, extra_len);
+		optval->extra_len = extra_len;
+	}
 }
 #define bond_opt_initval(optval, value) __bond_opt_init(optval, NULL, value, NULL, 0)
 #define bond_opt_initstr(optval, str) __bond_opt_init(optval, str, ULLONG_MAX, NULL, 0)
-- 
2.50.1


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH net-next v10 3/7] bonding: arp_ip_target helpers.
  2025-09-04 22:18 [PATCH net-next v10 0/7] bonding: Extend arp_ip_target format to allow for a list of vlan tags David Wilder
  2025-09-04 22:18 ` [PATCH net-next v10 1/7] bonding: Adding struct bond_arp_target David Wilder
  2025-09-04 22:18 ` [PATCH net-next v10 2/7] bonding: Adding extra_len field to struct bond_opt_value David Wilder
@ 2025-09-04 22:18 ` David Wilder
  2025-09-04 22:18 ` [PATCH net-next v10 4/7] bonding: Processing extended arp_ip_target from user space David Wilder
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 22+ messages in thread
From: David Wilder @ 2025-09-04 22:18 UTC (permalink / raw)
  To: netdev
  Cc: jv, wilder, pradeeps, pradeep, i.maximets, amorenoz, haliu,
	stephen, horms

Adding helpers and defines needed for extending the
arp_ip_target parameters.

Signed-off-by: David Wilder <wilder@us.ibm.com>
---
 include/net/bonding.h | 46 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 46 insertions(+)

diff --git a/include/net/bonding.h b/include/net/bonding.h
index a0eae209315f..8ebc39d08963 100644
--- a/include/net/bonding.h
+++ b/include/net/bonding.h
@@ -808,4 +808,50 @@ static inline netdev_tx_t bond_tx_drop(struct net_device *dev, struct sk_buff *s
 	return NET_XMIT_DROP;
 }
 
+/* Helpers for handling arp_ip_target */
+#define BOND_OPTION_STRING_MAX_SIZE 64
+#define BOND_MAX_VLAN_TAGS 5
+#define BOND_VLAN_PROTO_NONE cpu_to_be16(0xffff)
+
+static inline char *bond_arp_target_to_string(struct bond_arp_target *target,
+					    char *buf, int size)
+{
+	struct bond_vlan_tag *tags = target->tags;
+	int i, num = 0;
+
+	if (!(target->flags & BOND_TARGET_USERTAGS)) {
+		num = snprintf(&buf[0], size, "%pI4", &target->target_ip);
+		return buf;
+	}
+
+	num = snprintf(&buf[0], size, "%pI4[", &target->target_ip);
+	if (tags) {
+		for (i = 0; (tags[i].vlan_proto != BOND_VLAN_PROTO_NONE); i++) {
+			if (!tags[i].vlan_id)
+				continue;
+			if (i != 0)
+				num = num + snprintf(&buf[num], size-num, "/");
+			num = num + snprintf(&buf[num], size-num, "%u",
+					     tags[i].vlan_id);
+		}
+	}
+	snprintf(&buf[num], size-num, "]");
+	return buf;
+}
+
+static inline void bond_free_vlan_tag(struct bond_arp_target *target)
+{
+	kfree(target->tags);
+}
+
+static inline void __bond_free_vlan_tags(struct bond_arp_target *targets)
+{
+	int i;
+
+	for (i = 0; i < BOND_MAX_ARP_TARGETS && targets[i].tags; i++)
+		bond_free_vlan_tag(&targets[i]);
+}
+
+#define bond_free_vlan_tags(targets)  __bond_free_vlan_tags(targets)
+
 #endif /* _NET_BONDING_H */
-- 
2.50.1


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH net-next v10 4/7] bonding: Processing extended arp_ip_target from user space.
  2025-09-04 22:18 [PATCH net-next v10 0/7] bonding: Extend arp_ip_target format to allow for a list of vlan tags David Wilder
                   ` (2 preceding siblings ...)
  2025-09-04 22:18 ` [PATCH net-next v10 3/7] bonding: arp_ip_target helpers David Wilder
@ 2025-09-04 22:18 ` David Wilder
  2025-09-09 13:37   ` Paolo Abeni
  2025-09-09 18:42   ` Nikolay Aleksandrov
  2025-09-04 22:18 ` [PATCH net-next v10 5/7] bonding: Update to bond_arp_send_all() to use supplied vlan tags David Wilder
                   ` (3 subsequent siblings)
  7 siblings, 2 replies; 22+ messages in thread
From: David Wilder @ 2025-09-04 22:18 UTC (permalink / raw)
  To: netdev
  Cc: jv, wilder, pradeeps, pradeep, i.maximets, amorenoz, haliu,
	stephen, horms

Changes to bond_netlink and bond_options to process extended
format arp_ip_target option sent from user space via the ip
command.

The extended format adds a list of vlan tags to the ip target address.

Signed-off-by: David Wilder <wilder@us.ibm.com>
---
 drivers/net/bonding/bond_netlink.c |   5 +-
 drivers/net/bonding/bond_options.c | 121 +++++++++++++++++++++++------
 2 files changed, 99 insertions(+), 27 deletions(-)

diff --git a/drivers/net/bonding/bond_netlink.c b/drivers/net/bonding/bond_netlink.c
index a5887254ff23..28ee50ddf4e2 100644
--- a/drivers/net/bonding/bond_netlink.c
+++ b/drivers/net/bonding/bond_netlink.c
@@ -291,9 +291,10 @@ static int bond_changelink(struct net_device *bond_dev, struct nlattr *tb[],
 			if (nla_len(attr) < sizeof(target))
 				return -EINVAL;
 
-			target = nla_get_be32(attr);
+			bond_opt_initextra(&newval,
+					   (__force void *)nla_data(attr),
+					   nla_len(attr));
 
-			bond_opt_initval(&newval, (__force u64)target);
 			err = __bond_opt_set(bond, BOND_OPT_ARP_TARGETS,
 					     &newval,
 					     data[IFLA_BOND_ARP_IP_TARGET],
diff --git a/drivers/net/bonding/bond_options.c b/drivers/net/bonding/bond_options.c
index cf4cb301a738..61334633403d 100644
--- a/drivers/net/bonding/bond_options.c
+++ b/drivers/net/bonding/bond_options.c
@@ -31,8 +31,8 @@ static int bond_option_use_carrier_set(struct bonding *bond,
 				       const struct bond_opt_value *newval);
 static int bond_option_arp_interval_set(struct bonding *bond,
 					const struct bond_opt_value *newval);
-static int bond_option_arp_ip_target_add(struct bonding *bond, __be32 target);
-static int bond_option_arp_ip_target_rem(struct bonding *bond, __be32 target);
+static int bond_option_arp_ip_target_add(struct bonding *bond, struct bond_arp_target target);
+static int bond_option_arp_ip_target_rem(struct bonding *bond, struct bond_arp_target target);
 static int bond_option_arp_ip_targets_set(struct bonding *bond,
 					  const struct bond_opt_value *newval);
 static int bond_option_ns_ip6_targets_set(struct bonding *bond,
@@ -1133,7 +1133,7 @@ static int bond_option_arp_interval_set(struct bonding *bond,
 }
 
 static void _bond_options_arp_ip_target_set(struct bonding *bond, int slot,
-					    __be32 target,
+					    struct bond_arp_target target,
 					    unsigned long last_rx)
 {
 	struct bond_arp_target *targets = bond->params.arp_targets;
@@ -1143,24 +1143,25 @@ static void _bond_options_arp_ip_target_set(struct bonding *bond, int slot,
 	if (slot >= 0 && slot < BOND_MAX_ARP_TARGETS) {
 		bond_for_each_slave(bond, slave, iter)
 			slave->target_last_arp_rx[slot] = last_rx;
-		targets[slot].target_ip = target;
+		memcpy(&targets[slot], &target, sizeof(target));
 	}
 }
 
-static int _bond_option_arp_ip_target_add(struct bonding *bond, __be32 target)
+static int _bond_option_arp_ip_target_add(struct bonding *bond, struct bond_arp_target target)
 {
 	struct bond_arp_target *targets = bond->params.arp_targets;
+	char pbuf[BOND_OPTION_STRING_MAX_SIZE];
 	int ind;
 
-	if (!bond_is_ip_target_ok(target)) {
+	if (!bond_is_ip_target_ok(target.target_ip)) {
 		netdev_err(bond->dev, "invalid ARP target %pI4 specified for addition\n",
-			   &target);
+			   &target.target_ip);
 		return -EINVAL;
 	}
 
-	if (bond_get_targets_ip(targets, target) != -1) { /* dup */
+	if (bond_get_targets_ip(targets, target.target_ip) != -1) { /* dup */
 		netdev_err(bond->dev, "ARP target %pI4 is already present\n",
-			   &target);
+			   &target.target_ip);
 		return -EINVAL;
 	}
 
@@ -1170,43 +1171,44 @@ static int _bond_option_arp_ip_target_add(struct bonding *bond, __be32 target)
 		return -EINVAL;
 	}
 
-	netdev_dbg(bond->dev, "Adding ARP target %pI4\n", &target);
+	netdev_dbg(bond->dev, "Adding ARP target %s\n",
+		   bond_arp_target_to_string(&target, pbuf, sizeof(pbuf)));
 
 	_bond_options_arp_ip_target_set(bond, ind, target, jiffies);
 
 	return 0;
 }
 
-static int bond_option_arp_ip_target_add(struct bonding *bond, __be32 target)
+static int bond_option_arp_ip_target_add(struct bonding *bond, struct bond_arp_target target)
 {
 	return _bond_option_arp_ip_target_add(bond, target);
 }
 
-static int bond_option_arp_ip_target_rem(struct bonding *bond, __be32 target)
+static int bond_option_arp_ip_target_rem(struct bonding *bond, struct bond_arp_target target)
 {
 	struct bond_arp_target *targets = bond->params.arp_targets;
+	unsigned long *targets_rx;
 	struct list_head *iter;
 	struct slave *slave;
-	unsigned long *targets_rx;
 	int ind, i;
 
-	if (!bond_is_ip_target_ok(target)) {
+	if (!bond_is_ip_target_ok(target.target_ip)) {
 		netdev_err(bond->dev, "invalid ARP target %pI4 specified for removal\n",
-			   &target);
+			   &target.target_ip);
 		return -EINVAL;
 	}
 
-	ind = bond_get_targets_ip(targets, target);
+	ind = bond_get_targets_ip(targets, target.target_ip);
 	if (ind == -1) {
 		netdev_err(bond->dev, "unable to remove nonexistent ARP target %pI4\n",
-			   &target);
+			   &target.target_ip);
 		return -EINVAL;
 	}
 
 	if (ind == 0 && !targets[1].target_ip && bond->params.arp_interval)
 		netdev_warn(bond->dev, "Removing last arp target with arp_interval on\n");
 
-	netdev_dbg(bond->dev, "Removing ARP target %pI4\n", &target);
+	netdev_dbg(bond->dev, "Removing ARP target %pI4\n", &target.target_ip);
 
 	bond_for_each_slave(bond, slave, iter) {
 		targets_rx = slave->target_last_arp_rx;
@@ -1214,30 +1216,77 @@ static int bond_option_arp_ip_target_rem(struct bonding *bond, __be32 target)
 			targets_rx[i] = targets_rx[i+1];
 		targets_rx[i] = 0;
 	}
-	for (i = ind; (i < BOND_MAX_ARP_TARGETS - 1) && targets[i + 1].target_ip; i++)
-		targets[i] = targets[i+1];
+
+	bond_free_vlan_tag(&targets[ind]);
+
+	for (i = ind; (i < BOND_MAX_ARP_TARGETS - 1) && targets[i + 1].target_ip; i++) {
+		targets[i].target_ip = targets[i + 1].target_ip;
+		targets[i].tags = targets[i + 1].tags;
+		targets[i].flags = targets[i + 1].flags;
+	}
 	targets[i].target_ip = 0;
+	targets[i].flags = 0;
+	targets[i].tags = NULL;
 
 	return 0;
 }
 
 void bond_option_arp_ip_targets_clear(struct bonding *bond)
 {
+	struct bond_arp_target empty_target;
 	int i;
 
+	empty_target.target_ip = 0;
+	empty_target.flags = 0;
+	empty_target.tags = NULL;
+
 	for (i = 0; i < BOND_MAX_ARP_TARGETS; i++)
-		_bond_options_arp_ip_target_set(bond, i, 0, 0);
+		_bond_options_arp_ip_target_set(bond, i, empty_target, 0);
+}
+
+/**
+ * bond_validate_tags - validate an array of bond_vlan_tag.
+ * @tags: the array to validate
+ * @len: the length in bytes of @tags
+ *
+ * Validate that @tags points to a valid array of struct bond_vlan_tag.
+ * Returns: the length of the validated bytes in the array or -1 if no
+ * valid list is found.
+ */
+static int bond_validate_tags(struct bond_vlan_tag *tags, size_t len)
+{
+	size_t i, ntags = 0;
+
+	if (len == 0 || !tags)
+		return 0;
+
+	for (i = 0; i <= len; i = i + sizeof(struct bond_vlan_tag)) {
+		if (ntags > BOND_MAX_VLAN_TAGS)
+			break;
+
+		if (tags->vlan_proto == BOND_VLAN_PROTO_NONE)
+			return i + sizeof(struct bond_vlan_tag);
+
+		if (tags->vlan_id > 4094)
+			break;
+		tags++;
+		ntags++;
+	}
+	return -1;
 }
 
 static int bond_option_arp_ip_targets_set(struct bonding *bond,
 					  const struct bond_opt_value *newval)
 {
-	int ret = -EPERM;
-	__be32 target;
+	size_t len = (size_t)newval->extra_len;
+	char *extra = (char *)newval->extra;
+	struct bond_arp_target target;
+	int size, ret = -EPERM;
 
 	if (newval->string) {
+		/* Adding or removing arp_ip_target from sysfs */
 		if (strlen(newval->string) < 1 ||
-		    !in4_pton(newval->string + 1, -1, (u8 *)&target, -1, NULL)) {
+		    !in4_pton(newval->string + 1, -1, (u8 *)&target.target_ip, -1, NULL)) {
 			netdev_err(bond->dev, "invalid ARP target specified\n");
 			return ret;
 		}
@@ -1248,7 +1297,29 @@ static int bond_option_arp_ip_targets_set(struct bonding *bond,
 		else
 			netdev_err(bond->dev, "no command found in arp_ip_targets file - use +<addr> or -<addr>\n");
 	} else {
-		target = newval->value;
+		/* Adding arp_ip_target from netlink. aka: ip command */
+		if (len < sizeof(target.target_ip)) {
+			netdev_err(bond->dev, "invalid ARP target specified\n");
+			return ret;
+		}
+		memcpy(&target.target_ip, newval->extra, sizeof(__be32));
+		len = len - sizeof(target.target_ip);
+		extra = extra + sizeof(target.target_ip);
+
+		size = bond_validate_tags((struct bond_vlan_tag *)extra, len);
+
+		if (size > 0) {
+			target.tags = kmalloc((size_t)size, GFP_ATOMIC);
+			if (!target.tags)
+				return -ENOMEM;
+			memcpy(target.tags, extra, size);
+			target.flags |= BOND_TARGET_USERTAGS;
+		}
+
+		if (size == -1)
+			netdev_warn(bond->dev, "Invalid list of vlans provided with %pI4\n",
+				    &target.target_ip);
+
 		ret = bond_option_arp_ip_target_add(bond, target);
 	}
 
-- 
2.50.1


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH net-next v10 5/7] bonding: Update to bond_arp_send_all() to use supplied vlan tags
  2025-09-04 22:18 [PATCH net-next v10 0/7] bonding: Extend arp_ip_target format to allow for a list of vlan tags David Wilder
                   ` (3 preceding siblings ...)
  2025-09-04 22:18 ` [PATCH net-next v10 4/7] bonding: Processing extended arp_ip_target from user space David Wilder
@ 2025-09-04 22:18 ` David Wilder
  2025-09-09 13:39   ` Paolo Abeni
  2025-09-09 18:55   ` Nikolay Aleksandrov
  2025-09-04 22:18 ` [PATCH net-next v10 6/7] bonding: Update for extended arp_ip_target format David Wilder
                   ` (2 subsequent siblings)
  7 siblings, 2 replies; 22+ messages in thread
From: David Wilder @ 2025-09-04 22:18 UTC (permalink / raw)
  To: netdev
  Cc: jv, wilder, pradeeps, pradeep, i.maximets, amorenoz, haliu,
	stephen, horms

bond_arp_send_all() will pass the vlan tags supplied by
the user to bond_arp_send(). If vlan tags have not been
supplied the vlans in the path to the target will be
discovered by bond_verify_device_path(). The discovered
vlan tags are then saved to be used on future calls to
bond_arp_send().

bond_uninit() is also updated to free vlan tags when a
bond is destroyed.

Signed-off-by: David Wilder <wilder@us.ibm.com>
---
 drivers/net/bonding/bond_main.c | 22 +++++++++++++---------
 1 file changed, 13 insertions(+), 9 deletions(-)

diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index 7548119ca0f3..7288f8a5f1a5 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -3063,18 +3063,19 @@ struct bond_vlan_tag *bond_verify_device_path(struct net_device *start_dev,
 
 static void bond_arp_send_all(struct bonding *bond, struct slave *slave)
 {
-	struct rtable *rt;
-	struct bond_vlan_tag *tags;
 	struct bond_arp_target *targets = bond->params.arp_targets;
+	char pbuf[BOND_OPTION_STRING_MAX_SIZE];
+	struct bond_vlan_tag *tags;
 	__be32 target_ip, addr;
+	struct rtable *rt;
 	int i;
 
 	for (i = 0; i < BOND_MAX_ARP_TARGETS && targets[i].target_ip; i++) {
 		target_ip = targets[i].target_ip;
 		tags = targets[i].tags;
 
-		slave_dbg(bond->dev, slave->dev, "%s: target %pI4\n",
-			  __func__, &target_ip);
+		slave_dbg(bond->dev, slave->dev, "%s: target %s\n", __func__,
+			  bond_arp_target_to_string(&targets[i], pbuf, sizeof(pbuf)));
 
 		/* Find out through which dev should the packet go */
 		rt = ip_route_output(dev_net(bond->dev), target_ip, 0, 0, 0,
@@ -3096,9 +3097,13 @@ static void bond_arp_send_all(struct bonding *bond, struct slave *slave)
 		if (rt->dst.dev == bond->dev)
 			goto found;
 
-		rcu_read_lock();
-		tags = bond_verify_device_path(bond->dev, rt->dst.dev, 0);
-		rcu_read_unlock();
+		if (!tags) {
+			rcu_read_lock();
+			tags = bond_verify_device_path(bond->dev, rt->dst.dev, 0);
+			/* cache the tags */
+			targets[i].tags = tags;
+			rcu_read_unlock();
+		}
 
 		if (!IS_ERR_OR_NULL(tags))
 			goto found;
@@ -3114,7 +3119,6 @@ static void bond_arp_send_all(struct bonding *bond, struct slave *slave)
 		addr = bond_confirm_addr(rt->dst.dev, target_ip, 0);
 		ip_rt_put(rt);
 		bond_arp_send(slave, ARPOP_REQUEST, target_ip, addr, tags);
-		kfree(tags);
 	}
 }
 
@@ -6047,6 +6051,7 @@ static void bond_uninit(struct net_device *bond_dev)
 	bond_for_each_slave(bond, slave, iter)
 		__bond_release_one(bond_dev, slave->dev, true, true);
 	netdev_info(bond_dev, "Released all slaves\n");
+	bond_free_vlan_tags(bond->params.arp_targets);
 
 #ifdef CONFIG_XFRM_OFFLOAD
 	mutex_destroy(&bond->ipsec_lock);
@@ -6633,7 +6638,6 @@ static void __exit bonding_exit(void)
 
 	bond_netlink_fini();
 	unregister_pernet_subsys(&bond_net_ops);
-
 	bond_destroy_debugfs();
 
 #ifdef CONFIG_NET_POLL_CONTROLLER
-- 
2.50.1


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH net-next v10 6/7] bonding: Update for extended arp_ip_target format.
  2025-09-04 22:18 [PATCH net-next v10 0/7] bonding: Extend arp_ip_target format to allow for a list of vlan tags David Wilder
                   ` (4 preceding siblings ...)
  2025-09-04 22:18 ` [PATCH net-next v10 5/7] bonding: Update to bond_arp_send_all() to use supplied vlan tags David Wilder
@ 2025-09-04 22:18 ` David Wilder
  2025-09-09 13:46   ` Paolo Abeni
  2025-09-09 18:45   ` Nikolay Aleksandrov
  2025-09-04 22:18 ` [PATCH net-next v10 7/7] bonding: Selftest and documentation for the arp_ip_target parameter David Wilder
  2025-09-09 13:54 ` [PATCH net-next v10 0/7] bonding: Extend arp_ip_target format to allow for a list of vlan tags Nikolay Aleksandrov
  7 siblings, 2 replies; 22+ messages in thread
From: David Wilder @ 2025-09-04 22:18 UTC (permalink / raw)
  To: netdev
  Cc: jv, wilder, pradeeps, pradeep, i.maximets, amorenoz, haliu,
	stephen, horms

Updated bond_fill_info() to support extended arp_ip_target format.

Forward and backward compatibility between the kernel and iprout2 is
preserved.

Signed-off-by: David Wilder <wilder@us.ibm.com>
---
 drivers/net/bonding/bond_netlink.c | 30 +++++++++++++++++++++++++-----
 1 file changed, 25 insertions(+), 5 deletions(-)

diff --git a/drivers/net/bonding/bond_netlink.c b/drivers/net/bonding/bond_netlink.c
index 28ee50ddf4e2..1f0d3269a0b1 100644
--- a/drivers/net/bonding/bond_netlink.c
+++ b/drivers/net/bonding/bond_netlink.c
@@ -660,6 +660,7 @@ static int bond_fill_info(struct sk_buff *skb,
 			  const struct net_device *bond_dev)
 {
 	struct bonding *bond = netdev_priv(bond_dev);
+	struct bond_arp_target *arptargets;
 	unsigned int packets_per_slave;
 	int ifindex, i, targets_added;
 	struct nlattr *targets;
@@ -698,12 +699,31 @@ static int bond_fill_info(struct sk_buff *skb,
 		goto nla_put_failure;
 
 	targets_added = 0;
-	for (i = 0; i < BOND_MAX_ARP_TARGETS; i++) {
-		if (bond->params.arp_targets[i].target_ip) {
-			if (nla_put_be32(skb, i, bond->params.arp_targets[i].target_ip))
-				goto nla_put_failure;
-			targets_added = 1;
+
+	arptargets = bond->params.arp_targets;
+	for (i = 0; i < BOND_MAX_ARP_TARGETS && arptargets[i].target_ip ; i++) {
+		struct Data {
+			__be32 addr;
+			struct bond_vlan_tag vlans[BOND_MAX_VLAN_TAGS];
+		} __packed data;
+		int level, size;
+
+		data.addr = arptargets[i].target_ip;
+		size = sizeof(__be32);
+		targets_added = 1;
+
+		if (arptargets[i].flags & BOND_TARGET_USERTAGS) {
+			for (level = 0; level < BOND_MAX_VLAN_TAGS ; level++) {
+				data.vlans[level].vlan_proto = arptargets[i].tags[level].vlan_proto;
+				data.vlans[level].vlan_id = arptargets[i].tags[level].vlan_id;
+				size = size + sizeof(struct bond_vlan_tag);
+				if (arptargets[i].tags[level].vlan_proto == BOND_VLAN_PROTO_NONE)
+					break;
+				}
 		}
+
+		if (nla_put(skb, i, size, &data))
+			goto nla_put_failure;
 	}
 
 	if (targets_added)
-- 
2.50.1


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH net-next v10 7/7] bonding: Selftest and documentation for the arp_ip_target parameter.
  2025-09-04 22:18 [PATCH net-next v10 0/7] bonding: Extend arp_ip_target format to allow for a list of vlan tags David Wilder
                   ` (5 preceding siblings ...)
  2025-09-04 22:18 ` [PATCH net-next v10 6/7] bonding: Update for extended arp_ip_target format David Wilder
@ 2025-09-04 22:18 ` David Wilder
  2025-09-09 13:51   ` Paolo Abeni
  2025-09-09 13:54 ` [PATCH net-next v10 0/7] bonding: Extend arp_ip_target format to allow for a list of vlan tags Nikolay Aleksandrov
  7 siblings, 1 reply; 22+ messages in thread
From: David Wilder @ 2025-09-04 22:18 UTC (permalink / raw)
  To: netdev
  Cc: jv, wilder, pradeeps, pradeep, i.maximets, amorenoz, haliu,
	stephen, horms

This selftest provided a functional test for the arp_ip_target parameter
both with and without user supplied vlan tags.

and

Updates to the bonding documentation.

Signed-off-by: David Wilder <wilder@us.ibm.com>
---
 Documentation/networking/bonding.rst          |  11 ++
 .../selftests/drivers/net/bonding/Makefile    |   3 +-
 .../drivers/net/bonding/bond-arp-ip-target.sh | 180 ++++++++++++++++++
 .../selftests/drivers/net/bonding/config      |   1 +
 4 files changed, 194 insertions(+), 1 deletion(-)
 create mode 100755 tools/testing/selftests/drivers/net/bonding/bond-arp-ip-target.sh

diff --git a/Documentation/networking/bonding.rst b/Documentation/networking/bonding.rst
index a2b42ae719d2..a40debdbad57 100644
--- a/Documentation/networking/bonding.rst
+++ b/Documentation/networking/bonding.rst
@@ -313,6 +313,17 @@ arp_ip_target
 	maximum number of targets that can be specified is 16.  The
 	default value is no IP addresses.
 
+        When an arp_ip_target is configured the bonding driver will
+        attempt to automatically determine what vlans the arp probe will
+        pass through. This process of gathering vlan tags is required
+        for the arp probe to be sent. However, in some configurations
+        this process may fail. In these cases you may manually
+        supply a list of vlan tags. To specify a list of vlan tags
+        append the ipv4 address with [tag1/tag2...]. For example:
+        arp_ip_target=10.0.0.1[10]. If you simply need to disable the
+        vlan discovery process you may provide an empty list, for example:
+        arp_ip_target=10.0.0.1[].
+
 ns_ip6_target
 
 	Specifies the IPv6 addresses to use as IPv6 monitoring peers when
diff --git a/tools/testing/selftests/drivers/net/bonding/Makefile b/tools/testing/selftests/drivers/net/bonding/Makefile
index 44b98f17f8ff..b7d5e11fc9d3 100644
--- a/tools/testing/selftests/drivers/net/bonding/Makefile
+++ b/tools/testing/selftests/drivers/net/bonding/Makefile
@@ -11,7 +11,8 @@ TEST_PROGS := \
 	bond_options.sh \
 	bond-eth-type-change.sh \
 	bond_macvlan_ipvlan.sh \
-	bond_passive_lacp.sh
+	bond_passive_lacp.sh \
+	bond-arp-ip-target.sh
 
 TEST_FILES := \
 	lag_lib.sh \
diff --git a/tools/testing/selftests/drivers/net/bonding/bond-arp-ip-target.sh b/tools/testing/selftests/drivers/net/bonding/bond-arp-ip-target.sh
new file mode 100755
index 000000000000..124bfb6a2f02
--- /dev/null
+++ b/tools/testing/selftests/drivers/net/bonding/bond-arp-ip-target.sh
@@ -0,0 +1,180 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Test bonding arp_ip_target.
+# Topology for Bond mode 1,5,6 testing
+#
+#  +-------------------------+
+#  |                         | Server
+#  |        bond0.10.20      | 192.20.2.1/24
+#  |            |            |
+#  |         bond0.10        | 192.10.2.1/24
+#  |            |            |
+#  |          bond0          | 192.0.2.1/24
+#  |            |            |
+#  |            +            |
+#  |      eth0  |  eth1      |
+#  |        +---+---+        |
+#  |        |       |        |
+#  +-------------------------+
+#           |       |
+#  +-------------------------+
+#  |        |       |        |
+#  |    +---+-------+---+    |  Gateway
+#  |    |      br0      |    |
+#  |    +-------+-------+    |
+#  |            |            |
+#  +-------------------------+
+#               |
+#  +-------------------------+
+#  |            |            |  Client
+#  |          eth0           | 192.0.0.2/24
+#  |            |            |
+#  |         eth0.10         | 192.10.10.2/24
+#  |            |            |
+#  |        eth0.10.20       | 192.20.20.2/24
+#  +-------------------------+
+
+# shellcheck disable=SC2317
+
+lib_dir=$(dirname "$0")
+
+# shellcheck source=/dev/null # Ignore source warning.
+source "${lib_dir}"/bond_topo_2d1c.sh
+
+# shellcheck disable=SC2154 # Ignore unassigned referenced warning.
+echo "${c_ns}" "${s_ns}" > /dev/null
+
+DEBUG=${DEBUG:-0}
+test "${DEBUG}" -ne 0 && set -x
+
+# vlan subnets
+c_ip4="192.0.2.10"
+c_ip4v10="192.10.2.10"
+c_ip4v20="192.20.2.10"
+
+export ALL_TESTS="
+    no_vlan_hints
+    with_vlan_hints
+"
+
+# Build stacked vlans on top of an interface.
+stack_vlans()
+{
+    RET=0
+    local interface="$1"
+    local ns=$2
+    local last="$interface"
+    local tags="10 20"
+
+    if ! ip -n "${ns}" link show "${interface}" > /dev/null; then
+        RET=1
+        msg="Failed to create ${interface}"
+        return
+    fi
+
+    if [ "$ns" == "${s_ns}" ]; then host=1; else host=10;fi
+
+    for tag in $tags; do
+        ip -n "${ns}" link add link "$last" name "$last"."$tag" type vlan id "$tag"
+        ip -n "${ns}" address add 192."$tag".2."$host"/24 dev "$last"."$tag"
+        ip -n "${ns}" link set up dev "$last"."$tag"
+        last=$last.$tag
+    done
+}
+
+# Check for link flapping
+check_failure_count()
+{
+    RET=0
+    local ns=$1
+    local proc_file=/proc/net/bonding/$2
+    local  counts
+
+    # Give the bond time to settle.
+    sleep 10
+
+    counts=$(ip netns exec "${ns}" grep -F "Link Failure Count" "${proc_file}" \
+	    | awk -F: '{print $2}')
+
+    local i
+    for i in $counts; do
+        [ "$i" != 0 ] && RET=1
+    done
+}
+
+setup_bond_topo()
+{
+    setup_prepare
+    setup_wait
+    stack_vlans bond0 "${s_ns}"
+    stack_vlans eth0 "${c_ns}"
+}
+
+skip_with_vlan_hints()
+{
+    # check if iproute supports arp_ip_target with vlans option.
+    if ! ip -n "${s_ns}" link add bond2 type bond arp_ip_target 10.0.0.1[10]; then
+        ip -n "${s_ns}" link del bond2 2> /dev/null
+        return 0
+    fi
+    return 1
+}
+
+no_vlan_hints()
+{
+        RET=0
+        local targets="${c_ip4} ${c_ip4v10} ${c_ip4v20}"
+        local target
+        msg=""
+
+        for target in $targets; do
+                bond_reset "mode $mode arp_interval 100 arp_ip_target ${target}"
+
+                stack_vlans bond0 "${s_ns}"
+                if [ "$RET" -ne 0 ]; then
+                    log_test "no_vlan_hints" "${msg}"
+                    return
+                fi
+
+                check_failure_count "${s_ns}" bond0
+                log_test "arp_ip_target=${target} ${msg}"
+        done
+}
+
+with_vlan_hints()
+{
+        RET=0
+        local targets="${c_ip4}[] ${c_ip4v10}[10] ${c_ip4v20}[10/20]"
+        local target
+        msg=""
+
+        if skip_with_vlan_hints; then
+            log_test_skip "skip_with_vlan_hints" \
+	          "Installed iproute doesn't support extended arp_ip_target options."
+            return 0
+        fi
+
+        for target in $targets; do
+                bond_reset "mode $mode arp_interval 100 arp_ip_target ${target}"
+
+                stack_vlans bond0 "${s_ns}"
+                if [ "$RET" -ne 0 ]; then
+                    log_test "no_vlan_hints" "${msg}"
+                    return
+                fi
+
+                check_failure_count "${s_ns}" bond0
+                log_test "arp_ip_target=${target} ${msg}"
+        done
+}
+
+
+trap cleanup EXIT
+
+mode=active-backup
+
+setup_bond_topo
+tests_run
+
+exit "$EXIT_STATUS"
diff --git a/tools/testing/selftests/drivers/net/bonding/config b/tools/testing/selftests/drivers/net/bonding/config
index 4d16a69ffc65..90e9d7b19d98 100644
--- a/tools/testing/selftests/drivers/net/bonding/config
+++ b/tools/testing/selftests/drivers/net/bonding/config
@@ -10,3 +10,4 @@ CONFIG_NET_CLS_MATCHALL=m
 CONFIG_NET_SCH_INGRESS=y
 CONFIG_NLMON=y
 CONFIG_VETH=y
+CONFIG_VLAN_8021Q=y
-- 
2.50.1


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* Re: [PATCH net-next v10 4/7] bonding: Processing extended arp_ip_target from user space.
  2025-09-04 22:18 ` [PATCH net-next v10 4/7] bonding: Processing extended arp_ip_target from user space David Wilder
@ 2025-09-09 13:37   ` Paolo Abeni
  2025-09-09 18:42   ` Nikolay Aleksandrov
  1 sibling, 0 replies; 22+ messages in thread
From: Paolo Abeni @ 2025-09-09 13:37 UTC (permalink / raw)
  To: David Wilder, netdev
  Cc: jv, pradeeps, pradeep, i.maximets, amorenoz, haliu, stephen,
	horms

On 9/5/25 12:18 AM, David Wilder wrote:
> diff --git a/drivers/net/bonding/bond_options.c b/drivers/net/bonding/bond_options.c
> index cf4cb301a738..61334633403d 100644
> --- a/drivers/net/bonding/bond_options.c
> +++ b/drivers/net/bonding/bond_options.c
> @@ -31,8 +31,8 @@ static int bond_option_use_carrier_set(struct bonding *bond,
>  				       const struct bond_opt_value *newval);
>  static int bond_option_arp_interval_set(struct bonding *bond,
>  					const struct bond_opt_value *newval);
> -static int bond_option_arp_ip_target_add(struct bonding *bond, __be32 target);
> -static int bond_option_arp_ip_target_rem(struct bonding *bond, __be32 target);
> +static int bond_option_arp_ip_target_add(struct bonding *bond, struct bond_arp_target target);
> +static int bond_option_arp_ip_target_rem(struct bonding *bond, struct bond_arp_target target);

Above and elsewhere:

'struct bond_arp_target' -> 'const struct bond_arp_target *'

Side note, I replied by mistake to older revision of this series:

https://lore.kernel.org/netdev/dbc791a9-7b87-42a8-abba-fa63e5812008@redhat.com/T/#u
https://lore.kernel.org/netdev/8c0b5b0a-60ee-4ed4-b439-11d5c106ac6e@redhat.com/T/#u
https://lore.kernel.org/netdev/add4dcf4-b3c2-40dd-bc2f-de80619e7c6f@redhat.com/T/#u

but the comments there apply to v10.

> @@ -1214,30 +1216,77 @@ static int bond_option_arp_ip_target_rem(struct bonding *bond, __be32 target)
>  			targets_rx[i] = targets_rx[i+1];
>  		targets_rx[i] = 0;
>  	}
> -	for (i = ind; (i < BOND_MAX_ARP_TARGETS - 1) && targets[i + 1].target_ip; i++)
> -		targets[i] = targets[i+1];
> +
> +	bond_free_vlan_tag(&targets[ind]);
> +
> +	for (i = ind; (i < BOND_MAX_ARP_TARGETS - 1) && targets[i + 1].target_ip; i++) {
> +		targets[i].target_ip = targets[i + 1].target_ip;
> +		targets[i].tags = targets[i + 1].tags;
> +		targets[i].flags = targets[i + 1].flags;
> +	}
>  	targets[i].target_ip = 0;
> +	targets[i].flags = 0;
> +	targets[i].tags = NULL;

The above chunk of code is repeated elsewhere. Possibly factoring out
the initialization in an helper could be useful.

/P


^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH net-next v10 5/7] bonding: Update to bond_arp_send_all() to use supplied vlan tags
  2025-09-04 22:18 ` [PATCH net-next v10 5/7] bonding: Update to bond_arp_send_all() to use supplied vlan tags David Wilder
@ 2025-09-09 13:39   ` Paolo Abeni
  2025-09-09 18:55   ` Nikolay Aleksandrov
  1 sibling, 0 replies; 22+ messages in thread
From: Paolo Abeni @ 2025-09-09 13:39 UTC (permalink / raw)
  To: David Wilder, netdev
  Cc: jv, pradeeps, pradeep, i.maximets, amorenoz, haliu, stephen,
	horms

On 9/5/25 12:18 AM, David Wilder wrote:
> @@ -6633,7 +6638,6 @@ static void __exit bonding_exit(void)
>  
>  	bond_netlink_fini();
>  	unregister_pernet_subsys(&bond_net_ops);
> -
>  	bond_destroy_debugfs();
>  
>  #ifdef CONFIG_NET_POLL_CONTROLLER

Please avoid unneeded whitespace-only changes.

/P


^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH net-next v10 6/7] bonding: Update for extended arp_ip_target format.
  2025-09-04 22:18 ` [PATCH net-next v10 6/7] bonding: Update for extended arp_ip_target format David Wilder
@ 2025-09-09 13:46   ` Paolo Abeni
  2025-09-09 18:45   ` Nikolay Aleksandrov
  1 sibling, 0 replies; 22+ messages in thread
From: Paolo Abeni @ 2025-09-09 13:46 UTC (permalink / raw)
  To: David Wilder, netdev
  Cc: jv, pradeeps, pradeep, i.maximets, amorenoz, haliu, stephen,
	horms

On 9/5/25 12:18 AM, David Wilder wrote:
> diff --git a/drivers/net/bonding/bond_netlink.c b/drivers/net/bonding/bond_netlink.c
> index 28ee50ddf4e2..1f0d3269a0b1 100644
> --- a/drivers/net/bonding/bond_netlink.c
> +++ b/drivers/net/bonding/bond_netlink.c
> @@ -660,6 +660,7 @@ static int bond_fill_info(struct sk_buff *skb,
>  			  const struct net_device *bond_dev)
>  {
>  	struct bonding *bond = netdev_priv(bond_dev);
> +	struct bond_arp_target *arptargets;
>  	unsigned int packets_per_slave;
>  	int ifindex, i, targets_added;
>  	struct nlattr *targets;
> @@ -698,12 +699,31 @@ static int bond_fill_info(struct sk_buff *skb,
>  		goto nla_put_failure;
>  
>  	targets_added = 0;
> -	for (i = 0; i < BOND_MAX_ARP_TARGETS; i++) {
> -		if (bond->params.arp_targets[i].target_ip) {
> -			if (nla_put_be32(skb, i, bond->params.arp_targets[i].target_ip))
> -				goto nla_put_failure;
> -			targets_added = 1;
> +
> +	arptargets = bond->params.arp_targets;
> +	for (i = 0; i < BOND_MAX_ARP_TARGETS && arptargets[i].target_ip ; i++) {
> +		struct Data {

Please avoid camel-case names.

/P


^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH net-next v10 7/7] bonding: Selftest and documentation for the arp_ip_target parameter.
  2025-09-04 22:18 ` [PATCH net-next v10 7/7] bonding: Selftest and documentation for the arp_ip_target parameter David Wilder
@ 2025-09-09 13:51   ` Paolo Abeni
  0 siblings, 0 replies; 22+ messages in thread
From: Paolo Abeni @ 2025-09-09 13:51 UTC (permalink / raw)
  To: David Wilder, netdev
  Cc: jv, pradeeps, pradeep, i.maximets, amorenoz, haliu, stephen,
	horms

On 9/5/25 12:18 AM, David Wilder wrote:
> This selftest provided a functional test for the arp_ip_target parameter
> both with and without user supplied vlan tags.
> 
> and
> 
> Updates to the bonding documentation.

Quite a strange formatting choice for the commit message.

> Signed-off-by: David Wilder <wilder@us.ibm.com>
> ---
>  Documentation/networking/bonding.rst          |  11 ++
>  .../selftests/drivers/net/bonding/Makefile    |   3 +-
>  .../drivers/net/bonding/bond-arp-ip-target.sh | 180 ++++++++++++++++++
>  .../selftests/drivers/net/bonding/config      |   1 +
>  4 files changed, 194 insertions(+), 1 deletion(-)
>  create mode 100755 tools/testing/selftests/drivers/net/bonding/bond-arp-ip-target.sh

Does not apply cleanly anymore, please rebase.

[...]
> +# Check for link flapping
> +check_failure_count()
> +{
> +    RET=0
> +    local ns=$1
> +    local proc_file=/proc/net/bonding/$2
> +    local  counts
> +
> +    # Give the bond time to settle.
> +    sleep 10

Can you replace the above sleep with a loop explicitly checking for the
'settled' condition up to a longer interval? It will make the test both
more robust and faster.

/P


^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH net-next v10 0/7] bonding: Extend arp_ip_target format to allow for a list of vlan tags.
  2025-09-04 22:18 [PATCH net-next v10 0/7] bonding: Extend arp_ip_target format to allow for a list of vlan tags David Wilder
                   ` (6 preceding siblings ...)
  2025-09-04 22:18 ` [PATCH net-next v10 7/7] bonding: Selftest and documentation for the arp_ip_target parameter David Wilder
@ 2025-09-09 13:54 ` Nikolay Aleksandrov
  2025-09-09 17:55   ` David Wilder
  7 siblings, 1 reply; 22+ messages in thread
From: Nikolay Aleksandrov @ 2025-09-09 13:54 UTC (permalink / raw)
  To: David Wilder, netdev
  Cc: jv, pradeeps, pradeep, i.maximets, amorenoz, haliu, stephen,
	horms

On 9/5/25 01:18, David Wilder wrote:
> The current implementation of the arp monitor builds a list of vlan-tags by
> following the chain of net_devices above the bond. See bond_verify_device_path().
> Unfortunately, with some configurations, this is not possible. One example is
> when an ovs switch is configured above the bond.
> 
> This change extends the "arp_ip_target" parameter format to allow for a list of
> vlan tags to be included for each arp target. This new list of tags is optional
> and may be omitted to preserve the current format and process of discovering
> vlans.
> 
> The new format for arp_ip_target is:
> arp_ip_target ipv4-address[vlan-tag\...],...
> 
> For example:
> arp_ip_target 10.0.0.1[10/20]
> arp_ip_target 10.0.0.1[] (used to disable vlan discovery)
> 
> Change since V9
> Fix kdoc build error.
> 
> Changes since V8:
> Moved the #define BOND_MAX_VLAN_TAGS from patch 6 to patch 3.
> Thanks Simon for catching the bisection break.
> 
> Changes since V7:
> These changes should eliminate the CI failures I have been seeing.
> 1) patch 2, changed type of bond_opt_value.extra_len to size_t.
> 2) Patch 4, added bond_validate_tags() to validate the array of bond_vlan_tag provided by
>   the user.
> 
> Changes since V6:
> 1) I made a number of changes to fix the failure seen in the
> kernel CI.  I am still unable to reproduce the this failure, hopefully I
> have fixed it.  These change are in patch #4 to functions:
> bond_option_arp_ip_targets_clear() and
> bond_option_arp_ip_targets_set()
> 
> Changes since V5: Only the last 2 patches have changed since V5.
> 1) Fixed sparse warning in bond_fill_info().
> 2) Also in bond_fill_info() I resolved data.addr uninitialized when if condition is not met.
> Thank you Simon for catching this. Note: The change is different that what I shared earlier.
> 3) Fixed shellcheck warnings in test script: Blocked source warning, Ignored specific unassigned
> references and exported ALL_TESTS to resolve a reference warning.
> 
> Changes since V4:
> 1)Dropped changes to proc and sysfs APIs to bonding.  These APIs
> do not need to be updated to support new functionality.  Netlink
> and iproute2 have been updated to do the right thing, but the
> other APIs are more or less frozen in the past.
> 
> 2)Jakub reported a warning triggered in bond_info_seq_show() during
> testing.  I was unable to reproduce this warning or identify
> it with code inspection.  However, all my changes to bond_info_seq_show()
> have been dropped as unnecessary (see above).
> Hopefully this will resolve the issue.
> 
> 3)Selftest script has been updated based on the results of shellcheck.
> Two unresolved references that are not possible to resolve are all
> that remain.
> 
> 4)A patch was added updating bond_info_fill()
> to support "ip -d show <bond-device>" command.
> 
> The inclusion of a list of vlan tags is optional. The new logic
> preserves both forward and backward compatibility with the kernel
> and iproute2 versions.
> 
> Changes since V3:
> 1) Moved the parsing of the extended arp_ip_target out of the kernel and into
>     userspace (ip command). A separate patch to iproute2 to follow shortly.
> 2) Split up the patch set to make review easier.
> 
> Please see iproute changes in a separate posting.
> 
> Thank you for your time and reviews.
> 
> Signed-off-by: David Wilder <wilder@us.ibm.com>
> 
> David Wilder (7):
>    bonding: Adding struct bond_arp_target
>    bonding: Adding extra_len field to struct bond_opt_value.
>    bonding: arp_ip_target helpers.
>    bonding: Processing extended arp_ip_target from user space.
>    bonding: Update to bond_arp_send_all() to use supplied vlan tags
>    bonding: Update for extended arp_ip_target format.
>    bonding: Selftest and documentation for the arp_ip_target parameter.
> 
>   Documentation/networking/bonding.rst          |  11 ++
>   drivers/net/bonding/bond_main.c               |  47 +++--
>   drivers/net/bonding/bond_netlink.c            |  35 +++-
>   drivers/net/bonding/bond_options.c            | 135 +++++++++----
>   drivers/net/bonding/bond_procfs.c             |   4 +-
>   drivers/net/bonding/bond_sysfs.c              |   4 +-
>   include/net/bond_options.h                    |  29 ++-
>   include/net/bonding.h                         |  61 +++++-
>   .../selftests/drivers/net/bonding/Makefile    |   3 +-
>   .../drivers/net/bonding/bond-arp-ip-target.sh | 180 ++++++++++++++++++
>   .../selftests/drivers/net/bonding/config      |   1 +
>   11 files changed, 433 insertions(+), 77 deletions(-)
>   create mode 100755 tools/testing/selftests/drivers/net/bonding/bond-arp-ip-target.sh
> 

I'm sorry I'm late (v10) to the party, but I keep wondering:
Why keep extending sysfs support? It is supposed to be deprecated and most
of this set adds changes around bond sysfs option handling to parse a new format.

IMHO this new extension should be available through netlink only, that is much
simpler, less error-prone and doesn't require string parsing. At worst sysfs
should only show the values properly.

Cheers,
  Nik






^ permalink raw reply	[flat|nested] 22+ messages in thread

* RE: [PATCH net-next v10 0/7] bonding: Extend arp_ip_target format to allow for a list of vlan tags.
  2025-09-09 13:54 ` [PATCH net-next v10 0/7] bonding: Extend arp_ip_target format to allow for a list of vlan tags Nikolay Aleksandrov
@ 2025-09-09 17:55   ` David Wilder
  2025-09-09 18:04     ` Nikolay Aleksandrov
  0 siblings, 1 reply; 22+ messages in thread
From: David Wilder @ 2025-09-09 17:55 UTC (permalink / raw)
  To: Nikolay Aleksandrov, netdev@vger.kernel.org
  Cc: jv@jvosburgh.net, pradeeps@linux.vnet.ibm.com,
	Pradeep Satyanarayana, i.maximets@ovn.org, Adrian Moreno Zapata,
	Hangbin Liu, stephen@networkplumber.org, horms@kernel.org




________________________________________
From: Nikolay Aleksandrov <razor@blackwall.org>
Sent: Tuesday, September 9, 2025 6:54 AM
To: David Wilder; netdev@vger.kernel.org
Cc: jv@jvosburgh.net; pradeeps@linux.vnet.ibm.com; Pradeep Satyanarayana; i.maximets@ovn.org; Adrian Moreno Zapata; Hangbin Liu; stephen@networkplumber.org; horms@kernel.org
Subject: [EXTERNAL] Re: [PATCH net-next v10 0/7] bonding: Extend arp_ip_target format to allow for a list of vlan tags.

> On 9/5/25 01:18, David Wilder wrote:
> > Changes since V4:
> > 1)Dropped changes to proc and sysfs APIs to bonding.  These APIs
> > do not need to be updated to support new functionality.  Netlink
> > and iproute2 have been updated to do the right thing, but the
> > other APIs are more or less frozen in the past.
> >
>
> I'm sorry I'm late (v10) to the party, but I keep wondering:
> Why keep extending sysfs support? It is supposed to be deprecated and most
> of this set adds changes around bond sysfs option handling to parse a new format.
>
> IMHO this new extension should be available through netlink only, that is much
> simpler, less error-prone and doesn't require string parsing. At worst sysfs
> should only show the values properly.
>
> Cheers,
> Nik

Hi Nic
Thanks for the reviewing my patches..
I did originally extend the sysfs to support the extension, but dropped that support.
The only remaining change related to sysfs  keeps the original support working with out 
the new extension.







^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH net-next v10 0/7] bonding: Extend arp_ip_target format to allow for a list of vlan tags.
  2025-09-09 17:55   ` David Wilder
@ 2025-09-09 18:04     ` Nikolay Aleksandrov
  0 siblings, 0 replies; 22+ messages in thread
From: Nikolay Aleksandrov @ 2025-09-09 18:04 UTC (permalink / raw)
  To: David Wilder, netdev@vger.kernel.org
  Cc: jv@jvosburgh.net, pradeeps@linux.vnet.ibm.com,
	Pradeep Satyanarayana, i.maximets@ovn.org, Adrian Moreno Zapata,
	Hangbin Liu, stephen@networkplumber.org, horms@kernel.org

On 9/9/25 20:55, David Wilder wrote:
> 
> 
> 
> ________________________________________
> From: Nikolay Aleksandrov <razor@blackwall.org>
> Sent: Tuesday, September 9, 2025 6:54 AM
> To: David Wilder; netdev@vger.kernel.org
> Cc: jv@jvosburgh.net; pradeeps@linux.vnet.ibm.com; Pradeep Satyanarayana; i.maximets@ovn.org; Adrian Moreno Zapata; Hangbin Liu; stephen@networkplumber.org; horms@kernel.org
> Subject: [EXTERNAL] Re: [PATCH net-next v10 0/7] bonding: Extend arp_ip_target format to allow for a list of vlan tags.
> 
>> On 9/5/25 01:18, David Wilder wrote:
>>> Changes since V4:
>>> 1)Dropped changes to proc and sysfs APIs to bonding.  These APIs
>>> do not need to be updated to support new functionality.  Netlink
>>> and iproute2 have been updated to do the right thing, but the
>>> other APIs are more or less frozen in the past.
>>>
>>
>> I'm sorry I'm late (v10) to the party, but I keep wondering:
>> Why keep extending sysfs support? It is supposed to be deprecated and most
>> of this set adds changes around bond sysfs option handling to parse a new format.
>>
>> IMHO this new extension should be available through netlink only, that is much
>> simpler, less error-prone and doesn't require string parsing. At worst sysfs
>> should only show the values properly.
>>
>> Cheers,
>> Nik
> 
> Hi Nic
> Thanks for the reviewing my patches..
> I did originally extend the sysfs to support the extension, but dropped that support.
> The only remaining change related to sysfs  keeps the original support working with out
> the new extension.
> 
> 

Ohhh my bad, I need better glasses. :) I read the cover letter and saw the new format, missed
the part where sysfs was dropped. Sorry for the noise!

I do have one comment, but I'll add that to the respective patch.

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH net-next v10 4/7] bonding: Processing extended arp_ip_target from user space.
  2025-09-04 22:18 ` [PATCH net-next v10 4/7] bonding: Processing extended arp_ip_target from user space David Wilder
  2025-09-09 13:37   ` Paolo Abeni
@ 2025-09-09 18:42   ` Nikolay Aleksandrov
  2025-09-10 21:19     ` David Wilder
  1 sibling, 1 reply; 22+ messages in thread
From: Nikolay Aleksandrov @ 2025-09-09 18:42 UTC (permalink / raw)
  To: David Wilder, netdev
  Cc: jv, pradeeps, pradeep, i.maximets, amorenoz, haliu, stephen,
	horms

On 9/5/25 01:18, David Wilder wrote:
> Changes to bond_netlink and bond_options to process extended
> format arp_ip_target option sent from user space via the ip
> command.
> 
> The extended format adds a list of vlan tags to the ip target address.
> 
> Signed-off-by: David Wilder <wilder@us.ibm.com>
> ---
>   drivers/net/bonding/bond_netlink.c |   5 +-
>   drivers/net/bonding/bond_options.c | 121 +++++++++++++++++++++++------
>   2 files changed, 99 insertions(+), 27 deletions(-)
> 
> diff --git a/drivers/net/bonding/bond_netlink.c b/drivers/net/bonding/bond_netlink.c
> index a5887254ff23..28ee50ddf4e2 100644
> --- a/drivers/net/bonding/bond_netlink.c
> +++ b/drivers/net/bonding/bond_netlink.c
> @@ -291,9 +291,10 @@ static int bond_changelink(struct net_device *bond_dev, struct nlattr *tb[],
>   			if (nla_len(attr) < sizeof(target))
>   				return -EINVAL;
>   
> -			target = nla_get_be32(attr);
> +			bond_opt_initextra(&newval,
> +					   (__force void *)nla_data(attr),
> +					   nla_len(attr));
>   
> -			bond_opt_initval(&newval, (__force u64)target);
>   			err = __bond_opt_set(bond, BOND_OPT_ARP_TARGETS,
>   					     &newval,
>   					     data[IFLA_BOND_ARP_IP_TARGET],
> diff --git a/drivers/net/bonding/bond_options.c b/drivers/net/bonding/bond_options.c
> index cf4cb301a738..61334633403d 100644
> --- a/drivers/net/bonding/bond_options.c
> +++ b/drivers/net/bonding/bond_options.c
> @@ -31,8 +31,8 @@ static int bond_option_use_carrier_set(struct bonding *bond,
>   				       const struct bond_opt_value *newval);
>   static int bond_option_arp_interval_set(struct bonding *bond,
>   					const struct bond_opt_value *newval);
> -static int bond_option_arp_ip_target_add(struct bonding *bond, __be32 target);
> -static int bond_option_arp_ip_target_rem(struct bonding *bond, __be32 target);
> +static int bond_option_arp_ip_target_add(struct bonding *bond, struct bond_arp_target target);
> +static int bond_option_arp_ip_target_rem(struct bonding *bond, struct bond_arp_target target);
>   static int bond_option_arp_ip_targets_set(struct bonding *bond,
>   					  const struct bond_opt_value *newval);
>   static int bond_option_ns_ip6_targets_set(struct bonding *bond,
> @@ -1133,7 +1133,7 @@ static int bond_option_arp_interval_set(struct bonding *bond,
>   }
>   
>   static void _bond_options_arp_ip_target_set(struct bonding *bond, int slot,
> -					    __be32 target,
> +					    struct bond_arp_target target,
>   					    unsigned long last_rx)
>   {
>   	struct bond_arp_target *targets = bond->params.arp_targets;
> @@ -1143,24 +1143,25 @@ static void _bond_options_arp_ip_target_set(struct bonding *bond, int slot,
>   	if (slot >= 0 && slot < BOND_MAX_ARP_TARGETS) {
>   		bond_for_each_slave(bond, slave, iter)
>   			slave->target_last_arp_rx[slot] = last_rx;
> -		targets[slot].target_ip = target;
> +		memcpy(&targets[slot], &target, sizeof(target));
>   	}
>   }
>   
> -static int _bond_option_arp_ip_target_add(struct bonding *bond, __be32 target)
> +static int _bond_option_arp_ip_target_add(struct bonding *bond, struct bond_arp_target target)
>   {
>   	struct bond_arp_target *targets = bond->params.arp_targets;
> +	char pbuf[BOND_OPTION_STRING_MAX_SIZE];
>   	int ind;
>   
> -	if (!bond_is_ip_target_ok(target)) {
> +	if (!bond_is_ip_target_ok(target.target_ip)) {
>   		netdev_err(bond->dev, "invalid ARP target %pI4 specified for addition\n",
> -			   &target);
> +			   &target.target_ip);
>   		return -EINVAL;
>   	}
>   
> -	if (bond_get_targets_ip(targets, target) != -1) { /* dup */
> +	if (bond_get_targets_ip(targets, target.target_ip) != -1) { /* dup */
>   		netdev_err(bond->dev, "ARP target %pI4 is already present\n",
> -			   &target);
> +			   &target.target_ip);
>   		return -EINVAL;
>   	}
>   
> @@ -1170,43 +1171,44 @@ static int _bond_option_arp_ip_target_add(struct bonding *bond, __be32 target)
>   		return -EINVAL;
>   	}
>   
> -	netdev_dbg(bond->dev, "Adding ARP target %pI4\n", &target);
> +	netdev_dbg(bond->dev, "Adding ARP target %s\n",
> +		   bond_arp_target_to_string(&target, pbuf, sizeof(pbuf)));
>   
>   	_bond_options_arp_ip_target_set(bond, ind, target, jiffies);
>   
>   	return 0;
>   }
>   
> -static int bond_option_arp_ip_target_add(struct bonding *bond, __be32 target)
> +static int bond_option_arp_ip_target_add(struct bonding *bond, struct bond_arp_target target)
>   {
>   	return _bond_option_arp_ip_target_add(bond, target);
>   }
>   
> -static int bond_option_arp_ip_target_rem(struct bonding *bond, __be32 target)
> +static int bond_option_arp_ip_target_rem(struct bonding *bond, struct bond_arp_target target)
>   {
>   	struct bond_arp_target *targets = bond->params.arp_targets;
> +	unsigned long *targets_rx;
>   	struct list_head *iter;
>   	struct slave *slave;
> -	unsigned long *targets_rx;
>   	int ind, i;
>   
> -	if (!bond_is_ip_target_ok(target)) {
> +	if (!bond_is_ip_target_ok(target.target_ip)) {
>   		netdev_err(bond->dev, "invalid ARP target %pI4 specified for removal\n",
> -			   &target);
> +			   &target.target_ip);
>   		return -EINVAL;
>   	}
>   
> -	ind = bond_get_targets_ip(targets, target);
> +	ind = bond_get_targets_ip(targets, target.target_ip);
>   	if (ind == -1) {
>   		netdev_err(bond->dev, "unable to remove nonexistent ARP target %pI4\n",
> -			   &target);
> +			   &target.target_ip);
>   		return -EINVAL;
>   	}
>   
>   	if (ind == 0 && !targets[1].target_ip && bond->params.arp_interval)
>   		netdev_warn(bond->dev, "Removing last arp target with arp_interval on\n");
>   
> -	netdev_dbg(bond->dev, "Removing ARP target %pI4\n", &target);
> +	netdev_dbg(bond->dev, "Removing ARP target %pI4\n", &target.target_ip);
>   
>   	bond_for_each_slave(bond, slave, iter) {
>   		targets_rx = slave->target_last_arp_rx;
> @@ -1214,30 +1216,77 @@ static int bond_option_arp_ip_target_rem(struct bonding *bond, __be32 target)
>   			targets_rx[i] = targets_rx[i+1];
>   		targets_rx[i] = 0;
>   	}
> -	for (i = ind; (i < BOND_MAX_ARP_TARGETS - 1) && targets[i + 1].target_ip; i++)
> -		targets[i] = targets[i+1];
> +
> +	bond_free_vlan_tag(&targets[ind]);
> +
> +	for (i = ind; (i < BOND_MAX_ARP_TARGETS - 1) && targets[i + 1].target_ip; i++) {
> +		targets[i].target_ip = targets[i + 1].target_ip;
> +		targets[i].tags = targets[i + 1].tags;
> +		targets[i].flags = targets[i + 1].flags;
> +	}
>   	targets[i].target_ip = 0;
> +	targets[i].flags = 0;
> +	targets[i].tags = NULL;

This looks wrong, bond_free_vlan_tag() is kfree(tags) and then it is set to NULL but
these tags can be used with only RCU held (change is in patch 05) in a few places, there is
no synchronization and nothing to keep that code from either a use-after-free or a null
ptr dereference.

Check bond_loadbalance_arp_mon -> bond_send_validate.

>   
>   	return 0;
>   }
>   
>   void bond_option_arp_ip_targets_clear(struct bonding *bond)
>   {
> +	struct bond_arp_target empty_target;

empty_target = {} ...

>   	int i;
>   
> +	empty_target.target_ip = 0;
> +	empty_target.flags = 0;
> +	empty_target.tags = NULL;

... and you can drop these lines

> +
>   	for (i = 0; i < BOND_MAX_ARP_TARGETS; i++)
> -		_bond_options_arp_ip_target_set(bond, i, 0, 0);
> +		_bond_options_arp_ip_target_set(bond, i, empty_target, 0);
> +}
> +
> +/**
> + * bond_validate_tags - validate an array of bond_vlan_tag.
> + * @tags: the array to validate
> + * @len: the length in bytes of @tags
> + *
> + * Validate that @tags points to a valid array of struct bond_vlan_tag.
> + * Returns: the length of the validated bytes in the array or -1 if no
> + * valid list is found.
> + */
> +static int bond_validate_tags(struct bond_vlan_tag *tags, size_t len)
> +{
> +	size_t i, ntags = 0;
> +
> +	if (len == 0 || !tags)
> +		return 0;
> +
> +	for (i = 0; i <= len; i = i + sizeof(struct bond_vlan_tag)) {
> +		if (ntags > BOND_MAX_VLAN_TAGS)
> +			break;
> +
> +		if (tags->vlan_proto == BOND_VLAN_PROTO_NONE)
> +			return i + sizeof(struct bond_vlan_tag);

You suppose that there is at least sizeof(struct bond_vlan_tag) in tags
but there shouldn't be, it could be target_ip + 1 byte and here you will
be out of bounds.

> +> +		if (tags->vlan_id > 4094)

vlan tag 0 is invalid as well, and this should be using the vlan definitions
e.g. !tags->vlan_id || tags->vlan_id >= VLAN_VID_MASK

> +			break;
> +		tags++;
> +		ntags++;
> +	}
> +	return -1;
>   }
>   
>   static int bond_option_arp_ip_targets_set(struct bonding *bond,
>   					  const struct bond_opt_value *newval)
>   {
> -	int ret = -EPERM;
> -	__be32 target;
> +	size_t len = (size_t)newval->extra_len;
> +	char *extra = (char *)newval->extra;
> +	struct bond_arp_target target;
> +	int size, ret = -EPERM;
>   
>   	if (newval->string) {
> +		/* Adding or removing arp_ip_target from sysfs */
>   		if (strlen(newval->string) < 1 ||
> -		    !in4_pton(newval->string + 1, -1, (u8 *)&target, -1, NULL)) {
> +		    !in4_pton(newval->string + 1, -1, (u8 *)&target.target_ip, -1, NULL)) {
>   			netdev_err(bond->dev, "invalid ARP target specified\n");
>   			return ret;
>   		}
> @@ -1248,7 +1297,29 @@ static int bond_option_arp_ip_targets_set(struct bonding *bond,
>   		else
>   			netdev_err(bond->dev, "no command found in arp_ip_targets file - use +<addr> or -<addr>\n");
>   	} else {
> -		target = newval->value;
> +		/* Adding arp_ip_target from netlink. aka: ip command */
> +		if (len < sizeof(target.target_ip)) {

this check is redundant, we already validate it has at least sizeof(__be32) in bond_changelink()

> +			netdev_err(bond->dev, "invalid ARP target specified\n");
> +			return ret;
> +		}
> +		memcpy(&target.target_ip, newval->extra, sizeof(__be32));
> +		len = len - sizeof(target.target_ip);
> +		extra = extra + sizeof(target.target_ip);

len could be < sizeof(struct bond_vlan_tag), e.g. it could be 1
(see above my comment about tags len)

> +
> +		size = bond_validate_tags((struct bond_vlan_tag *)extra, len);
> +
> +		if (size > 0) {
> +			target.tags = kmalloc((size_t)size, GFP_ATOMIC);
> +			if (!target.tags)
> +				return -ENOMEM;
> +			memcpy(target.tags, extra, size);
> +			target.flags |= BOND_TARGET_USERTAGS;
> +		}

else if (size == -1)

> +
> +		if (size == -1)
> +			netdev_warn(bond->dev, "Invalid list of vlans provided with %pI4\n",
> +				    &target.target_ip);
> +
>   		ret = bond_option_arp_ip_target_add(bond, target);


I don't see target.tags freed if bond_option_arp_ip_target_add() results in an error.



^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH net-next v10 6/7] bonding: Update for extended arp_ip_target format.
  2025-09-04 22:18 ` [PATCH net-next v10 6/7] bonding: Update for extended arp_ip_target format David Wilder
  2025-09-09 13:46   ` Paolo Abeni
@ 2025-09-09 18:45   ` Nikolay Aleksandrov
  1 sibling, 0 replies; 22+ messages in thread
From: Nikolay Aleksandrov @ 2025-09-09 18:45 UTC (permalink / raw)
  To: David Wilder, netdev
  Cc: jv, pradeeps, pradeep, i.maximets, amorenoz, haliu, stephen,
	horms

On 9/5/25 01:18, David Wilder wrote:
> Updated bond_fill_info() to support extended arp_ip_target format.
> 
> Forward and backward compatibility between the kernel and iprout2 is

iproute2

> preserved.
> 
> Signed-off-by: David Wilder <wilder@us.ibm.com>
> ---
>   drivers/net/bonding/bond_netlink.c | 30 +++++++++++++++++++++++++-----
>   1 file changed, 25 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/net/bonding/bond_netlink.c b/drivers/net/bonding/bond_netlink.c
> index 28ee50ddf4e2..1f0d3269a0b1 100644
> --- a/drivers/net/bonding/bond_netlink.c
> +++ b/drivers/net/bonding/bond_netlink.c
> @@ -660,6 +660,7 @@ static int bond_fill_info(struct sk_buff *skb,
>   			  const struct net_device *bond_dev)
>   {
>   	struct bonding *bond = netdev_priv(bond_dev);
> +	struct bond_arp_target *arptargets;
>   	unsigned int packets_per_slave;
>   	int ifindex, i, targets_added;
>   	struct nlattr *targets;
> @@ -698,12 +699,31 @@ static int bond_fill_info(struct sk_buff *skb,
>   		goto nla_put_failure;
>   
>   	targets_added = 0;
> -	for (i = 0; i < BOND_MAX_ARP_TARGETS; i++) {
> -		if (bond->params.arp_targets[i].target_ip) {
> -			if (nla_put_be32(skb, i, bond->params.arp_targets[i].target_ip))
> -				goto nla_put_failure;
> -			targets_added = 1;
> +
> +	arptargets = bond->params.arp_targets;
> +	for (i = 0; i < BOND_MAX_ARP_TARGETS && arptargets[i].target_ip ; i++) {
> +		struct Data {
> +			__be32 addr;
> +			struct bond_vlan_tag vlans[BOND_MAX_VLAN_TAGS];
> +		} __packed data;
> +		int level, size;
> +
> +		data.addr = arptargets[i].target_ip;
> +		size = sizeof(__be32);
> +		targets_added = 1;
> +
> +		if (arptargets[i].flags & BOND_TARGET_USERTAGS) {
> +			for (level = 0; level < BOND_MAX_VLAN_TAGS ; level++) {
> +				data.vlans[level].vlan_proto = arptargets[i].tags[level].vlan_proto;
> +				data.vlans[level].vlan_id = arptargets[i].tags[level].vlan_id;
> +				size = size + sizeof(struct bond_vlan_tag);
> +				if (arptargets[i].tags[level].vlan_proto == BOND_VLAN_PROTO_NONE)
> +					break;
> +				}

indent seems off

>   		}
> +
> +		if (nla_put(skb, i, size, &data))
> +			goto nla_put_failure;
>   	}
>   
>   	if (targets_added)


^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH net-next v10 5/7] bonding: Update to bond_arp_send_all() to use supplied vlan tags
  2025-09-04 22:18 ` [PATCH net-next v10 5/7] bonding: Update to bond_arp_send_all() to use supplied vlan tags David Wilder
  2025-09-09 13:39   ` Paolo Abeni
@ 2025-09-09 18:55   ` Nikolay Aleksandrov
  2025-09-09 20:54     ` Jay Vosburgh
  1 sibling, 1 reply; 22+ messages in thread
From: Nikolay Aleksandrov @ 2025-09-09 18:55 UTC (permalink / raw)
  To: David Wilder, netdev
  Cc: jv, pradeeps, pradeep, i.maximets, amorenoz, haliu, stephen,
	horms

On 9/5/25 01:18, David Wilder wrote:
> bond_arp_send_all() will pass the vlan tags supplied by
> the user to bond_arp_send(). If vlan tags have not been
> supplied the vlans in the path to the target will be
> discovered by bond_verify_device_path(). The discovered
> vlan tags are then saved to be used on future calls to
> bond_arp_send().
> 
> bond_uninit() is also updated to free vlan tags when a
> bond is destroyed.
> 
> Signed-off-by: David Wilder <wilder@us.ibm.com>
> ---
>   drivers/net/bonding/bond_main.c | 22 +++++++++++++---------
>   1 file changed, 13 insertions(+), 9 deletions(-)
> 
> diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
> index 7548119ca0f3..7288f8a5f1a5 100644
> --- a/drivers/net/bonding/bond_main.c
> +++ b/drivers/net/bonding/bond_main.c
> @@ -3063,18 +3063,19 @@ struct bond_vlan_tag *bond_verify_device_path(struct net_device *start_dev,
>   
>   static void bond_arp_send_all(struct bonding *bond, struct slave *slave)
>   {
> -	struct rtable *rt;
> -	struct bond_vlan_tag *tags;
>   	struct bond_arp_target *targets = bond->params.arp_targets;
> +	char pbuf[BOND_OPTION_STRING_MAX_SIZE];
> +	struct bond_vlan_tag *tags;
>   	__be32 target_ip, addr;
> +	struct rtable *rt;
>   	int i;
>   
>   	for (i = 0; i < BOND_MAX_ARP_TARGETS && targets[i].target_ip; i++) {
>   		target_ip = targets[i].target_ip;
>   		tags = targets[i].tags;
>   
> -		slave_dbg(bond->dev, slave->dev, "%s: target %pI4\n",
> -			  __func__, &target_ip);
> +		slave_dbg(bond->dev, slave->dev, "%s: target %s\n", __func__,
> +			  bond_arp_target_to_string(&targets[i], pbuf, sizeof(pbuf)));
>   
>   		/* Find out through which dev should the packet go */
>   		rt = ip_route_output(dev_net(bond->dev), target_ip, 0, 0, 0,
> @@ -3096,9 +3097,13 @@ static void bond_arp_send_all(struct bonding *bond, struct slave *slave)
>   		if (rt->dst.dev == bond->dev)
>   			goto found;
>   
> -		rcu_read_lock();
> -		tags = bond_verify_device_path(bond->dev, rt->dst.dev, 0);
> -		rcu_read_unlock();
> +		if (!tags) {
> +			rcu_read_lock();
> +			tags = bond_verify_device_path(bond->dev, rt->dst.dev, 0);
> +			/* cache the tags */
> +			targets[i].tags = tags;
> +			rcu_read_unlock();

Surely you must be joking. You cannot overwrite the tags pointer without any synchronization.

> +		}
>   
>   		if (!IS_ERR_OR_NULL(tags))
>   			goto found;
> @@ -3114,7 +3119,6 @@ static void bond_arp_send_all(struct bonding *bond, struct slave *slave)
>   		addr = bond_confirm_addr(rt->dst.dev, target_ip, 0);
>   		ip_rt_put(rt);
>   		bond_arp_send(slave, ARPOP_REQUEST, target_ip, addr, tags);
> -		kfree(tags);
>   	}
>   }
>   
> @@ -6047,6 +6051,7 @@ static void bond_uninit(struct net_device *bond_dev)
>   	bond_for_each_slave(bond, slave, iter)
>   		__bond_release_one(bond_dev, slave->dev, true, true);
>   	netdev_info(bond_dev, "Released all slaves\n");
> +	bond_free_vlan_tags(bond->params.arp_targets);
>   
>   #ifdef CONFIG_XFRM_OFFLOAD
>   	mutex_destroy(&bond->ipsec_lock);
> @@ -6633,7 +6638,6 @@ static void __exit bonding_exit(void)
>   
>   	bond_netlink_fini();
>   	unregister_pernet_subsys(&bond_net_ops);
> -
>   	bond_destroy_debugfs();
>   
>   #ifdef CONFIG_NET_POLL_CONTROLLER


^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH net-next v10 5/7] bonding: Update to bond_arp_send_all() to use supplied vlan tags
  2025-09-09 18:55   ` Nikolay Aleksandrov
@ 2025-09-09 20:54     ` Jay Vosburgh
  2025-09-09 21:08       ` Jay Vosburgh
  0 siblings, 1 reply; 22+ messages in thread
From: Jay Vosburgh @ 2025-09-09 20:54 UTC (permalink / raw)
  To: Nikolay Aleksandrov
  Cc: David Wilder, netdev, pradeeps, pradeep, i.maximets, amorenoz,
	haliu, stephen, horms

Nikolay Aleksandrov <razor@blackwall.org> wrote:

>On 9/5/25 01:18, David Wilder wrote:
>> bond_arp_send_all() will pass the vlan tags supplied by
>> the user to bond_arp_send(). If vlan tags have not been
>> supplied the vlans in the path to the target will be
>> discovered by bond_verify_device_path(). The discovered
>> vlan tags are then saved to be used on future calls to
>> bond_arp_send().
>> bond_uninit() is also updated to free vlan tags when a
>> bond is destroyed.
>> Signed-off-by: David Wilder <wilder@us.ibm.com>
>> ---
>>   drivers/net/bonding/bond_main.c | 22 +++++++++++++---------
>>   1 file changed, 13 insertions(+), 9 deletions(-)
>> diff --git a/drivers/net/bonding/bond_main.c
>> b/drivers/net/bonding/bond_main.c
>> index 7548119ca0f3..7288f8a5f1a5 100644
>> --- a/drivers/net/bonding/bond_main.c
>> +++ b/drivers/net/bonding/bond_main.c
>> @@ -3063,18 +3063,19 @@ struct bond_vlan_tag *bond_verify_device_path(struct net_device *start_dev,
>>     static void bond_arp_send_all(struct bonding *bond, struct slave
>> *slave)
>>   {
>> -	struct rtable *rt;
>> -	struct bond_vlan_tag *tags;
>>   	struct bond_arp_target *targets = bond->params.arp_targets;
>> +	char pbuf[BOND_OPTION_STRING_MAX_SIZE];
>> +	struct bond_vlan_tag *tags;
>>   	__be32 target_ip, addr;
>> +	struct rtable *rt;
>>   	int i;
>>     	for (i = 0; i < BOND_MAX_ARP_TARGETS && targets[i].target_ip; i++)
>> {
>>   		target_ip = targets[i].target_ip;
>>   		tags = targets[i].tags;
>>   -		slave_dbg(bond->dev, slave->dev, "%s: target %pI4\n",
>> -			  __func__, &target_ip);
>> +		slave_dbg(bond->dev, slave->dev, "%s: target %s\n", __func__,
>> +			  bond_arp_target_to_string(&targets[i], pbuf, sizeof(pbuf)));
>>     		/* Find out through which dev should the packet go */
>>   		rt = ip_route_output(dev_net(bond->dev), target_ip, 0, 0, 0,
>> @@ -3096,9 +3097,13 @@ static void bond_arp_send_all(struct bonding *bond, struct slave *slave)
>>   		if (rt->dst.dev == bond->dev)
>>   			goto found;
>>   -		rcu_read_lock();
>> -		tags = bond_verify_device_path(bond->dev, rt->dst.dev, 0);
>> -		rcu_read_unlock();
>> +		if (!tags) {
>> +			rcu_read_lock();
>> +			tags = bond_verify_device_path(bond->dev, rt->dst.dev, 0);
>> +			/* cache the tags */
>> +			targets[i].tags = tags;
>> +			rcu_read_unlock();
>
>Surely you must be joking. You cannot overwrite the tags pointer without any synchronization.

	Agreed, I think this will race with at least bond_fill_info,
_bond_options_arp_ip_target_set, and bond_option_arp_ip_target_rem.

	Also, pretending for the moment that the above isn't an issue,
does this cache handle changes in real time?  I.e., if the VLAN above
the bond is replumbed without dismantling the bond, will the above
notice and do the right thing?

	The current code checks the device path on every call, and I
don't see how it's feasible to skip that.

	Separately, a random thought while looking at the code, I feel
like there ought to be a way to replace the GFP_ATOMIC memory allocation
in bond_verify_device_path with storage local to its caller (since it's
always immediately freed), but that's probably not something to get into
for this patch set.

	-J

>> +		}
>>     		if (!IS_ERR_OR_NULL(tags))
>>   			goto found;
>> @@ -3114,7 +3119,6 @@ static void bond_arp_send_all(struct bonding *bond, struct slave *slave)
>>   		addr = bond_confirm_addr(rt->dst.dev, target_ip, 0);
>>   		ip_rt_put(rt);
>>   		bond_arp_send(slave, ARPOP_REQUEST, target_ip, addr, tags);
>> -		kfree(tags);
>>   	}
>>   }
>>   @@ -6047,6 +6051,7 @@ static void bond_uninit(struct net_device
>> *bond_dev)
>>   	bond_for_each_slave(bond, slave, iter)
>>   		__bond_release_one(bond_dev, slave->dev, true, true);
>>   	netdev_info(bond_dev, "Released all slaves\n");
>> +	bond_free_vlan_tags(bond->params.arp_targets);
>>     #ifdef CONFIG_XFRM_OFFLOAD
>>   	mutex_destroy(&bond->ipsec_lock);
>> @@ -6633,7 +6638,6 @@ static void __exit bonding_exit(void)
>>     	bond_netlink_fini();
>>   	unregister_pernet_subsys(&bond_net_ops);
>> -
>>   	bond_destroy_debugfs();
>>     #ifdef CONFIG_NET_POLL_CONTROLLER
>

---
	-Jay Vosburgh, jv@jvosburgh.net

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH net-next v10 5/7] bonding: Update to bond_arp_send_all() to use supplied vlan tags
  2025-09-09 20:54     ` Jay Vosburgh
@ 2025-09-09 21:08       ` Jay Vosburgh
  2025-09-09 21:57         ` David Wilder
  0 siblings, 1 reply; 22+ messages in thread
From: Jay Vosburgh @ 2025-09-09 21:08 UTC (permalink / raw)
  To: netdev
  Cc: Nikolay Aleksandrov, David Wilder, pradeeps, pradeep, i.maximets,
	amorenoz, haliu, stephen, horms

Jay Vosburgh <jv@jvosburgh.net> wrote:

>Nikolay Aleksandrov <razor@blackwall.org> wrote:
>
>>On 9/5/25 01:18, David Wilder wrote:
>>> bond_arp_send_all() will pass the vlan tags supplied by
>>> the user to bond_arp_send(). If vlan tags have not been
>>> supplied the vlans in the path to the target will be
>>> discovered by bond_verify_device_path(). The discovered
>>> vlan tags are then saved to be used on future calls to
>>> bond_arp_send().
>>> bond_uninit() is also updated to free vlan tags when a
>>> bond is destroyed.
>>> Signed-off-by: David Wilder <wilder@us.ibm.com>
>>> ---
>>>   drivers/net/bonding/bond_main.c | 22 +++++++++++++---------
>>>   1 file changed, 13 insertions(+), 9 deletions(-)
>>> diff --git a/drivers/net/bonding/bond_main.c
>>> b/drivers/net/bonding/bond_main.c
>>> index 7548119ca0f3..7288f8a5f1a5 100644
>>> --- a/drivers/net/bonding/bond_main.c
>>> +++ b/drivers/net/bonding/bond_main.c
>>> @@ -3063,18 +3063,19 @@ struct bond_vlan_tag *bond_verify_device_path(struct net_device *start_dev,
>>>     static void bond_arp_send_all(struct bonding *bond, struct slave
>>> *slave)
>>>   {
>>> -	struct rtable *rt;
>>> -	struct bond_vlan_tag *tags;
>>>   	struct bond_arp_target *targets = bond->params.arp_targets;
>>> +	char pbuf[BOND_OPTION_STRING_MAX_SIZE];
>>> +	struct bond_vlan_tag *tags;
>>>   	__be32 target_ip, addr;
>>> +	struct rtable *rt;
>>>   	int i;
>>>     	for (i = 0; i < BOND_MAX_ARP_TARGETS && targets[i].target_ip; i++)
>>> {
>>>   		target_ip = targets[i].target_ip;
>>>   		tags = targets[i].tags;
>>>   -		slave_dbg(bond->dev, slave->dev, "%s: target %pI4\n",
>>> -			  __func__, &target_ip);
>>> +		slave_dbg(bond->dev, slave->dev, "%s: target %s\n", __func__,
>>> +			  bond_arp_target_to_string(&targets[i], pbuf, sizeof(pbuf)));
>>>     		/* Find out through which dev should the packet go */
>>>   		rt = ip_route_output(dev_net(bond->dev), target_ip, 0, 0, 0,
>>> @@ -3096,9 +3097,13 @@ static void bond_arp_send_all(struct bonding *bond, struct slave *slave)
>>>   		if (rt->dst.dev == bond->dev)
>>>   			goto found;
>>>   -		rcu_read_lock();
>>> -		tags = bond_verify_device_path(bond->dev, rt->dst.dev, 0);
>>> -		rcu_read_unlock();
>>> +		if (!tags) {
>>> +			rcu_read_lock();
>>> +			tags = bond_verify_device_path(bond->dev, rt->dst.dev, 0);
>>> +			/* cache the tags */
>>> +			targets[i].tags = tags;
>>> +			rcu_read_unlock();
>>
>>Surely you must be joking. You cannot overwrite the tags pointer without any synchronization.
>
>	Agreed, I think this will race with at least bond_fill_info,
>_bond_options_arp_ip_target_set, and bond_option_arp_ip_target_rem.
>
>	Also, pretending for the moment that the above isn't an issue,
>does this cache handle changes in real time?  I.e., if the VLAN above
>the bond is replumbed without dismantling the bond, will the above
>notice and do the right thing?
>
>	The current code checks the device path on every call, and I
>don't see how it's feasible to skip that.

	Ok, thinking this through a little more... the point of the
patch set is to permit the user to supply the tags via option setting
for cases that bond_verify_device_path can't figure things out.  So the
tags stashed as part of the bond (i.e., provided as option settings from
user space) should only be changable from user space.

	So, I think the way it'll have to work is, if user space
provided tags then use them, otherwise call bond_verify_device_path and
use whatever it says, but throw that away after each pass.

	If user space provided tags and then replumbs things, then it'll
be on user space to update the tags, as the option is essentially
overriding the automatic lookup provided by bond_verify_device_path.

	If the tags stashed in the bond configuration can only be
changed via user space option settings, I think that can be done safely
in an RCU manner (as netlink always operates with RTNL held, if memory
serves).

	-J

>	Separately, a random thought while looking at the code, I feel
>like there ought to be a way to replace the GFP_ATOMIC memory allocation
>in bond_verify_device_path with storage local to its caller (since it's
>always immediately freed), but that's probably not something to get into
>for this patch set.
>
>	-J
>
>>> +		}
>>>     		if (!IS_ERR_OR_NULL(tags))
>>>   			goto found;
>>> @@ -3114,7 +3119,6 @@ static void bond_arp_send_all(struct bonding *bond, struct slave *slave)
>>>   		addr = bond_confirm_addr(rt->dst.dev, target_ip, 0);
>>>   		ip_rt_put(rt);
>>>   		bond_arp_send(slave, ARPOP_REQUEST, target_ip, addr, tags);
>>> -		kfree(tags);
>>>   	}
>>>   }
>>>   @@ -6047,6 +6051,7 @@ static void bond_uninit(struct net_device
>>> *bond_dev)
>>>   	bond_for_each_slave(bond, slave, iter)
>>>   		__bond_release_one(bond_dev, slave->dev, true, true);
>>>   	netdev_info(bond_dev, "Released all slaves\n");
>>> +	bond_free_vlan_tags(bond->params.arp_targets);
>>>     #ifdef CONFIG_XFRM_OFFLOAD
>>>   	mutex_destroy(&bond->ipsec_lock);
>>> @@ -6633,7 +6638,6 @@ static void __exit bonding_exit(void)
>>>     	bond_netlink_fini();
>>>   	unregister_pernet_subsys(&bond_net_ops);
>>> -
>>>   	bond_destroy_debugfs();
>>>     #ifdef CONFIG_NET_POLL_CONTROLLER
>>
>
>---
>	-Jay Vosburgh, jv@jvosburgh.net
>

---
	-Jay Vosburgh, jv@jvosburgh.net

^ permalink raw reply	[flat|nested] 22+ messages in thread

* RE: [PATCH net-next v10 5/7] bonding: Update to bond_arp_send_all() to use supplied vlan tags
  2025-09-09 21:08       ` Jay Vosburgh
@ 2025-09-09 21:57         ` David Wilder
  0 siblings, 0 replies; 22+ messages in thread
From: David Wilder @ 2025-09-09 21:57 UTC (permalink / raw)
  To: Jay Vosburgh, netdev@vger.kernel.org
  Cc: Nikolay Aleksandrov, pradeeps@linux.vnet.ibm.com,
	Pradeep Satyanarayana, i.maximets@ovn.org, Adrian Moreno Zapata,
	Hangbin Liu, stephen@networkplumber.org, horms@kernel.org




________________________________________
From: Jay Vosburgh <jv@jvosburgh.net>
Sent: Tuesday, September 9, 2025 2:08 PM
To: netdev@vger.kernel.org
Cc: Nikolay Aleksandrov; David Wilder; pradeeps@linux.vnet.ibm.com; Pradeep Satyanarayana; i.maximets@ovn.org; Adrian Moreno Zapata; Hangbin Liu; stephen@networkplumber.org; horms@kernel.org
Subject: [EXTERNAL] Re: [PATCH net-next v10 5/7] bonding: Update to bond_arp_send_all() to use supplied vlan tags

>Jay Vosburgh <jv@jvosburgh.net> wrote:
>
>>Nikolay Aleksandrov <razor@blackwall.org> wrote:
>>
>>>On 9/5/25 01:18, David Wilder wrote:
>>>> bond_arp_send_all() will pass the vlan tags supplied by
>>>> the user to bond_arp_send(). If vlan tags have not been
>>>> supplied the vlans in the path to the target will be
>>>> discovered by bond_verify_device_path(). The discovered
>>>> vlan tags are then saved to be used on future calls to
>>>> bond_arp_send().
>>>> bond_uninit() is also updated to free vlan tags when a
>>>> bond is destroyed.
>>>> Signed-off-by: David Wilder <wilder@us.ibm.com>
>>>> ---
>>>>   drivers/net/bonding/bond_main.c | 22 +++++++++++++---------
>>>>   1 file changed, 13 insertions(+), 9 deletions(-)
>>>> diff --git a/drivers/net/bonding/bond_main.c
>>>> b/drivers/net/bonding/bond_main.c
>>>> index 7548119ca0f3..7288f8a5f1a5 100644
>>>> --- a/drivers/net/bonding/bond_main.c
>>>> +++ b/drivers/net/bonding/bond_main.c
>>>> @@ -3063,18 +3063,19 @@ struct bond_vlan_tag *bond_verify_device_path(struct net_device *start_dev,
>>>>     static void bond_arp_send_all(struct bonding *bond, struct slave
>>>> *slave)
>>>>   {
>>>> -   struct rtable *rt;
>>>> -   struct bond_vlan_tag *tags;
>>>>     struct bond_arp_target *targets = bond->params.arp_targets;
>>>> +   char pbuf[BOND_OPTION_STRING_MAX_SIZE];
>>>> +   struct bond_vlan_tag *tags;
>>>>     __be32 target_ip, addr;
>>>> +   struct rtable *rt;
>>>>     int i;
>>>>             for (i = 0; i < BOND_MAX_ARP_TARGETS && targets[i].target_ip; i++)
>>>> {
>>>>             target_ip = targets[i].target_ip;
>>>>             tags = targets[i].tags;
>>>>   -         slave_dbg(bond->dev, slave->dev, "%s: target %pI4\n",
>>>> -                     __func__, &target_ip);
>>>> +           slave_dbg(bond->dev, slave->dev, "%s: target %s\n", __func__,
>>>> +                     bond_arp_target_to_string(&targets[i], pbuf, sizeof(pbuf)));
>>>>                     /* Find out through which dev should the packet go */
>>>>             rt = ip_route_output(dev_net(bond->dev), target_ip, 0, 0, 0,
>>>> @@ -3096,9 +3097,13 @@ static void bond_arp_send_all(struct bonding *bond, struct slave *slave)
>>>>             if (rt->dst.dev == bond->dev)
>>>>                     goto found;
>>>>   -         rcu_read_lock();
>>>> -           tags = bond_verify_device_path(bond->dev, rt->dst.dev, 0);
>>>> -           rcu_read_unlock();
>>>> +           if (!tags) {
>>>> +                   rcu_read_lock();
>>>> +                   tags = bond_verify_device_path(bond->dev, rt->dst.dev, 0);
>>>> +                   /* cache the tags */
>>>> +                   targets[i].tags = tags;
>>>> +                   rcu_read_unlock();
>>>
>>>Surely you must be joking. You cannot overwrite the tags pointer without any synchronization.
>>
>>       Agreed, I think this will race with at least bond_fill_info,
>>_bond_options_arp_ip_target_set, and bond_option_arp_ip_target_rem.
>>
>>       Also, pretending for the moment that the above isn't an issue,
>>does this cache handle changes in real time?  I.e., if the VLAN above
>>the bond is replumbed without dismantling the bond, will the above
>>notice and do the right thing?
>>
>>       The current code checks the device path on every call, and I
>>don't see how it's feasible to skip that.
>
>        Ok, thinking this through a little more... the point of the
>patch set is to permit the user to supply the tags via option setting
>for cases that bond_verify_device_path can't figure things out.  So the
>tags stashed as part of the bond (i.e., provided as option settings from
>user space) should only be changable from user space.
>
>        So, I think the way it'll have to work is, if user space
>provided tags then use them, otherwise call bond_verify_device_path and
>use whatever it says, but throw that away after each pass.

Agreed. Sounds like caching the dynamic tags was a bad idea.  I did not imagine the
vlan to a path could change, if that is a concern then simply removing
the caching of the tags (and freeing any non-user supplied tags later) restores
the original behavior.

-                  /* cache the tags */
-                   targets[i].tags = tags;


>
>        If user space provided tags and then replumbs things, then it'll
>be on user space to update the tags, as the option is essentially
>overriding the automatic lookup provided by bond_verify_device_path.
>
>        If the tags stashed in the bond configuration can only be
>changed via user space option settings, I think that can be done safely
>in an RCU manner (as netlink always operates with RTNL held, if memory
>serves).

Agreed, I was depending the existing RTNL lock to protect configurations
changes from the user space.
>
>        -J

-David Wilder

^ permalink raw reply	[flat|nested] 22+ messages in thread

* RE: [PATCH net-next v10 4/7] bonding: Processing extended arp_ip_target from user space.
  2025-09-09 18:42   ` Nikolay Aleksandrov
@ 2025-09-10 21:19     ` David Wilder
  0 siblings, 0 replies; 22+ messages in thread
From: David Wilder @ 2025-09-10 21:19 UTC (permalink / raw)
  To: Nikolay Aleksandrov, netdev@vger.kernel.org
  Cc: jv@jvosburgh.net, pradeeps@linux.vnet.ibm.com,
	Pradeep Satyanarayana, i.maximets@ovn.org, Adrian Moreno Zapata,
	Hangbin Liu, stephen@networkplumber.org, horms@kernel.org




________________________________________
From: Nikolay Aleksandrov <razor@blackwall.org>
Sent: Tuesday, September 9, 2025 11:42 AM
To: David Wilder; netdev@vger.kernel.org
Cc: jv@jvosburgh.net; pradeeps@linux.vnet.ibm.com; Pradeep Satyanarayana; i.maximets@ovn.org; Adrian Moreno Zapata; Hangbin Liu; stephen@networkplumber.org; horms@kernel.org
Subject: [EXTERNAL] Re: [PATCH net-next v10 4/7] bonding: Processing extended arp_ip_target from user space.

Thank you Nikolay for you review and comments.
Responses inline.

> On 9/5/25 01:18, David Wilder wrote:
> > Changes to bond_netlink and bond_options to process extended
> > format arp_ip_target option sent from user space via the ip
> > command.
> >
> > The extended format adds a list of vlan tags to the ip target address.
> >
> > Signed-off-by: David Wilder <wilder@us.ibm.com>
> > ---
> >   drivers/net/bonding/bond_netlink.c |   5 +-
> >   drivers/net/bonding/bond_options.c | 121 +++++++++++++++++++++++------
> >   2 files changed, 99 insertions(+), 27 deletions(-)
> >
> > diff --git a/drivers/net/bonding/bond_netlink.c b/drivers/net/bonding/bond_netlink.c
> > index a5887254ff23..28ee50ddf4e2 100644
> > --- a/drivers/net/bonding/bond_netlink.c
> > +++ b/drivers/net/bonding/bond_netlink.c
> > @@ -291,9 +291,10 @@ static int bond_changelink(struct net_device *bond_dev, struct nlattr *tb[],
> >                       if (nla_len(attr) < sizeof(target))
> >                               return -EINVAL;
> >
> > -                     target = nla_get_be32(attr);
> > +                     bond_opt_initextra(&newval,
> > +                                        (__force void *)nla_data(attr),
> > +                                        nla_len(attr));
> >
> > -                     bond_opt_initval(&newval, (__force u64)target);
> >                       err = __bond_opt_set(bond, BOND_OPT_ARP_TARGETS,
> >                                            &newval,
> >                                            data[IFLA_BOND_ARP_IP_TARGET],
> > diff --git a/drivers/net/bonding/bond_options.c b/drivers/net/bonding/bond_options.c
> > index cf4cb301a738..61334633403d 100644
> > --- a/drivers/net/bonding/bond_options.c
> > +++ b/drivers/net/bonding/bond_options.c
> > @@ -31,8 +31,8 @@ static int bond_option_use_carrier_set(struct bonding *bond,
> >                                      const struct bond_opt_value *newval);
> >   static int bond_option_arp_interval_set(struct bonding *bond,
> >                                       const struct bond_opt_value *newval);
> > -static int bond_option_arp_ip_target_add(struct bonding *bond, __be32 target);
> > -static int bond_option_arp_ip_target_rem(struct bonding *bond, __be32 target);
> > +static int bond_option_arp_ip_target_add(struct bonding *bond, struct bond_arp_target target);
> > +static int bond_option_arp_ip_target_rem(struct bonding *bond, struct bond_arp_target target);
> >   static int bond_option_arp_ip_targets_set(struct bonding *bond,
> >                                         const struct bond_opt_value *newval);
> >   static int bond_option_ns_ip6_targets_set(struct bonding *bond,
> > @@ -1133,7 +1133,7 @@ static int bond_option_arp_interval_set(struct bonding *bond,
> >   }
> >
> >   static void _bond_options_arp_ip_target_set(struct bonding *bond, int slot,
> > -                                         __be32 target,
> > +                                         struct bond_arp_target target,
> >                                           unsigned long last_rx)
> >   {
> >       struct bond_arp_target *targets = bond->params.arp_targets;
> > @@ -1143,24 +1143,25 @@ static void _bond_options_arp_ip_target_set(struct bonding *bond, int slot,
> >       if (slot >= 0 && slot < BOND_MAX_ARP_TARGETS) {
> >               bond_for_each_slave(bond, slave, iter)
> >                       slave->target_last_arp_rx[slot] = last_rx;
> > -             targets[slot].target_ip = target;
> > +             memcpy(&targets[slot], &target, sizeof(target));
> >       }
> >   }
> >
> > -static int _bond_option_arp_ip_target_add(struct bonding *bond, __be32 target)
> > +static int _bond_option_arp_ip_target_add(struct bonding *bond, struct bond_arp_target target)
> >   {
> >       struct bond_arp_target *targets = bond->params.arp_targets;
> > +     char pbuf[BOND_OPTION_STRING_MAX_SIZE];
> >       int ind;
> >
> > -     if (!bond_is_ip_target_ok(target)) {
> > +     if (!bond_is_ip_target_ok(target.target_ip)) {
> >               netdev_err(bond->dev, "invalid ARP target %pI4 specified for addition\n",
> > -                        &target);
> > +                        &target.target_ip);
> >               return -EINVAL;
> >       }
> >
> > -     if (bond_get_targets_ip(targets, target) != -1) { /* dup */
> > +     if (bond_get_targets_ip(targets, target.target_ip) != -1) { /* dup */
> >               netdev_err(bond->dev, "ARP target %pI4 is already present\n",
> > -                        &target);
> > +                        &target.target_ip);
> >               return -EINVAL;
> >       }
> >
> > @@ -1170,43 +1171,44 @@ static int _bond_option_arp_ip_target_add(struct bonding *bond, __be32 target)
> >               return -EINVAL;
> >       }
> >
> > @@ -1170,43 +1171,44 @@ static int _bond_option_arp_ip_target_add(struct bonding *bond, __be32 target)
> >               return -EINVAL;
> >       }
> >
> > -     netdev_dbg(bond->dev, "Adding ARP target %pI4\n", &target);
> > +     netdev_dbg(bond->dev, "Adding ARP target %s\n",
> > +                bond_arp_target_to_string(&target, pbuf, sizeof(pbuf)));
> >
> >       _bond_options_arp_ip_target_set(bond, ind, target, jiffies);
> >
> >       return 0;
> >   }
> >
> > -static int bond_option_arp_ip_target_add(struct bonding *bond, __be32 target)
> > +static int bond_option_arp_ip_target_add(struct bonding *bond, struct bond_arp_target target)
> >   {
> >       return _bond_option_arp_ip_target_add(bond, target);
> >   }
> >
> > -static int bond_option_arp_ip_target_rem(struct bonding *bond, __be32 target)
> > +static int bond_option_arp_ip_target_rem(struct bonding *bond, struct bond_arp_target target)
> >   {
> >       struct bond_arp_target *targets = bond->params.arp_targets;
> > +     unsigned long *targets_rx;
> >       struct list_head *iter;
> >       struct slave *slave;
> > -     unsigned long *targets_rx;
> >       int ind, i;
> >
> > -     if (!bond_is_ip_target_ok(target)) {
> > +     if (!bond_is_ip_target_ok(target.target_ip)) {
> >               netdev_err(bond->dev, "invalid ARP target %pI4 specified for removal\n",
> > -                        &target);
> > +                        &target.target_ip);
> >               return -EINVAL;
> >       }
> >
> > -     ind = bond_get_targets_ip(targets, target);
> > +     ind = bond_get_targets_ip(targets, target.target_ip);
> >       if (ind == -1) {
> >               netdev_err(bond->dev, "unable to remove nonexistent ARP target %pI4\n",
> > -                        &target);
> > +                        &target.target_ip);
> >               return -EINVAL;
> >       }
> >
> >       if (ind == 0 && !targets[1].target_ip && bond->params.arp_interval)
> >               netdev_warn(bond->dev, "Removing last arp target with arp_interval on\n");
> >
> > -     netdev_dbg(bond->dev, "Removing ARP target %pI4\n", &target);
> > +     netdev_dbg(bond->dev, "Removing ARP target %pI4\n", &target.target_ip);
> >
> >       bond_for_each_slave(bond, slave, iter) {
> >               targets_rx = slave->target_last_arp_rx;
> > @@ -1214,30 +1216,77 @@ static int bond_option_arp_ip_target_rem(struct bonding *bond, __be32 target)
> >                       targets_rx[i] = targets_rx[i+1];
> >               targets_rx[i] = 0;
> >       }
> > -     for (i = ind; (i < BOND_MAX_ARP_TARGETS - 1) && targets[i + 1].target_ip; i++)
> > -             targets[i] = targets[i+1];
> > +
> > +     bond_free_vlan_tag(&targets[ind]);
> > +
> > +     for (i = ind; (i < BOND_MAX_ARP_TARGETS - 1) && targets[i + 1].target_ip; i++) {
> > +             targets[i].target_ip = targets[i + 1].target_ip;
> > +             targets[i].tags = targets[i + 1].tags;
> > +             targets[i].flags = targets[i + 1].flags;
> > +     }
> >       targets[i].target_ip = 0;
> > +     targets[i].flags = 0;
> > +     targets[i].tags = NULL;
>
> This looks wrong, bond_free_vlan_tag() is kfree(tags) and then it is set to NULL but
> these tags can be used with only RCU held (change is in patch 05) in a few places, there is
> no synchronization and nothing to keep that code from either a use-after-free or a null
> ptr dereference.
>
> Check bond_loadbalance_arp_mon -> bond_send_validate.
>
  I understand your concern, see the discussion about patch 5.
  If the setting of user supplied tags is performed during
  the creation of the bond with the RTNL held then this should not
  be an issue.  I need to think about what happens if the user attempts to
  create or change or delete an arp_ip_taget on an existing bond. The existing code
  had no special handling of that case.

> >
> >       return 0;
> >   }
> >
> >   void bond_option_arp_ip_targets_clear(struct bonding *bond)
> >   {
> > +     struct bond_arp_target empty_target;
>
> empty_target = {} ...
>
> >       int i;
> >
> > +     empty_target.target_ip = 0;
> > +     empty_target.flags = 0;
> > +     empty_target.tags = NULL;
>
> ... and you can drop these lines

Thanks,  I will make that change.

>
> > +
> >       for (i = 0; i < BOND_MAX_ARP_TARGETS; i++)
> > -             _bond_options_arp_ip_target_set(bond, i, 0, 0);
> > +             _bond_options_arp_ip_target_set(bond, i, empty_target, 0);
> > +}
> > +
> > +/**
> > + * bond_validate_tags - validate an array of bond_vlan_tag.
> > + * @tags: the array to validate
> > + * @len: the length in bytes of @tags
> > + *
> > + * Validate that @tags points to a valid array of struct bond_vlan_tag.
> > + * Returns: the length of the validated bytes in the array or -1 if no
> > + * valid list is found.
> > + */
> > +static int bond_validate_tags(struct bond_vlan_tag *tags, size_t len)
> > +{
> > +     size_t i, ntags = 0;
> > +
> > +     if (len == 0 || !tags)
> > +             return 0;
> > +
> > +     for (i = 0; i <= len; i = i + sizeof(struct bond_vlan_tag)) {
> > +             if (ntags > BOND_MAX_VLAN_TAGS)
> > +                     break;
> > +
> > +             if (tags->vlan_proto == BOND_VLAN_PROTO_NONE)
> > +                     return i + sizeof(struct bond_vlan_tag);
>
> You suppose that there is at least sizeof(struct bond_vlan_tag) in tags
> but there shouldn't be, it could be target_ip + 1 byte and here you will
> be out of bounds.

I will add a check for len < sizeof(struct bond_vlan_tag) .

>
> > +> +          if (tags->vlan_id > 4094)
>
> vlan tag 0 is invalid as well, and this should be using the vlan definitions
> e.g. !tags->vlan_id || tags->vlan_id >= VLAN_VID_MASK

I will make that change as well.

>
> > +                     break;
> > +             tags++;
> > +             ntags++;
> > +     }
> > +     return -1;
> >   }
> >
> >   static int bond_option_arp_ip_targets_set(struct bonding *bond,
> >                                         const struct bond_opt_value *newval)
> >   {
> > -     int ret = -EPERM;
> > -     __be32 target;
> > +     size_t len = (size_t)newval->extra_len;
> > +     char *extra = (char *)newval->extra;
> > +     struct bond_arp_target target;
> > +     int size, ret = -EPERM;
> >
> >       if (newval->string) {
> > +             /* Adding or removing arp_ip_target from sysfs */
> >               if (strlen(newval->string) < 1 ||
> > -                 !in4_pton(newval->string + 1, -1, (u8 *)&target, -1, NULL)) {
> > +                 !in4_pton(newval->string + 1, -1, (u8 *)&target.target_ip, -1, NULL)) {
> >                       netdev_err(bond->dev, "invalid ARP target specified\n");
> >                       return ret;
> >               }
> > @@ -1248,7 +1297,29 @@ static int bond_option_arp_ip_targets_set(struct bonding *bond,
> >               else
> >                       netdev_err(bond->dev, "no command found in arp_ip_targets file - use +<addr> or -<addr>\n");
> >       } else {
> > -             target = newval->value;
> > +             /* Adding arp_ip_target from netlink. aka: ip command */
> > +             if (len < sizeof(target.target_ip)) {
>
> this check is redundant, we already validate it has at least sizeof(__be32) in bond_changelink()

Check removed.

>
> > +                     netdev_err(bond->dev, "invalid ARP target specified\n");
> > +                     return ret;
> > +             }
> > +             memcpy(&target.target_ip, newval->extra, sizeof(__be32));
> > +             len = len - sizeof(target.target_ip);
> > +             extra = extra + sizeof(target.target_ip);
>
> len could be < sizeof(struct bond_vlan_tag), e.g. it could be 1
> (see above my comment about tags len)
>

 The new check in bond_validate_tags() should do it.

> > +
> > +             size = bond_validate_tags((struct bond_vlan_tag *)extra, len);
> > +
> > +             if (size > 0) {
> > +                     target.tags = kmalloc((size_t)size, GFP_ATOMIC);
> > +                     if (!target.tags)
> > +                             return -ENOMEM;
> > +                     memcpy(target.tags, extra, size);
> > +                     target.flags |= BOND_TARGET_USERTAGS;
> > +             }
>
> else if (size == -1)
>
> > +
> > +             if (size == -1)
> > +                     netdev_warn(bond->dev, "Invalid list of vlans provided with %pI4\n",
> > +                                 &target.target_ip);
> > +
> >               ret = bond_option_arp_ip_target_add(bond, target);
>
>
> I don't see target.tags freed if bond_option_arp_ip_target_add() results in an error.

Adding the kfree if (ret).

^ permalink raw reply	[flat|nested] 22+ messages in thread

end of thread, other threads:[~2025-09-10 21:19 UTC | newest]

Thread overview: 22+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-09-04 22:18 [PATCH net-next v10 0/7] bonding: Extend arp_ip_target format to allow for a list of vlan tags David Wilder
2025-09-04 22:18 ` [PATCH net-next v10 1/7] bonding: Adding struct bond_arp_target David Wilder
2025-09-04 22:18 ` [PATCH net-next v10 2/7] bonding: Adding extra_len field to struct bond_opt_value David Wilder
2025-09-04 22:18 ` [PATCH net-next v10 3/7] bonding: arp_ip_target helpers David Wilder
2025-09-04 22:18 ` [PATCH net-next v10 4/7] bonding: Processing extended arp_ip_target from user space David Wilder
2025-09-09 13:37   ` Paolo Abeni
2025-09-09 18:42   ` Nikolay Aleksandrov
2025-09-10 21:19     ` David Wilder
2025-09-04 22:18 ` [PATCH net-next v10 5/7] bonding: Update to bond_arp_send_all() to use supplied vlan tags David Wilder
2025-09-09 13:39   ` Paolo Abeni
2025-09-09 18:55   ` Nikolay Aleksandrov
2025-09-09 20:54     ` Jay Vosburgh
2025-09-09 21:08       ` Jay Vosburgh
2025-09-09 21:57         ` David Wilder
2025-09-04 22:18 ` [PATCH net-next v10 6/7] bonding: Update for extended arp_ip_target format David Wilder
2025-09-09 13:46   ` Paolo Abeni
2025-09-09 18:45   ` Nikolay Aleksandrov
2025-09-04 22:18 ` [PATCH net-next v10 7/7] bonding: Selftest and documentation for the arp_ip_target parameter David Wilder
2025-09-09 13:51   ` Paolo Abeni
2025-09-09 13:54 ` [PATCH net-next v10 0/7] bonding: Extend arp_ip_target format to allow for a list of vlan tags Nikolay Aleksandrov
2025-09-09 17:55   ` David Wilder
2025-09-09 18:04     ` Nikolay Aleksandrov

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).