ndisc.c 42.3 KB
Newer Older
L
Linus Torvalds 已提交
1 2
/*
 *	Neighbour Discovery for IPv6
3
 *	Linux INET6 implementation
L
Linus Torvalds 已提交
4 5
 *
 *	Authors:
6
 *	Pedro Roque		<roque@di.fc.ul.pt>
L
Linus Torvalds 已提交
7 8 9 10 11 12 13 14 15 16 17
 *	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:
 *
18
 *	Alexey I. Froloff		:	RFC6106 (DNSSL) support
19 20
 *	Pierre Ynard			:	export userland ND options
 *						through netlink (RDNSS support)
L
Linus Torvalds 已提交
21 22 23 24 25 26 27 28 29
 *	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
 */

30
#define pr_fmt(fmt) "ICMPv6: " fmt
L
Linus Torvalds 已提交
31 32 33 34 35 36 37 38 39 40 41 42

#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>
43
#include <linux/slab.h>
L
Linus Torvalds 已提交
44 45 46 47
#ifdef CONFIG_SYSCTL
#include <linux/sysctl.h>
#endif

48
#include <linux/if_addr.h>
L
Linus Torvalds 已提交
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
#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>

64 65 66
#include <net/netlink.h>
#include <linux/rtnetlink.h>

L
Linus Torvalds 已提交
67 68
#include <net/flow.h>
#include <net/ip6_checksum.h>
69
#include <net/inet_common.h>
L
Linus Torvalds 已提交
70 71 72 73 74
#include <linux/proc_fs.h>

#include <linux/netfilter.h>
#include <linux/netfilter_ipv6.h>

75 76 77 78 79 80 81 82 83
/* 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)

84 85
static u32 ndisc_hash(const void *pkey,
		      const struct net_device *dev,
86
		      __u32 *hash_rnd);
L
Linus Torvalds 已提交
87 88 89 90 91 92 93
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);

S
Stephen Hemminger 已提交
94
static const struct neigh_ops ndisc_generic_ops = {
L
Linus Torvalds 已提交
95 96 97 98 99 100 101
	.family =		AF_INET6,
	.solicit =		ndisc_solicit,
	.error_report =		ndisc_error_report,
	.output =		neigh_resolve_output,
	.connected_output =	neigh_connected_output,
};

S
Stephen Hemminger 已提交
102
static const struct neigh_ops ndisc_hh_ops = {
L
Linus Torvalds 已提交
103 104 105 106 107 108 109 110
	.family =		AF_INET6,
	.solicit =		ndisc_solicit,
	.error_report =		ndisc_error_report,
	.output =		neigh_resolve_output,
	.connected_output =	neigh_resolve_output,
};


S
Stephen Hemminger 已提交
111
static const struct neigh_ops ndisc_direct_ops = {
L
Linus Torvalds 已提交
112
	.family =		AF_INET6,
113 114
	.output =		neigh_direct_output,
	.connected_output =	neigh_direct_output,
L
Linus Torvalds 已提交
115 116 117 118 119 120 121 122 123 124 125 126
};

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 = {
127 128 129 130 131 132
		.tbl			= &nd_tbl,
		.base_reachable_time	= ND_REACHABLE_TIME,
		.retrans_time		= ND_RETRANS_TIMER,
		.gc_staletime		= 60 * HZ,
		.reachable_time		= ND_REACHABLE_TIME,
		.delay_probe_time	= 5 * HZ,
E
Eric Dumazet 已提交
133
		.queue_len_bytes	= 64*1024,
134 135 136 137 138
		.ucast_probes		= 3,
		.mcast_probes		= 3,
		.anycast_delay		= 1 * HZ,
		.proxy_delay		= (8 * HZ) / 10,
		.proxy_qlen		= 64,
L
Linus Torvalds 已提交
139 140 141 142 143 144 145
	},
	.gc_interval =	  30 * HZ,
	.gc_thresh1 =	 128,
	.gc_thresh2 =	 512,
	.gc_thresh3 =	1024,
};

146
static void ndisc_fill_addr_option(struct sk_buff *skb, int type, void *data)
L
Linus Torvalds 已提交
147
{
148 149 150 151
	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);
L
Linus Torvalds 已提交
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176

	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);
E
Eric Dumazet 已提交
177
	return cur <= end && cur->nd_opt_type == type ? cur : NULL;
L
Linus Torvalds 已提交
178 179
}

180 181
static inline int ndisc_is_useropt(struct nd_opt_hdr *opt)
{
182 183
	return opt->nd_opt_type == ND_OPT_RDNSS ||
		opt->nd_opt_type == ND_OPT_DNSSL;
184 185 186 187 188 189 190 191 192 193
}

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));
E
Eric Dumazet 已提交
194
	return cur <= end && ndisc_is_useropt(cur) ? cur : NULL;
195 196
}

197 198
struct ndisc_options *ndisc_parse_options(u8 *opt, int opt_len,
					  struct ndisc_options *ndopts)
L
Linus Torvalds 已提交
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217
{
	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]) {
218 219 220
				ND_PRINTK(2, warn,
					  "%s: duplicated ND6 option found: type=%d\n",
					  __func__, nd_opt->nd_opt_type);
L
Linus Torvalds 已提交
221 222 223 224 225 226
			} 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;
S
Stephen Hemminger 已提交
227
			if (!ndopts->nd_opt_array[nd_opt->nd_opt_type])
L
Linus Torvalds 已提交
228 229
				ndopts->nd_opt_array[nd_opt->nd_opt_type] = nd_opt;
			break;
230 231 232 233 234 235 236
#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
L
Linus Torvalds 已提交
237
		default:
238 239 240 241 242 243 244 245 246 247
			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.
				 */
248 249 250 251 252
				ND_PRINTK(2, notice,
					  "%s: ignored unsupported option; type=%d, len=%d\n",
					  __func__,
					  nd_opt->nd_opt_type,
					  nd_opt->nd_opt_len);
253
			}
L
Linus Torvalds 已提交
254 255 256 257 258 259 260
		}
		opt_len -= l;
		nd_opt = ((void *)nd_opt) + l;
	}
	return ndopts;
}

261
int ndisc_mc_map(const struct in6_addr *addr, char *buf, struct net_device *dev, int dir)
L
Linus Torvalds 已提交
262 263 264 265 266 267 268 269 270 271 272
{
	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:
273
		ipv6_ib_mc_map(addr, dev->broadcast, buf);
L
Linus Torvalds 已提交
274
		return 0;
275 276
	case ARPHRD_IPGRE:
		return ipv6_ipgre_mc_map(addr, dev->broadcast, buf);
L
Linus Torvalds 已提交
277 278 279 280 281 282 283 284 285
	default:
		if (dir) {
			memcpy(buf, dev->broadcast, dev->addr_len);
			return 0;
		}
	}
	return -EINVAL;
}

286 287
EXPORT_SYMBOL(ndisc_mc_map);

288 289
static u32 ndisc_hash(const void *pkey,
		      const struct net_device *dev,
290
		      __u32 *hash_rnd)
L
Linus Torvalds 已提交
291
{
292
	return ndisc_hashfn(pkey, dev, hash_rnd);
L
Linus Torvalds 已提交
293 294 295 296 297 298 299 300
}

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;
301
	bool is_multicast = ipv6_addr_is_multicast(addr);
