Commit 76ef6b80 authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'bonding-ipv6-NA-NS-monitor'

Hangbin Liu says:

====================
bonding: add IPv6 NS/NA monitor support

This patch add bond IPv6 NS/NA monitor support. A new option
ns_ip6_target is added, which is similar with arp_ip_target.
The IPv6 NS/NA monitor will take effect when there is a valid IPv6
address. Both ARP monitor and NS monitor will working at the same time.

A new extra storage field is added to struct bond_opt_value for IPv6 support.

Function bond_handle_vlan() is split from bond_arp_send() for both
IPv4/IPv6 usage.

To alloc NS message and send out. ndisc_ns_create() and ndisc_send_skb()
are exported.

v1 -> v2:
1. remove sysfs entry[1] and only keep netlink support.

RFC -> v1:
1. define BOND_MAX_ND_TARGETS as BOND_MAX_ARP_TARGETS
2. adjust for reverse xmas tree ordering of local variables
3. remove bond_do_ns_validate()
4. add extra field for bond_opt_value
5. set IS_ENABLED(CONFIG_IPV6) for IPv6 codes

[1] https://lore.kernel.org/netdev/8863.1645071997@famine


====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 91398a96 129e3c1b
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -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
+252 −43
Original line number Diff line number Diff line
@@ -88,6 +88,7 @@
#if IS_ENABLED(CONFIG_TLS_DEVICE)
#include <net/tls.h>
#endif
#include <net/ip6_route.h>

#include "bonding_priv.h"

@@ -2793,31 +2794,15 @@ static bool bond_has_this_ip(struct bonding *bond, __be32 ip)
	return ret;
}

/* We go to the (large) trouble of VLAN tagging ARP frames because
 * switches in VLAN mode (especially if ports are configured as
 * "native" to a VLAN) might not pass non-tagged frames.
 */
static void bond_arp_send(struct slave *slave, int arp_op, __be32 dest_ip,
			  __be32 src_ip, struct bond_vlan_tag *tags)
static bool bond_handle_vlan(struct slave *slave, struct bond_vlan_tag *tags,
			     struct sk_buff *skb)
{
	struct sk_buff *skb;
	struct bond_vlan_tag *outer_tag = tags;
	struct net_device *slave_dev = slave->dev;
	struct net_device *bond_dev = slave->bond->dev;

	slave_dbg(bond_dev, slave_dev, "arp %d on slave: dst %pI4 src %pI4\n",
		  arp_op, &dest_ip, &src_ip);

	skb = arp_create(arp_op, ETH_P_ARP, dest_ip, slave_dev, src_ip,
			 NULL, slave_dev->dev_addr, NULL);

	if (!skb) {
		net_err_ratelimited("ARP packet allocation failed\n");
		return;
	}
	struct net_device *slave_dev = slave->dev;
	struct bond_vlan_tag *outer_tag = tags;

	if (!tags || tags->vlan_proto == VLAN_N_VID)
		goto xmit;
		return true;

	tags++;

@@ -2834,7 +2819,7 @@ static void bond_arp_send(struct slave *slave, int arp_op, __be32 dest_ip,
						tags->vlan_id);
		if (!skb) {
			net_err_ratelimited("failed to insert inner VLAN tag\n");
			return;
			return false;
		}

		tags++;
@@ -2847,8 +2832,34 @@ static void bond_arp_send(struct slave *slave, int arp_op, __be32 dest_ip,
				       outer_tag->vlan_id);
	}

xmit:
	return true;
}

/* We go to the (large) trouble of VLAN tagging ARP frames because
 * switches in VLAN mode (especially if ports are configured as
 * "native" to a VLAN) might not pass non-tagged frames.
 */
static void bond_arp_send(struct slave *slave, int arp_op, __be32 dest_ip,
			  __be32 src_ip, struct bond_vlan_tag *tags)
{
	struct net_device *bond_dev = slave->bond->dev;
	struct net_device *slave_dev = slave->dev;
	struct sk_buff *skb;

	slave_dbg(bond_dev, slave_dev, "arp %d on slave: dst %pI4 src %pI4\n",
		  arp_op, &dest_ip, &src_ip);

	skb = arp_create(arp_op, ETH_P_ARP, dest_ip, slave_dev, src_ip,
			 NULL, slave_dev->dev_addr, NULL);

	if (!skb) {
		net_err_ratelimited("ARP packet allocation failed\n");
		return;
	}

	if (bond_handle_vlan(slave, tags, skb))
		arp_xmit(skb);
	return;
}

