18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *	Neighbour Discovery for IPv6
48c2ecf20Sopenharmony_ci *	Linux INET6 implementation
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci *	Authors:
78c2ecf20Sopenharmony_ci *	Pedro Roque		<roque@di.fc.ul.pt>
88c2ecf20Sopenharmony_ci *	Mike Shaver		<shaver@ingenia.com>
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci/*
128c2ecf20Sopenharmony_ci *	Changes:
138c2ecf20Sopenharmony_ci *
148c2ecf20Sopenharmony_ci *	Alexey I. Froloff		:	RFC6106 (DNSSL) support
158c2ecf20Sopenharmony_ci *	Pierre Ynard			:	export userland ND options
168c2ecf20Sopenharmony_ci *						through netlink (RDNSS support)
178c2ecf20Sopenharmony_ci *	Lars Fenneberg			:	fixed MTU setting on receipt
188c2ecf20Sopenharmony_ci *						of an RA.
198c2ecf20Sopenharmony_ci *	Janos Farkas			:	kmalloc failure checks
208c2ecf20Sopenharmony_ci *	Alexey Kuznetsov		:	state machine reworked
218c2ecf20Sopenharmony_ci *						and moved to net/core.
228c2ecf20Sopenharmony_ci *	Pekka Savola			:	RFC2461 validation
238c2ecf20Sopenharmony_ci *	YOSHIFUJI Hideaki @USAGI	:	Verify ND options properly
248c2ecf20Sopenharmony_ci */
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "ICMPv6: " fmt
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci#include <linux/module.h>
298c2ecf20Sopenharmony_ci#include <linux/errno.h>
308c2ecf20Sopenharmony_ci#include <linux/types.h>
318c2ecf20Sopenharmony_ci#include <linux/socket.h>
328c2ecf20Sopenharmony_ci#include <linux/sockios.h>
338c2ecf20Sopenharmony_ci#include <linux/sched.h>
348c2ecf20Sopenharmony_ci#include <linux/net.h>
358c2ecf20Sopenharmony_ci#include <linux/in6.h>
368c2ecf20Sopenharmony_ci#include <linux/route.h>
378c2ecf20Sopenharmony_ci#include <linux/init.h>
388c2ecf20Sopenharmony_ci#include <linux/rcupdate.h>
398c2ecf20Sopenharmony_ci#include <linux/slab.h>
408c2ecf20Sopenharmony_ci#ifdef CONFIG_SYSCTL
418c2ecf20Sopenharmony_ci#include <linux/sysctl.h>
428c2ecf20Sopenharmony_ci#endif
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci#include <linux/if_addr.h>
458c2ecf20Sopenharmony_ci#include <linux/if_ether.h>
468c2ecf20Sopenharmony_ci#include <linux/if_arp.h>
478c2ecf20Sopenharmony_ci#include <linux/ipv6.h>
488c2ecf20Sopenharmony_ci#include <linux/icmpv6.h>
498c2ecf20Sopenharmony_ci#include <linux/jhash.h>
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci#include <net/sock.h>
528c2ecf20Sopenharmony_ci#include <net/snmp.h>
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci#include <net/ipv6.h>
558c2ecf20Sopenharmony_ci#include <net/protocol.h>
568c2ecf20Sopenharmony_ci#include <net/ndisc.h>
578c2ecf20Sopenharmony_ci#include <net/ip6_route.h>
588c2ecf20Sopenharmony_ci#include <net/addrconf.h>
598c2ecf20Sopenharmony_ci#include <net/icmp.h>
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci#include <net/netlink.h>
628c2ecf20Sopenharmony_ci#include <linux/rtnetlink.h>
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci#include <net/flow.h>
658c2ecf20Sopenharmony_ci#include <net/ip6_checksum.h>
668c2ecf20Sopenharmony_ci#include <net/inet_common.h>
678c2ecf20Sopenharmony_ci#include <linux/proc_fs.h>
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci#include <linux/netfilter.h>
708c2ecf20Sopenharmony_ci#include <linux/netfilter_ipv6.h>
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistatic u32 ndisc_hash(const void *pkey,
738c2ecf20Sopenharmony_ci		      const struct net_device *dev,
748c2ecf20Sopenharmony_ci		      __u32 *hash_rnd);
758c2ecf20Sopenharmony_cistatic bool ndisc_key_eq(const struct neighbour *neigh, const void *pkey);
768c2ecf20Sopenharmony_cistatic bool ndisc_allow_add(const struct net_device *dev,
778c2ecf20Sopenharmony_ci			    struct netlink_ext_ack *extack);
788c2ecf20Sopenharmony_cistatic int ndisc_constructor(struct neighbour *neigh);
798c2ecf20Sopenharmony_cistatic void ndisc_solicit(struct neighbour *neigh, struct sk_buff *skb);
808c2ecf20Sopenharmony_cistatic void ndisc_error_report(struct neighbour *neigh, struct sk_buff *skb);
818c2ecf20Sopenharmony_cistatic int pndisc_constructor(struct pneigh_entry *n);
828c2ecf20Sopenharmony_cistatic void pndisc_destructor(struct pneigh_entry *n);
838c2ecf20Sopenharmony_cistatic void pndisc_redo(struct sk_buff *skb);
848c2ecf20Sopenharmony_cistatic int ndisc_is_multicast(const void *pkey);
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_cistatic const struct neigh_ops ndisc_generic_ops = {
878c2ecf20Sopenharmony_ci	.family =		AF_INET6,
888c2ecf20Sopenharmony_ci	.solicit =		ndisc_solicit,
898c2ecf20Sopenharmony_ci	.error_report =		ndisc_error_report,
908c2ecf20Sopenharmony_ci	.output =		neigh_resolve_output,
918c2ecf20Sopenharmony_ci	.connected_output =	neigh_connected_output,
928c2ecf20Sopenharmony_ci};
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_cistatic const struct neigh_ops ndisc_hh_ops = {
958c2ecf20Sopenharmony_ci	.family =		AF_INET6,
968c2ecf20Sopenharmony_ci	.solicit =		ndisc_solicit,
978c2ecf20Sopenharmony_ci	.error_report =		ndisc_error_report,
988c2ecf20Sopenharmony_ci	.output =		neigh_resolve_output,
998c2ecf20Sopenharmony_ci	.connected_output =	neigh_resolve_output,
1008c2ecf20Sopenharmony_ci};
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_cistatic const struct neigh_ops ndisc_direct_ops = {
1048c2ecf20Sopenharmony_ci	.family =		AF_INET6,
1058c2ecf20Sopenharmony_ci	.output =		neigh_direct_output,
1068c2ecf20Sopenharmony_ci	.connected_output =	neigh_direct_output,
1078c2ecf20Sopenharmony_ci};
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_cistruct neigh_table nd_tbl = {
1108c2ecf20Sopenharmony_ci	.family =	AF_INET6,
1118c2ecf20Sopenharmony_ci	.key_len =	sizeof(struct in6_addr),
1128c2ecf20Sopenharmony_ci	.protocol =	cpu_to_be16(ETH_P_IPV6),
1138c2ecf20Sopenharmony_ci	.hash =		ndisc_hash,
1148c2ecf20Sopenharmony_ci	.key_eq =	ndisc_key_eq,
1158c2ecf20Sopenharmony_ci	.constructor =	ndisc_constructor,
1168c2ecf20Sopenharmony_ci	.pconstructor =	pndisc_constructor,
1178c2ecf20Sopenharmony_ci	.pdestructor =	pndisc_destructor,
1188c2ecf20Sopenharmony_ci	.proxy_redo =	pndisc_redo,
1198c2ecf20Sopenharmony_ci	.is_multicast =	ndisc_is_multicast,
1208c2ecf20Sopenharmony_ci	.allow_add  =   ndisc_allow_add,
1218c2ecf20Sopenharmony_ci	.id =		"ndisc_cache",
1228c2ecf20Sopenharmony_ci	.parms = {
1238c2ecf20Sopenharmony_ci		.tbl			= &nd_tbl,
1248c2ecf20Sopenharmony_ci		.reachable_time		= ND_REACHABLE_TIME,
1258c2ecf20Sopenharmony_ci		.data = {
1268c2ecf20Sopenharmony_ci			[NEIGH_VAR_MCAST_PROBES] = 3,
1278c2ecf20Sopenharmony_ci			[NEIGH_VAR_UCAST_PROBES] = 3,
1288c2ecf20Sopenharmony_ci			[NEIGH_VAR_RETRANS_TIME] = ND_RETRANS_TIMER,
1298c2ecf20Sopenharmony_ci			[NEIGH_VAR_BASE_REACHABLE_TIME] = ND_REACHABLE_TIME,
1308c2ecf20Sopenharmony_ci			[NEIGH_VAR_DELAY_PROBE_TIME] = 5 * HZ,
1318c2ecf20Sopenharmony_ci			[NEIGH_VAR_GC_STALETIME] = 60 * HZ,
1328c2ecf20Sopenharmony_ci			[NEIGH_VAR_QUEUE_LEN_BYTES] = SK_WMEM_MAX,
1338c2ecf20Sopenharmony_ci			[NEIGH_VAR_PROXY_QLEN] = 64,
1348c2ecf20Sopenharmony_ci			[NEIGH_VAR_ANYCAST_DELAY] = 1 * HZ,
1358c2ecf20Sopenharmony_ci			[NEIGH_VAR_PROXY_DELAY] = (8 * HZ) / 10,
1368c2ecf20Sopenharmony_ci		},
1378c2ecf20Sopenharmony_ci	},
1388c2ecf20Sopenharmony_ci	.gc_interval =	  30 * HZ,
1398c2ecf20Sopenharmony_ci	.gc_thresh1 =	 128,
1408c2ecf20Sopenharmony_ci	.gc_thresh2 =	 512,
1418c2ecf20Sopenharmony_ci	.gc_thresh3 =	1024,
1428c2ecf20Sopenharmony_ci};
1438c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nd_tbl);
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_civoid __ndisc_fill_addr_option(struct sk_buff *skb, int type, void *data,
1468c2ecf20Sopenharmony_ci			      int data_len, int pad)
1478c2ecf20Sopenharmony_ci{
1488c2ecf20Sopenharmony_ci	int space = __ndisc_opt_addr_space(data_len, pad);
1498c2ecf20Sopenharmony_ci	u8 *opt = skb_put(skb, space);
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	opt[0] = type;
1528c2ecf20Sopenharmony_ci	opt[1] = space>>3;
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	memset(opt + 2, 0, pad);
1558c2ecf20Sopenharmony_ci	opt   += pad;
1568c2ecf20Sopenharmony_ci	space -= pad;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	memcpy(opt+2, data, data_len);
1598c2ecf20Sopenharmony_ci	data_len += 2;
1608c2ecf20Sopenharmony_ci	opt += data_len;
1618c2ecf20Sopenharmony_ci	space -= data_len;
1628c2ecf20Sopenharmony_ci	if (space > 0)
1638c2ecf20Sopenharmony_ci		memset(opt, 0, space);
1648c2ecf20Sopenharmony_ci}
1658c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(__ndisc_fill_addr_option);
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_cistatic inline void ndisc_fill_addr_option(struct sk_buff *skb, int type,
1688c2ecf20Sopenharmony_ci					  void *data, u8 icmp6_type)
1698c2ecf20Sopenharmony_ci{
1708c2ecf20Sopenharmony_ci	__ndisc_fill_addr_option(skb, type, data, skb->dev->addr_len,
1718c2ecf20Sopenharmony_ci				 ndisc_addr_option_pad(skb->dev->type));
1728c2ecf20Sopenharmony_ci	ndisc_ops_fill_addr_option(skb->dev, skb, icmp6_type);
1738c2ecf20Sopenharmony_ci}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_cistatic inline void ndisc_fill_redirect_addr_option(struct sk_buff *skb,
1768c2ecf20Sopenharmony_ci						   void *ha,
1778c2ecf20Sopenharmony_ci						   const u8 *ops_data)
1788c2ecf20Sopenharmony_ci{
1798c2ecf20Sopenharmony_ci	ndisc_fill_addr_option(skb, ND_OPT_TARGET_LL_ADDR, ha, NDISC_REDIRECT);
1808c2ecf20Sopenharmony_ci	ndisc_ops_fill_redirect_addr_option(skb->dev, skb, ops_data);
1818c2ecf20Sopenharmony_ci}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_cistatic struct nd_opt_hdr *ndisc_next_option(struct nd_opt_hdr *cur,
1848c2ecf20Sopenharmony_ci					    struct nd_opt_hdr *end)
1858c2ecf20Sopenharmony_ci{
1868c2ecf20Sopenharmony_ci	int type;
1878c2ecf20Sopenharmony_ci	if (!cur || !end || cur >= end)
1888c2ecf20Sopenharmony_ci		return NULL;
1898c2ecf20Sopenharmony_ci	type = cur->nd_opt_type;
1908c2ecf20Sopenharmony_ci	do {
1918c2ecf20Sopenharmony_ci		cur = ((void *)cur) + (cur->nd_opt_len << 3);
1928c2ecf20Sopenharmony_ci	} while (cur < end && cur->nd_opt_type != type);
1938c2ecf20Sopenharmony_ci	return cur <= end && cur->nd_opt_type == type ? cur : NULL;
1948c2ecf20Sopenharmony_ci}
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_cistatic inline int ndisc_is_useropt(const struct net_device *dev,
1978c2ecf20Sopenharmony_ci				   struct nd_opt_hdr *opt)
1988c2ecf20Sopenharmony_ci{
1998c2ecf20Sopenharmony_ci	return opt->nd_opt_type == ND_OPT_PREFIX_INFO ||
2008c2ecf20Sopenharmony_ci		opt->nd_opt_type == ND_OPT_RDNSS ||
2018c2ecf20Sopenharmony_ci		opt->nd_opt_type == ND_OPT_DNSSL ||
2028c2ecf20Sopenharmony_ci		opt->nd_opt_type == ND_OPT_CAPTIVE_PORTAL ||
2038c2ecf20Sopenharmony_ci		opt->nd_opt_type == ND_OPT_PREF64 ||
2048c2ecf20Sopenharmony_ci		ndisc_ops_is_useropt(dev, opt->nd_opt_type);
2058c2ecf20Sopenharmony_ci}
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_cistatic struct nd_opt_hdr *ndisc_next_useropt(const struct net_device *dev,
2088c2ecf20Sopenharmony_ci					     struct nd_opt_hdr *cur,
2098c2ecf20Sopenharmony_ci					     struct nd_opt_hdr *end)
2108c2ecf20Sopenharmony_ci{
2118c2ecf20Sopenharmony_ci	if (!cur || !end || cur >= end)
2128c2ecf20Sopenharmony_ci		return NULL;
2138c2ecf20Sopenharmony_ci	do {
2148c2ecf20Sopenharmony_ci		cur = ((void *)cur) + (cur->nd_opt_len << 3);
2158c2ecf20Sopenharmony_ci	} while (cur < end && !ndisc_is_useropt(dev, cur));
2168c2ecf20Sopenharmony_ci	return cur <= end && ndisc_is_useropt(dev, cur) ? cur : NULL;
2178c2ecf20Sopenharmony_ci}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_cistruct ndisc_options *ndisc_parse_options(const struct net_device *dev,
2208c2ecf20Sopenharmony_ci					  u8 *opt, int opt_len,
2218c2ecf20Sopenharmony_ci					  struct ndisc_options *ndopts)
2228c2ecf20Sopenharmony_ci{
2238c2ecf20Sopenharmony_ci	struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)opt;
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	if (!nd_opt || opt_len < 0 || !ndopts)
2268c2ecf20Sopenharmony_ci		return NULL;
2278c2ecf20Sopenharmony_ci	memset(ndopts, 0, sizeof(*ndopts));
2288c2ecf20Sopenharmony_ci	while (opt_len) {
2298c2ecf20Sopenharmony_ci		int l;
2308c2ecf20Sopenharmony_ci		if (opt_len < sizeof(struct nd_opt_hdr))
2318c2ecf20Sopenharmony_ci			return NULL;
2328c2ecf20Sopenharmony_ci		l = nd_opt->nd_opt_len << 3;
2338c2ecf20Sopenharmony_ci		if (opt_len < l || l == 0)
2348c2ecf20Sopenharmony_ci			return NULL;
2358c2ecf20Sopenharmony_ci		if (ndisc_ops_parse_options(dev, nd_opt, ndopts))
2368c2ecf20Sopenharmony_ci			goto next_opt;
2378c2ecf20Sopenharmony_ci		switch (nd_opt->nd_opt_type) {
2388c2ecf20Sopenharmony_ci		case ND_OPT_SOURCE_LL_ADDR:
2398c2ecf20Sopenharmony_ci		case ND_OPT_TARGET_LL_ADDR:
2408c2ecf20Sopenharmony_ci		case ND_OPT_MTU:
2418c2ecf20Sopenharmony_ci		case ND_OPT_NONCE:
2428c2ecf20Sopenharmony_ci		case ND_OPT_REDIRECT_HDR:
2438c2ecf20Sopenharmony_ci			if (ndopts->nd_opt_array[nd_opt->nd_opt_type]) {
2448c2ecf20Sopenharmony_ci				ND_PRINTK(2, warn,
2458c2ecf20Sopenharmony_ci					  "%s: duplicated ND6 option found: type=%d\n",
2468c2ecf20Sopenharmony_ci					  __func__, nd_opt->nd_opt_type);
2478c2ecf20Sopenharmony_ci			} else {
2488c2ecf20Sopenharmony_ci				ndopts->nd_opt_array[nd_opt->nd_opt_type] = nd_opt;
2498c2ecf20Sopenharmony_ci			}
2508c2ecf20Sopenharmony_ci			break;
2518c2ecf20Sopenharmony_ci		case ND_OPT_PREFIX_INFO:
2528c2ecf20Sopenharmony_ci			ndopts->nd_opts_pi_end = nd_opt;
2538c2ecf20Sopenharmony_ci			if (!ndopts->nd_opt_array[nd_opt->nd_opt_type])
2548c2ecf20Sopenharmony_ci				ndopts->nd_opt_array[nd_opt->nd_opt_type] = nd_opt;
2558c2ecf20Sopenharmony_ci			break;
2568c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_ROUTE_INFO
2578c2ecf20Sopenharmony_ci		case ND_OPT_ROUTE_INFO:
2588c2ecf20Sopenharmony_ci			ndopts->nd_opts_ri_end = nd_opt;
2598c2ecf20Sopenharmony_ci			if (!ndopts->nd_opts_ri)
2608c2ecf20Sopenharmony_ci				ndopts->nd_opts_ri = nd_opt;
2618c2ecf20Sopenharmony_ci			break;
2628c2ecf20Sopenharmony_ci#endif
2638c2ecf20Sopenharmony_ci		default:
2648c2ecf20Sopenharmony_ci			if (ndisc_is_useropt(dev, nd_opt)) {
2658c2ecf20Sopenharmony_ci				ndopts->nd_useropts_end = nd_opt;
2668c2ecf20Sopenharmony_ci				if (!ndopts->nd_useropts)
2678c2ecf20Sopenharmony_ci					ndopts->nd_useropts = nd_opt;
2688c2ecf20Sopenharmony_ci			} else {
2698c2ecf20Sopenharmony_ci				/*
2708c2ecf20Sopenharmony_ci				 * Unknown options must be silently ignored,
2718c2ecf20Sopenharmony_ci				 * to accommodate future extension to the
2728c2ecf20Sopenharmony_ci				 * protocol.
2738c2ecf20Sopenharmony_ci				 */
2748c2ecf20Sopenharmony_ci				ND_PRINTK(2, notice,
2758c2ecf20Sopenharmony_ci					  "%s: ignored unsupported option; type=%d, len=%d\n",
2768c2ecf20Sopenharmony_ci					  __func__,
2778c2ecf20Sopenharmony_ci					  nd_opt->nd_opt_type,
2788c2ecf20Sopenharmony_ci					  nd_opt->nd_opt_len);
2798c2ecf20Sopenharmony_ci			}
2808c2ecf20Sopenharmony_ci		}
2818c2ecf20Sopenharmony_cinext_opt:
2828c2ecf20Sopenharmony_ci		opt_len -= l;
2838c2ecf20Sopenharmony_ci		nd_opt = ((void *)nd_opt) + l;
2848c2ecf20Sopenharmony_ci	}
2858c2ecf20Sopenharmony_ci	return ndopts;
2868c2ecf20Sopenharmony_ci}
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ciint ndisc_mc_map(const struct in6_addr *addr, char *buf, struct net_device *dev, int dir)
2898c2ecf20Sopenharmony_ci{
2908c2ecf20Sopenharmony_ci	switch (dev->type) {
2918c2ecf20Sopenharmony_ci	case ARPHRD_ETHER:
2928c2ecf20Sopenharmony_ci	case ARPHRD_IEEE802:	/* Not sure. Check it later. --ANK */
2938c2ecf20Sopenharmony_ci	case ARPHRD_FDDI:
2948c2ecf20Sopenharmony_ci		ipv6_eth_mc_map(addr, buf);
2958c2ecf20Sopenharmony_ci		return 0;
2968c2ecf20Sopenharmony_ci	case ARPHRD_ARCNET:
2978c2ecf20Sopenharmony_ci		ipv6_arcnet_mc_map(addr, buf);
2988c2ecf20Sopenharmony_ci		return 0;
2998c2ecf20Sopenharmony_ci	case ARPHRD_INFINIBAND:
3008c2ecf20Sopenharmony_ci		ipv6_ib_mc_map(addr, dev->broadcast, buf);
3018c2ecf20Sopenharmony_ci		return 0;
3028c2ecf20Sopenharmony_ci	case ARPHRD_IPGRE:
3038c2ecf20Sopenharmony_ci		return ipv6_ipgre_mc_map(addr, dev->broadcast, buf);
3048c2ecf20Sopenharmony_ci	default:
3058c2ecf20Sopenharmony_ci		if (dir) {
3068c2ecf20Sopenharmony_ci			memcpy(buf, dev->broadcast, dev->addr_len);
3078c2ecf20Sopenharmony_ci			return 0;
3088c2ecf20Sopenharmony_ci		}
3098c2ecf20Sopenharmony_ci	}
3108c2ecf20Sopenharmony_ci	return -EINVAL;
3118c2ecf20Sopenharmony_ci}
3128c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ndisc_mc_map);
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_cistatic u32 ndisc_hash(const void *pkey,
3158c2ecf20Sopenharmony_ci		      const struct net_device *dev,
3168c2ecf20Sopenharmony_ci		      __u32 *hash_rnd)
3178c2ecf20Sopenharmony_ci{
3188c2ecf20Sopenharmony_ci	return ndisc_hashfn(pkey, dev, hash_rnd);
3198c2ecf20Sopenharmony_ci}
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_cistatic bool ndisc_key_eq(const struct neighbour *n, const void *pkey)
3228c2ecf20Sopenharmony_ci{
3238c2ecf20Sopenharmony_ci	return neigh_key_eq128(n, pkey);
3248c2ecf20Sopenharmony_ci}
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_cistatic int ndisc_constructor(struct neighbour *neigh)
3278c2ecf20Sopenharmony_ci{
3288c2ecf20Sopenharmony_ci	struct in6_addr *addr = (struct in6_addr *)&neigh->primary_key;
3298c2ecf20Sopenharmony_ci	struct net_device *dev = neigh->dev;
3308c2ecf20Sopenharmony_ci	struct inet6_dev *in6_dev;
3318c2ecf20Sopenharmony_ci	struct neigh_parms *parms;
3328c2ecf20Sopenharmony_ci	bool is_multicast = ipv6_addr_is_multicast(addr);
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	in6_dev = in6_dev_get(dev);
3358c2ecf20Sopenharmony_ci	if (!in6_dev) {
3368c2ecf20Sopenharmony_ci		return -EINVAL;
3378c2ecf20Sopenharmony_ci	}
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	parms = in6_dev->nd_parms;
3408c2ecf20Sopenharmony_ci	__neigh_parms_put(neigh->parms);
3418c2ecf20Sopenharmony_ci	neigh->parms = neigh_parms_clone(parms);
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	neigh->type = is_multicast ? RTN_MULTICAST : RTN_UNICAST;
3448c2ecf20Sopenharmony_ci	if (!dev->header_ops) {
3458c2ecf20Sopenharmony_ci		neigh->nud_state = NUD_NOARP;
3468c2ecf20Sopenharmony_ci		neigh->ops = &ndisc_direct_ops;
3478c2ecf20Sopenharmony_ci		neigh->output = neigh_direct_output;
3488c2ecf20Sopenharmony_ci	} else {
3498c2ecf20Sopenharmony_ci		if (is_multicast) {
3508c2ecf20Sopenharmony_ci			neigh->nud_state = NUD_NOARP;
3518c2ecf20Sopenharmony_ci			ndisc_mc_map(addr, neigh->ha, dev, 1);
3528c2ecf20Sopenharmony_ci		} else if (dev->flags&(IFF_NOARP|IFF_LOOPBACK)) {
3538c2ecf20Sopenharmony_ci			neigh->nud_state = NUD_NOARP;
3548c2ecf20Sopenharmony_ci			memcpy(neigh->ha, dev->dev_addr, dev->addr_len);
3558c2ecf20Sopenharmony_ci			if (dev->flags&IFF_LOOPBACK)
3568c2ecf20Sopenharmony_ci				neigh->type = RTN_LOCAL;
3578c2ecf20Sopenharmony_ci		} else if (dev->flags&IFF_POINTOPOINT) {
3588c2ecf20Sopenharmony_ci			neigh->nud_state = NUD_NOARP;
3598c2ecf20Sopenharmony_ci			memcpy(neigh->ha, dev->broadcast, dev->addr_len);
3608c2ecf20Sopenharmony_ci		}
3618c2ecf20Sopenharmony_ci		if (dev->header_ops->cache)
3628c2ecf20Sopenharmony_ci			neigh->ops = &ndisc_hh_ops;
3638c2ecf20Sopenharmony_ci		else
3648c2ecf20Sopenharmony_ci			neigh->ops = &ndisc_generic_ops;
3658c2ecf20Sopenharmony_ci		if (neigh->nud_state&NUD_VALID)
3668c2ecf20Sopenharmony_ci			neigh->output = neigh->ops->connected_output;
3678c2ecf20Sopenharmony_ci		else
3688c2ecf20Sopenharmony_ci			neigh->output = neigh->ops->output;
3698c2ecf20Sopenharmony_ci	}
3708c2ecf20Sopenharmony_ci	in6_dev_put(in6_dev);
3718c2ecf20Sopenharmony_ci	return 0;
3728c2ecf20Sopenharmony_ci}
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_cistatic int pndisc_constructor(struct pneigh_entry *n)
3758c2ecf20Sopenharmony_ci{
3768c2ecf20Sopenharmony_ci	struct in6_addr *addr = (struct in6_addr *)&n->key;
3778c2ecf20Sopenharmony_ci	struct in6_addr maddr;
3788c2ecf20Sopenharmony_ci	struct net_device *dev = n->dev;
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	if (!dev || !__in6_dev_get(dev))
3818c2ecf20Sopenharmony_ci		return -EINVAL;
3828c2ecf20Sopenharmony_ci	addrconf_addr_solict_mult(addr, &maddr);
3838c2ecf20Sopenharmony_ci	ipv6_dev_mc_inc(dev, &maddr);
3848c2ecf20Sopenharmony_ci	return 0;
3858c2ecf20Sopenharmony_ci}
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_cistatic void pndisc_destructor(struct pneigh_entry *n)
3888c2ecf20Sopenharmony_ci{
3898c2ecf20Sopenharmony_ci	struct in6_addr *addr = (struct in6_addr *)&n->key;
3908c2ecf20Sopenharmony_ci	struct in6_addr maddr;
3918c2ecf20Sopenharmony_ci	struct net_device *dev = n->dev;
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	if (!dev || !__in6_dev_get(dev))
3948c2ecf20Sopenharmony_ci		return;
3958c2ecf20Sopenharmony_ci	addrconf_addr_solict_mult(addr, &maddr);
3968c2ecf20Sopenharmony_ci	ipv6_dev_mc_dec(dev, &maddr);
3978c2ecf20Sopenharmony_ci}
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci/* called with rtnl held */
4008c2ecf20Sopenharmony_cistatic bool ndisc_allow_add(const struct net_device *dev,
4018c2ecf20Sopenharmony_ci			    struct netlink_ext_ack *extack)
4028c2ecf20Sopenharmony_ci{
4038c2ecf20Sopenharmony_ci	struct inet6_dev *idev = __in6_dev_get(dev);
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	if (!idev || idev->cnf.disable_ipv6) {
4068c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG(extack, "IPv6 is disabled on this device");
4078c2ecf20Sopenharmony_ci		return false;
4088c2ecf20Sopenharmony_ci	}
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	return true;
4118c2ecf20Sopenharmony_ci}
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_cistatic struct sk_buff *ndisc_alloc_skb(struct net_device *dev,
4148c2ecf20Sopenharmony_ci				       int len)
4158c2ecf20Sopenharmony_ci{
4168c2ecf20Sopenharmony_ci	int hlen = LL_RESERVED_SPACE(dev);
4178c2ecf20Sopenharmony_ci	int tlen = dev->needed_tailroom;
4188c2ecf20Sopenharmony_ci	struct sock *sk = dev_net(dev)->ipv6.ndisc_sk;
4198c2ecf20Sopenharmony_ci	struct sk_buff *skb;
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	skb = alloc_skb(hlen + sizeof(struct ipv6hdr) + len + tlen, GFP_ATOMIC);
4228c2ecf20Sopenharmony_ci	if (!skb) {
4238c2ecf20Sopenharmony_ci		ND_PRINTK(0, err, "ndisc: %s failed to allocate an skb\n",
4248c2ecf20Sopenharmony_ci			  __func__);
4258c2ecf20Sopenharmony_ci		return NULL;
4268c2ecf20Sopenharmony_ci	}
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	skb->protocol = htons(ETH_P_IPV6);
4298c2ecf20Sopenharmony_ci	skb->dev = dev;
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	skb_reserve(skb, hlen + sizeof(struct ipv6hdr));
4328c2ecf20Sopenharmony_ci	skb_reset_transport_header(skb);
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	/* Manually assign socket ownership as we avoid calling
4358c2ecf20Sopenharmony_ci	 * sock_alloc_send_pskb() to bypass wmem buffer limits
4368c2ecf20Sopenharmony_ci	 */
4378c2ecf20Sopenharmony_ci	skb_set_owner_w(skb, sk);
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	return skb;
4408c2ecf20Sopenharmony_ci}
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_cistatic void ip6_nd_hdr(struct sk_buff *skb,
4438c2ecf20Sopenharmony_ci		       const struct in6_addr *saddr,
4448c2ecf20Sopenharmony_ci		       const struct in6_addr *daddr,
4458c2ecf20Sopenharmony_ci		       int hop_limit, int len)
4468c2ecf20Sopenharmony_ci{
4478c2ecf20Sopenharmony_ci	struct ipv6hdr *hdr;
4488c2ecf20Sopenharmony_ci	struct inet6_dev *idev;
4498c2ecf20Sopenharmony_ci	unsigned tclass;
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	rcu_read_lock();
4528c2ecf20Sopenharmony_ci	idev = __in6_dev_get(skb->dev);
4538c2ecf20Sopenharmony_ci	tclass = idev ? idev->cnf.ndisc_tclass : 0;
4548c2ecf20Sopenharmony_ci	rcu_read_unlock();
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	skb_push(skb, sizeof(*hdr));
4578c2ecf20Sopenharmony_ci	skb_reset_network_header(skb);
4588c2ecf20Sopenharmony_ci	hdr = ipv6_hdr(skb);
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	ip6_flow_hdr(hdr, tclass, 0);
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	hdr->payload_len = htons(len);
4638c2ecf20Sopenharmony_ci	hdr->nexthdr = IPPROTO_ICMPV6;
4648c2ecf20Sopenharmony_ci	hdr->hop_limit = hop_limit;
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	hdr->saddr = *saddr;
4678c2ecf20Sopenharmony_ci	hdr->daddr = *daddr;
4688c2ecf20Sopenharmony_ci}
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_cistatic void ndisc_send_skb(struct sk_buff *skb,
4718c2ecf20Sopenharmony_ci			   const struct in6_addr *daddr,
4728c2ecf20Sopenharmony_ci			   const struct in6_addr *saddr)
4738c2ecf20Sopenharmony_ci{
4748c2ecf20Sopenharmony_ci	struct dst_entry *dst = skb_dst(skb);
4758c2ecf20Sopenharmony_ci	struct net *net = dev_net(skb->dev);
4768c2ecf20Sopenharmony_ci	struct sock *sk = net->ipv6.ndisc_sk;
4778c2ecf20Sopenharmony_ci	struct inet6_dev *idev;
4788c2ecf20Sopenharmony_ci	int err;
4798c2ecf20Sopenharmony_ci	struct icmp6hdr *icmp6h = icmp6_hdr(skb);
4808c2ecf20Sopenharmony_ci	u8 type;
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	type = icmp6h->icmp6_type;
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci	if (!dst) {
4858c2ecf20Sopenharmony_ci		struct flowi6 fl6;
4868c2ecf20Sopenharmony_ci		int oif = skb->dev->ifindex;
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci		icmpv6_flow_init(sk, &fl6, type, saddr, daddr, oif);
4898c2ecf20Sopenharmony_ci		dst = icmp6_dst_alloc(skb->dev, &fl6);
4908c2ecf20Sopenharmony_ci		if (IS_ERR(dst)) {
4918c2ecf20Sopenharmony_ci			kfree_skb(skb);
4928c2ecf20Sopenharmony_ci			return;
4938c2ecf20Sopenharmony_ci		}
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci		skb_dst_set(skb, dst);
4968c2ecf20Sopenharmony_ci	}
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci	icmp6h->icmp6_cksum = csum_ipv6_magic(saddr, daddr, skb->len,
4998c2ecf20Sopenharmony_ci					      IPPROTO_ICMPV6,
5008c2ecf20Sopenharmony_ci					      csum_partial(icmp6h,
5018c2ecf20Sopenharmony_ci							   skb->len, 0));
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	ip6_nd_hdr(skb, saddr, daddr, inet6_sk(sk)->hop_limit, skb->len);
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci	rcu_read_lock();
5068c2ecf20Sopenharmony_ci	idev = __in6_dev_get(dst->dev);
5078c2ecf20Sopenharmony_ci	IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUT, skb->len);
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT,
5108c2ecf20Sopenharmony_ci		      net, sk, skb, NULL, dst->dev,
5118c2ecf20Sopenharmony_ci		      dst_output);
5128c2ecf20Sopenharmony_ci	if (!err) {
5138c2ecf20Sopenharmony_ci		ICMP6MSGOUT_INC_STATS(net, idev, type);
5148c2ecf20Sopenharmony_ci		ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS);
5158c2ecf20Sopenharmony_ci	}
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	rcu_read_unlock();
5188c2ecf20Sopenharmony_ci}
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_civoid ndisc_send_na(struct net_device *dev, const struct in6_addr *daddr,
5218c2ecf20Sopenharmony_ci		   const struct in6_addr *solicited_addr,
5228c2ecf20Sopenharmony_ci		   bool router, bool solicited, bool override, bool inc_opt)
5238c2ecf20Sopenharmony_ci{
5248c2ecf20Sopenharmony_ci	struct sk_buff *skb;
5258c2ecf20Sopenharmony_ci	struct in6_addr tmpaddr;
5268c2ecf20Sopenharmony_ci	struct inet6_ifaddr *ifp;
5278c2ecf20Sopenharmony_ci	const struct in6_addr *src_addr;
5288c2ecf20Sopenharmony_ci	struct nd_msg *msg;
5298c2ecf20Sopenharmony_ci	int optlen = 0;
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	/* for anycast or proxy, solicited_addr != src_addr */
5328c2ecf20Sopenharmony_ci	ifp = ipv6_get_ifaddr(dev_net(dev), solicited_addr, dev, 1);
5338c2ecf20Sopenharmony_ci	if (ifp) {
5348c2ecf20Sopenharmony_ci		src_addr = solicited_addr;
5358c2ecf20Sopenharmony_ci		if (ifp->flags & IFA_F_OPTIMISTIC)
5368c2ecf20Sopenharmony_ci			override = false;
5378c2ecf20Sopenharmony_ci		inc_opt |= ifp->idev->cnf.force_tllao;
5388c2ecf20Sopenharmony_ci		in6_ifa_put(ifp);
5398c2ecf20Sopenharmony_ci	} else {
5408c2ecf20Sopenharmony_ci		if (ipv6_dev_get_saddr(dev_net(dev), dev, daddr,
5418c2ecf20Sopenharmony_ci				       inet6_sk(dev_net(dev)->ipv6.ndisc_sk)->srcprefs,
5428c2ecf20Sopenharmony_ci				       &tmpaddr))
5438c2ecf20Sopenharmony_ci			return;
5448c2ecf20Sopenharmony_ci		src_addr = &tmpaddr;
5458c2ecf20Sopenharmony_ci	}
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci	if (!dev->addr_len)
5488c2ecf20Sopenharmony_ci		inc_opt = false;
5498c2ecf20Sopenharmony_ci	if (inc_opt)
5508c2ecf20Sopenharmony_ci		optlen += ndisc_opt_addr_space(dev,
5518c2ecf20Sopenharmony_ci					       NDISC_NEIGHBOUR_ADVERTISEMENT);
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci	skb = ndisc_alloc_skb(dev, sizeof(*msg) + optlen);
5548c2ecf20Sopenharmony_ci	if (!skb)
5558c2ecf20Sopenharmony_ci		return;
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci	msg = skb_put(skb, sizeof(*msg));
5588c2ecf20Sopenharmony_ci	*msg = (struct nd_msg) {
5598c2ecf20Sopenharmony_ci		.icmph = {
5608c2ecf20Sopenharmony_ci			.icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT,
5618c2ecf20Sopenharmony_ci			.icmp6_router = router,
5628c2ecf20Sopenharmony_ci			.icmp6_solicited = solicited,
5638c2ecf20Sopenharmony_ci			.icmp6_override = override,
5648c2ecf20Sopenharmony_ci		},
5658c2ecf20Sopenharmony_ci		.target = *solicited_addr,
5668c2ecf20Sopenharmony_ci	};
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	if (inc_opt)
5698c2ecf20Sopenharmony_ci		ndisc_fill_addr_option(skb, ND_OPT_TARGET_LL_ADDR,
5708c2ecf20Sopenharmony_ci				       dev->dev_addr,
5718c2ecf20Sopenharmony_ci				       NDISC_NEIGHBOUR_ADVERTISEMENT);
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci	ndisc_send_skb(skb, daddr, src_addr);
5748c2ecf20Sopenharmony_ci}
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_cistatic void ndisc_send_unsol_na(struct net_device *dev)
5778c2ecf20Sopenharmony_ci{
5788c2ecf20Sopenharmony_ci	struct inet6_dev *idev;
5798c2ecf20Sopenharmony_ci	struct inet6_ifaddr *ifa;
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ci	idev = in6_dev_get(dev);
5828c2ecf20Sopenharmony_ci	if (!idev)
5838c2ecf20Sopenharmony_ci		return;
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci	read_lock_bh(&idev->lock);
5868c2ecf20Sopenharmony_ci	list_for_each_entry(ifa, &idev->addr_list, if_list) {
5878c2ecf20Sopenharmony_ci		/* skip tentative addresses until dad completes */
5888c2ecf20Sopenharmony_ci		if (ifa->flags & IFA_F_TENTATIVE &&
5898c2ecf20Sopenharmony_ci		    !(ifa->flags & IFA_F_OPTIMISTIC))
5908c2ecf20Sopenharmony_ci			continue;
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci		ndisc_send_na(dev, &in6addr_linklocal_allnodes, &ifa->addr,
5938c2ecf20Sopenharmony_ci			      /*router=*/ !!idev->cnf.forwarding,
5948c2ecf20Sopenharmony_ci			      /*solicited=*/ false, /*override=*/ true,
5958c2ecf20Sopenharmony_ci			      /*inc_opt=*/ true);
5968c2ecf20Sopenharmony_ci	}
5978c2ecf20Sopenharmony_ci	read_unlock_bh(&idev->lock);
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci	in6_dev_put(idev);
6008c2ecf20Sopenharmony_ci}
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_civoid ndisc_send_ns(struct net_device *dev, const struct in6_addr *solicit,
6038c2ecf20Sopenharmony_ci		   const struct in6_addr *daddr, const struct in6_addr *saddr,
6048c2ecf20Sopenharmony_ci		   u64 nonce)
6058c2ecf20Sopenharmony_ci{
6068c2ecf20Sopenharmony_ci	struct sk_buff *skb;
6078c2ecf20Sopenharmony_ci	struct in6_addr addr_buf;
6088c2ecf20Sopenharmony_ci	int inc_opt = dev->addr_len;
6098c2ecf20Sopenharmony_ci	int optlen = 0;
6108c2ecf20Sopenharmony_ci	struct nd_msg *msg;
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci	if (!saddr) {
6138c2ecf20Sopenharmony_ci		if (ipv6_get_lladdr(dev, &addr_buf,
6148c2ecf20Sopenharmony_ci				   (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)))
6158c2ecf20Sopenharmony_ci			return;
6168c2ecf20Sopenharmony_ci		saddr = &addr_buf;
6178c2ecf20Sopenharmony_ci	}
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci	if (ipv6_addr_any(saddr))
6208c2ecf20Sopenharmony_ci		inc_opt = false;
6218c2ecf20Sopenharmony_ci	if (inc_opt)
6228c2ecf20Sopenharmony_ci		optlen += ndisc_opt_addr_space(dev,
6238c2ecf20Sopenharmony_ci					       NDISC_NEIGHBOUR_SOLICITATION);
6248c2ecf20Sopenharmony_ci	if (nonce != 0)
6258c2ecf20Sopenharmony_ci		optlen += 8;
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci	skb = ndisc_alloc_skb(dev, sizeof(*msg) + optlen);
6288c2ecf20Sopenharmony_ci	if (!skb)
6298c2ecf20Sopenharmony_ci		return;
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci	msg = skb_put(skb, sizeof(*msg));
6328c2ecf20Sopenharmony_ci	*msg = (struct nd_msg) {
6338c2ecf20Sopenharmony_ci		.icmph = {
6348c2ecf20Sopenharmony_ci			.icmp6_type = NDISC_NEIGHBOUR_SOLICITATION,
6358c2ecf20Sopenharmony_ci		},
6368c2ecf20Sopenharmony_ci		.target = *solicit,
6378c2ecf20Sopenharmony_ci	};
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci	if (inc_opt)
6408c2ecf20Sopenharmony_ci		ndisc_fill_addr_option(skb, ND_OPT_SOURCE_LL_ADDR,
6418c2ecf20Sopenharmony_ci				       dev->dev_addr,
6428c2ecf20Sopenharmony_ci				       NDISC_NEIGHBOUR_SOLICITATION);
6438c2ecf20Sopenharmony_ci	if (nonce != 0) {
6448c2ecf20Sopenharmony_ci		u8 *opt = skb_put(skb, 8);
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ci		opt[0] = ND_OPT_NONCE;
6478c2ecf20Sopenharmony_ci		opt[1] = 8 >> 3;
6488c2ecf20Sopenharmony_ci		memcpy(opt + 2, &nonce, 6);
6498c2ecf20Sopenharmony_ci	}
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_ci	ndisc_send_skb(skb, daddr, saddr);
6528c2ecf20Sopenharmony_ci}
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_civoid ndisc_send_rs(struct net_device *dev, const struct in6_addr *saddr,
6558c2ecf20Sopenharmony_ci		   const struct in6_addr *daddr)
6568c2ecf20Sopenharmony_ci{
6578c2ecf20Sopenharmony_ci	struct sk_buff *skb;
6588c2ecf20Sopenharmony_ci	struct rs_msg *msg;
6598c2ecf20Sopenharmony_ci	int send_sllao = dev->addr_len;
6608c2ecf20Sopenharmony_ci	int optlen = 0;
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
6638c2ecf20Sopenharmony_ci	/*
6648c2ecf20Sopenharmony_ci	 * According to section 2.2 of RFC 4429, we must not
6658c2ecf20Sopenharmony_ci	 * send router solicitations with a sllao from
6668c2ecf20Sopenharmony_ci	 * optimistic addresses, but we may send the solicitation
6678c2ecf20Sopenharmony_ci	 * if we don't include the sllao.  So here we check
6688c2ecf20Sopenharmony_ci	 * if our address is optimistic, and if so, we
6698c2ecf20Sopenharmony_ci	 * suppress the inclusion of the sllao.
6708c2ecf20Sopenharmony_ci	 */
6718c2ecf20Sopenharmony_ci	if (send_sllao) {
6728c2ecf20Sopenharmony_ci		struct inet6_ifaddr *ifp = ipv6_get_ifaddr(dev_net(dev), saddr,
6738c2ecf20Sopenharmony_ci							   dev, 1);
6748c2ecf20Sopenharmony_ci		if (ifp) {
6758c2ecf20Sopenharmony_ci			if (ifp->flags & IFA_F_OPTIMISTIC)  {
6768c2ecf20Sopenharmony_ci				send_sllao = 0;
6778c2ecf20Sopenharmony_ci			}
6788c2ecf20Sopenharmony_ci			in6_ifa_put(ifp);
6798c2ecf20Sopenharmony_ci		} else {
6808c2ecf20Sopenharmony_ci			send_sllao = 0;
6818c2ecf20Sopenharmony_ci		}
6828c2ecf20Sopenharmony_ci	}
6838c2ecf20Sopenharmony_ci#endif
6848c2ecf20Sopenharmony_ci	if (send_sllao)
6858c2ecf20Sopenharmony_ci		optlen += ndisc_opt_addr_space(dev, NDISC_ROUTER_SOLICITATION);
6868c2ecf20Sopenharmony_ci
6878c2ecf20Sopenharmony_ci	skb = ndisc_alloc_skb(dev, sizeof(*msg) + optlen);
6888c2ecf20Sopenharmony_ci	if (!skb)
6898c2ecf20Sopenharmony_ci		return;
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci	msg = skb_put(skb, sizeof(*msg));
6928c2ecf20Sopenharmony_ci	*msg = (struct rs_msg) {
6938c2ecf20Sopenharmony_ci		.icmph = {
6948c2ecf20Sopenharmony_ci			.icmp6_type = NDISC_ROUTER_SOLICITATION,
6958c2ecf20Sopenharmony_ci		},
6968c2ecf20Sopenharmony_ci	};
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci	if (send_sllao)
6998c2ecf20Sopenharmony_ci		ndisc_fill_addr_option(skb, ND_OPT_SOURCE_LL_ADDR,
7008c2ecf20Sopenharmony_ci				       dev->dev_addr,
7018c2ecf20Sopenharmony_ci				       NDISC_ROUTER_SOLICITATION);
7028c2ecf20Sopenharmony_ci
7038c2ecf20Sopenharmony_ci	ndisc_send_skb(skb, daddr, saddr);
7048c2ecf20Sopenharmony_ci}
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_cistatic void ndisc_error_report(struct neighbour *neigh, struct sk_buff *skb)
7088c2ecf20Sopenharmony_ci{
7098c2ecf20Sopenharmony_ci	/*
7108c2ecf20Sopenharmony_ci	 *	"The sender MUST return an ICMP
7118c2ecf20Sopenharmony_ci	 *	 destination unreachable"
7128c2ecf20Sopenharmony_ci	 */
7138c2ecf20Sopenharmony_ci	dst_link_failure(skb);
7148c2ecf20Sopenharmony_ci	kfree_skb(skb);
7158c2ecf20Sopenharmony_ci}
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci/* Called with locked neigh: either read or both */
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_cistatic void ndisc_solicit(struct neighbour *neigh, struct sk_buff *skb)
7208c2ecf20Sopenharmony_ci{
7218c2ecf20Sopenharmony_ci	struct in6_addr *saddr = NULL;
7228c2ecf20Sopenharmony_ci	struct in6_addr mcaddr;
7238c2ecf20Sopenharmony_ci	struct net_device *dev = neigh->dev;
7248c2ecf20Sopenharmony_ci	struct in6_addr *target = (struct in6_addr *)&neigh->primary_key;
7258c2ecf20Sopenharmony_ci	int probes = atomic_read(&neigh->probes);
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_ci	if (skb && ipv6_chk_addr_and_flags(dev_net(dev), &ipv6_hdr(skb)->saddr,
7288c2ecf20Sopenharmony_ci					   dev, false, 1,
7298c2ecf20Sopenharmony_ci					   IFA_F_TENTATIVE|IFA_F_OPTIMISTIC))
7308c2ecf20Sopenharmony_ci		saddr = &ipv6_hdr(skb)->saddr;
7318c2ecf20Sopenharmony_ci	probes -= NEIGH_VAR(neigh->parms, UCAST_PROBES);
7328c2ecf20Sopenharmony_ci	if (probes < 0) {
7338c2ecf20Sopenharmony_ci		if (!(neigh->nud_state & NUD_VALID)) {
7348c2ecf20Sopenharmony_ci			ND_PRINTK(1, dbg,
7358c2ecf20Sopenharmony_ci				  "%s: trying to ucast probe in NUD_INVALID: %pI6\n",
7368c2ecf20Sopenharmony_ci				  __func__, target);
7378c2ecf20Sopenharmony_ci		}
7388c2ecf20Sopenharmony_ci		ndisc_send_ns(dev, target, target, saddr, 0);
7398c2ecf20Sopenharmony_ci	} else if ((probes -= NEIGH_VAR(neigh->parms, APP_PROBES)) < 0) {
7408c2ecf20Sopenharmony_ci		neigh_app_ns(neigh);
7418c2ecf20Sopenharmony_ci	} else {
7428c2ecf20Sopenharmony_ci		addrconf_addr_solict_mult(target, &mcaddr);
7438c2ecf20Sopenharmony_ci		ndisc_send_ns(dev, target, &mcaddr, saddr, 0);
7448c2ecf20Sopenharmony_ci	}
7458c2ecf20Sopenharmony_ci}
7468c2ecf20Sopenharmony_ci
7478c2ecf20Sopenharmony_cistatic int pndisc_is_router(const void *pkey,
7488c2ecf20Sopenharmony_ci			    struct net_device *dev)
7498c2ecf20Sopenharmony_ci{
7508c2ecf20Sopenharmony_ci	struct pneigh_entry *n;
7518c2ecf20Sopenharmony_ci	int ret = -1;
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_ci	read_lock_bh(&nd_tbl.lock);
7548c2ecf20Sopenharmony_ci	n = __pneigh_lookup(&nd_tbl, dev_net(dev), pkey, dev);
7558c2ecf20Sopenharmony_ci	if (n)
7568c2ecf20Sopenharmony_ci		ret = !!(n->flags & NTF_ROUTER);
7578c2ecf20Sopenharmony_ci	read_unlock_bh(&nd_tbl.lock);
7588c2ecf20Sopenharmony_ci
7598c2ecf20Sopenharmony_ci	return ret;
7608c2ecf20Sopenharmony_ci}
7618c2ecf20Sopenharmony_ci
7628c2ecf20Sopenharmony_civoid ndisc_update(const struct net_device *dev, struct neighbour *neigh,
7638c2ecf20Sopenharmony_ci		  const u8 *lladdr, u8 new, u32 flags, u8 icmp6_type,
7648c2ecf20Sopenharmony_ci		  struct ndisc_options *ndopts)
7658c2ecf20Sopenharmony_ci{
7668c2ecf20Sopenharmony_ci	neigh_update(neigh, lladdr, new, flags, 0);
7678c2ecf20Sopenharmony_ci	/* report ndisc ops about neighbour update */
7688c2ecf20Sopenharmony_ci	ndisc_ops_update(dev, neigh, flags, icmp6_type, ndopts);
7698c2ecf20Sopenharmony_ci}
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_cistatic void ndisc_recv_ns(struct sk_buff *skb)
7728c2ecf20Sopenharmony_ci{
7738c2ecf20Sopenharmony_ci	struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb);
7748c2ecf20Sopenharmony_ci	const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
7758c2ecf20Sopenharmony_ci	const struct in6_addr *daddr = &ipv6_hdr(skb)->daddr;
7768c2ecf20Sopenharmony_ci	u8 *lladdr = NULL;
7778c2ecf20Sopenharmony_ci	u32 ndoptlen = skb_tail_pointer(skb) - (skb_transport_header(skb) +
7788c2ecf20Sopenharmony_ci				    offsetof(struct nd_msg, opt));
7798c2ecf20Sopenharmony_ci	struct ndisc_options ndopts;
7808c2ecf20Sopenharmony_ci	struct net_device *dev = skb->dev;
7818c2ecf20Sopenharmony_ci	struct inet6_ifaddr *ifp;
7828c2ecf20Sopenharmony_ci	struct inet6_dev *idev = NULL;
7838c2ecf20Sopenharmony_ci	struct neighbour *neigh;
7848c2ecf20Sopenharmony_ci	int dad = ipv6_addr_any(saddr);
7858c2ecf20Sopenharmony_ci	bool inc;
7868c2ecf20Sopenharmony_ci	int is_router = -1;
7878c2ecf20Sopenharmony_ci	u64 nonce = 0;
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_ci	if (skb->len < sizeof(struct nd_msg)) {
7908c2ecf20Sopenharmony_ci		ND_PRINTK(2, warn, "NS: packet too short\n");
7918c2ecf20Sopenharmony_ci		return;
7928c2ecf20Sopenharmony_ci	}
7938c2ecf20Sopenharmony_ci
7948c2ecf20Sopenharmony_ci	if (ipv6_addr_is_multicast(&msg->target)) {
7958c2ecf20Sopenharmony_ci		ND_PRINTK(2, warn, "NS: multicast target address\n");
7968c2ecf20Sopenharmony_ci		return;
7978c2ecf20Sopenharmony_ci	}
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_ci	/*
8008c2ecf20Sopenharmony_ci	 * RFC2461 7.1.1:
8018c2ecf20Sopenharmony_ci	 * DAD has to be destined for solicited node multicast address.
8028c2ecf20Sopenharmony_ci	 */
8038c2ecf20Sopenharmony_ci	if (dad && !ipv6_addr_is_solict_mult(daddr)) {
8048c2ecf20Sopenharmony_ci		ND_PRINTK(2, warn, "NS: bad DAD packet (wrong destination)\n");
8058c2ecf20Sopenharmony_ci		return;
8068c2ecf20Sopenharmony_ci	}
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_ci	if (!ndisc_parse_options(dev, msg->opt, ndoptlen, &ndopts)) {
8098c2ecf20Sopenharmony_ci		ND_PRINTK(2, warn, "NS: invalid ND options\n");
8108c2ecf20Sopenharmony_ci		return;
8118c2ecf20Sopenharmony_ci	}
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_ci	if (ndopts.nd_opts_src_lladdr) {
8148c2ecf20Sopenharmony_ci		lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr, dev);
8158c2ecf20Sopenharmony_ci		if (!lladdr) {
8168c2ecf20Sopenharmony_ci			ND_PRINTK(2, warn,
8178c2ecf20Sopenharmony_ci				  "NS: invalid link-layer address length\n");
8188c2ecf20Sopenharmony_ci			return;
8198c2ecf20Sopenharmony_ci		}
8208c2ecf20Sopenharmony_ci
8218c2ecf20Sopenharmony_ci		/* RFC2461 7.1.1:
8228c2ecf20Sopenharmony_ci		 *	If the IP source address is the unspecified address,
8238c2ecf20Sopenharmony_ci		 *	there MUST NOT be source link-layer address option
8248c2ecf20Sopenharmony_ci		 *	in the message.
8258c2ecf20Sopenharmony_ci		 */
8268c2ecf20Sopenharmony_ci		if (dad) {
8278c2ecf20Sopenharmony_ci			ND_PRINTK(2, warn,
8288c2ecf20Sopenharmony_ci				  "NS: bad DAD packet (link-layer address option)\n");
8298c2ecf20Sopenharmony_ci			return;
8308c2ecf20Sopenharmony_ci		}
8318c2ecf20Sopenharmony_ci	}
8328c2ecf20Sopenharmony_ci	if (ndopts.nd_opts_nonce && ndopts.nd_opts_nonce->nd_opt_len == 1)
8338c2ecf20Sopenharmony_ci		memcpy(&nonce, (u8 *)(ndopts.nd_opts_nonce + 1), 6);
8348c2ecf20Sopenharmony_ci
8358c2ecf20Sopenharmony_ci	inc = ipv6_addr_is_multicast(daddr);
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_ci	ifp = ipv6_get_ifaddr(dev_net(dev), &msg->target, dev, 1);
8388c2ecf20Sopenharmony_ci	if (ifp) {
8398c2ecf20Sopenharmony_cihave_ifp:
8408c2ecf20Sopenharmony_ci		if (ifp->flags & (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)) {
8418c2ecf20Sopenharmony_ci			if (dad) {
8428c2ecf20Sopenharmony_ci				if (nonce != 0 && ifp->dad_nonce == nonce) {
8438c2ecf20Sopenharmony_ci					u8 *np = (u8 *)&nonce;
8448c2ecf20Sopenharmony_ci					/* Matching nonce if looped back */
8458c2ecf20Sopenharmony_ci					ND_PRINTK(2, notice,
8468c2ecf20Sopenharmony_ci						  "%s: IPv6 DAD loopback for address %pI6c nonce %pM ignored\n",
8478c2ecf20Sopenharmony_ci						  ifp->idev->dev->name,
8488c2ecf20Sopenharmony_ci						  &ifp->addr, np);
8498c2ecf20Sopenharmony_ci					goto out;
8508c2ecf20Sopenharmony_ci				}
8518c2ecf20Sopenharmony_ci				/*
8528c2ecf20Sopenharmony_ci				 * We are colliding with another node
8538c2ecf20Sopenharmony_ci				 * who is doing DAD
8548c2ecf20Sopenharmony_ci				 * so fail our DAD process
8558c2ecf20Sopenharmony_ci				 */
8568c2ecf20Sopenharmony_ci				addrconf_dad_failure(skb, ifp);
8578c2ecf20Sopenharmony_ci				return;
8588c2ecf20Sopenharmony_ci			} else {
8598c2ecf20Sopenharmony_ci				/*
8608c2ecf20Sopenharmony_ci				 * This is not a dad solicitation.
8618c2ecf20Sopenharmony_ci				 * If we are an optimistic node,
8628c2ecf20Sopenharmony_ci				 * we should respond.
8638c2ecf20Sopenharmony_ci				 * Otherwise, we should ignore it.
8648c2ecf20Sopenharmony_ci				 */
8658c2ecf20Sopenharmony_ci				if (!(ifp->flags & IFA_F_OPTIMISTIC))
8668c2ecf20Sopenharmony_ci					goto out;
8678c2ecf20Sopenharmony_ci			}
8688c2ecf20Sopenharmony_ci		}
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_ci		idev = ifp->idev;
8718c2ecf20Sopenharmony_ci	} else {
8728c2ecf20Sopenharmony_ci		struct net *net = dev_net(dev);
8738c2ecf20Sopenharmony_ci
8748c2ecf20Sopenharmony_ci		/* perhaps an address on the master device */
8758c2ecf20Sopenharmony_ci		if (netif_is_l3_slave(dev)) {
8768c2ecf20Sopenharmony_ci			struct net_device *mdev;
8778c2ecf20Sopenharmony_ci
8788c2ecf20Sopenharmony_ci			mdev = netdev_master_upper_dev_get_rcu(dev);
8798c2ecf20Sopenharmony_ci			if (mdev) {
8808c2ecf20Sopenharmony_ci				ifp = ipv6_get_ifaddr(net, &msg->target, mdev, 1);
8818c2ecf20Sopenharmony_ci				if (ifp)
8828c2ecf20Sopenharmony_ci					goto have_ifp;
8838c2ecf20Sopenharmony_ci			}
8848c2ecf20Sopenharmony_ci		}
8858c2ecf20Sopenharmony_ci
8868c2ecf20Sopenharmony_ci		idev = in6_dev_get(dev);
8878c2ecf20Sopenharmony_ci		if (!idev) {
8888c2ecf20Sopenharmony_ci			/* XXX: count this drop? */
8898c2ecf20Sopenharmony_ci			return;
8908c2ecf20Sopenharmony_ci		}
8918c2ecf20Sopenharmony_ci
8928c2ecf20Sopenharmony_ci		if (ipv6_chk_acast_addr(net, dev, &msg->target) ||
8938c2ecf20Sopenharmony_ci		    (idev->cnf.forwarding &&
8948c2ecf20Sopenharmony_ci		     (net->ipv6.devconf_all->proxy_ndp || idev->cnf.proxy_ndp) &&
8958c2ecf20Sopenharmony_ci		     (is_router = pndisc_is_router(&msg->target, dev)) >= 0)) {
8968c2ecf20Sopenharmony_ci			if (!(NEIGH_CB(skb)->flags & LOCALLY_ENQUEUED) &&
8978c2ecf20Sopenharmony_ci			    skb->pkt_type != PACKET_HOST &&
8988c2ecf20Sopenharmony_ci			    inc &&
8998c2ecf20Sopenharmony_ci			    NEIGH_VAR(idev->nd_parms, PROXY_DELAY) != 0) {
9008c2ecf20Sopenharmony_ci				/*
9018c2ecf20Sopenharmony_ci				 * for anycast or proxy,
9028c2ecf20Sopenharmony_ci				 * sender should delay its response
9038c2ecf20Sopenharmony_ci				 * by a random time between 0 and
9048c2ecf20Sopenharmony_ci				 * MAX_ANYCAST_DELAY_TIME seconds.
9058c2ecf20Sopenharmony_ci				 * (RFC2461) -- yoshfuji
9068c2ecf20Sopenharmony_ci				 */
9078c2ecf20Sopenharmony_ci				struct sk_buff *n = skb_clone(skb, GFP_ATOMIC);
9088c2ecf20Sopenharmony_ci				if (n)
9098c2ecf20Sopenharmony_ci					pneigh_enqueue(&nd_tbl, idev->nd_parms, n);
9108c2ecf20Sopenharmony_ci				goto out;
9118c2ecf20Sopenharmony_ci			}
9128c2ecf20Sopenharmony_ci		} else
9138c2ecf20Sopenharmony_ci			goto out;
9148c2ecf20Sopenharmony_ci	}
9158c2ecf20Sopenharmony_ci
9168c2ecf20Sopenharmony_ci	if (is_router < 0)
9178c2ecf20Sopenharmony_ci		is_router = idev->cnf.forwarding;
9188c2ecf20Sopenharmony_ci
9198c2ecf20Sopenharmony_ci	if (dad) {
9208c2ecf20Sopenharmony_ci		ndisc_send_na(dev, &in6addr_linklocal_allnodes, &msg->target,
9218c2ecf20Sopenharmony_ci			      !!is_router, false, (ifp != NULL), true);
9228c2ecf20Sopenharmony_ci		goto out;
9238c2ecf20Sopenharmony_ci	}
9248c2ecf20Sopenharmony_ci
9258c2ecf20Sopenharmony_ci	if (inc)
9268c2ecf20Sopenharmony_ci		NEIGH_CACHE_STAT_INC(&nd_tbl, rcv_probes_mcast);
9278c2ecf20Sopenharmony_ci	else
9288c2ecf20Sopenharmony_ci		NEIGH_CACHE_STAT_INC(&nd_tbl, rcv_probes_ucast);
9298c2ecf20Sopenharmony_ci
9308c2ecf20Sopenharmony_ci	/*
9318c2ecf20Sopenharmony_ci	 *	update / create cache entry
9328c2ecf20Sopenharmony_ci	 *	for the source address
9338c2ecf20Sopenharmony_ci	 */
9348c2ecf20Sopenharmony_ci	neigh = __neigh_lookup(&nd_tbl, saddr, dev,
9358c2ecf20Sopenharmony_ci			       !inc || lladdr || !dev->addr_len);
9368c2ecf20Sopenharmony_ci	if (neigh)
9378c2ecf20Sopenharmony_ci		ndisc_update(dev, neigh, lladdr, NUD_STALE,
9388c2ecf20Sopenharmony_ci			     NEIGH_UPDATE_F_WEAK_OVERRIDE|
9398c2ecf20Sopenharmony_ci			     NEIGH_UPDATE_F_OVERRIDE,
9408c2ecf20Sopenharmony_ci			     NDISC_NEIGHBOUR_SOLICITATION, &ndopts);
9418c2ecf20Sopenharmony_ci	if (neigh || !dev->header_ops) {
9428c2ecf20Sopenharmony_ci		ndisc_send_na(dev, saddr, &msg->target, !!is_router,
9438c2ecf20Sopenharmony_ci			      true, (ifp != NULL && inc), inc);
9448c2ecf20Sopenharmony_ci		if (neigh)
9458c2ecf20Sopenharmony_ci			neigh_release(neigh);
9468c2ecf20Sopenharmony_ci	}
9478c2ecf20Sopenharmony_ci
9488c2ecf20Sopenharmony_ciout:
9498c2ecf20Sopenharmony_ci	if (ifp)
9508c2ecf20Sopenharmony_ci		in6_ifa_put(ifp);
9518c2ecf20Sopenharmony_ci	else
9528c2ecf20Sopenharmony_ci		in6_dev_put(idev);
9538c2ecf20Sopenharmony_ci}
9548c2ecf20Sopenharmony_ci
9558c2ecf20Sopenharmony_cistatic void ndisc_recv_na(struct sk_buff *skb)
9568c2ecf20Sopenharmony_ci{
9578c2ecf20Sopenharmony_ci	struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb);
9588c2ecf20Sopenharmony_ci	struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
9598c2ecf20Sopenharmony_ci	const struct in6_addr *daddr = &ipv6_hdr(skb)->daddr;
9608c2ecf20Sopenharmony_ci	u8 *lladdr = NULL;
9618c2ecf20Sopenharmony_ci	u32 ndoptlen = skb_tail_pointer(skb) - (skb_transport_header(skb) +
9628c2ecf20Sopenharmony_ci				    offsetof(struct nd_msg, opt));
9638c2ecf20Sopenharmony_ci	struct ndisc_options ndopts;
9648c2ecf20Sopenharmony_ci	struct net_device *dev = skb->dev;
9658c2ecf20Sopenharmony_ci	struct inet6_dev *idev = __in6_dev_get(dev);
9668c2ecf20Sopenharmony_ci	struct inet6_ifaddr *ifp;
9678c2ecf20Sopenharmony_ci	struct neighbour *neigh;
9688c2ecf20Sopenharmony_ci
9698c2ecf20Sopenharmony_ci	if (skb->len < sizeof(struct nd_msg)) {
9708c2ecf20Sopenharmony_ci		ND_PRINTK(2, warn, "NA: packet too short\n");
9718c2ecf20Sopenharmony_ci		return;
9728c2ecf20Sopenharmony_ci	}
9738c2ecf20Sopenharmony_ci
9748c2ecf20Sopenharmony_ci	if (ipv6_addr_is_multicast(&msg->target)) {
9758c2ecf20Sopenharmony_ci		ND_PRINTK(2, warn, "NA: target address is multicast\n");
9768c2ecf20Sopenharmony_ci		return;
9778c2ecf20Sopenharmony_ci	}
9788c2ecf20Sopenharmony_ci
9798c2ecf20Sopenharmony_ci	if (ipv6_addr_is_multicast(daddr) &&
9808c2ecf20Sopenharmony_ci	    msg->icmph.icmp6_solicited) {
9818c2ecf20Sopenharmony_ci		ND_PRINTK(2, warn, "NA: solicited NA is multicasted\n");
9828c2ecf20Sopenharmony_ci		return;
9838c2ecf20Sopenharmony_ci	}
9848c2ecf20Sopenharmony_ci
9858c2ecf20Sopenharmony_ci	/* For some 802.11 wireless deployments (and possibly other networks),
9868c2ecf20Sopenharmony_ci	 * there will be a NA proxy and unsolicitd packets are attacks
9878c2ecf20Sopenharmony_ci	 * and thus should not be accepted.
9888c2ecf20Sopenharmony_ci	 */
9898c2ecf20Sopenharmony_ci	if (!msg->icmph.icmp6_solicited && idev &&
9908c2ecf20Sopenharmony_ci	    idev->cnf.drop_unsolicited_na)
9918c2ecf20Sopenharmony_ci		return;
9928c2ecf20Sopenharmony_ci
9938c2ecf20Sopenharmony_ci	if (!ndisc_parse_options(dev, msg->opt, ndoptlen, &ndopts)) {
9948c2ecf20Sopenharmony_ci		ND_PRINTK(2, warn, "NS: invalid ND option\n");
9958c2ecf20Sopenharmony_ci		return;
9968c2ecf20Sopenharmony_ci	}
9978c2ecf20Sopenharmony_ci	if (ndopts.nd_opts_tgt_lladdr) {
9988c2ecf20Sopenharmony_ci		lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr, dev);
9998c2ecf20Sopenharmony_ci		if (!lladdr) {
10008c2ecf20Sopenharmony_ci			ND_PRINTK(2, warn,
10018c2ecf20Sopenharmony_ci				  "NA: invalid link-layer address length\n");
10028c2ecf20Sopenharmony_ci			return;
10038c2ecf20Sopenharmony_ci		}
10048c2ecf20Sopenharmony_ci	}
10058c2ecf20Sopenharmony_ci	ifp = ipv6_get_ifaddr(dev_net(dev), &msg->target, dev, 1);
10068c2ecf20Sopenharmony_ci	if (ifp) {
10078c2ecf20Sopenharmony_ci		if (skb->pkt_type != PACKET_LOOPBACK
10088c2ecf20Sopenharmony_ci		    && (ifp->flags & IFA_F_TENTATIVE)) {
10098c2ecf20Sopenharmony_ci				addrconf_dad_failure(skb, ifp);
10108c2ecf20Sopenharmony_ci				return;
10118c2ecf20Sopenharmony_ci		}
10128c2ecf20Sopenharmony_ci		/* What should we make now? The advertisement
10138c2ecf20Sopenharmony_ci		   is invalid, but ndisc specs say nothing
10148c2ecf20Sopenharmony_ci		   about it. It could be misconfiguration, or
10158c2ecf20Sopenharmony_ci		   an smart proxy agent tries to help us :-)
10168c2ecf20Sopenharmony_ci
10178c2ecf20Sopenharmony_ci		   We should not print the error if NA has been
10188c2ecf20Sopenharmony_ci		   received from loopback - it is just our own
10198c2ecf20Sopenharmony_ci		   unsolicited advertisement.
10208c2ecf20Sopenharmony_ci		 */
10218c2ecf20Sopenharmony_ci		if (skb->pkt_type != PACKET_LOOPBACK)
10228c2ecf20Sopenharmony_ci			ND_PRINTK(1, warn,
10238c2ecf20Sopenharmony_ci				  "NA: %pM advertised our address %pI6c on %s!\n",
10248c2ecf20Sopenharmony_ci				  eth_hdr(skb)->h_source, &ifp->addr, ifp->idev->dev->name);
10258c2ecf20Sopenharmony_ci		in6_ifa_put(ifp);
10268c2ecf20Sopenharmony_ci		return;
10278c2ecf20Sopenharmony_ci	}
10288c2ecf20Sopenharmony_ci	neigh = neigh_lookup(&nd_tbl, &msg->target, dev);
10298c2ecf20Sopenharmony_ci
10308c2ecf20Sopenharmony_ci	if (neigh) {
10318c2ecf20Sopenharmony_ci		u8 old_flags = neigh->flags;
10328c2ecf20Sopenharmony_ci		struct net *net = dev_net(dev);
10338c2ecf20Sopenharmony_ci
10348c2ecf20Sopenharmony_ci		if (neigh->nud_state & NUD_FAILED)
10358c2ecf20Sopenharmony_ci			goto out;
10368c2ecf20Sopenharmony_ci
10378c2ecf20Sopenharmony_ci		/*
10388c2ecf20Sopenharmony_ci		 * Don't update the neighbor cache entry on a proxy NA from
10398c2ecf20Sopenharmony_ci		 * ourselves because either the proxied node is off link or it
10408c2ecf20Sopenharmony_ci		 * has already sent a NA to us.
10418c2ecf20Sopenharmony_ci		 */
10428c2ecf20Sopenharmony_ci		if (lladdr && !memcmp(lladdr, dev->dev_addr, dev->addr_len) &&
10438c2ecf20Sopenharmony_ci		    net->ipv6.devconf_all->forwarding && net->ipv6.devconf_all->proxy_ndp &&
10448c2ecf20Sopenharmony_ci		    pneigh_lookup(&nd_tbl, net, &msg->target, dev, 0)) {
10458c2ecf20Sopenharmony_ci			/* XXX: idev->cnf.proxy_ndp */
10468c2ecf20Sopenharmony_ci			goto out;
10478c2ecf20Sopenharmony_ci		}
10488c2ecf20Sopenharmony_ci
10498c2ecf20Sopenharmony_ci		ndisc_update(dev, neigh, lladdr,
10508c2ecf20Sopenharmony_ci			     msg->icmph.icmp6_solicited ? NUD_REACHABLE : NUD_STALE,
10518c2ecf20Sopenharmony_ci			     NEIGH_UPDATE_F_WEAK_OVERRIDE|
10528c2ecf20Sopenharmony_ci			     (msg->icmph.icmp6_override ? NEIGH_UPDATE_F_OVERRIDE : 0)|
10538c2ecf20Sopenharmony_ci			     NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
10548c2ecf20Sopenharmony_ci			     (msg->icmph.icmp6_router ? NEIGH_UPDATE_F_ISROUTER : 0),
10558c2ecf20Sopenharmony_ci			     NDISC_NEIGHBOUR_ADVERTISEMENT, &ndopts);
10568c2ecf20Sopenharmony_ci
10578c2ecf20Sopenharmony_ci		if ((old_flags & ~neigh->flags) & NTF_ROUTER) {
10588c2ecf20Sopenharmony_ci			/*
10598c2ecf20Sopenharmony_ci			 * Change: router to host
10608c2ecf20Sopenharmony_ci			 */
10618c2ecf20Sopenharmony_ci			rt6_clean_tohost(dev_net(dev),  saddr);
10628c2ecf20Sopenharmony_ci		}
10638c2ecf20Sopenharmony_ci
10648c2ecf20Sopenharmony_ciout:
10658c2ecf20Sopenharmony_ci		neigh_release(neigh);
10668c2ecf20Sopenharmony_ci	}
10678c2ecf20Sopenharmony_ci}
10688c2ecf20Sopenharmony_ci
10698c2ecf20Sopenharmony_cistatic void ndisc_recv_rs(struct sk_buff *skb)
10708c2ecf20Sopenharmony_ci{
10718c2ecf20Sopenharmony_ci	struct rs_msg *rs_msg = (struct rs_msg *)skb_transport_header(skb);
10728c2ecf20Sopenharmony_ci	unsigned long ndoptlen = skb->len - sizeof(*rs_msg);
10738c2ecf20Sopenharmony_ci	struct neighbour *neigh;
10748c2ecf20Sopenharmony_ci	struct inet6_dev *idev;
10758c2ecf20Sopenharmony_ci	const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
10768c2ecf20Sopenharmony_ci	struct ndisc_options ndopts;
10778c2ecf20Sopenharmony_ci	u8 *lladdr = NULL;
10788c2ecf20Sopenharmony_ci
10798c2ecf20Sopenharmony_ci	if (skb->len < sizeof(*rs_msg))
10808c2ecf20Sopenharmony_ci		return;
10818c2ecf20Sopenharmony_ci
10828c2ecf20Sopenharmony_ci	idev = __in6_dev_get(skb->dev);
10838c2ecf20Sopenharmony_ci	if (!idev) {
10848c2ecf20Sopenharmony_ci		ND_PRINTK(1, err, "RS: can't find in6 device\n");
10858c2ecf20Sopenharmony_ci		return;
10868c2ecf20Sopenharmony_ci	}
10878c2ecf20Sopenharmony_ci
10888c2ecf20Sopenharmony_ci	/* Don't accept RS if we're not in router mode */
10898c2ecf20Sopenharmony_ci	if (!idev->cnf.forwarding)
10908c2ecf20Sopenharmony_ci		goto out;
10918c2ecf20Sopenharmony_ci
10928c2ecf20Sopenharmony_ci	/*
10938c2ecf20Sopenharmony_ci	 * Don't update NCE if src = ::;
10948c2ecf20Sopenharmony_ci	 * this implies that the source node has no ip address assigned yet.
10958c2ecf20Sopenharmony_ci	 */
10968c2ecf20Sopenharmony_ci	if (ipv6_addr_any(saddr))
10978c2ecf20Sopenharmony_ci		goto out;
10988c2ecf20Sopenharmony_ci
10998c2ecf20Sopenharmony_ci	/* Parse ND options */
11008c2ecf20Sopenharmony_ci	if (!ndisc_parse_options(skb->dev, rs_msg->opt, ndoptlen, &ndopts)) {
11018c2ecf20Sopenharmony_ci		ND_PRINTK(2, notice, "NS: invalid ND option, ignored\n");
11028c2ecf20Sopenharmony_ci		goto out;
11038c2ecf20Sopenharmony_ci	}
11048c2ecf20Sopenharmony_ci
11058c2ecf20Sopenharmony_ci	if (ndopts.nd_opts_src_lladdr) {
11068c2ecf20Sopenharmony_ci		lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr,
11078c2ecf20Sopenharmony_ci					     skb->dev);
11088c2ecf20Sopenharmony_ci		if (!lladdr)
11098c2ecf20Sopenharmony_ci			goto out;
11108c2ecf20Sopenharmony_ci	}
11118c2ecf20Sopenharmony_ci
11128c2ecf20Sopenharmony_ci	neigh = __neigh_lookup(&nd_tbl, saddr, skb->dev, 1);
11138c2ecf20Sopenharmony_ci	if (neigh) {
11148c2ecf20Sopenharmony_ci		ndisc_update(skb->dev, neigh, lladdr, NUD_STALE,
11158c2ecf20Sopenharmony_ci			     NEIGH_UPDATE_F_WEAK_OVERRIDE|
11168c2ecf20Sopenharmony_ci			     NEIGH_UPDATE_F_OVERRIDE|
11178c2ecf20Sopenharmony_ci			     NEIGH_UPDATE_F_OVERRIDE_ISROUTER,
11188c2ecf20Sopenharmony_ci			     NDISC_ROUTER_SOLICITATION, &ndopts);
11198c2ecf20Sopenharmony_ci		neigh_release(neigh);
11208c2ecf20Sopenharmony_ci	}
11218c2ecf20Sopenharmony_ciout:
11228c2ecf20Sopenharmony_ci	return;
11238c2ecf20Sopenharmony_ci}
11248c2ecf20Sopenharmony_ci
11258c2ecf20Sopenharmony_cistatic void ndisc_ra_useropt(struct sk_buff *ra, struct nd_opt_hdr *opt)
11268c2ecf20Sopenharmony_ci{
11278c2ecf20Sopenharmony_ci	struct icmp6hdr *icmp6h = (struct icmp6hdr *)skb_transport_header(ra);
11288c2ecf20Sopenharmony_ci	struct sk_buff *skb;
11298c2ecf20Sopenharmony_ci	struct nlmsghdr *nlh;
11308c2ecf20Sopenharmony_ci	struct nduseroptmsg *ndmsg;
11318c2ecf20Sopenharmony_ci	struct net *net = dev_net(ra->dev);
11328c2ecf20Sopenharmony_ci	int err;
11338c2ecf20Sopenharmony_ci	int base_size = NLMSG_ALIGN(sizeof(struct nduseroptmsg)
11348c2ecf20Sopenharmony_ci				    + (opt->nd_opt_len << 3));
11358c2ecf20Sopenharmony_ci	size_t msg_size = base_size + nla_total_size(sizeof(struct in6_addr));
11368c2ecf20Sopenharmony_ci
11378c2ecf20Sopenharmony_ci	skb = nlmsg_new(msg_size, GFP_ATOMIC);
11388c2ecf20Sopenharmony_ci	if (!skb) {
11398c2ecf20Sopenharmony_ci		err = -ENOBUFS;
11408c2ecf20Sopenharmony_ci		goto errout;
11418c2ecf20Sopenharmony_ci	}
11428c2ecf20Sopenharmony_ci
11438c2ecf20Sopenharmony_ci	nlh = nlmsg_put(skb, 0, 0, RTM_NEWNDUSEROPT, base_size, 0);
11448c2ecf20Sopenharmony_ci	if (!nlh) {
11458c2ecf20Sopenharmony_ci		goto nla_put_failure;
11468c2ecf20Sopenharmony_ci	}
11478c2ecf20Sopenharmony_ci
11488c2ecf20Sopenharmony_ci	ndmsg = nlmsg_data(nlh);
11498c2ecf20Sopenharmony_ci	ndmsg->nduseropt_family = AF_INET6;
11508c2ecf20Sopenharmony_ci	ndmsg->nduseropt_ifindex = ra->dev->ifindex;
11518c2ecf20Sopenharmony_ci	ndmsg->nduseropt_icmp_type = icmp6h->icmp6_type;
11528c2ecf20Sopenharmony_ci	ndmsg->nduseropt_icmp_code = icmp6h->icmp6_code;
11538c2ecf20Sopenharmony_ci	ndmsg->nduseropt_opts_len = opt->nd_opt_len << 3;
11548c2ecf20Sopenharmony_ci
11558c2ecf20Sopenharmony_ci	memcpy(ndmsg + 1, opt, opt->nd_opt_len << 3);
11568c2ecf20Sopenharmony_ci
11578c2ecf20Sopenharmony_ci	if (nla_put_in6_addr(skb, NDUSEROPT_SRCADDR, &ipv6_hdr(ra)->saddr))
11588c2ecf20Sopenharmony_ci		goto nla_put_failure;
11598c2ecf20Sopenharmony_ci	nlmsg_end(skb, nlh);
11608c2ecf20Sopenharmony_ci
11618c2ecf20Sopenharmony_ci	rtnl_notify(skb, net, 0, RTNLGRP_ND_USEROPT, NULL, GFP_ATOMIC);
11628c2ecf20Sopenharmony_ci	return;
11638c2ecf20Sopenharmony_ci
11648c2ecf20Sopenharmony_cinla_put_failure:
11658c2ecf20Sopenharmony_ci	nlmsg_free(skb);
11668c2ecf20Sopenharmony_ci	err = -EMSGSIZE;
11678c2ecf20Sopenharmony_cierrout:
11688c2ecf20Sopenharmony_ci	rtnl_set_sk_err(net, RTNLGRP_ND_USEROPT, err);
11698c2ecf20Sopenharmony_ci}
11708c2ecf20Sopenharmony_ci
11718c2ecf20Sopenharmony_cistatic void ndisc_router_discovery(struct sk_buff *skb)
11728c2ecf20Sopenharmony_ci{
11738c2ecf20Sopenharmony_ci	struct ra_msg *ra_msg = (struct ra_msg *)skb_transport_header(skb);
11748c2ecf20Sopenharmony_ci	struct neighbour *neigh = NULL;
11758c2ecf20Sopenharmony_ci	struct inet6_dev *in6_dev;
11768c2ecf20Sopenharmony_ci	struct fib6_info *rt = NULL;
11778c2ecf20Sopenharmony_ci	struct net *net;
11788c2ecf20Sopenharmony_ci	int lifetime;
11798c2ecf20Sopenharmony_ci	struct ndisc_options ndopts;
11808c2ecf20Sopenharmony_ci	int optlen;
11818c2ecf20Sopenharmony_ci	unsigned int pref = 0;
11828c2ecf20Sopenharmony_ci	__u32 old_if_flags;
11838c2ecf20Sopenharmony_ci	bool send_ifinfo_notify = false;
11848c2ecf20Sopenharmony_ci
11858c2ecf20Sopenharmony_ci	__u8 *opt = (__u8 *)(ra_msg + 1);
11868c2ecf20Sopenharmony_ci
11878c2ecf20Sopenharmony_ci	optlen = (skb_tail_pointer(skb) - skb_transport_header(skb)) -
11888c2ecf20Sopenharmony_ci		sizeof(struct ra_msg);
11898c2ecf20Sopenharmony_ci
11908c2ecf20Sopenharmony_ci	ND_PRINTK(2, info,
11918c2ecf20Sopenharmony_ci		  "RA: %s, dev: %s\n",
11928c2ecf20Sopenharmony_ci		  __func__, skb->dev->name);
11938c2ecf20Sopenharmony_ci	if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) {
11948c2ecf20Sopenharmony_ci		ND_PRINTK(2, warn, "RA: source address is not link-local\n");
11958c2ecf20Sopenharmony_ci		return;
11968c2ecf20Sopenharmony_ci	}
11978c2ecf20Sopenharmony_ci	if (optlen < 0) {
11988c2ecf20Sopenharmony_ci		ND_PRINTK(2, warn, "RA: packet too short\n");
11998c2ecf20Sopenharmony_ci		return;
12008c2ecf20Sopenharmony_ci	}
12018c2ecf20Sopenharmony_ci
12028c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_NDISC_NODETYPE
12038c2ecf20Sopenharmony_ci	if (skb->ndisc_nodetype == NDISC_NODETYPE_HOST) {
12048c2ecf20Sopenharmony_ci		ND_PRINTK(2, warn, "RA: from host or unauthorized router\n");
12058c2ecf20Sopenharmony_ci		return;
12068c2ecf20Sopenharmony_ci	}
12078c2ecf20Sopenharmony_ci#endif
12088c2ecf20Sopenharmony_ci
12098c2ecf20Sopenharmony_ci	/*
12108c2ecf20Sopenharmony_ci	 *	set the RA_RECV flag in the interface
12118c2ecf20Sopenharmony_ci	 */
12128c2ecf20Sopenharmony_ci
12138c2ecf20Sopenharmony_ci	in6_dev = __in6_dev_get(skb->dev);
12148c2ecf20Sopenharmony_ci	if (!in6_dev) {
12158c2ecf20Sopenharmony_ci		ND_PRINTK(0, err, "RA: can't find inet6 device for %s\n",
12168c2ecf20Sopenharmony_ci			  skb->dev->name);
12178c2ecf20Sopenharmony_ci		return;
12188c2ecf20Sopenharmony_ci	}
12198c2ecf20Sopenharmony_ci
12208c2ecf20Sopenharmony_ci	if (!ndisc_parse_options(skb->dev, opt, optlen, &ndopts)) {
12218c2ecf20Sopenharmony_ci		ND_PRINTK(2, warn, "RA: invalid ND options\n");
12228c2ecf20Sopenharmony_ci		return;
12238c2ecf20Sopenharmony_ci	}
12248c2ecf20Sopenharmony_ci
12258c2ecf20Sopenharmony_ci	if (!ipv6_accept_ra(in6_dev)) {
12268c2ecf20Sopenharmony_ci		ND_PRINTK(2, info,
12278c2ecf20Sopenharmony_ci			  "RA: %s, did not accept ra for dev: %s\n",
12288c2ecf20Sopenharmony_ci			  __func__, skb->dev->name);
12298c2ecf20Sopenharmony_ci		goto skip_linkparms;
12308c2ecf20Sopenharmony_ci	}
12318c2ecf20Sopenharmony_ci
12328c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_NDISC_NODETYPE
12338c2ecf20Sopenharmony_ci	/* skip link-specific parameters from interior routers */
12348c2ecf20Sopenharmony_ci	if (skb->ndisc_nodetype == NDISC_NODETYPE_NODEFAULT) {
12358c2ecf20Sopenharmony_ci		ND_PRINTK(2, info,
12368c2ecf20Sopenharmony_ci			  "RA: %s, nodetype is NODEFAULT, dev: %s\n",
12378c2ecf20Sopenharmony_ci			  __func__, skb->dev->name);
12388c2ecf20Sopenharmony_ci		goto skip_linkparms;
12398c2ecf20Sopenharmony_ci	}
12408c2ecf20Sopenharmony_ci#endif
12418c2ecf20Sopenharmony_ci
12428c2ecf20Sopenharmony_ci	if (in6_dev->if_flags & IF_RS_SENT) {
12438c2ecf20Sopenharmony_ci		/*
12448c2ecf20Sopenharmony_ci		 *	flag that an RA was received after an RS was sent
12458c2ecf20Sopenharmony_ci		 *	out on this interface.
12468c2ecf20Sopenharmony_ci		 */
12478c2ecf20Sopenharmony_ci		in6_dev->if_flags |= IF_RA_RCVD;
12488c2ecf20Sopenharmony_ci	}
12498c2ecf20Sopenharmony_ci
12508c2ecf20Sopenharmony_ci	/*
12518c2ecf20Sopenharmony_ci	 * Remember the managed/otherconf flags from most recently
12528c2ecf20Sopenharmony_ci	 * received RA message (RFC 2462) -- yoshfuji
12538c2ecf20Sopenharmony_ci	 */
12548c2ecf20Sopenharmony_ci	old_if_flags = in6_dev->if_flags;
12558c2ecf20Sopenharmony_ci	in6_dev->if_flags = (in6_dev->if_flags & ~(IF_RA_MANAGED |
12568c2ecf20Sopenharmony_ci				IF_RA_OTHERCONF)) |
12578c2ecf20Sopenharmony_ci				(ra_msg->icmph.icmp6_addrconf_managed ?
12588c2ecf20Sopenharmony_ci					IF_RA_MANAGED : 0) |
12598c2ecf20Sopenharmony_ci				(ra_msg->icmph.icmp6_addrconf_other ?
12608c2ecf20Sopenharmony_ci					IF_RA_OTHERCONF : 0);
12618c2ecf20Sopenharmony_ci
12628c2ecf20Sopenharmony_ci	if (old_if_flags != in6_dev->if_flags)
12638c2ecf20Sopenharmony_ci		send_ifinfo_notify = true;
12648c2ecf20Sopenharmony_ci
12658c2ecf20Sopenharmony_ci	if (!in6_dev->cnf.accept_ra_defrtr) {
12668c2ecf20Sopenharmony_ci		ND_PRINTK(2, info,
12678c2ecf20Sopenharmony_ci			  "RA: %s, defrtr is false for dev: %s\n",
12688c2ecf20Sopenharmony_ci			  __func__, skb->dev->name);
12698c2ecf20Sopenharmony_ci		goto skip_defrtr;
12708c2ecf20Sopenharmony_ci	}
12718c2ecf20Sopenharmony_ci
12728c2ecf20Sopenharmony_ci	lifetime = ntohs(ra_msg->icmph.icmp6_rt_lifetime);
12738c2ecf20Sopenharmony_ci	if (lifetime != 0 && lifetime < in6_dev->cnf.accept_ra_min_lft) {
12748c2ecf20Sopenharmony_ci		ND_PRINTK(2, info,
12758c2ecf20Sopenharmony_ci			  "RA: router lifetime (%ds) is too short: %s\n",
12768c2ecf20Sopenharmony_ci			  lifetime, skb->dev->name);
12778c2ecf20Sopenharmony_ci		goto skip_defrtr;
12788c2ecf20Sopenharmony_ci	}
12798c2ecf20Sopenharmony_ci
12808c2ecf20Sopenharmony_ci	/* Do not accept RA with source-addr found on local machine unless
12818c2ecf20Sopenharmony_ci	 * accept_ra_from_local is set to true.
12828c2ecf20Sopenharmony_ci	 */
12838c2ecf20Sopenharmony_ci	net = dev_net(in6_dev->dev);
12848c2ecf20Sopenharmony_ci	if (!in6_dev->cnf.accept_ra_from_local &&
12858c2ecf20Sopenharmony_ci	    ipv6_chk_addr(net, &ipv6_hdr(skb)->saddr, in6_dev->dev, 0)) {
12868c2ecf20Sopenharmony_ci		ND_PRINTK(2, info,
12878c2ecf20Sopenharmony_ci			  "RA from local address detected on dev: %s: default router ignored\n",
12888c2ecf20Sopenharmony_ci			  skb->dev->name);
12898c2ecf20Sopenharmony_ci		goto skip_defrtr;
12908c2ecf20Sopenharmony_ci	}
12918c2ecf20Sopenharmony_ci
12928c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_ROUTER_PREF
12938c2ecf20Sopenharmony_ci	pref = ra_msg->icmph.icmp6_router_pref;
12948c2ecf20Sopenharmony_ci	/* 10b is handled as if it were 00b (medium) */
12958c2ecf20Sopenharmony_ci	if (pref == ICMPV6_ROUTER_PREF_INVALID ||
12968c2ecf20Sopenharmony_ci	    !in6_dev->cnf.accept_ra_rtr_pref)
12978c2ecf20Sopenharmony_ci		pref = ICMPV6_ROUTER_PREF_MEDIUM;
12988c2ecf20Sopenharmony_ci#endif
12998c2ecf20Sopenharmony_ci	/* routes added from RAs do not use nexthop objects */
13008c2ecf20Sopenharmony_ci	rt = rt6_get_dflt_router(net, &ipv6_hdr(skb)->saddr, skb->dev);
13018c2ecf20Sopenharmony_ci	if (rt) {
13028c2ecf20Sopenharmony_ci		neigh = ip6_neigh_lookup(&rt->fib6_nh->fib_nh_gw6,
13038c2ecf20Sopenharmony_ci					 rt->fib6_nh->fib_nh_dev, NULL,
13048c2ecf20Sopenharmony_ci					  &ipv6_hdr(skb)->saddr);
13058c2ecf20Sopenharmony_ci		if (!neigh) {
13068c2ecf20Sopenharmony_ci			ND_PRINTK(0, err,
13078c2ecf20Sopenharmony_ci				  "RA: %s got default router without neighbour\n",
13088c2ecf20Sopenharmony_ci				  __func__);
13098c2ecf20Sopenharmony_ci			fib6_info_release(rt);
13108c2ecf20Sopenharmony_ci			return;
13118c2ecf20Sopenharmony_ci		}
13128c2ecf20Sopenharmony_ci	}
13138c2ecf20Sopenharmony_ci	if (rt && lifetime == 0) {
13148c2ecf20Sopenharmony_ci		ip6_del_rt(net, rt, false);
13158c2ecf20Sopenharmony_ci		rt = NULL;
13168c2ecf20Sopenharmony_ci	}
13178c2ecf20Sopenharmony_ci
13188c2ecf20Sopenharmony_ci	ND_PRINTK(3, info, "RA: rt: %p  lifetime: %d, for dev: %s\n",
13198c2ecf20Sopenharmony_ci		  rt, lifetime, skb->dev->name);
13208c2ecf20Sopenharmony_ci	if (!rt && lifetime) {
13218c2ecf20Sopenharmony_ci		ND_PRINTK(3, info, "RA: adding default router\n");
13228c2ecf20Sopenharmony_ci
13238c2ecf20Sopenharmony_ci		rt = rt6_add_dflt_router(net, &ipv6_hdr(skb)->saddr,
13248c2ecf20Sopenharmony_ci					 skb->dev, pref);
13258c2ecf20Sopenharmony_ci		if (!rt) {
13268c2ecf20Sopenharmony_ci			ND_PRINTK(0, err,
13278c2ecf20Sopenharmony_ci				  "RA: %s failed to add default route\n",
13288c2ecf20Sopenharmony_ci				  __func__);
13298c2ecf20Sopenharmony_ci			return;
13308c2ecf20Sopenharmony_ci		}
13318c2ecf20Sopenharmony_ci
13328c2ecf20Sopenharmony_ci		neigh = ip6_neigh_lookup(&rt->fib6_nh->fib_nh_gw6,
13338c2ecf20Sopenharmony_ci					 rt->fib6_nh->fib_nh_dev, NULL,
13348c2ecf20Sopenharmony_ci					  &ipv6_hdr(skb)->saddr);
13358c2ecf20Sopenharmony_ci		if (!neigh) {
13368c2ecf20Sopenharmony_ci			ND_PRINTK(0, err,
13378c2ecf20Sopenharmony_ci				  "RA: %s got default router without neighbour\n",
13388c2ecf20Sopenharmony_ci				  __func__);
13398c2ecf20Sopenharmony_ci			fib6_info_release(rt);
13408c2ecf20Sopenharmony_ci			return;
13418c2ecf20Sopenharmony_ci		}
13428c2ecf20Sopenharmony_ci		neigh->flags |= NTF_ROUTER;
13438c2ecf20Sopenharmony_ci	} else if (rt) {
13448c2ecf20Sopenharmony_ci		rt->fib6_flags = (rt->fib6_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);
13458c2ecf20Sopenharmony_ci	}
13468c2ecf20Sopenharmony_ci
13478c2ecf20Sopenharmony_ci	if (rt)
13488c2ecf20Sopenharmony_ci		fib6_set_expires(rt, jiffies + (HZ * lifetime));
13498c2ecf20Sopenharmony_ci	if (in6_dev->cnf.accept_ra_min_hop_limit < 256 &&
13508c2ecf20Sopenharmony_ci	    ra_msg->icmph.icmp6_hop_limit) {
13518c2ecf20Sopenharmony_ci		if (in6_dev->cnf.accept_ra_min_hop_limit <= ra_msg->icmph.icmp6_hop_limit) {
13528c2ecf20Sopenharmony_ci			in6_dev->cnf.hop_limit = ra_msg->icmph.icmp6_hop_limit;
13538c2ecf20Sopenharmony_ci			fib6_metric_set(rt, RTAX_HOPLIMIT,
13548c2ecf20Sopenharmony_ci					ra_msg->icmph.icmp6_hop_limit);
13558c2ecf20Sopenharmony_ci		} else {
13568c2ecf20Sopenharmony_ci			ND_PRINTK(2, warn, "RA: Got route advertisement with lower hop_limit than minimum\n");
13578c2ecf20Sopenharmony_ci		}
13588c2ecf20Sopenharmony_ci	}
13598c2ecf20Sopenharmony_ci
13608c2ecf20Sopenharmony_ciskip_defrtr:
13618c2ecf20Sopenharmony_ci
13628c2ecf20Sopenharmony_ci	/*
13638c2ecf20Sopenharmony_ci	 *	Update Reachable Time and Retrans Timer
13648c2ecf20Sopenharmony_ci	 */
13658c2ecf20Sopenharmony_ci
13668c2ecf20Sopenharmony_ci	if (in6_dev->nd_parms) {
13678c2ecf20Sopenharmony_ci		unsigned long rtime = ntohl(ra_msg->retrans_timer);
13688c2ecf20Sopenharmony_ci
13698c2ecf20Sopenharmony_ci		if (rtime && rtime/1000 < MAX_SCHEDULE_TIMEOUT/HZ) {
13708c2ecf20Sopenharmony_ci			rtime = (rtime*HZ)/1000;
13718c2ecf20Sopenharmony_ci			if (rtime < HZ/100)
13728c2ecf20Sopenharmony_ci				rtime = HZ/100;
13738c2ecf20Sopenharmony_ci			NEIGH_VAR_SET(in6_dev->nd_parms, RETRANS_TIME, rtime);
13748c2ecf20Sopenharmony_ci			in6_dev->tstamp = jiffies;
13758c2ecf20Sopenharmony_ci			send_ifinfo_notify = true;
13768c2ecf20Sopenharmony_ci		}
13778c2ecf20Sopenharmony_ci
13788c2ecf20Sopenharmony_ci		rtime = ntohl(ra_msg->reachable_time);
13798c2ecf20Sopenharmony_ci		if (rtime && rtime/1000 < MAX_SCHEDULE_TIMEOUT/(3*HZ)) {
13808c2ecf20Sopenharmony_ci			rtime = (rtime*HZ)/1000;
13818c2ecf20Sopenharmony_ci
13828c2ecf20Sopenharmony_ci			if (rtime < HZ/10)
13838c2ecf20Sopenharmony_ci				rtime = HZ/10;
13848c2ecf20Sopenharmony_ci
13858c2ecf20Sopenharmony_ci			if (rtime != NEIGH_VAR(in6_dev->nd_parms, BASE_REACHABLE_TIME)) {
13868c2ecf20Sopenharmony_ci				NEIGH_VAR_SET(in6_dev->nd_parms,
13878c2ecf20Sopenharmony_ci					      BASE_REACHABLE_TIME, rtime);
13888c2ecf20Sopenharmony_ci				NEIGH_VAR_SET(in6_dev->nd_parms,
13898c2ecf20Sopenharmony_ci					      GC_STALETIME, 3 * rtime);
13908c2ecf20Sopenharmony_ci				in6_dev->nd_parms->reachable_time = neigh_rand_reach_time(rtime);
13918c2ecf20Sopenharmony_ci				in6_dev->tstamp = jiffies;
13928c2ecf20Sopenharmony_ci				send_ifinfo_notify = true;
13938c2ecf20Sopenharmony_ci			}
13948c2ecf20Sopenharmony_ci		}
13958c2ecf20Sopenharmony_ci	}
13968c2ecf20Sopenharmony_ci
13978c2ecf20Sopenharmony_ci	/*
13988c2ecf20Sopenharmony_ci	 *	Send a notify if RA changed managed/otherconf flags or timer settings
13998c2ecf20Sopenharmony_ci	 */
14008c2ecf20Sopenharmony_ci	if (send_ifinfo_notify)
14018c2ecf20Sopenharmony_ci		inet6_ifinfo_notify(RTM_NEWLINK, in6_dev);
14028c2ecf20Sopenharmony_ci
14038c2ecf20Sopenharmony_ciskip_linkparms:
14048c2ecf20Sopenharmony_ci
14058c2ecf20Sopenharmony_ci	/*
14068c2ecf20Sopenharmony_ci	 *	Process options.
14078c2ecf20Sopenharmony_ci	 */
14088c2ecf20Sopenharmony_ci
14098c2ecf20Sopenharmony_ci	if (!neigh)
14108c2ecf20Sopenharmony_ci		neigh = __neigh_lookup(&nd_tbl, &ipv6_hdr(skb)->saddr,
14118c2ecf20Sopenharmony_ci				       skb->dev, 1);
14128c2ecf20Sopenharmony_ci	if (neigh) {
14138c2ecf20Sopenharmony_ci		u8 *lladdr = NULL;
14148c2ecf20Sopenharmony_ci		if (ndopts.nd_opts_src_lladdr) {
14158c2ecf20Sopenharmony_ci			lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr,
14168c2ecf20Sopenharmony_ci						     skb->dev);
14178c2ecf20Sopenharmony_ci			if (!lladdr) {
14188c2ecf20Sopenharmony_ci				ND_PRINTK(2, warn,
14198c2ecf20Sopenharmony_ci					  "RA: invalid link-layer address length\n");
14208c2ecf20Sopenharmony_ci				goto out;
14218c2ecf20Sopenharmony_ci			}
14228c2ecf20Sopenharmony_ci		}
14238c2ecf20Sopenharmony_ci		ndisc_update(skb->dev, neigh, lladdr, NUD_STALE,
14248c2ecf20Sopenharmony_ci			     NEIGH_UPDATE_F_WEAK_OVERRIDE|
14258c2ecf20Sopenharmony_ci			     NEIGH_UPDATE_F_OVERRIDE|
14268c2ecf20Sopenharmony_ci			     NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
14278c2ecf20Sopenharmony_ci			     NEIGH_UPDATE_F_ISROUTER,
14288c2ecf20Sopenharmony_ci			     NDISC_ROUTER_ADVERTISEMENT, &ndopts);
14298c2ecf20Sopenharmony_ci	}
14308c2ecf20Sopenharmony_ci
14318c2ecf20Sopenharmony_ci	if (!ipv6_accept_ra(in6_dev)) {
14328c2ecf20Sopenharmony_ci		ND_PRINTK(2, info,
14338c2ecf20Sopenharmony_ci			  "RA: %s, accept_ra is false for dev: %s\n",
14348c2ecf20Sopenharmony_ci			  __func__, skb->dev->name);
14358c2ecf20Sopenharmony_ci		goto out;
14368c2ecf20Sopenharmony_ci	}
14378c2ecf20Sopenharmony_ci
14388c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_ROUTE_INFO
14398c2ecf20Sopenharmony_ci	if (!in6_dev->cnf.accept_ra_from_local &&
14408c2ecf20Sopenharmony_ci	    ipv6_chk_addr(dev_net(in6_dev->dev), &ipv6_hdr(skb)->saddr,
14418c2ecf20Sopenharmony_ci			  in6_dev->dev, 0)) {
14428c2ecf20Sopenharmony_ci		ND_PRINTK(2, info,
14438c2ecf20Sopenharmony_ci			  "RA from local address detected on dev: %s: router info ignored.\n",
14448c2ecf20Sopenharmony_ci			  skb->dev->name);
14458c2ecf20Sopenharmony_ci		goto skip_routeinfo;
14468c2ecf20Sopenharmony_ci	}
14478c2ecf20Sopenharmony_ci
14488c2ecf20Sopenharmony_ci	if (in6_dev->cnf.accept_ra_rtr_pref && ndopts.nd_opts_ri) {
14498c2ecf20Sopenharmony_ci		struct nd_opt_hdr *p;
14508c2ecf20Sopenharmony_ci		for (p = ndopts.nd_opts_ri;
14518c2ecf20Sopenharmony_ci		     p;
14528c2ecf20Sopenharmony_ci		     p = ndisc_next_option(p, ndopts.nd_opts_ri_end)) {
14538c2ecf20Sopenharmony_ci			struct route_info *ri = (struct route_info *)p;
14548c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_NDISC_NODETYPE
14558c2ecf20Sopenharmony_ci			if (skb->ndisc_nodetype == NDISC_NODETYPE_NODEFAULT &&
14568c2ecf20Sopenharmony_ci			    ri->prefix_len == 0)
14578c2ecf20Sopenharmony_ci				continue;
14588c2ecf20Sopenharmony_ci#endif
14598c2ecf20Sopenharmony_ci			if (ri->prefix_len == 0 &&
14608c2ecf20Sopenharmony_ci			    !in6_dev->cnf.accept_ra_defrtr)
14618c2ecf20Sopenharmony_ci				continue;
14628c2ecf20Sopenharmony_ci			if (ri->lifetime != 0 &&
14638c2ecf20Sopenharmony_ci			    ntohl(ri->lifetime) < in6_dev->cnf.accept_ra_min_lft)
14648c2ecf20Sopenharmony_ci				continue;
14658c2ecf20Sopenharmony_ci			if (ri->prefix_len < in6_dev->cnf.accept_ra_rt_info_min_plen)
14668c2ecf20Sopenharmony_ci				continue;
14678c2ecf20Sopenharmony_ci			if (ri->prefix_len > in6_dev->cnf.accept_ra_rt_info_max_plen)
14688c2ecf20Sopenharmony_ci				continue;
14698c2ecf20Sopenharmony_ci			rt6_route_rcv(skb->dev, (u8 *)p, (p->nd_opt_len) << 3,
14708c2ecf20Sopenharmony_ci				      &ipv6_hdr(skb)->saddr);
14718c2ecf20Sopenharmony_ci		}
14728c2ecf20Sopenharmony_ci	}
14738c2ecf20Sopenharmony_ci
14748c2ecf20Sopenharmony_ciskip_routeinfo:
14758c2ecf20Sopenharmony_ci#endif
14768c2ecf20Sopenharmony_ci
14778c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_NDISC_NODETYPE
14788c2ecf20Sopenharmony_ci	/* skip link-specific ndopts from interior routers */
14798c2ecf20Sopenharmony_ci	if (skb->ndisc_nodetype == NDISC_NODETYPE_NODEFAULT) {
14808c2ecf20Sopenharmony_ci		ND_PRINTK(2, info,
14818c2ecf20Sopenharmony_ci			  "RA: %s, nodetype is NODEFAULT (interior routes), dev: %s\n",
14828c2ecf20Sopenharmony_ci			  __func__, skb->dev->name);
14838c2ecf20Sopenharmony_ci		goto out;
14848c2ecf20Sopenharmony_ci	}
14858c2ecf20Sopenharmony_ci#endif
14868c2ecf20Sopenharmony_ci
14878c2ecf20Sopenharmony_ci	if (in6_dev->cnf.accept_ra_pinfo && ndopts.nd_opts_pi) {
14888c2ecf20Sopenharmony_ci		struct nd_opt_hdr *p;
14898c2ecf20Sopenharmony_ci		for (p = ndopts.nd_opts_pi;
14908c2ecf20Sopenharmony_ci		     p;
14918c2ecf20Sopenharmony_ci		     p = ndisc_next_option(p, ndopts.nd_opts_pi_end)) {
14928c2ecf20Sopenharmony_ci			addrconf_prefix_rcv(skb->dev, (u8 *)p,
14938c2ecf20Sopenharmony_ci					    (p->nd_opt_len) << 3,
14948c2ecf20Sopenharmony_ci					    ndopts.nd_opts_src_lladdr != NULL);
14958c2ecf20Sopenharmony_ci		}
14968c2ecf20Sopenharmony_ci	}
14978c2ecf20Sopenharmony_ci
14988c2ecf20Sopenharmony_ci	if (ndopts.nd_opts_mtu && in6_dev->cnf.accept_ra_mtu) {
14998c2ecf20Sopenharmony_ci		__be32 n;
15008c2ecf20Sopenharmony_ci		u32 mtu;
15018c2ecf20Sopenharmony_ci
15028c2ecf20Sopenharmony_ci		memcpy(&n, ((u8 *)(ndopts.nd_opts_mtu+1))+2, sizeof(mtu));
15038c2ecf20Sopenharmony_ci		mtu = ntohl(n);
15048c2ecf20Sopenharmony_ci
15058c2ecf20Sopenharmony_ci		if (mtu < IPV6_MIN_MTU || mtu > skb->dev->mtu) {
15068c2ecf20Sopenharmony_ci			ND_PRINTK(2, warn, "RA: invalid mtu: %d\n", mtu);
15078c2ecf20Sopenharmony_ci		} else if (in6_dev->cnf.mtu6 != mtu) {
15088c2ecf20Sopenharmony_ci			in6_dev->cnf.mtu6 = mtu;
15098c2ecf20Sopenharmony_ci			fib6_metric_set(rt, RTAX_MTU, mtu);
15108c2ecf20Sopenharmony_ci			rt6_mtu_change(skb->dev, mtu);
15118c2ecf20Sopenharmony_ci		}
15128c2ecf20Sopenharmony_ci	}
15138c2ecf20Sopenharmony_ci
15148c2ecf20Sopenharmony_ci	if (ndopts.nd_useropts) {
15158c2ecf20Sopenharmony_ci		struct nd_opt_hdr *p;
15168c2ecf20Sopenharmony_ci		for (p = ndopts.nd_useropts;
15178c2ecf20Sopenharmony_ci		     p;
15188c2ecf20Sopenharmony_ci		     p = ndisc_next_useropt(skb->dev, p,
15198c2ecf20Sopenharmony_ci					    ndopts.nd_useropts_end)) {
15208c2ecf20Sopenharmony_ci			ndisc_ra_useropt(skb, p);
15218c2ecf20Sopenharmony_ci		}
15228c2ecf20Sopenharmony_ci	}
15238c2ecf20Sopenharmony_ci
15248c2ecf20Sopenharmony_ci	if (ndopts.nd_opts_tgt_lladdr || ndopts.nd_opts_rh) {
15258c2ecf20Sopenharmony_ci		ND_PRINTK(2, warn, "RA: invalid RA options\n");
15268c2ecf20Sopenharmony_ci	}
15278c2ecf20Sopenharmony_ciout:
15288c2ecf20Sopenharmony_ci	fib6_info_release(rt);
15298c2ecf20Sopenharmony_ci	if (neigh)
15308c2ecf20Sopenharmony_ci		neigh_release(neigh);
15318c2ecf20Sopenharmony_ci}
15328c2ecf20Sopenharmony_ci
15338c2ecf20Sopenharmony_cistatic void ndisc_redirect_rcv(struct sk_buff *skb)
15348c2ecf20Sopenharmony_ci{
15358c2ecf20Sopenharmony_ci	u8 *hdr;
15368c2ecf20Sopenharmony_ci	struct ndisc_options ndopts;
15378c2ecf20Sopenharmony_ci	struct rd_msg *msg = (struct rd_msg *)skb_transport_header(skb);
15388c2ecf20Sopenharmony_ci	u32 ndoptlen = skb_tail_pointer(skb) - (skb_transport_header(skb) +
15398c2ecf20Sopenharmony_ci				    offsetof(struct rd_msg, opt));
15408c2ecf20Sopenharmony_ci
15418c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_NDISC_NODETYPE
15428c2ecf20Sopenharmony_ci	switch (skb->ndisc_nodetype) {
15438c2ecf20Sopenharmony_ci	case NDISC_NODETYPE_HOST:
15448c2ecf20Sopenharmony_ci	case NDISC_NODETYPE_NODEFAULT:
15458c2ecf20Sopenharmony_ci		ND_PRINTK(2, warn,
15468c2ecf20Sopenharmony_ci			  "Redirect: from host or unauthorized router\n");
15478c2ecf20Sopenharmony_ci		return;
15488c2ecf20Sopenharmony_ci	}
15498c2ecf20Sopenharmony_ci#endif
15508c2ecf20Sopenharmony_ci
15518c2ecf20Sopenharmony_ci	if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) {
15528c2ecf20Sopenharmony_ci		ND_PRINTK(2, warn,
15538c2ecf20Sopenharmony_ci			  "Redirect: source address is not link-local\n");
15548c2ecf20Sopenharmony_ci		return;
15558c2ecf20Sopenharmony_ci	}
15568c2ecf20Sopenharmony_ci
15578c2ecf20Sopenharmony_ci	if (!ndisc_parse_options(skb->dev, msg->opt, ndoptlen, &ndopts))
15588c2ecf20Sopenharmony_ci		return;
15598c2ecf20Sopenharmony_ci
15608c2ecf20Sopenharmony_ci	if (!ndopts.nd_opts_rh) {
15618c2ecf20Sopenharmony_ci		ip6_redirect_no_header(skb, dev_net(skb->dev),
15628c2ecf20Sopenharmony_ci					skb->dev->ifindex);
15638c2ecf20Sopenharmony_ci		return;
15648c2ecf20Sopenharmony_ci	}
15658c2ecf20Sopenharmony_ci
15668c2ecf20Sopenharmony_ci	hdr = (u8 *)ndopts.nd_opts_rh;
15678c2ecf20Sopenharmony_ci	hdr += 8;
15688c2ecf20Sopenharmony_ci	if (!pskb_pull(skb, hdr - skb_transport_header(skb)))
15698c2ecf20Sopenharmony_ci		return;
15708c2ecf20Sopenharmony_ci
15718c2ecf20Sopenharmony_ci	icmpv6_notify(skb, NDISC_REDIRECT, 0, 0);
15728c2ecf20Sopenharmony_ci}
15738c2ecf20Sopenharmony_ci
15748c2ecf20Sopenharmony_cistatic void ndisc_fill_redirect_hdr_option(struct sk_buff *skb,
15758c2ecf20Sopenharmony_ci					   struct sk_buff *orig_skb,
15768c2ecf20Sopenharmony_ci					   int rd_len)
15778c2ecf20Sopenharmony_ci{
15788c2ecf20Sopenharmony_ci	u8 *opt = skb_put(skb, rd_len);
15798c2ecf20Sopenharmony_ci
15808c2ecf20Sopenharmony_ci	memset(opt, 0, 8);
15818c2ecf20Sopenharmony_ci	*(opt++) = ND_OPT_REDIRECT_HDR;
15828c2ecf20Sopenharmony_ci	*(opt++) = (rd_len >> 3);
15838c2ecf20Sopenharmony_ci	opt += 6;
15848c2ecf20Sopenharmony_ci
15858c2ecf20Sopenharmony_ci	skb_copy_bits(orig_skb, skb_network_offset(orig_skb), opt,
15868c2ecf20Sopenharmony_ci		      rd_len - 8);
15878c2ecf20Sopenharmony_ci}
15888c2ecf20Sopenharmony_ci
15898c2ecf20Sopenharmony_civoid ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target)
15908c2ecf20Sopenharmony_ci{
15918c2ecf20Sopenharmony_ci	struct net_device *dev = skb->dev;
15928c2ecf20Sopenharmony_ci	struct net *net = dev_net(dev);
15938c2ecf20Sopenharmony_ci	struct sock *sk = net->ipv6.ndisc_sk;
15948c2ecf20Sopenharmony_ci	int optlen = 0;
15958c2ecf20Sopenharmony_ci	struct inet_peer *peer;
15968c2ecf20Sopenharmony_ci	struct sk_buff *buff;
15978c2ecf20Sopenharmony_ci	struct rd_msg *msg;
15988c2ecf20Sopenharmony_ci	struct in6_addr saddr_buf;
15998c2ecf20Sopenharmony_ci	struct rt6_info *rt;
16008c2ecf20Sopenharmony_ci	struct dst_entry *dst;
16018c2ecf20Sopenharmony_ci	struct flowi6 fl6;
16028c2ecf20Sopenharmony_ci	int rd_len;
16038c2ecf20Sopenharmony_ci	u8 ha_buf[MAX_ADDR_LEN], *ha = NULL,
16048c2ecf20Sopenharmony_ci	   ops_data_buf[NDISC_OPS_REDIRECT_DATA_SPACE], *ops_data = NULL;
16058c2ecf20Sopenharmony_ci	bool ret;
16068c2ecf20Sopenharmony_ci
16078c2ecf20Sopenharmony_ci	if (netif_is_l3_master(skb->dev)) {
16088c2ecf20Sopenharmony_ci		dev = __dev_get_by_index(dev_net(skb->dev), IPCB(skb)->iif);
16098c2ecf20Sopenharmony_ci		if (!dev)
16108c2ecf20Sopenharmony_ci			return;
16118c2ecf20Sopenharmony_ci	}
16128c2ecf20Sopenharmony_ci
16138c2ecf20Sopenharmony_ci	if (ipv6_get_lladdr(dev, &saddr_buf, IFA_F_TENTATIVE)) {
16148c2ecf20Sopenharmony_ci		ND_PRINTK(2, warn, "Redirect: no link-local address on %s\n",
16158c2ecf20Sopenharmony_ci			  dev->name);
16168c2ecf20Sopenharmony_ci		return;
16178c2ecf20Sopenharmony_ci	}
16188c2ecf20Sopenharmony_ci
16198c2ecf20Sopenharmony_ci	if (!ipv6_addr_equal(&ipv6_hdr(skb)->daddr, target) &&
16208c2ecf20Sopenharmony_ci	    ipv6_addr_type(target) != (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) {
16218c2ecf20Sopenharmony_ci		ND_PRINTK(2, warn,
16228c2ecf20Sopenharmony_ci			  "Redirect: target address is not link-local unicast\n");
16238c2ecf20Sopenharmony_ci		return;
16248c2ecf20Sopenharmony_ci	}
16258c2ecf20Sopenharmony_ci
16268c2ecf20Sopenharmony_ci	icmpv6_flow_init(sk, &fl6, NDISC_REDIRECT,
16278c2ecf20Sopenharmony_ci			 &saddr_buf, &ipv6_hdr(skb)->saddr, dev->ifindex);
16288c2ecf20Sopenharmony_ci
16298c2ecf20Sopenharmony_ci	dst = ip6_route_output(net, NULL, &fl6);
16308c2ecf20Sopenharmony_ci	if (dst->error) {
16318c2ecf20Sopenharmony_ci		dst_release(dst);
16328c2ecf20Sopenharmony_ci		return;
16338c2ecf20Sopenharmony_ci	}
16348c2ecf20Sopenharmony_ci	dst = xfrm_lookup(net, dst, flowi6_to_flowi(&fl6), NULL, 0);
16358c2ecf20Sopenharmony_ci	if (IS_ERR(dst))
16368c2ecf20Sopenharmony_ci		return;
16378c2ecf20Sopenharmony_ci
16388c2ecf20Sopenharmony_ci	rt = (struct rt6_info *) dst;
16398c2ecf20Sopenharmony_ci
16408c2ecf20Sopenharmony_ci	if (rt->rt6i_flags & RTF_GATEWAY) {
16418c2ecf20Sopenharmony_ci		ND_PRINTK(2, warn,
16428c2ecf20Sopenharmony_ci			  "Redirect: destination is not a neighbour\n");
16438c2ecf20Sopenharmony_ci		goto release;
16448c2ecf20Sopenharmony_ci	}
16458c2ecf20Sopenharmony_ci	peer = inet_getpeer_v6(net->ipv6.peers, &ipv6_hdr(skb)->saddr, 1);
16468c2ecf20Sopenharmony_ci	ret = inet_peer_xrlim_allow(peer, 1*HZ);
16478c2ecf20Sopenharmony_ci	if (peer)
16488c2ecf20Sopenharmony_ci		inet_putpeer(peer);
16498c2ecf20Sopenharmony_ci	if (!ret)
16508c2ecf20Sopenharmony_ci		goto release;
16518c2ecf20Sopenharmony_ci
16528c2ecf20Sopenharmony_ci	if (dev->addr_len) {
16538c2ecf20Sopenharmony_ci		struct neighbour *neigh = dst_neigh_lookup(skb_dst(skb), target);
16548c2ecf20Sopenharmony_ci		if (!neigh) {
16558c2ecf20Sopenharmony_ci			ND_PRINTK(2, warn,
16568c2ecf20Sopenharmony_ci				  "Redirect: no neigh for target address\n");
16578c2ecf20Sopenharmony_ci			goto release;
16588c2ecf20Sopenharmony_ci		}
16598c2ecf20Sopenharmony_ci
16608c2ecf20Sopenharmony_ci		read_lock_bh(&neigh->lock);
16618c2ecf20Sopenharmony_ci		if (neigh->nud_state & NUD_VALID) {
16628c2ecf20Sopenharmony_ci			memcpy(ha_buf, neigh->ha, dev->addr_len);
16638c2ecf20Sopenharmony_ci			read_unlock_bh(&neigh->lock);
16648c2ecf20Sopenharmony_ci			ha = ha_buf;
16658c2ecf20Sopenharmony_ci			optlen += ndisc_redirect_opt_addr_space(dev, neigh,
16668c2ecf20Sopenharmony_ci								ops_data_buf,
16678c2ecf20Sopenharmony_ci								&ops_data);
16688c2ecf20Sopenharmony_ci		} else
16698c2ecf20Sopenharmony_ci			read_unlock_bh(&neigh->lock);
16708c2ecf20Sopenharmony_ci
16718c2ecf20Sopenharmony_ci		neigh_release(neigh);
16728c2ecf20Sopenharmony_ci	}
16738c2ecf20Sopenharmony_ci
16748c2ecf20Sopenharmony_ci	rd_len = min_t(unsigned int,
16758c2ecf20Sopenharmony_ci		       IPV6_MIN_MTU - sizeof(struct ipv6hdr) - sizeof(*msg) - optlen,
16768c2ecf20Sopenharmony_ci		       skb->len + 8);
16778c2ecf20Sopenharmony_ci	rd_len &= ~0x7;
16788c2ecf20Sopenharmony_ci	optlen += rd_len;
16798c2ecf20Sopenharmony_ci
16808c2ecf20Sopenharmony_ci	buff = ndisc_alloc_skb(dev, sizeof(*msg) + optlen);
16818c2ecf20Sopenharmony_ci	if (!buff)
16828c2ecf20Sopenharmony_ci		goto release;
16838c2ecf20Sopenharmony_ci
16848c2ecf20Sopenharmony_ci	msg = skb_put(buff, sizeof(*msg));
16858c2ecf20Sopenharmony_ci	*msg = (struct rd_msg) {
16868c2ecf20Sopenharmony_ci		.icmph = {
16878c2ecf20Sopenharmony_ci			.icmp6_type = NDISC_REDIRECT,
16888c2ecf20Sopenharmony_ci		},
16898c2ecf20Sopenharmony_ci		.target = *target,
16908c2ecf20Sopenharmony_ci		.dest = ipv6_hdr(skb)->daddr,
16918c2ecf20Sopenharmony_ci	};
16928c2ecf20Sopenharmony_ci
16938c2ecf20Sopenharmony_ci	/*
16948c2ecf20Sopenharmony_ci	 *	include target_address option
16958c2ecf20Sopenharmony_ci	 */
16968c2ecf20Sopenharmony_ci
16978c2ecf20Sopenharmony_ci	if (ha)
16988c2ecf20Sopenharmony_ci		ndisc_fill_redirect_addr_option(buff, ha, ops_data);
16998c2ecf20Sopenharmony_ci
17008c2ecf20Sopenharmony_ci	/*
17018c2ecf20Sopenharmony_ci	 *	build redirect option and copy skb over to the new packet.
17028c2ecf20Sopenharmony_ci	 */
17038c2ecf20Sopenharmony_ci
17048c2ecf20Sopenharmony_ci	if (rd_len)
17058c2ecf20Sopenharmony_ci		ndisc_fill_redirect_hdr_option(buff, skb, rd_len);
17068c2ecf20Sopenharmony_ci
17078c2ecf20Sopenharmony_ci	skb_dst_set(buff, dst);
17088c2ecf20Sopenharmony_ci	ndisc_send_skb(buff, &ipv6_hdr(skb)->saddr, &saddr_buf);
17098c2ecf20Sopenharmony_ci	return;
17108c2ecf20Sopenharmony_ci
17118c2ecf20Sopenharmony_cirelease:
17128c2ecf20Sopenharmony_ci	dst_release(dst);
17138c2ecf20Sopenharmony_ci}
17148c2ecf20Sopenharmony_ci
17158c2ecf20Sopenharmony_cistatic void pndisc_redo(struct sk_buff *skb)
17168c2ecf20Sopenharmony_ci{
17178c2ecf20Sopenharmony_ci	ndisc_recv_ns(skb);
17188c2ecf20Sopenharmony_ci	kfree_skb(skb);
17198c2ecf20Sopenharmony_ci}
17208c2ecf20Sopenharmony_ci
17218c2ecf20Sopenharmony_cistatic int ndisc_is_multicast(const void *pkey)
17228c2ecf20Sopenharmony_ci{
17238c2ecf20Sopenharmony_ci	return ipv6_addr_is_multicast((struct in6_addr *)pkey);
17248c2ecf20Sopenharmony_ci}
17258c2ecf20Sopenharmony_ci
17268c2ecf20Sopenharmony_cistatic bool ndisc_suppress_frag_ndisc(struct sk_buff *skb)
17278c2ecf20Sopenharmony_ci{
17288c2ecf20Sopenharmony_ci	struct inet6_dev *idev = __in6_dev_get(skb->dev);
17298c2ecf20Sopenharmony_ci
17308c2ecf20Sopenharmony_ci	if (!idev)
17318c2ecf20Sopenharmony_ci		return true;
17328c2ecf20Sopenharmony_ci	if (IP6CB(skb)->flags & IP6SKB_FRAGMENTED &&
17338c2ecf20Sopenharmony_ci	    idev->cnf.suppress_frag_ndisc) {
17348c2ecf20Sopenharmony_ci		net_warn_ratelimited("Received fragmented ndisc packet. Carefully consider disabling suppress_frag_ndisc.\n");
17358c2ecf20Sopenharmony_ci		return true;
17368c2ecf20Sopenharmony_ci	}
17378c2ecf20Sopenharmony_ci	return false;
17388c2ecf20Sopenharmony_ci}
17398c2ecf20Sopenharmony_ci
17408c2ecf20Sopenharmony_ciint ndisc_rcv(struct sk_buff *skb)
17418c2ecf20Sopenharmony_ci{
17428c2ecf20Sopenharmony_ci	struct nd_msg *msg;
17438c2ecf20Sopenharmony_ci
17448c2ecf20Sopenharmony_ci	if (ndisc_suppress_frag_ndisc(skb))
17458c2ecf20Sopenharmony_ci		return 0;
17468c2ecf20Sopenharmony_ci
17478c2ecf20Sopenharmony_ci	if (skb_linearize(skb))
17488c2ecf20Sopenharmony_ci		return 0;
17498c2ecf20Sopenharmony_ci
17508c2ecf20Sopenharmony_ci	msg = (struct nd_msg *)skb_transport_header(skb);
17518c2ecf20Sopenharmony_ci
17528c2ecf20Sopenharmony_ci	__skb_push(skb, skb->data - skb_transport_header(skb));
17538c2ecf20Sopenharmony_ci
17548c2ecf20Sopenharmony_ci	if (ipv6_hdr(skb)->hop_limit != 255) {
17558c2ecf20Sopenharmony_ci		ND_PRINTK(2, warn, "NDISC: invalid hop-limit: %d\n",
17568c2ecf20Sopenharmony_ci			  ipv6_hdr(skb)->hop_limit);
17578c2ecf20Sopenharmony_ci		return 0;
17588c2ecf20Sopenharmony_ci	}
17598c2ecf20Sopenharmony_ci
17608c2ecf20Sopenharmony_ci	if (msg->icmph.icmp6_code != 0) {
17618c2ecf20Sopenharmony_ci		ND_PRINTK(2, warn, "NDISC: invalid ICMPv6 code: %d\n",
17628c2ecf20Sopenharmony_ci			  msg->icmph.icmp6_code);
17638c2ecf20Sopenharmony_ci		return 0;
17648c2ecf20Sopenharmony_ci	}
17658c2ecf20Sopenharmony_ci
17668c2ecf20Sopenharmony_ci	switch (msg->icmph.icmp6_type) {
17678c2ecf20Sopenharmony_ci	case NDISC_NEIGHBOUR_SOLICITATION:
17688c2ecf20Sopenharmony_ci		memset(NEIGH_CB(skb), 0, sizeof(struct neighbour_cb));
17698c2ecf20Sopenharmony_ci		ndisc_recv_ns(skb);
17708c2ecf20Sopenharmony_ci		break;
17718c2ecf20Sopenharmony_ci
17728c2ecf20Sopenharmony_ci	case NDISC_NEIGHBOUR_ADVERTISEMENT:
17738c2ecf20Sopenharmony_ci		ndisc_recv_na(skb);
17748c2ecf20Sopenharmony_ci		break;
17758c2ecf20Sopenharmony_ci
17768c2ecf20Sopenharmony_ci	case NDISC_ROUTER_SOLICITATION:
17778c2ecf20Sopenharmony_ci		ndisc_recv_rs(skb);
17788c2ecf20Sopenharmony_ci		break;
17798c2ecf20Sopenharmony_ci
17808c2ecf20Sopenharmony_ci	case NDISC_ROUTER_ADVERTISEMENT:
17818c2ecf20Sopenharmony_ci		ndisc_router_discovery(skb);
17828c2ecf20Sopenharmony_ci		break;
17838c2ecf20Sopenharmony_ci
17848c2ecf20Sopenharmony_ci	case NDISC_REDIRECT:
17858c2ecf20Sopenharmony_ci		ndisc_redirect_rcv(skb);
17868c2ecf20Sopenharmony_ci		break;
17878c2ecf20Sopenharmony_ci	}
17888c2ecf20Sopenharmony_ci
17898c2ecf20Sopenharmony_ci	return 0;
17908c2ecf20Sopenharmony_ci}
17918c2ecf20Sopenharmony_ci
17928c2ecf20Sopenharmony_cistatic int ndisc_netdev_event(struct notifier_block *this, unsigned long event, void *ptr)
17938c2ecf20Sopenharmony_ci{
17948c2ecf20Sopenharmony_ci	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
17958c2ecf20Sopenharmony_ci	struct netdev_notifier_change_info *change_info;
17968c2ecf20Sopenharmony_ci	struct net *net = dev_net(dev);
17978c2ecf20Sopenharmony_ci	struct inet6_dev *idev;
17988c2ecf20Sopenharmony_ci
17998c2ecf20Sopenharmony_ci	switch (event) {
18008c2ecf20Sopenharmony_ci	case NETDEV_CHANGEADDR:
18018c2ecf20Sopenharmony_ci		neigh_changeaddr(&nd_tbl, dev);
18028c2ecf20Sopenharmony_ci		fib6_run_gc(0, net, false);
18038c2ecf20Sopenharmony_ci		fallthrough;
18048c2ecf20Sopenharmony_ci	case NETDEV_UP:
18058c2ecf20Sopenharmony_ci		idev = in6_dev_get(dev);
18068c2ecf20Sopenharmony_ci		if (!idev)
18078c2ecf20Sopenharmony_ci			break;
18088c2ecf20Sopenharmony_ci		if (idev->cnf.ndisc_notify ||
18098c2ecf20Sopenharmony_ci		    net->ipv6.devconf_all->ndisc_notify)
18108c2ecf20Sopenharmony_ci			ndisc_send_unsol_na(dev);
18118c2ecf20Sopenharmony_ci		in6_dev_put(idev);
18128c2ecf20Sopenharmony_ci		break;
18138c2ecf20Sopenharmony_ci	case NETDEV_CHANGE:
18148c2ecf20Sopenharmony_ci		change_info = ptr;
18158c2ecf20Sopenharmony_ci		if (change_info->flags_changed & IFF_NOARP)
18168c2ecf20Sopenharmony_ci			neigh_changeaddr(&nd_tbl, dev);
18178c2ecf20Sopenharmony_ci		if (!netif_carrier_ok(dev))
18188c2ecf20Sopenharmony_ci			neigh_carrier_down(&nd_tbl, dev);
18198c2ecf20Sopenharmony_ci		break;
18208c2ecf20Sopenharmony_ci	case NETDEV_DOWN:
18218c2ecf20Sopenharmony_ci		neigh_ifdown(&nd_tbl, dev);
18228c2ecf20Sopenharmony_ci		fib6_run_gc(0, net, false);
18238c2ecf20Sopenharmony_ci		break;
18248c2ecf20Sopenharmony_ci	case NETDEV_NOTIFY_PEERS:
18258c2ecf20Sopenharmony_ci		ndisc_send_unsol_na(dev);
18268c2ecf20Sopenharmony_ci		break;
18278c2ecf20Sopenharmony_ci	default:
18288c2ecf20Sopenharmony_ci		break;
18298c2ecf20Sopenharmony_ci	}
18308c2ecf20Sopenharmony_ci
18318c2ecf20Sopenharmony_ci	return NOTIFY_DONE;
18328c2ecf20Sopenharmony_ci}
18338c2ecf20Sopenharmony_ci
18348c2ecf20Sopenharmony_cistatic struct notifier_block ndisc_netdev_notifier = {
18358c2ecf20Sopenharmony_ci	.notifier_call = ndisc_netdev_event,
18368c2ecf20Sopenharmony_ci	.priority = ADDRCONF_NOTIFY_PRIORITY - 5,
18378c2ecf20Sopenharmony_ci};
18388c2ecf20Sopenharmony_ci
18398c2ecf20Sopenharmony_ci#ifdef CONFIG_SYSCTL
18408c2ecf20Sopenharmony_cistatic void ndisc_warn_deprecated_sysctl(struct ctl_table *ctl,
18418c2ecf20Sopenharmony_ci					 const char *func, const char *dev_name)
18428c2ecf20Sopenharmony_ci{
18438c2ecf20Sopenharmony_ci	static char warncomm[TASK_COMM_LEN];
18448c2ecf20Sopenharmony_ci	static int warned;
18458c2ecf20Sopenharmony_ci	if (strcmp(warncomm, current->comm) && warned < 5) {
18468c2ecf20Sopenharmony_ci		strcpy(warncomm, current->comm);
18478c2ecf20Sopenharmony_ci		pr_warn("process `%s' is using deprecated sysctl (%s) net.ipv6.neigh.%s.%s - use net.ipv6.neigh.%s.%s_ms instead\n",
18488c2ecf20Sopenharmony_ci			warncomm, func,
18498c2ecf20Sopenharmony_ci			dev_name, ctl->procname,
18508c2ecf20Sopenharmony_ci			dev_name, ctl->procname);
18518c2ecf20Sopenharmony_ci		warned++;
18528c2ecf20Sopenharmony_ci	}
18538c2ecf20Sopenharmony_ci}
18548c2ecf20Sopenharmony_ci
18558c2ecf20Sopenharmony_ciint ndisc_ifinfo_sysctl_change(struct ctl_table *ctl, int write, void *buffer,
18568c2ecf20Sopenharmony_ci		size_t *lenp, loff_t *ppos)
18578c2ecf20Sopenharmony_ci{
18588c2ecf20Sopenharmony_ci	struct net_device *dev = ctl->extra1;
18598c2ecf20Sopenharmony_ci	struct inet6_dev *idev;
18608c2ecf20Sopenharmony_ci	int ret;
18618c2ecf20Sopenharmony_ci
18628c2ecf20Sopenharmony_ci	if ((strcmp(ctl->procname, "retrans_time") == 0) ||
18638c2ecf20Sopenharmony_ci	    (strcmp(ctl->procname, "base_reachable_time") == 0))
18648c2ecf20Sopenharmony_ci		ndisc_warn_deprecated_sysctl(ctl, "syscall", dev ? dev->name : "default");
18658c2ecf20Sopenharmony_ci
18668c2ecf20Sopenharmony_ci	if (strcmp(ctl->procname, "retrans_time") == 0)
18678c2ecf20Sopenharmony_ci		ret = neigh_proc_dointvec(ctl, write, buffer, lenp, ppos);
18688c2ecf20Sopenharmony_ci
18698c2ecf20Sopenharmony_ci	else if (strcmp(ctl->procname, "base_reachable_time") == 0)
18708c2ecf20Sopenharmony_ci		ret = neigh_proc_dointvec_jiffies(ctl, write,
18718c2ecf20Sopenharmony_ci						  buffer, lenp, ppos);
18728c2ecf20Sopenharmony_ci
18738c2ecf20Sopenharmony_ci	else if ((strcmp(ctl->procname, "retrans_time_ms") == 0) ||
18748c2ecf20Sopenharmony_ci		 (strcmp(ctl->procname, "base_reachable_time_ms") == 0))
18758c2ecf20Sopenharmony_ci		ret = neigh_proc_dointvec_ms_jiffies(ctl, write,
18768c2ecf20Sopenharmony_ci						     buffer, lenp, ppos);
18778c2ecf20Sopenharmony_ci	else
18788c2ecf20Sopenharmony_ci		ret = -1;
18798c2ecf20Sopenharmony_ci
18808c2ecf20Sopenharmony_ci	if (write && ret == 0 && dev && (idev = in6_dev_get(dev)) != NULL) {
18818c2ecf20Sopenharmony_ci		if (ctl->data == &NEIGH_VAR(idev->nd_parms, BASE_REACHABLE_TIME))
18828c2ecf20Sopenharmony_ci			idev->nd_parms->reachable_time =
18838c2ecf20Sopenharmony_ci					neigh_rand_reach_time(NEIGH_VAR(idev->nd_parms, BASE_REACHABLE_TIME));
18848c2ecf20Sopenharmony_ci		idev->tstamp = jiffies;
18858c2ecf20Sopenharmony_ci		inet6_ifinfo_notify(RTM_NEWLINK, idev);
18868c2ecf20Sopenharmony_ci		in6_dev_put(idev);
18878c2ecf20Sopenharmony_ci	}
18888c2ecf20Sopenharmony_ci	return ret;
18898c2ecf20Sopenharmony_ci}
18908c2ecf20Sopenharmony_ci
18918c2ecf20Sopenharmony_ci
18928c2ecf20Sopenharmony_ci#endif
18938c2ecf20Sopenharmony_ci
18948c2ecf20Sopenharmony_cistatic int __net_init ndisc_net_init(struct net *net)
18958c2ecf20Sopenharmony_ci{
18968c2ecf20Sopenharmony_ci	struct ipv6_pinfo *np;
18978c2ecf20Sopenharmony_ci	struct sock *sk;
18988c2ecf20Sopenharmony_ci	int err;
18998c2ecf20Sopenharmony_ci
19008c2ecf20Sopenharmony_ci	err = inet_ctl_sock_create(&sk, PF_INET6,
19018c2ecf20Sopenharmony_ci				   SOCK_RAW, IPPROTO_ICMPV6, net);
19028c2ecf20Sopenharmony_ci	if (err < 0) {
19038c2ecf20Sopenharmony_ci		ND_PRINTK(0, err,
19048c2ecf20Sopenharmony_ci			  "NDISC: Failed to initialize the control socket (err %d)\n",
19058c2ecf20Sopenharmony_ci			  err);
19068c2ecf20Sopenharmony_ci		return err;
19078c2ecf20Sopenharmony_ci	}
19088c2ecf20Sopenharmony_ci
19098c2ecf20Sopenharmony_ci	net->ipv6.ndisc_sk = sk;
19108c2ecf20Sopenharmony_ci
19118c2ecf20Sopenharmony_ci	np = inet6_sk(sk);
19128c2ecf20Sopenharmony_ci	np->hop_limit = 255;
19138c2ecf20Sopenharmony_ci	/* Do not loopback ndisc messages */
19148c2ecf20Sopenharmony_ci	np->mc_loop = 0;
19158c2ecf20Sopenharmony_ci
19168c2ecf20Sopenharmony_ci	return 0;
19178c2ecf20Sopenharmony_ci}
19188c2ecf20Sopenharmony_ci
19198c2ecf20Sopenharmony_cistatic void __net_exit ndisc_net_exit(struct net *net)
19208c2ecf20Sopenharmony_ci{
19218c2ecf20Sopenharmony_ci	inet_ctl_sock_destroy(net->ipv6.ndisc_sk);
19228c2ecf20Sopenharmony_ci}
19238c2ecf20Sopenharmony_ci
19248c2ecf20Sopenharmony_cistatic struct pernet_operations ndisc_net_ops = {
19258c2ecf20Sopenharmony_ci	.init = ndisc_net_init,
19268c2ecf20Sopenharmony_ci	.exit = ndisc_net_exit,
19278c2ecf20Sopenharmony_ci};
19288c2ecf20Sopenharmony_ci
19298c2ecf20Sopenharmony_ciint __init ndisc_init(void)
19308c2ecf20Sopenharmony_ci{
19318c2ecf20Sopenharmony_ci	int err;
19328c2ecf20Sopenharmony_ci
19338c2ecf20Sopenharmony_ci	err = register_pernet_subsys(&ndisc_net_ops);
19348c2ecf20Sopenharmony_ci	if (err)
19358c2ecf20Sopenharmony_ci		return err;
19368c2ecf20Sopenharmony_ci	/*
19378c2ecf20Sopenharmony_ci	 * Initialize the neighbour table
19388c2ecf20Sopenharmony_ci	 */
19398c2ecf20Sopenharmony_ci	neigh_table_init(NEIGH_ND_TABLE, &nd_tbl);
19408c2ecf20Sopenharmony_ci
19418c2ecf20Sopenharmony_ci#ifdef CONFIG_SYSCTL
19428c2ecf20Sopenharmony_ci	err = neigh_sysctl_register(NULL, &nd_tbl.parms,
19438c2ecf20Sopenharmony_ci				    ndisc_ifinfo_sysctl_change);
19448c2ecf20Sopenharmony_ci	if (err)
19458c2ecf20Sopenharmony_ci		goto out_unregister_pernet;
19468c2ecf20Sopenharmony_ciout:
19478c2ecf20Sopenharmony_ci#endif
19488c2ecf20Sopenharmony_ci	return err;
19498c2ecf20Sopenharmony_ci
19508c2ecf20Sopenharmony_ci#ifdef CONFIG_SYSCTL
19518c2ecf20Sopenharmony_ciout_unregister_pernet:
19528c2ecf20Sopenharmony_ci	unregister_pernet_subsys(&ndisc_net_ops);
19538c2ecf20Sopenharmony_ci	goto out;
19548c2ecf20Sopenharmony_ci#endif
19558c2ecf20Sopenharmony_ci}
19568c2ecf20Sopenharmony_ci
19578c2ecf20Sopenharmony_ciint __init ndisc_late_init(void)
19588c2ecf20Sopenharmony_ci{
19598c2ecf20Sopenharmony_ci	return register_netdevice_notifier(&ndisc_netdev_notifier);
19608c2ecf20Sopenharmony_ci}
19618c2ecf20Sopenharmony_ci
19628c2ecf20Sopenharmony_civoid ndisc_late_cleanup(void)
19638c2ecf20Sopenharmony_ci{
19648c2ecf20Sopenharmony_ci	unregister_netdevice_notifier(&ndisc_netdev_notifier);
19658c2ecf20Sopenharmony_ci}
19668c2ecf20Sopenharmony_ci
19678c2ecf20Sopenharmony_civoid ndisc_cleanup(void)
19688c2ecf20Sopenharmony_ci{
19698c2ecf20Sopenharmony_ci#ifdef CONFIG_SYSCTL
19708c2ecf20Sopenharmony_ci	neigh_sysctl_unregister(&nd_tbl.parms);
19718c2ecf20Sopenharmony_ci#endif
19728c2ecf20Sopenharmony_ci	neigh_table_clear(NEIGH_ND_TABLE, &nd_tbl);
19738c2ecf20Sopenharmony_ci	unregister_pernet_subsys(&ndisc_net_ops);
19748c2ecf20Sopenharmony_ci}
1975