L
Linus Torvalds 已提交
302 303 304 305 306 307 308 309 310 311 312

	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;
313
	if (!dev->header_ops) {
L
Linus Torvalds 已提交
314 315
		neigh->nud_state = NUD_NOARP;
		neigh->ops = &ndisc_direct_ops;
316
		neigh->output = neigh_direct_output;
L
Linus Torvalds 已提交
317 318 319 320 321 322 323 324 325 326 327 328 329
	} 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);
		}
330
		if (dev->header_ops->cache)
L
Linus Torvalds 已提交
331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367
			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);
}

368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385
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;
	int err;

	skb = sock_alloc_send_skb(sk,
				  hlen + sizeof(struct ipv6hdr) + len + tlen,
				  1, &err);
	if (!skb) {
		ND_PRINTK(0, err, "ndisc: %s failed to allocate an skb, err=%d\n",
			  __func__, err);
		return NULL;
	}

386 387 388
	skb->protocol = htons(ETH_P_IPV6);
	skb->dev = dev;

389
	skb_reserve(skb, hlen + sizeof(struct ipv6hdr));
390
	skb_reset_transport_header(skb);
391 392 393 394

	return skb;
}

395
static void ip6_nd_hdr(struct sk_buff *skb,
396 397
		       const struct in6_addr *saddr,
		       const struct in6_addr *daddr,
398
		       int hop_limit, int len)
399 400 401
{
	struct ipv6hdr *hdr;

402
	skb_push(skb, sizeof(*hdr));
403 404 405 406 407 408
	skb_reset_network_header(skb);
	hdr = ipv6_hdr(skb);

	ip6_flow_hdr(hdr, 0, 0);

	hdr->payload_len = htons(len);
409 410
	hdr->nexthdr = IPPROTO_ICMPV6;
	hdr->hop_limit = hop_limit;
411 412 413 414 415

	hdr->saddr = *saddr;
	hdr->daddr = *daddr;
}

416 417 418 419 420 421
static struct sk_buff *ndisc_build_skb(struct net_device *dev,
				       const struct in6_addr *daddr,
				       const struct in6_addr *saddr,
				       struct icmp6hdr *icmp6h,
				       const struct in6_addr *target,
				       int llinfo)
L
Linus Torvalds 已提交
422
{
423
	struct net *net = dev_net(dev);
424
	struct sock *sk = net->ipv6.ndisc_sk;
425
	struct sk_buff *skb;
426 427
	struct icmp6hdr *hdr;
	int len;
428
	int optlen = 0;
429
	u8 *opt;
L
Linus Torvalds 已提交
430

431 432 433 434 435
	if (!dev->addr_len)
		llinfo = 0;

	len = sizeof(struct icmp6hdr) + (target ? sizeof(*target) : 0);
	if (llinfo)
436
		optlen += ndisc_opt_addr_space(dev);
L
Linus Torvalds 已提交
437

438
	skb = ndisc_alloc_skb(dev, len + optlen);
439
	if (!skb)
440
		return NULL;
L
Linus Torvalds 已提交
441

442
	skb_put(skb, len);
L
Linus Torvalds 已提交
443

444 445
	hdr = (struct icmp6hdr *)skb_transport_header(skb);
	memcpy(hdr, icmp6h, sizeof(*hdr));
L
Linus Torvalds 已提交
446

447 448
	opt = skb_transport_header(skb) + sizeof(struct icmp6hdr);
	if (target) {
A
Alexey Dobriyan 已提交
449
		*(struct in6_addr *)opt = *target;
450 451
		opt += sizeof(*target);
	}
L
Linus Torvalds 已提交
452

453
	if (llinfo)
454
		ndisc_fill_addr_option(skb, llinfo, dev->dev_addr);
L
Linus Torvalds 已提交
455

456
	hdr->icmp6_cksum = csum_ipv6_magic(saddr, daddr, skb->len,
457
					   IPPROTO_ICMPV6,
458
					   csum_partial(hdr,
459 460 461
							skb->len, 0));

	ip6_nd_hdr(skb, saddr, daddr, inet6_sk(sk)->hop_limit, skb->len);
L
Linus Torvalds 已提交
462

463 464 465
	return skb;
}

466
static void ndisc_send_skb(struct sk_buff *skb,
467
			   const struct in6_addr *daddr,
468
			   const struct in6_addr *saddr)
469
{
470
	struct flowi6 fl6;
471
	struct dst_entry *dst;
472
	struct net *net = dev_net(skb->dev);
473 474 475
	struct sock *sk = net->ipv6.ndisc_sk;
	struct inet6_dev *idev;
	int err;
476
	struct icmp6hdr *icmp6h = icmp6_hdr(skb);
477 478 479 480
	u8 type;

	type = icmp6h->icmp6_type;

481 482
	icmpv6_flow_init(sk, &fl6, type, saddr, daddr, skb->dev->ifindex);
	dst = icmp6_dst_alloc(skb->dev, &fl6);
483
	if (IS_ERR(dst)) {
484 485 486 487
		kfree_skb(skb);
		return;
	}

E
Eric Dumazet 已提交
488
	skb_dst_set(skb, dst);
489

E
Eric Dumazet 已提交
490 491
	rcu_read_lock();
	idev = __in6_dev_get(dst->dev);
492
	IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUT, skb->len);
493

494
	err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, skb, NULL, dst->dev,
495
		      dst_output);
L
Linus Torvalds 已提交
496
	if (!err) {
497
		ICMP6MSGOUT_INC_STATS(net, idev, type);
498
		ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS);
L
Linus Torvalds 已提交
499 500
	}

E
Eric Dumazet 已提交
501
	rcu_read_unlock();
502
}
L
Linus Torvalds 已提交
503

504 505 506 507 508 509 510 511 512 513 514 515 516 517 518
/*
 *	Send a Neighbour Discover packet
 */
static void __ndisc_send(struct net_device *dev,
			 const struct in6_addr *daddr,
			 const struct in6_addr *saddr,
			 struct icmp6hdr *icmp6h, const struct in6_addr *target,
			 int llinfo)
{
	struct sk_buff *skb;

	skb = ndisc_build_skb(dev, daddr, saddr, icmp6h, target, llinfo);
	if (!skb)
		return;

519
	ndisc_send_skb(skb, daddr, saddr);
520 521
}

522
static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
523 524
			  const struct in6_addr *daddr,
			  const struct in6_addr *solicited_addr,
525
			  bool router, bool solicited, bool override, bool inc_opt)
526 527 528
{
	struct in6_addr tmpaddr;
	struct inet6_ifaddr *ifp;
529
	const struct in6_addr *src_addr;
530 531 532 533 534
	struct icmp6hdr icmp6h = {
		.icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT,
	};

	/* for anycast or proxy, solicited_addr != src_addr */
535
	ifp = ipv6_get_ifaddr(dev_net(dev), solicited_addr, dev, 1);
536 537 538 539
	if (ifp) {
		src_addr = solicited_addr;
		if (ifp->flags & IFA_F_OPTIMISTIC)
			override = 0;
540
		inc_opt |= ifp->idev->cnf.force_tllao;
541 542
		in6_ifa_put(ifp);
	} else {
543
		if (ipv6_dev_get_saddr(dev_net(dev), dev, daddr,
544
				       inet6_sk(dev_net(dev)->ipv6.ndisc_sk)->srcprefs,
545
				       &tmpaddr))
546 547 548 549 550 551 552 553
			return;
		src_addr = &tmpaddr;
	}

	icmp6h.icmp6_router = router;
	icmp6h.icmp6_solicited = solicited;
	icmp6h.icmp6_override = override;

554
	__ndisc_send(dev, daddr, src_addr, &icmp6h, solicited_addr,
555
		     inc_opt ? ND_OPT_TARGET_LL_ADDR : 0);
556 557
}

