bonding: add new option ns_ip6_target

This patch add a new bonding option ns_ip6_target, which correspond
to the arp_ip_target. With this we set IPv6 targets and send IPv6 NS
request to determine the health of the link.

For other related options like the validation, we still use
arp_validate, and will change to ns_validate later.

Note: the sysfs configuration support was removed based on
https://lore.kernel.org/netdev/8863.1645071997@famine

Signed-off-by: Hangbin Liu <liuhangbin@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Hangbin Liu 2022-02-21 13:54:57 +08:00 committed by David S. Miller
parent 4e24be018e
commit 129e3c1bab
7 changed files with 155 additions and 0 deletions

View File

@ -313,6 +313,17 @@ arp_ip_target
maximum number of targets that can be specified is 16. The maximum number of targets that can be specified is 16. The
default value is no IP addresses. default value is no IP addresses.
ns_ip6_target
Specifies the IPv6 addresses to use as IPv6 monitoring peers when
arp_interval is > 0. These are the targets of the NS request
sent to determine the health of the link to the targets.
Specify these values in ffff:ffff::ffff:ffff format. Multiple IPv6
addresses must be separated by a comma. At least one IPv6
address must be given for NS/NA monitoring to function. The
maximum number of targets that can be specified is 16. The
default value is no IPv6 addresses.
arp_validate arp_validate
Specifies whether or not ARP probes and replies should be Specifies whether or not ARP probes and replies should be

View File

@ -14,6 +14,7 @@
#include <net/netlink.h> #include <net/netlink.h>
#include <net/rtnetlink.h> #include <net/rtnetlink.h>
#include <net/bonding.h> #include <net/bonding.h>
#include <net/ipv6.h>
static size_t bond_get_slave_size(const struct net_device *bond_dev, static size_t bond_get_slave_size(const struct net_device *bond_dev,
const struct net_device *slave_dev) const struct net_device *slave_dev)
@ -111,6 +112,7 @@ static const struct nla_policy bond_policy[IFLA_BOND_MAX + 1] = {
[IFLA_BOND_TLB_DYNAMIC_LB] = { .type = NLA_U8 }, [IFLA_BOND_TLB_DYNAMIC_LB] = { .type = NLA_U8 },
[IFLA_BOND_PEER_NOTIF_DELAY] = { .type = NLA_U32 }, [IFLA_BOND_PEER_NOTIF_DELAY] = { .type = NLA_U32 },
[IFLA_BOND_MISSED_MAX] = { .type = NLA_U8 }, [IFLA_BOND_MISSED_MAX] = { .type = NLA_U8 },
[IFLA_BOND_NS_IP6_TARGET] = { .type = NLA_NESTED },
}; };
static const struct nla_policy bond_slave_policy[IFLA_BOND_SLAVE_MAX + 1] = { static const struct nla_policy bond_slave_policy[IFLA_BOND_SLAVE_MAX + 1] = {
@ -272,6 +274,40 @@ static int bond_changelink(struct net_device *bond_dev, struct nlattr *tb[],
if (err) if (err)
return err; return err;
} }
#if IS_ENABLED(CONFIG_IPV6)
if (data[IFLA_BOND_NS_IP6_TARGET]) {
struct nlattr *attr;
int i = 0, rem;
bond_option_ns_ip6_targets_clear(bond);
nla_for_each_nested(attr, data[IFLA_BOND_NS_IP6_TARGET], rem) {
struct in6_addr addr6;
if (nla_len(attr) < sizeof(addr6)) {
NL_SET_ERR_MSG(extack, "Invalid IPv6 address");
return -EINVAL;
}
addr6 = nla_get_in6_addr(attr);
if (ipv6_addr_type(&addr6) & IPV6_ADDR_LINKLOCAL) {
NL_SET_ERR_MSG(extack, "Invalid IPv6 addr6");
return -EINVAL;
}
bond_opt_initextra(&newval, &addr6, sizeof(addr6));
err = __bond_opt_set(bond, BOND_OPT_NS_TARGETS,
&newval);
if (err)
break;
i++;
}
if (i == 0 && bond->params.arp_interval)
netdev_warn(bond->dev, "Removing last ns target with arp_interval on\n");
if (err)
return err;
}
#endif
if (data[IFLA_BOND_ARP_VALIDATE]) { if (data[IFLA_BOND_ARP_VALIDATE]) {
int arp_validate = nla_get_u32(data[IFLA_BOND_ARP_VALIDATE]); int arp_validate = nla_get_u32(data[IFLA_BOND_ARP_VALIDATE]);
@ -526,6 +562,9 @@ static size_t bond_get_size(const struct net_device *bond_dev)
nla_total_size(sizeof(u8)) + /* IFLA_BOND_TLB_DYNAMIC_LB */ nla_total_size(sizeof(u8)) + /* IFLA_BOND_TLB_DYNAMIC_LB */
nla_total_size(sizeof(u32)) + /* IFLA_BOND_PEER_NOTIF_DELAY */ nla_total_size(sizeof(u32)) + /* IFLA_BOND_PEER_NOTIF_DELAY */
nla_total_size(sizeof(u8)) + /* IFLA_BOND_MISSED_MAX */ nla_total_size(sizeof(u8)) + /* IFLA_BOND_MISSED_MAX */
/* IFLA_BOND_NS_IP6_TARGET */
nla_total_size(sizeof(struct nlattr)) +
nla_total_size(sizeof(struct in6_addr)) * BOND_MAX_NS_TARGETS +
0; 0;
} }
@ -603,6 +642,26 @@ static int bond_fill_info(struct sk_buff *skb,
bond->params.arp_all_targets)) bond->params.arp_all_targets))
goto nla_put_failure; goto nla_put_failure;
#if IS_ENABLED(CONFIG_IPV6)
targets = nla_nest_start(skb, IFLA_BOND_NS_IP6_TARGET);
if (!targets)
goto nla_put_failure;
targets_added = 0;
for (i = 0; i < BOND_MAX_NS_TARGETS; i++) {
if (!ipv6_addr_any(&bond->params.ns_targets[i])) {
if (nla_put_in6_addr(skb, i, &bond->params.ns_targets[i]))
goto nla_put_failure;
targets_added = 1;
}
}
if (targets_added)
nla_nest_end(skb, targets);
else
nla_nest_cancel(skb, targets);
#endif
primary = rtnl_dereference(bond->primary_slave); primary = rtnl_dereference(bond->primary_slave);
if (primary && if (primary &&
nla_put_u32(skb, IFLA_BOND_PRIMARY, primary->dev->ifindex)) nla_put_u32(skb, IFLA_BOND_PRIMARY, primary->dev->ifindex))