/* Validate the device path between the @start_dev and the @end_dev.
@@ -2965,30 +2976,17 @@ static void bond_validate_arp(struct bonding *bond, struct slave *slave, __be32
	slave->target_last_arp_rx[i] = jiffies;
}

int bond_arp_rcv(const struct sk_buff *skb, struct bonding *bond,
static int bond_arp_rcv(const struct sk_buff *skb, struct bonding *bond,
			struct slave *slave)
{
	struct arphdr *arp = (struct arphdr *)skb->data;
	struct slave *curr_active_slave, *curr_arp_slave;
	unsigned char *arp_ptr;
	__be32 sip, tip;
	int is_arp = skb->protocol == __cpu_to_be16(ETH_P_ARP);
	unsigned int alen;

	if (!slave_do_arp_validate(bond, slave)) {
		if ((slave_do_arp_validate_only(bond) && is_arp) ||
		    !slave_do_arp_validate_only(bond))
			slave->last_rx = jiffies;
		return RX_HANDLER_ANOTHER;
	} else if (!is_arp) {
		return RX_HANDLER_ANOTHER;
	}

	alen = arp_hdr_len(bond->dev);

	slave_dbg(bond->dev, slave->dev, "%s: skb->dev %s\n",
		   __func__, skb->dev->name);

	if (alen > skb_headlen(skb)) {
		arp = kmalloc(alen, GFP_ATOMIC);
		if (!arp)
@@ -3059,6 +3057,216 @@ int bond_arp_rcv(const struct sk_buff *skb, struct bonding *bond,
	return RX_HANDLER_ANOTHER;
}

#if IS_ENABLED(CONFIG_IPV6)
static void bond_ns_send(struct slave *slave, const struct in6_addr *daddr,
			 const struct in6_addr *saddr, struct bond_vlan_tag *tags)
{
	struct net_device *bond_dev = slave->bond->dev;
	struct net_device *slave_dev = slave->dev;
	struct in6_addr mcaddr;
	struct sk_buff *skb;

	slave_dbg(bond_dev, slave_dev, "NS on slave: dst %pI6c src %pI6c\n",
		  daddr, saddr);

	skb = ndisc_ns_create(slave_dev, daddr, saddr, 0);
	if (!skb) {
		net_err_ratelimited("NS packet allocation failed\n");
		return;
	}

	addrconf_addr_solict_mult(daddr, &mcaddr);
	if (bond_handle_vlan(slave, tags, skb))
		ndisc_send_skb(skb, &mcaddr, saddr);
}

static void bond_ns_send_all(struct bonding *bond, struct slave *slave)
{
	struct in6_addr *targets = bond->params.ns_targets;
	struct bond_vlan_tag *tags;
	struct dst_entry *dst;
	struct in6_addr saddr;
	struct flowi6 fl6;
	int i;

	for (i = 0; i < BOND_MAX_NS_TARGETS && !ipv6_addr_any(&targets[i]); i++) {
		slave_dbg(bond->dev, slave->dev, "%s: target %pI6c\n",
			  __func__, &targets[i]);
		tags = NULL;

		/* Find out through which dev should the packet go */
		memset(&fl6, 0, sizeof(struct flowi6));
		fl6.daddr = targets[i];
		fl6.flowi6_oif = bond->dev->ifindex;

		dst = ip6_route_output(dev_net(bond->dev), NULL, &fl6);
		if (dst->error) {
			dst_release(dst);
			/* there's no route to target - try to send arp
			 * probe to generate any traffic (arp_validate=0)
			 */
			if (bond->params.arp_validate)
				pr_warn_once("%s: no route to ns_ip6_target %pI6c and arp_validate is set\n",
					     bond->dev->name,
					     &targets[i]);
			bond_ns_send(slave, &targets[i], &in6addr_any, tags);
			continue;
		}

		/* bond device itself */
		if (dst->dev == bond->dev)
			goto found;

		rcu_read_lock();
		tags = bond_verify_device_path(bond->dev, dst->dev, 0);
		rcu_read_unlock();

		if (!IS_ERR_OR_NULL(tags))
			goto found;

		/* Not our device - skip */
		slave_dbg(bond->dev, slave->dev, "no path to ns_ip6_target %pI6c via dst->dev %s\n",
			  &targets[i], dst->dev ? dst->dev->name : "NULL");

		dst_release(dst);
		continue;

found:
		if (!ipv6_dev_get_saddr(dev_net(dst->dev), dst->dev, &targets[i], 0, &saddr))
			bond_ns_send(slave, &targets[i], &saddr, tags);
		dst_release(dst);
		kfree(tags);
	}
}

static int bond_confirm_addr6(struct net_device *dev,
			      struct netdev_nested_priv *priv)
{
	struct in6_addr *addr = (struct in6_addr *)priv->data;

	return ipv6_chk_addr(dev_net(dev), addr, dev, 0);
}