558 559 560 561 562 563 564 565 566 567 568
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) {
569
		ndisc_send_na(dev, NULL, &in6addr_linklocal_allnodes, &ifa->addr,
570 571 572 573 574 575 576 577 578
			      /*router=*/ !!idev->cnf.forwarding,
			      /*solicited=*/ false, /*override=*/ true,
			      /*inc_opt=*/ true);
	}
	read_unlock_bh(&idev->lock);

	in6_dev_put(idev);
}

L
Linus Torvalds 已提交
579
void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,
580 581
		   const struct in6_addr *solicit,
		   const struct in6_addr *daddr, const struct in6_addr *saddr)
L
Linus Torvalds 已提交
582 583
{
	struct in6_addr addr_buf;
584 585 586
	struct icmp6hdr icmp6h = {
		.icmp6_type = NDISC_NEIGHBOUR_SOLICITATION,
	};
L
Linus Torvalds 已提交
587 588

	if (saddr == NULL) {
589 590
		if (ipv6_get_lladdr(dev, &addr_buf,
				   (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)))
L
Linus Torvalds 已提交
591 592 593 594
			return;
		saddr = &addr_buf;
	}

595
	__ndisc_send(dev, daddr, saddr, &icmp6h, solicit,
596
		     !ipv6_addr_any(saddr) ? ND_OPT_SOURCE_LL_ADDR : 0);
L
Linus Torvalds 已提交
597 598
}

599 600
void ndisc_send_rs(struct net_device *dev, const struct in6_addr *saddr,
		   const struct in6_addr *daddr)
L
Linus Torvalds 已提交
601
{
602 603 604
	struct icmp6hdr icmp6h = {
		.icmp6_type = NDISC_ROUTER_SOLICITATION,
	};
605 606 607 608 609 610 611 612 613
	int send_sllao = dev->addr_len;

#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
J
Joe Perches 已提交
614
	 * suppress the inclusion of the sllao.
615 616
	 */
	if (send_sllao) {
617
		struct inet6_ifaddr *ifp = ipv6_get_ifaddr(dev_net(dev), saddr,
618
							   dev, 1);
619 620
		if (ifp) {
			if (ifp->flags & IFA_F_OPTIMISTIC)  {
621
				send_sllao = 0;
622
			}
623
			in6_ifa_put(ifp);
624 625 626 627 628
		} else {
			send_sllao = 0;
		}
	}
#endif
629
	__ndisc_send(dev, daddr, saddr, &icmp6h, NULL,
630
		     send_sllao ? ND_OPT_SOURCE_LL_ADDR : 0);
L
Linus Torvalds 已提交
631
}
632

L
Linus Torvalds 已提交
633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653

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);

654
	if (skb && ipv6_chk_addr(dev_net(dev), &ipv6_hdr(skb)->saddr, dev, 1))
655
		saddr = &ipv6_hdr(skb)->saddr;
L
Linus Torvalds 已提交
656 657 658

	if ((probes -= neigh->parms->ucast_probes) < 0) {
		if (!(neigh->nud_state & NUD_VALID)) {
659 660 661
			ND_PRINTK(1, dbg,
				  "%s: trying to ucast probe in NUD_INVALID: %pI6\n",
				  __func__, target);
L
Linus Torvalds 已提交
662 663 664 665 666 667 668 669 670 671 672 673
		}
		ndisc_send_ns(dev, neigh, target, target, saddr);
	} else if ((probes -= neigh->parms->app_probes) < 0) {
#ifdef CONFIG_ARPD
		neigh_app_ns(neigh);
#endif
	} else {
		addrconf_addr_solict_mult(target, &mcaddr);
		ndisc_send_ns(dev, NULL, target, &mcaddr, saddr);
	}
}

674 675
static int pndisc_is_router(const void *pkey,
			    struct net_device *dev)
676 677
{
	struct pneigh_entry *n;
678
	int ret = -1;
679 680

	read_lock_bh(&nd_tbl.lock);
681 682 683
	n = __pneigh_lookup(&nd_tbl, dev_net(dev), pkey, dev);
	if (n)
		ret = !!(n->flags & NTF_ROUTER);
684 685
	read_unlock_bh(&nd_tbl.lock);

686
	return ret;
687 688
}

L
Linus Torvalds 已提交
689 690
static void ndisc_recv_ns(struct sk_buff *skb)
{
691
	struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb);
692 693
	const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
	const struct in6_addr *daddr = &ipv6_hdr(skb)->daddr;
L
Linus Torvalds 已提交
694
	u8 *lladdr = NULL;
695 696
	u32 ndoptlen = skb->tail - (skb->transport_header +
				    offsetof(struct nd_msg, opt));
L
Linus Torvalds 已提交
697 698 699 700 701 702
	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);
703
	bool inc;
704
	int is_router = -1;
L
Linus Torvalds 已提交
705

706 707 708 709 710
	if (skb->len < sizeof(struct nd_msg)) {
		ND_PRINTK(2, warn, "NS: packet too short\n");
		return;
	}

L
Linus Torvalds 已提交
711
	if (ipv6_addr_is_multicast(&msg->target)) {
712
		ND_PRINTK(2, warn, "NS: multicast target address\n");
L
Linus Torvalds 已提交
713 714 715 716 717 718 719
		return;
	}

	/*
	 * RFC2461 7.1.1:
	 * DAD has to be destined for solicited node multicast address.
	 */
720
	if (dad && !ipv6_addr_is_solict_mult(daddr)) {
721
		ND_PRINTK(2, warn, "NS: bad DAD packet (wrong destination)\n");
L
Linus Torvalds 已提交
722 723 724 725
		return;
	}

	if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts)) {
726
		ND_PRINTK(2, warn, "NS: invalid ND options\n");
L
Linus Torvalds 已提交
727 728 729 730 731 732
		return;
	}

	if (ndopts.nd_opts_src_lladdr) {
		lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr, dev);
		if (!lladdr) {
733 734
			ND_PRINTK(2, warn,
				  "NS: invalid link-layer address length\n");
L
Linus Torvalds 已提交
735 736 737 738
			return;
		}

		/* RFC2461 7.1.1:
739 740
		 *	If the IP source address is the unspecified address,
		 *	there MUST NOT be source link-layer address option
L
Linus Torvalds 已提交
741 742 743
		 *	in the message.
		 */
		if (dad) {
744 745
			ND_PRINTK(2, warn,
				  "NS: bad DAD packet (link-layer address option)\n");
L
Linus Torvalds 已提交
746 747 748 749 750 751
			return;
		}
	}

	inc = ipv6_addr_is_multicast(daddr);

