2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* Neighbour Discovery for IPv6
|
2007-02-09 22:24:49 +08:00
|
|
|
* Linux INET6 implementation
|
2005-04-17 06:20:36 +08:00
|
|
|
*
|
|
|
|
* Authors:
|
2007-02-09 22:24:49 +08:00
|
|
|
* Pedro Roque <roque@di.fc.ul.pt>
|
2005-04-17 06:20:36 +08:00
|
|
|
* Mike Shaver <shaver@ingenia.com>
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version
|
|
|
|
* 2 of the License, or (at your option) any later version.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Changes:
|
|
|
|
*
|
2012-04-06 13:50:58 +08:00
|
|
|
* Alexey I. Froloff : RFC6106 (DNSSL) support
|
2007-10-11 12:22:05 +08:00
|
|
|
* Pierre Ynard : export userland ND options
|
|
|
|
* through netlink (RDNSS support)
|
2005-04-17 06:20:36 +08:00
|
|
|
* Lars Fenneberg : fixed MTU setting on receipt
|
|
|
|
* of an RA.
|
|
|
|
* Janos Farkas : kmalloc failure checks
|
|
|
|
* Alexey Kuznetsov : state machine reworked
|
|
|
|
* and moved to net/core.
|
|
|
|
* Pekka Savola : RFC2461 validation
|
|
|
|
* YOSHIFUJI Hideaki @USAGI : Verify ND options properly
|
|
|
|
*/
|
|
|
|
|
2012-05-17 03:28:38 +08:00
|
|
|
#define pr_fmt(fmt) "ICMPv6: " fmt
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/errno.h>
|
|
|
|
#include <linux/types.h>
|
|
|
|
#include <linux/socket.h>
|
|
|
|
#include <linux/sockios.h>
|
|
|
|
#include <linux/sched.h>
|
|
|
|
#include <linux/net.h>
|
|
|
|
#include <linux/in6.h>
|
|
|
|
#include <linux/route.h>
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/rcupdate.h>
|
include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h
percpu.h is included by sched.h and module.h and thus ends up being
included when building most .c files. percpu.h includes slab.h which
in turn includes gfp.h making everything defined by the two files
universally available and complicating inclusion dependencies.
percpu.h -> slab.h dependency is about to be removed. Prepare for
this change by updating users of gfp and slab facilities include those
headers directly instead of assuming availability. As this conversion
needs to touch large number of source files, the following script is
used as the basis of conversion.
http://userweb.kernel.org/~tj/misc/slabh-sweep.py
The script does the followings.
* Scan files for gfp and slab usages and update includes such that
only the necessary includes are there. ie. if only gfp is used,
gfp.h, if slab is used, slab.h.
* When the script inserts a new include, it looks at the include
blocks and try to put the new include such that its order conforms
to its surrounding. It's put in the include block which contains
core kernel includes, in the same order that the rest are ordered -
alphabetical, Christmas tree, rev-Xmas-tree or at the end if there
doesn't seem to be any matching order.
* If the script can't find a place to put a new include (mostly
because the file doesn't have fitting include block), it prints out
an error message indicating which .h file needs to be added to the
file.
The conversion was done in the following steps.
1. The initial automatic conversion of all .c files updated slightly
over 4000 files, deleting around 700 includes and adding ~480 gfp.h
and ~3000 slab.h inclusions. The script emitted errors for ~400
files.
2. Each error was manually checked. Some didn't need the inclusion,
some needed manual addition while adding it to implementation .h or
embedding .c file was more appropriate for others. This step added
inclusions to around 150 files.
3. The script was run again and the output was compared to the edits
from #2 to make sure no file was left behind.
4. Several build tests were done and a couple of problems were fixed.
e.g. lib/decompress_*.c used malloc/free() wrappers around slab
APIs requiring slab.h to be added manually.
5. The script was run on all .h files but without automatically
editing them as sprinkling gfp.h and slab.h inclusions around .h
files could easily lead to inclusion dependency hell. Most gfp.h
inclusion directives were ignored as stuff from gfp.h was usually
wildly available and often used in preprocessor macros. Each
slab.h inclusion directive was examined and added manually as
necessary.
6. percpu.h was updated not to include slab.h.
7. Build test were done on the following configurations and failures
were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my
distributed build env didn't work with gcov compiles) and a few
more options had to be turned off depending on archs to make things
build (like ipr on powerpc/64 which failed due to missing writeq).
* x86 and x86_64 UP and SMP allmodconfig and a custom test config.
* powerpc and powerpc64 SMP allmodconfig
* sparc and sparc64 SMP allmodconfig
* ia64 SMP allmodconfig
* s390 SMP allmodconfig
* alpha SMP allmodconfig
* um on x86_64 SMP allmodconfig
8. percpu.h modifications were reverted so that it could be applied as
a separate patch and serve as bisection point.
Given the fact that I had only a couple of failures from tests on step
6, I'm fairly confident about the coverage of this conversion patch.
If there is a breakage, it's likely to be something in one of the arch
headers which should be easily discoverable easily on most builds of
the specific arch.
Signed-off-by: Tejun Heo <tj@kernel.org>
Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
2010-03-24 16:04:11 +08:00
|
|
|
#include <linux/slab.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#ifdef CONFIG_SYSCTL
|
|
|
|
#include <linux/sysctl.h>
|
|
|
|
#endif
|
|
|
|
|
2006-08-05 14:04:54 +08:00
|
|
|
#include <linux/if_addr.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <linux/if_arp.h>
|
|
|
|
#include <linux/ipv6.h>
|
|
|
|
#include <linux/icmpv6.h>
|
|
|
|
#include <linux/jhash.h>
|
|
|
|
|
|
|
|
#include <net/sock.h>
|
|
|
|
#include <net/snmp.h>
|
|
|
|
|
|
|
|
#include <net/ipv6.h>
|
|
|
|
#include <net/protocol.h>
|
|
|
|
#include <net/ndisc.h>
|
|
|
|
#include <net/ip6_route.h>
|
|
|
|
#include <net/addrconf.h>
|
|
|
|
#include <net/icmp.h>
|
|
|
|
|
2007-10-11 12:22:05 +08:00
|
|
|
#include <net/netlink.h>
|
|
|
|
#include <linux/rtnetlink.h>
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <net/flow.h>
|
|
|
|
#include <net/ip6_checksum.h>
|
2008-04-04 05:31:03 +08:00
|
|
|
#include <net/inet_common.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <linux/proc_fs.h>
|
|
|
|
|
|
|
|
#include <linux/netfilter.h>
|
|
|
|
#include <linux/netfilter_ipv6.h>
|
|
|
|
|
2012-05-17 03:28:38 +08:00
|
|
|
/* Set to 3 to get tracing... */
|
|
|
|
#define ND_DEBUG 1
|
|
|
|
|
|
|
|
#define ND_PRINTK(val, level, fmt, ...) \
|
|
|
|
do { \
|
|
|
|
if (val <= ND_DEBUG) \
|
|
|
|
net_##level##_ratelimited(fmt, ##__VA_ARGS__); \
|
|
|
|
} while (0)
|
|
|
|
|
2010-10-04 14:15:44 +08:00
|
|
|
static u32 ndisc_hash(const void *pkey,
|
|
|
|
const struct net_device *dev,
|
2011-12-29 04:06:58 +08:00
|
|
|
__u32 *hash_rnd);
|
2005-04-17 06:20:36 +08:00
|
|
|
static int ndisc_constructor(struct neighbour *neigh);
|
|
|
|
static void ndisc_solicit(struct neighbour *neigh, struct sk_buff *skb);
|
|
|
|
static void ndisc_error_report(struct neighbour *neigh, struct sk_buff *skb);
|
|
|
|
static int pndisc_constructor(struct pneigh_entry *n);
|
|
|
|
static void pndisc_destructor(struct pneigh_entry *n);
|
|
|
|
static void pndisc_redo(struct sk_buff *skb);
|
|
|
|
|
2009-09-01 19:13:19 +08:00
|
|
|
static const struct neigh_ops ndisc_generic_ops = {
|
2005-04-17 06:20:36 +08:00
|
|
|
.family = AF_INET6,
|
|
|
|
.solicit = ndisc_solicit,
|
|
|
|
.error_report = ndisc_error_report,
|
|
|
|
.output = neigh_resolve_output,
|
|
|
|
.connected_output = neigh_connected_output,
|
|
|
|
};
|
|
|
|
|
2009-09-01 19:13:19 +08:00
|
|
|
static const struct neigh_ops ndisc_hh_ops = {
|
2005-04-17 06:20:36 +08:00
|
|
|
.family = AF_INET6,
|
|
|
|
.solicit = ndisc_solicit,
|
|
|
|
.error_report = ndisc_error_report,
|
|
|
|
.output = neigh_resolve_output,
|
|
|
|
.connected_output = neigh_resolve_output,
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2009-09-01 19:13:19 +08:00
|
|
|
static const struct neigh_ops ndisc_direct_ops = {
|
2005-04-17 06:20:36 +08:00
|
|
|
.family = AF_INET6,
|
2011-07-18 04:34:11 +08:00
|
|
|
.output = neigh_direct_output,
|
|
|
|
.connected_output = neigh_direct_output,
|
2005-04-17 06:20:36 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
struct neigh_table nd_tbl = {
|
|
|
|
.family = AF_INET6,
|
|
|
|
.key_len = sizeof(struct in6_addr),
|
|
|
|
.hash = ndisc_hash,
|
|
|
|
.constructor = ndisc_constructor,
|
|
|
|
.pconstructor = pndisc_constructor,
|
|
|
|
.pdestructor = pndisc_destructor,
|
|
|
|
.proxy_redo = pndisc_redo,
|
|
|
|
.id = "ndisc_cache",
|
|
|
|
.parms = {
|
2010-12-02 02:05:12 +08:00
|
|
|
.tbl = &nd_tbl,
|
|
|
|
.reachable_time = ND_REACHABLE_TIME,
|
2013-12-08 02:26:53 +08:00
|
|
|
.data = {
|
|
|
|
[NEIGH_VAR_MCAST_PROBES] = 3,
|
|
|
|
[NEIGH_VAR_UCAST_PROBES] = 3,
|
|
|
|
[NEIGH_VAR_RETRANS_TIME] = ND_RETRANS_TIMER,
|
|
|
|
[NEIGH_VAR_BASE_REACHABLE_TIME] = ND_REACHABLE_TIME,
|
|
|
|
[NEIGH_VAR_DELAY_PROBE_TIME] = 5 * HZ,
|
|
|
|
[NEIGH_VAR_GC_STALETIME] = 60 * HZ,
|
|
|
|
[NEIGH_VAR_QUEUE_LEN_BYTES] = 64 * 1024,
|
|
|
|
[NEIGH_VAR_PROXY_QLEN] = 64,
|
|
|
|
[NEIGH_VAR_ANYCAST_DELAY] = 1 * HZ,
|
|
|
|
[NEIGH_VAR_PROXY_DELAY] = (8 * HZ) / 10,
|
|
|
|
},
|
2005-04-17 06:20:36 +08:00
|
|
|
},
|
|
|
|
.gc_interval = 30 * HZ,
|
|
|
|
.gc_thresh1 = 128,
|
|
|
|
.gc_thresh2 = 512,
|
|
|
|
.gc_thresh3 = 1024,
|
|
|
|
};
|
|
|
|
|
2013-01-21 14:48:53 +08:00
|
|
|
static void ndisc_fill_addr_option(struct sk_buff *skb, int type, void *data)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2013-01-21 14:48:53 +08:00
|
|
|
int pad = ndisc_addr_option_pad(skb->dev->type);
|
|
|
|
int data_len = skb->dev->addr_len;
|
|
|
|
int space = ndisc_opt_addr_space(skb->dev);
|
|
|
|
u8 *opt = skb_put(skb, space);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
opt[0] = type;
|
|
|
|
opt[1] = space>>3;
|
|
|
|
|
|
|
|
memset(opt + 2, 0, pad);
|
|
|
|
opt += pad;
|
|
|
|
space -= pad;
|
|
|
|
|
|
|
|
memcpy(opt+2, data, data_len);
|
|
|
|
data_len += 2;
|
|
|
|
opt += data_len;
|
|
|
|
if ((space -= data_len) > 0)
|
|
|
|
memset(opt, 0, space);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct nd_opt_hdr *ndisc_next_option(struct nd_opt_hdr *cur,
|
|
|
|
struct nd_opt_hdr *end)
|
|
|
|
{
|
|
|
|
int type;
|
|
|
|
if (!cur || !end || cur >= end)
|
|
|
|
return NULL;
|
|
|
|
type = cur->nd_opt_type;
|
|
|
|
do {
|
|
|
|
cur = ((void *)cur) + (cur->nd_opt_len << 3);
|
|
|
|
} while(cur < end && cur->nd_opt_type != type);
|
2010-09-23 04:43:57 +08:00
|
|
|
return cur <= end && cur->nd_opt_type == type ? cur : NULL;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2007-10-11 12:22:05 +08:00
|
|
|
static inline int ndisc_is_useropt(struct nd_opt_hdr *opt)
|
|
|
|
{
|
2012-04-06 13:50:58 +08:00
|
|
|
return opt->nd_opt_type == ND_OPT_RDNSS ||
|
|
|
|
opt->nd_opt_type == ND_OPT_DNSSL;
|
2007-10-11 12:22:05 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct nd_opt_hdr *ndisc_next_useropt(struct nd_opt_hdr *cur,
|
|
|
|
struct nd_opt_hdr *end)
|
|
|
|
{
|
|
|
|
if (!cur || !end || cur >= end)
|
|
|
|
return NULL;
|
|
|
|
do {
|
|
|
|
cur = ((void *)cur) + (cur->nd_opt_len << 3);
|
|
|
|
} while(cur < end && !ndisc_is_useropt(cur));
|
2010-09-23 04:43:57 +08:00
|
|
|
return cur <= end && ndisc_is_useropt(cur) ? cur : NULL;
|
2007-10-11 12:22:05 +08:00
|
|
|
}
|
|
|
|
|
2012-07-12 14:26:46 +08:00
|
|
|
struct ndisc_options *ndisc_parse_options(u8 *opt, int opt_len,
|
|
|
|
struct ndisc_options *ndopts)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)opt;
|
|
|
|
|
|
|
|
if (!nd_opt || opt_len < 0 || !ndopts)
|
|
|
|
return NULL;
|
|
|
|
memset(ndopts, 0, sizeof(*ndopts));
|
|
|
|
while (opt_len) {
|
|
|
|
int l;
|
|
|
|
if (opt_len < sizeof(struct nd_opt_hdr))
|
|
|
|
return NULL;
|
|
|
|
l = nd_opt->nd_opt_len << 3;
|
|
|
|
if (opt_len < l || l == 0)
|
|
|
|
return NULL;
|
|
|
|
switch (nd_opt->nd_opt_type) {
|
|
|
|
case ND_OPT_SOURCE_LL_ADDR:
|
|
|
|
case ND_OPT_TARGET_LL_ADDR:
|
|
|
|
case ND_OPT_MTU:
|
|
|
|
case ND_OPT_REDIRECT_HDR:
|
|
|
|
if (ndopts->nd_opt_array[nd_opt->nd_opt_type]) {
|
2012-05-17 03:28:38 +08:00
|
|
|
ND_PRINTK(2, warn,
|
|
|
|
"%s: duplicated ND6 option found: type=%d\n",
|
|
|
|
__func__, nd_opt->nd_opt_type);
|
2005-04-17 06:20:36 +08:00
|
|
|
} else {
|
|
|
|
ndopts->nd_opt_array[nd_opt->nd_opt_type] = nd_opt;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case ND_OPT_PREFIX_INFO:
|
|
|
|
ndopts->nd_opts_pi_end = nd_opt;
|
2007-10-09 16:59:42 +08:00
|
|
|
if (!ndopts->nd_opt_array[nd_opt->nd_opt_type])
|
2005-04-17 06:20:36 +08:00
|
|
|
ndopts->nd_opt_array[nd_opt->nd_opt_type] = nd_opt;
|
|
|
|
break;
|
2006-03-21 09:06:24 +08:00
|
|
|
#ifdef CONFIG_IPV6_ROUTE_INFO
|
|
|
|
case ND_OPT_ROUTE_INFO:
|
|
|
|
ndopts->nd_opts_ri_end = nd_opt;
|
|
|
|
if (!ndopts->nd_opts_ri)
|
|
|
|
ndopts->nd_opts_ri = nd_opt;
|
|
|
|
break;
|
|
|
|
#endif
|
2005-04-17 06:20:36 +08:00
|
|
|
default:
|
2007-10-11 12:22:05 +08:00
|
|
|
if (ndisc_is_useropt(nd_opt)) {
|
|
|
|
ndopts->nd_useropts_end = nd_opt;
|
|
|
|
if (!ndopts->nd_useropts)
|
|
|
|
ndopts->nd_useropts = nd_opt;
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* Unknown options must be silently ignored,
|
|
|
|
* to accommodate future extension to the
|
|
|
|
* protocol.
|
|
|
|
*/
|
2012-05-17 03:28:38 +08:00
|
|
|
ND_PRINTK(2, notice,
|
|
|
|
"%s: ignored unsupported option; type=%d, len=%d\n",
|
|
|
|
__func__,
|
|
|
|
nd_opt->nd_opt_type,
|
|
|
|
nd_opt->nd_opt_len);
|
2007-10-11 12:22:05 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
opt_len -= l;
|
|
|
|
nd_opt = ((void *)nd_opt) + l;
|
|
|
|
}
|
|
|
|
return ndopts;
|
|
|
|
}
|
|
|
|
|
2011-04-22 12:53:02 +08:00
|
|
|
int ndisc_mc_map(const struct in6_addr *addr, char *buf, struct net_device *dev, int dir)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
switch (dev->type) {
|
|
|
|
case ARPHRD_ETHER:
|
|
|
|
case ARPHRD_IEEE802: /* Not sure. Check it later. --ANK */
|
|
|
|
case ARPHRD_FDDI:
|
|
|
|
ipv6_eth_mc_map(addr, buf);
|
|
|
|
return 0;
|
|
|
|
case ARPHRD_ARCNET:
|
|
|
|
ipv6_arcnet_mc_map(addr, buf);
|
|
|
|
return 0;
|
|
|
|
case ARPHRD_INFINIBAND:
|
2007-12-11 04:38:41 +08:00
|
|
|
ipv6_ib_mc_map(addr, dev->broadcast, buf);
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
2011-03-29 06:40:53 +08:00
|
|
|
case ARPHRD_IPGRE:
|
|
|
|
return ipv6_ipgre_mc_map(addr, dev->broadcast, buf);
|
2005-04-17 06:20:36 +08:00
|
|
|
default:
|
|
|
|
if (dir) {
|
|
|
|
memcpy(buf, dev->broadcast, dev->addr_len);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2007-02-22 21:05:40 +08:00
|
|
|
EXPORT_SYMBOL(ndisc_mc_map);
|
|
|
|
|
2010-10-04 14:15:44 +08:00
|
|
|
static u32 ndisc_hash(const void *pkey,
|
|
|
|
const struct net_device *dev,
|
2011-12-29 04:06:58 +08:00
|
|
|
__u32 *hash_rnd)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2011-12-29 04:06:58 +08:00
|
|
|
return ndisc_hashfn(pkey, dev, hash_rnd);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int ndisc_constructor(struct neighbour *neigh)
|
|
|
|
{
|
|
|
|
struct in6_addr *addr = (struct in6_addr*)&neigh->primary_key;
|
|
|
|
struct net_device *dev = neigh->dev;
|
|
|
|
struct inet6_dev *in6_dev;
|
|
|
|
struct neigh_parms *parms;
|
2012-05-19 02:57:34 +08:00
|
|
|
bool is_multicast = ipv6_addr_is_multicast(addr);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
in6_dev = in6_dev_get(dev);
|
|
|
|
if (in6_dev == NULL) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
parms = in6_dev->nd_parms;
|
|
|
|
__neigh_parms_put(neigh->parms);
|
|
|
|
neigh->parms = neigh_parms_clone(parms);
|
|
|
|
|
|
|
|
neigh->type = is_multicast ? RTN_MULTICAST : RTN_UNICAST;
|
2007-10-09 16:40:57 +08:00
|
|
|
if (!dev->header_ops) {
|
2005-04-17 06:20:36 +08:00
|
|
|
neigh->nud_state = NUD_NOARP;
|
|
|
|
neigh->ops = &ndisc_direct_ops;
|
2011-07-18 04:34:11 +08:00
|
|
|
neigh->output = neigh_direct_output;
|
2005-04-17 06:20:36 +08:00
|
|
|
} else {
|
|
|
|
if (is_multicast) {
|
|
|
|
neigh->nud_state = NUD_NOARP;
|
|
|
|
ndisc_mc_map(addr, neigh->ha, dev, 1);
|
|
|
|
} else if (dev->flags&(IFF_NOARP|IFF_LOOPBACK)) {
|
|
|
|
neigh->nud_state = NUD_NOARP;
|
|
|
|
memcpy(neigh->ha, dev->dev_addr, dev->addr_len);
|
|
|
|
if (dev->flags&IFF_LOOPBACK)
|
|
|
|
neigh->type = RTN_LOCAL;
|
|
|
|
} else if (dev->flags&IFF_POINTOPOINT) {
|
|
|
|
neigh->nud_state = NUD_NOARP;
|
|
|
|
memcpy(neigh->ha, dev->broadcast, dev->addr_len);
|
|
|
|
}
|
2007-10-09 16:40:57 +08:00
|
|
|
if (dev->header_ops->cache)
|
2005-04-17 06:20:36 +08:00
|
|
|
neigh->ops = &ndisc_hh_ops;
|
|
|
|
else
|
|
|
|
neigh->ops = &ndisc_generic_ops;
|
|
|
|
if (neigh->nud_state&NUD_VALID)
|
|
|
|
neigh->output = neigh->ops->connected_output;
|
|
|
|
else
|
|
|
|
neigh->output = neigh->ops->output;
|
|
|
|
}
|
|
|
|
in6_dev_put(in6_dev);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int pndisc_constructor(struct pneigh_entry *n)
|
|
|
|
{
|
|
|
|
struct in6_addr *addr = (struct in6_addr*)&n->key;
|
|
|
|
struct in6_addr maddr;
|
|
|
|
struct net_device *dev = n->dev;
|
|
|
|
|
|
|
|
if (dev == NULL || __in6_dev_get(dev) == NULL)
|
|
|
|
return -EINVAL;
|
|
|
|
addrconf_addr_solict_mult(addr, &maddr);
|
|
|
|
ipv6_dev_mc_inc(dev, &maddr);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pndisc_destructor(struct pneigh_entry *n)
|
|
|
|
{
|
|
|
|
struct in6_addr *addr = (struct in6_addr*)&n->key;
|
|
|
|
struct in6_addr maddr;
|
|
|
|
struct net_device *dev = n->dev;
|
|
|
|
|
|
|
|
if (dev == NULL || __in6_dev_get(dev) == NULL)
|
|
|
|
return;
|
|
|
|
addrconf_addr_solict_mult(addr, &maddr);
|
|
|
|
ipv6_dev_mc_dec(dev, &maddr);
|
|
|
|
}
|
|
|
|
|
2013-01-21 14:48:14 +08:00
|
|
|
static struct sk_buff *ndisc_alloc_skb(struct net_device *dev,
|
|
|
|
int len)
|
|
|
|
{
|
|
|
|
int hlen = LL_RESERVED_SPACE(dev);
|
|
|
|
int tlen = dev->needed_tailroom;
|
|
|
|
struct sock *sk = dev_net(dev)->ipv6.ndisc_sk;
|
|
|
|
struct sk_buff *skb;
|
|
|
|
|
2013-09-03 19:37:01 +08:00
|
|
|
skb = alloc_skb(hlen + sizeof(struct ipv6hdr) + len + tlen, GFP_ATOMIC);
|
2013-01-21 14:48:14 +08:00
|
|
|
if (!skb) {
|
2013-09-03 19:37:01 +08:00
|
|
|
ND_PRINTK(0, err, "ndisc: %s failed to allocate an skb\n",
|
|
|
|
__func__);
|
2013-01-21 14:48:14 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2013-01-21 14:48:29 +08:00
|
|
|
skb->protocol = htons(ETH_P_IPV6);
|
|
|
|
skb->dev = dev;
|
|
|
|
|
2013-01-21 14:48:39 +08:00
|
|
|
skb_reserve(skb, hlen + sizeof(struct ipv6hdr));
|
2013-01-21 14:48:44 +08:00
|
|
|
skb_reset_transport_header(skb);
|
2013-01-21 14:48:14 +08:00
|
|
|
|
2013-09-03 19:37:01 +08:00
|
|
|
/* Manually assign socket ownership as we avoid calling
|
|
|
|
* sock_alloc_send_pskb() to bypass wmem buffer limits
|
|
|
|
*/
|
|
|
|
skb_set_owner_w(skb, sk);
|
|
|
|
|
2013-01-21 14:48:14 +08:00
|
|
|
return skb;
|
|
|
|
}
|
|
|
|
|
2013-01-21 14:48:29 +08:00
|
|
|
static void ip6_nd_hdr(struct sk_buff *skb,
|
2013-01-21 14:48:19 +08:00
|
|
|
const struct in6_addr *saddr,
|
|
|
|
const struct in6_addr *daddr,
|
2013-01-21 14:48:24 +08:00
|
|
|
int hop_limit, int len)
|
2013-01-21 14:48:19 +08:00
|
|
|
{
|
|
|
|
struct ipv6hdr *hdr;
|
|
|
|
|
2013-01-21 14:48:39 +08:00
|
|
|
skb_push(skb, sizeof(*hdr));
|
2013-01-21 14:48:19 +08:00
|
|
|
skb_reset_network_header(skb);
|
|
|
|
hdr = ipv6_hdr(skb);
|
|
|
|
|
|
|
|
ip6_flow_hdr(hdr, 0, 0);
|
|
|
|
|
|
|
|
hdr->payload_len = htons(len);
|
2013-01-21 14:48:24 +08:00
|
|
|
hdr->nexthdr = IPPROTO_ICMPV6;
|
|
|
|
hdr->hop_limit = hop_limit;
|
2013-01-21 14:48:19 +08:00
|
|
|
|
|
|
|
hdr->saddr = *saddr;
|
|
|
|
hdr->daddr = *daddr;
|
|
|
|
}
|
|
|
|
|
2013-01-21 14:48:34 +08:00
|
|
|
static void ndisc_send_skb(struct sk_buff *skb,
|
2012-12-13 01:40:26 +08:00
|
|
|
const struct in6_addr *daddr,
|
2013-01-21 14:48:58 +08:00
|
|
|
const struct in6_addr *saddr)
|
bonding: send IPv6 neighbor advertisement on failover
This patch adds better IPv6 failover support for bonding devices,
especially when in active-backup mode and there are only IPv6 addresses
configured, as reported by Alex Sidorenko.
- Creates a new file, net/drivers/bonding/bond_ipv6.c, for the
IPv6-specific routines. Both regular bonds and VLANs over bonds
are supported.
- Adds a new tunable, num_unsol_na, to limit the number of unsolicited
IPv6 Neighbor Advertisements that are sent on a failover event.
Default is 1.
- Creates two new IPv6 neighbor discovery functions:
ndisc_build_skb()
ndisc_send_skb()
These were required to support VLANs since we have to be able to
add the VLAN id to the skb since ndisc_send_na() and friends
shouldn't be asked to do this. These two routines are basically
__ndisc_send() split into two pieces, in a slightly different order.
- Updates Documentation/networking/bonding.txt and bumps the rev of bond
support to 3.4.0.
On failover, this new code will generate one packet:
- An unsolicited IPv6 Neighbor Advertisement, which helps the switch
learn that the address has moved to the new slave.
Testing has shown that sending just the NA results in pretty good
behavior when in active-back mode, I saw no lost ping packets for example.
Signed-off-by: Brian Haley <brian.haley@hp.com>
Signed-off-by: Jay Vosburgh <fubar@us.ibm.com>
Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
2008-11-05 09:51:14 +08:00
|
|
|
{
|
2013-01-21 14:49:03 +08:00
|
|
|
struct dst_entry *dst = skb_dst(skb);
|
2013-01-21 14:48:34 +08:00
|
|
|
struct net *net = dev_net(skb->dev);
|
2013-01-21 14:49:08 +08:00
|
|
|
struct sock *sk = net->ipv6.ndisc_sk;
|
bonding: send IPv6 neighbor advertisement on failover
This patch adds better IPv6 failover support for bonding devices,
especially when in active-backup mode and there are only IPv6 addresses
configured, as reported by Alex Sidorenko.
- Creates a new file, net/drivers/bonding/bond_ipv6.c, for the
IPv6-specific routines. Both regular bonds and VLANs over bonds
are supported.
- Adds a new tunable, num_unsol_na, to limit the number of unsolicited
IPv6 Neighbor Advertisements that are sent on a failover event.
Default is 1.
- Creates two new IPv6 neighbor discovery functions:
ndisc_build_skb()
ndisc_send_skb()
These were required to support VLANs since we have to be able to
add the VLAN id to the skb since ndisc_send_na() and friends
shouldn't be asked to do this. These two routines are basically
__ndisc_send() split into two pieces, in a slightly different order.
- Updates Documentation/networking/bonding.txt and bumps the rev of bond
support to 3.4.0.
On failover, this new code will generate one packet:
- An unsolicited IPv6 Neighbor Advertisement, which helps the switch
learn that the address has moved to the new slave.
Testing has shown that sending just the NA results in pretty good
behavior when in active-back mode, I saw no lost ping packets for example.
Signed-off-by: Brian Haley <brian.haley@hp.com>
Signed-off-by: Jay Vosburgh <fubar@us.ibm.com>
Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
2008-11-05 09:51:14 +08:00
|
|
|
struct inet6_dev *idev;
|
|
|
|
int err;
|
2013-01-21 14:48:58 +08:00
|
|
|
struct icmp6hdr *icmp6h = icmp6_hdr(skb);
|
bonding: send IPv6 neighbor advertisement on failover
This patch adds better IPv6 failover support for bonding devices,
especially when in active-backup mode and there are only IPv6 addresses
configured, as reported by Alex Sidorenko.
- Creates a new file, net/drivers/bonding/bond_ipv6.c, for the
IPv6-specific routines. Both regular bonds and VLANs over bonds
are supported.
- Adds a new tunable, num_unsol_na, to limit the number of unsolicited
IPv6 Neighbor Advertisements that are sent on a failover event.
Default is 1.
- Creates two new IPv6 neighbor discovery functions:
ndisc_build_skb()
ndisc_send_skb()
These were required to support VLANs since we have to be able to
add the VLAN id to the skb since ndisc_send_na() and friends
shouldn't be asked to do this. These two routines are basically
__ndisc_send() split into two pieces, in a slightly different order.
- Updates Documentation/networking/bonding.txt and bumps the rev of bond
support to 3.4.0.
On failover, this new code will generate one packet:
- An unsolicited IPv6 Neighbor Advertisement, which helps the switch
learn that the address has moved to the new slave.
Testing has shown that sending just the NA results in pretty good
behavior when in active-back mode, I saw no lost ping packets for example.
Signed-off-by: Brian Haley <brian.haley@hp.com>
Signed-off-by: Jay Vosburgh <fubar@us.ibm.com>
Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
2008-11-05 09:51:14 +08:00
|
|
|
u8 type;
|
|
|
|
|
|
|
|
type = icmp6h->icmp6_type;
|
|
|
|
|
2013-01-21 14:49:03 +08:00
|
|
|
if (!dst) {
|
|
|
|
struct flowi6 fl6;
|
bonding: send IPv6 neighbor advertisement on failover
This patch adds better IPv6 failover support for bonding devices,
especially when in active-backup mode and there are only IPv6 addresses
configured, as reported by Alex Sidorenko.
- Creates a new file, net/drivers/bonding/bond_ipv6.c, for the
IPv6-specific routines. Both regular bonds and VLANs over bonds
are supported.
- Adds a new tunable, num_unsol_na, to limit the number of unsolicited
IPv6 Neighbor Advertisements that are sent on a failover event.
Default is 1.
- Creates two new IPv6 neighbor discovery functions:
ndisc_build_skb()
ndisc_send_skb()
These were required to support VLANs since we have to be able to
add the VLAN id to the skb since ndisc_send_na() and friends
shouldn't be asked to do this. These two routines are basically
__ndisc_send() split into two pieces, in a slightly different order.
- Updates Documentation/networking/bonding.txt and bumps the rev of bond
support to 3.4.0.
On failover, this new code will generate one packet:
- An unsolicited IPv6 Neighbor Advertisement, which helps the switch
learn that the address has moved to the new slave.
Testing has shown that sending just the NA results in pretty good
behavior when in active-back mode, I saw no lost ping packets for example.
Signed-off-by: Brian Haley <brian.haley@hp.com>
Signed-off-by: Jay Vosburgh <fubar@us.ibm.com>
Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
2008-11-05 09:51:14 +08:00
|
|
|
|
2013-01-21 14:49:03 +08:00
|
|
|
icmpv6_flow_init(sk, &fl6, type, saddr, daddr, skb->dev->ifindex);
|
|
|
|
dst = icmp6_dst_alloc(skb->dev, &fl6);
|
|
|
|
if (IS_ERR(dst)) {
|
|
|
|
kfree_skb(skb);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
skb_dst_set(skb, dst);
|
|
|
|
}
|
2007-04-24 19:44:52 +08:00
|
|
|
|
2013-01-21 14:49:08 +08:00
|
|
|
icmp6h->icmp6_cksum = csum_ipv6_magic(saddr, daddr, skb->len,
|
|
|
|
IPPROTO_ICMPV6,
|
|
|
|
csum_partial(icmp6h,
|
|
|
|
skb->len, 0));
|
|
|
|
|
|
|
|
ip6_nd_hdr(skb, saddr, daddr, inet6_sk(sk)->hop_limit, skb->len);
|
|
|
|
|
2011-07-28 05:13:03 +08:00
|
|
|
rcu_read_lock();
|
|
|
|
idev = __in6_dev_get(dst->dev);
|
2009-04-27 17:45:02 +08:00
|
|
|
IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUT, skb->len);
|
2007-04-24 19:44:52 +08:00
|
|
|
|
2010-03-23 11:09:07 +08:00
|
|
|
err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, skb, NULL, dst->dev,
|
2007-11-20 10:53:30 +08:00
|
|
|
dst_output);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (!err) {
|
2008-10-09 01:33:50 +08:00
|
|
|
ICMP6MSGOUT_INC_STATS(net, idev, type);
|
2008-10-09 01:33:06 +08:00
|
|
|
ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2011-07-28 05:13:03 +08:00
|
|
|
rcu_read_unlock();
|
2007-02-09 22:24:49 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2013-08-31 13:44:36 +08:00
|
|
|
void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
|
|
|
|
const struct in6_addr *daddr,
|
|
|
|
const struct in6_addr *solicited_addr,
|
|
|
|
bool router, bool solicited, bool override, bool inc_opt)
|
2007-04-24 19:44:52 +08:00
|
|
|
{
|
2013-01-21 14:49:13 +08:00
|
|
|
struct sk_buff *skb;
|
2007-04-24 19:44:52 +08:00
|
|
|
struct in6_addr tmpaddr;
|
|
|
|
struct inet6_ifaddr *ifp;
|
[IPV6]: Make address arguments const.
- net/ipv6/addrconf.c:
ipv6_get_ifaddr(), ipv6_dev_get_saddr()
- net/ipv6/mcast.c:
ipv6_sock_mc_join(), ipv6_sock_mc_drop(),
inet6_mc_check(),
ipv6_dev_mc_inc(), __ipv6_dev_mc_dec(), ipv6_dev_mc_dec(),
ipv6_chk_mcast_addr()
- net/ipv6/route.c:
rt6_lookup(), icmp6_dst_alloc()
- net/ipv6/ip6_output.c:
ip6_nd_hdr()
- net/ipv6/ndisc.c:
ndisc_send_ns(), ndisc_send_rs(), ndisc_send_redirect(),
ndisc_get_neigh(), __ndisc_send()
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
2008-04-10 14:42:10 +08:00
|
|
|
const struct in6_addr *src_addr;
|
2013-01-21 14:49:17 +08:00
|
|
|
struct nd_msg *msg;
|
|
|
|
int optlen = 0;
|
2007-04-24 19:44:52 +08:00
|
|
|
|
|
|
|
/* for anycast or proxy, solicited_addr != src_addr */
|
2008-03-25 20:47:49 +08:00
|
|
|
ifp = ipv6_get_ifaddr(dev_net(dev), solicited_addr, dev, 1);
|
2007-04-24 19:44:52 +08:00
|
|
|
if (ifp) {
|
|
|
|
src_addr = solicited_addr;
|
|
|
|
if (ifp->flags & IFA_F_OPTIMISTIC)
|
2013-07-13 16:26:51 +08:00
|
|
|
override = false;
|
2010-06-21 19:00:13 +08:00
|
|
|
inc_opt |= ifp->idev->cnf.force_tllao;
|
2007-04-24 19:44:52 +08:00
|
|
|
in6_ifa_put(ifp);
|
|
|
|
} else {
|
2008-08-15 06:33:21 +08:00
|
|
|
if (ipv6_dev_get_saddr(dev_net(dev), dev, daddr,
|
2008-03-25 20:47:49 +08:00
|
|
|
inet6_sk(dev_net(dev)->ipv6.ndisc_sk)->srcprefs,
|
2008-03-25 08:37:42 +08:00
|
|
|
&tmpaddr))
|
2007-04-24 19:44:52 +08:00
|
|
|
return;
|
|
|
|
src_addr = &tmpaddr;
|
|
|
|
}
|
|
|
|
|
2013-01-21 14:49:17 +08:00
|
|
|
if (!dev->addr_len)
|
|
|
|
inc_opt = 0;
|
|
|
|
if (inc_opt)
|
|
|
|
optlen += ndisc_opt_addr_space(dev);
|
2007-04-24 19:44:52 +08:00
|
|
|
|
2013-01-21 14:49:17 +08:00
|
|
|
skb = ndisc_alloc_skb(dev, sizeof(*msg) + optlen);
|
2013-01-21 14:49:13 +08:00
|
|
|
if (!skb)
|
|
|
|
return;
|
|
|
|
|
2013-01-21 14:49:17 +08:00
|
|
|
msg = (struct nd_msg *)skb_put(skb, sizeof(*msg));
|
|
|
|
*msg = (struct nd_msg) {
|
|
|
|
.icmph = {
|
|
|
|
.icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT,
|
|
|
|
.icmp6_router = router,
|
|
|
|
.icmp6_solicited = solicited,
|
|
|
|
.icmp6_override = override,
|
|
|
|
},
|
|
|
|
.target = *solicited_addr,
|
|
|
|
};
|
|
|
|
|
|
|
|
if (inc_opt)
|
|
|
|
ndisc_fill_addr_option(skb, ND_OPT_TARGET_LL_ADDR,
|
|
|
|
dev->dev_addr);
|
|
|
|
|
|
|
|
|
2013-01-21 14:49:13 +08:00
|
|
|
ndisc_send_skb(skb, daddr, src_addr);
|
2007-04-24 19:44:52 +08:00
|
|
|
}
|
|
|
|
|
2011-04-15 21:46:02 +08:00
|
|
|
static void ndisc_send_unsol_na(struct net_device *dev)
|
|
|
|
{
|
|
|
|
struct inet6_dev *idev;
|
|
|
|
struct inet6_ifaddr *ifa;
|
|
|
|
|
|
|
|
idev = in6_dev_get(dev);
|
|
|
|
if (!idev)
|
|
|
|
return;
|
|
|
|
|
|
|
|
read_lock_bh(&idev->lock);
|
|
|
|
list_for_each_entry(ifa, &idev->addr_list, if_list) {
|
2012-11-12 15:50:17 +08:00
|
|
|
ndisc_send_na(dev, NULL, &in6addr_linklocal_allnodes, &ifa->addr,
|
2011-04-15 21:46:02 +08:00
|
|
|
/*router=*/ !!idev->cnf.forwarding,
|
|
|
|
/*solicited=*/ false, /*override=*/ true,
|
|
|
|
/*inc_opt=*/ true);
|
|
|
|
}
|
|
|
|
read_unlock_bh(&idev->lock);
|
|
|
|
|
|
|
|
in6_dev_put(idev);
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,
|
[IPV6]: Make address arguments const.
- net/ipv6/addrconf.c:
ipv6_get_ifaddr(), ipv6_dev_get_saddr()
- net/ipv6/mcast.c:
ipv6_sock_mc_join(), ipv6_sock_mc_drop(),
inet6_mc_check(),
ipv6_dev_mc_inc(), __ipv6_dev_mc_dec(), ipv6_dev_mc_dec(),
ipv6_chk_mcast_addr()
- net/ipv6/route.c:
rt6_lookup(), icmp6_dst_alloc()
- net/ipv6/ip6_output.c:
ip6_nd_hdr()
- net/ipv6/ndisc.c:
ndisc_send_ns(), ndisc_send_rs(), ndisc_send_redirect(),
ndisc_get_neigh(), __ndisc_send()
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
2008-04-10 14:42:10 +08:00
|
|
|
const struct in6_addr *solicit,
|
|
|
|
const struct in6_addr *daddr, const struct in6_addr *saddr)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2013-01-21 14:49:13 +08:00
|
|
|
struct sk_buff *skb;
|
2005-04-17 06:20:36 +08:00
|
|
|
struct in6_addr addr_buf;
|
2013-01-21 14:49:17 +08:00
|
|
|
int inc_opt = dev->addr_len;
|
|
|
|
int optlen = 0;
|
|
|
|
struct nd_msg *msg;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (saddr == NULL) {
|
2007-04-26 08:08:10 +08:00
|
|
|
if (ipv6_get_lladdr(dev, &addr_buf,
|
|
|
|
(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)))
|
2005-04-17 06:20:36 +08:00
|
|
|
return;
|
|
|
|
saddr = &addr_buf;
|
|
|
|
}
|
|
|
|
|
2013-01-21 14:49:17 +08:00
|
|
|
if (ipv6_addr_any(saddr))
|
2013-07-13 16:26:51 +08:00
|
|
|
inc_opt = false;
|
2013-01-21 14:49:17 +08:00
|
|
|
if (inc_opt)
|
|
|
|
optlen += ndisc_opt_addr_space(dev);
|
|
|
|
|
|
|
|
skb = ndisc_alloc_skb(dev, sizeof(*msg) + optlen);
|
2013-01-21 14:49:13 +08:00
|
|
|
if (!skb)
|
|
|
|
return;
|
|
|
|
|
2013-01-21 14:49:17 +08:00
|
|
|
msg = (struct nd_msg *)skb_put(skb, sizeof(*msg));
|
|
|
|
*msg = (struct nd_msg) {
|
|
|
|
.icmph = {
|
|
|
|
.icmp6_type = NDISC_NEIGHBOUR_SOLICITATION,
|
|
|
|
},
|
|
|
|
.target = *solicit,
|
|
|
|
};
|
|
|
|
|
|
|
|
if (inc_opt)
|
|
|
|
ndisc_fill_addr_option(skb, ND_OPT_SOURCE_LL_ADDR,
|
|
|
|
dev->dev_addr);
|
|
|
|
|
2013-01-21 14:49:13 +08:00
|
|
|
ndisc_send_skb(skb, daddr, saddr);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
[IPV6]: Make address arguments const.
- net/ipv6/addrconf.c:
ipv6_get_ifaddr(), ipv6_dev_get_saddr()
- net/ipv6/mcast.c:
ipv6_sock_mc_join(), ipv6_sock_mc_drop(),
inet6_mc_check(),
ipv6_dev_mc_inc(), __ipv6_dev_mc_dec(), ipv6_dev_mc_dec(),
ipv6_chk_mcast_addr()
- net/ipv6/route.c:
rt6_lookup(), icmp6_dst_alloc()
- net/ipv6/ip6_output.c:
ip6_nd_hdr()
- net/ipv6/ndisc.c:
ndisc_send_ns(), ndisc_send_rs(), ndisc_send_redirect(),
ndisc_get_neigh(), __ndisc_send()
Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
2008-04-10 14:42:10 +08:00
|
|
|
void ndisc_send_rs(struct net_device *dev, const struct in6_addr *saddr,
|
|
|
|
const struct in6_addr *daddr)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2013-01-21 14:49:13 +08:00
|
|
|
struct sk_buff *skb;
|
2013-01-21 14:49:17 +08:00
|
|
|
struct rs_msg *msg;
|
2007-04-26 08:08:10 +08:00
|
|
|
int send_sllao = dev->addr_len;
|
2013-01-21 14:49:17 +08:00
|
|
|
int optlen = 0;
|
2007-04-26 08:08:10 +08:00
|
|
|
|
|
|
|
#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
|
|
|
|
/*
|
|
|
|
* According to section 2.2 of RFC 4429, we must not
|
|
|
|
* send router solicitations with a sllao from
|
|
|
|
* optimistic addresses, but we may send the solicitation
|
|
|
|
* if we don't include the sllao. So here we check
|
|
|
|
* if our address is optimistic, and if so, we
|
2007-12-21 06:01:35 +08:00
|
|
|
* suppress the inclusion of the sllao.
|
2007-04-26 08:08:10 +08:00
|
|
|
*/
|
|
|
|
if (send_sllao) {
|
2008-03-25 20:47:49 +08:00
|
|
|
struct inet6_ifaddr *ifp = ipv6_get_ifaddr(dev_net(dev), saddr,
|
2008-01-11 14:44:09 +08:00
|
|
|
dev, 1);
|
2007-04-26 08:08:10 +08:00
|
|
|
if (ifp) {
|
|
|
|
if (ifp->flags & IFA_F_OPTIMISTIC) {
|
2007-02-28 22:13:20 +08:00
|
|
|
send_sllao = 0;
|
2007-04-26 08:08:10 +08:00
|
|
|
}
|
2007-02-28 22:13:20 +08:00
|
|
|
in6_ifa_put(ifp);
|
2007-04-26 08:08:10 +08:00
|
|
|
} else {
|
|
|
|
send_sllao = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
2013-01-21 14:49:17 +08:00
|
|
|
if (send_sllao)
|
|
|
|
optlen += ndisc_opt_addr_space(dev);
|
|
|
|
|
|
|
|
skb = ndisc_alloc_skb(dev, sizeof(*msg) + optlen);
|
2013-01-21 14:49:13 +08:00
|
|
|
if (!skb)
|
|
|
|
return;
|
|
|
|
|
2013-01-21 14:49:17 +08:00
|
|
|
msg = (struct rs_msg *)skb_put(skb, sizeof(*msg));
|
|
|
|
*msg = (struct rs_msg) {
|
|
|
|
.icmph = {
|
|
|
|
.icmp6_type = NDISC_ROUTER_SOLICITATION,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
if (send_sllao)
|
|
|
|
ndisc_fill_addr_option(skb, ND_OPT_SOURCE_LL_ADDR,
|
|
|
|
dev->dev_addr);
|
|
|
|
|
2013-01-21 14:49:13 +08:00
|
|
|
ndisc_send_skb(skb, daddr, saddr);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2007-02-09 22:24:49 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
static void ndisc_error_report(struct neighbour *neigh, struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* "The sender MUST return an ICMP
|
|
|
|
* destination unreachable"
|
|
|
|
*/
|
|
|
|
dst_link_failure(skb);
|
|
|
|
kfree_skb(skb);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Called with locked neigh: either read or both */
|
|
|
|
|
|
|
|
static void ndisc_solicit(struct neighbour *neigh, struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
struct in6_addr *saddr = NULL;
|
|
|
|
struct in6_addr mcaddr;
|
|
|
|
struct net_device *dev = neigh->dev;
|
|
|
|
struct in6_addr *target = (struct in6_addr *)&neigh->primary_key;
|
|
|
|
int probes = atomic_read(&neigh->probes);
|
|
|
|
|
2008-03-25 20:47:49 +08:00
|
|
|
if (skb && ipv6_chk_addr(dev_net(dev), &ipv6_hdr(skb)->saddr, dev, 1))
|
2007-04-26 08:54:47 +08:00
|
|
|
saddr = &ipv6_hdr(skb)->saddr;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2013-12-08 02:26:53 +08:00
|
|
|
if ((probes -= NEIGH_VAR(neigh->parms, UCAST_PROBES)) < 0) {
|
2005-04-17 06:20:36 +08:00
|
|
|
if (!(neigh->nud_state & NUD_VALID)) {
|
2012-05-17 03:28:38 +08:00
|
|
|
ND_PRINTK(1, dbg,
|
|
|
|
"%s: trying to ucast probe in NUD_INVALID: %pI6\n",
|
|
|
|
__func__, target);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
ndisc_send_ns(dev, neigh, target, target, saddr);
|
2013-12-08 02:26:53 +08:00
|
|
|
} else if ((probes -= NEIGH_VAR(neigh->parms, APP_PROBES)) < 0) {
|
2005-04-17 06:20:36 +08:00
|
|
|
neigh_app_ns(neigh);
|
|
|
|
} else {
|
|
|
|
addrconf_addr_solict_mult(target, &mcaddr);
|
|
|
|
ndisc_send_ns(dev, NULL, target, &mcaddr, saddr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-03-28 12:37:58 +08:00
|
|
|
static int pndisc_is_router(const void *pkey,
|
|
|
|
struct net_device *dev)
|
2008-03-25 05:48:59 +08:00
|
|
|
{
|
|
|
|
struct pneigh_entry *n;
|
2008-03-28 12:37:58 +08:00
|
|
|
int ret = -1;
|
2008-03-25 05:48:59 +08:00
|
|
|
|
|
|
|
read_lock_bh(&nd_tbl.lock);
|
2008-03-28 12:37:58 +08:00
|
|
|
n = __pneigh_lookup(&nd_tbl, dev_net(dev), pkey, dev);
|
|
|
|
if (n)
|
|
|
|
ret = !!(n->flags & NTF_ROUTER);
|
2008-03-25 05:48:59 +08:00
|
|
|
read_unlock_bh(&nd_tbl.lock);
|
|
|
|
|
2008-03-28 12:37:58 +08:00
|
|
|
return ret;
|
2008-03-25 05:48:59 +08:00
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
static void ndisc_recv_ns(struct sk_buff *skb)
|
|
|
|
{
|
2007-04-26 09:04:18 +08:00
|
|
|
struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb);
|
2011-04-22 12:53:02 +08:00
|
|
|
const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
|
|
|
|
const struct in6_addr *daddr = &ipv6_hdr(skb)->daddr;
|
2005-04-17 06:20:36 +08:00
|
|
|
u8 *lladdr = NULL;
|
2013-05-29 04:34:26 +08:00
|
|
|
u32 ndoptlen = skb_tail_pointer(skb) - (skb_transport_header(skb) +
|
2007-04-20 11:29:13 +08:00
|
|
|
offsetof(struct nd_msg, opt));
|
2005-04-17 06:20:36 +08:00
|
|
|
struct ndisc_options ndopts;
|
|
|
|
struct net_device *dev = skb->dev;
|
|
|
|
struct inet6_ifaddr *ifp;
|
|
|
|
struct inet6_dev *idev = NULL;
|
|
|
|
struct neighbour *neigh;
|
|
|
|
int dad = ipv6_addr_any(saddr);
|
2012-05-19 02:57:34 +08:00
|
|
|
bool inc;
|
2008-03-28 12:37:58 +08:00
|
|
|
int is_router = -1;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2013-01-18 10:05:03 +08:00
|
|
|
if (skb->len < sizeof(struct nd_msg)) {
|
|
|
|
ND_PRINTK(2, warn, "NS: packet too short\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
if (ipv6_addr_is_multicast(&msg->target)) {
|
2012-05-17 03:28:38 +08:00
|
|
|
ND_PRINTK(2, warn, "NS: multicast target address\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* RFC2461 7.1.1:
|
|
|
|
* DAD has to be destined for solicited node multicast address.
|
|
|
|
*/
|
2013-01-20 15:39:00 +08:00
|
|
|
if (dad && !ipv6_addr_is_solict_mult(daddr)) {
|
2012-05-17 03:28:38 +08:00
|
|
|
ND_PRINTK(2, warn, "NS: bad DAD packet (wrong destination)\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts)) {
|
2012-05-17 03:28:38 +08:00
|
|
|
ND_PRINTK(2, warn, "NS: invalid ND options\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ndopts.nd_opts_src_lladdr) {
|
|
|
|
lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr, dev);
|
|
|
|
if (!lladdr) {
|
2012-05-17 03:28:38 +08:00
|
|
|
ND_PRINTK(2, warn,
|
|
|
|
"NS: invalid link-layer address length\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* RFC2461 7.1.1:
|
2007-02-09 22:24:49 +08:00
|
|
|
* If the IP source address is the unspecified address,
|
|
|
|
* there MUST NOT be source link-layer address option
|
2005-04-17 06:20:36 +08:00
|
|
|
* in the message.
|
|
|
|
*/
|
|
|
|
if (dad) {
|
2012-05-17 03:28:38 +08:00
|
|
|
ND_PRINTK(2, warn,
|
|
|
|
"NS: bad DAD packet (link-layer address option)\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
inc = ipv6_addr_is_multicast(daddr);
|
|
|
|
|
2008-03-25 20:47:49 +08:00
|
|
|
ifp = ipv6_get_ifaddr(dev_net(dev), &msg->target, dev, 1);
|
2008-03-08 03:14:49 +08:00
|
|
|
if (ifp) {
|
2007-04-26 08:08:10 +08:00
|
|
|
|
|
|
|
if (ifp->flags & (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)) {
|
|
|
|
if (dad) {
|
|
|
|
/*
|
|
|
|
* We are colliding with another node
|
|
|
|
* who is doing DAD
|
|
|
|
* so fail our DAD process
|
|
|
|
*/
|
|
|
|
addrconf_dad_failure(ifp);
|
2007-09-11 17:04:49 +08:00
|
|
|
return;
|
2007-04-26 08:08:10 +08:00
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* This is not a dad solicitation.
|
|
|
|
* If we are an optimistic node,
|
|
|
|
* we should respond.
|
|
|
|
* Otherwise, we should ignore it.
|
|
|
|
*/
|
|
|
|
if (!(ifp->flags & IFA_F_OPTIMISTIC))
|
2005-04-17 06:20:36 +08:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
idev = ifp->idev;
|
|
|
|
} else {
|
2008-07-20 13:35:03 +08:00
|
|
|
struct net *net = dev_net(dev);
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
idev = in6_dev_get(dev);
|
|
|
|
if (!idev) {
|
|
|
|
/* XXX: count this drop? */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2008-07-20 13:35:03 +08:00
|
|
|
if (ipv6_chk_acast_addr(net, dev, &msg->target) ||
|
2007-02-09 22:24:49 +08:00
|
|
|
(idev->cnf.forwarding &&
|
2008-07-20 13:35:03 +08:00
|
|
|
(net->ipv6.devconf_all->proxy_ndp || idev->cnf.proxy_ndp) &&
|
2008-03-28 12:37:58 +08:00
|
|
|
(is_router = pndisc_is_router(&msg->target, dev)) >= 0)) {
|
2005-08-15 08:24:31 +08:00
|
|
|
if (!(NEIGH_CB(skb)->flags & LOCALLY_ENQUEUED) &&
|
2005-04-17 06:20:36 +08:00
|
|
|
skb->pkt_type != PACKET_HOST &&
|
2013-07-13 16:26:51 +08:00
|
|
|
inc &&
|
2013-12-08 02:26:53 +08:00
|
|
|
NEIGH_VAR(idev->nd_parms, PROXY_DELAY) != 0) {
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* for anycast or proxy,
|
2007-02-09 22:24:49 +08:00
|
|
|
* sender should delay its response
|
|
|
|
* by a random time between 0 and
|
2005-04-17 06:20:36 +08:00
|
|
|
* MAX_ANYCAST_DELAY_TIME seconds.
|
|
|
|
* (RFC2461) -- yoshfuji
|
|
|
|
*/
|
|
|
|
struct sk_buff *n = skb_clone(skb, GFP_ATOMIC);
|
|
|
|
if (n)
|
|
|
|
pneigh_enqueue(&nd_tbl, idev->nd_parms, n);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2008-03-28 12:37:58 +08:00
|
|
|
if (is_router < 0)
|
2013-01-20 15:39:18 +08:00
|
|
|
is_router = idev->cnf.forwarding;
|
2006-09-23 05:43:19 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
if (dad) {
|
2008-04-10 14:42:11 +08:00
|
|
|
ndisc_send_na(dev, NULL, &in6addr_linklocal_allnodes, &msg->target,
|
2013-01-20 15:39:18 +08:00
|
|
|
!!is_router, false, (ifp != NULL), true);
|
2005-04-17 06:20:36 +08:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (inc)
|
|
|
|
NEIGH_CACHE_STAT_INC(&nd_tbl, rcv_probes_mcast);
|
|
|
|
else
|
|
|
|
NEIGH_CACHE_STAT_INC(&nd_tbl, rcv_probes_ucast);
|
|
|
|
|
2007-02-09 22:24:49 +08:00
|
|
|
/*
|
2005-04-17 06:20:36 +08:00
|
|
|
* update / create cache entry
|
|
|
|
* for the source address
|
|
|
|
*/
|
|
|
|
neigh = __neigh_lookup(&nd_tbl, saddr, dev,
|
|
|
|
!inc || lladdr || !dev->addr_len);
|
|
|
|
if (neigh)
|
2007-02-09 22:24:49 +08:00
|
|
|
neigh_update(neigh, lladdr, NUD_STALE,
|
2005-04-17 06:20:36 +08:00
|
|
|
NEIGH_UPDATE_F_WEAK_OVERRIDE|
|
|
|
|
NEIGH_UPDATE_F_OVERRIDE);
|
2007-10-09 16:40:57 +08:00
|
|
|
if (neigh || !dev->header_ops) {
|
2005-04-17 06:20:36 +08:00
|
|
|
ndisc_send_na(dev, neigh, saddr, &msg->target,
|
2013-01-20 15:39:18 +08:00
|
|
|
!!is_router,
|
|
|
|
true, (ifp != NULL && inc), inc);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (neigh)
|
|
|
|
neigh_release(neigh);
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
if (ifp)
|
|
|
|
in6_ifa_put(ifp);
|
|
|
|
else
|
|
|
|
in6_dev_put(idev);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ndisc_recv_na(struct sk_buff *skb)
|
|
|
|
{
|
2007-04-26 09:04:18 +08:00
|
|
|
struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb);
|
2014-05-15 15:56:14 +08:00
|
|
|
struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
|
2011-04-22 12:53:02 +08:00
|
|
|
const struct in6_addr *daddr = &ipv6_hdr(skb)->daddr;
|
2005-04-17 06:20:36 +08:00
|
|
|
u8 *lladdr = NULL;
|
2013-05-29 04:34:26 +08:00
|
|
|
u32 ndoptlen = skb_tail_pointer(skb) - (skb_transport_header(skb) +
|
2007-04-20 11:29:13 +08:00
|
|
|
offsetof(struct nd_msg, opt));
|
2005-04-17 06:20:36 +08:00
|
|
|
struct ndisc_options ndopts;
|
|
|
|
struct net_device *dev = skb->dev;
|
|
|
|
struct inet6_ifaddr *ifp;
|
|
|
|
struct neighbour *neigh;
|
|
|
|
|
|
|
|
if (skb->len < sizeof(struct nd_msg)) {
|
2012-05-17 03:28:38 +08:00
|
|
|
ND_PRINTK(2, warn, "NA: packet too short\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ipv6_addr_is_multicast(&msg->target)) {
|
2012-05-17 03:28:38 +08:00
|
|
|
ND_PRINTK(2, warn, "NA: target address is multicast\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ipv6_addr_is_multicast(daddr) &&
|
|
|
|
msg->icmph.icmp6_solicited) {
|
2012-05-17 03:28:38 +08:00
|
|
|
ND_PRINTK(2, warn, "NA: solicited NA is multicasted\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
return;
|
|
|
|
}
|
2007-02-09 22:24:49 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts)) {
|
2012-05-17 03:28:38 +08:00
|
|
|
ND_PRINTK(2, warn, "NS: invalid ND option\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (ndopts.nd_opts_tgt_lladdr) {
|
|
|
|
lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr, dev);
|
|
|
|
if (!lladdr) {
|
2012-05-17 03:28:38 +08:00
|
|
|
ND_PRINTK(2, warn,
|
|
|
|
"NA: invalid link-layer address length\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2008-03-25 20:47:49 +08:00
|
|
|
ifp = ipv6_get_ifaddr(dev_net(dev), &msg->target, dev, 1);
|
2008-03-08 03:14:49 +08:00
|
|
|
if (ifp) {
|
2011-04-14 05:09:25 +08:00
|
|
|
if (skb->pkt_type != PACKET_LOOPBACK
|
|
|
|
&& (ifp->flags & IFA_F_TENTATIVE)) {
|
|
|
|
addrconf_dad_failure(ifp);
|
|
|
|
return;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
/* What should we make now? The advertisement
|
|
|
|
is invalid, but ndisc specs say nothing
|
|
|
|
about it. It could be misconfiguration, or
|
|
|
|
an smart proxy agent tries to help us :-)
|
2008-12-10 07:48:32 +08:00
|
|
|
|
|
|
|
We should not print the error if NA has been
|
|
|
|
received from loopback - it is just our own
|
|
|
|
unsolicited advertisement.
|
2005-04-17 06:20:36 +08:00
|
|
|
*/
|
2008-12-10 07:48:32 +08:00
|
|
|
if (skb->pkt_type != PACKET_LOOPBACK)
|
2012-05-17 03:28:38 +08:00
|
|
|
ND_PRINTK(1, warn,
|
|
|
|
"NA: someone advertises our address %pI6 on %s!\n",
|
|
|
|
&ifp->addr, ifp->idev->dev->name);
|
2005-04-17 06:20:36 +08:00
|
|
|
in6_ifa_put(ifp);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
neigh = neigh_lookup(&nd_tbl, &msg->target, dev);
|
|
|
|
|
|
|
|
if (neigh) {
|
|
|
|
u8 old_flags = neigh->flags;
|
2008-07-20 13:35:03 +08:00
|
|
|
struct net *net = dev_net(dev);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (neigh->nud_state & NUD_FAILED)
|
|
|
|
goto out;
|
|
|
|
|
2006-09-23 05:42:46 +08:00
|
|
|
/*
|
|
|
|
* Don't update the neighbor cache entry on a proxy NA from
|
|
|
|
* ourselves because either the proxied node is off link or it
|
|
|
|
* has already sent a NA to us.
|
|
|
|
*/
|
|
|
|
if (lladdr && !memcmp(lladdr, dev->dev_addr, dev->addr_len) &&
|
2008-07-20 13:35:03 +08:00
|
|
|
net->ipv6.devconf_all->forwarding && net->ipv6.devconf_all->proxy_ndp &&
|
|
|
|
pneigh_lookup(&nd_tbl, net, &msg->target, dev, 0)) {
|
2012-11-07 13:05:38 +08:00
|
|
|
/* XXX: idev->cnf.proxy_ndp */
|
2006-09-23 05:42:46 +08:00
|
|
|
goto out;
|
2006-09-23 05:43:49 +08:00
|
|
|
}
|
2006-09-23 05:42:46 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
neigh_update(neigh, lladdr,
|
|
|
|
msg->icmph.icmp6_solicited ? NUD_REACHABLE : NUD_STALE,
|
|
|
|
NEIGH_UPDATE_F_WEAK_OVERRIDE|
|
|
|
|
(msg->icmph.icmp6_override ? NEIGH_UPDATE_F_OVERRIDE : 0)|
|
|
|
|
NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
|
|
|
|
(msg->icmph.icmp6_router ? NEIGH_UPDATE_F_ISROUTER : 0));
|
|
|
|
|
|
|
|
if ((old_flags & ~neigh->flags) & NTF_ROUTER) {
|
|
|
|
/*
|
|
|
|
* Change: router to host
|
|
|
|
*/
|
2014-05-15 15:56:14 +08:00
|
|
|
rt6_clean_tohost(dev_net(dev), saddr);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
neigh_release(neigh);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ndisc_recv_rs(struct sk_buff *skb)
|
|
|
|
{
|
2007-04-26 09:04:18 +08:00
|
|
|
struct rs_msg *rs_msg = (struct rs_msg *)skb_transport_header(skb);
|
2005-04-17 06:20:36 +08:00
|
|
|
unsigned long ndoptlen = skb->len - sizeof(*rs_msg);
|
|
|
|
struct neighbour *neigh;
|
|
|
|
struct inet6_dev *idev;
|
2011-04-22 12:53:02 +08:00
|
|
|
const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
|
2005-04-17 06:20:36 +08:00
|
|
|
struct ndisc_options ndopts;
|
|
|
|
u8 *lladdr = NULL;
|
|
|
|
|
|
|
|
if (skb->len < sizeof(*rs_msg))
|
|
|
|
return;
|
|
|
|
|
2011-07-28 05:13:03 +08:00
|
|
|
idev = __in6_dev_get(skb->dev);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (!idev) {
|
2012-05-17 03:28:38 +08:00
|
|
|
ND_PRINTK(1, err, "RS: can't find in6 device\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Don't accept RS if we're not in router mode */
|
|
|
|
if (!idev->cnf.forwarding)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Don't update NCE if src = ::;
|
|
|
|
* this implies that the source node has no ip address assigned yet.
|
|
|
|
*/
|
|
|
|
if (ipv6_addr_any(saddr))
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
/* Parse ND options */
|
|
|
|
if (!ndisc_parse_options(rs_msg->opt, ndoptlen, &ndopts)) {
|
2012-05-17 03:28:38 +08:00
|
|
|
ND_PRINTK(2, notice, "NS: invalid ND option, ignored\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ndopts.nd_opts_src_lladdr) {
|
|
|
|
lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr,
|
|
|
|
skb->dev);
|
|
|
|
if (!lladdr)
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
neigh = __neigh_lookup(&nd_tbl, saddr, skb->dev, 1);
|
|
|
|
if (neigh) {
|
|
|
|
neigh_update(neigh, lladdr, NUD_STALE,
|
|
|
|
NEIGH_UPDATE_F_WEAK_OVERRIDE|
|
|
|
|
NEIGH_UPDATE_F_OVERRIDE|
|
|
|
|
NEIGH_UPDATE_F_OVERRIDE_ISROUTER);
|
|
|
|
neigh_release(neigh);
|
|
|
|
}
|
|
|
|
out:
|
2011-07-28 05:13:03 +08:00
|
|
|
return;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2007-10-11 12:22:05 +08:00
|
|
|
static void ndisc_ra_useropt(struct sk_buff *ra, struct nd_opt_hdr *opt)
|
|
|
|
{
|
|
|
|
struct icmp6hdr *icmp6h = (struct icmp6hdr *)skb_transport_header(ra);
|
|
|
|
struct sk_buff *skb;
|
|
|
|
struct nlmsghdr *nlh;
|
|
|
|
struct nduseroptmsg *ndmsg;
|
2008-03-25 20:47:49 +08:00
|
|
|
struct net *net = dev_net(ra->dev);
|
2007-10-11 12:22:05 +08:00
|
|
|
int err;
|
|
|
|
int base_size = NLMSG_ALIGN(sizeof(struct nduseroptmsg)
|
|
|
|
+ (opt->nd_opt_len << 3));
|
|
|
|
size_t msg_size = base_size + nla_total_size(sizeof(struct in6_addr));
|
|
|
|
|
|
|
|
skb = nlmsg_new(msg_size, GFP_ATOMIC);
|
|
|
|
if (skb == NULL) {
|
|
|
|
err = -ENOBUFS;
|
|
|
|
goto errout;
|
|
|
|
}
|
|
|
|
|
|
|
|
nlh = nlmsg_put(skb, 0, 0, RTM_NEWNDUSEROPT, base_size, 0);
|
|
|
|
if (nlh == NULL) {
|
|
|
|
goto nla_put_failure;
|
|
|
|
}
|
|
|
|
|
|
|
|
ndmsg = nlmsg_data(nlh);
|
|
|
|
ndmsg->nduseropt_family = AF_INET6;
|
2007-11-13 09:58:35 +08:00
|
|
|
ndmsg->nduseropt_ifindex = ra->dev->ifindex;
|
2007-10-11 12:22:05 +08:00
|
|
|
ndmsg->nduseropt_icmp_type = icmp6h->icmp6_type;
|
|
|
|
ndmsg->nduseropt_icmp_code = icmp6h->icmp6_code;
|
|
|
|
ndmsg->nduseropt_opts_len = opt->nd_opt_len << 3;
|
|
|
|
|
|
|
|
memcpy(ndmsg + 1, opt, opt->nd_opt_len << 3);
|
|
|
|
|
2012-04-02 08:27:33 +08:00
|
|
|
if (nla_put(skb, NDUSEROPT_SRCADDR, sizeof(struct in6_addr),
|
|
|
|
&ipv6_hdr(ra)->saddr))
|
|
|
|
goto nla_put_failure;
|
2007-10-11 12:22:05 +08:00
|
|
|
nlmsg_end(skb, nlh);
|
|
|
|
|
2009-02-25 15:18:28 +08:00
|
|
|
rtnl_notify(skb, net, 0, RTNLGRP_ND_USEROPT, NULL, GFP_ATOMIC);
|
2007-10-11 12:22:05 +08:00
|
|
|
return;
|
|
|
|
|
|
|
|
nla_put_failure:
|
|
|
|
nlmsg_free(skb);
|
|
|
|
err = -EMSGSIZE;
|
|
|
|
errout:
|
2008-03-08 03:14:49 +08:00
|
|
|
rtnl_set_sk_err(net, RTNLGRP_ND_USEROPT, err);
|
2007-10-11 12:22:05 +08:00
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
static void ndisc_router_discovery(struct sk_buff *skb)
|
|
|
|
{
|
2007-04-26 09:04:18 +08:00
|
|
|
struct ra_msg *ra_msg = (struct ra_msg *)skb_transport_header(skb);
|
2005-04-17 06:20:36 +08:00
|
|
|
struct neighbour *neigh = NULL;
|
|
|
|
struct inet6_dev *in6_dev;
|
2006-03-21 08:55:08 +08:00
|
|
|
struct rt6_info *rt = NULL;
|
2005-04-17 06:20:36 +08:00
|
|
|
int lifetime;
|
|
|
|
struct ndisc_options ndopts;
|
|
|
|
int optlen;
|
2006-03-21 09:04:53 +08:00
|
|
|
unsigned int pref = 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
__u8 * opt = (__u8 *)(ra_msg + 1);
|
|
|
|
|
2013-05-29 04:34:26 +08:00
|
|
|
optlen = (skb_tail_pointer(skb) - skb_transport_header(skb)) -
|
|
|
|
sizeof(struct ra_msg);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2014-06-26 05:44:52 +08:00
|
|
|
ND_PRINTK(2, info,
|
|
|
|
"RA: %s, dev: %s\n",
|
|
|
|
__func__, skb->dev->name);
|
2007-04-26 08:54:47 +08:00
|
|
|
if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) {
|
2012-05-17 03:28:38 +08:00
|
|
|
ND_PRINTK(2, warn, "RA: source address is not link-local\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (optlen < 0) {
|
2012-05-17 03:28:38 +08:00
|
|
|
ND_PRINTK(2, warn, "RA: packet too short\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2008-03-16 11:59:18 +08:00
|
|
|
#ifdef CONFIG_IPV6_NDISC_NODETYPE
|
2008-03-12 06:35:59 +08:00
|
|
|
if (skb->ndisc_nodetype == NDISC_NODETYPE_HOST) {
|
2012-05-17 03:28:38 +08:00
|
|
|
ND_PRINTK(2, warn, "RA: from host or unauthorized router\n");
|
2008-03-12 06:35:59 +08:00
|
|
|
return;
|
|
|
|
}
|
2008-03-16 11:59:18 +08:00
|
|
|
#endif
|
2008-03-12 06:35:59 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* set the RA_RECV flag in the interface
|
|
|
|
*/
|
|
|
|
|
2011-07-28 05:13:03 +08:00
|
|
|
in6_dev = __in6_dev_get(skb->dev);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (in6_dev == NULL) {
|
2012-05-17 03:28:38 +08:00
|
|
|
ND_PRINTK(0, err, "RA: can't find inet6 device for %s\n",
|
|
|
|
skb->dev->name);
|
2005-04-17 06:20:36 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ndisc_parse_options(opt, optlen, &ndopts)) {
|
2012-05-17 03:28:38 +08:00
|
|
|
ND_PRINTK(2, warn, "RA: invalid ND options\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-06-26 05:44:52 +08:00
|
|
|
if (!ipv6_accept_ra(in6_dev)) {
|
|
|
|
ND_PRINTK(2, info,
|
|
|
|
"RA: %s, did not accept ra for dev: %s\n",
|
|
|
|
__func__, skb->dev->name);
|
2009-08-29 15:04:09 +08:00
|
|
|
goto skip_linkparms;
|
2014-06-26 05:44:52 +08:00
|
|
|
}
|
2009-08-29 15:04:09 +08:00
|
|
|
|
2008-03-16 11:59:18 +08:00
|
|
|
#ifdef CONFIG_IPV6_NDISC_NODETYPE
|
2008-03-12 06:35:59 +08:00
|
|
|
/* skip link-specific parameters from interior routers */
|
2014-06-26 05:44:52 +08:00
|
|
|
if (skb->ndisc_nodetype == NDISC_NODETYPE_NODEFAULT) {
|
|
|
|
ND_PRINTK(2, info,
|
|
|
|
"RA: %s, nodetype is NODEFAULT, dev: %s\n",
|
|
|
|
__func__, skb->dev->name);
|
2008-03-12 06:35:59 +08:00
|
|
|
goto skip_linkparms;
|
2014-06-26 05:44:52 +08:00
|
|
|
}
|
2008-03-16 11:59:18 +08:00
|
|
|
#endif
|
2008-03-12 06:35:59 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
if (in6_dev->if_flags & IF_RS_SENT) {
|
|
|
|
/*
|
|
|
|
* flag that an RA was received after an RS was sent
|
|
|
|
* out on this interface.
|
|
|
|
*/
|
|
|
|
in6_dev->if_flags |= IF_RA_RCVD;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Remember the managed/otherconf flags from most recently
|
|
|
|
* received RA message (RFC 2462) -- yoshfuji
|
|
|
|
*/
|
|
|
|
in6_dev->if_flags = (in6_dev->if_flags & ~(IF_RA_MANAGED |
|
|
|
|
IF_RA_OTHERCONF)) |
|
|
|
|
(ra_msg->icmph.icmp6_addrconf_managed ?
|
|
|
|
IF_RA_MANAGED : 0) |
|
|
|
|
(ra_msg->icmph.icmp6_addrconf_other ?
|
|
|
|
IF_RA_OTHERCONF : 0);
|
|
|
|
|
2014-06-26 05:44:52 +08:00
|
|
|
if (!in6_dev->cnf.accept_ra_defrtr) {
|
|
|
|
ND_PRINTK(2, info,
|
|
|
|
"RA: %s, defrtr is false for dev: %s\n",
|
|
|
|
__func__, skb->dev->name);
|
2006-03-21 08:55:08 +08:00
|
|
|
goto skip_defrtr;
|
2014-06-26 05:44:52 +08:00
|
|
|
}
|
2006-03-21 08:55:08 +08:00
|
|
|
|
2014-06-26 05:44:53 +08:00
|
|
|
/* Do not accept RA with source-addr found on local machine unless
|
|
|
|
* accept_ra_from_local is set to true.
|
|
|
|
*/
|
2014-07-10 18:02:46 +08:00
|
|
|
if (!in6_dev->cnf.accept_ra_from_local &&
|
|
|
|
ipv6_chk_addr(dev_net(in6_dev->dev), &ipv6_hdr(skb)->saddr,
|
|
|
|
NULL, 0)) {
|
2014-06-26 05:44:52 +08:00
|
|
|
ND_PRINTK(2, info,
|
2014-06-26 05:44:53 +08:00
|
|
|
"RA from local address detected on dev: %s: default router ignored\n",
|
|
|
|
skb->dev->name);
|
2011-10-25 07:13:15 +08:00
|
|
|
goto skip_defrtr;
|
2014-06-26 05:44:52 +08:00
|
|
|
}
|
2011-10-25 07:13:15 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
lifetime = ntohs(ra_msg->icmph.icmp6_rt_lifetime);
|
|
|
|
|
2006-03-21 09:04:53 +08:00
|
|
|
#ifdef CONFIG_IPV6_ROUTER_PREF
|
|
|
|
pref = ra_msg->icmph.icmp6_router_pref;
|
|
|
|
/* 10b is handled as if it were 00b (medium) */
|
2006-03-21 09:05:30 +08:00
|
|
|
if (pref == ICMPV6_ROUTER_PREF_INVALID ||
|
2007-06-23 07:07:04 +08:00
|
|
|
!in6_dev->cnf.accept_ra_rtr_pref)
|
2006-03-21 09:04:53 +08:00
|
|
|
pref = ICMPV6_ROUTER_PREF_MEDIUM;
|
|
|
|
#endif
|
|
|
|
|
2007-04-26 08:54:47 +08:00
|
|
|
rt = rt6_get_dflt_router(&ipv6_hdr(skb)->saddr, skb->dev);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2012-01-28 07:07:56 +08:00
|
|
|
if (rt) {
|
|
|
|
neigh = dst_neigh_lookup(&rt->dst, &ipv6_hdr(skb)->saddr);
|
|
|
|
if (!neigh) {
|
2012-05-17 03:28:38 +08:00
|
|
|
ND_PRINTK(0, err,
|
|
|
|
"RA: %s got default router without neighbour\n",
|
|
|
|
__func__);
|
2012-10-29 08:13:19 +08:00
|
|
|
ip6_rt_put(rt);
|
2012-01-28 07:07:56 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
if (rt && lifetime == 0) {
|
2006-08-22 15:00:21 +08:00
|
|
|
ip6_del_rt(rt);
|
2005-04-17 06:20:36 +08:00
|
|
|
rt = NULL;
|
|
|
|
}
|
|
|
|
|
2014-06-26 05:44:52 +08:00
|
|
|
ND_PRINTK(3, info, "RA: rt: %p lifetime: %d, for dev: %s\n",
|
|
|
|
rt, lifetime, skb->dev->name);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (rt == NULL && lifetime) {
|
2014-06-26 05:44:52 +08:00
|
|
|
ND_PRINTK(3, info, "RA: adding default router\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-04-26 08:54:47 +08:00
|
|
|
rt = rt6_add_dflt_router(&ipv6_hdr(skb)->saddr, skb->dev, pref);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (rt == NULL) {
|
2012-05-17 03:28:38 +08:00
|
|
|
ND_PRINTK(0, err,
|
|
|
|
"RA: %s failed to add default route\n",
|
|
|
|
__func__);
|
2005-04-17 06:20:36 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-01-28 07:07:56 +08:00
|
|
|
neigh = dst_neigh_lookup(&rt->dst, &ipv6_hdr(skb)->saddr);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (neigh == NULL) {
|
2012-05-17 03:28:38 +08:00
|
|
|
ND_PRINTK(0, err,
|
|
|
|
"RA: %s got default router without neighbour\n",
|
|
|
|
__func__);
|
2012-10-29 08:13:19 +08:00
|
|
|
ip6_rt_put(rt);
|
2005-04-17 06:20:36 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
neigh->flags |= NTF_ROUTER;
|
2006-03-21 09:04:53 +08:00
|
|
|
} else if (rt) {
|
2008-10-16 06:47:49 +08:00
|
|
|
rt->rt6i_flags = (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (rt)
|
2012-04-06 08:13:10 +08:00
|
|
|
rt6_set_expires(rt, jiffies + (HZ * lifetime));
|
2005-04-17 06:20:36 +08:00
|
|
|
if (ra_msg->icmph.icmp6_hop_limit) {
|
|
|
|
in6_dev->cnf.hop_limit = ra_msg->icmph.icmp6_hop_limit;
|
|
|
|
if (rt)
|
2010-12-09 13:16:57 +08:00
|
|
|
dst_metric_set(&rt->dst, RTAX_HOPLIMIT,
|
|
|
|
ra_msg->icmph.icmp6_hop_limit);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2006-03-21 08:55:08 +08:00
|
|
|
skip_defrtr:
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* Update Reachable Time and Retrans Timer
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (in6_dev->nd_parms) {
|
|
|
|
unsigned long rtime = ntohl(ra_msg->retrans_timer);
|
|
|
|
|
|
|
|
if (rtime && rtime/1000 < MAX_SCHEDULE_TIMEOUT/HZ) {
|
|
|
|
rtime = (rtime*HZ)/1000;
|
|
|
|
if (rtime < HZ/10)
|
|
|
|
rtime = HZ/10;
|
2013-12-08 02:26:53 +08:00
|
|
|
NEIGH_VAR_SET(in6_dev->nd_parms, RETRANS_TIME, rtime);
|
2005-04-17 06:20:36 +08:00
|
|
|
in6_dev->tstamp = jiffies;
|
|
|
|
inet6_ifinfo_notify(RTM_NEWLINK, in6_dev);
|
|
|
|
}
|
|
|
|
|
|
|
|
rtime = ntohl(ra_msg->reachable_time);
|
|
|
|
if (rtime && rtime/1000 < MAX_SCHEDULE_TIMEOUT/(3*HZ)) {
|
|
|
|
rtime = (rtime*HZ)/1000;
|
|
|
|
|
|
|
|
if (rtime < HZ/10)
|
|
|
|
rtime = HZ/10;
|
|
|
|
|
2013-12-08 02:26:53 +08:00
|
|
|
if (rtime != NEIGH_VAR(in6_dev->nd_parms, BASE_REACHABLE_TIME)) {
|
|
|
|
NEIGH_VAR_SET(in6_dev->nd_parms,
|
|
|
|
BASE_REACHABLE_TIME, rtime);
|
|
|
|
NEIGH_VAR_SET(in6_dev->nd_parms,
|
|
|
|
GC_STALETIME, 3 * rtime);
|
2005-04-17 06:20:36 +08:00
|
|
|
in6_dev->nd_parms->reachable_time = neigh_rand_reach_time(rtime);
|
|
|
|
in6_dev->tstamp = jiffies;
|
|
|
|
inet6_ifinfo_notify(RTM_NEWLINK, in6_dev);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-03-12 06:35:59 +08:00
|
|
|
skip_linkparms:
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* Process options.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (!neigh)
|
2007-04-26 08:54:47 +08:00
|
|
|
neigh = __neigh_lookup(&nd_tbl, &ipv6_hdr(skb)->saddr,
|
2005-04-17 06:20:36 +08:00
|
|
|
skb->dev, 1);
|
|
|
|
if (neigh) {
|
|
|
|
u8 *lladdr = NULL;
|
|
|
|
if (ndopts.nd_opts_src_lladdr) {
|
|
|
|
lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr,
|
|
|
|
skb->dev);
|
|
|
|
if (!lladdr) {
|
2012-05-17 03:28:38 +08:00
|
|
|
ND_PRINTK(2, warn,
|
|
|
|
"RA: invalid link-layer address length\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
neigh_update(neigh, lladdr, NUD_STALE,
|
|
|
|
NEIGH_UPDATE_F_WEAK_OVERRIDE|
|
|
|
|
NEIGH_UPDATE_F_OVERRIDE|
|
|
|
|
NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
|
|
|
|
NEIGH_UPDATE_F_ISROUTER);
|
|
|
|
}
|
|
|
|
|
2014-06-26 05:44:52 +08:00
|
|
|
if (!ipv6_accept_ra(in6_dev)) {
|
|
|
|
ND_PRINTK(2, info,
|
|
|
|
"RA: %s, accept_ra is false for dev: %s\n",
|
|
|
|
__func__, skb->dev->name);
|
2009-08-29 15:04:09 +08:00
|
|
|
goto out;
|
2014-06-26 05:44:52 +08:00
|
|
|
}
|
2009-08-29 15:04:09 +08:00
|
|
|
|
2006-03-21 09:06:24 +08:00
|
|
|
#ifdef CONFIG_IPV6_ROUTE_INFO
|
2014-07-10 18:02:46 +08:00
|
|
|
if (!in6_dev->cnf.accept_ra_from_local &&
|
|
|
|
ipv6_chk_addr(dev_net(in6_dev->dev), &ipv6_hdr(skb)->saddr,
|
|
|
|
NULL, 0)) {
|
2014-06-26 05:44:52 +08:00
|
|
|
ND_PRINTK(2, info,
|
2014-06-26 05:44:53 +08:00
|
|
|
"RA from local address detected on dev: %s: router info ignored.\n",
|
|
|
|
skb->dev->name);
|
2011-10-25 07:13:15 +08:00
|
|
|
goto skip_routeinfo;
|
2014-06-26 05:44:52 +08:00
|
|
|
}
|
2011-10-25 07:13:15 +08:00
|
|
|
|
2006-03-21 09:07:03 +08:00
|
|
|
if (in6_dev->cnf.accept_ra_rtr_pref && ndopts.nd_opts_ri) {
|
2006-03-21 09:06:24 +08:00
|
|
|
struct nd_opt_hdr *p;
|
|
|
|
for (p = ndopts.nd_opts_ri;
|
|
|
|
p;
|
|
|
|
p = ndisc_next_option(p, ndopts.nd_opts_ri_end)) {
|
2008-03-16 11:56:52 +08:00
|
|
|
struct route_info *ri = (struct route_info *)p;
|
|
|
|
#ifdef CONFIG_IPV6_NDISC_NODETYPE
|
|
|
|
if (skb->ndisc_nodetype == NDISC_NODETYPE_NODEFAULT &&
|
|
|
|
ri->prefix_len == 0)
|
|
|
|
continue;
|
|
|
|
#endif
|
2013-11-26 15:46:56 +08:00
|
|
|
if (ri->prefix_len == 0 &&
|
|
|
|
!in6_dev->cnf.accept_ra_defrtr)
|
|
|
|
continue;
|
2008-03-16 11:56:52 +08:00
|
|
|
if (ri->prefix_len > in6_dev->cnf.accept_ra_rt_info_max_plen)
|
2006-03-21 09:07:03 +08:00
|
|
|
continue;
|
2006-03-21 09:06:24 +08:00
|
|
|
rt6_route_rcv(skb->dev, (u8*)p, (p->nd_opt_len) << 3,
|
2007-04-26 08:54:47 +08:00
|
|
|
&ipv6_hdr(skb)->saddr);
|
2006-03-21 09:06:24 +08:00
|
|
|
}
|
|
|
|
}
|
2011-10-25 07:13:15 +08:00
|
|
|
|
|
|
|
skip_routeinfo:
|
2006-03-21 09:06:24 +08:00
|
|
|
#endif
|
|
|
|
|
2008-03-16 11:59:18 +08:00
|
|
|
#ifdef CONFIG_IPV6_NDISC_NODETYPE
|
2008-03-12 06:35:59 +08:00
|
|
|
/* skip link-specific ndopts from interior routers */
|
2014-06-26 05:44:52 +08:00
|
|
|
if (skb->ndisc_nodetype == NDISC_NODETYPE_NODEFAULT) {
|
|
|
|
ND_PRINTK(2, info,
|
|
|
|
"RA: %s, nodetype is NODEFAULT (interior routes), dev: %s\n",
|
|
|
|
__func__, skb->dev->name);
|
2008-03-12 06:35:59 +08:00
|
|
|
goto out;
|
2014-06-26 05:44:52 +08:00
|
|
|
}
|
2008-03-16 11:59:18 +08:00
|
|
|
#endif
|
2008-03-12 06:35:59 +08:00
|
|
|
|
2006-03-21 08:55:26 +08:00
|
|
|
if (in6_dev->cnf.accept_ra_pinfo && ndopts.nd_opts_pi) {
|
2005-04-17 06:20:36 +08:00
|
|
|
struct nd_opt_hdr *p;
|
|
|
|
for (p = ndopts.nd_opts_pi;
|
|
|
|
p;
|
|
|
|
p = ndisc_next_option(p, ndopts.nd_opts_pi_end)) {
|
2012-01-04 18:49:15 +08:00
|
|
|
addrconf_prefix_rcv(skb->dev, (u8 *)p,
|
|
|
|
(p->nd_opt_len) << 3,
|
|
|
|
ndopts.nd_opts_src_lladdr != NULL);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ndopts.nd_opts_mtu) {
|
2006-11-15 12:56:00 +08:00
|
|
|
__be32 n;
|
2005-04-17 06:20:36 +08:00
|
|
|
u32 mtu;
|
|
|
|
|
2006-11-15 12:56:00 +08:00
|
|
|
memcpy(&n, ((u8*)(ndopts.nd_opts_mtu+1))+2, sizeof(mtu));
|
|
|
|
mtu = ntohl(n);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (mtu < IPV6_MIN_MTU || mtu > skb->dev->mtu) {
|
2012-05-17 03:28:38 +08:00
|
|
|
ND_PRINTK(2, warn, "RA: invalid mtu: %d\n", mtu);
|
2005-04-17 06:20:36 +08:00
|
|
|
} else if (in6_dev->cnf.mtu6 != mtu) {
|
|
|
|
in6_dev->cnf.mtu6 = mtu;
|
|
|
|
|
|
|
|
if (rt)
|
2010-12-09 13:16:57 +08:00
|
|
|
dst_metric_set(&rt->dst, RTAX_MTU, mtu);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
rt6_mtu_change(skb->dev, mtu);
|
|
|
|
}
|
|
|
|
}
|
2007-02-09 22:24:49 +08:00
|
|
|
|
2007-10-11 12:22:05 +08:00
|
|
|
if (ndopts.nd_useropts) {
|
2008-01-22 16:32:53 +08:00
|
|
|
struct nd_opt_hdr *p;
|
|
|
|
for (p = ndopts.nd_useropts;
|
|
|
|
p;
|
|
|
|
p = ndisc_next_useropt(p, ndopts.nd_useropts_end)) {
|
|
|
|
ndisc_ra_useropt(skb, p);
|
2007-10-11 12:22:05 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
if (ndopts.nd_opts_tgt_lladdr || ndopts.nd_opts_rh) {
|
2012-05-17 03:28:38 +08:00
|
|
|
ND_PRINTK(2, warn, "RA: invalid RA options\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
out:
|
2012-10-29 08:13:19 +08:00
|
|
|
ip6_rt_put(rt);
|
2012-01-28 07:07:56 +08:00
|
|
|
if (neigh)
|
2005-04-17 06:20:36 +08:00
|
|
|
neigh_release(neigh);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ndisc_redirect_rcv(struct sk_buff *skb)
|
|
|
|
{
|
2012-12-14 10:59:59 +08:00
|
|
|
u8 *hdr;
|
|
|
|
struct ndisc_options ndopts;
|
|
|
|
struct rd_msg *msg = (struct rd_msg *)skb_transport_header(skb);
|
2013-05-29 04:34:26 +08:00
|
|
|
u32 ndoptlen = skb_tail_pointer(skb) - (skb_transport_header(skb) +
|
2012-12-14 10:59:59 +08:00
|
|
|
offsetof(struct rd_msg, opt));
|
|
|
|
|
2008-03-16 11:59:18 +08:00
|
|
|
#ifdef CONFIG_IPV6_NDISC_NODETYPE
|
2008-03-12 06:35:59 +08:00
|
|
|
switch (skb->ndisc_nodetype) {
|
|
|
|
case NDISC_NODETYPE_HOST:
|
|
|
|
case NDISC_NODETYPE_NODEFAULT:
|
2012-05-17 03:28:38 +08:00
|
|
|
ND_PRINTK(2, warn,
|
|
|
|
"Redirect: from host or unauthorized router\n");
|
2008-03-12 06:35:59 +08:00
|
|
|
return;
|
|
|
|
}
|
2008-03-16 11:59:18 +08:00
|
|
|
#endif
|
2008-03-12 06:35:59 +08:00
|
|
|
|
2007-04-26 08:54:47 +08:00
|
|
|
if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) {
|
2012-05-17 03:28:38 +08:00
|
|
|
ND_PRINTK(2, warn,
|
|
|
|
"Redirect: source address is not link-local\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-12-14 10:59:59 +08:00
|
|
|
if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts))
|
|
|
|
return;
|
|
|
|
|
2013-08-22 12:07:35 +08:00
|
|
|
if (!ndopts.nd_opts_rh) {
|
2013-09-04 19:44:21 +08:00
|
|
|
ip6_redirect_no_header(skb, dev_net(skb->dev),
|
|
|
|
skb->dev->ifindex, 0);
|
2012-12-14 10:59:59 +08:00
|
|
|
return;
|
2013-08-22 12:07:35 +08:00
|
|
|
}
|
2012-12-14 10:59:59 +08:00
|
|
|
|
|
|
|
hdr = (u8 *)ndopts.nd_opts_rh;
|
|
|
|
hdr += 8;
|
|
|
|
if (!pskb_pull(skb, hdr - skb_transport_header(skb)))
|
|
|
|
return;
|
|
|
|
|
2012-07-12 15:33:37 +08:00
|
|
|
icmpv6_notify(skb, NDISC_REDIRECT, 0, 0);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2013-01-21 14:48:53 +08:00
|
|
|
static void ndisc_fill_redirect_hdr_option(struct sk_buff *skb,
|
|
|
|
struct sk_buff *orig_skb,
|
|
|
|
int rd_len)
|
2013-01-21 14:48:09 +08:00
|
|
|
{
|
2013-01-21 14:48:53 +08:00
|
|
|
u8 *opt = skb_put(skb, rd_len);
|
|
|
|
|
2013-01-21 14:48:09 +08:00
|
|
|
memset(opt, 0, 8);
|
|
|
|
*(opt++) = ND_OPT_REDIRECT_HDR;
|
|
|
|
*(opt++) = (rd_len >> 3);
|
|
|
|
opt += 6;
|
|
|
|
|
|
|
|
memcpy(opt, ipv6_hdr(orig_skb), rd_len - 8);
|
|
|
|
}
|
|
|
|
|
2012-01-28 07:30:48 +08:00
|
|
|
void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2008-03-08 03:15:34 +08:00
|
|
|
struct net_device *dev = skb->dev;
|
2008-03-25 20:47:49 +08:00
|
|
|
struct net *net = dev_net(dev);
|
2008-03-08 03:15:34 +08:00
|
|
|
struct sock *sk = net->ipv6.ndisc_sk;
|
2013-01-21 14:48:49 +08:00
|
|
|
int optlen = 0;
|
2012-06-09 14:24:18 +08:00
|
|
|
struct inet_peer *peer;
|
2005-04-17 06:20:36 +08:00
|
|
|
struct sk_buff *buff;
|
2013-01-06 00:34:51 +08:00
|
|
|
struct rd_msg *msg;
|
2005-04-17 06:20:36 +08:00
|
|
|
struct in6_addr saddr_buf;
|
|
|
|
struct rt6_info *rt;
|
|
|
|
struct dst_entry *dst;
|
2011-03-13 05:22:43 +08:00
|
|
|
struct flowi6 fl6;
|
2005-04-17 06:20:36 +08:00
|
|
|
int rd_len;
|
|
|
|
u8 ha_buf[MAX_ADDR_LEN], *ha = NULL;
|
2012-07-10 18:58:16 +08:00
|
|
|
bool ret;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-04-26 08:08:10 +08:00
|
|
|
if (ipv6_get_lladdr(dev, &saddr_buf, IFA_F_TENTATIVE)) {
|
2012-05-17 03:28:38 +08:00
|
|
|
ND_PRINTK(2, warn, "Redirect: no link-local address on %s\n",
|
|
|
|
dev->name);
|
2007-02-09 22:24:49 +08:00
|
|
|
return;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-04-26 08:54:47 +08:00
|
|
|
if (!ipv6_addr_equal(&ipv6_hdr(skb)->daddr, target) &&
|
2007-10-08 15:12:05 +08:00
|
|
|
ipv6_addr_type(target) != (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) {
|
2012-05-17 03:28:38 +08:00
|
|
|
ND_PRINTK(2, warn,
|
|
|
|
"Redirect: target address is not link-local unicast\n");
|
[IPV6]: fix BUG of ndisc_send_redirect()
When I tested IPv6 redirect function about kernel 2.6.19.1, and found
that the kernel can send redirect packets whose target address is global
address, and the target is not the actual endpoint of communication.
But the criteria conform to RFC2461, the target address defines as
following:
Target Address An IP address that is a better first hop to use for
he ICMP Destination Address. When the target is
the actual endpoint of communication, i.e., the
destination is a neighbor, the Target Address field
MUST contain the same value as the ICMP Destination
Address field. Otherwise the target is a better
first-hop router and the Target Address MUST be the
router's link-local address so that hosts can
uniquely identify routers.
According to this definition, when a router redirect to a host, the
target address either the better first-hop router's link-local address
or the same as the ICMP destination address field. But the function of
ndisc_send_redirect() in net/ipv6/ndisc.c, does not check the target
address correctly.
There is another definition about receive Redirect message in RFC2461:
8.1. Validation of Redirect Messages
A host MUST silently discard any received Redirect message that does
not satisfy all of the following validity checks:
......
- The ICMP Target Address is either a link-local address (when
redirected to a router) or the same as the ICMP Destination
Address (when redirected to the on-link destination).
......
And the receive redirect function of ndisc_redirect_rcv() implemented
this definition, checks the target address correctly.
if (ipv6_addr_equal(dest, target)) {
on_link = 1;
} else if (!(ipv6_addr_type(target) & IPV6_ADDR_LINKLOCAL)) {
ND_PRINTK2(KERN_WARNING
"ICMPv6 Redirect: target address is not link-local.\n");
return;
}
So, I think the send redirect function must check the target address
also.
Signed-off-by: Li Yewang <lyw@nanjing-fnst.com>
Acked-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
2007-01-31 06:33:20 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-03-13 05:22:43 +08:00
|
|
|
icmpv6_flow_init(sk, &fl6, NDISC_REDIRECT,
|
2007-12-07 07:43:30 +08:00
|
|
|
&saddr_buf, &ipv6_hdr(skb)->saddr, dev->ifindex);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2011-03-13 05:22:43 +08:00
|
|
|
dst = ip6_route_output(net, NULL, &fl6);
|
2012-02-22 06:10:49 +08:00
|
|
|
if (dst->error) {
|
|
|
|
dst_release(dst);
|
2005-04-17 06:20:36 +08:00
|
|
|
return;
|
2012-02-22 06:10:49 +08:00
|
|
|
}
|
2011-03-13 05:22:43 +08:00
|
|
|
dst = xfrm_lookup(net, dst, flowi6_to_flowi(&fl6), NULL, 0);
|
2011-03-03 05:27:41 +08:00
|
|
|
if (IS_ERR(dst))
|
2005-04-17 06:20:36 +08:00
|
|
|
return;
|
|
|
|
|
|
|
|
rt = (struct rt6_info *) dst;
|
|
|
|
|
|
|
|
if (rt->rt6i_flags & RTF_GATEWAY) {
|
2012-05-17 03:28:38 +08:00
|
|
|
ND_PRINTK(2, warn,
|
|
|
|
"Redirect: destination is not a neighbour\n");
|
2009-02-07 15:47:37 +08:00
|
|
|
goto release;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2012-07-10 18:58:16 +08:00
|
|
|
peer = inet_getpeer_v6(net->ipv6.peers, &rt->rt6i_dst.addr, 1);
|
|
|
|
ret = inet_peer_xrlim_allow(peer, 1*HZ);
|
|
|
|
if (peer)
|
|
|
|
inet_putpeer(peer);
|
|
|
|
if (!ret)
|
2009-02-07 15:47:37 +08:00
|
|
|
goto release;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (dev->addr_len) {
|
2012-01-28 07:30:48 +08:00
|
|
|
struct neighbour *neigh = dst_neigh_lookup(skb_dst(skb), target);
|
|
|
|
if (!neigh) {
|
2012-05-17 03:28:38 +08:00
|
|
|
ND_PRINTK(2, warn,
|
|
|
|
"Redirect: no neigh for target address\n");
|
2012-01-28 07:30:48 +08:00
|
|
|
goto release;
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
read_lock_bh(&neigh->lock);
|
|
|
|
if (neigh->nud_state & NUD_VALID) {
|
|
|
|
memcpy(ha_buf, neigh->ha, dev->addr_len);
|
|
|
|
read_unlock_bh(&neigh->lock);
|
|
|
|
ha = ha_buf;
|
2013-01-21 14:48:49 +08:00
|
|
|
optlen += ndisc_opt_addr_space(dev);
|
2005-04-17 06:20:36 +08:00
|
|
|
} else
|
|
|
|
read_unlock_bh(&neigh->lock);
|
2012-01-28 07:30:48 +08:00
|
|
|
|
|
|
|
neigh_release(neigh);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
rd_len = min_t(unsigned int,
|
2013-01-21 14:48:49 +08:00
|
|
|
IPV6_MIN_MTU - sizeof(struct ipv6hdr) - sizeof(*msg) - optlen,
|
|
|
|
skb->len + 8);
|
2005-04-17 06:20:36 +08:00
|
|
|
rd_len &= ~0x7;
|
2013-01-21 14:48:49 +08:00
|
|
|
optlen += rd_len;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2013-01-21 14:48:49 +08:00
|
|
|
buff = ndisc_alloc_skb(dev, sizeof(*msg) + optlen);
|
2013-01-21 14:48:14 +08:00
|
|
|
if (!buff)
|
2009-02-07 15:47:37 +08:00
|
|
|
goto release;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2013-01-21 14:49:25 +08:00
|
|
|
msg = (struct rd_msg *)skb_put(buff, sizeof(*msg));
|
|
|
|
*msg = (struct rd_msg) {
|
|
|
|
.icmph = {
|
|
|
|
.icmp6_type = NDISC_REDIRECT,
|
|
|
|
},
|
|
|
|
.target = *target,
|
|
|
|
.dest = ipv6_hdr(skb)->daddr,
|
|
|
|
};
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* include target_address option
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (ha)
|
2013-05-31 09:27:55 +08:00
|
|
|
ndisc_fill_addr_option(buff, ND_OPT_TARGET_LL_ADDR, ha);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* build redirect option and copy skb over to the new packet.
|
|
|
|
*/
|
|
|
|
|
2013-01-21 14:48:09 +08:00
|
|
|
if (rd_len)
|
2013-01-21 14:48:53 +08:00
|
|
|
ndisc_fill_redirect_hdr_option(buff, skb, rd_len);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2009-06-02 13:19:30 +08:00
|
|
|
skb_dst_set(buff, dst);
|
2013-01-21 14:49:03 +08:00
|
|
|
ndisc_send_skb(buff, &ipv6_hdr(skb)->saddr, &saddr_buf);
|
2009-02-07 15:47:37 +08:00
|
|
|
return;
|
|
|
|
|
|
|
|
release:
|
|
|
|
dst_release(dst);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void pndisc_redo(struct sk_buff *skb)
|
|
|
|
{
|
2005-10-06 03:11:41 +08:00
|
|
|
ndisc_recv_ns(skb);
|
2005-04-17 06:20:36 +08:00
|
|
|
kfree_skb(skb);
|
|
|
|
}
|
|
|
|
|
2013-08-27 07:36:51 +08:00
|
|
|
static bool ndisc_suppress_frag_ndisc(struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
struct inet6_dev *idev = __in6_dev_get(skb->dev);
|
|
|
|
|
|
|
|
if (!idev)
|
|
|
|
return true;
|
|
|
|
if (IP6CB(skb)->flags & IP6SKB_FRAGMENTED &&
|
|
|
|
idev->cnf.suppress_frag_ndisc) {
|
|
|
|
net_warn_ratelimited("Received fragmented ndisc packet. Carefully consider disabling suppress_frag_ndisc.\n");
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
int ndisc_rcv(struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
struct nd_msg *msg;
|
|
|
|
|
2013-08-27 07:36:51 +08:00
|
|
|
if (ndisc_suppress_frag_ndisc(skb))
|
|
|
|
return 0;
|
|
|
|
|
2013-01-21 14:48:03 +08:00
|
|
|
if (skb_linearize(skb))
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
|
2007-04-26 09:04:18 +08:00
|
|
|
msg = (struct nd_msg *)skb_transport_header(skb);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-04-26 09:04:18 +08:00
|
|
|
__skb_push(skb, skb->data - skb_transport_header(skb));
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-04-26 08:54:47 +08:00
|
|
|
if (ipv6_hdr(skb)->hop_limit != 255) {
|
2012-05-17 03:28:38 +08:00
|
|
|
ND_PRINTK(2, warn, "NDISC: invalid hop-limit: %d\n",
|
|
|
|
ipv6_hdr(skb)->hop_limit);
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (msg->icmph.icmp6_code != 0) {
|
2012-05-17 03:28:38 +08:00
|
|
|
ND_PRINTK(2, warn, "NDISC: invalid ICMPv6 code: %d\n",
|
|
|
|
msg->icmph.icmp6_code);
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-08-15 08:24:31 +08:00
|
|
|
memset(NEIGH_CB(skb), 0, sizeof(struct neighbour_cb));
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
switch (msg->icmph.icmp6_type) {
|
|
|
|
case NDISC_NEIGHBOUR_SOLICITATION:
|
|
|
|
ndisc_recv_ns(skb);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NDISC_NEIGHBOUR_ADVERTISEMENT:
|
|
|
|
ndisc_recv_na(skb);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NDISC_ROUTER_SOLICITATION:
|
|
|
|
ndisc_recv_rs(skb);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NDISC_ROUTER_ADVERTISEMENT:
|
|
|
|
ndisc_router_discovery(skb);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NDISC_REDIRECT:
|
|
|
|
ndisc_redirect_rcv(skb);
|
|
|
|
break;
|
2007-04-21 08:09:22 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ndisc_netdev_event(struct notifier_block *this, unsigned long event, void *ptr)
|
|
|
|
{
|
2013-05-28 09:30:21 +08:00
|
|
|
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
|
2008-03-25 20:47:49 +08:00
|
|
|
struct net *net = dev_net(dev);
|
2012-11-07 00:46:20 +08:00
|
|
|
struct inet6_dev *idev;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
switch (event) {
|
|
|
|
case NETDEV_CHANGEADDR:
|
|
|
|
neigh_changeaddr(&nd_tbl, dev);
|
2013-08-01 16:04:14 +08:00
|
|
|
fib6_run_gc(0, net, false);
|
2012-11-07 00:46:20 +08:00
|
|
|
idev = in6_dev_get(dev);
|
|
|
|
if (!idev)
|
|
|
|
break;
|
|
|
|
if (idev->cnf.ndisc_notify)
|
|
|
|
ndisc_send_unsol_na(dev);
|
|
|
|
in6_dev_put(idev);
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
case NETDEV_DOWN:
|
|
|
|
neigh_ifdown(&nd_tbl, dev);
|
2013-08-01 16:04:14 +08:00
|
|
|
fib6_run_gc(0, net, false);
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
2011-04-15 21:46:02 +08:00
|
|
|
case NETDEV_NOTIFY_PEERS:
|
|
|
|
ndisc_send_unsol_na(dev);
|
|
|
|
break;
|
2005-04-17 06:20:36 +08:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NOTIFY_DONE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct notifier_block ndisc_netdev_notifier = {
|
|
|
|
.notifier_call = ndisc_netdev_event,
|
|
|
|
};
|
|
|
|
|
|
|
|
#ifdef CONFIG_SYSCTL
|
|
|
|
static void ndisc_warn_deprecated_sysctl(struct ctl_table *ctl,
|
|
|
|
const char *func, const char *dev_name)
|
|
|
|
{
|
|
|
|
static char warncomm[TASK_COMM_LEN];
|
|
|
|
static int warned;
|
|
|
|
if (strcmp(warncomm, current->comm) && warned < 5) {
|
|
|
|
strcpy(warncomm, current->comm);
|
2012-05-15 22:11:53 +08:00
|
|
|
pr_warn("process `%s' is using deprecated sysctl (%s) net.ipv6.neigh.%s.%s - use net.ipv6.neigh.%s.%s_ms instead\n",
|
2005-04-17 06:20:36 +08:00
|
|
|
warncomm, func,
|
|
|
|
dev_name, ctl->procname,
|
|
|
|
dev_name, ctl->procname);
|
|
|
|
warned++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-09-24 06:57:19 +08:00
|
|
|
int ndisc_ifinfo_sysctl_change(struct ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *ppos)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct net_device *dev = ctl->extra1;
|
|
|
|
struct inet6_dev *idev;
|
|
|
|
int ret;
|
|
|
|
|
2007-10-18 18:05:25 +08:00
|
|
|
if ((strcmp(ctl->procname, "retrans_time") == 0) ||
|
|
|
|
(strcmp(ctl->procname, "base_reachable_time") == 0))
|
2005-04-17 06:20:36 +08:00
|
|
|
ndisc_warn_deprecated_sysctl(ctl, "syscall", dev ? dev->name : "default");
|
|
|
|
|
2007-10-18 18:05:25 +08:00
|
|
|
if (strcmp(ctl->procname, "retrans_time") == 0)
|
2013-12-08 02:26:54 +08:00
|
|
|
ret = neigh_proc_dointvec(ctl, write, buffer, lenp, ppos);
|
2007-10-18 18:05:25 +08:00
|
|
|
|
|
|
|
else if (strcmp(ctl->procname, "base_reachable_time") == 0)
|
2013-12-08 02:26:54 +08:00
|
|
|
ret = neigh_proc_dointvec_jiffies(ctl, write,
|
|
|
|
buffer, lenp, ppos);
|
2007-10-18 18:05:25 +08:00
|
|
|
|
|
|
|
else if ((strcmp(ctl->procname, "retrans_time_ms") == 0) ||
|
2007-10-29 16:32:23 +08:00
|
|
|
(strcmp(ctl->procname, "base_reachable_time_ms") == 0))
|
2013-12-08 02:26:54 +08:00
|
|
|
ret = neigh_proc_dointvec_ms_jiffies(ctl, write,
|
|
|
|
buffer, lenp, ppos);
|
2007-10-18 18:05:25 +08:00
|
|
|
else
|
2005-04-17 06:20:36 +08:00
|
|
|
ret = -1;
|
|
|
|
|
|
|
|
if (write && ret == 0 && dev && (idev = in6_dev_get(dev)) != NULL) {
|
2013-12-08 02:26:53 +08:00
|
|
|
if (ctl->data == &NEIGH_VAR(idev->nd_parms, BASE_REACHABLE_TIME))
|
|
|
|
idev->nd_parms->reachable_time =
|
|
|
|
neigh_rand_reach_time(NEIGH_VAR(idev->nd_parms, BASE_REACHABLE_TIME));
|
2005-04-17 06:20:36 +08:00
|
|
|
idev->tstamp = jiffies;
|
|
|
|
inet6_ifinfo_notify(RTM_NEWLINK, idev);
|
|
|
|
in6_dev_put(idev);
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2010-01-17 11:35:32 +08:00
|
|
|
static int __net_init ndisc_net_init(struct net *net)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct ipv6_pinfo *np;
|
|
|
|
struct sock *sk;
|
2007-02-09 22:24:49 +08:00
|
|
|
int err;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-04-04 05:31:03 +08:00
|
|
|
err = inet_ctl_sock_create(&sk, PF_INET6,
|
|
|
|
SOCK_RAW, IPPROTO_ICMPV6, net);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (err < 0) {
|
2012-05-17 03:28:38 +08:00
|
|
|
ND_PRINTK(0, err,
|
|
|
|
"NDISC: Failed to initialize the control socket (err %d)\n",
|
|
|
|
err);
|
2005-04-17 06:20:36 +08:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2008-04-04 05:31:03 +08:00
|
|
|
net->ipv6.ndisc_sk = sk;
|
2008-03-08 03:15:34 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
np = inet6_sk(sk);
|
|
|
|
np->hop_limit = 255;
|
|
|
|
/* Do not loopback ndisc messages */
|
|
|
|
np->mc_loop = 0;
|
|
|
|
|
2008-03-08 03:15:34 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-01-17 11:35:32 +08:00
|
|
|
static void __net_exit ndisc_net_exit(struct net *net)
|
2008-03-08 03:15:34 +08:00
|
|
|
{
|
2008-04-04 05:31:03 +08:00
|
|
|
inet_ctl_sock_destroy(net->ipv6.ndisc_sk);
|
2008-03-08 03:15:34 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct pernet_operations ndisc_net_ops = {
|
|
|
|
.init = ndisc_net_init,
|
|
|
|
.exit = ndisc_net_exit,
|
|
|
|
};
|
|
|
|
|
|
|
|
int __init ndisc_init(void)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
|
|
|
err = register_pernet_subsys(&ndisc_net_ops);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2007-02-09 22:24:49 +08:00
|
|
|
/*
|
|
|
|
* Initialize the neighbour table
|
|
|
|
*/
|
2005-04-17 06:20:36 +08:00
|
|
|
neigh_table_init(&nd_tbl);
|
|
|
|
|
|
|
|
#ifdef CONFIG_SYSCTL
|
2013-12-08 02:26:55 +08:00
|
|
|
err = neigh_sysctl_register(NULL, &nd_tbl.parms,
|
2014-07-25 04:19:37 +08:00
|
|
|
ndisc_ifinfo_sysctl_change);
|
2008-03-08 03:15:34 +08:00
|
|
|
if (err)
|
|
|
|
goto out_unregister_pernet;
|
|
|
|
out:
|
2013-11-16 10:52:08 +08:00
|
|
|
#endif
|
2008-03-08 03:15:34 +08:00
|
|
|
return err;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-03-08 03:15:34 +08:00
|
|
|
#ifdef CONFIG_SYSCTL
|
|
|
|
out_unregister_pernet:
|
|
|
|
unregister_pernet_subsys(&ndisc_net_ops);
|
|
|
|
goto out;
|
2013-09-10 03:45:04 +08:00
|
|
|
#endif
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2013-09-10 03:45:04 +08:00
|
|
|
int __init ndisc_late_init(void)
|
|
|
|
{
|
|
|
|
return register_netdevice_notifier(&ndisc_netdev_notifier);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ndisc_late_cleanup(void)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2006-11-04 08:08:19 +08:00
|
|
|
unregister_netdevice_notifier(&ndisc_netdev_notifier);
|
2013-09-10 03:45:04 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void ndisc_cleanup(void)
|
|
|
|
{
|
2005-04-17 06:20:36 +08:00
|
|
|
#ifdef CONFIG_SYSCTL
|
|
|
|
neigh_sysctl_unregister(&nd_tbl.parms);
|
|
|
|
#endif
|
|
|
|
neigh_table_clear(&nd_tbl);
|
2008-03-08 03:15:34 +08:00
|
|
|
unregister_pernet_subsys(&ndisc_net_ops);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|