static bool bond_has_this_ip6(struct bonding *bond, struct in6_addr *addr)
{
	struct netdev_nested_priv priv = {
		.data = addr,
	};
	int ret = false;

	if (bond_confirm_addr6(bond->dev, &priv))
		return true;

	rcu_read_lock();
	if (netdev_walk_all_upper_dev_rcu(bond->dev, bond_confirm_addr6, &priv))
		ret = true;
	rcu_read_unlock();

	return ret;
}

static void bond_validate_ns(struct bonding *bond, struct slave *slave,
			     struct in6_addr *saddr, struct in6_addr *daddr)
{
	int i;

	if (ipv6_addr_any(saddr) || !bond_has_this_ip6(bond, daddr)) {
		slave_dbg(bond->dev, slave->dev, "%s: sip %pI6c tip %pI6c not found\n",
			  __func__, saddr, daddr);
		return;
	}

	i = bond_get_targets_ip6(bond->params.ns_targets, saddr);
	if (i == -1) {
		slave_dbg(bond->dev, slave->dev, "%s: sip %pI6c not found in targets\n",
			  __func__, saddr);
		return;
	}
	slave->last_rx = jiffies;
	slave->target_last_arp_rx[i] = jiffies;
}

static int bond_na_rcv(const struct sk_buff *skb, struct bonding *bond,
		       struct slave *slave)
{
	struct slave *curr_active_slave, *curr_arp_slave;
	struct icmp6hdr *hdr = icmp6_hdr(skb);
	struct in6_addr *saddr, *daddr;

	if (skb->pkt_type == PACKET_OTHERHOST ||
	    skb->pkt_type == PACKET_LOOPBACK ||
	    hdr->icmp6_type != NDISC_NEIGHBOUR_ADVERTISEMENT)
		goto out;

	saddr = &ipv6_hdr(skb)->saddr;
	daddr = &ipv6_hdr(skb)->daddr;

	slave_dbg(bond->dev, slave->dev, "%s: %s/%d av %d sv %d sip %pI6c tip %pI6c\n",
		  __func__, slave->dev->name, bond_slave_state(slave),
		  bond->params.arp_validate, slave_do_arp_validate(bond, slave),
		  saddr, daddr);

	curr_active_slave = rcu_dereference(bond->curr_active_slave);
	curr_arp_slave = rcu_dereference(bond->current_arp_slave);

	/* We 'trust' the received ARP enough to validate it if:
	 * see bond_arp_rcv().
	 */
	if (bond_is_active_slave(slave))
		bond_validate_ns(bond, slave, saddr, daddr);
	else if (curr_active_slave &&
		 time_after(slave_last_rx(bond, curr_active_slave),
			    curr_active_slave->last_link_up))
		bond_validate_ns(bond, slave, saddr, daddr);
	else if (curr_arp_slave &&
		 bond_time_in_interval(bond,
				       dev_trans_start(curr_arp_slave->dev), 1))
		bond_validate_ns(bond, slave, saddr, daddr);

out:
	return RX_HANDLER_ANOTHER;
}
#endif

int bond_rcv_validate(const struct sk_buff *skb, struct bonding *bond,
		      struct slave *slave)
{
#if IS_ENABLED(CONFIG_IPV6)
	bool is_ipv6 = skb->protocol == __cpu_to_be16(ETH_P_IPV6);
#endif
	bool is_arp = skb->protocol == __cpu_to_be16(ETH_P_ARP);

	slave_dbg(bond->dev, slave->dev, "%s: skb->dev %s\n",
		  __func__, skb->dev->name);

	/* Use arp validate logic for both ARP and NS */
	if (!slave_do_arp_validate(bond, slave)) {
		if ((slave_do_arp_validate_only(bond) && is_arp) ||
#if IS_ENABLED(CONFIG_IPV6)
		    (slave_do_arp_validate_only(bond) && is_ipv6) ||
#endif
		    !slave_do_arp_validate_only(bond))
			slave->last_rx = jiffies;
		return RX_HANDLER_ANOTHER;
	} else if (is_arp) {
		return bond_arp_rcv(skb, bond, slave);
#if IS_ENABLED(CONFIG_IPV6)
	} else if (is_ipv6) {
		return bond_na_rcv(skb, bond, slave);
#endif
	} else {
		return RX_HANDLER_ANOTHER;
	}
}

static void bond_send_validate(struct bonding *bond, struct slave *slave)
{
	bond_arp_send_all(bond, slave);
#if IS_ENABLED(CONFIG_IPV6)
	bond_ns_send_all(bond, slave);
#endif
}