752
	ifp = ipv6_get_ifaddr(dev_net(dev), &msg->target, dev, 1);
753
	if (ifp) {
754 755 756 757 758 759 760 761 762

		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);
763
				return;
764 765 766 767 768 769 770 771
			} 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))
L
Linus Torvalds 已提交
772 773 774 775 776 777
					goto out;
			}
		}

		idev = ifp->idev;
	} else {
778 779
		struct net *net = dev_net(dev);

L
Linus Torvalds 已提交
780 781 782 783 784 785
		idev = in6_dev_get(dev);
		if (!idev) {
			/* XXX: count this drop? */
			return;
		}

786
		if (ipv6_chk_acast_addr(net, dev, &msg->target) ||
787
		    (idev->cnf.forwarding &&
788
		     (net->ipv6.devconf_all->proxy_ndp || idev->cnf.proxy_ndp) &&
789
		     (is_router = pndisc_is_router(&msg->target, dev)) >= 0)) {
790
			if (!(NEIGH_CB(skb)->flags & LOCALLY_ENQUEUED) &&
L
Linus Torvalds 已提交
791 792 793 794 795
			    skb->pkt_type != PACKET_HOST &&
			    inc != 0 &&
			    idev->nd_parms->proxy_delay != 0) {
				/*
				 * for anycast or proxy,
796 797
				 * sender should delay its response
				 * by a random time between 0 and
L
Linus Torvalds 已提交
798 799 800 801 802 803 804 805 806 807 808 809
				 * 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;
	}

810
	if (is_router < 0)
811
		is_router = idev->cnf.forwarding;
812

L
Linus Torvalds 已提交
813
	if (dad) {
814
		ndisc_send_na(dev, NULL, &in6addr_linklocal_allnodes, &msg->target,
815
			      !!is_router, false, (ifp != NULL), true);
L
Linus Torvalds 已提交
816 817 818 819 820 821 822 823
		goto out;
	}

	if (inc)
		NEIGH_CACHE_STAT_INC(&nd_tbl, rcv_probes_mcast);
	else
		NEIGH_CACHE_STAT_INC(&nd_tbl, rcv_probes_ucast);

824
	/*
L
Linus Torvalds 已提交
825 826 827 828 829 830
	 *	update / create cache entry
	 *	for the source address
	 */
	neigh = __neigh_lookup(&nd_tbl, saddr, dev,
			       !inc || lladdr || !dev->addr_len);
	if (neigh)
831
		neigh_update(neigh, lladdr, NUD_STALE,
L
Linus Torvalds 已提交
832 833
			     NEIGH_UPDATE_F_WEAK_OVERRIDE|
			     NEIGH_UPDATE_F_OVERRIDE);
834
	if (neigh || !dev->header_ops) {
L
Linus Torvalds 已提交
835
		ndisc_send_na(dev, neigh, saddr, &msg->target,
836 837
			      !!is_router,
			      true, (ifp != NULL && inc), inc);
L
Linus Torvalds 已提交
838 839 840 841 842 843 844 845 846 847 848 849 850
		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)
{
851
	struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb);
852 853
	const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
	const struct in6_addr *daddr = &ipv6_hdr(skb)->daddr;
L
Linus Torvalds 已提交
854
	u8 *lladdr = NULL;
855 856
	u32 ndoptlen = skb->tail - (skb->transport_header +
				    offsetof(struct nd_msg, opt));
L
Linus Torvalds 已提交
857 858 859 860 861 862
	struct ndisc_options ndopts;
	struct net_device *dev = skb->dev;
	struct inet6_ifaddr *ifp;
	struct neighbour *neigh;

	if (skb->len < sizeof(struct nd_msg)) {
863
		ND_PRINTK(2, warn, "NA: packet too short\n");
L
Linus Torvalds 已提交
864 865 866 867
		return;
	}

	if (ipv6_addr_is_multicast(&msg->target)) {
868
		ND_PRINTK(2, warn, "NA: target address is multicast\n");
L
Linus Torvalds 已提交
869 870 871 872 873
		return;
	}

	if (ipv6_addr_is_multicast(daddr) &&
	    msg->icmph.icmp6_solicited) {
874
		ND_PRINTK(2, warn, "NA: solicited NA is multicasted\n");
L
Linus Torvalds 已提交
875 876
		return;
	}
877

L
Linus Torvalds 已提交
878
	if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts)) {
879
		ND_PRINTK(2, warn, "NS: invalid ND option\n");
L
Linus Torvalds 已提交
880 881 882 883 884
		return;
	}
	if (ndopts.nd_opts_tgt_lladdr) {
		lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr, dev);
		if (!lladdr) {
885 886
			ND_PRINTK(2, warn,
				  "NA: invalid link-layer address length\n");
L
Linus Torvalds 已提交
887 888 889
			return;
		}
	}
890
	ifp = ipv6_get_ifaddr(dev_net(dev), &msg->target, dev, 1);
891
	if (ifp) {
892 893 894 895
		if (skb->pkt_type != PACKET_LOOPBACK
		    && (ifp->flags & IFA_F_TENTATIVE)) {
				addrconf_dad_failure(ifp);
				return;
L
Linus Torvalds 已提交
896 897 898 899 900
		}
		/* 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 :-)
901 902 903 904

		   We should not print the error if NA has been
		   received from loopback - it is just our own
		   unsolicited advertisement.
L
Linus Torvalds 已提交
905
		 */
906
		if (skb->pkt_type != PACKET_LOOPBACK)
907 908 909
			ND_PRINTK(1, warn,
				  "NA: someone advertises our address %pI6 on %s!\n",
				  &ifp->addr, ifp->idev->dev->name);
L
Linus Torvalds 已提交
910 911 912 913 914 915 916
		in6_ifa_put(ifp);
		return;
	}
	neigh = neigh_lookup(&nd_tbl, &msg->target, dev);

	if (neigh) {
		u8 old_flags = neigh->flags;
917
		struct net *net = dev_net(dev);
L
Linus Torvalds 已提交
918 919 920 921

		if (neigh->nud_state & NUD_FAILED)
			goto out;

922 923 924 925 926 927
		/*
		 * 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) &&
928 929
		    net->ipv6.devconf_all->forwarding && net->ipv6.devconf_all->proxy_ndp &&
		    pneigh_lookup(&nd_tbl, net, &msg->target, dev, 0)) {
930
			/* XXX: idev->cnf.proxy_ndp */
931
			goto out;
932
		}
933

L
Linus Torvalds 已提交
934 935 936 937 938 939 940 941 942 943 944 945 946 947
		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
			 */
			struct rt6_info *rt;
			rt = rt6_get_dflt_router(saddr, dev);
			if (rt)
948
				ip6_del_rt(rt);
L
Linus Torvalds 已提交
949 950 951 952 953 954 955 956 957
		}

out:
		neigh_release(neigh);
	}
}

