mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-04-29 10:34:22 -04:00
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: David S. Miller <davem@davemloft.net>
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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:
|
||||
arp_xmit(skb);
|
||||
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,
|
||||
struct slave *slave)
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -29,8 +29,11 @@
|
||||
#include <net/bond_3ad.h>
|
||||
#include <net/bond_alb.h>
|
||||
#include <net/bond_options.h>
|
||||
#include <net/ipv6.h>
|
||||
#include <net/addrconf.h>
|
||||
|
||||
#define BOND_MAX_ARP_TARGETS 16
|
||||
#define BOND_MAX_NS_TARGETS BOND_MAX_ARP_TARGETS
|
||||
|
||||
#define BOND_DEFAULT_MIIMON 100
|
||||
|
||||
@@ -146,6 +149,7 @@ struct bond_params {
|
||||
struct reciprocal_value reciprocal_packets_per_slave;
|
||||
u16 ad_actor_sys_prio;
|
||||
u16 ad_user_port_key;
|
||||
struct in6_addr ns_targets[BOND_MAX_NS_TARGETS];
|
||||
|
||||
/* 2 bytes of padding : see ether_addr_equal_64bits() */
|
||||
u8 ad_actor_system[ETH_ALEN + 2];
|
||||
@@ -499,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.
|
||||
*/
|
||||
@@ -628,7 +639,7 @@ struct bond_net {
|
||||
struct class_attribute class_attr_bonding_masters;
|
||||
};
|
||||
|
||||
int bond_arp_rcv(const struct sk_buff *skb, struct bonding *bond, struct slave *slave);
|
||||
int bond_rcv_validate(const struct sk_buff *skb, struct bonding *bond, struct slave *slave);
|
||||
netdev_tx_t bond_dev_queue_xmit(struct bonding *bond, struct sk_buff *skb, struct net_device *slave_dev);
|
||||
int bond_create(struct net *net, const char *name);
|
||||
int bond_create_sysfs(struct bond_net *net);
|
||||
@@ -735,6 +746,19 @@ static inline int bond_get_targets_ip(__be32 *targets, __be32 ip)
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int bond_get_targets_ip6(struct in6_addr *targets, struct in6_addr *ip)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < BOND_MAX_NS_TARGETS; i++)
|
||||
if (ipv6_addr_equal(&targets[i], ip))
|
||||
return i;
|
||||
else if (ipv6_addr_any(&targets[i]))
|
||||
break;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* exported from bond_main.c */
|
||||
extern unsigned int bond_net_id;
|
||||
|
||||
|
||||
@@ -447,10 +447,15 @@ void ndisc_cleanup(void);
|
||||
|
||||
int ndisc_rcv(struct sk_buff *skb);
|
||||
|
||||
struct sk_buff *ndisc_ns_create(struct net_device *dev, const struct in6_addr *solicit,
|
||||
const struct in6_addr *saddr, u64 nonce);
|
||||
void ndisc_send_ns(struct net_device *dev, const struct in6_addr *solicit,
|
||||
const struct in6_addr *daddr, const struct in6_addr *saddr,
|
||||
u64 nonce);
|
||||
|
||||
void ndisc_send_skb(struct sk_buff *skb, const struct in6_addr *daddr,
|
||||
const struct in6_addr *saddr);
|
||||
|
||||
void ndisc_send_rs(struct net_device *dev,
|
||||
const struct in6_addr *saddr, const struct in6_addr *daddr);
|
||||
void ndisc_send_na(struct net_device *dev, const struct in6_addr *daddr,
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
|
||||
@@ -466,9 +466,8 @@ static void ip6_nd_hdr(struct sk_buff *skb,
|
||||
hdr->daddr = *daddr;
|
||||
}
|
||||
|
||||
static void ndisc_send_skb(struct sk_buff *skb,
|
||||
const struct in6_addr *daddr,
|
||||
const struct in6_addr *saddr)
|
||||
void ndisc_send_skb(struct sk_buff *skb, const struct in6_addr *daddr,
|
||||
const struct in6_addr *saddr)
|
||||
{
|
||||
struct dst_entry *dst = skb_dst(skb);
|
||||
struct net *net = dev_net(skb->dev);
|
||||
@@ -515,6 +514,7 @@ static void ndisc_send_skb(struct sk_buff *skb,
|
||||
|
||||
rcu_read_unlock();
|
||||
}
|
||||
EXPORT_SYMBOL(ndisc_send_skb);
|
||||
|
||||
void ndisc_send_na(struct net_device *dev, const struct in6_addr *daddr,
|
||||
const struct in6_addr *solicited_addr,
|
||||
@@ -598,22 +598,16 @@ static void ndisc_send_unsol_na(struct net_device *dev)
|
||||
in6_dev_put(idev);
|
||||
}
|
||||
|
||||
void ndisc_send_ns(struct net_device *dev, const struct in6_addr *solicit,
|
||||
const struct in6_addr *daddr, const struct in6_addr *saddr,
|
||||
u64 nonce)
|
||||
struct sk_buff *ndisc_ns_create(struct net_device *dev, const struct in6_addr *solicit,
|
||||
const struct in6_addr *saddr, u64 nonce)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
struct in6_addr addr_buf;
|
||||
int inc_opt = dev->addr_len;
|
||||
int optlen = 0;
|
||||
struct sk_buff *skb;
|
||||
struct nd_msg *msg;
|
||||
int optlen = 0;
|
||||
|
||||
if (!saddr) {
|
||||
if (ipv6_get_lladdr(dev, &addr_buf,
|
||||
(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)))
|
||||
return;
|
||||
saddr = &addr_buf;
|
||||
}
|
||||
if (!saddr)
|
||||
return NULL;
|
||||
|
||||
if (ipv6_addr_any(saddr))
|
||||
inc_opt = false;
|
||||
@@ -625,7 +619,7 @@ void ndisc_send_ns(struct net_device *dev, const struct in6_addr *solicit,
|
||||
|
||||
skb = ndisc_alloc_skb(dev, sizeof(*msg) + optlen);
|
||||
if (!skb)
|
||||
return;
|
||||
return NULL;
|
||||
|
||||
msg = skb_put(skb, sizeof(*msg));
|
||||
*msg = (struct nd_msg) {
|
||||
@@ -647,7 +641,28 @@ void ndisc_send_ns(struct net_device *dev, const struct in6_addr *solicit,
|
||||
memcpy(opt + 2, &nonce, 6);
|
||||
}
|
||||
|
||||
ndisc_send_skb(skb, daddr, saddr);
|
||||
return skb;
|
||||
}
|
||||
EXPORT_SYMBOL(ndisc_ns_create);
|
||||
|
||||
void ndisc_send_ns(struct net_device *dev, const struct in6_addr *solicit,
|
||||
const struct in6_addr *daddr, const struct in6_addr *saddr,
|
||||
u64 nonce)
|
||||
{
|
||||
struct in6_addr addr_buf;
|
||||
struct sk_buff *skb;
|
||||
|
||||
if (!saddr) {
|
||||
if (ipv6_get_lladdr(dev, &addr_buf,
|
||||
(IFA_F_TENTATIVE | IFA_F_OPTIMISTIC)))
|
||||
return;
|
||||
saddr = &addr_buf;
|
||||
}
|
||||
|
||||
skb = ndisc_ns_create(dev, solicit, saddr, nonce);
|
||||
|
||||
if (skb)
|
||||
ndisc_send_skb(skb, daddr, saddr);
|
||||
}
|
||||
|
||||
void ndisc_send_rs(struct net_device *dev, const struct in6_addr *saddr,
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user