/* function to verify if we're in the arp_interval timeslice, returns true if
 * (last_act - arp_interval) <= jiffies <= (last_act + mod * arp_interval +
 * arp_interval/2) . the arp_interval/2 is needed for really fast networks.
@@ -3154,7 +3362,7 @@ static void bond_loadbalance_arp_mon(struct bonding *bond)
		 * to be unstable during low/no traffic periods
		 */
		if (bond_slave_is_up(slave))
			bond_arp_send_all(bond, slave);
			bond_send_validate(bond, slave);
	}

	rcu_read_unlock();
@@ -3368,7 +3576,7 @@ static bool bond_ab_arp_probe(struct bonding *bond)
			    curr_active_slave->dev->name);

	if (curr_active_slave) {
		bond_arp_send_all(bond, curr_active_slave);
		bond_send_validate(bond, curr_active_slave);
		return should_notify_rtnl;
	}

@@ -3420,7 +3628,7 @@ static bool bond_ab_arp_probe(struct bonding *bond)
	bond_set_slave_link_state(new_slave, BOND_LINK_BACK,
				  BOND_SLAVE_NOTIFY_LATER);
	bond_set_slave_active_flags(new_slave, BOND_SLAVE_NOTIFY_LATER);
	bond_arp_send_all(bond, new_slave);
	bond_send_validate(bond, new_slave);
	new_slave->last_link_up = jiffies;
	rcu_assign_pointer(bond->current_arp_slave, new_slave);

@@ -3956,7 +4164,7 @@ static int bond_open(struct net_device *bond_dev)

	if (bond->params.arp_interval) {  /* arp interval, in milliseconds. */
		queue_delayed_work(bond->wq, &bond->arp_work, 0);
		bond->recv_probe = bond_arp_rcv;
		bond->recv_probe = bond_rcv_validate;
	}

	if (BOND_MODE(bond) == BOND_MODE_8023AD) {
@@ -5937,6 +6145,7 @@ static int bond_check_params(struct bond_params *params)
		strscpy_pad(params->primary, primary, sizeof(params->primary));

	memcpy(params->arp_targets, arp_target, sizeof(arp_target));
	memset(params->ns_targets, 0, sizeof(struct in6_addr) * BOND_MAX_NS_TARGETS);

	return 0;
}
+59 −0
Original line number Diff line number Diff line
@@ -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))
+73 −1
Original line number Diff line number Diff line
@@ -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",
@@ -1052,7 +1065,7 @@ static int bond_option_arp_interval_set(struct bonding *bond,
			cancel_delayed_work_sync(&bond->arp_work);
		} else {
			/* arp_validate can be set only in active-backup mode */
			bond->recv_probe = bond_arp_rcv;
			bond->recv_probe = bond_rcv_validate;
			cancel_delayed_work_sync(&bond->mii_work);
			queue_delayed_work(bond->wq, &bond->arp_work, 0);
		}
@@ -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)
{
+22 −9
Original line number Diff line number Diff line
@@ -66,19 +66,24 @@ enum {
	BOND_OPT_PEER_NOTIF_DELAY,
	BOND_OPT_LACP_ACTIVE,
	BOND_OPT_MISSED_MAX,
	BOND_OPT_NS_TARGETS,
	BOND_OPT_LAST
};

/* This structure is used for storing option values and for passing option
 * values when changing an option. The logic when used as an arg is as follows:
 * - if string != NULL -> parse it, if the opt is RAW type then return it, else
 *   return the parse result
 * - if string == NULL -> parse value
 * - 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
 */

#define BOND_OPT_EXTRA_MAXLEN 16
struct bond_opt_value {
	char *string;
	u64 value;
	u32 flags;
	char extra[BOND_OPT_EXTRA_MAXLEN];
};

struct bonding;
@@ -118,18 +123,26 @@ const struct bond_opt_value *bond_opt_get_val(unsigned int option, u64 val);
 * When value is ULLONG_MAX then string will be used.
 */
static inline void __bond_opt_init(struct bond_opt_value *optval,
				   char *string, u64 value)
				   char *string, u64 value,
				   void *extra, size_t extra_len)
{
	memset(optval, 0, sizeof(*optval));
	optval->value = ULLONG_MAX;
	if (value == ULLONG_MAX)
		optval->string = string;
	else
	if (value != ULLONG_MAX)
		optval->value = value;
	else if (string)
		optval->string = string;
	else if (extra_len <= BOND_OPT_EXTRA_MAXLEN)
		memcpy(optval->extra, extra, extra_len);
}
#define bond_opt_initval(optval, value) __bond_opt_init(optval, NULL, value)
#define bond_opt_initstr(optval, str) __bond_opt_init(optval, str, ULLONG_MAX)
#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)
#define bond_opt_initextra(optval, extra, extra_len) \
	__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 */
Loading