static void ndisc_recv_rs(struct sk_buff *skb)
{
958
	struct rs_msg *rs_msg = (struct rs_msg *)skb_transport_header(skb);
L
Linus Torvalds 已提交
959 960 961
	unsigned long ndoptlen = skb->len - sizeof(*rs_msg);
	struct neighbour *neigh;
	struct inet6_dev *idev;
962
	const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
L
Linus Torvalds 已提交
963 964 965 966 967 968
	struct ndisc_options ndopts;
	u8 *lladdr = NULL;

	if (skb->len < sizeof(*rs_msg))
		return;

E
Eric Dumazet 已提交
969
	idev = __in6_dev_get(skb->dev);
L
Linus Torvalds 已提交
970
	if (!idev) {
971
		ND_PRINTK(1, err, "RS: can't find in6 device\n");
L
Linus Torvalds 已提交
972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987
		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)) {
988
		ND_PRINTK(2, notice, "NS: invalid ND option, ignored\n");
L
Linus Torvalds 已提交
989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007
		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:
E
Eric Dumazet 已提交
1008
	return;
L
Linus Torvalds 已提交
1009 1010
}

1011 1012 1013 1014 1015 1016
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;
1017
	struct net *net = dev_net(ra->dev);
1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035
	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;
1036
	ndmsg->nduseropt_ifindex = ra->dev->ifindex;
1037 1038 1039 1040 1041 1042
	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);

D
David S. Miller 已提交
1043 1044 1045
	if (nla_put(skb, NDUSEROPT_SRCADDR, sizeof(struct in6_addr),
		    &ipv6_hdr(ra)->saddr))
		goto nla_put_failure;
1046 1047
	nlmsg_end(skb, nlh);

1048
	rtnl_notify(skb, net, 0, RTNLGRP_ND_USEROPT, NULL, GFP_ATOMIC);
1049 1050 1051 1052 1053 1054
	return;

nla_put_failure:
	nlmsg_free(skb);
	err = -EMSGSIZE;
errout:
1055
	rtnl_set_sk_err(net, RTNLGRP_ND_USEROPT, err);
1056 1057
}

L
Linus Torvalds 已提交
1058 1059
static void ndisc_router_discovery(struct sk_buff *skb)
{
1060
	struct ra_msg *ra_msg = (struct ra_msg *)skb_transport_header(skb);
L
Linus Torvalds 已提交
1061 1062
	struct neighbour *neigh = NULL;
	struct inet6_dev *in6_dev;
1063
	struct rt6_info *rt = NULL;
L
Linus Torvalds 已提交
1064 1065 1066
	int lifetime;
	struct ndisc_options ndopts;
	int optlen;
1067
	unsigned int pref = 0;
L
Linus Torvalds 已提交
1068 1069 1070

	__u8 * opt = (__u8 *)(ra_msg + 1);

1071
	optlen = (skb->tail - skb->transport_header) - sizeof(struct ra_msg);
L
Linus Torvalds 已提交
1072

1073
	if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) {
1074
		ND_PRINTK(2, warn, "RA: source address is not link-local\n");
L
Linus Torvalds 已提交
1075 1076 1077
		return;
	}
	if (optlen < 0) {
1078
		ND_PRINTK(2, warn, "RA: packet too short\n");
L
Linus Torvalds 已提交
1079 1080 1081
		return;
	}

1082
#ifdef CONFIG_IPV6_NDISC_NODETYPE
1083
	if (skb->ndisc_nodetype == NDISC_NODETYPE_HOST) {
1084
		ND_PRINTK(2, warn, "RA: from host or unauthorized router\n");
1085 1086
		return;
	}
1087
#endif
1088

L
Linus Torvalds 已提交
1089 1090 1091 1092
	/*
	 *	set the RA_RECV flag in the interface
	 */

E
Eric Dumazet 已提交
1093
	in6_dev = __in6_dev_get(skb->dev);
L
Linus Torvalds 已提交
1094
	if (in6_dev == NULL) {
1095 1096
		ND_PRINTK(0, err, "RA: can't find inet6 device for %s\n",
			  skb->dev->name);
L
Linus Torvalds 已提交
1097 1098 1099 1100
		return;
	}

	if (!ndisc_parse_options(opt, optlen, &ndopts)) {
1101
		ND_PRINTK(2, warn, "RA: invalid ND options\n");
L
Linus Torvalds 已提交
1102 1103 1104
		return;
	}

1105
	if (!ipv6_accept_ra(in6_dev))
1106 1107
		goto skip_linkparms;

1108
#ifdef CONFIG_IPV6_NDISC_NODETYPE
1109 1110 1111
	/* skip link-specific parameters from interior routers */
	if (skb->ndisc_nodetype == NDISC_NODETYPE_NODEFAULT)
		goto skip_linkparms;
1112
#endif
1113

L
Linus Torvalds 已提交
1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132
	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);

1133 1134 1135
	if (!in6_dev->cnf.accept_ra_defrtr)
		goto skip_defrtr;

1136 1137 1138
	if (ipv6_chk_addr(dev_net(in6_dev->dev), &ipv6_hdr(skb)->saddr, NULL, 0))
		goto skip_defrtr;

L
Linus Torvalds 已提交
1139 1140
	lifetime = ntohs(ra_msg->icmph.icmp6_rt_lifetime);

1141 1142 1143
#ifdef CONFIG_IPV6_ROUTER_PREF
	pref = ra_msg->icmph.icmp6_router_pref;
	/* 10b is handled as if it were 00b (medium) */
1144
	if (pref == ICMPV6_ROUTER_PREF_INVALID ||
1145
	    !in6_dev->cnf.accept_ra_rtr_pref)
1146 1147 1148
		pref = ICMPV6_ROUTER_PREF_MEDIUM;
#endif

1149
	rt = rt6_get_dflt_router(&ipv6_hdr(skb)->saddr, skb->dev);
L
Linus Torvalds 已提交
1150

1151 1152 1153
	if (rt) {
		neigh = dst_neigh_lookup(&rt->dst, &ipv6_hdr(skb)->saddr);
		if (!neigh) {
1154 1155 1156
			ND_PRINTK(0, err,
				  "RA: %s got default router without neighbour\n",
				  __func__);
A
Amerigo Wang 已提交
1157
			ip6_rt_put(rt);
1158 1159 1160
			return;
		}
	}
L
Linus Torvalds 已提交
1161
	if (rt && lifetime == 0) {
1162
		ip6_del_rt(rt);
L
Linus Torvalds 已提交
1163 1164 1165 1166
		rt = NULL;
	}

	if (rt == NULL && lifetime) {
1167
		ND_PRINTK(3, dbg, "RA: adding default router\n");
L
Linus Torvalds 已提交
1168

1169
		rt = rt6_add_dflt_router(&ipv6_hdr(skb)->saddr, skb->dev, pref);
L
Linus Torvalds 已提交
1170
		if (rt == NULL) {
1171 1172 1173
			ND_PRINTK(0, err,
				  "RA: %s failed to add default route\n",
				  __func__);
L
Linus Torvalds 已提交
1174 1175 1176
			return;
		}

1177
		neigh = dst_neigh_lookup(&rt->dst, &ipv6_hdr(skb)->saddr);
L
Linus Torvalds 已提交
1178
		if (neigh == NULL) {
1179 1180 1181
			ND_PRINTK(0, err,
				  "RA: %s got default router without neighbour\n",
				  __func__);
A
Amerigo Wang 已提交
1182
			ip6_rt_put(rt);
L
Linus Torvalds 已提交
1183 1184 1185
			return;
		}
		neigh->flags |= NTF_ROUTER;
1186
	} else if (rt) {
1187
		rt->rt6i_flags = (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);
L
Linus Torvalds 已提交
1188 1189 1190
	}

	if (rt)
