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 --- 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 17c7542be6a5..7e938077bbde 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -3163,26 +3163,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; } @@ -3200,15 +3203,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); } } @@ -6164,7 +6167,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; @@ -6358,7 +6361,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 57fff2421f1b..9939e28dedd9 100644 --- a/drivers/net/bonding/bond_netlink.c +++ b/drivers/net/bonding/bond_netlink.c @@ -700,8 +700,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 1d639a3be6ba..e04487f8d79a 100644 --- a/drivers/net/bonding/bond_options.c +++ b/drivers/net/bonding/bond_options.c @@ -1113,7 +1113,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) { @@ -1141,20 +1141,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)) { @@ -1189,7 +1189,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; @@ -1208,20 +1208,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 1e13bb170515..d7c09e0a14dd 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 e06f0d63b2c1..27fbce667a4c 100644 --- a/include/net/bonding.h +++ b/include/net/bonding.h @@ -137,7 +137,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; @@ -277,11 +277,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 * @@ -525,7 +520,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]; @@ -763,14 +758,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.43.5 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 --- 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..b95023bf40c3 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; + u16 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.43.5 Adding helpers and defines needed for extending the arp_ip_target parameters. Signed-off-by: David Wilder --- include/net/bonding.h | 45 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/include/net/bonding.h b/include/net/bonding.h index 27fbce667a4c..1989b71ffa16 100644 --- a/include/net/bonding.h +++ b/include/net/bonding.h @@ -809,4 +809,49 @@ 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_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.43.5 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 --- drivers/net/bonding/bond_netlink.c | 5 +- drivers/net/bonding/bond_options.c | 81 +++++++++++++++++++++--------- 2 files changed, 60 insertions(+), 26 deletions(-) diff --git a/drivers/net/bonding/bond_netlink.c b/drivers/net/bonding/bond_netlink.c index 9939e28dedd9..5486ef40907e 100644 --- a/drivers/net/bonding/bond_netlink.c +++ b/drivers/net/bonding/bond_netlink.c @@ -293,9 +293,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 e04487f8d79a..1455e7f15c31 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, @@ -1138,7 +1138,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; @@ -1148,24 +1148,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; } @@ -1175,43 +1176,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; @@ -1219,30 +1221,45 @@ 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.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); } static int bond_option_arp_ip_targets_set(struct bonding *bond, const struct bond_opt_value *newval) { + size_t len = (size_t)newval->extra_len; + char *extra = (char *)newval->extra; + struct bond_arp_target target; int ret = -EPERM; - __be32 target; 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; } @@ -1253,7 +1270,23 @@ 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 + or -\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); + + if (len) + target.tags = kmalloc(len, GFP_ATOMIC); + + if (target.tags) { + memcpy(target.tags, extra, len); + target.flags |= BOND_TARGET_USERTAGS; + } + ret = bond_option_arp_ip_target_add(bond, target); } -- 2.43.5 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(). bonding_exit() is also updated to free vlan tags when a bond is destroyed. Signed-off-by: David Wilder --- 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 7e938077bbde..2f153c89e401 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -3161,18 +3161,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, @@ -3194,9 +3195,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; @@ -3212,7 +3217,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); } } @@ -6732,7 +6736,7 @@ static void __exit bonding_exit(void) bond_netlink_fini(); unregister_pernet_subsys(&bond_net_ops); - + bond_free_vlan_tags(bonding_defaults.arp_targets); bond_destroy_debugfs(); #ifdef CONFIG_NET_POLL_CONTROLLER -- 2.43.5 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 --- drivers/net/bonding/bond_netlink.c | 28 ++++++++++++++++++++++++++-- include/net/bonding.h | 1 + 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/drivers/net/bonding/bond_netlink.c b/drivers/net/bonding/bond_netlink.c index 5486ef40907e..6e8aebe5629f 100644 --- a/drivers/net/bonding/bond_netlink.c +++ b/drivers/net/bonding/bond_netlink.c @@ -701,8 +701,32 @@ 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].target_ip) { - if (nla_put_be32(skb, i, bond->params.arp_targets[i].target_ip)) + struct bond_arp_target *target = &bond->params.arp_targets[i]; + struct Data { + __u32 addr; + struct bond_vlan_tag vlans[BOND_MAX_VLAN_TAGS + 1]; + } data; + int size = 0; + + if (target->target_ip) { + data.addr = target->target_ip; + size = sizeof(target->target_ip); + } + + for (int level = 0; target->flags & BOND_TARGET_USERTAGS && target->tags; level++) { + if (level > BOND_MAX_VLAN_TAGS) + goto nla_put_failure; + + memcpy(&data.vlans[level], &target->tags[level], + sizeof(struct bond_vlan_tag)); + size = size + sizeof(struct bond_vlan_tag); + + if (target->tags[level].vlan_proto == BOND_VLAN_PROTO_NONE) + break; + } + + if (size) { + if (nla_put(skb, i, size, &data)) goto nla_put_failure; targets_added = 1; } diff --git a/include/net/bonding.h b/include/net/bonding.h index 1989b71ffa16..2502cf8428b3 100644 --- a/include/net/bonding.h +++ b/include/net/bonding.h @@ -811,6 +811,7 @@ static inline netdev_tx_t bond_tx_drop(struct net_device *dev, struct sk_buff *s /* 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, -- 2.43.5 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 --- Documentation/networking/bonding.rst | 11 ++ .../selftests/drivers/net/bonding/Makefile | 3 +- .../drivers/net/bonding/bond-arp-ip-target.sh | 179 ++++++++++++++++++ 3 files changed, 192 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 f8f5766703d4..4a80da56b784 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 2b10854e4b1e..c59bb2912a38 100644 --- a/tools/testing/selftests/drivers/net/bonding/Makefile +++ b/tools/testing/selftests/drivers/net/bonding/Makefile @@ -10,7 +10,8 @@ TEST_PROGS := \ mode-2-recovery-updelay.sh \ bond_options.sh \ bond-eth-type-change.sh \ - bond_macvlan_ipvlan.sh + bond_macvlan_ipvlan.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..e9af27f17d0f --- /dev/null +++ b/tools/testing/selftests/drivers/net/bonding/bond-arp-ip-target.sh @@ -0,0 +1,179 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +# +# Test bonding arp_ip_target. +# Topology for Bond mode 1,5,6 testing +# +# +-------------------------+ +# | | Server +# | bond.10.20.30 | 192.30.2.1/24 +# | | | +# | 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 +# | | | +# | eth0.10.20.30 | 192.30.30.2/24 +# +-------------------------+ + +lib_dir=$(dirname "$0") + +# shellcheck source=./bond_topo_2d1c.sh +source "${lib_dir}"/bond_topo_2d1c.sh + +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" + +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" -- 2.43.5