bonding: add new option ns_ip6_target
authorHangbin Liu <liuhangbin@gmail.com>
Mon, 21 Feb 2022 05:54:57 +0000 (13:54 +0800)
committerDavid S. Miller <davem@davemloft.net>
Mon, 21 Feb 2022 12:13:45 +0000 (12:13 +0000)
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>
Documentation/networking/bonding.rst
drivers/net/bonding/bond_netlink.c
drivers/net/bonding/bond_options.c
include/net/bond_options.h
include/net/bonding.h
include/uapi/linux/if_link.h
tools/include/uapi/linux/if_link.h

index ab98373..525e684 100644 (file)
@@ -313,6 +313,17 @@ arp_ip_target
        maximum number of targets that can be specified is 16.  The
        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
 
        Specifies whether or not ARP probes and replies should be
index 1007bf6..f427fa1 100644 (file)
@@ -14,6 +14,7 @@
 #include <net/netlink.h>
 #include <net/rtnetlink.h>
 #include <net/bonding.h>
+#include <net/ipv6.h>
 
 static size_t bond_get_slave_size(const struct net_device *bond_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_PEER_NOTIF_DELAY]    = { .type = NLA_U32 },
        [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] = {
@@ -272,6 +274,40 @@ static int bond_changelink(struct net_device *bond_dev, struct nlattr *tb[],
                if (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]) {
                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(u32)) +   /* IFLA_BOND_PEER_NOTIF_DELAY */
                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;
 }
 
@@ -603,6 +642,26 @@ static int bond_fill_info(struct sk_buff *skb,
                        bond->params.arp_all_targets))
                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);
        if (primary &&
            nla_put_u32(skb, IFLA_BOND_PRIMARY, primary->dev->ifindex))
index ab57513..64f7db2 100644 (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_targets_set(struct bonding *bond,
                                          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,
                                        const struct bond_opt_value *newval);
 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,
                .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] = {
                .id = BOND_OPT_DOWNDELAY,
                .name = "downdelay",
@@ -1184,6 +1197,65 @@ static int bond_option_arp_ip_targets_set(struct bonding *bond,
        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,
                                        const struct bond_opt_value *newval)
 {
index 286b29c..61b4906 100644 (file)
@@ -66,6 +66,7 @@ enum {
        BOND_OPT_PEER_NOTIF_DELAY,
        BOND_OPT_LACP_ACTIVE,
        BOND_OPT_MISSED_MAX,
+       BOND_OPT_NS_TARGETS,
        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)
 
 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 */
index f3b986f..d0dfe72 100644 (file)
@@ -503,6 +503,13 @@ static inline int bond_is_ip_target_ok(__be32 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
  * arp_targets.
  */
index 6218f93..e1ba2d5 100644 (file)
@@ -860,6 +860,7 @@ enum {
        IFLA_BOND_PEER_NOTIF_DELAY,
        IFLA_BOND_AD_LACP_ACTIVE,
        IFLA_BOND_MISSED_MAX,
+       IFLA_BOND_NS_IP6_TARGET,
        __IFLA_BOND_MAX,
 };
 
index 6218f93..e1ba2d5 100644 (file)
@@ -860,6 +860,7 @@ enum {
        IFLA_BOND_PEER_NOTIF_DELAY,
        IFLA_BOND_AD_LACP_ACTIVE,
        IFLA_BOND_MISSED_MAX,
+       IFLA_BOND_NS_IP6_TARGET,
        __IFLA_BOND_MAX,
 };