1191
		rt6_set_expires(rt, jiffies + (HZ * lifetime));
L
Linus Torvalds 已提交
1192 1193 1194
	if (ra_msg->icmph.icmp6_hop_limit) {
		in6_dev->cnf.hop_limit = ra_msg->icmph.icmp6_hop_limit;
		if (rt)
1195 1196
			dst_metric_set(&rt->dst, RTAX_HOPLIMIT,
				       ra_msg->icmph.icmp6_hop_limit);
L
Linus Torvalds 已提交
1197 1198
	}

1199 1200
skip_defrtr:

L
Linus Torvalds 已提交
1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233
	/*
	 *	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;
			in6_dev->nd_parms->retrans_time = rtime;
			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;

			if (rtime != in6_dev->nd_parms->base_reachable_time) {
				in6_dev->nd_parms->base_reachable_time = rtime;
				in6_dev->nd_parms->gc_staletime = 3 * rtime;
				in6_dev->nd_parms->reachable_time = neigh_rand_reach_time(rtime);
				in6_dev->tstamp = jiffies;
				inet6_ifinfo_notify(RTM_NEWLINK, in6_dev);
			}
		}
	}

1234 1235
skip_linkparms:

L
Linus Torvalds 已提交
1236 1237 1238 1239 1240
	/*
	 *	Process options.
	 */

	if (!neigh)
1241
		neigh = __neigh_lookup(&nd_tbl, &ipv6_hdr(skb)->saddr,
L
Linus Torvalds 已提交
1242 1243 1244 1245 1246 1247 1248
				       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) {
1249 1250
				ND_PRINTK(2, warn,
					  "RA: invalid link-layer address length\n");
L
Linus Torvalds 已提交
1251 1252 1253 1254 1255 1256 1257 1258 1259 1260
				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);
	}

1261
	if (!ipv6_accept_ra(in6_dev))
1262 1263
		goto out;

1264
#ifdef CONFIG_IPV6_ROUTE_INFO
1265 1266 1267
	if (ipv6_chk_addr(dev_net(in6_dev->dev), &ipv6_hdr(skb)->saddr, NULL, 0))
		goto skip_routeinfo;

1268
	if (in6_dev->cnf.accept_ra_rtr_pref && ndopts.nd_opts_ri) {
1269 1270 1271 1272
		struct nd_opt_hdr *p;
		for (p = ndopts.nd_opts_ri;
		     p;
		     p = ndisc_next_option(p, ndopts.nd_opts_ri_end)) {
1273 1274 1275 1276 1277 1278 1279
			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
			if (ri->prefix_len > in6_dev->cnf.accept_ra_rt_info_max_plen)
1280
				continue;
1281
			rt6_route_rcv(skb->dev, (u8*)p, (p->nd_opt_len) << 3,
1282
				      &ipv6_hdr(skb)->saddr);
1283 1284
		}
	}
1285 1286

skip_routeinfo:
1287 1288
#endif

1289
#ifdef CONFIG_IPV6_NDISC_NODETYPE
1290 1291 1292
	/* skip link-specific ndopts from interior routers */
	if (skb->ndisc_nodetype == NDISC_NODETYPE_NODEFAULT)
		goto out;
1293
#endif
1294

1295
	if (in6_dev->cnf.accept_ra_pinfo && ndopts.nd_opts_pi) {
L
Linus Torvalds 已提交
1296 1297 1298 1299
		struct nd_opt_hdr *p;
		for (p = ndopts.nd_opts_pi;
		     p;
		     p = ndisc_next_option(p, ndopts.nd_opts_pi_end)) {
1300 1301 1302
			addrconf_prefix_rcv(skb->dev, (u8 *)p,
					    (p->nd_opt_len) << 3,
					    ndopts.nd_opts_src_lladdr != NULL);
L
Linus Torvalds 已提交
1303 1304 1305 1306
		}
	}

	if (ndopts.nd_opts_mtu) {
A
Al Viro 已提交
1307
		__be32 n;
L
Linus Torvalds 已提交
1308 1309
		u32 mtu;

A
Al Viro 已提交
1310 1311
		memcpy(&n, ((u8*)(ndopts.nd_opts_mtu+1))+2, sizeof(mtu));
		mtu = ntohl(n);
L
Linus Torvalds 已提交
1312 1313

		if (mtu < IPV6_MIN_MTU || mtu > skb->dev->mtu) {
1314
			ND_PRINTK(2, warn, "RA: invalid mtu: %d\n", mtu);
L
Linus Torvalds 已提交
1315 1316 1317 1318
		} else if (in6_dev->cnf.mtu6 != mtu) {
			in6_dev->cnf.mtu6 = mtu;

			if (rt)
1319
				dst_metric_set(&rt->dst, RTAX_MTU, mtu);
L
Linus Torvalds 已提交
1320 1321 1322 1323

			rt6_mtu_change(skb->dev, mtu);
		}
	}
1324

1325
	if (ndopts.nd_useropts) {
1326 1327 1328 1329 1330
		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);
1331 1332 1333
		}
	}

L
Linus Torvalds 已提交
1334
	if (ndopts.nd_opts_tgt_lladdr || ndopts.nd_opts_rh) {
1335
		ND_PRINTK(2, warn, "RA: invalid RA options\n");
L
Linus Torvalds 已提交
1336 1337
	}
out:
A
Amerigo Wang 已提交
1338
	ip6_rt_put(rt);
1339
	if (neigh)
L
Linus Torvalds 已提交
1340 1341 1342 1343 1344
		neigh_release(neigh);
}

static void ndisc_redirect_rcv(struct sk_buff *skb)
{
1345 1346 1347 1348 1349 1350
	u8 *hdr;
	struct ndisc_options ndopts;
	struct rd_msg *msg = (struct rd_msg *)skb_transport_header(skb);
	u32 ndoptlen = skb->tail - (skb->transport_header +
				    offsetof(struct rd_msg, opt));

1351
#ifdef CONFIG_IPV6_NDISC_NODETYPE
1352 1353 1354
	switch (skb->ndisc_nodetype) {
	case NDISC_NODETYPE_HOST:
	case NDISC_NODETYPE_NODEFAULT:
1355 1356
		ND_PRINTK(2, warn,
			  "Redirect: from host or unauthorized router\n");
1357 1358
		return;
	}
1359
#endif
1360

1361
	if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) {
1362 1363
		ND_PRINTK(2, warn,
			  "Redirect: source address is not link-local\n");
L
Linus Torvalds 已提交
1364 1365 1366
		return;
	}

1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377
	if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts))
		return;

	if (!ndopts.nd_opts_rh)
		return;

	hdr = (u8 *)ndopts.nd_opts_rh;
	hdr += 8;
	if (!pskb_pull(skb, hdr - skb_transport_header(skb)))
		return;

1378
	icmpv6_notify(skb, NDISC_REDIRECT, 0, 0);
L
Linus Torvalds 已提交
1379 1380
}

1381 1382 1383
static void ndisc_fill_redirect_hdr_option(struct sk_buff *skb,
					   struct sk_buff *orig_skb,
					   int rd_len)
