xref: /openbmc/linux/net/ipv6/ndisc.c (revision fd5e9fccbd504c5179ab57ff695c610bca8809d6)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  *	Neighbour Discovery for IPv6
41da177e4SLinus Torvalds  *	Linux INET6 implementation
51da177e4SLinus Torvalds  *
61da177e4SLinus Torvalds  *	Authors:
71da177e4SLinus Torvalds  *	Pedro Roque		<roque@di.fc.ul.pt>
81da177e4SLinus Torvalds  *	Mike Shaver		<shaver@ingenia.com>
91da177e4SLinus Torvalds  */
101da177e4SLinus Torvalds 
111da177e4SLinus Torvalds /*
121da177e4SLinus Torvalds  *	Changes:
131da177e4SLinus Torvalds  *
14e35f30c1SAlexey I. Froloff  *	Alexey I. Froloff		:	RFC6106 (DNSSL) support
1531910575SPierre Ynard  *	Pierre Ynard			:	export userland ND options
1631910575SPierre Ynard  *						through netlink (RDNSS support)
171da177e4SLinus Torvalds  *	Lars Fenneberg			:	fixed MTU setting on receipt
181da177e4SLinus Torvalds  *						of an RA.
191da177e4SLinus Torvalds  *	Janos Farkas			:	kmalloc failure checks
201da177e4SLinus Torvalds  *	Alexey Kuznetsov		:	state machine reworked
211da177e4SLinus Torvalds  *						and moved to net/core.
221da177e4SLinus Torvalds  *	Pekka Savola			:	RFC2461 validation
231da177e4SLinus Torvalds  *	YOSHIFUJI Hideaki @USAGI	:	Verify ND options properly
241da177e4SLinus Torvalds  */
251da177e4SLinus Torvalds 
26675418d5SJoe Perches #define pr_fmt(fmt) "ICMPv6: " fmt
271da177e4SLinus Torvalds 
281da177e4SLinus Torvalds #include <linux/module.h>
291da177e4SLinus Torvalds #include <linux/errno.h>
301da177e4SLinus Torvalds #include <linux/types.h>
311da177e4SLinus Torvalds #include <linux/socket.h>
321da177e4SLinus Torvalds #include <linux/sockios.h>
331da177e4SLinus Torvalds #include <linux/sched.h>
341da177e4SLinus Torvalds #include <linux/net.h>
351da177e4SLinus Torvalds #include <linux/in6.h>
361da177e4SLinus Torvalds #include <linux/route.h>
371da177e4SLinus Torvalds #include <linux/init.h>
381da177e4SLinus Torvalds #include <linux/rcupdate.h>
395a0e3ad6STejun Heo #include <linux/slab.h>
401da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
411da177e4SLinus Torvalds #include <linux/sysctl.h>
421da177e4SLinus Torvalds #endif
431da177e4SLinus Torvalds 
441823730fSThomas Graf #include <linux/if_addr.h>
45da13c59bSVishwanath Pai #include <linux/if_ether.h>
461da177e4SLinus Torvalds #include <linux/if_arp.h>
471da177e4SLinus Torvalds #include <linux/ipv6.h>
481da177e4SLinus Torvalds #include <linux/icmpv6.h>
491da177e4SLinus Torvalds #include <linux/jhash.h>
501da177e4SLinus Torvalds 
511da177e4SLinus Torvalds #include <net/sock.h>
521da177e4SLinus Torvalds #include <net/snmp.h>
531da177e4SLinus Torvalds 
541da177e4SLinus Torvalds #include <net/ipv6.h>
551da177e4SLinus Torvalds #include <net/protocol.h>
561da177e4SLinus Torvalds #include <net/ndisc.h>
571da177e4SLinus Torvalds #include <net/ip6_route.h>
581da177e4SLinus Torvalds #include <net/addrconf.h>
591da177e4SLinus Torvalds #include <net/icmp.h>
601da177e4SLinus Torvalds 
6131910575SPierre Ynard #include <net/netlink.h>
6231910575SPierre Ynard #include <linux/rtnetlink.h>
6331910575SPierre Ynard 
641da177e4SLinus Torvalds #include <net/flow.h>
651da177e4SLinus Torvalds #include <net/ip6_checksum.h>
661ed8516fSDenis V. Lunev #include <net/inet_common.h>
671da177e4SLinus Torvalds #include <linux/proc_fs.h>
681da177e4SLinus Torvalds 
691da177e4SLinus Torvalds #include <linux/netfilter.h>
701da177e4SLinus Torvalds #include <linux/netfilter_ipv6.h>
711da177e4SLinus Torvalds 
72d6bf7817SEric Dumazet static u32 ndisc_hash(const void *pkey,
73d6bf7817SEric Dumazet 		      const struct net_device *dev,
742c2aba6cSDavid S. Miller 		      __u32 *hash_rnd);
7560395a20SEric W. Biederman static bool ndisc_key_eq(const struct neighbour *neigh, const void *pkey);
76b8fb1ab4SDavid Ahern static bool ndisc_allow_add(const struct net_device *dev,
77b8fb1ab4SDavid Ahern 			    struct netlink_ext_ack *extack);
781da177e4SLinus Torvalds static int ndisc_constructor(struct neighbour *neigh);
791da177e4SLinus Torvalds static void ndisc_solicit(struct neighbour *neigh, struct sk_buff *skb);
801da177e4SLinus Torvalds static void ndisc_error_report(struct neighbour *neigh, struct sk_buff *skb);
811da177e4SLinus Torvalds static int pndisc_constructor(struct pneigh_entry *n);
821da177e4SLinus Torvalds static void pndisc_destructor(struct pneigh_entry *n);
831da177e4SLinus Torvalds static void pndisc_redo(struct sk_buff *skb);
848cf8821eSJeff Dike static int ndisc_is_multicast(const void *pkey);
851da177e4SLinus Torvalds 
8689d69d2bSStephen Hemminger static const struct neigh_ops ndisc_generic_ops = {
871da177e4SLinus Torvalds 	.family =		AF_INET6,
881da177e4SLinus Torvalds 	.solicit =		ndisc_solicit,
891da177e4SLinus Torvalds 	.error_report =		ndisc_error_report,
901da177e4SLinus Torvalds 	.output =		neigh_resolve_output,
911da177e4SLinus Torvalds 	.connected_output =	neigh_connected_output,
921da177e4SLinus Torvalds };
931da177e4SLinus Torvalds 
9489d69d2bSStephen Hemminger static const struct neigh_ops ndisc_hh_ops = {
951da177e4SLinus Torvalds 	.family =		AF_INET6,
961da177e4SLinus Torvalds 	.solicit =		ndisc_solicit,
971da177e4SLinus Torvalds 	.error_report =		ndisc_error_report,
981da177e4SLinus Torvalds 	.output =		neigh_resolve_output,
991da177e4SLinus Torvalds 	.connected_output =	neigh_resolve_output,
1001da177e4SLinus Torvalds };
1011da177e4SLinus Torvalds 
1021da177e4SLinus Torvalds 
10389d69d2bSStephen Hemminger static const struct neigh_ops ndisc_direct_ops = {
1041da177e4SLinus Torvalds 	.family =		AF_INET6,
1058f40b161SDavid S. Miller 	.output =		neigh_direct_output,
1068f40b161SDavid S. Miller 	.connected_output =	neigh_direct_output,
1071da177e4SLinus Torvalds };
1081da177e4SLinus Torvalds 
1091da177e4SLinus Torvalds struct neigh_table nd_tbl = {
1101da177e4SLinus Torvalds 	.family =	AF_INET6,
1111da177e4SLinus Torvalds 	.key_len =	sizeof(struct in6_addr),
112bdf53c58SEric W. Biederman 	.protocol =	cpu_to_be16(ETH_P_IPV6),
1131da177e4SLinus Torvalds 	.hash =		ndisc_hash,
11460395a20SEric W. Biederman 	.key_eq =	ndisc_key_eq,
1151da177e4SLinus Torvalds 	.constructor =	ndisc_constructor,
1161da177e4SLinus Torvalds 	.pconstructor =	pndisc_constructor,
1171da177e4SLinus Torvalds 	.pdestructor =	pndisc_destructor,
1181da177e4SLinus Torvalds 	.proxy_redo =	pndisc_redo,
1198cf8821eSJeff Dike 	.is_multicast =	ndisc_is_multicast,
120b8fb1ab4SDavid Ahern 	.allow_add  =   ndisc_allow_add,
1211da177e4SLinus Torvalds 	.id =		"ndisc_cache",
1221da177e4SLinus Torvalds 	.parms = {
1231da177e4SLinus Torvalds 		.tbl			= &nd_tbl,
124b672083eSShan Wei 		.reachable_time		= ND_REACHABLE_TIME,
1251f9248e5SJiri Pirko 		.data = {
1261f9248e5SJiri Pirko 			[NEIGH_VAR_MCAST_PROBES] = 3,
1271f9248e5SJiri Pirko 			[NEIGH_VAR_UCAST_PROBES] = 3,
1281f9248e5SJiri Pirko 			[NEIGH_VAR_RETRANS_TIME] = ND_RETRANS_TIMER,
1291f9248e5SJiri Pirko 			[NEIGH_VAR_BASE_REACHABLE_TIME] = ND_REACHABLE_TIME,
1301f9248e5SJiri Pirko 			[NEIGH_VAR_DELAY_PROBE_TIME] = 5 * HZ,
131211da42eSYuwei Wang 			[NEIGH_VAR_INTERVAL_PROBE_TIME_MS] = 5 * HZ,
1321f9248e5SJiri Pirko 			[NEIGH_VAR_GC_STALETIME] = 60 * HZ,
133eaa72dc4SEric Dumazet 			[NEIGH_VAR_QUEUE_LEN_BYTES] = SK_WMEM_MAX,
1341f9248e5SJiri Pirko 			[NEIGH_VAR_PROXY_QLEN] = 64,
1351f9248e5SJiri Pirko 			[NEIGH_VAR_ANYCAST_DELAY] = 1 * HZ,
1361f9248e5SJiri Pirko 			[NEIGH_VAR_PROXY_DELAY] = (8 * HZ) / 10,
1371f9248e5SJiri Pirko 		},
1381da177e4SLinus Torvalds 	},
1391da177e4SLinus Torvalds 	.gc_interval =	  30 * HZ,
1401da177e4SLinus Torvalds 	.gc_thresh1 =	 128,
1411da177e4SLinus Torvalds 	.gc_thresh2 =	 512,
1421da177e4SLinus Torvalds 	.gc_thresh3 =	1024,
1431da177e4SLinus Torvalds };
144c4850687SDavid Ahern EXPORT_SYMBOL_GPL(nd_tbl);
1451da177e4SLinus Torvalds 
__ndisc_fill_addr_option(struct sk_buff * skb,int type,const void * data,int data_len,int pad)1461a8a23d2SJakub Kicinski void __ndisc_fill_addr_option(struct sk_buff *skb, int type, const void *data,
1478ec5da41SAlexander Aring 			      int data_len, int pad)
1481da177e4SLinus Torvalds {
1498ec5da41SAlexander Aring 	int space = __ndisc_opt_addr_space(data_len, pad);
1505f5a0115SYOSHIFUJI Hideaki / 吉藤英明 	u8 *opt = skb_put(skb, space);
1511da177e4SLinus Torvalds 
1521da177e4SLinus Torvalds 	opt[0] = type;
1531da177e4SLinus Torvalds 	opt[1] = space>>3;
1541da177e4SLinus Torvalds 
1551da177e4SLinus Torvalds 	memset(opt + 2, 0, pad);
1561da177e4SLinus Torvalds 	opt   += pad;
1571da177e4SLinus Torvalds 	space -= pad;
1581da177e4SLinus Torvalds 
1591da177e4SLinus Torvalds 	memcpy(opt+2, data, data_len);
1601da177e4SLinus Torvalds 	data_len += 2;
1611da177e4SLinus Torvalds 	opt += data_len;
162e5d08d71SIan Morris 	space -= data_len;
163e5d08d71SIan Morris 	if (space > 0)
1641da177e4SLinus Torvalds 		memset(opt, 0, space);
1651da177e4SLinus Torvalds }
166cc84b3c6SAlexander Aring EXPORT_SYMBOL_GPL(__ndisc_fill_addr_option);
1671da177e4SLinus Torvalds 
ndisc_fill_addr_option(struct sk_buff * skb,int type,const void * data,u8 icmp6_type)1688ec5da41SAlexander Aring static inline void ndisc_fill_addr_option(struct sk_buff *skb, int type,
1691a8a23d2SJakub Kicinski 					  const void *data, u8 icmp6_type)
1708ec5da41SAlexander Aring {
1718ec5da41SAlexander Aring 	__ndisc_fill_addr_option(skb, type, data, skb->dev->addr_len,
1728ec5da41SAlexander Aring 				 ndisc_addr_option_pad(skb->dev->type));
173f997c55cSAlexander Aring 	ndisc_ops_fill_addr_option(skb->dev, skb, icmp6_type);
174f997c55cSAlexander Aring }
175f997c55cSAlexander Aring 
ndisc_fill_redirect_addr_option(struct sk_buff * skb,void * ha,const u8 * ops_data)176f997c55cSAlexander Aring static inline void ndisc_fill_redirect_addr_option(struct sk_buff *skb,
177f997c55cSAlexander Aring 						   void *ha,
178f997c55cSAlexander Aring 						   const u8 *ops_data)
179f997c55cSAlexander Aring {
180f997c55cSAlexander Aring 	ndisc_fill_addr_option(skb, ND_OPT_TARGET_LL_ADDR, ha, NDISC_REDIRECT);
181f997c55cSAlexander Aring 	ndisc_ops_fill_redirect_addr_option(skb->dev, skb, ops_data);
1828ec5da41SAlexander Aring }
1838ec5da41SAlexander Aring 
ndisc_next_option(struct nd_opt_hdr * cur,struct nd_opt_hdr * end)1841da177e4SLinus Torvalds static struct nd_opt_hdr *ndisc_next_option(struct nd_opt_hdr *cur,
1851da177e4SLinus Torvalds 					    struct nd_opt_hdr *end)
1861da177e4SLinus Torvalds {
1871da177e4SLinus Torvalds 	int type;
1881da177e4SLinus Torvalds 	if (!cur || !end || cur >= end)
1891da177e4SLinus Torvalds 		return NULL;
1901da177e4SLinus Torvalds 	type = cur->nd_opt_type;
1911da177e4SLinus Torvalds 	do {
1921da177e4SLinus Torvalds 		cur = ((void *)cur) + (cur->nd_opt_len << 3);
1931da177e4SLinus Torvalds 	} while (cur < end && cur->nd_opt_type != type);
194a02cec21SEric Dumazet 	return cur <= end && cur->nd_opt_type == type ? cur : NULL;
1951da177e4SLinus Torvalds }
1961da177e4SLinus Torvalds 
ndisc_is_useropt(const struct net_device * dev,struct nd_opt_hdr * opt)197f997c55cSAlexander Aring static inline int ndisc_is_useropt(const struct net_device *dev,
198f997c55cSAlexander Aring 				   struct nd_opt_hdr *opt)
19931910575SPierre Ynard {
200048c796bSMaciej Żenczykowski 	return opt->nd_opt_type == ND_OPT_PREFIX_INFO ||
201048c796bSMaciej Żenczykowski 		opt->nd_opt_type == ND_OPT_RDNSS ||
202f997c55cSAlexander Aring 		opt->nd_opt_type == ND_OPT_DNSSL ||
20366b5f1c4SMaciej Żenczykowski 		opt->nd_opt_type == ND_OPT_CAPTIVE_PORTAL ||
204c24a77edSMaciej Żenczykowski 		opt->nd_opt_type == ND_OPT_PREF64 ||
205f997c55cSAlexander Aring 		ndisc_ops_is_useropt(dev, opt->nd_opt_type);
20631910575SPierre Ynard }
20731910575SPierre Ynard 
ndisc_next_useropt(const struct net_device * dev,struct nd_opt_hdr * cur,struct nd_opt_hdr * end)208f997c55cSAlexander Aring static struct nd_opt_hdr *ndisc_next_useropt(const struct net_device *dev,
209f997c55cSAlexander Aring 					     struct nd_opt_hdr *cur,
21031910575SPierre Ynard 					     struct nd_opt_hdr *end)
21131910575SPierre Ynard {
21231910575SPierre Ynard 	if (!cur || !end || cur >= end)
21331910575SPierre Ynard 		return NULL;
21431910575SPierre Ynard 	do {
21531910575SPierre Ynard 		cur = ((void *)cur) + (cur->nd_opt_len << 3);
216f997c55cSAlexander Aring 	} while (cur < end && !ndisc_is_useropt(dev, cur));
217f997c55cSAlexander Aring 	return cur <= end && ndisc_is_useropt(dev, cur) ? cur : NULL;
21831910575SPierre Ynard }
21931910575SPierre Ynard 
ndisc_parse_options(const struct net_device * dev,u8 * opt,int opt_len,struct ndisc_options * ndopts)220f997c55cSAlexander Aring struct ndisc_options *ndisc_parse_options(const struct net_device *dev,
221f997c55cSAlexander Aring 					  u8 *opt, int opt_len,
2221da177e4SLinus Torvalds 					  struct ndisc_options *ndopts)
2231da177e4SLinus Torvalds {
2241da177e4SLinus Torvalds 	struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)opt;
2251da177e4SLinus Torvalds 
2261da177e4SLinus Torvalds 	if (!nd_opt || opt_len < 0 || !ndopts)
2271da177e4SLinus Torvalds 		return NULL;
2281da177e4SLinus Torvalds 	memset(ndopts, 0, sizeof(*ndopts));
2291da177e4SLinus Torvalds 	while (opt_len) {
23093e2beaeSMaciej Żenczykowski 		bool unknown = false;
2311da177e4SLinus Torvalds 		int l;
2321da177e4SLinus Torvalds 		if (opt_len < sizeof(struct nd_opt_hdr))
2331da177e4SLinus Torvalds 			return NULL;
2341da177e4SLinus Torvalds 		l = nd_opt->nd_opt_len << 3;
2351da177e4SLinus Torvalds 		if (opt_len < l || l == 0)
2361da177e4SLinus Torvalds 			return NULL;
237f997c55cSAlexander Aring 		if (ndisc_ops_parse_options(dev, nd_opt, ndopts))
238f997c55cSAlexander Aring 			goto next_opt;
2391da177e4SLinus Torvalds 		switch (nd_opt->nd_opt_type) {
2401da177e4SLinus Torvalds 		case ND_OPT_SOURCE_LL_ADDR:
2411da177e4SLinus Torvalds 		case ND_OPT_TARGET_LL_ADDR:
2421da177e4SLinus Torvalds 		case ND_OPT_MTU:
243adc176c5SErik Nordmark 		case ND_OPT_NONCE:
2441da177e4SLinus Torvalds 		case ND_OPT_REDIRECT_HDR:
2451da177e4SLinus Torvalds 			if (ndopts->nd_opt_array[nd_opt->nd_opt_type]) {
246675418d5SJoe Perches 				ND_PRINTK(2, warn,
247f3213831SJoe Perches 					  "%s: duplicated ND6 option found: type=%d\n",
248675418d5SJoe Perches 					  __func__, nd_opt->nd_opt_type);
2491da177e4SLinus Torvalds 			} else {
2501da177e4SLinus Torvalds 				ndopts->nd_opt_array[nd_opt->nd_opt_type] = nd_opt;
2511da177e4SLinus Torvalds 			}
2521da177e4SLinus Torvalds 			break;
2531da177e4SLinus Torvalds 		case ND_OPT_PREFIX_INFO:
2541da177e4SLinus Torvalds 			ndopts->nd_opts_pi_end = nd_opt;
255cfcabdccSStephen Hemminger 			if (!ndopts->nd_opt_array[nd_opt->nd_opt_type])
2561da177e4SLinus Torvalds 				ndopts->nd_opt_array[nd_opt->nd_opt_type] = nd_opt;
2571da177e4SLinus Torvalds 			break;
25870ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO
25970ceb4f5SYOSHIFUJI Hideaki 		case ND_OPT_ROUTE_INFO:
26070ceb4f5SYOSHIFUJI Hideaki 			ndopts->nd_opts_ri_end = nd_opt;
26170ceb4f5SYOSHIFUJI Hideaki 			if (!ndopts->nd_opts_ri)
26270ceb4f5SYOSHIFUJI Hideaki 				ndopts->nd_opts_ri = nd_opt;
26370ceb4f5SYOSHIFUJI Hideaki 			break;
26470ceb4f5SYOSHIFUJI Hideaki #endif
2651da177e4SLinus Torvalds 		default:
26693e2beaeSMaciej Żenczykowski 			unknown = true;
26793e2beaeSMaciej Żenczykowski 		}
268f997c55cSAlexander Aring 		if (ndisc_is_useropt(dev, nd_opt)) {
26931910575SPierre Ynard 			ndopts->nd_useropts_end = nd_opt;
27031910575SPierre Ynard 			if (!ndopts->nd_useropts)
27131910575SPierre Ynard 				ndopts->nd_useropts = nd_opt;
27293e2beaeSMaciej Żenczykowski 		} else if (unknown) {
2731da177e4SLinus Torvalds 			/*
2741da177e4SLinus Torvalds 			 * Unknown options must be silently ignored,
27531910575SPierre Ynard 			 * to accommodate future extension to the
27631910575SPierre Ynard 			 * protocol.
2771da177e4SLinus Torvalds 			 */
278675418d5SJoe Perches 			ND_PRINTK(2, notice,
279f3213831SJoe Perches 				  "%s: ignored unsupported option; type=%d, len=%d\n",
2800dc47877SHarvey Harrison 				  __func__,
281675418d5SJoe Perches 				  nd_opt->nd_opt_type,
282675418d5SJoe Perches 				  nd_opt->nd_opt_len);
2831da177e4SLinus Torvalds 		}
284f997c55cSAlexander Aring next_opt:
2851da177e4SLinus Torvalds 		opt_len -= l;
2861da177e4SLinus Torvalds 		nd_opt = ((void *)nd_opt) + l;
2871da177e4SLinus Torvalds 	}
2881da177e4SLinus Torvalds 	return ndopts;
2891da177e4SLinus Torvalds }
2901da177e4SLinus Torvalds 
ndisc_mc_map(const struct in6_addr * addr,char * buf,struct net_device * dev,int dir)291b71d1d42SEric Dumazet int ndisc_mc_map(const struct in6_addr *addr, char *buf, struct net_device *dev, int dir)
2921da177e4SLinus Torvalds {
2931da177e4SLinus Torvalds 	switch (dev->type) {
2941da177e4SLinus Torvalds 	case ARPHRD_ETHER:
2951da177e4SLinus Torvalds 	case ARPHRD_IEEE802:	/* Not sure. Check it later. --ANK */
2961da177e4SLinus Torvalds 	case ARPHRD_FDDI:
2971da177e4SLinus Torvalds 		ipv6_eth_mc_map(addr, buf);
2981da177e4SLinus Torvalds 		return 0;
2991da177e4SLinus Torvalds 	case ARPHRD_ARCNET:
3001da177e4SLinus Torvalds 		ipv6_arcnet_mc_map(addr, buf);
3011da177e4SLinus Torvalds 		return 0;
3021da177e4SLinus Torvalds 	case ARPHRD_INFINIBAND:
303a9e527e3SRolf Manderscheid 		ipv6_ib_mc_map(addr, dev->broadcast, buf);
3041da177e4SLinus Torvalds 		return 0;
30593ca3bb5STimo Teräs 	case ARPHRD_IPGRE:
30693ca3bb5STimo Teräs 		return ipv6_ipgre_mc_map(addr, dev->broadcast, buf);
3071da177e4SLinus Torvalds 	default:
3081da177e4SLinus Torvalds 		if (dir) {
3091da177e4SLinus Torvalds 			memcpy(buf, dev->broadcast, dev->addr_len);
3101da177e4SLinus Torvalds 			return 0;
3111da177e4SLinus Torvalds 		}
3121da177e4SLinus Torvalds 	}
3131da177e4SLinus Torvalds 	return -EINVAL;
3141da177e4SLinus Torvalds }
3157159039aSYOSHIFUJI Hideaki EXPORT_SYMBOL(ndisc_mc_map);
3167159039aSYOSHIFUJI Hideaki 
ndisc_hash(const void * pkey,const struct net_device * dev,__u32 * hash_rnd)317d6bf7817SEric Dumazet static u32 ndisc_hash(const void *pkey,
318d6bf7817SEric Dumazet 		      const struct net_device *dev,
3192c2aba6cSDavid S. Miller 		      __u32 *hash_rnd)
3201da177e4SLinus Torvalds {
3212c2aba6cSDavid S. Miller 	return ndisc_hashfn(pkey, dev, hash_rnd);
3221da177e4SLinus Torvalds }
3231da177e4SLinus Torvalds 
ndisc_key_eq(const struct neighbour * n,const void * pkey)32460395a20SEric W. Biederman static bool ndisc_key_eq(const struct neighbour *n, const void *pkey)
32560395a20SEric W. Biederman {
32660395a20SEric W. Biederman 	return neigh_key_eq128(n, pkey);
32760395a20SEric W. Biederman }
32860395a20SEric W. Biederman 
ndisc_constructor(struct neighbour * neigh)3291da177e4SLinus Torvalds static int ndisc_constructor(struct neighbour *neigh)
3301da177e4SLinus Torvalds {
3311da177e4SLinus Torvalds 	struct in6_addr *addr = (struct in6_addr *)&neigh->primary_key;
3321da177e4SLinus Torvalds 	struct net_device *dev = neigh->dev;
3331da177e4SLinus Torvalds 	struct inet6_dev *in6_dev;
3341da177e4SLinus Torvalds 	struct neigh_parms *parms;
335a50feda5SEric Dumazet 	bool is_multicast = ipv6_addr_is_multicast(addr);
3361da177e4SLinus Torvalds 
3371da177e4SLinus Torvalds 	in6_dev = in6_dev_get(dev);
33863159f29SIan Morris 	if (!in6_dev) {
3391da177e4SLinus Torvalds 		return -EINVAL;
3401da177e4SLinus Torvalds 	}
3411da177e4SLinus Torvalds 
3421da177e4SLinus Torvalds 	parms = in6_dev->nd_parms;
3431da177e4SLinus Torvalds 	__neigh_parms_put(neigh->parms);
3441da177e4SLinus Torvalds 	neigh->parms = neigh_parms_clone(parms);
3451da177e4SLinus Torvalds 
3461da177e4SLinus Torvalds 	neigh->type = is_multicast ? RTN_MULTICAST : RTN_UNICAST;
3473b04dddeSStephen Hemminger 	if (!dev->header_ops) {
3481da177e4SLinus Torvalds 		neigh->nud_state = NUD_NOARP;
3491da177e4SLinus Torvalds 		neigh->ops = &ndisc_direct_ops;
3508f40b161SDavid S. Miller 		neigh->output = neigh_direct_output;
3511da177e4SLinus Torvalds 	} else {
3521da177e4SLinus Torvalds 		if (is_multicast) {
3531da177e4SLinus Torvalds 			neigh->nud_state = NUD_NOARP;
3541da177e4SLinus Torvalds 			ndisc_mc_map(addr, neigh->ha, dev, 1);
3551da177e4SLinus Torvalds 		} else if (dev->flags&(IFF_NOARP|IFF_LOOPBACK)) {
3561da177e4SLinus Torvalds 			neigh->nud_state = NUD_NOARP;
3571da177e4SLinus Torvalds 			memcpy(neigh->ha, dev->dev_addr, dev->addr_len);
3581da177e4SLinus Torvalds 			if (dev->flags&IFF_LOOPBACK)
3591da177e4SLinus Torvalds 				neigh->type = RTN_LOCAL;
3601da177e4SLinus Torvalds 		} else if (dev->flags&IFF_POINTOPOINT) {
3611da177e4SLinus Torvalds 			neigh->nud_state = NUD_NOARP;
3621da177e4SLinus Torvalds 			memcpy(neigh->ha, dev->broadcast, dev->addr_len);
3631da177e4SLinus Torvalds 		}
3643b04dddeSStephen Hemminger 		if (dev->header_ops->cache)
3651da177e4SLinus Torvalds 			neigh->ops = &ndisc_hh_ops;
3661da177e4SLinus Torvalds 		else
3671da177e4SLinus Torvalds 			neigh->ops = &ndisc_generic_ops;
3681da177e4SLinus Torvalds 		if (neigh->nud_state&NUD_VALID)
3691da177e4SLinus Torvalds 			neigh->output = neigh->ops->connected_output;
3701da177e4SLinus Torvalds 		else
3711da177e4SLinus Torvalds 			neigh->output = neigh->ops->output;
3721da177e4SLinus Torvalds 	}
3731da177e4SLinus Torvalds 	in6_dev_put(in6_dev);
3741da177e4SLinus Torvalds 	return 0;
3751da177e4SLinus Torvalds }
3761da177e4SLinus Torvalds 
pndisc_constructor(struct pneigh_entry * n)3771da177e4SLinus Torvalds static int pndisc_constructor(struct pneigh_entry *n)
3781da177e4SLinus Torvalds {
3791da177e4SLinus Torvalds 	struct in6_addr *addr = (struct in6_addr *)&n->key;
3801da177e4SLinus Torvalds 	struct in6_addr maddr;
3811da177e4SLinus Torvalds 	struct net_device *dev = n->dev;
3821da177e4SLinus Torvalds 
38363159f29SIan Morris 	if (!dev || !__in6_dev_get(dev))
3841da177e4SLinus Torvalds 		return -EINVAL;
3851da177e4SLinus Torvalds 	addrconf_addr_solict_mult(addr, &maddr);
3861da177e4SLinus Torvalds 	ipv6_dev_mc_inc(dev, &maddr);
3871da177e4SLinus Torvalds 	return 0;
3881da177e4SLinus Torvalds }
3891da177e4SLinus Torvalds 
pndisc_destructor(struct pneigh_entry * n)3901da177e4SLinus Torvalds static void pndisc_destructor(struct pneigh_entry *n)
3911da177e4SLinus Torvalds {
3921da177e4SLinus Torvalds 	struct in6_addr *addr = (struct in6_addr *)&n->key;
3931da177e4SLinus Torvalds 	struct in6_addr maddr;
3941da177e4SLinus Torvalds 	struct net_device *dev = n->dev;
3951da177e4SLinus Torvalds 
39663159f29SIan Morris 	if (!dev || !__in6_dev_get(dev))
3971da177e4SLinus Torvalds 		return;
3981da177e4SLinus Torvalds 	addrconf_addr_solict_mult(addr, &maddr);
3991da177e4SLinus Torvalds 	ipv6_dev_mc_dec(dev, &maddr);
4001da177e4SLinus Torvalds }
4011da177e4SLinus Torvalds 
402b8fb1ab4SDavid Ahern /* called with rtnl held */
ndisc_allow_add(const struct net_device * dev,struct netlink_ext_ack * extack)403b8fb1ab4SDavid Ahern static bool ndisc_allow_add(const struct net_device *dev,
404b8fb1ab4SDavid Ahern 			    struct netlink_ext_ack *extack)
405b8fb1ab4SDavid Ahern {
406b8fb1ab4SDavid Ahern 	struct inet6_dev *idev = __in6_dev_get(dev);
407b8fb1ab4SDavid Ahern 
408b8fb1ab4SDavid Ahern 	if (!idev || idev->cnf.disable_ipv6) {
409b8fb1ab4SDavid Ahern 		NL_SET_ERR_MSG(extack, "IPv6 is disabled on this device");
410b8fb1ab4SDavid Ahern 		return false;
411b8fb1ab4SDavid Ahern 	}
412b8fb1ab4SDavid Ahern 
413b8fb1ab4SDavid Ahern 	return true;
414b8fb1ab4SDavid Ahern }
415b8fb1ab4SDavid Ahern 
ndisc_alloc_skb(struct net_device * dev,int len)416de09334bSYOSHIFUJI Hideaki / 吉藤英明 static struct sk_buff *ndisc_alloc_skb(struct net_device *dev,
417de09334bSYOSHIFUJI Hideaki / 吉藤英明 				       int len)
418de09334bSYOSHIFUJI Hideaki / 吉藤英明 {
419de09334bSYOSHIFUJI Hideaki / 吉藤英明 	int hlen = LL_RESERVED_SPACE(dev);
420de09334bSYOSHIFUJI Hideaki / 吉藤英明 	int tlen = dev->needed_tailroom;
421de09334bSYOSHIFUJI Hideaki / 吉藤英明 	struct sk_buff *skb;
422de09334bSYOSHIFUJI Hideaki / 吉藤英明 
42325a6e6b8SThomas Graf 	skb = alloc_skb(hlen + sizeof(struct ipv6hdr) + len + tlen, GFP_ATOMIC);
4249e0ec817SEric Dumazet 	if (!skb)
425de09334bSYOSHIFUJI Hideaki / 吉藤英明 		return NULL;
426de09334bSYOSHIFUJI Hideaki / 吉藤英明 
427f382d03aSYOSHIFUJI Hideaki / 吉藤英明 	skb->protocol = htons(ETH_P_IPV6);
428f382d03aSYOSHIFUJI Hideaki / 吉藤英明 	skb->dev = dev;
429f382d03aSYOSHIFUJI Hideaki / 吉藤英明 
430527a150fSYOSHIFUJI Hideaki / 吉藤英明 	skb_reserve(skb, hlen + sizeof(struct ipv6hdr));
4315135e633SYOSHIFUJI Hideaki / 吉藤英明 	skb_reset_transport_header(skb);
432de09334bSYOSHIFUJI Hideaki / 吉藤英明 
43325a6e6b8SThomas Graf 	/* Manually assign socket ownership as we avoid calling
43425a6e6b8SThomas Graf 	 * sock_alloc_send_pskb() to bypass wmem buffer limits
43525a6e6b8SThomas Graf 	 */
4369e0ec817SEric Dumazet 	rcu_read_lock();
4379e0ec817SEric Dumazet 	skb_set_owner_w(skb, dev_net_rcu(dev)->ipv6.ndisc_sk);
4389e0ec817SEric Dumazet 	rcu_read_unlock();
43925a6e6b8SThomas Graf 
440de09334bSYOSHIFUJI Hideaki / 吉藤英明 	return skb;
441de09334bSYOSHIFUJI Hideaki / 吉藤英明 }
442de09334bSYOSHIFUJI Hideaki / 吉藤英明 
ip6_nd_hdr(struct sk_buff * skb,const struct in6_addr * saddr,const struct in6_addr * daddr,int hop_limit,int len)443f382d03aSYOSHIFUJI Hideaki / 吉藤英明 static void ip6_nd_hdr(struct sk_buff *skb,
4442576f17dSYOSHIFUJI Hideaki / 吉藤英明 		       const struct in6_addr *saddr,
4452576f17dSYOSHIFUJI Hideaki / 吉藤英明 		       const struct in6_addr *daddr,
446c8d6c380SYOSHIFUJI Hideaki / 吉藤英明 		       int hop_limit, int len)
4472576f17dSYOSHIFUJI Hideaki / 吉藤英明 {
4482576f17dSYOSHIFUJI Hideaki / 吉藤英明 	struct ipv6hdr *hdr;
4492210d6b2SMaciej Żenczykowski 	struct inet6_dev *idev;
4502210d6b2SMaciej Żenczykowski 	unsigned tclass;
4512210d6b2SMaciej Żenczykowski 
4522210d6b2SMaciej Żenczykowski 	rcu_read_lock();
4532210d6b2SMaciej Żenczykowski 	idev = __in6_dev_get(skb->dev);
4542210d6b2SMaciej Żenczykowski 	tclass = idev ? idev->cnf.ndisc_tclass : 0;
4552210d6b2SMaciej Żenczykowski 	rcu_read_unlock();
4562576f17dSYOSHIFUJI Hideaki / 吉藤英明 
457527a150fSYOSHIFUJI Hideaki / 吉藤英明 	skb_push(skb, sizeof(*hdr));
4582576f17dSYOSHIFUJI Hideaki / 吉藤英明 	skb_reset_network_header(skb);
4592576f17dSYOSHIFUJI Hideaki / 吉藤英明 	hdr = ipv6_hdr(skb);
4602576f17dSYOSHIFUJI Hideaki / 吉藤英明 
4612210d6b2SMaciej Żenczykowski 	ip6_flow_hdr(hdr, tclass, 0);
4622576f17dSYOSHIFUJI Hideaki / 吉藤英明 
4632576f17dSYOSHIFUJI Hideaki / 吉藤英明 	hdr->payload_len = htons(len);
464c8d6c380SYOSHIFUJI Hideaki / 吉藤英明 	hdr->nexthdr = IPPROTO_ICMPV6;
465c8d6c380SYOSHIFUJI Hideaki / 吉藤英明 	hdr->hop_limit = hop_limit;
4662576f17dSYOSHIFUJI Hideaki / 吉藤英明 
4672576f17dSYOSHIFUJI Hideaki / 吉藤英明 	hdr->saddr = *saddr;
4682576f17dSYOSHIFUJI Hideaki / 吉藤英明 	hdr->daddr = *daddr;
4692576f17dSYOSHIFUJI Hideaki / 吉藤英明 }
4702576f17dSYOSHIFUJI Hideaki / 吉藤英明 
ndisc_send_skb(struct sk_buff * skb,const struct in6_addr * daddr,const struct in6_addr * saddr)471696c6544SHangbin Liu void ndisc_send_skb(struct sk_buff *skb, const struct in6_addr *daddr,
472aa4bdd4bSYOSHIFUJI Hideaki / 吉藤英明 		    const struct in6_addr *saddr)
473305d552aSBrian Haley {
474aa4bdd4bSYOSHIFUJI Hideaki / 吉藤英明 	struct icmp6hdr *icmp6h = icmp6_hdr(skb);
475*ae38982fSEric Dumazet 	struct dst_entry *dst = skb_dst(skb);
476*ae38982fSEric Dumazet 	struct inet6_dev *idev;
477*ae38982fSEric Dumazet 	struct net *net;
478*ae38982fSEric Dumazet 	struct sock *sk;
479*ae38982fSEric Dumazet 	int err;
480305d552aSBrian Haley 	u8 type;
481305d552aSBrian Haley 
482305d552aSBrian Haley 	type = icmp6h->icmp6_type;
483305d552aSBrian Haley 
484*ae38982fSEric Dumazet 	rcu_read_lock();
485*ae38982fSEric Dumazet 
486*ae38982fSEric Dumazet 	net = dev_net_rcu(skb->dev);
487*ae38982fSEric Dumazet 	sk = net->ipv6.ndisc_sk;
488f4de84c6SYOSHIFUJI Hideaki / 吉藤英明 	if (!dst) {
489f4de84c6SYOSHIFUJI Hideaki / 吉藤英明 		struct flowi6 fl6;
490e0d56fddSDavid Ahern 		int oif = skb->dev->ifindex;
491f4de84c6SYOSHIFUJI Hideaki / 吉藤英明 
492ca254490SDavid Ahern 		icmpv6_flow_init(sk, &fl6, type, saddr, daddr, oif);
493af9a9976SYOSHIFUJI Hideaki / 吉藤英明 		dst = icmp6_dst_alloc(skb->dev, &fl6);
494452edd59SDavid S. Miller 		if (IS_ERR(dst)) {
495*ae38982fSEric Dumazet 			rcu_read_unlock();
496305d552aSBrian Haley 			kfree_skb(skb);
497305d552aSBrian Haley 			return;
498305d552aSBrian Haley 		}
499305d552aSBrian Haley 
500adf30907SEric Dumazet 		skb_dst_set(skb, dst);
501f4de84c6SYOSHIFUJI Hideaki / 吉藤英明 	}
502e1ec7842SYOSHIFUJI Hideaki 
5037b3d9b06SYOSHIFUJI Hideaki / 吉藤英明 	icmp6h->icmp6_cksum = csum_ipv6_magic(saddr, daddr, skb->len,
5047b3d9b06SYOSHIFUJI Hideaki / 吉藤英明 					      IPPROTO_ICMPV6,
5057b3d9b06SYOSHIFUJI Hideaki / 吉藤英明 					      csum_partial(icmp6h,
5067b3d9b06SYOSHIFUJI Hideaki / 吉藤英明 							   skb->len, 0));
5077b3d9b06SYOSHIFUJI Hideaki / 吉藤英明 
5087b3d9b06SYOSHIFUJI Hideaki / 吉藤英明 	ip6_nd_hdr(skb, saddr, daddr, inet6_sk(sk)->hop_limit, skb->len);
5097b3d9b06SYOSHIFUJI Hideaki / 吉藤英明 
510cfdf7647SEric Dumazet 	idev = __in6_dev_get(dst->dev);
51156712f74SHeng Guo 	IP6_INC_STATS(net, idev, IPSTATS_MIB_OUTREQUESTS);
512e1ec7842SYOSHIFUJI Hideaki 
51329a26a56SEric W. Biederman 	err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT,
51429a26a56SEric W. Biederman 		      net, sk, skb, NULL, dst->dev,
51513206b6bSEric W. Biederman 		      dst_output);
516e1ec7842SYOSHIFUJI Hideaki 	if (!err) {
5175c5d244bSDenis V. Lunev 		ICMP6MSGOUT_INC_STATS(net, idev, type);
518a862f6a6SDenis V. Lunev 		ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS);
519e1ec7842SYOSHIFUJI Hideaki 	}
520e1ec7842SYOSHIFUJI Hideaki 
521cfdf7647SEric Dumazet 	rcu_read_unlock();
522e1ec7842SYOSHIFUJI Hideaki }
523696c6544SHangbin Liu EXPORT_SYMBOL(ndisc_send_skb);
524e1ec7842SYOSHIFUJI Hideaki 
ndisc_send_na(struct net_device * dev,const struct in6_addr * daddr,const struct in6_addr * solicited_addr,bool router,bool solicited,bool override,bool inc_opt)52538cf595bSJiri Benc void ndisc_send_na(struct net_device *dev, const struct in6_addr *daddr,
5269acd9f3aSYOSHIFUJI Hideaki 		   const struct in6_addr *solicited_addr,
527fb568637SYOSHIFUJI Hideaki / 吉藤英明 		   bool router, bool solicited, bool override, bool inc_opt)
5281da177e4SLinus Torvalds {
529b44b5f4aSYOSHIFUJI Hideaki / 吉藤英明 	struct sk_buff *skb;
5301da177e4SLinus Torvalds 	struct in6_addr tmpaddr;
5311da177e4SLinus Torvalds 	struct inet6_ifaddr *ifp;
5329acd9f3aSYOSHIFUJI Hideaki 	const struct in6_addr *src_addr;
5331cb3fe51SYOSHIFUJI Hideaki / 吉藤英明 	struct nd_msg *msg;
5341cb3fe51SYOSHIFUJI Hideaki / 吉藤英明 	int optlen = 0;
5351da177e4SLinus Torvalds 
5361da177e4SLinus Torvalds 	/* for anycast or proxy, solicited_addr != src_addr */
537c346dca1SYOSHIFUJI Hideaki 	ifp = ipv6_get_ifaddr(dev_net(dev), solicited_addr, dev, 1);
5381da177e4SLinus Torvalds 	if (ifp) {
5391da177e4SLinus Torvalds 		src_addr = solicited_addr;
54095c385b4SNeil Horman 		if (ifp->flags & IFA_F_OPTIMISTIC)
541f2f79ccaSDaniel Baluta 			override = false;
5429f888160Sstephen hemminger 		inc_opt |= ifp->idev->cnf.force_tllao;
5431da177e4SLinus Torvalds 		in6_ifa_put(ifp);
5441da177e4SLinus Torvalds 	} else {
545191cd582SBrian Haley 		if (ipv6_dev_get_saddr(dev_net(dev), dev, daddr,
546c346dca1SYOSHIFUJI Hideaki 				       inet6_sk(dev_net(dev)->ipv6.ndisc_sk)->srcprefs,
5477cbca67cSYOSHIFUJI Hideaki 				       &tmpaddr))
5481da177e4SLinus Torvalds 			return;
5491da177e4SLinus Torvalds 		src_addr = &tmpaddr;
5501da177e4SLinus Torvalds 	}
5511da177e4SLinus Torvalds 
5521cb3fe51SYOSHIFUJI Hideaki / 吉藤英明 	if (!dev->addr_len)
5539a21ac94SGustavo A. R. Silva 		inc_opt = false;
5541cb3fe51SYOSHIFUJI Hideaki / 吉藤英明 	if (inc_opt)
555f997c55cSAlexander Aring 		optlen += ndisc_opt_addr_space(dev,
556f997c55cSAlexander Aring 					       NDISC_NEIGHBOUR_ADVERTISEMENT);
5571da177e4SLinus Torvalds 
5581cb3fe51SYOSHIFUJI Hideaki / 吉藤英明 	skb = ndisc_alloc_skb(dev, sizeof(*msg) + optlen);
559b44b5f4aSYOSHIFUJI Hideaki / 吉藤英明 	if (!skb)
560b44b5f4aSYOSHIFUJI Hideaki / 吉藤英明 		return;
561b44b5f4aSYOSHIFUJI Hideaki / 吉藤英明 
5624df864c1SJohannes Berg 	msg = skb_put(skb, sizeof(*msg));
5631cb3fe51SYOSHIFUJI Hideaki / 吉藤英明 	*msg = (struct nd_msg) {
5641cb3fe51SYOSHIFUJI Hideaki / 吉藤英明 		.icmph = {
5651cb3fe51SYOSHIFUJI Hideaki / 吉藤英明 			.icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT,
5661cb3fe51SYOSHIFUJI Hideaki / 吉藤英明 			.icmp6_router = router,
5671cb3fe51SYOSHIFUJI Hideaki / 吉藤英明 			.icmp6_solicited = solicited,
5681cb3fe51SYOSHIFUJI Hideaki / 吉藤英明 			.icmp6_override = override,
5691cb3fe51SYOSHIFUJI Hideaki / 吉藤英明 		},
5701cb3fe51SYOSHIFUJI Hideaki / 吉藤英明 		.target = *solicited_addr,
5711cb3fe51SYOSHIFUJI Hideaki / 吉藤英明 	};
5721cb3fe51SYOSHIFUJI Hideaki / 吉藤英明 
5731cb3fe51SYOSHIFUJI Hideaki / 吉藤英明 	if (inc_opt)
5741cb3fe51SYOSHIFUJI Hideaki / 吉藤英明 		ndisc_fill_addr_option(skb, ND_OPT_TARGET_LL_ADDR,
575f997c55cSAlexander Aring 				       dev->dev_addr,
576f997c55cSAlexander Aring 				       NDISC_NEIGHBOUR_ADVERTISEMENT);
5771cb3fe51SYOSHIFUJI Hideaki / 吉藤英明 
578b44b5f4aSYOSHIFUJI Hideaki / 吉藤英明 	ndisc_send_skb(skb, daddr, src_addr);
5791da177e4SLinus Torvalds }
5801da177e4SLinus Torvalds 
ndisc_send_unsol_na(struct net_device * dev)581f47b9464SBen Hutchings static void ndisc_send_unsol_na(struct net_device *dev)
582f47b9464SBen Hutchings {
583f47b9464SBen Hutchings 	struct inet6_dev *idev;
584f47b9464SBen Hutchings 	struct inet6_ifaddr *ifa;
585f47b9464SBen Hutchings 
586f47b9464SBen Hutchings 	idev = in6_dev_get(dev);
587f47b9464SBen Hutchings 	if (!idev)
588f47b9464SBen Hutchings 		return;
589f47b9464SBen Hutchings 
590f47b9464SBen Hutchings 	read_lock_bh(&idev->lock);
591f47b9464SBen Hutchings 	list_for_each_entry(ifa, &idev->addr_list, if_list) {
592c76fe2d9SDavid Ahern 		/* skip tentative addresses until dad completes */
593c76fe2d9SDavid Ahern 		if (ifa->flags & IFA_F_TENTATIVE &&
594c76fe2d9SDavid Ahern 		    !(ifa->flags & IFA_F_OPTIMISTIC))
595c76fe2d9SDavid Ahern 			continue;
596c76fe2d9SDavid Ahern 
59738cf595bSJiri Benc 		ndisc_send_na(dev, &in6addr_linklocal_allnodes, &ifa->addr,
598f47b9464SBen Hutchings 			      /*router=*/ !!idev->cnf.forwarding,
599f47b9464SBen Hutchings 			      /*solicited=*/ false, /*override=*/ true,
600f47b9464SBen Hutchings 			      /*inc_opt=*/ true);
601f47b9464SBen Hutchings 	}
602f47b9464SBen Hutchings 	read_unlock_bh(&idev->lock);
603f47b9464SBen Hutchings 
604f47b9464SBen Hutchings 	in6_dev_put(idev);
605f47b9464SBen Hutchings }
606f47b9464SBen Hutchings 
ndisc_ns_create(struct net_device * dev,const struct in6_addr * solicit,const struct in6_addr * saddr,u64 nonce)607696c6544SHangbin Liu struct sk_buff *ndisc_ns_create(struct net_device *dev, const struct in6_addr *solicit,
608696c6544SHangbin Liu 				const struct in6_addr *saddr, u64 nonce)
6091da177e4SLinus Torvalds {
6101cb3fe51SYOSHIFUJI Hideaki / 吉藤英明 	int inc_opt = dev->addr_len;
611696c6544SHangbin Liu 	struct sk_buff *skb;
6121cb3fe51SYOSHIFUJI Hideaki / 吉藤英明 	struct nd_msg *msg;
613696c6544SHangbin Liu 	int optlen = 0;
6141da177e4SLinus Torvalds 
615696c6544SHangbin Liu 	if (!saddr)
616696c6544SHangbin Liu 		return NULL;
6171da177e4SLinus Torvalds 
6181cb3fe51SYOSHIFUJI Hideaki / 吉藤英明 	if (ipv6_addr_any(saddr))
619f2f79ccaSDaniel Baluta 		inc_opt = false;
6201cb3fe51SYOSHIFUJI Hideaki / 吉藤英明 	if (inc_opt)
621f997c55cSAlexander Aring 		optlen += ndisc_opt_addr_space(dev,
622f997c55cSAlexander Aring 					       NDISC_NEIGHBOUR_SOLICITATION);
623adc176c5SErik Nordmark 	if (nonce != 0)
624adc176c5SErik Nordmark 		optlen += 8;
6251cb3fe51SYOSHIFUJI Hideaki / 吉藤英明 
6261cb3fe51SYOSHIFUJI Hideaki / 吉藤英明 	skb = ndisc_alloc_skb(dev, sizeof(*msg) + optlen);
627b44b5f4aSYOSHIFUJI Hideaki / 吉藤英明 	if (!skb)
628696c6544SHangbin Liu 		return NULL;
629b44b5f4aSYOSHIFUJI Hideaki / 吉藤英明 
6304df864c1SJohannes Berg 	msg = skb_put(skb, sizeof(*msg));
6311cb3fe51SYOSHIFUJI Hideaki / 吉藤英明 	*msg = (struct nd_msg) {
6321cb3fe51SYOSHIFUJI Hideaki / 吉藤英明 		.icmph = {
6331cb3fe51SYOSHIFUJI Hideaki / 吉藤英明 			.icmp6_type = NDISC_NEIGHBOUR_SOLICITATION,
6341cb3fe51SYOSHIFUJI Hideaki / 吉藤英明 		},
6351cb3fe51SYOSHIFUJI Hideaki / 吉藤英明 		.target = *solicit,
6361cb3fe51SYOSHIFUJI Hideaki / 吉藤英明 	};
6371cb3fe51SYOSHIFUJI Hideaki / 吉藤英明 
6381cb3fe51SYOSHIFUJI Hideaki / 吉藤英明 	if (inc_opt)
6391cb3fe51SYOSHIFUJI Hideaki / 吉藤英明 		ndisc_fill_addr_option(skb, ND_OPT_SOURCE_LL_ADDR,
640f997c55cSAlexander Aring 				       dev->dev_addr,
641f997c55cSAlexander Aring 				       NDISC_NEIGHBOUR_SOLICITATION);
642adc176c5SErik Nordmark 	if (nonce != 0) {
643adc176c5SErik Nordmark 		u8 *opt = skb_put(skb, 8);
644adc176c5SErik Nordmark 
645adc176c5SErik Nordmark 		opt[0] = ND_OPT_NONCE;
646adc176c5SErik Nordmark 		opt[1] = 8 >> 3;
647adc176c5SErik Nordmark 		memcpy(opt + 2, &nonce, 6);
648adc176c5SErik Nordmark 	}
6491cb3fe51SYOSHIFUJI Hideaki / 吉藤英明 
650696c6544SHangbin Liu 	return skb;
651696c6544SHangbin Liu }
652696c6544SHangbin Liu EXPORT_SYMBOL(ndisc_ns_create);
653696c6544SHangbin Liu 
ndisc_send_ns(struct net_device * dev,const struct in6_addr * solicit,const struct in6_addr * daddr,const struct in6_addr * saddr,u64 nonce)654696c6544SHangbin Liu void ndisc_send_ns(struct net_device *dev, const struct in6_addr *solicit,
655696c6544SHangbin Liu 		   const struct in6_addr *daddr, const struct in6_addr *saddr,
656696c6544SHangbin Liu 		   u64 nonce)
657696c6544SHangbin Liu {
658696c6544SHangbin Liu 	struct in6_addr addr_buf;
659696c6544SHangbin Liu 	struct sk_buff *skb;
660696c6544SHangbin Liu 
661696c6544SHangbin Liu 	if (!saddr) {
662696c6544SHangbin Liu 		if (ipv6_get_lladdr(dev, &addr_buf,
663696c6544SHangbin Liu 				    (IFA_F_TENTATIVE | IFA_F_OPTIMISTIC)))
664696c6544SHangbin Liu 			return;
665696c6544SHangbin Liu 		saddr = &addr_buf;
666696c6544SHangbin Liu 	}
667696c6544SHangbin Liu 
668696c6544SHangbin Liu 	skb = ndisc_ns_create(dev, solicit, saddr, nonce);
669696c6544SHangbin Liu 
670696c6544SHangbin Liu 	if (skb)
671b44b5f4aSYOSHIFUJI Hideaki / 吉藤英明 		ndisc_send_skb(skb, daddr, saddr);
6721da177e4SLinus Torvalds }
6731da177e4SLinus Torvalds 
ndisc_send_rs(struct net_device * dev,const struct in6_addr * saddr,const struct in6_addr * daddr)6749acd9f3aSYOSHIFUJI Hideaki void ndisc_send_rs(struct net_device *dev, const struct in6_addr *saddr,
6759acd9f3aSYOSHIFUJI Hideaki 		   const struct in6_addr *daddr)
6761da177e4SLinus Torvalds {
677b44b5f4aSYOSHIFUJI Hideaki / 吉藤英明 	struct sk_buff *skb;
6781cb3fe51SYOSHIFUJI Hideaki / 吉藤英明 	struct rs_msg *msg;
67995c385b4SNeil Horman 	int send_sllao = dev->addr_len;
6801cb3fe51SYOSHIFUJI Hideaki / 吉藤英明 	int optlen = 0;
68195c385b4SNeil Horman 
68295c385b4SNeil Horman #ifdef CONFIG_IPV6_OPTIMISTIC_DAD
68395c385b4SNeil Horman 	/*
68495c385b4SNeil Horman 	 * According to section 2.2 of RFC 4429, we must not
68595c385b4SNeil Horman 	 * send router solicitations with a sllao from
68695c385b4SNeil Horman 	 * optimistic addresses, but we may send the solicitation
68795c385b4SNeil Horman 	 * if we don't include the sllao.  So here we check
68895c385b4SNeil Horman 	 * if our address is optimistic, and if so, we
689bea85195SJoe Perches 	 * suppress the inclusion of the sllao.
69095c385b4SNeil Horman 	 */
69195c385b4SNeil Horman 	if (send_sllao) {
692c346dca1SYOSHIFUJI Hideaki 		struct inet6_ifaddr *ifp = ipv6_get_ifaddr(dev_net(dev), saddr,
6931cab3da6SDaniel Lezcano 							   dev, 1);
69495c385b4SNeil Horman 		if (ifp) {
69595c385b4SNeil Horman 			if (ifp->flags & IFA_F_OPTIMISTIC)  {
69695c385b4SNeil Horman 				send_sllao = 0;
69795c385b4SNeil Horman 			}
698ca043569SYOSHIFUJI Hideaki 			in6_ifa_put(ifp);
69995c385b4SNeil Horman 		} else {
70095c385b4SNeil Horman 			send_sllao = 0;
70195c385b4SNeil Horman 		}
70295c385b4SNeil Horman 	}
70395c385b4SNeil Horman #endif
7041cb3fe51SYOSHIFUJI Hideaki / 吉藤英明 	if (send_sllao)
705f997c55cSAlexander Aring 		optlen += ndisc_opt_addr_space(dev, NDISC_ROUTER_SOLICITATION);
7061cb3fe51SYOSHIFUJI Hideaki / 吉藤英明 
7071cb3fe51SYOSHIFUJI Hideaki / 吉藤英明 	skb = ndisc_alloc_skb(dev, sizeof(*msg) + optlen);
708b44b5f4aSYOSHIFUJI Hideaki / 吉藤英明 	if (!skb)
709b44b5f4aSYOSHIFUJI Hideaki / 吉藤英明 		return;
710b44b5f4aSYOSHIFUJI Hideaki / 吉藤英明 
7114df864c1SJohannes Berg 	msg = skb_put(skb, sizeof(*msg));
7121cb3fe51SYOSHIFUJI Hideaki / 吉藤英明 	*msg = (struct rs_msg) {
7131cb3fe51SYOSHIFUJI Hideaki / 吉藤英明 		.icmph = {
7141cb3fe51SYOSHIFUJI Hideaki / 吉藤英明 			.icmp6_type = NDISC_ROUTER_SOLICITATION,
7151cb3fe51SYOSHIFUJI Hideaki / 吉藤英明 		},
7161cb3fe51SYOSHIFUJI Hideaki / 吉藤英明 	};
7171cb3fe51SYOSHIFUJI Hideaki / 吉藤英明 
7181cb3fe51SYOSHIFUJI Hideaki / 吉藤英明 	if (send_sllao)
7191cb3fe51SYOSHIFUJI Hideaki / 吉藤英明 		ndisc_fill_addr_option(skb, ND_OPT_SOURCE_LL_ADDR,
720f997c55cSAlexander Aring 				       dev->dev_addr,
721f997c55cSAlexander Aring 				       NDISC_ROUTER_SOLICITATION);
7221cb3fe51SYOSHIFUJI Hideaki / 吉藤英明 
723b44b5f4aSYOSHIFUJI Hideaki / 吉藤英明 	ndisc_send_skb(skb, daddr, saddr);
7241da177e4SLinus Torvalds }
7251da177e4SLinus Torvalds 
7261da177e4SLinus Torvalds 
ndisc_error_report(struct neighbour * neigh,struct sk_buff * skb)7271da177e4SLinus Torvalds static void ndisc_error_report(struct neighbour *neigh, struct sk_buff *skb)
7281da177e4SLinus Torvalds {
7291da177e4SLinus Torvalds 	/*
7301da177e4SLinus Torvalds 	 *	"The sender MUST return an ICMP
7311da177e4SLinus Torvalds 	 *	 destination unreachable"
7321da177e4SLinus Torvalds 	 */
7331da177e4SLinus Torvalds 	dst_link_failure(skb);
7341da177e4SLinus Torvalds 	kfree_skb(skb);
7351da177e4SLinus Torvalds }
7361da177e4SLinus Torvalds 
7371da177e4SLinus Torvalds /* Called with locked neigh: either read or both */
7381da177e4SLinus Torvalds 
ndisc_solicit(struct neighbour * neigh,struct sk_buff * skb)7391da177e4SLinus Torvalds static void ndisc_solicit(struct neighbour *neigh, struct sk_buff *skb)
7401da177e4SLinus Torvalds {
7411da177e4SLinus Torvalds 	struct in6_addr *saddr = NULL;
7421da177e4SLinus Torvalds 	struct in6_addr mcaddr;
7431da177e4SLinus Torvalds 	struct net_device *dev = neigh->dev;
7441da177e4SLinus Torvalds 	struct in6_addr *target = (struct in6_addr *)&neigh->primary_key;
7451da177e4SLinus Torvalds 	int probes = atomic_read(&neigh->probes);
7461da177e4SLinus Torvalds 
747c58da4c6SErik Kline 	if (skb && ipv6_chk_addr_and_flags(dev_net(dev), &ipv6_hdr(skb)->saddr,
748232378e8SDavid Ahern 					   dev, false, 1,
749c58da4c6SErik Kline 					   IFA_F_TENTATIVE|IFA_F_OPTIMISTIC))
7500660e03fSArnaldo Carvalho de Melo 		saddr = &ipv6_hdr(skb)->saddr;
751e5d08d71SIan Morris 	probes -= NEIGH_VAR(neigh->parms, UCAST_PROBES);
752e5d08d71SIan Morris 	if (probes < 0) {
753b071af52SEric Dumazet 		if (!(READ_ONCE(neigh->nud_state) & NUD_VALID)) {
754675418d5SJoe Perches 			ND_PRINTK(1, dbg,
755675418d5SJoe Perches 				  "%s: trying to ucast probe in NUD_INVALID: %pI6\n",
7560c6ce78aSHarvey Harrison 				  __func__, target);
7571da177e4SLinus Torvalds 		}
758adc176c5SErik Nordmark 		ndisc_send_ns(dev, target, target, saddr, 0);
7591f9248e5SJiri Pirko 	} else if ((probes -= NEIGH_VAR(neigh->parms, APP_PROBES)) < 0) {
7601da177e4SLinus Torvalds 		neigh_app_ns(neigh);
7611da177e4SLinus Torvalds 	} else {
7621da177e4SLinus Torvalds 		addrconf_addr_solict_mult(target, &mcaddr);
763adc176c5SErik Nordmark 		ndisc_send_ns(dev, target, &mcaddr, saddr, 0);
7641da177e4SLinus Torvalds 	}
7651da177e4SLinus Torvalds }
7661da177e4SLinus Torvalds 
pndisc_is_router(const void * pkey,struct net_device * dev)7670736ffc0SYOSHIFUJI Hideaki static int pndisc_is_router(const void *pkey,
7680736ffc0SYOSHIFUJI Hideaki 			    struct net_device *dev)
769fa86d322SPavel Emelyanov {
770fa86d322SPavel Emelyanov 	struct pneigh_entry *n;
7710736ffc0SYOSHIFUJI Hideaki 	int ret = -1;
772fa86d322SPavel Emelyanov 
773fa86d322SPavel Emelyanov 	read_lock_bh(&nd_tbl.lock);
7740736ffc0SYOSHIFUJI Hideaki 	n = __pneigh_lookup(&nd_tbl, dev_net(dev), pkey, dev);
7750736ffc0SYOSHIFUJI Hideaki 	if (n)
7760736ffc0SYOSHIFUJI Hideaki 		ret = !!(n->flags & NTF_ROUTER);
777fa86d322SPavel Emelyanov 	read_unlock_bh(&nd_tbl.lock);
778fa86d322SPavel Emelyanov 
7790736ffc0SYOSHIFUJI Hideaki 	return ret;
780fa86d322SPavel Emelyanov }
781fa86d322SPavel Emelyanov 
ndisc_update(const struct net_device * dev,struct neighbour * neigh,const u8 * lladdr,u8 new,u32 flags,u8 icmp6_type,struct ndisc_options * ndopts)782f997c55cSAlexander Aring void ndisc_update(const struct net_device *dev, struct neighbour *neigh,
783f997c55cSAlexander Aring 		  const u8 *lladdr, u8 new, u32 flags, u8 icmp6_type,
784f997c55cSAlexander Aring 		  struct ndisc_options *ndopts)
785f997c55cSAlexander Aring {
7867b8f7a40SRoopa Prabhu 	neigh_update(neigh, lladdr, new, flags, 0);
787f997c55cSAlexander Aring 	/* report ndisc ops about neighbour update */
788f997c55cSAlexander Aring 	ndisc_ops_update(dev, neigh, flags, icmp6_type, ndopts);
789f997c55cSAlexander Aring }
790f997c55cSAlexander Aring 
ndisc_recv_ns(struct sk_buff * skb)7917c9c8913SEric Dumazet static enum skb_drop_reason ndisc_recv_ns(struct sk_buff *skb)
7921da177e4SLinus Torvalds {
7939c70220bSArnaldo Carvalho de Melo 	struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb);
794b71d1d42SEric Dumazet 	const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
795b71d1d42SEric Dumazet 	const struct in6_addr *daddr = &ipv6_hdr(skb)->daddr;
7961da177e4SLinus Torvalds 	u8 *lladdr = NULL;
79729a3cad5SSimon Horman 	u32 ndoptlen = skb_tail_pointer(skb) - (skb_transport_header(skb) +
79827a884dcSArnaldo Carvalho de Melo 				    offsetof(struct nd_msg, opt));
7991da177e4SLinus Torvalds 	struct ndisc_options ndopts;
8001da177e4SLinus Torvalds 	struct net_device *dev = skb->dev;
8011da177e4SLinus Torvalds 	struct inet6_ifaddr *ifp;
8021da177e4SLinus Torvalds 	struct inet6_dev *idev = NULL;
8031da177e4SLinus Torvalds 	struct neighbour *neigh;
8041da177e4SLinus Torvalds 	int dad = ipv6_addr_any(saddr);
8050736ffc0SYOSHIFUJI Hideaki 	int is_router = -1;
8067c9c8913SEric Dumazet 	SKB_DR(reason);
807adc176c5SErik Nordmark 	u64 nonce = 0;
8087c9c8913SEric Dumazet 	bool inc;
8091da177e4SLinus Torvalds 
8107c9c8913SEric Dumazet 	if (skb->len < sizeof(struct nd_msg))
8117c9c8913SEric Dumazet 		return SKB_DROP_REASON_PKT_TOO_SMALL;
812115b0aa6SYOSHIFUJI Hideaki / 吉藤英明 
8131da177e4SLinus Torvalds 	if (ipv6_addr_is_multicast(&msg->target)) {
814675418d5SJoe Perches 		ND_PRINTK(2, warn, "NS: multicast target address\n");
8157c9c8913SEric Dumazet 		return reason;
8161da177e4SLinus Torvalds 	}
8171da177e4SLinus Torvalds 
8181da177e4SLinus Torvalds 	/*
8191da177e4SLinus Torvalds 	 * RFC2461 7.1.1:
8201da177e4SLinus Torvalds 	 * DAD has to be destined for solicited node multicast address.
8211da177e4SLinus Torvalds 	 */
822ca97a644SYOSHIFUJI Hideaki / 吉藤英明 	if (dad && !ipv6_addr_is_solict_mult(daddr)) {
823675418d5SJoe Perches 		ND_PRINTK(2, warn, "NS: bad DAD packet (wrong destination)\n");
8247c9c8913SEric Dumazet 		return reason;
8251da177e4SLinus Torvalds 	}
8261da177e4SLinus Torvalds 
827784d4477SEric Dumazet 	if (!ndisc_parse_options(dev, msg->opt, ndoptlen, &ndopts))
828784d4477SEric Dumazet 		return SKB_DROP_REASON_IPV6_NDISC_BAD_OPTIONS;
8291da177e4SLinus Torvalds 
8301da177e4SLinus Torvalds 	if (ndopts.nd_opts_src_lladdr) {
8311da177e4SLinus Torvalds 		lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr, dev);
8321da177e4SLinus Torvalds 		if (!lladdr) {
833675418d5SJoe Perches 			ND_PRINTK(2, warn,
834675418d5SJoe Perches 				  "NS: invalid link-layer address length\n");
8357c9c8913SEric Dumazet 			return reason;
8361da177e4SLinus Torvalds 		}
8371da177e4SLinus Torvalds 
8381da177e4SLinus Torvalds 		/* RFC2461 7.1.1:
8391da177e4SLinus Torvalds 		 *	If the IP source address is the unspecified address,
8401da177e4SLinus Torvalds 		 *	there MUST NOT be source link-layer address option
8411da177e4SLinus Torvalds 		 *	in the message.
8421da177e4SLinus Torvalds 		 */
8431da177e4SLinus Torvalds 		if (dad) {
844675418d5SJoe Perches 			ND_PRINTK(2, warn,
845675418d5SJoe Perches 				  "NS: bad DAD packet (link-layer address option)\n");
8467c9c8913SEric Dumazet 			return reason;
8471da177e4SLinus Torvalds 		}
8481da177e4SLinus Torvalds 	}
849e6651599SSabrina Dubroca 	if (ndopts.nd_opts_nonce && ndopts.nd_opts_nonce->nd_opt_len == 1)
850adc176c5SErik Nordmark 		memcpy(&nonce, (u8 *)(ndopts.nd_opts_nonce + 1), 6);
8511da177e4SLinus Torvalds 
8521da177e4SLinus Torvalds 	inc = ipv6_addr_is_multicast(daddr);
8531da177e4SLinus Torvalds 
854c346dca1SYOSHIFUJI Hideaki 	ifp = ipv6_get_ifaddr(dev_net(dev), &msg->target, dev, 1);
855a18bc695SDaniel Lezcano 	if (ifp) {
856ca254490SDavid Ahern have_ifp:
85795c385b4SNeil Horman 		if (ifp->flags & (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)) {
85895c385b4SNeil Horman 			if (dad) {
859adc176c5SErik Nordmark 				if (nonce != 0 && ifp->dad_nonce == nonce) {
860adc176c5SErik Nordmark 					u8 *np = (u8 *)&nonce;
861adc176c5SErik Nordmark 					/* Matching nonce if looped back */
862adc176c5SErik Nordmark 					ND_PRINTK(2, notice,
863adc176c5SErik Nordmark 						  "%s: IPv6 DAD loopback for address %pI6c nonce %pM ignored\n",
864adc176c5SErik Nordmark 						  ifp->idev->dev->name,
865adc176c5SErik Nordmark 						  &ifp->addr, np);
866adc176c5SErik Nordmark 					goto out;
867adc176c5SErik Nordmark 				}
86895c385b4SNeil Horman 				/*
86995c385b4SNeil Horman 				 * We are colliding with another node
87095c385b4SNeil Horman 				 * who is doing DAD
87195c385b4SNeil Horman 				 * so fail our DAD process
87295c385b4SNeil Horman 				 */
873da13c59bSVishwanath Pai 				addrconf_dad_failure(skb, ifp);
8747c9c8913SEric Dumazet 				return reason;
87595c385b4SNeil Horman 			} else {
87695c385b4SNeil Horman 				/*
87795c385b4SNeil Horman 				 * This is not a dad solicitation.
87895c385b4SNeil Horman 				 * If we are an optimistic node,
87995c385b4SNeil Horman 				 * we should respond.
88095c385b4SNeil Horman 				 * Otherwise, we should ignore it.
88195c385b4SNeil Horman 				 */
88295c385b4SNeil Horman 				if (!(ifp->flags & IFA_F_OPTIMISTIC))
88395c385b4SNeil Horman 					goto out;
88495c385b4SNeil Horman 			}
8851da177e4SLinus Torvalds 		}
8861da177e4SLinus Torvalds 
8871da177e4SLinus Torvalds 		idev = ifp->idev;
8881da177e4SLinus Torvalds 	} else {
88953b7997fSYOSHIFUJI Hideaki 		struct net *net = dev_net(dev);
89053b7997fSYOSHIFUJI Hideaki 
891ca254490SDavid Ahern 		/* perhaps an address on the master device */
892ca254490SDavid Ahern 		if (netif_is_l3_slave(dev)) {
893ca254490SDavid Ahern 			struct net_device *mdev;
894ca254490SDavid Ahern 
895ca254490SDavid Ahern 			mdev = netdev_master_upper_dev_get_rcu(dev);
896ca254490SDavid Ahern 			if (mdev) {
897ca254490SDavid Ahern 				ifp = ipv6_get_ifaddr(net, &msg->target, mdev, 1);
898ca254490SDavid Ahern 				if (ifp)
899ca254490SDavid Ahern 					goto have_ifp;
900ca254490SDavid Ahern 			}
901ca254490SDavid Ahern 		}
902ca254490SDavid Ahern 
9031da177e4SLinus Torvalds 		idev = in6_dev_get(dev);
9041da177e4SLinus Torvalds 		if (!idev) {
9051da177e4SLinus Torvalds 			/* XXX: count this drop? */
9067c9c8913SEric Dumazet 			return reason;
9071da177e4SLinus Torvalds 		}
9081da177e4SLinus Torvalds 
90953b7997fSYOSHIFUJI Hideaki 		if (ipv6_chk_acast_addr(net, dev, &msg->target) ||
9101da177e4SLinus Torvalds 		    (idev->cnf.forwarding &&
91153b7997fSYOSHIFUJI Hideaki 		     (net->ipv6.devconf_all->proxy_ndp || idev->cnf.proxy_ndp) &&
9120736ffc0SYOSHIFUJI Hideaki 		     (is_router = pndisc_is_router(&msg->target, dev)) >= 0)) {
913a61bbcf2SPatrick McHardy 			if (!(NEIGH_CB(skb)->flags & LOCALLY_ENQUEUED) &&
9141da177e4SLinus Torvalds 			    skb->pkt_type != PACKET_HOST &&
915f2f79ccaSDaniel Baluta 			    inc &&
9161f9248e5SJiri Pirko 			    NEIGH_VAR(idev->nd_parms, PROXY_DELAY) != 0) {
9171da177e4SLinus Torvalds 				/*
9181da177e4SLinus Torvalds 				 * for anycast or proxy,
9191da177e4SLinus Torvalds 				 * sender should delay its response
9201da177e4SLinus Torvalds 				 * by a random time between 0 and
9211da177e4SLinus Torvalds 				 * MAX_ANYCAST_DELAY_TIME seconds.
9221da177e4SLinus Torvalds 				 * (RFC2461) -- yoshfuji
9231da177e4SLinus Torvalds 				 */
9241da177e4SLinus Torvalds 				struct sk_buff *n = skb_clone(skb, GFP_ATOMIC);
9251da177e4SLinus Torvalds 				if (n)
9261da177e4SLinus Torvalds 					pneigh_enqueue(&nd_tbl, idev->nd_parms, n);
9271da177e4SLinus Torvalds 				goto out;
9281da177e4SLinus Torvalds 			}
929c34b8bb1SEric Dumazet 		} else {
930c34b8bb1SEric Dumazet 			SKB_DR_SET(reason, IPV6_NDISC_NS_OTHERHOST);
9311da177e4SLinus Torvalds 			goto out;
9321da177e4SLinus Torvalds 		}
933c34b8bb1SEric Dumazet 	}
9341da177e4SLinus Torvalds 
9350736ffc0SYOSHIFUJI Hideaki 	if (is_router < 0)
936fb568637SYOSHIFUJI Hideaki / 吉藤英明 		is_router = idev->cnf.forwarding;
93762dd9318SVille Nuorvala 
9381da177e4SLinus Torvalds 	if (dad) {
93938cf595bSJiri Benc 		ndisc_send_na(dev, &in6addr_linklocal_allnodes, &msg->target,
940fb568637SYOSHIFUJI Hideaki / 吉藤英明 			      !!is_router, false, (ifp != NULL), true);
9411da177e4SLinus Torvalds 		goto out;
9421da177e4SLinus Torvalds 	}
9431da177e4SLinus Torvalds 
9441da177e4SLinus Torvalds 	if (inc)
9451da177e4SLinus Torvalds 		NEIGH_CACHE_STAT_INC(&nd_tbl, rcv_probes_mcast);
9461da177e4SLinus Torvalds 	else
9471da177e4SLinus Torvalds 		NEIGH_CACHE_STAT_INC(&nd_tbl, rcv_probes_ucast);
9481da177e4SLinus Torvalds 
9491da177e4SLinus Torvalds 	/*
9501da177e4SLinus Torvalds 	 *	update / create cache entry
9511da177e4SLinus Torvalds 	 *	for the source address
9521da177e4SLinus Torvalds 	 */
9531da177e4SLinus Torvalds 	neigh = __neigh_lookup(&nd_tbl, saddr, dev,
9541da177e4SLinus Torvalds 			       !inc || lladdr || !dev->addr_len);
9551da177e4SLinus Torvalds 	if (neigh)
956f997c55cSAlexander Aring 		ndisc_update(dev, neigh, lladdr, NUD_STALE,
9571da177e4SLinus Torvalds 			     NEIGH_UPDATE_F_WEAK_OVERRIDE|
958f997c55cSAlexander Aring 			     NEIGH_UPDATE_F_OVERRIDE,
959f997c55cSAlexander Aring 			     NDISC_NEIGHBOUR_SOLICITATION, &ndopts);
9603b04dddeSStephen Hemminger 	if (neigh || !dev->header_ops) {
96138cf595bSJiri Benc 		ndisc_send_na(dev, saddr, &msg->target, !!is_router,
962fb568637SYOSHIFUJI Hideaki / 吉藤英明 			      true, (ifp != NULL && inc), inc);
9631da177e4SLinus Torvalds 		if (neigh)
9641da177e4SLinus Torvalds 			neigh_release(neigh);
9657c9c8913SEric Dumazet 		reason = SKB_CONSUMED;
9661da177e4SLinus Torvalds 	}
9671da177e4SLinus Torvalds 
9681da177e4SLinus Torvalds out:
9691da177e4SLinus Torvalds 	if (ifp)
9701da177e4SLinus Torvalds 		in6_ifa_put(ifp);
9711da177e4SLinus Torvalds 	else
9721da177e4SLinus Torvalds 		in6_dev_put(idev);
9737c9c8913SEric Dumazet 	return reason;
9741da177e4SLinus Torvalds }
9751da177e4SLinus Torvalds 
accept_untracked_na(struct net_device * dev,struct in6_addr * saddr)976aaa5f515SJaehee Park static int accept_untracked_na(struct net_device *dev, struct in6_addr *saddr)
977aaa5f515SJaehee Park {
978aaa5f515SJaehee Park 	struct inet6_dev *idev = __in6_dev_get(dev);
979aaa5f515SJaehee Park 
980aaa5f515SJaehee Park 	switch (idev->cnf.accept_untracked_na) {
981aaa5f515SJaehee Park 	case 0: /* Don't accept untracked na (absent in neighbor cache) */
982aaa5f515SJaehee Park 		return 0;
983aaa5f515SJaehee Park 	case 1: /* Create new entries from na if currently untracked */
984aaa5f515SJaehee Park 		return 1;
985aaa5f515SJaehee Park 	case 2: /* Create new entries from untracked na only if saddr is in the
986aaa5f515SJaehee Park 		 * same subnet as an address configured on the interface that
987aaa5f515SJaehee Park 		 * received the na
988aaa5f515SJaehee Park 		 */
989aaa5f515SJaehee Park 		return !!ipv6_chk_prefix(saddr, dev);
990aaa5f515SJaehee Park 	default:
991aaa5f515SJaehee Park 		return 0;
992aaa5f515SJaehee Park 	}
993aaa5f515SJaehee Park }
994aaa5f515SJaehee Park 
ndisc_recv_na(struct sk_buff * skb)9953009f9aeSEric Dumazet static enum skb_drop_reason ndisc_recv_na(struct sk_buff *skb)
9961da177e4SLinus Torvalds {
9979c70220bSArnaldo Carvalho de Melo 	struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb);
998be7a010dSDuan Jiong 	struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
999b71d1d42SEric Dumazet 	const struct in6_addr *daddr = &ipv6_hdr(skb)->daddr;
10001da177e4SLinus Torvalds 	u8 *lladdr = NULL;
100129a3cad5SSimon Horman 	u32 ndoptlen = skb_tail_pointer(skb) - (skb_transport_header(skb) +
100227a884dcSArnaldo Carvalho de Melo 				    offsetof(struct nd_msg, opt));
10031da177e4SLinus Torvalds 	struct ndisc_options ndopts;
10041da177e4SLinus Torvalds 	struct net_device *dev = skb->dev;
10057a02bf89SJohannes Berg 	struct inet6_dev *idev = __in6_dev_get(dev);
10061da177e4SLinus Torvalds 	struct inet6_ifaddr *ifp;
10071da177e4SLinus Torvalds 	struct neighbour *neigh;
10083009f9aeSEric Dumazet 	SKB_DR(reason);
10093e0b8f52SArun Ajith S 	u8 new_state;
10101da177e4SLinus Torvalds 
10113009f9aeSEric Dumazet 	if (skb->len < sizeof(struct nd_msg))
10123009f9aeSEric Dumazet 		return SKB_DROP_REASON_PKT_TOO_SMALL;
10131da177e4SLinus Torvalds 
10141da177e4SLinus Torvalds 	if (ipv6_addr_is_multicast(&msg->target)) {
1015675418d5SJoe Perches 		ND_PRINTK(2, warn, "NA: target address is multicast\n");
10163009f9aeSEric Dumazet 		return reason;
10171da177e4SLinus Torvalds 	}
10181da177e4SLinus Torvalds 
10191da177e4SLinus Torvalds 	if (ipv6_addr_is_multicast(daddr) &&
10201da177e4SLinus Torvalds 	    msg->icmph.icmp6_solicited) {
1021675418d5SJoe Perches 		ND_PRINTK(2, warn, "NA: solicited NA is multicasted\n");
10223009f9aeSEric Dumazet 		return reason;
10231da177e4SLinus Torvalds 	}
10241da177e4SLinus Torvalds 
10257a02bf89SJohannes Berg 	/* For some 802.11 wireless deployments (and possibly other networks),
10267a02bf89SJohannes Berg 	 * there will be a NA proxy and unsolicitd packets are attacks
10277a02bf89SJohannes Berg 	 * and thus should not be accepted.
10283e0b8f52SArun Ajith S 	 * drop_unsolicited_na takes precedence over accept_untracked_na
10297a02bf89SJohannes Berg 	 */
10307a02bf89SJohannes Berg 	if (!msg->icmph.icmp6_solicited && idev &&
10317a02bf89SJohannes Berg 	    idev->cnf.drop_unsolicited_na)
10323009f9aeSEric Dumazet 		return reason;
10337a02bf89SJohannes Berg 
1034784d4477SEric Dumazet 	if (!ndisc_parse_options(dev, msg->opt, ndoptlen, &ndopts))
1035784d4477SEric Dumazet 		return SKB_DROP_REASON_IPV6_NDISC_BAD_OPTIONS;
1036784d4477SEric Dumazet 
10371da177e4SLinus Torvalds 	if (ndopts.nd_opts_tgt_lladdr) {
10381da177e4SLinus Torvalds 		lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr, dev);
10391da177e4SLinus Torvalds 		if (!lladdr) {
1040675418d5SJoe Perches 			ND_PRINTK(2, warn,
1041675418d5SJoe Perches 				  "NA: invalid link-layer address length\n");
10423009f9aeSEric Dumazet 			return reason;
10431da177e4SLinus Torvalds 		}
10441da177e4SLinus Torvalds 	}
1045c346dca1SYOSHIFUJI Hideaki 	ifp = ipv6_get_ifaddr(dev_net(dev), &msg->target, dev, 1);
1046a18bc695SDaniel Lezcano 	if (ifp) {
1047bd015928SDaniel Walter 		if (skb->pkt_type != PACKET_LOOPBACK
1048bd015928SDaniel Walter 		    && (ifp->flags & IFA_F_TENTATIVE)) {
1049da13c59bSVishwanath Pai 				addrconf_dad_failure(skb, ifp);
10503009f9aeSEric Dumazet 				return reason;
10511da177e4SLinus Torvalds 		}
10521da177e4SLinus Torvalds 		/* What should we make now? The advertisement
10531da177e4SLinus Torvalds 		   is invalid, but ndisc specs say nothing
10541da177e4SLinus Torvalds 		   about it. It could be misconfiguration, or
10551da177e4SLinus Torvalds 		   an smart proxy agent tries to help us :-)
105624fc7b86SJan Sembera 
105724fc7b86SJan Sembera 		   We should not print the error if NA has been
105824fc7b86SJan Sembera 		   received from loopback - it is just our own
105924fc7b86SJan Sembera 		   unsolicited advertisement.
10601da177e4SLinus Torvalds 		 */
106124fc7b86SJan Sembera 		if (skb->pkt_type != PACKET_LOOPBACK)
1062675418d5SJoe Perches 			ND_PRINTK(1, warn,
1063da13c59bSVishwanath Pai 				  "NA: %pM advertised our address %pI6c on %s!\n",
1064da13c59bSVishwanath Pai 				  eth_hdr(skb)->h_source, &ifp->addr, ifp->idev->dev->name);
10651da177e4SLinus Torvalds 		in6_ifa_put(ifp);
10663009f9aeSEric Dumazet 		return reason;
10671da177e4SLinus Torvalds 	}
10683e0b8f52SArun Ajith S 
10693e0b8f52SArun Ajith S 	neigh = neigh_lookup(&nd_tbl, &msg->target, dev);
10703e0b8f52SArun Ajith S 
1071f9a2fb73SArun Ajith S 	/* RFC 9131 updates original Neighbour Discovery RFC 4861.
10723e0b8f52SArun Ajith S 	 * NAs with Target LL Address option without a corresponding
10733e0b8f52SArun Ajith S 	 * entry in the neighbour cache can now create a STALE neighbour
10743e0b8f52SArun Ajith S 	 * cache entry on routers.
1075f9a2fb73SArun Ajith S 	 *
10763e0b8f52SArun Ajith S 	 *   entry accept  fwding  solicited        behaviour
10773e0b8f52SArun Ajith S 	 * ------- ------  ------  ---------    ----------------------
10783e0b8f52SArun Ajith S 	 * present      X       X         0     Set state to STALE
10793e0b8f52SArun Ajith S 	 * present      X       X         1     Set state to REACHABLE
10803e0b8f52SArun Ajith S 	 *  absent      0       X         X     Do nothing
10813e0b8f52SArun Ajith S 	 *  absent      1       0         X     Do nothing
10823e0b8f52SArun Ajith S 	 *  absent      1       1         X     Add a new STALE entry
10833e0b8f52SArun Ajith S 	 *
1084f9a2fb73SArun Ajith S 	 * Note that we don't do a (daddr == all-routers-mcast) check.
1085f9a2fb73SArun Ajith S 	 */
10863e0b8f52SArun Ajith S 	new_state = msg->icmph.icmp6_solicited ? NUD_REACHABLE : NUD_STALE;
1087aaa5f515SJaehee Park 	if (!neigh && lladdr && idev && idev->cnf.forwarding) {
1088aaa5f515SJaehee Park 		if (accept_untracked_na(dev, saddr)) {
10893e0b8f52SArun Ajith S 			neigh = neigh_create(&nd_tbl, &msg->target, dev);
10903e0b8f52SArun Ajith S 			new_state = NUD_STALE;
10913e0b8f52SArun Ajith S 		}
1092aaa5f515SJaehee Park 	}
10931da177e4SLinus Torvalds 
10943e0b8f52SArun Ajith S 	if (neigh && !IS_ERR(neigh)) {
10951da177e4SLinus Torvalds 		u8 old_flags = neigh->flags;
109653b7997fSYOSHIFUJI Hideaki 		struct net *net = dev_net(dev);
10971da177e4SLinus Torvalds 
1098b071af52SEric Dumazet 		if (READ_ONCE(neigh->nud_state) & NUD_FAILED)
10991da177e4SLinus Torvalds 			goto out;
11001da177e4SLinus Torvalds 
11015f3e6e9eSVille Nuorvala 		/*
11025f3e6e9eSVille Nuorvala 		 * Don't update the neighbor cache entry on a proxy NA from
11035f3e6e9eSVille Nuorvala 		 * ourselves because either the proxied node is off link or it
11045f3e6e9eSVille Nuorvala 		 * has already sent a NA to us.
11055f3e6e9eSVille Nuorvala 		 */
11065f3e6e9eSVille Nuorvala 		if (lladdr && !memcmp(lladdr, dev->dev_addr, dev->addr_len) &&
110753b7997fSYOSHIFUJI Hideaki 		    net->ipv6.devconf_all->forwarding && net->ipv6.devconf_all->proxy_ndp &&
110853b7997fSYOSHIFUJI Hideaki 		    pneigh_lookup(&nd_tbl, net, &msg->target, dev, 0)) {
1109b20b6d97SNicolas Dichtel 			/* XXX: idev->cnf.proxy_ndp */
11105f3e6e9eSVille Nuorvala 			goto out;
1111fbea49e1SYOSHIFUJI Hideaki 		}
11125f3e6e9eSVille Nuorvala 
1113f997c55cSAlexander Aring 		ndisc_update(dev, neigh, lladdr,
11143e0b8f52SArun Ajith S 			     new_state,
11151da177e4SLinus Torvalds 			     NEIGH_UPDATE_F_WEAK_OVERRIDE|
11161da177e4SLinus Torvalds 			     (msg->icmph.icmp6_override ? NEIGH_UPDATE_F_OVERRIDE : 0)|
11171da177e4SLinus Torvalds 			     NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
1118f997c55cSAlexander Aring 			     (msg->icmph.icmp6_router ? NEIGH_UPDATE_F_ISROUTER : 0),
1119f997c55cSAlexander Aring 			     NDISC_NEIGHBOUR_ADVERTISEMENT, &ndopts);
11201da177e4SLinus Torvalds 
11211da177e4SLinus Torvalds 		if ((old_flags & ~neigh->flags) & NTF_ROUTER) {
11221da177e4SLinus Torvalds 			/*
11231da177e4SLinus Torvalds 			 * Change: router to host
11241da177e4SLinus Torvalds 			 */
1125be7a010dSDuan Jiong 			rt6_clean_tohost(dev_net(dev),  saddr);
11261da177e4SLinus Torvalds 		}
11273009f9aeSEric Dumazet 		reason = SKB_CONSUMED;
11281da177e4SLinus Torvalds out:
11291da177e4SLinus Torvalds 		neigh_release(neigh);
11301da177e4SLinus Torvalds 	}
11313009f9aeSEric Dumazet 	return reason;
11321da177e4SLinus Torvalds }
11331da177e4SLinus Torvalds 
ndisc_recv_rs(struct sk_buff * skb)1134243e37c6SEric Dumazet static enum skb_drop_reason ndisc_recv_rs(struct sk_buff *skb)
11351da177e4SLinus Torvalds {
11369c70220bSArnaldo Carvalho de Melo 	struct rs_msg *rs_msg = (struct rs_msg *)skb_transport_header(skb);
11371da177e4SLinus Torvalds 	unsigned long ndoptlen = skb->len - sizeof(*rs_msg);
11381da177e4SLinus Torvalds 	struct neighbour *neigh;
11391da177e4SLinus Torvalds 	struct inet6_dev *idev;
1140b71d1d42SEric Dumazet 	const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
11411da177e4SLinus Torvalds 	struct ndisc_options ndopts;
11421da177e4SLinus Torvalds 	u8 *lladdr = NULL;
1143243e37c6SEric Dumazet 	SKB_DR(reason);
11441da177e4SLinus Torvalds 
11451da177e4SLinus Torvalds 	if (skb->len < sizeof(*rs_msg))
1146243e37c6SEric Dumazet 		return SKB_DROP_REASON_PKT_TOO_SMALL;
11471da177e4SLinus Torvalds 
1148cfdf7647SEric Dumazet 	idev = __in6_dev_get(skb->dev);
11491da177e4SLinus Torvalds 	if (!idev) {
1150675418d5SJoe Perches 		ND_PRINTK(1, err, "RS: can't find in6 device\n");
1151243e37c6SEric Dumazet 		return reason;
11521da177e4SLinus Torvalds 	}
11531da177e4SLinus Torvalds 
11541da177e4SLinus Torvalds 	/* Don't accept RS if we're not in router mode */
11551da177e4SLinus Torvalds 	if (!idev->cnf.forwarding)
11561da177e4SLinus Torvalds 		goto out;
11571da177e4SLinus Torvalds 
11581da177e4SLinus Torvalds 	/*
11591da177e4SLinus Torvalds 	 * Don't update NCE if src = ::;
11601da177e4SLinus Torvalds 	 * this implies that the source node has no ip address assigned yet.
11611da177e4SLinus Torvalds 	 */
11621da177e4SLinus Torvalds 	if (ipv6_addr_any(saddr))
11631da177e4SLinus Torvalds 		goto out;
11641da177e4SLinus Torvalds 
11651da177e4SLinus Torvalds 	/* Parse ND options */
1166784d4477SEric Dumazet 	if (!ndisc_parse_options(skb->dev, rs_msg->opt, ndoptlen, &ndopts))
1167784d4477SEric Dumazet 		return SKB_DROP_REASON_IPV6_NDISC_BAD_OPTIONS;
11681da177e4SLinus Torvalds 
11691da177e4SLinus Torvalds 	if (ndopts.nd_opts_src_lladdr) {
11701da177e4SLinus Torvalds 		lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr,
11711da177e4SLinus Torvalds 					     skb->dev);
11721da177e4SLinus Torvalds 		if (!lladdr)
11731da177e4SLinus Torvalds 			goto out;
11741da177e4SLinus Torvalds 	}
11751da177e4SLinus Torvalds 
11761da177e4SLinus Torvalds 	neigh = __neigh_lookup(&nd_tbl, saddr, skb->dev, 1);
11771da177e4SLinus Torvalds 	if (neigh) {
1178f997c55cSAlexander Aring 		ndisc_update(skb->dev, neigh, lladdr, NUD_STALE,
11791da177e4SLinus Torvalds 			     NEIGH_UPDATE_F_WEAK_OVERRIDE|
11801da177e4SLinus Torvalds 			     NEIGH_UPDATE_F_OVERRIDE|
1181f997c55cSAlexander Aring 			     NEIGH_UPDATE_F_OVERRIDE_ISROUTER,
1182f997c55cSAlexander Aring 			     NDISC_ROUTER_SOLICITATION, &ndopts);
11831da177e4SLinus Torvalds 		neigh_release(neigh);
1184243e37c6SEric Dumazet 		reason = SKB_CONSUMED;
11851da177e4SLinus Torvalds 	}
11861da177e4SLinus Torvalds out:
1187243e37c6SEric Dumazet 	return reason;
11881da177e4SLinus Torvalds }
11891da177e4SLinus Torvalds 
ndisc_ra_useropt(struct sk_buff * ra,struct nd_opt_hdr * opt)119031910575SPierre Ynard static void ndisc_ra_useropt(struct sk_buff *ra, struct nd_opt_hdr *opt)
119131910575SPierre Ynard {
119231910575SPierre Ynard 	struct icmp6hdr *icmp6h = (struct icmp6hdr *)skb_transport_header(ra);
119331910575SPierre Ynard 	struct sk_buff *skb;
119431910575SPierre Ynard 	struct nlmsghdr *nlh;
119531910575SPierre Ynard 	struct nduseroptmsg *ndmsg;
1196c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(ra->dev);
119731910575SPierre Ynard 	int err;
119831910575SPierre Ynard 	int base_size = NLMSG_ALIGN(sizeof(struct nduseroptmsg)
119931910575SPierre Ynard 				    + (opt->nd_opt_len << 3));
120031910575SPierre Ynard 	size_t msg_size = base_size + nla_total_size(sizeof(struct in6_addr));
120131910575SPierre Ynard 
120231910575SPierre Ynard 	skb = nlmsg_new(msg_size, GFP_ATOMIC);
120363159f29SIan Morris 	if (!skb) {
120431910575SPierre Ynard 		err = -ENOBUFS;
120531910575SPierre Ynard 		goto errout;
120631910575SPierre Ynard 	}
120731910575SPierre Ynard 
120831910575SPierre Ynard 	nlh = nlmsg_put(skb, 0, 0, RTM_NEWNDUSEROPT, base_size, 0);
120963159f29SIan Morris 	if (!nlh) {
121031910575SPierre Ynard 		goto nla_put_failure;
121131910575SPierre Ynard 	}
121231910575SPierre Ynard 
121331910575SPierre Ynard 	ndmsg = nlmsg_data(nlh);
121431910575SPierre Ynard 	ndmsg->nduseropt_family = AF_INET6;
1215dbb2ed24SPierre Ynard 	ndmsg->nduseropt_ifindex = ra->dev->ifindex;
121631910575SPierre Ynard 	ndmsg->nduseropt_icmp_type = icmp6h->icmp6_type;
121731910575SPierre Ynard 	ndmsg->nduseropt_icmp_code = icmp6h->icmp6_code;
121831910575SPierre Ynard 	ndmsg->nduseropt_opts_len = opt->nd_opt_len << 3;
121931910575SPierre Ynard 
122031910575SPierre Ynard 	memcpy(ndmsg + 1, opt, opt->nd_opt_len << 3);
122131910575SPierre Ynard 
1222930345eaSJiri Benc 	if (nla_put_in6_addr(skb, NDUSEROPT_SRCADDR, &ipv6_hdr(ra)->saddr))
1223c78679e8SDavid S. Miller 		goto nla_put_failure;
122431910575SPierre Ynard 	nlmsg_end(skb, nlh);
122531910575SPierre Ynard 
12261ce85fe4SPablo Neira Ayuso 	rtnl_notify(skb, net, 0, RTNLGRP_ND_USEROPT, NULL, GFP_ATOMIC);
122731910575SPierre Ynard 	return;
122831910575SPierre Ynard 
122931910575SPierre Ynard nla_put_failure:
123031910575SPierre Ynard 	nlmsg_free(skb);
123131910575SPierre Ynard 	err = -EMSGSIZE;
123231910575SPierre Ynard errout:
1233a18bc695SDaniel Lezcano 	rtnl_set_sk_err(net, RTNLGRP_ND_USEROPT, err);
123431910575SPierre Ynard }
123531910575SPierre Ynard 
ndisc_router_discovery(struct sk_buff * skb)12362f326d9dSEric Dumazet static enum skb_drop_reason ndisc_router_discovery(struct sk_buff *skb)
12371da177e4SLinus Torvalds {
12389c70220bSArnaldo Carvalho de Melo 	struct ra_msg *ra_msg = (struct ra_msg *)skb_transport_header(skb);
12392f326d9dSEric Dumazet 	bool send_ifinfo_notify = false;
12401da177e4SLinus Torvalds 	struct neighbour *neigh = NULL;
12411da177e4SLinus Torvalds 	struct ndisc_options ndopts;
12422f326d9dSEric Dumazet 	struct fib6_info *rt = NULL;
12432f326d9dSEric Dumazet 	struct inet6_dev *in6_dev;
12442f326d9dSEric Dumazet 	u32 defrtr_usr_metric;
1245ebacaaa0SYOSHIFUJI Hideaki 	unsigned int pref = 0;
1246a394eef5SMarius Tomaschewski 	__u32 old_if_flags;
12472f326d9dSEric Dumazet 	struct net *net;
12482f326d9dSEric Dumazet 	SKB_DR(reason);
12492f326d9dSEric Dumazet 	int lifetime;
12502f326d9dSEric Dumazet 	int optlen;
12511da177e4SLinus Torvalds 
12521da177e4SLinus Torvalds 	__u8 *opt = (__u8 *)(ra_msg + 1);
12531da177e4SLinus Torvalds 
125429a3cad5SSimon Horman 	optlen = (skb_tail_pointer(skb) - skb_transport_header(skb)) -
125529a3cad5SSimon Horman 		sizeof(struct ra_msg);
12561da177e4SLinus Torvalds 
1257f2a762d8SBen Greear 	ND_PRINTK(2, info,
1258f2a762d8SBen Greear 		  "RA: %s, dev: %s\n",
1259f2a762d8SBen Greear 		  __func__, skb->dev->name);
12600660e03fSArnaldo Carvalho de Melo 	if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) {
1261675418d5SJoe Perches 		ND_PRINTK(2, warn, "RA: source address is not link-local\n");
12622f326d9dSEric Dumazet 		return reason;
12631da177e4SLinus Torvalds 	}
12642f326d9dSEric Dumazet 	if (optlen < 0)
12652f326d9dSEric Dumazet 		return SKB_DROP_REASON_PKT_TOO_SMALL;
12661da177e4SLinus Torvalds 
1267de357cc0SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_NDISC_NODETYPE
1268fadf6bf0STemplin, Fred L 	if (skb->ndisc_nodetype == NDISC_NODETYPE_HOST) {
1269675418d5SJoe Perches 		ND_PRINTK(2, warn, "RA: from host or unauthorized router\n");
12702f326d9dSEric Dumazet 		return reason;
1271fadf6bf0STemplin, Fred L 	}
1272de357cc0SYOSHIFUJI Hideaki #endif
1273fadf6bf0STemplin, Fred L 
1274cfdf7647SEric Dumazet 	in6_dev = __in6_dev_get(skb->dev);
127563159f29SIan Morris 	if (!in6_dev) {
1276675418d5SJoe Perches 		ND_PRINTK(0, err, "RA: can't find inet6 device for %s\n",
12771da177e4SLinus Torvalds 			  skb->dev->name);
12782f326d9dSEric Dumazet 		return reason;
12791da177e4SLinus Torvalds 	}
12801da177e4SLinus Torvalds 
1281784d4477SEric Dumazet 	if (!ndisc_parse_options(skb->dev, opt, optlen, &ndopts))
1282784d4477SEric Dumazet 		return SKB_DROP_REASON_IPV6_NDISC_BAD_OPTIONS;
12831da177e4SLinus Torvalds 
1284f2a762d8SBen Greear 	if (!ipv6_accept_ra(in6_dev)) {
1285f2a762d8SBen Greear 		ND_PRINTK(2, info,
1286f2a762d8SBen Greear 			  "RA: %s, did not accept ra for dev: %s\n",
1287f2a762d8SBen Greear 			  __func__, skb->dev->name);
128831ce8c71SDavid Ward 		goto skip_linkparms;
1289f2a762d8SBen Greear 	}
129031ce8c71SDavid Ward 
1291de357cc0SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_NDISC_NODETYPE
1292fadf6bf0STemplin, Fred L 	/* skip link-specific parameters from interior routers */
1293f2a762d8SBen Greear 	if (skb->ndisc_nodetype == NDISC_NODETYPE_NODEFAULT) {
1294f2a762d8SBen Greear 		ND_PRINTK(2, info,
1295f2a762d8SBen Greear 			  "RA: %s, nodetype is NODEFAULT, dev: %s\n",
1296f2a762d8SBen Greear 			  __func__, skb->dev->name);
1297fadf6bf0STemplin, Fred L 		goto skip_linkparms;
1298f2a762d8SBen Greear 	}
1299de357cc0SYOSHIFUJI Hideaki #endif
1300fadf6bf0STemplin, Fred L 
13011da177e4SLinus Torvalds 	if (in6_dev->if_flags & IF_RS_SENT) {
13021da177e4SLinus Torvalds 		/*
13031da177e4SLinus Torvalds 		 *	flag that an RA was received after an RS was sent
13041da177e4SLinus Torvalds 		 *	out on this interface.
13051da177e4SLinus Torvalds 		 */
13061da177e4SLinus Torvalds 		in6_dev->if_flags |= IF_RA_RCVD;
13071da177e4SLinus Torvalds 	}
13081da177e4SLinus Torvalds 
13091da177e4SLinus Torvalds 	/*
13101da177e4SLinus Torvalds 	 * Remember the managed/otherconf flags from most recently
13111da177e4SLinus Torvalds 	 * received RA message (RFC 2462) -- yoshfuji
13121da177e4SLinus Torvalds 	 */
1313a394eef5SMarius Tomaschewski 	old_if_flags = in6_dev->if_flags;
13141da177e4SLinus Torvalds 	in6_dev->if_flags = (in6_dev->if_flags & ~(IF_RA_MANAGED |
13151da177e4SLinus Torvalds 				IF_RA_OTHERCONF)) |
13161da177e4SLinus Torvalds 				(ra_msg->icmph.icmp6_addrconf_managed ?
13171da177e4SLinus Torvalds 					IF_RA_MANAGED : 0) |
13181da177e4SLinus Torvalds 				(ra_msg->icmph.icmp6_addrconf_other ?
13191da177e4SLinus Torvalds 					IF_RA_OTHERCONF : 0);
13201da177e4SLinus Torvalds 
1321a394eef5SMarius Tomaschewski 	if (old_if_flags != in6_dev->if_flags)
13222053aeb6SMarius Tomaschewski 		send_ifinfo_notify = true;
1323a394eef5SMarius Tomaschewski 
1324f2a762d8SBen Greear 	if (!in6_dev->cnf.accept_ra_defrtr) {
1325f2a762d8SBen Greear 		ND_PRINTK(2, info,
1326f2a762d8SBen Greear 			  "RA: %s, defrtr is false for dev: %s\n",
1327f2a762d8SBen Greear 			  __func__, skb->dev->name);
132865f5c7c1SYOSHIFUJI Hideaki 		goto skip_defrtr;
1329f2a762d8SBen Greear 	}
133065f5c7c1SYOSHIFUJI Hideaki 
13315027d54aSPatrick Rohr 	lifetime = ntohs(ra_msg->icmph.icmp6_rt_lifetime);
13325027d54aSPatrick Rohr 	if (lifetime != 0 && lifetime < in6_dev->cnf.accept_ra_min_lft) {
13335027d54aSPatrick Rohr 		ND_PRINTK(2, info,
13345027d54aSPatrick Rohr 			  "RA: router lifetime (%ds) is too short: %s\n",
13355027d54aSPatrick Rohr 			  lifetime, skb->dev->name);
13365027d54aSPatrick Rohr 		goto skip_defrtr;
13375027d54aSPatrick Rohr 	}
13385027d54aSPatrick Rohr 
1339d9333196SBen Greear 	/* Do not accept RA with source-addr found on local machine unless
1340d9333196SBen Greear 	 * accept_ra_from_local is set to true.
1341d9333196SBen Greear 	 */
1342afb1d4b5SDavid Ahern 	net = dev_net(in6_dev->dev);
1343b6428817SLi RongQing 	if (!in6_dev->cnf.accept_ra_from_local &&
1344afb1d4b5SDavid Ahern 	    ipv6_chk_addr(net, &ipv6_hdr(skb)->saddr, in6_dev->dev, 0)) {
1345f2a762d8SBen Greear 		ND_PRINTK(2, info,
1346d9333196SBen Greear 			  "RA from local address detected on dev: %s: default router ignored\n",
1347d9333196SBen Greear 			  skb->dev->name);
13489f56220fSAndreas Hofmeister 		goto skip_defrtr;
1349f2a762d8SBen Greear 	}
13509f56220fSAndreas Hofmeister 
1351ebacaaa0SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTER_PREF
1352ebacaaa0SYOSHIFUJI Hideaki 	pref = ra_msg->icmph.icmp6_router_pref;
1353ebacaaa0SYOSHIFUJI Hideaki 	/* 10b is handled as if it were 00b (medium) */
1354930d6ff2SYOSHIFUJI Hideaki 	if (pref == ICMPV6_ROUTER_PREF_INVALID ||
13556d5b78cdSYOSHIFUJI Hideaki 	    !in6_dev->cnf.accept_ra_rtr_pref)
1356ebacaaa0SYOSHIFUJI Hideaki 		pref = ICMPV6_ROUTER_PREF_MEDIUM;
1357ebacaaa0SYOSHIFUJI Hideaki #endif
1358f88d8ea6SDavid Ahern 	/* routes added from RAs do not use nexthop objects */
1359afb1d4b5SDavid Ahern 	rt = rt6_get_dflt_router(net, &ipv6_hdr(skb)->saddr, skb->dev);
1360eb857186SDavid S. Miller 	if (rt) {
13611cf844c7SDavid Ahern 		neigh = ip6_neigh_lookup(&rt->fib6_nh->fib_nh_gw6,
13621cf844c7SDavid Ahern 					 rt->fib6_nh->fib_nh_dev, NULL,
1363f8a1b43bSDavid Ahern 					  &ipv6_hdr(skb)->saddr);
1364eb857186SDavid S. Miller 		if (!neigh) {
1365675418d5SJoe Perches 			ND_PRINTK(0, err,
1366675418d5SJoe Perches 				  "RA: %s got default router without neighbour\n",
1367eb857186SDavid S. Miller 				  __func__);
136893531c67SDavid Ahern 			fib6_info_release(rt);
13692f326d9dSEric Dumazet 			return reason;
1370eb857186SDavid S. Miller 		}
1371eb857186SDavid S. Miller 	}
13726b2e04bcSPraveen Chaudhary 	/* Set default route metric as specified by user */
13736b2e04bcSPraveen Chaudhary 	defrtr_usr_metric = in6_dev->cnf.ra_defrtr_metric;
13746b2e04bcSPraveen Chaudhary 	/* delete the route if lifetime is 0 or if metric needs change */
13756b2e04bcSPraveen Chaudhary 	if (rt && (lifetime == 0 || rt->fib6_metric != defrtr_usr_metric)) {
137611dd74b3SRoopa Prabhu 		ip6_del_rt(net, rt, false);
13771da177e4SLinus Torvalds 		rt = NULL;
13781da177e4SLinus Torvalds 	}
13791da177e4SLinus Torvalds 
13806b2e04bcSPraveen Chaudhary 	ND_PRINTK(3, info, "RA: rt: %p  lifetime: %d, metric: %d, for dev: %s\n",
13816b2e04bcSPraveen Chaudhary 		  rt, lifetime, defrtr_usr_metric, skb->dev->name);
138263159f29SIan Morris 	if (!rt && lifetime) {
1383f2a762d8SBen Greear 		ND_PRINTK(3, info, "RA: adding default router\n");
13841da177e4SLinus Torvalds 
13857396ba87SXin Xiong 		if (neigh)
13867396ba87SXin Xiong 			neigh_release(neigh);
13877396ba87SXin Xiong 
1388afb1d4b5SDavid Ahern 		rt = rt6_add_dflt_router(net, &ipv6_hdr(skb)->saddr,
13896b2e04bcSPraveen Chaudhary 					 skb->dev, pref, defrtr_usr_metric);
139063159f29SIan Morris 		if (!rt) {
1391675418d5SJoe Perches 			ND_PRINTK(0, err,
1392675418d5SJoe Perches 				  "RA: %s failed to add default route\n",
13930dc47877SHarvey Harrison 				  __func__);
13942f326d9dSEric Dumazet 			return reason;
13951da177e4SLinus Torvalds 		}
13961da177e4SLinus Torvalds 
13971cf844c7SDavid Ahern 		neigh = ip6_neigh_lookup(&rt->fib6_nh->fib_nh_gw6,
13981cf844c7SDavid Ahern 					 rt->fib6_nh->fib_nh_dev, NULL,
1399f8a1b43bSDavid Ahern 					  &ipv6_hdr(skb)->saddr);
140063159f29SIan Morris 		if (!neigh) {
1401675418d5SJoe Perches 			ND_PRINTK(0, err,
1402675418d5SJoe Perches 				  "RA: %s got default router without neighbour\n",
14030dc47877SHarvey Harrison 				  __func__);
140493531c67SDavid Ahern 			fib6_info_release(rt);
14052f326d9dSEric Dumazet 			return reason;
14061da177e4SLinus Torvalds 		}
14071da177e4SLinus Torvalds 		neigh->flags |= NTF_ROUTER;
1408806c37ddSKalash Nainwal 	} else if (rt && IPV6_EXTRACT_PREF(rt->fib6_flags) != pref) {
1409806c37ddSKalash Nainwal 		struct nl_info nlinfo = {
1410806c37ddSKalash Nainwal 			.nl_net = net,
1411806c37ddSKalash Nainwal 		};
141293c2fb25SDavid Ahern 		rt->fib6_flags = (rt->fib6_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);
1413806c37ddSKalash Nainwal 		inet6_rt_notify(RTM_NEWROUTE, rt, &nlinfo, NLM_F_REPLACE);
14141da177e4SLinus Torvalds 	}
14151da177e4SLinus Torvalds 
14161da177e4SLinus Torvalds 	if (rt)
141714895687SDavid Ahern 		fib6_set_expires(rt, jiffies + (HZ * lifetime));
14188013d1d7SHangbin Liu 	if (in6_dev->cnf.accept_ra_min_hop_limit < 256 &&
14198013d1d7SHangbin Liu 	    ra_msg->icmph.icmp6_hop_limit) {
14208013d1d7SHangbin Liu 		if (in6_dev->cnf.accept_ra_min_hop_limit <= ra_msg->icmph.icmp6_hop_limit) {
14211da177e4SLinus Torvalds 			in6_dev->cnf.hop_limit = ra_msg->icmph.icmp6_hop_limit;
1422d4ead6b3SDavid Ahern 			fib6_metric_set(rt, RTAX_HOPLIMIT,
1423defb3519SDavid S. Miller 					ra_msg->icmph.icmp6_hop_limit);
14248013d1d7SHangbin Liu 		} else {
14258013d1d7SHangbin Liu 			ND_PRINTK(2, warn, "RA: Got route advertisement with lower hop_limit than minimum\n");
14268013d1d7SHangbin Liu 		}
14271da177e4SLinus Torvalds 	}
14281da177e4SLinus Torvalds 
142965f5c7c1SYOSHIFUJI Hideaki skip_defrtr:
143065f5c7c1SYOSHIFUJI Hideaki 
14311da177e4SLinus Torvalds 	/*
14321da177e4SLinus Torvalds 	 *	Update Reachable Time and Retrans Timer
14331da177e4SLinus Torvalds 	 */
14341da177e4SLinus Torvalds 
14351da177e4SLinus Torvalds 	if (in6_dev->nd_parms) {
14361da177e4SLinus Torvalds 		unsigned long rtime = ntohl(ra_msg->retrans_timer);
14371da177e4SLinus Torvalds 
14381da177e4SLinus Torvalds 		if (rtime && rtime/1000 < MAX_SCHEDULE_TIMEOUT/HZ) {
14391da177e4SLinus Torvalds 			rtime = (rtime*HZ)/1000;
144019e16d22SHangbin Liu 			if (rtime < HZ/100)
144119e16d22SHangbin Liu 				rtime = HZ/100;
14421f9248e5SJiri Pirko 			NEIGH_VAR_SET(in6_dev->nd_parms, RETRANS_TIME, rtime);
14431da177e4SLinus Torvalds 			in6_dev->tstamp = jiffies;
14442053aeb6SMarius Tomaschewski 			send_ifinfo_notify = true;
14451da177e4SLinus Torvalds 		}
14461da177e4SLinus Torvalds 
14471da177e4SLinus Torvalds 		rtime = ntohl(ra_msg->reachable_time);
14481da177e4SLinus Torvalds 		if (rtime && rtime/1000 < MAX_SCHEDULE_TIMEOUT/(3*HZ)) {
14491da177e4SLinus Torvalds 			rtime = (rtime*HZ)/1000;
14501da177e4SLinus Torvalds 
14511da177e4SLinus Torvalds 			if (rtime < HZ/10)
14521da177e4SLinus Torvalds 				rtime = HZ/10;
14531da177e4SLinus Torvalds 
14541f9248e5SJiri Pirko 			if (rtime != NEIGH_VAR(in6_dev->nd_parms, BASE_REACHABLE_TIME)) {
14551f9248e5SJiri Pirko 				NEIGH_VAR_SET(in6_dev->nd_parms,
14561f9248e5SJiri Pirko 					      BASE_REACHABLE_TIME, rtime);
14571f9248e5SJiri Pirko 				NEIGH_VAR_SET(in6_dev->nd_parms,
14581f9248e5SJiri Pirko 					      GC_STALETIME, 3 * rtime);
14591da177e4SLinus Torvalds 				in6_dev->nd_parms->reachable_time = neigh_rand_reach_time(rtime);
14601da177e4SLinus Torvalds 				in6_dev->tstamp = jiffies;
14612053aeb6SMarius Tomaschewski 				send_ifinfo_notify = true;
14622053aeb6SMarius Tomaschewski 			}
14632053aeb6SMarius Tomaschewski 		}
14642053aeb6SMarius Tomaschewski 	}
14652053aeb6SMarius Tomaschewski 
1466fadf6bf0STemplin, Fred L skip_linkparms:
1467fadf6bf0STemplin, Fred L 
14681da177e4SLinus Torvalds 	/*
14691da177e4SLinus Torvalds 	 *	Process options.
14701da177e4SLinus Torvalds 	 */
14711da177e4SLinus Torvalds 
14721da177e4SLinus Torvalds 	if (!neigh)
14730660e03fSArnaldo Carvalho de Melo 		neigh = __neigh_lookup(&nd_tbl, &ipv6_hdr(skb)->saddr,
14741da177e4SLinus Torvalds 				       skb->dev, 1);
14751da177e4SLinus Torvalds 	if (neigh) {
14761da177e4SLinus Torvalds 		u8 *lladdr = NULL;
14771da177e4SLinus Torvalds 		if (ndopts.nd_opts_src_lladdr) {
14781da177e4SLinus Torvalds 			lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr,
14791da177e4SLinus Torvalds 						     skb->dev);
14801da177e4SLinus Torvalds 			if (!lladdr) {
1481675418d5SJoe Perches 				ND_PRINTK(2, warn,
1482675418d5SJoe Perches 					  "RA: invalid link-layer address length\n");
14831da177e4SLinus Torvalds 				goto out;
14841da177e4SLinus Torvalds 			}
14851da177e4SLinus Torvalds 		}
1486f997c55cSAlexander Aring 		ndisc_update(skb->dev, neigh, lladdr, NUD_STALE,
14871da177e4SLinus Torvalds 			     NEIGH_UPDATE_F_WEAK_OVERRIDE|
14881da177e4SLinus Torvalds 			     NEIGH_UPDATE_F_OVERRIDE|
14891da177e4SLinus Torvalds 			     NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
1490f997c55cSAlexander Aring 			     NEIGH_UPDATE_F_ISROUTER,
1491f997c55cSAlexander Aring 			     NDISC_ROUTER_ADVERTISEMENT, &ndopts);
14922f326d9dSEric Dumazet 		reason = SKB_CONSUMED;
14931da177e4SLinus Torvalds 	}
14941da177e4SLinus Torvalds 
1495f2a762d8SBen Greear 	if (!ipv6_accept_ra(in6_dev)) {
1496f2a762d8SBen Greear 		ND_PRINTK(2, info,
1497f2a762d8SBen Greear 			  "RA: %s, accept_ra is false for dev: %s\n",
1498f2a762d8SBen Greear 			  __func__, skb->dev->name);
149931ce8c71SDavid Ward 		goto out;
1500f2a762d8SBen Greear 	}
150131ce8c71SDavid Ward 
150270ceb4f5SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_ROUTE_INFO
1503b6428817SLi RongQing 	if (!in6_dev->cnf.accept_ra_from_local &&
1504d9333196SBen Greear 	    ipv6_chk_addr(dev_net(in6_dev->dev), &ipv6_hdr(skb)->saddr,
1505c1a9a291SHannes Frederic Sowa 			  in6_dev->dev, 0)) {
1506f2a762d8SBen Greear 		ND_PRINTK(2, info,
1507d9333196SBen Greear 			  "RA from local address detected on dev: %s: router info ignored.\n",
1508d9333196SBen Greear 			  skb->dev->name);
15099f56220fSAndreas Hofmeister 		goto skip_routeinfo;
1510f2a762d8SBen Greear 	}
15119f56220fSAndreas Hofmeister 
151209c884d4SYOSHIFUJI Hideaki 	if (in6_dev->cnf.accept_ra_rtr_pref && ndopts.nd_opts_ri) {
151370ceb4f5SYOSHIFUJI Hideaki 		struct nd_opt_hdr *p;
151470ceb4f5SYOSHIFUJI Hideaki 		for (p = ndopts.nd_opts_ri;
151570ceb4f5SYOSHIFUJI Hideaki 		     p;
151670ceb4f5SYOSHIFUJI Hideaki 		     p = ndisc_next_option(p, ndopts.nd_opts_ri_end)) {
15176294e000SYOSHIFUJI Hideaki 			struct route_info *ri = (struct route_info *)p;
15186294e000SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_NDISC_NODETYPE
15196294e000SYOSHIFUJI Hideaki 			if (skb->ndisc_nodetype == NDISC_NODETYPE_NODEFAULT &&
15206294e000SYOSHIFUJI Hideaki 			    ri->prefix_len == 0)
15216294e000SYOSHIFUJI Hideaki 				continue;
15226294e000SYOSHIFUJI Hideaki #endif
152330e56918SDuan Jiong 			if (ri->prefix_len == 0 &&
152430e56918SDuan Jiong 			    !in6_dev->cnf.accept_ra_defrtr)
152530e56918SDuan Jiong 				continue;
15265027d54aSPatrick Rohr 			if (ri->lifetime != 0 &&
15275027d54aSPatrick Rohr 			    ntohl(ri->lifetime) < in6_dev->cnf.accept_ra_min_lft)
15285027d54aSPatrick Rohr 				continue;
1529bbea124bSJoel Scherpelz 			if (ri->prefix_len < in6_dev->cnf.accept_ra_rt_info_min_plen)
1530bbea124bSJoel Scherpelz 				continue;
15316294e000SYOSHIFUJI Hideaki 			if (ri->prefix_len > in6_dev->cnf.accept_ra_rt_info_max_plen)
153209c884d4SYOSHIFUJI Hideaki 				continue;
153370ceb4f5SYOSHIFUJI Hideaki 			rt6_route_rcv(skb->dev, (u8 *)p, (p->nd_opt_len) << 3,
15340660e03fSArnaldo Carvalho de Melo 				      &ipv6_hdr(skb)->saddr);
153570ceb4f5SYOSHIFUJI Hideaki 		}
153670ceb4f5SYOSHIFUJI Hideaki 	}
15379f56220fSAndreas Hofmeister 
15389f56220fSAndreas Hofmeister skip_routeinfo:
153970ceb4f5SYOSHIFUJI Hideaki #endif
154070ceb4f5SYOSHIFUJI Hideaki 
1541de357cc0SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_NDISC_NODETYPE
1542fadf6bf0STemplin, Fred L 	/* skip link-specific ndopts from interior routers */
1543f2a762d8SBen Greear 	if (skb->ndisc_nodetype == NDISC_NODETYPE_NODEFAULT) {
1544f2a762d8SBen Greear 		ND_PRINTK(2, info,
1545f2a762d8SBen Greear 			  "RA: %s, nodetype is NODEFAULT (interior routes), dev: %s\n",
1546f2a762d8SBen Greear 			  __func__, skb->dev->name);
1547fadf6bf0STemplin, Fred L 		goto out;
1548f2a762d8SBen Greear 	}
1549de357cc0SYOSHIFUJI Hideaki #endif
1550fadf6bf0STemplin, Fred L 
1551c4fd30ebSYOSHIFUJI Hideaki 	if (in6_dev->cnf.accept_ra_pinfo && ndopts.nd_opts_pi) {
15521da177e4SLinus Torvalds 		struct nd_opt_hdr *p;
15531da177e4SLinus Torvalds 		for (p = ndopts.nd_opts_pi;
15541da177e4SLinus Torvalds 		     p;
15551da177e4SLinus Torvalds 		     p = ndisc_next_option(p, ndopts.nd_opts_pi_end)) {
1556e6bff995SNeil Horman 			addrconf_prefix_rcv(skb->dev, (u8 *)p,
1557e6bff995SNeil Horman 					    (p->nd_opt_len) << 3,
1558e6bff995SNeil Horman 					    ndopts.nd_opts_src_lladdr != NULL);
15591da177e4SLinus Torvalds 		}
15601da177e4SLinus Torvalds 	}
15611da177e4SLinus Torvalds 
1562c2943f14SHarout Hedeshian 	if (ndopts.nd_opts_mtu && in6_dev->cnf.accept_ra_mtu) {
1563e69a4adcSAl Viro 		__be32 n;
15641da177e4SLinus Torvalds 		u32 mtu;
15651da177e4SLinus Torvalds 
1566e69a4adcSAl Viro 		memcpy(&n, ((u8 *)(ndopts.nd_opts_mtu+1))+2, sizeof(mtu));
1567e69a4adcSAl Viro 		mtu = ntohl(n);
15681da177e4SLinus Torvalds 
156949b99da2SRocco Yue 		if (in6_dev->ra_mtu != mtu) {
157049b99da2SRocco Yue 			in6_dev->ra_mtu = mtu;
157149b99da2SRocco Yue 			send_ifinfo_notify = true;
157249b99da2SRocco Yue 		}
157349b99da2SRocco Yue 
15741da177e4SLinus Torvalds 		if (mtu < IPV6_MIN_MTU || mtu > skb->dev->mtu) {
1575675418d5SJoe Perches 			ND_PRINTK(2, warn, "RA: invalid mtu: %d\n", mtu);
15761da177e4SLinus Torvalds 		} else if (in6_dev->cnf.mtu6 != mtu) {
15771da177e4SLinus Torvalds 			in6_dev->cnf.mtu6 = mtu;
1578d4ead6b3SDavid Ahern 			fib6_metric_set(rt, RTAX_MTU, mtu);
15791da177e4SLinus Torvalds 			rt6_mtu_change(skb->dev, mtu);
15801da177e4SLinus Torvalds 		}
15811da177e4SLinus Torvalds 	}
15821da177e4SLinus Torvalds 
158331910575SPierre Ynard 	if (ndopts.nd_useropts) {
158461cf46adSYOSHIFUJI Hideaki 		struct nd_opt_hdr *p;
158561cf46adSYOSHIFUJI Hideaki 		for (p = ndopts.nd_useropts;
158661cf46adSYOSHIFUJI Hideaki 		     p;
1587f997c55cSAlexander Aring 		     p = ndisc_next_useropt(skb->dev, p,
1588f997c55cSAlexander Aring 					    ndopts.nd_useropts_end)) {
158961cf46adSYOSHIFUJI Hideaki 			ndisc_ra_useropt(skb, p);
159031910575SPierre Ynard 		}
159131910575SPierre Ynard 	}
159231910575SPierre Ynard 
15931da177e4SLinus Torvalds 	if (ndopts.nd_opts_tgt_lladdr || ndopts.nd_opts_rh) {
1594675418d5SJoe Perches 		ND_PRINTK(2, warn, "RA: invalid RA options\n");
15951da177e4SLinus Torvalds 	}
15961da177e4SLinus Torvalds out:
159749b99da2SRocco Yue 	/* Send a notify if RA changed managed/otherconf flags or
159849b99da2SRocco Yue 	 * timer settings or ra_mtu value
159949b99da2SRocco Yue 	 */
160049b99da2SRocco Yue 	if (send_ifinfo_notify)
160149b99da2SRocco Yue 		inet6_ifinfo_notify(RTM_NEWLINK, in6_dev);
160249b99da2SRocco Yue 
160393531c67SDavid Ahern 	fib6_info_release(rt);
1604eb857186SDavid S. Miller 	if (neigh)
16051da177e4SLinus Torvalds 		neigh_release(neigh);
16062f326d9dSEric Dumazet 	return reason;
16071da177e4SLinus Torvalds }
16081da177e4SLinus Torvalds 
ndisc_redirect_rcv(struct sk_buff * skb)1609ec993edfSEric Dumazet static enum skb_drop_reason ndisc_redirect_rcv(struct sk_buff *skb)
16101da177e4SLinus Torvalds {
1611093d04d4SDuan Jiong 	struct rd_msg *msg = (struct rd_msg *)skb_transport_header(skb);
161229a3cad5SSimon Horman 	u32 ndoptlen = skb_tail_pointer(skb) - (skb_transport_header(skb) +
1613093d04d4SDuan Jiong 				    offsetof(struct rd_msg, opt));
1614ec993edfSEric Dumazet 	struct ndisc_options ndopts;
1615ec993edfSEric Dumazet 	SKB_DR(reason);
1616ec993edfSEric Dumazet 	u8 *hdr;
1617093d04d4SDuan Jiong 
1618de357cc0SYOSHIFUJI Hideaki #ifdef CONFIG_IPV6_NDISC_NODETYPE
1619fadf6bf0STemplin, Fred L 	switch (skb->ndisc_nodetype) {
1620fadf6bf0STemplin, Fred L 	case NDISC_NODETYPE_HOST:
1621fadf6bf0STemplin, Fred L 	case NDISC_NODETYPE_NODEFAULT:
1622675418d5SJoe Perches 		ND_PRINTK(2, warn,
1623675418d5SJoe Perches 			  "Redirect: from host or unauthorized router\n");
1624ec993edfSEric Dumazet 		return reason;
1625fadf6bf0STemplin, Fred L 	}
1626de357cc0SYOSHIFUJI Hideaki #endif
1627fadf6bf0STemplin, Fred L 
16280660e03fSArnaldo Carvalho de Melo 	if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) {
1629675418d5SJoe Perches 		ND_PRINTK(2, warn,
1630675418d5SJoe Perches 			  "Redirect: source address is not link-local\n");
1631ec993edfSEric Dumazet 		return reason;
16321da177e4SLinus Torvalds 	}
16331da177e4SLinus Torvalds 
1634f997c55cSAlexander Aring 	if (!ndisc_parse_options(skb->dev, msg->opt, ndoptlen, &ndopts))
1635784d4477SEric Dumazet 		return SKB_DROP_REASON_IPV6_NDISC_BAD_OPTIONS;
1636093d04d4SDuan Jiong 
1637c92a59ecSDuan Jiong 	if (!ndopts.nd_opts_rh) {
1638b55b76b2SDuan Jiong 		ip6_redirect_no_header(skb, dev_net(skb->dev),
1639d456336dSMaciej Żenczykowski 					skb->dev->ifindex);
1640ec993edfSEric Dumazet 		return reason;
1641c92a59ecSDuan Jiong 	}
1642093d04d4SDuan Jiong 
1643093d04d4SDuan Jiong 	hdr = (u8 *)ndopts.nd_opts_rh;
1644093d04d4SDuan Jiong 	hdr += 8;
1645093d04d4SDuan Jiong 	if (!pskb_pull(skb, hdr - skb_transport_header(skb)))
1646ec993edfSEric Dumazet 		return SKB_DROP_REASON_PKT_TOO_SMALL;
1647093d04d4SDuan Jiong 
1648ec993edfSEric Dumazet 	return icmpv6_notify(skb, NDISC_REDIRECT, 0, 0);
16491da177e4SLinus Torvalds }
16501da177e4SLinus Torvalds 
ndisc_fill_redirect_hdr_option(struct sk_buff * skb,struct sk_buff * orig_skb,int rd_len)16515f5a0115SYOSHIFUJI Hideaki / 吉藤英明 static void ndisc_fill_redirect_hdr_option(struct sk_buff *skb,
16525f5a0115SYOSHIFUJI Hideaki / 吉藤英明 					   struct sk_buff *orig_skb,
16539c86dafeSYOSHIFUJI Hideaki / 吉藤英明 					   int rd_len)
16549c86dafeSYOSHIFUJI Hideaki / 吉藤英明 {
16555f5a0115SYOSHIFUJI Hideaki / 吉藤英明 	u8 *opt = skb_put(skb, rd_len);
16565f5a0115SYOSHIFUJI Hideaki / 吉藤英明 
16579c86dafeSYOSHIFUJI Hideaki / 吉藤英明 	memset(opt, 0, 8);
16589c86dafeSYOSHIFUJI Hideaki / 吉藤英明 	*(opt++) = ND_OPT_REDIRECT_HDR;
16599c86dafeSYOSHIFUJI Hideaki / 吉藤英明 	*(opt++) = (rd_len >> 3);
16609c86dafeSYOSHIFUJI Hideaki / 吉藤英明 	opt += 6;
16619c86dafeSYOSHIFUJI Hideaki / 吉藤英明 
16629f62c15fSLorenzo Bianconi 	skb_copy_bits(orig_skb, skb_network_offset(orig_skb), opt,
16639f62c15fSLorenzo Bianconi 		      rd_len - 8);
16649c86dafeSYOSHIFUJI Hideaki / 吉藤英明 }
16659c86dafeSYOSHIFUJI Hideaki / 吉藤英明 
ndisc_send_redirect(struct sk_buff * skb,const struct in6_addr * target)16664991969aSDavid S. Miller void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target)
16671da177e4SLinus Torvalds {
16681762f7e8SDaniel Lezcano 	struct net_device *dev = skb->dev;
1669c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(dev);
16701762f7e8SDaniel Lezcano 	struct sock *sk = net->ipv6.ndisc_sk;
16712ce13576SYOSHIFUJI Hideaki / 吉藤英明 	int optlen = 0;
1672fbfe95a4SDavid S. Miller 	struct inet_peer *peer;
16731da177e4SLinus Torvalds 	struct sk_buff *buff;
167471bcdba0SYOSHIFUJI Hideaki / 吉藤英明 	struct rd_msg *msg;
16751da177e4SLinus Torvalds 	struct in6_addr saddr_buf;
16761da177e4SLinus Torvalds 	struct rt6_info *rt;
16771da177e4SLinus Torvalds 	struct dst_entry *dst;
16784c9483b2SDavid S. Miller 	struct flowi6 fl6;
16791da177e4SLinus Torvalds 	int rd_len;
1680f997c55cSAlexander Aring 	u8 ha_buf[MAX_ADDR_LEN], *ha = NULL,
1681f997c55cSAlexander Aring 	   ops_data_buf[NDISC_OPS_REDIRECT_DATA_SPACE], *ops_data = NULL;
16821d861aa4SDavid S. Miller 	bool ret;
16831da177e4SLinus Torvalds 
16842f17becfSStephen Suryaputra 	if (netif_is_l3_master(skb->dev)) {
168535b3f615SEric Dumazet 		dev = dev_get_by_index_rcu(dev_net(skb->dev), IPCB(skb)->iif);
16862f17becfSStephen Suryaputra 		if (!dev)
16872f17becfSStephen Suryaputra 			return;
16882f17becfSStephen Suryaputra 	}
16892f17becfSStephen Suryaputra 
169095c385b4SNeil Horman 	if (ipv6_get_lladdr(dev, &saddr_buf, IFA_F_TENTATIVE)) {
1691675418d5SJoe Perches 		ND_PRINTK(2, warn, "Redirect: no link-local address on %s\n",
16921da177e4SLinus Torvalds 			  dev->name);
16931da177e4SLinus Torvalds 		return;
16941da177e4SLinus Torvalds 	}
16951da177e4SLinus Torvalds 
16960660e03fSArnaldo Carvalho de Melo 	if (!ipv6_addr_equal(&ipv6_hdr(skb)->daddr, target) &&
1697bf0b48dfSBrian Haley 	    ipv6_addr_type(target) != (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) {
1698675418d5SJoe Perches 		ND_PRINTK(2, warn,
1699675418d5SJoe Perches 			  "Redirect: target address is not link-local unicast\n");
170029556526SLi Yewang 		return;
170129556526SLi Yewang 	}
170229556526SLi Yewang 
17034c9483b2SDavid S. Miller 	icmpv6_flow_init(sk, &fl6, NDISC_REDIRECT,
1704e0d56fddSDavid Ahern 			 &saddr_buf, &ipv6_hdr(skb)->saddr, dev->ifindex);
17051da177e4SLinus Torvalds 
17064c9483b2SDavid S. Miller 	dst = ip6_route_output(net, NULL, &fl6);
17075095d64dSRongQing.Li 	if (dst->error) {
17085095d64dSRongQing.Li 		dst_release(dst);
17091da177e4SLinus Torvalds 		return;
17105095d64dSRongQing.Li 	}
17114c9483b2SDavid S. Miller 	dst = xfrm_lookup(net, dst, flowi6_to_flowi(&fl6), NULL, 0);
1712452edd59SDavid S. Miller 	if (IS_ERR(dst))
17131da177e4SLinus Torvalds 		return;
17141da177e4SLinus Torvalds 
1715797a4c1fSEric Dumazet 	rt = dst_rt6_info(dst);
17161da177e4SLinus Torvalds 
17171da177e4SLinus Torvalds 	if (rt->rt6i_flags & RTF_GATEWAY) {
1718675418d5SJoe Perches 		ND_PRINTK(2, warn,
1719675418d5SJoe Perches 			  "Redirect: destination is not a neighbour\n");
1720d73f0801SIlpo Järvinen 		goto release;
17211da177e4SLinus Torvalds 	}
1722280fb099SEric Dumazet 
1723280fb099SEric Dumazet 	rcu_read_lock();
1724fdaa6b3cSEric Dumazet 	peer = inet_getpeer_v6(net->ipv6.peers, &ipv6_hdr(skb)->saddr);
17251d861aa4SDavid S. Miller 	ret = inet_peer_xrlim_allow(peer, 1*HZ);
1726280fb099SEric Dumazet 	rcu_read_unlock();
1727280fb099SEric Dumazet 
17281d861aa4SDavid S. Miller 	if (!ret)
1729d73f0801SIlpo Järvinen 		goto release;
17301da177e4SLinus Torvalds 
17311da177e4SLinus Torvalds 	if (dev->addr_len) {
17324991969aSDavid S. Miller 		struct neighbour *neigh = dst_neigh_lookup(skb_dst(skb), target);
17334991969aSDavid S. Miller 		if (!neigh) {
1734675418d5SJoe Perches 			ND_PRINTK(2, warn,
1735675418d5SJoe Perches 				  "Redirect: no neigh for target address\n");
17364991969aSDavid S. Miller 			goto release;
17374991969aSDavid S. Miller 		}
17384991969aSDavid S. Miller 
17391da177e4SLinus Torvalds 		read_lock_bh(&neigh->lock);
17401da177e4SLinus Torvalds 		if (neigh->nud_state & NUD_VALID) {
17411da177e4SLinus Torvalds 			memcpy(ha_buf, neigh->ha, dev->addr_len);
17421da177e4SLinus Torvalds 			read_unlock_bh(&neigh->lock);
17431da177e4SLinus Torvalds 			ha = ha_buf;
1744f997c55cSAlexander Aring 			optlen += ndisc_redirect_opt_addr_space(dev, neigh,
1745f997c55cSAlexander Aring 								ops_data_buf,
1746f997c55cSAlexander Aring 								&ops_data);
17471da177e4SLinus Torvalds 		} else
17481da177e4SLinus Torvalds 			read_unlock_bh(&neigh->lock);
17494991969aSDavid S. Miller 
17504991969aSDavid S. Miller 		neigh_release(neigh);
17511da177e4SLinus Torvalds 	}
17521da177e4SLinus Torvalds 
17531da177e4SLinus Torvalds 	rd_len = min_t(unsigned int,
17542ce13576SYOSHIFUJI Hideaki / 吉藤英明 		       IPV6_MIN_MTU - sizeof(struct ipv6hdr) - sizeof(*msg) - optlen,
17552ce13576SYOSHIFUJI Hideaki / 吉藤英明 		       skb->len + 8);
17561da177e4SLinus Torvalds 	rd_len &= ~0x7;
17572ce13576SYOSHIFUJI Hideaki / 吉藤英明 	optlen += rd_len;
17581da177e4SLinus Torvalds 
17592ce13576SYOSHIFUJI Hideaki / 吉藤英明 	buff = ndisc_alloc_skb(dev, sizeof(*msg) + optlen);
1760de09334bSYOSHIFUJI Hideaki / 吉藤英明 	if (!buff)
1761d73f0801SIlpo Järvinen 		goto release;
17621da177e4SLinus Torvalds 
17634df864c1SJohannes Berg 	msg = skb_put(buff, sizeof(*msg));
17644d5c152eSYOSHIFUJI Hideaki / 吉藤英明 	*msg = (struct rd_msg) {
17654d5c152eSYOSHIFUJI Hideaki / 吉藤英明 		.icmph = {
17664d5c152eSYOSHIFUJI Hideaki / 吉藤英明 			.icmp6_type = NDISC_REDIRECT,
17674d5c152eSYOSHIFUJI Hideaki / 吉藤英明 		},
17684d5c152eSYOSHIFUJI Hideaki / 吉藤英明 		.target = *target,
17694d5c152eSYOSHIFUJI Hideaki / 吉藤英明 		.dest = ipv6_hdr(skb)->daddr,
17704d5c152eSYOSHIFUJI Hideaki / 吉藤英明 	};
17711da177e4SLinus Torvalds 
17721da177e4SLinus Torvalds 	/*
17731da177e4SLinus Torvalds 	 *	include target_address option
17741da177e4SLinus Torvalds 	 */
17751da177e4SLinus Torvalds 
17761da177e4SLinus Torvalds 	if (ha)
1777f997c55cSAlexander Aring 		ndisc_fill_redirect_addr_option(buff, ha, ops_data);
17781da177e4SLinus Torvalds 
17791da177e4SLinus Torvalds 	/*
17801da177e4SLinus Torvalds 	 *	build redirect option and copy skb over to the new packet.
17811da177e4SLinus Torvalds 	 */
17821da177e4SLinus Torvalds 
17839c86dafeSYOSHIFUJI Hideaki / 吉藤英明 	if (rd_len)
17845f5a0115SYOSHIFUJI Hideaki / 吉藤英明 		ndisc_fill_redirect_hdr_option(buff, skb, rd_len);
17851da177e4SLinus Torvalds 
1786adf30907SEric Dumazet 	skb_dst_set(buff, dst);
1787f4de84c6SYOSHIFUJI Hideaki / 吉藤英明 	ndisc_send_skb(buff, &ipv6_hdr(skb)->saddr, &saddr_buf);
1788d73f0801SIlpo Järvinen 	return;
1789d73f0801SIlpo Järvinen 
1790d73f0801SIlpo Järvinen release:
1791d73f0801SIlpo Järvinen 	dst_release(dst);
17921da177e4SLinus Torvalds }
17931da177e4SLinus Torvalds 
pndisc_redo(struct sk_buff * skb)17941da177e4SLinus Torvalds static void pndisc_redo(struct sk_buff *skb)
17951da177e4SLinus Torvalds {
17967c9c8913SEric Dumazet 	enum skb_drop_reason reason = ndisc_recv_ns(skb);
17977c9c8913SEric Dumazet 
17987c9c8913SEric Dumazet 	kfree_skb_reason(skb, reason);
17991da177e4SLinus Torvalds }
18001da177e4SLinus Torvalds 
ndisc_is_multicast(const void * pkey)18018cf8821eSJeff Dike static int ndisc_is_multicast(const void *pkey)
18028cf8821eSJeff Dike {
18038cf8821eSJeff Dike 	return ipv6_addr_is_multicast((struct in6_addr *)pkey);
18048cf8821eSJeff Dike }
18058cf8821eSJeff Dike 
ndisc_suppress_frag_ndisc(struct sk_buff * skb)1806b800c3b9SHannes Frederic Sowa static bool ndisc_suppress_frag_ndisc(struct sk_buff *skb)
1807b800c3b9SHannes Frederic Sowa {
1808b800c3b9SHannes Frederic Sowa 	struct inet6_dev *idev = __in6_dev_get(skb->dev);
1809b800c3b9SHannes Frederic Sowa 
1810b800c3b9SHannes Frederic Sowa 	if (!idev)
1811b800c3b9SHannes Frederic Sowa 		return true;
1812b800c3b9SHannes Frederic Sowa 	if (IP6CB(skb)->flags & IP6SKB_FRAGMENTED &&
1813b800c3b9SHannes Frederic Sowa 	    idev->cnf.suppress_frag_ndisc) {
1814b800c3b9SHannes Frederic Sowa 		net_warn_ratelimited("Received fragmented ndisc packet. Carefully consider disabling suppress_frag_ndisc.\n");
1815b800c3b9SHannes Frederic Sowa 		return true;
1816b800c3b9SHannes Frederic Sowa 	}
1817b800c3b9SHannes Frederic Sowa 	return false;
1818b800c3b9SHannes Frederic Sowa }
1819b800c3b9SHannes Frederic Sowa 
ndisc_rcv(struct sk_buff * skb)1820545dbcd1SEric Dumazet enum skb_drop_reason ndisc_rcv(struct sk_buff *skb)
18211da177e4SLinus Torvalds {
18221da177e4SLinus Torvalds 	struct nd_msg *msg;
1823545dbcd1SEric Dumazet 	SKB_DR(reason);
18241da177e4SLinus Torvalds 
1825b800c3b9SHannes Frederic Sowa 	if (ndisc_suppress_frag_ndisc(skb))
1826545dbcd1SEric Dumazet 		return SKB_DROP_REASON_IPV6_NDISC_FRAG;
1827b800c3b9SHannes Frederic Sowa 
18286bce6b4eSYOSHIFUJI Hideaki / 吉藤英明 	if (skb_linearize(skb))
1829545dbcd1SEric Dumazet 		return SKB_DROP_REASON_NOMEM;
18301da177e4SLinus Torvalds 
18319c70220bSArnaldo Carvalho de Melo 	msg = (struct nd_msg *)skb_transport_header(skb);
18321da177e4SLinus Torvalds 
18339c70220bSArnaldo Carvalho de Melo 	__skb_push(skb, skb->data - skb_transport_header(skb));
18341da177e4SLinus Torvalds 
18350660e03fSArnaldo Carvalho de Melo 	if (ipv6_hdr(skb)->hop_limit != 255) {
1836675418d5SJoe Perches 		ND_PRINTK(2, warn, "NDISC: invalid hop-limit: %d\n",
18370660e03fSArnaldo Carvalho de Melo 			  ipv6_hdr(skb)->hop_limit);
1838545dbcd1SEric Dumazet 		return SKB_DROP_REASON_IPV6_NDISC_HOP_LIMIT;
18391da177e4SLinus Torvalds 	}
18401da177e4SLinus Torvalds 
18411da177e4SLinus Torvalds 	if (msg->icmph.icmp6_code != 0) {
1842675418d5SJoe Perches 		ND_PRINTK(2, warn, "NDISC: invalid ICMPv6 code: %d\n",
18431da177e4SLinus Torvalds 			  msg->icmph.icmp6_code);
1844545dbcd1SEric Dumazet 		return SKB_DROP_REASON_IPV6_NDISC_BAD_CODE;
18451da177e4SLinus Torvalds 	}
18461da177e4SLinus Torvalds 
18471da177e4SLinus Torvalds 	switch (msg->icmph.icmp6_type) {
18481da177e4SLinus Torvalds 	case NDISC_NEIGHBOUR_SOLICITATION:
1849ee1abcf6SStefano Brivio 		memset(NEIGH_CB(skb), 0, sizeof(struct neighbour_cb));
18507c9c8913SEric Dumazet 		reason = ndisc_recv_ns(skb);
18511da177e4SLinus Torvalds 		break;
18521da177e4SLinus Torvalds 
18531da177e4SLinus Torvalds 	case NDISC_NEIGHBOUR_ADVERTISEMENT:
18543009f9aeSEric Dumazet 		reason = ndisc_recv_na(skb);
18551da177e4SLinus Torvalds 		break;
18561da177e4SLinus Torvalds 
18571da177e4SLinus Torvalds 	case NDISC_ROUTER_SOLICITATION:
1858243e37c6SEric Dumazet 		reason = ndisc_recv_rs(skb);
18591da177e4SLinus Torvalds 		break;
18601da177e4SLinus Torvalds 
18611da177e4SLinus Torvalds 	case NDISC_ROUTER_ADVERTISEMENT:
18622f326d9dSEric Dumazet 		reason = ndisc_router_discovery(skb);
18631da177e4SLinus Torvalds 		break;
18641da177e4SLinus Torvalds 
18651da177e4SLinus Torvalds 	case NDISC_REDIRECT:
1866ec993edfSEric Dumazet 		reason = ndisc_redirect_rcv(skb);
18671da177e4SLinus Torvalds 		break;
18683ff50b79SStephen Hemminger 	}
18691da177e4SLinus Torvalds 
1870545dbcd1SEric Dumazet 	return reason;
18711da177e4SLinus Torvalds }
18721da177e4SLinus Torvalds 
ndisc_netdev_event(struct notifier_block * this,unsigned long event,void * ptr)18731da177e4SLinus Torvalds static int ndisc_netdev_event(struct notifier_block *this, unsigned long event, void *ptr)
18741da177e4SLinus Torvalds {
1875351638e7SJiri Pirko 	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
1876c8507fb2SEric Dumazet 	struct netdev_notifier_change_info *change_info;
1877c346dca1SYOSHIFUJI Hideaki 	struct net *net = dev_net(dev);
18785cb04436SHannes Frederic Sowa 	struct inet6_dev *idev;
187918ac597aSJames Prestwood 	bool evict_nocarrier;
18801da177e4SLinus Torvalds 
18811da177e4SLinus Torvalds 	switch (event) {
18821da177e4SLinus Torvalds 	case NETDEV_CHANGEADDR:
18831da177e4SLinus Torvalds 		neigh_changeaddr(&nd_tbl, dev);
18842ac3ac8fSMichal Kubeček 		fib6_run_gc(0, net, false);
1885a8eceea8SJoe Perches 		fallthrough;
18864a6e3c5dSDavid Ahern 	case NETDEV_UP:
18875cb04436SHannes Frederic Sowa 		idev = in6_dev_get(dev);
18885cb04436SHannes Frederic Sowa 		if (!idev)
18895cb04436SHannes Frederic Sowa 			break;
1890fc1f8f4fSDavid Ahern 		if (idev->cnf.ndisc_notify ||
1891fc1f8f4fSDavid Ahern 		    net->ipv6.devconf_all->ndisc_notify)
18925cb04436SHannes Frederic Sowa 			ndisc_send_unsol_na(dev);
18935cb04436SHannes Frederic Sowa 		in6_dev_put(idev);
18941da177e4SLinus Torvalds 		break;
1895c8507fb2SEric Dumazet 	case NETDEV_CHANGE:
189618ac597aSJames Prestwood 		idev = in6_dev_get(dev);
189718ac597aSJames Prestwood 		if (!idev)
189818ac597aSJames Prestwood 			evict_nocarrier = true;
189918ac597aSJames Prestwood 		else {
190018ac597aSJames Prestwood 			evict_nocarrier = idev->cnf.ndisc_evict_nocarrier &&
190118ac597aSJames Prestwood 					  net->ipv6.devconf_all->ndisc_evict_nocarrier;
190218ac597aSJames Prestwood 			in6_dev_put(idev);
190318ac597aSJames Prestwood 		}
190418ac597aSJames Prestwood 
1905c8507fb2SEric Dumazet 		change_info = ptr;
1906c8507fb2SEric Dumazet 		if (change_info->flags_changed & IFF_NOARP)
1907c8507fb2SEric Dumazet 			neigh_changeaddr(&nd_tbl, dev);
190818ac597aSJames Prestwood 		if (evict_nocarrier && !netif_carrier_ok(dev))
1909859bd2efSDavid Ahern 			neigh_carrier_down(&nd_tbl, dev);
1910c8507fb2SEric Dumazet 		break;
19111da177e4SLinus Torvalds 	case NETDEV_DOWN:
19121da177e4SLinus Torvalds 		neigh_ifdown(&nd_tbl, dev);
19132ac3ac8fSMichal Kubeček 		fib6_run_gc(0, net, false);
19141da177e4SLinus Torvalds 		break;
1915f47b9464SBen Hutchings 	case NETDEV_NOTIFY_PEERS:
1916f47b9464SBen Hutchings 		ndisc_send_unsol_na(dev);
1917f47b9464SBen Hutchings 		break;
19181da177e4SLinus Torvalds 	default:
19191da177e4SLinus Torvalds 		break;
19201da177e4SLinus Torvalds 	}
19211da177e4SLinus Torvalds 
19221da177e4SLinus Torvalds 	return NOTIFY_DONE;
19231da177e4SLinus Torvalds }
19241da177e4SLinus Torvalds 
19251da177e4SLinus Torvalds static struct notifier_block ndisc_netdev_notifier = {
19261da177e4SLinus Torvalds 	.notifier_call = ndisc_netdev_event,
19276eb79393SDavid Ahern 	.priority = ADDRCONF_NOTIFY_PRIORITY - 5,
19281da177e4SLinus Torvalds };
19291da177e4SLinus Torvalds 
19301da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
ndisc_warn_deprecated_sysctl(struct ctl_table * ctl,const char * func,const char * dev_name)19311da177e4SLinus Torvalds static void ndisc_warn_deprecated_sysctl(struct ctl_table *ctl,
19321da177e4SLinus Torvalds 					 const char *func, const char *dev_name)
19331da177e4SLinus Torvalds {
19341da177e4SLinus Torvalds 	static char warncomm[TASK_COMM_LEN];
19351da177e4SLinus Torvalds 	static int warned;
19361da177e4SLinus Torvalds 	if (strcmp(warncomm, current->comm) && warned < 5) {
19371da177e4SLinus Torvalds 		strcpy(warncomm, current->comm);
1938f3213831SJoe Perches 		pr_warn("process `%s' is using deprecated sysctl (%s) net.ipv6.neigh.%s.%s - use net.ipv6.neigh.%s.%s_ms instead\n",
19391da177e4SLinus Torvalds 			warncomm, func,
19401da177e4SLinus Torvalds 			dev_name, ctl->procname,
19411da177e4SLinus Torvalds 			dev_name, ctl->procname);
19421da177e4SLinus Torvalds 		warned++;
19431da177e4SLinus Torvalds 	}
19441da177e4SLinus Torvalds }
19451da177e4SLinus Torvalds 
ndisc_ifinfo_sysctl_change(struct ctl_table * ctl,int write,void * buffer,size_t * lenp,loff_t * ppos)194632927393SChristoph Hellwig int ndisc_ifinfo_sysctl_change(struct ctl_table *ctl, int write, void *buffer,
194732927393SChristoph Hellwig 		size_t *lenp, loff_t *ppos)
19481da177e4SLinus Torvalds {
19491da177e4SLinus Torvalds 	struct net_device *dev = ctl->extra1;
19501da177e4SLinus Torvalds 	struct inet6_dev *idev;
19511da177e4SLinus Torvalds 	int ret;
19521da177e4SLinus Torvalds 
1953d12af679SEric W. Biederman 	if ((strcmp(ctl->procname, "retrans_time") == 0) ||
1954d12af679SEric W. Biederman 	    (strcmp(ctl->procname, "base_reachable_time") == 0))
19551da177e4SLinus Torvalds 		ndisc_warn_deprecated_sysctl(ctl, "syscall", dev ? dev->name : "default");
19561da177e4SLinus Torvalds 
1957d12af679SEric W. Biederman 	if (strcmp(ctl->procname, "retrans_time") == 0)
1958cb5b09c1SJiri Pirko 		ret = neigh_proc_dointvec(ctl, write, buffer, lenp, ppos);
1959d12af679SEric W. Biederman 
1960d12af679SEric W. Biederman 	else if (strcmp(ctl->procname, "base_reachable_time") == 0)
1961cb5b09c1SJiri Pirko 		ret = neigh_proc_dointvec_jiffies(ctl, write,
19628d65af78SAlexey Dobriyan 						  buffer, lenp, ppos);
1963d12af679SEric W. Biederman 
1964d12af679SEric W. Biederman 	else if ((strcmp(ctl->procname, "retrans_time_ms") == 0) ||
1965ad02ac14SYOSHIFUJI Hideaki 		 (strcmp(ctl->procname, "base_reachable_time_ms") == 0))
1966cb5b09c1SJiri Pirko 		ret = neigh_proc_dointvec_ms_jiffies(ctl, write,
19678d65af78SAlexey Dobriyan 						     buffer, lenp, ppos);
1968d12af679SEric W. Biederman 	else
19691da177e4SLinus Torvalds 		ret = -1;
19701da177e4SLinus Torvalds 
19711da177e4SLinus Torvalds 	if (write && ret == 0 && dev && (idev = in6_dev_get(dev)) != NULL) {
19721f9248e5SJiri Pirko 		if (ctl->data == &NEIGH_VAR(idev->nd_parms, BASE_REACHABLE_TIME))
19731f9248e5SJiri Pirko 			idev->nd_parms->reachable_time =
19741f9248e5SJiri Pirko 					neigh_rand_reach_time(NEIGH_VAR(idev->nd_parms, BASE_REACHABLE_TIME));
19751da177e4SLinus Torvalds 		idev->tstamp = jiffies;
19761da177e4SLinus Torvalds 		inet6_ifinfo_notify(RTM_NEWLINK, idev);
19771da177e4SLinus Torvalds 		in6_dev_put(idev);
19781da177e4SLinus Torvalds 	}
19791da177e4SLinus Torvalds 	return ret;
19801da177e4SLinus Torvalds }
19811da177e4SLinus Torvalds 
19821da177e4SLinus Torvalds 
19831da177e4SLinus Torvalds #endif
19841da177e4SLinus Torvalds 
ndisc_net_init(struct net * net)19852c8c1e72SAlexey Dobriyan static int __net_init ndisc_net_init(struct net *net)
19861da177e4SLinus Torvalds {
19871da177e4SLinus Torvalds 	struct ipv6_pinfo *np;
19881da177e4SLinus Torvalds 	struct sock *sk;
19891da177e4SLinus Torvalds 	int err;
19901da177e4SLinus Torvalds 
19911ed8516fSDenis V. Lunev 	err = inet_ctl_sock_create(&sk, PF_INET6,
19921ed8516fSDenis V. Lunev 				   SOCK_RAW, IPPROTO_ICMPV6, net);
19931da177e4SLinus Torvalds 	if (err < 0) {
1994675418d5SJoe Perches 		ND_PRINTK(0, err,
1995675418d5SJoe Perches 			  "NDISC: Failed to initialize the control socket (err %d)\n",
19961da177e4SLinus Torvalds 			  err);
19971da177e4SLinus Torvalds 		return err;
19981da177e4SLinus Torvalds 	}
19991da177e4SLinus Torvalds 
20001ed8516fSDenis V. Lunev 	net->ipv6.ndisc_sk = sk;
20011762f7e8SDaniel Lezcano 
20021da177e4SLinus Torvalds 	np = inet6_sk(sk);
20031da177e4SLinus Torvalds 	np->hop_limit = 255;
20041da177e4SLinus Torvalds 	/* Do not loopback ndisc messages */
20051da177e4SLinus Torvalds 	np->mc_loop = 0;
20061da177e4SLinus Torvalds 
20071762f7e8SDaniel Lezcano 	return 0;
20081762f7e8SDaniel Lezcano }
20091762f7e8SDaniel Lezcano 
ndisc_net_exit(struct net * net)20102c8c1e72SAlexey Dobriyan static void __net_exit ndisc_net_exit(struct net *net)
20111762f7e8SDaniel Lezcano {
20121ed8516fSDenis V. Lunev 	inet_ctl_sock_destroy(net->ipv6.ndisc_sk);
20131762f7e8SDaniel Lezcano }
20141762f7e8SDaniel Lezcano 
20151762f7e8SDaniel Lezcano static struct pernet_operations ndisc_net_ops = {
20161762f7e8SDaniel Lezcano 	.init = ndisc_net_init,
20171762f7e8SDaniel Lezcano 	.exit = ndisc_net_exit,
20181762f7e8SDaniel Lezcano };
20191762f7e8SDaniel Lezcano 
ndisc_init(void)20201762f7e8SDaniel Lezcano int __init ndisc_init(void)
20211762f7e8SDaniel Lezcano {
20221762f7e8SDaniel Lezcano 	int err;
20231762f7e8SDaniel Lezcano 
20241762f7e8SDaniel Lezcano 	err = register_pernet_subsys(&ndisc_net_ops);
20251762f7e8SDaniel Lezcano 	if (err)
20261762f7e8SDaniel Lezcano 		return err;
20271da177e4SLinus Torvalds 	/*
20281da177e4SLinus Torvalds 	 * Initialize the neighbour table
20291da177e4SLinus Torvalds 	 */
2030d7480fd3SWANG Cong 	neigh_table_init(NEIGH_ND_TABLE, &nd_tbl);
20311da177e4SLinus Torvalds 
20321da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
203373af614aSJiri Pirko 	err = neigh_sysctl_register(NULL, &nd_tbl.parms,
203456ec0fb1SHimangi Saraogi 				    ndisc_ifinfo_sysctl_change);
20351762f7e8SDaniel Lezcano 	if (err)
20361762f7e8SDaniel Lezcano 		goto out_unregister_pernet;
20371762f7e8SDaniel Lezcano out:
2038bcd081a3SFabio Estevam #endif
20391762f7e8SDaniel Lezcano 	return err;
20401da177e4SLinus Torvalds 
20411762f7e8SDaniel Lezcano #ifdef CONFIG_SYSCTL
20421762f7e8SDaniel Lezcano out_unregister_pernet:
20431762f7e8SDaniel Lezcano 	unregister_pernet_subsys(&ndisc_net_ops);
20441762f7e8SDaniel Lezcano 	goto out;
20452c861cc6SMichal Kubeček #endif
20462c861cc6SMichal Kubeček }
20472c861cc6SMichal Kubeček 
ndisc_late_init(void)20482c861cc6SMichal Kubeček int __init ndisc_late_init(void)
20492c861cc6SMichal Kubeček {
20502c861cc6SMichal Kubeček 	return register_netdevice_notifier(&ndisc_netdev_notifier);
20512c861cc6SMichal Kubeček }
20522c861cc6SMichal Kubeček 
ndisc_late_cleanup(void)20532c861cc6SMichal Kubeček void ndisc_late_cleanup(void)
20542c861cc6SMichal Kubeček {
20552c861cc6SMichal Kubeček 	unregister_netdevice_notifier(&ndisc_netdev_notifier);
20561da177e4SLinus Torvalds }
20571da177e4SLinus Torvalds 
ndisc_cleanup(void)20581da177e4SLinus Torvalds void ndisc_cleanup(void)
20591da177e4SLinus Torvalds {
20601da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL
20611da177e4SLinus Torvalds 	neigh_sysctl_unregister(&nd_tbl.parms);
20621da177e4SLinus Torvalds #endif
2063d7480fd3SWANG Cong 	neigh_table_clear(NEIGH_ND_TABLE, &nd_tbl);
20641762f7e8SDaniel Lezcano 	unregister_pernet_subsys(&ndisc_net_ops);
20651da177e4SLinus Torvalds }
2066