View File

@ -34,6 +34,10 @@ 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_rem(struct bonding *bond, __be32 target);
static int bond_option_arp_ip_targets_set(struct bonding *bond, static int bond_option_arp_ip_targets_set(struct bonding *bond,
const struct bond_opt_value *newval); const struct bond_opt_value *newval);
#if IS_ENABLED(CONFIG_IPV6)
static int bond_option_ns_ip6_targets_set(struct bonding *bond,
const struct bond_opt_value *newval);
#endif
static int bond_option_arp_validate_set(struct bonding *bond, static int bond_option_arp_validate_set(struct bonding *bond,
const struct bond_opt_value *newval); const struct bond_opt_value *newval);
static int bond_option_arp_all_targets_set(struct bonding *bond, static int bond_option_arp_all_targets_set(struct bonding *bond,
@ -295,6 +299,15 @@ static const struct bond_option bond_opts[BOND_OPT_LAST] = {
.flags = BOND_OPTFLAG_RAWVAL, .flags = BOND_OPTFLAG_RAWVAL,
.set = bond_option_arp_ip_targets_set .set = bond_option_arp_ip_targets_set
}, },
#if IS_ENABLED(CONFIG_IPV6)
[BOND_OPT_NS_TARGETS] = {
.id = BOND_OPT_NS_TARGETS,
.name = "ns_ip6_target",
.desc = "NS targets in ffff:ffff::ffff:ffff form",
.flags = BOND_OPTFLAG_RAWVAL,
.set = bond_option_ns_ip6_targets_set
},
#endif
[BOND_OPT_DOWNDELAY] = { [BOND_OPT_DOWNDELAY] = {
.id = BOND_OPT_DOWNDELAY, .id = BOND_OPT_DOWNDELAY,
.name = "downdelay", .name = "downdelay",
@ -1184,6 +1197,65 @@ static int bond_option_arp_ip_targets_set(struct bonding *bond,
return ret; return ret;
} }
#if IS_ENABLED(CONFIG_IPV6)
static void _bond_options_ns_ip6_target_set(struct bonding *bond, int slot,
struct in6_addr *target,
unsigned long last_rx)
{
struct in6_addr *targets = bond->params.ns_targets;
struct list_head *iter;
struct slave *slave;
if (slot >= 0 && slot < BOND_MAX_NS_TARGETS) {
bond_for_each_slave(bond, slave, iter)
slave->target_last_arp_rx[slot] = last_rx;
targets[slot] = *target;
}
}
void bond_option_ns_ip6_targets_clear(struct bonding *bond)
{
struct in6_addr addr_any = in6addr_any;
int i;
for (i = 0; i < BOND_MAX_NS_TARGETS; i++)
_bond_options_ns_ip6_target_set(bond, i, &addr_any, 0);
}
static int bond_option_ns_ip6_targets_set(struct bonding *bond,
const struct bond_opt_value *newval)
{
struct in6_addr *target = (struct in6_addr *)newval->extra;
struct in6_addr *targets = bond->params.ns_targets;
struct in6_addr addr_any = in6addr_any;
int index;
if (!bond_is_ip6_target_ok(target)) {
netdev_err(bond->dev, "invalid NS target %pI6c specified for addition\n",
target);
return -EINVAL;
}
if (bond_get_targets_ip6(targets, target) != -1) { /* dup */
netdev_err(bond->dev, "NS target %pI6c is already present\n",
target);
return -EINVAL;
}
index = bond_get_targets_ip6(targets, &addr_any); /* first free slot */
if (index == -1) {
netdev_err(bond->dev, "NS target table is full!\n");
return -EINVAL;
}
netdev_dbg(bond->dev, "Adding NS target %pI6c\n", target);
_bond_options_ns_ip6_target_set(bond, index, target, jiffies);
return 0;
}
#endif
static int bond_option_arp_validate_set(struct bonding *bond, static int bond_option_arp_validate_set(struct bonding *bond,
const struct bond_opt_value *newval) const struct bond_opt_value *newval)
{ {

View File

@ -66,6 +66,7 @@ enum {
BOND_OPT_PEER_NOTIF_DELAY, BOND_OPT_PEER_NOTIF_DELAY,
BOND_OPT_LACP_ACTIVE, BOND_OPT_LACP_ACTIVE,
BOND_OPT_MISSED_MAX, BOND_OPT_MISSED_MAX,
BOND_OPT_NS_TARGETS,
BOND_OPT_LAST BOND_OPT_LAST
}; };
@ -140,5 +141,8 @@ static inline void __bond_opt_init(struct bond_opt_value *optval,
__bond_opt_init(optval, NULL, ULLONG_MAX, extra, extra_len) __bond_opt_init(optval, NULL, ULLONG_MAX, extra, extra_len)
void bond_option_arp_ip_targets_clear(struct bonding *bond); void bond_option_arp_ip_targets_clear(struct bonding *bond);
#if IS_ENABLED(CONFIG_IPV6)
void bond_option_ns_ip6_targets_clear(struct bonding *bond);
#endif
#endif /* _NET_BOND_OPTIONS_H */ #endif /* _NET_BOND_OPTIONS_H */

View File

@ -503,6 +503,13 @@ static inline int bond_is_ip_target_ok(__be32 addr)
return !ipv4_is_lbcast(addr) && !ipv4_is_zeronet(addr); return !ipv4_is_lbcast(addr) && !ipv4_is_zeronet(addr);
} }
static inline int bond_is_ip6_target_ok(struct in6_addr *addr)
{
return !ipv6_addr_any(addr) &&
!ipv6_addr_loopback(addr) &&
!ipv6_addr_is_multicast(addr);
}
/* Get the oldest arp which we've received on this slave for bond's /* Get the oldest arp which we've received on this slave for bond's
* arp_targets. * arp_targets.
*/ */

View File

@ -860,6 +860,7 @@ enum {
IFLA_BOND_PEER_NOTIF_DELAY, IFLA_BOND_PEER_NOTIF_DELAY,
IFLA_BOND_AD_LACP_ACTIVE, IFLA_BOND_AD_LACP_ACTIVE,
IFLA_BOND_MISSED_MAX, IFLA_BOND_MISSED_MAX,
IFLA_BOND_NS_IP6_TARGET,
__IFLA_BOND_MAX, __IFLA_BOND_MAX,
}; };

View File

@ -860,6 +860,7 @@ enum {
IFLA_BOND_PEER_NOTIF_DELAY, IFLA_BOND_PEER_NOTIF_DELAY,
IFLA_BOND_AD_LACP_ACTIVE, IFLA_BOND_AD_LACP_ACTIVE,
IFLA_BOND_MISSED_MAX, IFLA_BOND_MISSED_MAX,
IFLA_BOND_NS_IP6_TARGET,
__IFLA_BOND_MAX, __IFLA_BOND_MAX,
}; };