1384
{
1385 1386
	u8 *opt = skb_put(skb, rd_len);

1387 1388 1389 1390 1391 1392 1393 1394
	memset(opt, 0, 8);
	*(opt++) = ND_OPT_REDIRECT_HDR;
	*(opt++) = (rd_len >> 3);
	opt += 6;

	memcpy(opt, ipv6_hdr(orig_skb), rd_len - 8);
}

1395
void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target)
L
Linus Torvalds 已提交
1396
{
1397
	struct net_device *dev = skb->dev;
1398
	struct net *net = dev_net(dev);
1399
	struct sock *sk = net->ipv6.ndisc_sk;
1400
	int optlen = 0;
1401
	struct inet_peer *peer;
L
Linus Torvalds 已提交
1402
	struct sk_buff *buff;
1403
	struct rd_msg *msg;
L
Linus Torvalds 已提交
1404 1405 1406 1407
	struct in6_addr saddr_buf;
	struct rt6_info *rt;
	struct dst_entry *dst;
	struct inet6_dev *idev;
1408
	struct flowi6 fl6;
L
Linus Torvalds 已提交
1409 1410 1411
	int rd_len;
	int err;
	u8 ha_buf[MAX_ADDR_LEN], *ha = NULL;
1412
	bool ret;
L
Linus Torvalds 已提交
1413

1414
	if (ipv6_get_lladdr(dev, &saddr_buf, IFA_F_TENTATIVE)) {
1415 1416
		ND_PRINTK(2, warn, "Redirect: no link-local address on %s\n",
			  dev->name);
1417 1418
		return;
	}
L
Linus Torvalds 已提交
1419

1420
	if (!ipv6_addr_equal(&ipv6_hdr(skb)->daddr, target) &&
1421
	    ipv6_addr_type(target) != (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) {
1422 1423
		ND_PRINTK(2, warn,
			  "Redirect: target address is not link-local unicast\n");
1424 1425 1426
		return;
	}

1427
	icmpv6_flow_init(sk, &fl6, NDISC_REDIRECT,
1428
			 &saddr_buf, &ipv6_hdr(skb)->saddr, dev->ifindex);
L
Linus Torvalds 已提交
1429

1430
	dst = ip6_route_output(net, NULL, &fl6);
1431 1432
	if (dst->error) {
		dst_release(dst);
L
Linus Torvalds 已提交
1433
		return;
1434
	}
1435
	dst = xfrm_lookup(net, dst, flowi6_to_flowi(&fl6), NULL, 0);
1436
	if (IS_ERR(dst))
L
Linus Torvalds 已提交
1437 1438 1439 1440 1441
		return;

	rt = (struct rt6_info *) dst;

	if (rt->rt6i_flags & RTF_GATEWAY) {
1442 1443
		ND_PRINTK(2, warn,
			  "Redirect: destination is not a neighbour\n");
I
Ilpo Järvinen 已提交
1444
		goto release;
L
Linus Torvalds 已提交
1445
	}
1446 1447 1448 1449 1450
	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)
I
Ilpo Järvinen 已提交
1451
		goto release;
L
Linus Torvalds 已提交
1452 1453

	if (dev->addr_len) {
1454 1455
		struct neighbour *neigh = dst_neigh_lookup(skb_dst(skb), target);
		if (!neigh) {
1456 1457
			ND_PRINTK(2, warn,
				  "Redirect: no neigh for target address\n");
1458 1459 1460
			goto release;
		}

L
Linus Torvalds 已提交
1461 1462 1463 1464 1465
		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;
1466
			optlen += ndisc_opt_addr_space(dev);
L
Linus Torvalds 已提交
1467 1468
		} else
			read_unlock_bh(&neigh->lock);
1469 1470

		neigh_release(neigh);
L
Linus Torvalds 已提交
1471 1472 1473
	}

	rd_len = min_t(unsigned int,
1474 1475
		       IPV6_MIN_MTU - sizeof(struct ipv6hdr) - sizeof(*msg) - optlen,
		       skb->len + 8);
L
Linus Torvalds 已提交
1476
	rd_len &= ~0x7;
1477
	optlen += rd_len;
L
Linus Torvalds 已提交
1478

1479
	buff = ndisc_alloc_skb(dev, sizeof(*msg) + optlen);
1480
	if (!buff)
I
Ilpo Järvinen 已提交
1481
		goto release;
L
Linus Torvalds 已提交
1482

1483
	skb_put(buff, sizeof(*msg));
1484
	msg = (struct rd_msg *)icmp6_hdr(buff);
L
Linus Torvalds 已提交
1485

1486 1487
	memset(&msg->icmph, 0, sizeof(struct icmp6hdr));
	msg->icmph.icmp6_type = NDISC_REDIRECT;
L
Linus Torvalds 已提交
1488 1489 1490 1491 1492

	/*
	 *	copy target and destination addresses
	 */

1493 1494
	msg->target = *target;
	msg->dest = ipv6_hdr(skb)->daddr;
L
Linus Torvalds 已提交
1495 1496 1497 1498 1499 1500

	/*
	 *	include target_address option
	 */

	if (ha)
1501
		ndisc_fill_addr_option(skb, ND_OPT_TARGET_LL_ADDR, ha);
L
Linus Torvalds 已提交
1502 1503 1504 1505 1506

	/*
	 *	build redirect option and copy skb over to the new packet.
	 */

1507
	if (rd_len)
1508
		ndisc_fill_redirect_hdr_option(buff, skb, rd_len);
L
Linus Torvalds 已提交
1509

1510
	msg->icmph.icmp6_cksum = csum_ipv6_magic(&saddr_buf, &ipv6_hdr(skb)->saddr,
1511 1512 1513 1514 1515
						 buff->len, IPPROTO_ICMPV6,
						 csum_partial(msg, buff->len, 0));

	ip6_nd_hdr(buff, &saddr_buf, &ipv6_hdr(skb)->saddr,
		   inet6_sk(sk)->hop_limit, buff->len);
L
Linus Torvalds 已提交
1516

E
Eric Dumazet 已提交
1517
	skb_dst_set(buff, dst);
E
Eric Dumazet 已提交
1518 1519
	rcu_read_lock();
	idev = __in6_dev_get(dst->dev);
1520
	IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUT, skb->len);
1521
	err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, buff, NULL, dst->dev,
1522
		      dst_output);
L
Linus Torvalds 已提交
1523
	if (!err) {
1524
		ICMP6MSGOUT_INC_STATS(net, idev, NDISC_REDIRECT);
1525
		ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS);
L
Linus Torvalds 已提交
1526 1527
	}

E
Eric Dumazet 已提交
1528
	rcu_read_unlock();
I
Ilpo Järvinen 已提交
1529 1530 1531 1532
	return;

release:
	dst_release(dst);
L
Linus Torvalds 已提交
1533 1534 1535 1536
}

static void pndisc_redo(struct sk_buff *skb)
{
1537
	ndisc_recv_ns(skb);
L
Linus Torvalds 已提交
1538 1539 1540 1541 1542 1543 1544
	kfree_skb(skb);
}

