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

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
...@@ -313,6 +313,17 @@ arp_ip_target ...@@ -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
......
This diff is collapsed.
...@@ -14,6 +14,7 @@ ...@@ -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] = { ...@@ -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[], ...@@ -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) ...@@ -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, ...@@ -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))
......
...@@ -34,6 +34,10 @@ static int bond_option_arp_ip_target_add(struct bonding *bond, __be32 target); ...@@ -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] = { ...@@ -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",
...@@ -1052,7 +1065,7 @@ static int bond_option_arp_interval_set(struct bonding *bond, ...@@ -1052,7 +1065,7 @@ static int bond_option_arp_interval_set(struct bonding *bond,
cancel_delayed_work_sync(&bond->arp_work); cancel_delayed_work_sync(&bond->arp_work);
} else { } else {
/* arp_validate can be set only in active-backup mode */ /* 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); cancel_delayed_work_sync(&bond->mii_work);
queue_delayed_work(bond->wq, &bond->arp_work, 0); queue_delayed_work(bond->wq, &bond->arp_work, 0);
} }
...@@ -1184,6 +1197,65 @@ static int bond_option_arp_ip_targets_set(struct bonding *bond, ...@@ -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)
{ {
......
...@@ -66,19 +66,24 @@ enum { ...@@ -66,19 +66,24 @@ 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
}; };
/* This structure is used for storing option values and for passing option /* 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: * 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 * - if value != ULLONG_MAX -> parse value
* return the parse result * - if string != NULL -> parse string
* - if string == NULL -> parse value * - 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 { struct bond_opt_value {
char *string; char *string;
u64 value; u64 value;
u32 flags; u32 flags;
char extra[BOND_OPT_EXTRA_MAXLEN];
}; };
struct bonding; struct bonding;
...@@ -118,18 +123,26 @@ const struct bond_opt_value *bond_opt_get_val(unsigned int option, u64 val); ...@@ -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. * When value is ULLONG_MAX then string will be used.
*/ */
static inline void __bond_opt_init(struct bond_opt_value *optval, 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)); memset(optval, 0, sizeof(*optval));
optval->value = ULLONG_MAX; optval->value = ULLONG_MAX;
if (value == ULLONG_MAX) if (value != ULLONG_MAX)
optval->string = string;
else
optval->value = value; 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_initval(optval, value) __bond_opt_init(optval, NULL, value, NULL, 0)
#define bond_opt_initstr(optval, str) __bond_opt_init(optval, str, ULLONG_MAX) #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); 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 */
...@@ -29,8 +29,11 @@ ...@@ -29,8 +29,11 @@
#include <net/bond_3ad.h> #include <net/bond_3ad.h>
#include <net/bond_alb.h> #include <net/bond_alb.h>
#include <net/bond_options.h> #include <net/bond_options.h>
#include <net/ipv6.h>
#include <net/addrconf.h>
#define BOND_MAX_ARP_TARGETS 16 #define BOND_MAX_ARP_TARGETS 16
#define BOND_MAX_NS_TARGETS BOND_MAX_ARP_TARGETS
#define BOND_DEFAULT_MIIMON 100 #define BOND_DEFAULT_MIIMON 100
...@@ -146,6 +149,7 @@ struct bond_params { ...@@ -146,6 +149,7 @@ struct bond_params {
struct reciprocal_value reciprocal_packets_per_slave; struct reciprocal_value reciprocal_packets_per_slave;
u16 ad_actor_sys_prio; u16 ad_actor_sys_prio;
u16 ad_user_port_key; u16 ad_user_port_key;
struct in6_addr ns_targets[BOND_MAX_NS_TARGETS];
/* 2 bytes of padding : see ether_addr_equal_64bits() */ /* 2 bytes of padding : see ether_addr_equal_64bits() */
u8 ad_actor_system[ETH_ALEN + 2]; u8 ad_actor_system[ETH_ALEN + 2];
...@@ -499,6 +503,13 @@ static inline int bond_is_ip_target_ok(__be32 addr) ...@@ -499,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.
*/ */
...@@ -628,7 +639,7 @@ struct bond_net { ...@@ -628,7 +639,7 @@ struct bond_net {
struct class_attribute class_attr_bonding_masters; 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); 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(struct net *net, const char *name);
int bond_create_sysfs(struct bond_net *net); int bond_create_sysfs(struct bond_net *net);
...@@ -735,6 +746,19 @@ static inline int bond_get_targets_ip(__be32 *targets, __be32 ip) ...@@ -735,6 +746,19 @@ static inline int bond_get_targets_ip(__be32 *targets, __be32 ip)
return -1; 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 */ /* exported from bond_main.c */
extern unsigned int bond_net_id; extern unsigned int bond_net_id;
......
...@@ -447,10 +447,15 @@ void ndisc_cleanup(void); ...@@ -447,10 +447,15 @@ void ndisc_cleanup(void);
int ndisc_rcv(struct sk_buff *skb); 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, void ndisc_send_ns(struct net_device *dev, const struct in6_addr *solicit,
const struct in6_addr *daddr, const struct in6_addr *saddr, const struct in6_addr *daddr, const struct in6_addr *saddr,
u64 nonce); 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, void ndisc_send_rs(struct net_device *dev,
const struct in6_addr *saddr, const struct in6_addr *daddr); const struct in6_addr *saddr, const struct in6_addr *daddr);
void ndisc_send_na(struct net_device *dev, const struct in6_addr *daddr, void ndisc_send_na(struct net_device *dev, const struct in6_addr *daddr,
......
...@@ -860,6 +860,7 @@ enum { ...@@ -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,
}; };
......
...@@ -466,9 +466,8 @@ static void ip6_nd_hdr(struct sk_buff *skb, ...@@ -466,9 +466,8 @@ static void ip6_nd_hdr(struct sk_buff *skb,
hdr->daddr = *daddr; hdr->daddr = *daddr;
} }
static void ndisc_send_skb(struct sk_buff *skb, void ndisc_send_skb(struct sk_buff *skb, const struct in6_addr *daddr,
const struct in6_addr *daddr, const struct in6_addr *saddr)
const struct in6_addr *saddr)
{ {
struct dst_entry *dst = skb_dst(skb); struct dst_entry *dst = skb_dst(skb);
struct net *net = dev_net(skb->dev); struct net *net = dev_net(skb->dev);
...@@ -515,6 +514,7 @@ static void ndisc_send_skb(struct sk_buff *skb, ...@@ -515,6 +514,7 @@ static void ndisc_send_skb(struct sk_buff *skb,
rcu_read_unlock(); rcu_read_unlock();
} }
EXPORT_SYMBOL(ndisc_send_skb);
void ndisc_send_na(struct net_device *dev, const struct in6_addr *daddr, void ndisc_send_na(struct net_device *dev, const struct in6_addr *daddr,
const struct in6_addr *solicited_addr, const struct in6_addr *solicited_addr,
...@@ -598,22 +598,16 @@ static void ndisc_send_unsol_na(struct net_device *dev) ...@@ -598,22 +598,16 @@ static void ndisc_send_unsol_na(struct net_device *dev)
in6_dev_put(idev); in6_dev_put(idev);
} }
void ndisc_send_ns(struct net_device *dev, const struct in6_addr *solicit, struct sk_buff *ndisc_ns_create(struct net_device *dev, const struct in6_addr *solicit,
const struct in6_addr *daddr, const struct in6_addr *saddr, const struct in6_addr *saddr, u64 nonce)
u64 nonce)
{ {
struct sk_buff *skb;
struct in6_addr addr_buf;
int inc_opt = dev->addr_len; int inc_opt = dev->addr_len;
int optlen = 0; struct sk_buff *skb;
struct nd_msg *msg; struct nd_msg *msg;
int optlen = 0;
if (!saddr) { if (!saddr)
if (ipv6_get_lladdr(dev, &addr_buf, return NULL;
(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)))
return;
saddr = &addr_buf;
}
if (ipv6_addr_any(saddr)) if (ipv6_addr_any(saddr))
inc_opt = false; inc_opt = false;
...@@ -625,7 +619,7 @@ void ndisc_send_ns(struct net_device *dev, const struct in6_addr *solicit, ...@@ -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); skb = ndisc_alloc_skb(dev, sizeof(*msg) + optlen);
if (!skb) if (!skb)
return; return NULL;
msg = skb_put(skb, sizeof(*msg)); msg = skb_put(skb, sizeof(*msg));
*msg = (struct nd_msg) { *msg = (struct nd_msg) {
...@@ -647,7 +641,28 @@ void ndisc_send_ns(struct net_device *dev, const struct in6_addr *solicit, ...@@ -647,7 +641,28 @@ void ndisc_send_ns(struct net_device *dev, const struct in6_addr *solicit,
memcpy(opt + 2, &nonce, 6); 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, void ndisc_send_rs(struct net_device *dev, const struct in6_addr *saddr,
......
...@@ -860,6 +860,7 @@ enum { ...@@ -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,
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment