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 c4d53e8e7c15..ac654b384ea1 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -3146,26 +3146,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; } @@ -3183,15 +3186,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); } } @@ -6096,7 +6099,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; @@ -6290,7 +6293,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 ac5e402c34bc..1a3d17754c0a 100644 --- a/drivers/net/bonding/bond_netlink.c +++ b/drivers/net/bonding/bond_netlink.c @@ -688,8 +688,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 91893c29b899..e4b7eb376575 100644 --- a/drivers/net/bonding/bond_options.c +++ b/drivers/net/bonding/bond_options.c @@ -1090,7 +1090,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) { @@ -1118,20 +1118,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)) { @@ -1166,7 +1166,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; @@ -1185,20 +1185,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 18687ccf0638..0a7d690cb005 100644 --- a/include/net/bond_options.h +++ b/include/net/bond_options.h @@ -119,6 +119,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 95f67b308c19..5b4c43f02c89 100644 --- a/include/net/bonding.h +++ b/include/net/bonding.h @@ -135,7 +135,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; @@ -274,11 +274,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 * @@ -522,7 +517,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]; @@ -760,14 +755,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 0a7d690cb005..d5d59081a9ce 100644 --- a/include/net/bond_options.h +++ b/include/net/bond_options.h @@ -85,14 +85,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; @@ -167,8 +168,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 5b4c43f02c89..a111c50399d3 100644 --- a/include/net/bonding.h +++ b/include/net/bonding.h @@ -806,4 +806,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 1a3d17754c0a..23e27530c4d8 100644 --- a/drivers/net/bonding/bond_netlink.c +++ b/drivers/net/bonding/bond_netlink.c @@ -292,9 +292,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 e4b7eb376575..6e0611de1087 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, @@ -1115,7 +1115,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; @@ -1125,24 +1125,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; } @@ -1152,43 +1153,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; @@ -1196,30 +1198,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; } @@ -1230,7 +1247,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 ac654b384ea1..92c64030b432 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -3144,18 +3144,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, @@ -3177,9 +3178,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; @@ -3195,7 +3200,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); } } @@ -6663,7 +6667,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 /sys/class/net//bonding/arp_ip_target and /proc/net/bonding/ will display vlan tags if they have been configured by the user Signed-off-by: David Wilder --- drivers/net/bonding/bond_procfs.c | 5 ++++- drivers/net/bonding/bond_sysfs.c | 9 ++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/net/bonding/bond_procfs.c b/drivers/net/bonding/bond_procfs.c index 94e6fd7041ee..b07944396912 100644 --- a/drivers/net/bonding/bond_procfs.c +++ b/drivers/net/bonding/bond_procfs.c @@ -111,6 +111,7 @@ static void bond_info_show_master(struct seq_file *seq) /* ARP information */ if (bond->params.arp_interval > 0) { + char pbuf[BOND_OPTION_STRING_MAX_SIZE]; int printed = 0; seq_printf(seq, "ARP Polling Interval (ms): %d\n", @@ -125,7 +126,9 @@ static void bond_info_show_master(struct seq_file *seq) break; if (printed) seq_printf(seq, ","); - seq_printf(seq, " %pI4", &bond->params.arp_targets[i].target_ip); + bond_arp_target_to_string(&bond->params.arp_targets[i], + pbuf, sizeof(pbuf)); + seq_printf(seq, " %s", pbuf); printed = 1; } seq_printf(seq, "\n"); diff --git a/drivers/net/bonding/bond_sysfs.c b/drivers/net/bonding/bond_sysfs.c index d7c09e0a14dd..870e0d90b77c 100644 --- a/drivers/net/bonding/bond_sysfs.c +++ b/drivers/net/bonding/bond_sysfs.c @@ -286,13 +286,16 @@ static ssize_t bonding_show_arp_targets(struct device *d, struct device_attribute *attr, char *buf) { + char pbuf[BOND_OPTION_STRING_MAX_SIZE]; struct bonding *bond = to_bond(d); int i, res = 0; for (i = 0; i < BOND_MAX_ARP_TARGETS; i++) { - if (bond->params.arp_targets[i].target_ip) - res += sysfs_emit_at(buf, res, "%pI4 ", - &bond->params.arp_targets[i].target_ip); + if (bond->params.arp_targets[i].target_ip) { + bond_arp_target_to_string(&bond->params.arp_targets[i], + pbuf, sizeof(pbuf)); + res += sysfs_emit_at(buf, res, "%s ", pbuf); + } } if (res) buf[res-1] = '\n'; /* eat the leftover space */ -- 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 | 194 ++++++++++++++++++ 3 files changed, 207 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 a4c1291d2561..aa76b94b1d88 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..89c698d28701 --- /dev/null +++ b/tools/testing/selftests/drivers/net/bonding/bond-arp-ip-target.sh @@ -0,0 +1,194 @@ +#!/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") +source ${lib_dir}/bond_topo_2d1c.sh + +DEBUG=${DEBUG:-0} +test ${DEBUG} -ne 0 && set -x + +# vlan subnets +s_ip4v10="192.10.2.1" +s_ip4v20="192.20.2.1" +s_ip4v30="192.30.2.1" +c_ip4v10="192.10.2.10" +c_ip4v20="192.20.2.10" +c_ip4v30="192.30.2.10" + +ALL_TESTS=" + no_vlan_hints + with_vlan_hints +" + +# load bonding modules and set options. +load_bonding() { + local bond_options="$*" + + lsmod | grep bonding > /dev/null + if [ $? == 0 ]; then + rmmod bonding + fi + modprobe bonding ${bond_options} +} + +# 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" + + ip -n ${ns} link show ${interface} > /dev/null + if [[ $? -ne 0 ]] && RET=1; then + 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() +{ + load_bonding $* + setup_prepare + setup_wait + stack_vlans bond0 ${s_ns} + stack_vlans eth0 ${c_ns} +} + +skip_with_vlan_hints() +{ + local skip=1 + + # check if iproute support prio option + ip -n ${s_ns} link add bond2 type bond arp_ip_target 10.0.0.1[10] + [[ $? -ne 0 ]] && skip=0 + ip -n ${s_ns} link del bond2 2> /dev/null + + return $skip +} + +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" \ + "Current 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 # dyndbg=+p +tests_run + +exit $EXIT_STATUS -- 2.43.5