int ndisc_rcv(struct sk_buff *skb)
{
	struct nd_msg *msg;

1545
	if (skb_linearize(skb))
L
Linus Torvalds 已提交
1546 1547
		return 0;

1548
	msg = (struct nd_msg *)skb_transport_header(skb);
L
Linus Torvalds 已提交
1549

1550
	__skb_push(skb, skb->data - skb_transport_header(skb));
L
Linus Torvalds 已提交
1551

1552
	if (ipv6_hdr(skb)->hop_limit != 255) {
1553 1554
		ND_PRINTK(2, warn, "NDISC: invalid hop-limit: %d\n",
			  ipv6_hdr(skb)->hop_limit);
L
Linus Torvalds 已提交
1555 1556 1557 1558
		return 0;
	}

	if (msg->icmph.icmp6_code != 0) {
1559 1560
		ND_PRINTK(2, warn, "NDISC: invalid ICMPv6 code: %d\n",
			  msg->icmph.icmp6_code);
L
Linus Torvalds 已提交
1561 1562 1563
		return 0;
	}

1564 1565
	memset(NEIGH_CB(skb), 0, sizeof(struct neighbour_cb));

L
Linus Torvalds 已提交
1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585
	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;
1586
	}
L
Linus Torvalds 已提交
1587 1588 1589 1590 1591 1592 1593

	return 0;
}

static int ndisc_netdev_event(struct notifier_block *this, unsigned long event, void *ptr)
{
	struct net_device *dev = ptr;
1594
	struct net *net = dev_net(dev);
1595
	struct inet6_dev *idev;
L
Linus Torvalds 已提交
1596 1597 1598 1599

	switch (event) {
	case NETDEV_CHANGEADDR:
		neigh_changeaddr(&nd_tbl, dev);
1600
		fib6_run_gc(~0UL, net);
1601 1602 1603 1604 1605 1606
		idev = in6_dev_get(dev);
		if (!idev)
			break;
		if (idev->cnf.ndisc_notify)
			ndisc_send_unsol_na(dev);
		in6_dev_put(idev);
L
Linus Torvalds 已提交
1607 1608 1609
		break;
	case NETDEV_DOWN:
		neigh_ifdown(&nd_tbl, dev);
1610
		fib6_run_gc(~0UL, net);
L
Linus Torvalds 已提交
1611
		break;
1612 1613 1614
	case NETDEV_NOTIFY_PEERS:
		ndisc_send_unsol_na(dev);
		break;
L
Linus Torvalds 已提交
1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633
	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);
1634
		pr_warn("process `%s' is using deprecated sysctl (%s) net.ipv6.neigh.%s.%s - use net.ipv6.neigh.%s.%s_ms instead\n",
L
Linus Torvalds 已提交
1635 1636 1637 1638 1639 1640 1641
			warncomm, func,
			dev_name, ctl->procname,
			dev_name, ctl->procname);
		warned++;
	}
}

1642
int ndisc_ifinfo_sysctl_change(struct ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *ppos)
L
Linus Torvalds 已提交
1643 1644 1645 1646 1647
{
	struct net_device *dev = ctl->extra1;
	struct inet6_dev *idev;
	int ret;

1648 1649
	if ((strcmp(ctl->procname, "retrans_time") == 0) ||
	    (strcmp(ctl->procname, "base_reachable_time") == 0))
L
Linus Torvalds 已提交
1650 1651
		ndisc_warn_deprecated_sysctl(ctl, "syscall", dev ? dev->name : "default");

1652
	if (strcmp(ctl->procname, "retrans_time") == 0)
1653
		ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
1654 1655

	else if (strcmp(ctl->procname, "base_reachable_time") == 0)
L
Linus Torvalds 已提交
1656
		ret = proc_dointvec_jiffies(ctl, write,
1657
					    buffer, lenp, ppos);
1658 1659

	else if ((strcmp(ctl->procname, "retrans_time_ms") == 0) ||
1660
		 (strcmp(ctl->procname, "base_reachable_time_ms") == 0))
L
Linus Torvalds 已提交
1661
		ret = proc_dointvec_ms_jiffies(ctl, write,
1662
					       buffer, lenp, ppos);
1663
	else
L
Linus Torvalds 已提交
1664 1665 1666
		ret = -1;

	if (write && ret == 0 && dev && (idev = in6_dev_get(dev)) != NULL) {
1667
		if (ctl->data == &idev->nd_parms->base_reachable_time)
L
Linus Torvalds 已提交
1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678
			idev->nd_parms->reachable_time = neigh_rand_reach_time(idev->nd_parms->base_reachable_time);
		idev->tstamp = jiffies;
		inet6_ifinfo_notify(RTM_NEWLINK, idev);
		in6_dev_put(idev);
	}
	return ret;
}


#endif

1679
static int __net_init ndisc_net_init(struct net *net)
L
Linus Torvalds 已提交
1680 1681 1682
{
	struct ipv6_pinfo *np;
	struct sock *sk;
1683
	int err;
L
Linus Torvalds 已提交
1684

1685 1686
	err = inet_ctl_sock_create(&sk, PF_INET6,
				   SOCK_RAW, IPPROTO_ICMPV6, net);
L
Linus Torvalds 已提交
1687
	if (err < 0) {
1688 1689 1690
		ND_PRINTK(0, err,
			  "NDISC: Failed to initialize the control socket (err %d)\n",
			  err);
L
Linus Torvalds 已提交
1691 1692 1693
		return err;
	}

1694
	net->ipv6.ndisc_sk = sk;
1695

L
Linus Torvalds 已提交
1696 1697 1698 1699 1700
	np = inet6_sk(sk);
	np->hop_limit = 255;
	/* Do not loopback ndisc messages */
	np->mc_loop = 0;

1701 1702 1703
	return 0;
}

1704
static void __net_exit ndisc_net_exit(struct net *net)
1705
{
1706
	inet_ctl_sock_destroy(net->ipv6.ndisc_sk);
1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720
}

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;
1721 1722 1723
	/*
	 * Initialize the neighbour table
	 */
L
Linus Torvalds 已提交
1724 1725 1726
	neigh_table_init(&nd_tbl);

#ifdef CONFIG_SYSCTL
1727
	err = neigh_sysctl_register(NULL, &nd_tbl.parms, "ipv6",
1728
				    &ndisc_ifinfo_sysctl_change);
1729 1730
	if (err)
		goto out_unregister_pernet;
L
Linus Torvalds 已提交
1731
#endif
1732 1733 1734 1735 1736
	err = register_netdevice_notifier(&ndisc_netdev_notifier);
	if (err)
		goto out_unregister_sysctl;
out:
	return err;
L
Linus Torvalds 已提交
1737

1738 1739 1740 1741 1742 1743 1744
out_unregister_sysctl:
#ifdef CONFIG_SYSCTL
	neigh_sysctl_unregister(&nd_tbl.parms);
out_unregister_pernet:
#endif
	unregister_pernet_subsys(&ndisc_net_ops);
	goto out;
L
Linus Torvalds 已提交
1745 1746 1747 1748
}

void ndisc_cleanup(void)
{
1749
	unregister_netdevice_notifier(&ndisc_netdev_notifier);
L
Linus Torvalds 已提交
1750 1751 1752 1753
#ifdef CONFIG_SYSCTL
	neigh_sysctl_unregister(&nd_tbl.parms);
#endif
	neigh_table_clear(&nd_tbl);
1754
	unregister_pernet_subsys(&ndisc_net_ops);
L
Linus Torvalds 已提交
1755
}