162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *	IPv6 Address [auto]configuration
462306a36Sopenharmony_ci *	Linux INET6 implementation
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci *	Authors:
762306a36Sopenharmony_ci *	Pedro Roque		<roque@di.fc.ul.pt>
862306a36Sopenharmony_ci *	Alexey Kuznetsov	<kuznet@ms2.inr.ac.ru>
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci/*
1262306a36Sopenharmony_ci *	Changes:
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci *	Janos Farkas			:	delete timer on ifdown
1562306a36Sopenharmony_ci *	<chexum@bankinf.banki.hu>
1662306a36Sopenharmony_ci *	Andi Kleen			:	kill double kfree on module
1762306a36Sopenharmony_ci *						unload.
1862306a36Sopenharmony_ci *	Maciej W. Rozycki		:	FDDI support
1962306a36Sopenharmony_ci *	sekiya@USAGI			:	Don't send too many RS
2062306a36Sopenharmony_ci *						packets.
2162306a36Sopenharmony_ci *	yoshfuji@USAGI			:       Fixed interval between DAD
2262306a36Sopenharmony_ci *						packets.
2362306a36Sopenharmony_ci *	YOSHIFUJI Hideaki @USAGI	:	improved accuracy of
2462306a36Sopenharmony_ci *						address validation timer.
2562306a36Sopenharmony_ci *	YOSHIFUJI Hideaki @USAGI	:	Privacy Extensions (RFC3041)
2662306a36Sopenharmony_ci *						support.
2762306a36Sopenharmony_ci *	Yuji SEKIYA @USAGI		:	Don't assign a same IPv6
2862306a36Sopenharmony_ci *						address on a same interface.
2962306a36Sopenharmony_ci *	YOSHIFUJI Hideaki @USAGI	:	ARCnet support
3062306a36Sopenharmony_ci *	YOSHIFUJI Hideaki @USAGI	:	convert /proc/net/if_inet6 to
3162306a36Sopenharmony_ci *						seq_file.
3262306a36Sopenharmony_ci *	YOSHIFUJI Hideaki @USAGI	:	improved source address
3362306a36Sopenharmony_ci *						selection; consider scope,
3462306a36Sopenharmony_ci *						status etc.
3562306a36Sopenharmony_ci */
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#define pr_fmt(fmt) "IPv6: " fmt
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci#include <linux/errno.h>
4062306a36Sopenharmony_ci#include <linux/types.h>
4162306a36Sopenharmony_ci#include <linux/kernel.h>
4262306a36Sopenharmony_ci#include <linux/sched/signal.h>
4362306a36Sopenharmony_ci#include <linux/socket.h>
4462306a36Sopenharmony_ci#include <linux/sockios.h>
4562306a36Sopenharmony_ci#include <linux/net.h>
4662306a36Sopenharmony_ci#include <linux/inet.h>
4762306a36Sopenharmony_ci#include <linux/in6.h>
4862306a36Sopenharmony_ci#include <linux/netdevice.h>
4962306a36Sopenharmony_ci#include <linux/if_addr.h>
5062306a36Sopenharmony_ci#include <linux/if_arp.h>
5162306a36Sopenharmony_ci#include <linux/if_arcnet.h>
5262306a36Sopenharmony_ci#include <linux/if_infiniband.h>
5362306a36Sopenharmony_ci#include <linux/route.h>
5462306a36Sopenharmony_ci#include <linux/inetdevice.h>
5562306a36Sopenharmony_ci#include <linux/init.h>
5662306a36Sopenharmony_ci#include <linux/slab.h>
5762306a36Sopenharmony_ci#ifdef CONFIG_SYSCTL
5862306a36Sopenharmony_ci#include <linux/sysctl.h>
5962306a36Sopenharmony_ci#endif
6062306a36Sopenharmony_ci#include <linux/capability.h>
6162306a36Sopenharmony_ci#include <linux/delay.h>
6262306a36Sopenharmony_ci#include <linux/notifier.h>
6362306a36Sopenharmony_ci#include <linux/string.h>
6462306a36Sopenharmony_ci#include <linux/hash.h>
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci#include <net/net_namespace.h>
6762306a36Sopenharmony_ci#include <net/sock.h>
6862306a36Sopenharmony_ci#include <net/snmp.h>
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci#include <net/6lowpan.h>
7162306a36Sopenharmony_ci#include <net/firewire.h>
7262306a36Sopenharmony_ci#include <net/ipv6.h>
7362306a36Sopenharmony_ci#include <net/protocol.h>
7462306a36Sopenharmony_ci#include <net/ndisc.h>
7562306a36Sopenharmony_ci#include <net/ip6_route.h>
7662306a36Sopenharmony_ci#include <net/addrconf.h>
7762306a36Sopenharmony_ci#include <net/tcp.h>
7862306a36Sopenharmony_ci#include <net/ip.h>
7962306a36Sopenharmony_ci#include <net/netlink.h>
8062306a36Sopenharmony_ci#include <net/pkt_sched.h>
8162306a36Sopenharmony_ci#include <net/l3mdev.h>
8262306a36Sopenharmony_ci#include <linux/if_tunnel.h>
8362306a36Sopenharmony_ci#include <linux/rtnetlink.h>
8462306a36Sopenharmony_ci#include <linux/netconf.h>
8562306a36Sopenharmony_ci#include <linux/random.h>
8662306a36Sopenharmony_ci#include <linux/uaccess.h>
8762306a36Sopenharmony_ci#include <asm/unaligned.h>
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci#include <linux/proc_fs.h>
9062306a36Sopenharmony_ci#include <linux/seq_file.h>
9162306a36Sopenharmony_ci#include <linux/export.h>
9262306a36Sopenharmony_ci#include <linux/ioam6.h>
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci#define	INFINITY_LIFE_TIME	0xFFFFFFFF
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci#define IPV6_MAX_STRLEN \
9762306a36Sopenharmony_ci	sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_cistatic inline u32 cstamp_delta(unsigned long cstamp)
10062306a36Sopenharmony_ci{
10162306a36Sopenharmony_ci	return (cstamp - INITIAL_JIFFIES) * 100UL / HZ;
10262306a36Sopenharmony_ci}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_cistatic inline s32 rfc3315_s14_backoff_init(s32 irt)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	/* multiply 'initial retransmission time' by 0.9 .. 1.1 */
10762306a36Sopenharmony_ci	u64 tmp = get_random_u32_inclusive(900000, 1100000) * (u64)irt;
10862306a36Sopenharmony_ci	do_div(tmp, 1000000);
10962306a36Sopenharmony_ci	return (s32)tmp;
11062306a36Sopenharmony_ci}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_cistatic inline s32 rfc3315_s14_backoff_update(s32 rt, s32 mrt)
11362306a36Sopenharmony_ci{
11462306a36Sopenharmony_ci	/* multiply 'retransmission timeout' by 1.9 .. 2.1 */
11562306a36Sopenharmony_ci	u64 tmp = get_random_u32_inclusive(1900000, 2100000) * (u64)rt;
11662306a36Sopenharmony_ci	do_div(tmp, 1000000);
11762306a36Sopenharmony_ci	if ((s32)tmp > mrt) {
11862306a36Sopenharmony_ci		/* multiply 'maximum retransmission time' by 0.9 .. 1.1 */
11962306a36Sopenharmony_ci		tmp = get_random_u32_inclusive(900000, 1100000) * (u64)mrt;
12062306a36Sopenharmony_ci		do_div(tmp, 1000000);
12162306a36Sopenharmony_ci	}
12262306a36Sopenharmony_ci	return (s32)tmp;
12362306a36Sopenharmony_ci}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci#ifdef CONFIG_SYSCTL
12662306a36Sopenharmony_cistatic int addrconf_sysctl_register(struct inet6_dev *idev);
12762306a36Sopenharmony_cistatic void addrconf_sysctl_unregister(struct inet6_dev *idev);
12862306a36Sopenharmony_ci#else
12962306a36Sopenharmony_cistatic inline int addrconf_sysctl_register(struct inet6_dev *idev)
13062306a36Sopenharmony_ci{
13162306a36Sopenharmony_ci	return 0;
13262306a36Sopenharmony_ci}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_cistatic inline void addrconf_sysctl_unregister(struct inet6_dev *idev)
13562306a36Sopenharmony_ci{
13662306a36Sopenharmony_ci}
13762306a36Sopenharmony_ci#endif
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_cistatic void ipv6_gen_rnd_iid(struct in6_addr *addr);
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_cistatic int ipv6_generate_eui64(u8 *eui, struct net_device *dev);
14262306a36Sopenharmony_cistatic int ipv6_count_addresses(const struct inet6_dev *idev);
14362306a36Sopenharmony_cistatic int ipv6_generate_stable_address(struct in6_addr *addr,
14462306a36Sopenharmony_ci					u8 dad_count,
14562306a36Sopenharmony_ci					const struct inet6_dev *idev);
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci#define IN6_ADDR_HSIZE_SHIFT	8
14862306a36Sopenharmony_ci#define IN6_ADDR_HSIZE		(1 << IN6_ADDR_HSIZE_SHIFT)
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_cistatic void addrconf_verify(struct net *net);
15162306a36Sopenharmony_cistatic void addrconf_verify_rtnl(struct net *net);
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_cistatic struct workqueue_struct *addrconf_wq;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_cistatic void addrconf_join_anycast(struct inet6_ifaddr *ifp);
15662306a36Sopenharmony_cistatic void addrconf_leave_anycast(struct inet6_ifaddr *ifp);
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_cistatic void addrconf_type_change(struct net_device *dev,
15962306a36Sopenharmony_ci				 unsigned long event);
16062306a36Sopenharmony_cistatic int addrconf_ifdown(struct net_device *dev, bool unregister);
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_cistatic struct fib6_info *addrconf_get_prefix_route(const struct in6_addr *pfx,
16362306a36Sopenharmony_ci						  int plen,
16462306a36Sopenharmony_ci						  const struct net_device *dev,
16562306a36Sopenharmony_ci						  u32 flags, u32 noflags,
16662306a36Sopenharmony_ci						  bool no_gw);
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_cistatic void addrconf_dad_start(struct inet6_ifaddr *ifp);
16962306a36Sopenharmony_cistatic void addrconf_dad_work(struct work_struct *w);
17062306a36Sopenharmony_cistatic void addrconf_dad_completed(struct inet6_ifaddr *ifp, bool bump_id,
17162306a36Sopenharmony_ci				   bool send_na);
17262306a36Sopenharmony_cistatic void addrconf_dad_run(struct inet6_dev *idev, bool restart);
17362306a36Sopenharmony_cistatic void addrconf_rs_timer(struct timer_list *t);
17462306a36Sopenharmony_cistatic void __ipv6_ifa_notify(int event, struct inet6_ifaddr *ifa);
17562306a36Sopenharmony_cistatic void ipv6_ifa_notify(int event, struct inet6_ifaddr *ifa);
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_cistatic void inet6_prefix_notify(int event, struct inet6_dev *idev,
17862306a36Sopenharmony_ci				struct prefix_info *pinfo);
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_cistatic struct ipv6_devconf ipv6_devconf __read_mostly = {
18162306a36Sopenharmony_ci	.forwarding		= 0,
18262306a36Sopenharmony_ci	.hop_limit		= IPV6_DEFAULT_HOPLIMIT,
18362306a36Sopenharmony_ci	.mtu6			= IPV6_MIN_MTU,
18462306a36Sopenharmony_ci	.accept_ra		= 1,
18562306a36Sopenharmony_ci	.accept_redirects	= 1,
18662306a36Sopenharmony_ci	.autoconf		= 1,
18762306a36Sopenharmony_ci	.force_mld_version	= 0,
18862306a36Sopenharmony_ci	.mldv1_unsolicited_report_interval = 10 * HZ,
18962306a36Sopenharmony_ci	.mldv2_unsolicited_report_interval = HZ,
19062306a36Sopenharmony_ci	.dad_transmits		= 1,
19162306a36Sopenharmony_ci	.rtr_solicits		= MAX_RTR_SOLICITATIONS,
19262306a36Sopenharmony_ci	.rtr_solicit_interval	= RTR_SOLICITATION_INTERVAL,
19362306a36Sopenharmony_ci	.rtr_solicit_max_interval = RTR_SOLICITATION_MAX_INTERVAL,
19462306a36Sopenharmony_ci	.rtr_solicit_delay	= MAX_RTR_SOLICITATION_DELAY,
19562306a36Sopenharmony_ci	.use_tempaddr		= 0,
19662306a36Sopenharmony_ci	.temp_valid_lft		= TEMP_VALID_LIFETIME,
19762306a36Sopenharmony_ci	.temp_prefered_lft	= TEMP_PREFERRED_LIFETIME,
19862306a36Sopenharmony_ci	.regen_max_retry	= REGEN_MAX_RETRY,
19962306a36Sopenharmony_ci	.max_desync_factor	= MAX_DESYNC_FACTOR,
20062306a36Sopenharmony_ci	.max_addresses		= IPV6_MAX_ADDRESSES,
20162306a36Sopenharmony_ci	.accept_ra_defrtr	= 1,
20262306a36Sopenharmony_ci	.ra_defrtr_metric	= IP6_RT_PRIO_USER,
20362306a36Sopenharmony_ci	.accept_ra_from_local	= 0,
20462306a36Sopenharmony_ci	.accept_ra_min_hop_limit= 1,
20562306a36Sopenharmony_ci	.accept_ra_min_lft	= 0,
20662306a36Sopenharmony_ci	.accept_ra_pinfo	= 1,
20762306a36Sopenharmony_ci#ifdef CONFIG_IPV6_ROUTER_PREF
20862306a36Sopenharmony_ci	.accept_ra_rtr_pref	= 1,
20962306a36Sopenharmony_ci	.rtr_probe_interval	= 60 * HZ,
21062306a36Sopenharmony_ci#ifdef CONFIG_IPV6_ROUTE_INFO
21162306a36Sopenharmony_ci	.accept_ra_rt_info_min_plen = 0,
21262306a36Sopenharmony_ci	.accept_ra_rt_info_max_plen = 0,
21362306a36Sopenharmony_ci#endif
21462306a36Sopenharmony_ci#endif
21562306a36Sopenharmony_ci	.proxy_ndp		= 0,
21662306a36Sopenharmony_ci	.accept_source_route	= 0,	/* we do not accept RH0 by default. */
21762306a36Sopenharmony_ci	.disable_ipv6		= 0,
21862306a36Sopenharmony_ci	.accept_dad		= 0,
21962306a36Sopenharmony_ci	.suppress_frag_ndisc	= 1,
22062306a36Sopenharmony_ci	.accept_ra_mtu		= 1,
22162306a36Sopenharmony_ci	.stable_secret		= {
22262306a36Sopenharmony_ci		.initialized = false,
22362306a36Sopenharmony_ci	},
22462306a36Sopenharmony_ci	.use_oif_addrs_only	= 0,
22562306a36Sopenharmony_ci	.ignore_routes_with_linkdown = 0,
22662306a36Sopenharmony_ci	.keep_addr_on_down	= 0,
22762306a36Sopenharmony_ci	.seg6_enabled		= 0,
22862306a36Sopenharmony_ci#ifdef CONFIG_IPV6_SEG6_HMAC
22962306a36Sopenharmony_ci	.seg6_require_hmac	= 0,
23062306a36Sopenharmony_ci#endif
23162306a36Sopenharmony_ci	.enhanced_dad           = 1,
23262306a36Sopenharmony_ci	.addr_gen_mode		= IN6_ADDR_GEN_MODE_EUI64,
23362306a36Sopenharmony_ci	.disable_policy		= 0,
23462306a36Sopenharmony_ci	.rpl_seg_enabled	= 0,
23562306a36Sopenharmony_ci	.ioam6_enabled		= 0,
23662306a36Sopenharmony_ci	.ioam6_id               = IOAM6_DEFAULT_IF_ID,
23762306a36Sopenharmony_ci	.ioam6_id_wide		= IOAM6_DEFAULT_IF_ID_WIDE,
23862306a36Sopenharmony_ci	.ndisc_evict_nocarrier	= 1,
23962306a36Sopenharmony_ci};
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_cistatic struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
24262306a36Sopenharmony_ci	.forwarding		= 0,
24362306a36Sopenharmony_ci	.hop_limit		= IPV6_DEFAULT_HOPLIMIT,
24462306a36Sopenharmony_ci	.mtu6			= IPV6_MIN_MTU,
24562306a36Sopenharmony_ci	.accept_ra		= 1,
24662306a36Sopenharmony_ci	.accept_redirects	= 1,
24762306a36Sopenharmony_ci	.autoconf		= 1,
24862306a36Sopenharmony_ci	.force_mld_version	= 0,
24962306a36Sopenharmony_ci	.mldv1_unsolicited_report_interval = 10 * HZ,
25062306a36Sopenharmony_ci	.mldv2_unsolicited_report_interval = HZ,
25162306a36Sopenharmony_ci	.dad_transmits		= 1,
25262306a36Sopenharmony_ci	.rtr_solicits		= MAX_RTR_SOLICITATIONS,
25362306a36Sopenharmony_ci	.rtr_solicit_interval	= RTR_SOLICITATION_INTERVAL,
25462306a36Sopenharmony_ci	.rtr_solicit_max_interval = RTR_SOLICITATION_MAX_INTERVAL,
25562306a36Sopenharmony_ci	.rtr_solicit_delay	= MAX_RTR_SOLICITATION_DELAY,
25662306a36Sopenharmony_ci	.use_tempaddr		= 0,
25762306a36Sopenharmony_ci	.temp_valid_lft		= TEMP_VALID_LIFETIME,
25862306a36Sopenharmony_ci	.temp_prefered_lft	= TEMP_PREFERRED_LIFETIME,
25962306a36Sopenharmony_ci	.regen_max_retry	= REGEN_MAX_RETRY,
26062306a36Sopenharmony_ci	.max_desync_factor	= MAX_DESYNC_FACTOR,
26162306a36Sopenharmony_ci	.max_addresses		= IPV6_MAX_ADDRESSES,
26262306a36Sopenharmony_ci	.accept_ra_defrtr	= 1,
26362306a36Sopenharmony_ci	.ra_defrtr_metric	= IP6_RT_PRIO_USER,
26462306a36Sopenharmony_ci	.accept_ra_from_local	= 0,
26562306a36Sopenharmony_ci	.accept_ra_min_hop_limit= 1,
26662306a36Sopenharmony_ci	.accept_ra_min_lft	= 0,
26762306a36Sopenharmony_ci	.accept_ra_pinfo	= 1,
26862306a36Sopenharmony_ci#ifdef CONFIG_IPV6_ROUTER_PREF
26962306a36Sopenharmony_ci	.accept_ra_rtr_pref	= 1,
27062306a36Sopenharmony_ci	.rtr_probe_interval	= 60 * HZ,
27162306a36Sopenharmony_ci#ifdef CONFIG_IPV6_ROUTE_INFO
27262306a36Sopenharmony_ci	.accept_ra_rt_info_min_plen = 0,
27362306a36Sopenharmony_ci	.accept_ra_rt_info_max_plen = 0,
27462306a36Sopenharmony_ci#endif
27562306a36Sopenharmony_ci#endif
27662306a36Sopenharmony_ci	.proxy_ndp		= 0,
27762306a36Sopenharmony_ci	.accept_source_route	= 0,	/* we do not accept RH0 by default. */
27862306a36Sopenharmony_ci	.disable_ipv6		= 0,
27962306a36Sopenharmony_ci	.accept_dad		= 1,
28062306a36Sopenharmony_ci	.suppress_frag_ndisc	= 1,
28162306a36Sopenharmony_ci	.accept_ra_mtu		= 1,
28262306a36Sopenharmony_ci	.stable_secret		= {
28362306a36Sopenharmony_ci		.initialized = false,
28462306a36Sopenharmony_ci	},
28562306a36Sopenharmony_ci	.use_oif_addrs_only	= 0,
28662306a36Sopenharmony_ci	.ignore_routes_with_linkdown = 0,
28762306a36Sopenharmony_ci	.keep_addr_on_down	= 0,
28862306a36Sopenharmony_ci	.seg6_enabled		= 0,
28962306a36Sopenharmony_ci#ifdef CONFIG_IPV6_SEG6_HMAC
29062306a36Sopenharmony_ci	.seg6_require_hmac	= 0,
29162306a36Sopenharmony_ci#endif
29262306a36Sopenharmony_ci	.enhanced_dad           = 1,
29362306a36Sopenharmony_ci	.addr_gen_mode		= IN6_ADDR_GEN_MODE_EUI64,
29462306a36Sopenharmony_ci	.disable_policy		= 0,
29562306a36Sopenharmony_ci	.rpl_seg_enabled	= 0,
29662306a36Sopenharmony_ci	.ioam6_enabled		= 0,
29762306a36Sopenharmony_ci	.ioam6_id               = IOAM6_DEFAULT_IF_ID,
29862306a36Sopenharmony_ci	.ioam6_id_wide		= IOAM6_DEFAULT_IF_ID_WIDE,
29962306a36Sopenharmony_ci	.ndisc_evict_nocarrier	= 1,
30062306a36Sopenharmony_ci};
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci/* Check if link is ready: is it up and is a valid qdisc available */
30362306a36Sopenharmony_cistatic inline bool addrconf_link_ready(const struct net_device *dev)
30462306a36Sopenharmony_ci{
30562306a36Sopenharmony_ci	return netif_oper_up(dev) && !qdisc_tx_is_noop(dev);
30662306a36Sopenharmony_ci}
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_cistatic void addrconf_del_rs_timer(struct inet6_dev *idev)
30962306a36Sopenharmony_ci{
31062306a36Sopenharmony_ci	if (del_timer(&idev->rs_timer))
31162306a36Sopenharmony_ci		__in6_dev_put(idev);
31262306a36Sopenharmony_ci}
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_cistatic void addrconf_del_dad_work(struct inet6_ifaddr *ifp)
31562306a36Sopenharmony_ci{
31662306a36Sopenharmony_ci	if (cancel_delayed_work(&ifp->dad_work))
31762306a36Sopenharmony_ci		__in6_ifa_put(ifp);
31862306a36Sopenharmony_ci}
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_cistatic void addrconf_mod_rs_timer(struct inet6_dev *idev,
32162306a36Sopenharmony_ci				  unsigned long when)
32262306a36Sopenharmony_ci{
32362306a36Sopenharmony_ci	if (!mod_timer(&idev->rs_timer, jiffies + when))
32462306a36Sopenharmony_ci		in6_dev_hold(idev);
32562306a36Sopenharmony_ci}
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_cistatic void addrconf_mod_dad_work(struct inet6_ifaddr *ifp,
32862306a36Sopenharmony_ci				   unsigned long delay)
32962306a36Sopenharmony_ci{
33062306a36Sopenharmony_ci	in6_ifa_hold(ifp);
33162306a36Sopenharmony_ci	if (mod_delayed_work(addrconf_wq, &ifp->dad_work, delay))
33262306a36Sopenharmony_ci		in6_ifa_put(ifp);
33362306a36Sopenharmony_ci}
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_cistatic int snmp6_alloc_dev(struct inet6_dev *idev)
33662306a36Sopenharmony_ci{
33762306a36Sopenharmony_ci	int i;
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	idev->stats.ipv6 = alloc_percpu_gfp(struct ipstats_mib, GFP_KERNEL_ACCOUNT);
34062306a36Sopenharmony_ci	if (!idev->stats.ipv6)
34162306a36Sopenharmony_ci		goto err_ip;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	for_each_possible_cpu(i) {
34462306a36Sopenharmony_ci		struct ipstats_mib *addrconf_stats;
34562306a36Sopenharmony_ci		addrconf_stats = per_cpu_ptr(idev->stats.ipv6, i);
34662306a36Sopenharmony_ci		u64_stats_init(&addrconf_stats->syncp);
34762306a36Sopenharmony_ci	}
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	idev->stats.icmpv6dev = kzalloc(sizeof(struct icmpv6_mib_device),
35162306a36Sopenharmony_ci					GFP_KERNEL);
35262306a36Sopenharmony_ci	if (!idev->stats.icmpv6dev)
35362306a36Sopenharmony_ci		goto err_icmp;
35462306a36Sopenharmony_ci	idev->stats.icmpv6msgdev = kzalloc(sizeof(struct icmpv6msg_mib_device),
35562306a36Sopenharmony_ci					   GFP_KERNEL_ACCOUNT);
35662306a36Sopenharmony_ci	if (!idev->stats.icmpv6msgdev)
35762306a36Sopenharmony_ci		goto err_icmpmsg;
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	return 0;
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_cierr_icmpmsg:
36262306a36Sopenharmony_ci	kfree(idev->stats.icmpv6dev);
36362306a36Sopenharmony_cierr_icmp:
36462306a36Sopenharmony_ci	free_percpu(idev->stats.ipv6);
36562306a36Sopenharmony_cierr_ip:
36662306a36Sopenharmony_ci	return -ENOMEM;
36762306a36Sopenharmony_ci}
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_cistatic struct inet6_dev *ipv6_add_dev(struct net_device *dev)
37062306a36Sopenharmony_ci{
37162306a36Sopenharmony_ci	struct inet6_dev *ndev;
37262306a36Sopenharmony_ci	int err = -ENOMEM;
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	ASSERT_RTNL();
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	if (dev->mtu < IPV6_MIN_MTU && dev != blackhole_netdev)
37762306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	ndev = kzalloc(sizeof(*ndev), GFP_KERNEL_ACCOUNT);
38062306a36Sopenharmony_ci	if (!ndev)
38162306a36Sopenharmony_ci		return ERR_PTR(err);
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	rwlock_init(&ndev->lock);
38462306a36Sopenharmony_ci	ndev->dev = dev;
38562306a36Sopenharmony_ci	INIT_LIST_HEAD(&ndev->addr_list);
38662306a36Sopenharmony_ci	timer_setup(&ndev->rs_timer, addrconf_rs_timer, 0);
38762306a36Sopenharmony_ci	memcpy(&ndev->cnf, dev_net(dev)->ipv6.devconf_dflt, sizeof(ndev->cnf));
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	if (ndev->cnf.stable_secret.initialized)
39062306a36Sopenharmony_ci		ndev->cnf.addr_gen_mode = IN6_ADDR_GEN_MODE_STABLE_PRIVACY;
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	ndev->cnf.mtu6 = dev->mtu;
39362306a36Sopenharmony_ci	ndev->ra_mtu = 0;
39462306a36Sopenharmony_ci	ndev->nd_parms = neigh_parms_alloc(dev, &nd_tbl);
39562306a36Sopenharmony_ci	if (!ndev->nd_parms) {
39662306a36Sopenharmony_ci		kfree(ndev);
39762306a36Sopenharmony_ci		return ERR_PTR(err);
39862306a36Sopenharmony_ci	}
39962306a36Sopenharmony_ci	if (ndev->cnf.forwarding)
40062306a36Sopenharmony_ci		dev_disable_lro(dev);
40162306a36Sopenharmony_ci	/* We refer to the device */
40262306a36Sopenharmony_ci	netdev_hold(dev, &ndev->dev_tracker, GFP_KERNEL);
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	if (snmp6_alloc_dev(ndev) < 0) {
40562306a36Sopenharmony_ci		netdev_dbg(dev, "%s: cannot allocate memory for statistics\n",
40662306a36Sopenharmony_ci			   __func__);
40762306a36Sopenharmony_ci		neigh_parms_release(&nd_tbl, ndev->nd_parms);
40862306a36Sopenharmony_ci		netdev_put(dev, &ndev->dev_tracker);
40962306a36Sopenharmony_ci		kfree(ndev);
41062306a36Sopenharmony_ci		return ERR_PTR(err);
41162306a36Sopenharmony_ci	}
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	if (dev != blackhole_netdev) {
41462306a36Sopenharmony_ci		if (snmp6_register_dev(ndev) < 0) {
41562306a36Sopenharmony_ci			netdev_dbg(dev, "%s: cannot create /proc/net/dev_snmp6/%s\n",
41662306a36Sopenharmony_ci				   __func__, dev->name);
41762306a36Sopenharmony_ci			goto err_release;
41862306a36Sopenharmony_ci		}
41962306a36Sopenharmony_ci	}
42062306a36Sopenharmony_ci	/* One reference from device. */
42162306a36Sopenharmony_ci	refcount_set(&ndev->refcnt, 1);
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	if (dev->flags & (IFF_NOARP | IFF_LOOPBACK))
42462306a36Sopenharmony_ci		ndev->cnf.accept_dad = -1;
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6_SIT)
42762306a36Sopenharmony_ci	if (dev->type == ARPHRD_SIT && (dev->priv_flags & IFF_ISATAP)) {
42862306a36Sopenharmony_ci		pr_info("%s: Disabled Multicast RS\n", dev->name);
42962306a36Sopenharmony_ci		ndev->cnf.rtr_solicits = 0;
43062306a36Sopenharmony_ci	}
43162306a36Sopenharmony_ci#endif
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	INIT_LIST_HEAD(&ndev->tempaddr_list);
43462306a36Sopenharmony_ci	ndev->desync_factor = U32_MAX;
43562306a36Sopenharmony_ci	if ((dev->flags&IFF_LOOPBACK) ||
43662306a36Sopenharmony_ci	    dev->type == ARPHRD_TUNNEL ||
43762306a36Sopenharmony_ci	    dev->type == ARPHRD_TUNNEL6 ||
43862306a36Sopenharmony_ci	    dev->type == ARPHRD_SIT ||
43962306a36Sopenharmony_ci	    dev->type == ARPHRD_NONE) {
44062306a36Sopenharmony_ci		ndev->cnf.use_tempaddr = -1;
44162306a36Sopenharmony_ci	}
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	ndev->token = in6addr_any;
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	if (netif_running(dev) && addrconf_link_ready(dev))
44662306a36Sopenharmony_ci		ndev->if_flags |= IF_READY;
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	ipv6_mc_init_dev(ndev);
44962306a36Sopenharmony_ci	ndev->tstamp = jiffies;
45062306a36Sopenharmony_ci	if (dev != blackhole_netdev) {
45162306a36Sopenharmony_ci		err = addrconf_sysctl_register(ndev);
45262306a36Sopenharmony_ci		if (err) {
45362306a36Sopenharmony_ci			ipv6_mc_destroy_dev(ndev);
45462306a36Sopenharmony_ci			snmp6_unregister_dev(ndev);
45562306a36Sopenharmony_ci			goto err_release;
45662306a36Sopenharmony_ci		}
45762306a36Sopenharmony_ci	}
45862306a36Sopenharmony_ci	/* protected by rtnl_lock */
45962306a36Sopenharmony_ci	rcu_assign_pointer(dev->ip6_ptr, ndev);
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	if (dev != blackhole_netdev) {
46262306a36Sopenharmony_ci		/* Join interface-local all-node multicast group */
46362306a36Sopenharmony_ci		ipv6_dev_mc_inc(dev, &in6addr_interfacelocal_allnodes);
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci		/* Join all-node multicast group */
46662306a36Sopenharmony_ci		ipv6_dev_mc_inc(dev, &in6addr_linklocal_allnodes);
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci		/* Join all-router multicast group if forwarding is set */
46962306a36Sopenharmony_ci		if (ndev->cnf.forwarding && (dev->flags & IFF_MULTICAST))
47062306a36Sopenharmony_ci			ipv6_dev_mc_inc(dev, &in6addr_linklocal_allrouters);
47162306a36Sopenharmony_ci	}
47262306a36Sopenharmony_ci	return ndev;
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_cierr_release:
47562306a36Sopenharmony_ci	neigh_parms_release(&nd_tbl, ndev->nd_parms);
47662306a36Sopenharmony_ci	ndev->dead = 1;
47762306a36Sopenharmony_ci	in6_dev_finish_destroy(ndev);
47862306a36Sopenharmony_ci	return ERR_PTR(err);
47962306a36Sopenharmony_ci}
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_cistatic struct inet6_dev *ipv6_find_idev(struct net_device *dev)
48262306a36Sopenharmony_ci{
48362306a36Sopenharmony_ci	struct inet6_dev *idev;
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	ASSERT_RTNL();
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	idev = __in6_dev_get(dev);
48862306a36Sopenharmony_ci	if (!idev) {
48962306a36Sopenharmony_ci		idev = ipv6_add_dev(dev);
49062306a36Sopenharmony_ci		if (IS_ERR(idev))
49162306a36Sopenharmony_ci			return idev;
49262306a36Sopenharmony_ci	}
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	if (dev->flags&IFF_UP)
49562306a36Sopenharmony_ci		ipv6_mc_up(idev);
49662306a36Sopenharmony_ci	return idev;
49762306a36Sopenharmony_ci}
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_cistatic int inet6_netconf_msgsize_devconf(int type)
50062306a36Sopenharmony_ci{
50162306a36Sopenharmony_ci	int size =  NLMSG_ALIGN(sizeof(struct netconfmsg))
50262306a36Sopenharmony_ci		    + nla_total_size(4);	/* NETCONFA_IFINDEX */
50362306a36Sopenharmony_ci	bool all = false;
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	if (type == NETCONFA_ALL)
50662306a36Sopenharmony_ci		all = true;
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	if (all || type == NETCONFA_FORWARDING)
50962306a36Sopenharmony_ci		size += nla_total_size(4);
51062306a36Sopenharmony_ci#ifdef CONFIG_IPV6_MROUTE
51162306a36Sopenharmony_ci	if (all || type == NETCONFA_MC_FORWARDING)
51262306a36Sopenharmony_ci		size += nla_total_size(4);
51362306a36Sopenharmony_ci#endif
51462306a36Sopenharmony_ci	if (all || type == NETCONFA_PROXY_NEIGH)
51562306a36Sopenharmony_ci		size += nla_total_size(4);
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	if (all || type == NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN)
51862306a36Sopenharmony_ci		size += nla_total_size(4);
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	return size;
52162306a36Sopenharmony_ci}
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_cistatic int inet6_netconf_fill_devconf(struct sk_buff *skb, int ifindex,
52462306a36Sopenharmony_ci				      struct ipv6_devconf *devconf, u32 portid,
52562306a36Sopenharmony_ci				      u32 seq, int event, unsigned int flags,
52662306a36Sopenharmony_ci				      int type)
52762306a36Sopenharmony_ci{
52862306a36Sopenharmony_ci	struct nlmsghdr  *nlh;
52962306a36Sopenharmony_ci	struct netconfmsg *ncm;
53062306a36Sopenharmony_ci	bool all = false;
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct netconfmsg),
53362306a36Sopenharmony_ci			flags);
53462306a36Sopenharmony_ci	if (!nlh)
53562306a36Sopenharmony_ci		return -EMSGSIZE;
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	if (type == NETCONFA_ALL)
53862306a36Sopenharmony_ci		all = true;
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	ncm = nlmsg_data(nlh);
54162306a36Sopenharmony_ci	ncm->ncm_family = AF_INET6;
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	if (nla_put_s32(skb, NETCONFA_IFINDEX, ifindex) < 0)
54462306a36Sopenharmony_ci		goto nla_put_failure;
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	if (!devconf)
54762306a36Sopenharmony_ci		goto out;
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	if ((all || type == NETCONFA_FORWARDING) &&
55062306a36Sopenharmony_ci	    nla_put_s32(skb, NETCONFA_FORWARDING, devconf->forwarding) < 0)
55162306a36Sopenharmony_ci		goto nla_put_failure;
55262306a36Sopenharmony_ci#ifdef CONFIG_IPV6_MROUTE
55362306a36Sopenharmony_ci	if ((all || type == NETCONFA_MC_FORWARDING) &&
55462306a36Sopenharmony_ci	    nla_put_s32(skb, NETCONFA_MC_FORWARDING,
55562306a36Sopenharmony_ci			atomic_read(&devconf->mc_forwarding)) < 0)
55662306a36Sopenharmony_ci		goto nla_put_failure;
55762306a36Sopenharmony_ci#endif
55862306a36Sopenharmony_ci	if ((all || type == NETCONFA_PROXY_NEIGH) &&
55962306a36Sopenharmony_ci	    nla_put_s32(skb, NETCONFA_PROXY_NEIGH, devconf->proxy_ndp) < 0)
56062306a36Sopenharmony_ci		goto nla_put_failure;
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	if ((all || type == NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN) &&
56362306a36Sopenharmony_ci	    nla_put_s32(skb, NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN,
56462306a36Sopenharmony_ci			devconf->ignore_routes_with_linkdown) < 0)
56562306a36Sopenharmony_ci		goto nla_put_failure;
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ciout:
56862306a36Sopenharmony_ci	nlmsg_end(skb, nlh);
56962306a36Sopenharmony_ci	return 0;
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_cinla_put_failure:
57262306a36Sopenharmony_ci	nlmsg_cancel(skb, nlh);
57362306a36Sopenharmony_ci	return -EMSGSIZE;
57462306a36Sopenharmony_ci}
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_civoid inet6_netconf_notify_devconf(struct net *net, int event, int type,
57762306a36Sopenharmony_ci				  int ifindex, struct ipv6_devconf *devconf)
57862306a36Sopenharmony_ci{
57962306a36Sopenharmony_ci	struct sk_buff *skb;
58062306a36Sopenharmony_ci	int err = -ENOBUFS;
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	skb = nlmsg_new(inet6_netconf_msgsize_devconf(type), GFP_KERNEL);
58362306a36Sopenharmony_ci	if (!skb)
58462306a36Sopenharmony_ci		goto errout;
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci	err = inet6_netconf_fill_devconf(skb, ifindex, devconf, 0, 0,
58762306a36Sopenharmony_ci					 event, 0, type);
58862306a36Sopenharmony_ci	if (err < 0) {
58962306a36Sopenharmony_ci		/* -EMSGSIZE implies BUG in inet6_netconf_msgsize_devconf() */
59062306a36Sopenharmony_ci		WARN_ON(err == -EMSGSIZE);
59162306a36Sopenharmony_ci		kfree_skb(skb);
59262306a36Sopenharmony_ci		goto errout;
59362306a36Sopenharmony_ci	}
59462306a36Sopenharmony_ci	rtnl_notify(skb, net, 0, RTNLGRP_IPV6_NETCONF, NULL, GFP_KERNEL);
59562306a36Sopenharmony_ci	return;
59662306a36Sopenharmony_cierrout:
59762306a36Sopenharmony_ci	rtnl_set_sk_err(net, RTNLGRP_IPV6_NETCONF, err);
59862306a36Sopenharmony_ci}
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_cistatic const struct nla_policy devconf_ipv6_policy[NETCONFA_MAX+1] = {
60162306a36Sopenharmony_ci	[NETCONFA_IFINDEX]	= { .len = sizeof(int) },
60262306a36Sopenharmony_ci	[NETCONFA_FORWARDING]	= { .len = sizeof(int) },
60362306a36Sopenharmony_ci	[NETCONFA_PROXY_NEIGH]	= { .len = sizeof(int) },
60462306a36Sopenharmony_ci	[NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN]	= { .len = sizeof(int) },
60562306a36Sopenharmony_ci};
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_cistatic int inet6_netconf_valid_get_req(struct sk_buff *skb,
60862306a36Sopenharmony_ci				       const struct nlmsghdr *nlh,
60962306a36Sopenharmony_ci				       struct nlattr **tb,
61062306a36Sopenharmony_ci				       struct netlink_ext_ack *extack)
61162306a36Sopenharmony_ci{
61262306a36Sopenharmony_ci	int i, err;
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(struct netconfmsg))) {
61562306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Invalid header for netconf get request");
61662306a36Sopenharmony_ci		return -EINVAL;
61762306a36Sopenharmony_ci	}
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	if (!netlink_strict_get_check(skb))
62062306a36Sopenharmony_ci		return nlmsg_parse_deprecated(nlh, sizeof(struct netconfmsg),
62162306a36Sopenharmony_ci					      tb, NETCONFA_MAX,
62262306a36Sopenharmony_ci					      devconf_ipv6_policy, extack);
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	err = nlmsg_parse_deprecated_strict(nlh, sizeof(struct netconfmsg),
62562306a36Sopenharmony_ci					    tb, NETCONFA_MAX,
62662306a36Sopenharmony_ci					    devconf_ipv6_policy, extack);
62762306a36Sopenharmony_ci	if (err)
62862306a36Sopenharmony_ci		return err;
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	for (i = 0; i <= NETCONFA_MAX; i++) {
63162306a36Sopenharmony_ci		if (!tb[i])
63262306a36Sopenharmony_ci			continue;
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci		switch (i) {
63562306a36Sopenharmony_ci		case NETCONFA_IFINDEX:
63662306a36Sopenharmony_ci			break;
63762306a36Sopenharmony_ci		default:
63862306a36Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "Unsupported attribute in netconf get request");
63962306a36Sopenharmony_ci			return -EINVAL;
64062306a36Sopenharmony_ci		}
64162306a36Sopenharmony_ci	}
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci	return 0;
64462306a36Sopenharmony_ci}
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_cistatic int inet6_netconf_get_devconf(struct sk_buff *in_skb,
64762306a36Sopenharmony_ci				     struct nlmsghdr *nlh,
64862306a36Sopenharmony_ci				     struct netlink_ext_ack *extack)
64962306a36Sopenharmony_ci{
65062306a36Sopenharmony_ci	struct net *net = sock_net(in_skb->sk);
65162306a36Sopenharmony_ci	struct nlattr *tb[NETCONFA_MAX+1];
65262306a36Sopenharmony_ci	struct inet6_dev *in6_dev = NULL;
65362306a36Sopenharmony_ci	struct net_device *dev = NULL;
65462306a36Sopenharmony_ci	struct sk_buff *skb;
65562306a36Sopenharmony_ci	struct ipv6_devconf *devconf;
65662306a36Sopenharmony_ci	int ifindex;
65762306a36Sopenharmony_ci	int err;
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci	err = inet6_netconf_valid_get_req(in_skb, nlh, tb, extack);
66062306a36Sopenharmony_ci	if (err < 0)
66162306a36Sopenharmony_ci		return err;
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	if (!tb[NETCONFA_IFINDEX])
66462306a36Sopenharmony_ci		return -EINVAL;
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	err = -EINVAL;
66762306a36Sopenharmony_ci	ifindex = nla_get_s32(tb[NETCONFA_IFINDEX]);
66862306a36Sopenharmony_ci	switch (ifindex) {
66962306a36Sopenharmony_ci	case NETCONFA_IFINDEX_ALL:
67062306a36Sopenharmony_ci		devconf = net->ipv6.devconf_all;
67162306a36Sopenharmony_ci		break;
67262306a36Sopenharmony_ci	case NETCONFA_IFINDEX_DEFAULT:
67362306a36Sopenharmony_ci		devconf = net->ipv6.devconf_dflt;
67462306a36Sopenharmony_ci		break;
67562306a36Sopenharmony_ci	default:
67662306a36Sopenharmony_ci		dev = dev_get_by_index(net, ifindex);
67762306a36Sopenharmony_ci		if (!dev)
67862306a36Sopenharmony_ci			return -EINVAL;
67962306a36Sopenharmony_ci		in6_dev = in6_dev_get(dev);
68062306a36Sopenharmony_ci		if (!in6_dev)
68162306a36Sopenharmony_ci			goto errout;
68262306a36Sopenharmony_ci		devconf = &in6_dev->cnf;
68362306a36Sopenharmony_ci		break;
68462306a36Sopenharmony_ci	}
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	err = -ENOBUFS;
68762306a36Sopenharmony_ci	skb = nlmsg_new(inet6_netconf_msgsize_devconf(NETCONFA_ALL), GFP_KERNEL);
68862306a36Sopenharmony_ci	if (!skb)
68962306a36Sopenharmony_ci		goto errout;
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci	err = inet6_netconf_fill_devconf(skb, ifindex, devconf,
69262306a36Sopenharmony_ci					 NETLINK_CB(in_skb).portid,
69362306a36Sopenharmony_ci					 nlh->nlmsg_seq, RTM_NEWNETCONF, 0,
69462306a36Sopenharmony_ci					 NETCONFA_ALL);
69562306a36Sopenharmony_ci	if (err < 0) {
69662306a36Sopenharmony_ci		/* -EMSGSIZE implies BUG in inet6_netconf_msgsize_devconf() */
69762306a36Sopenharmony_ci		WARN_ON(err == -EMSGSIZE);
69862306a36Sopenharmony_ci		kfree_skb(skb);
69962306a36Sopenharmony_ci		goto errout;
70062306a36Sopenharmony_ci	}
70162306a36Sopenharmony_ci	err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
70262306a36Sopenharmony_cierrout:
70362306a36Sopenharmony_ci	if (in6_dev)
70462306a36Sopenharmony_ci		in6_dev_put(in6_dev);
70562306a36Sopenharmony_ci	dev_put(dev);
70662306a36Sopenharmony_ci	return err;
70762306a36Sopenharmony_ci}
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci/* Combine dev_addr_genid and dev_base_seq to detect changes.
71062306a36Sopenharmony_ci */
71162306a36Sopenharmony_cistatic u32 inet6_base_seq(const struct net *net)
71262306a36Sopenharmony_ci{
71362306a36Sopenharmony_ci	u32 res = atomic_read(&net->ipv6.dev_addr_genid) +
71462306a36Sopenharmony_ci		  net->dev_base_seq;
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci	/* Must not return 0 (see nl_dump_check_consistent()).
71762306a36Sopenharmony_ci	 * Chose a value far away from 0.
71862306a36Sopenharmony_ci	 */
71962306a36Sopenharmony_ci	if (!res)
72062306a36Sopenharmony_ci		res = 0x80000000;
72162306a36Sopenharmony_ci	return res;
72262306a36Sopenharmony_ci}
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_cistatic int inet6_netconf_dump_devconf(struct sk_buff *skb,
72662306a36Sopenharmony_ci				      struct netlink_callback *cb)
72762306a36Sopenharmony_ci{
72862306a36Sopenharmony_ci	const struct nlmsghdr *nlh = cb->nlh;
72962306a36Sopenharmony_ci	struct net *net = sock_net(skb->sk);
73062306a36Sopenharmony_ci	int h, s_h;
73162306a36Sopenharmony_ci	int idx, s_idx;
73262306a36Sopenharmony_ci	struct net_device *dev;
73362306a36Sopenharmony_ci	struct inet6_dev *idev;
73462306a36Sopenharmony_ci	struct hlist_head *head;
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	if (cb->strict_check) {
73762306a36Sopenharmony_ci		struct netlink_ext_ack *extack = cb->extack;
73862306a36Sopenharmony_ci		struct netconfmsg *ncm;
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci		if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ncm))) {
74162306a36Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "Invalid header for netconf dump request");
74262306a36Sopenharmony_ci			return -EINVAL;
74362306a36Sopenharmony_ci		}
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci		if (nlmsg_attrlen(nlh, sizeof(*ncm))) {
74662306a36Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "Invalid data after header in netconf dump request");
74762306a36Sopenharmony_ci			return -EINVAL;
74862306a36Sopenharmony_ci		}
74962306a36Sopenharmony_ci	}
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci	s_h = cb->args[0];
75262306a36Sopenharmony_ci	s_idx = idx = cb->args[1];
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
75562306a36Sopenharmony_ci		idx = 0;
75662306a36Sopenharmony_ci		head = &net->dev_index_head[h];
75762306a36Sopenharmony_ci		rcu_read_lock();
75862306a36Sopenharmony_ci		cb->seq = inet6_base_seq(net);
75962306a36Sopenharmony_ci		hlist_for_each_entry_rcu(dev, head, index_hlist) {
76062306a36Sopenharmony_ci			if (idx < s_idx)
76162306a36Sopenharmony_ci				goto cont;
76262306a36Sopenharmony_ci			idev = __in6_dev_get(dev);
76362306a36Sopenharmony_ci			if (!idev)
76462306a36Sopenharmony_ci				goto cont;
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci			if (inet6_netconf_fill_devconf(skb, dev->ifindex,
76762306a36Sopenharmony_ci						       &idev->cnf,
76862306a36Sopenharmony_ci						       NETLINK_CB(cb->skb).portid,
76962306a36Sopenharmony_ci						       nlh->nlmsg_seq,
77062306a36Sopenharmony_ci						       RTM_NEWNETCONF,
77162306a36Sopenharmony_ci						       NLM_F_MULTI,
77262306a36Sopenharmony_ci						       NETCONFA_ALL) < 0) {
77362306a36Sopenharmony_ci				rcu_read_unlock();
77462306a36Sopenharmony_ci				goto done;
77562306a36Sopenharmony_ci			}
77662306a36Sopenharmony_ci			nl_dump_check_consistent(cb, nlmsg_hdr(skb));
77762306a36Sopenharmony_cicont:
77862306a36Sopenharmony_ci			idx++;
77962306a36Sopenharmony_ci		}
78062306a36Sopenharmony_ci		rcu_read_unlock();
78162306a36Sopenharmony_ci	}
78262306a36Sopenharmony_ci	if (h == NETDEV_HASHENTRIES) {
78362306a36Sopenharmony_ci		if (inet6_netconf_fill_devconf(skb, NETCONFA_IFINDEX_ALL,
78462306a36Sopenharmony_ci					       net->ipv6.devconf_all,
78562306a36Sopenharmony_ci					       NETLINK_CB(cb->skb).portid,
78662306a36Sopenharmony_ci					       nlh->nlmsg_seq,
78762306a36Sopenharmony_ci					       RTM_NEWNETCONF, NLM_F_MULTI,
78862306a36Sopenharmony_ci					       NETCONFA_ALL) < 0)
78962306a36Sopenharmony_ci			goto done;
79062306a36Sopenharmony_ci		else
79162306a36Sopenharmony_ci			h++;
79262306a36Sopenharmony_ci	}
79362306a36Sopenharmony_ci	if (h == NETDEV_HASHENTRIES + 1) {
79462306a36Sopenharmony_ci		if (inet6_netconf_fill_devconf(skb, NETCONFA_IFINDEX_DEFAULT,
79562306a36Sopenharmony_ci					       net->ipv6.devconf_dflt,
79662306a36Sopenharmony_ci					       NETLINK_CB(cb->skb).portid,
79762306a36Sopenharmony_ci					       nlh->nlmsg_seq,
79862306a36Sopenharmony_ci					       RTM_NEWNETCONF, NLM_F_MULTI,
79962306a36Sopenharmony_ci					       NETCONFA_ALL) < 0)
80062306a36Sopenharmony_ci			goto done;
80162306a36Sopenharmony_ci		else
80262306a36Sopenharmony_ci			h++;
80362306a36Sopenharmony_ci	}
80462306a36Sopenharmony_cidone:
80562306a36Sopenharmony_ci	cb->args[0] = h;
80662306a36Sopenharmony_ci	cb->args[1] = idx;
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci	return skb->len;
80962306a36Sopenharmony_ci}
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci#ifdef CONFIG_SYSCTL
81262306a36Sopenharmony_cistatic void dev_forward_change(struct inet6_dev *idev)
81362306a36Sopenharmony_ci{
81462306a36Sopenharmony_ci	struct net_device *dev;
81562306a36Sopenharmony_ci	struct inet6_ifaddr *ifa;
81662306a36Sopenharmony_ci	LIST_HEAD(tmp_addr_list);
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci	if (!idev)
81962306a36Sopenharmony_ci		return;
82062306a36Sopenharmony_ci	dev = idev->dev;
82162306a36Sopenharmony_ci	if (idev->cnf.forwarding)
82262306a36Sopenharmony_ci		dev_disable_lro(dev);
82362306a36Sopenharmony_ci	if (dev->flags & IFF_MULTICAST) {
82462306a36Sopenharmony_ci		if (idev->cnf.forwarding) {
82562306a36Sopenharmony_ci			ipv6_dev_mc_inc(dev, &in6addr_linklocal_allrouters);
82662306a36Sopenharmony_ci			ipv6_dev_mc_inc(dev, &in6addr_interfacelocal_allrouters);
82762306a36Sopenharmony_ci			ipv6_dev_mc_inc(dev, &in6addr_sitelocal_allrouters);
82862306a36Sopenharmony_ci		} else {
82962306a36Sopenharmony_ci			ipv6_dev_mc_dec(dev, &in6addr_linklocal_allrouters);
83062306a36Sopenharmony_ci			ipv6_dev_mc_dec(dev, &in6addr_interfacelocal_allrouters);
83162306a36Sopenharmony_ci			ipv6_dev_mc_dec(dev, &in6addr_sitelocal_allrouters);
83262306a36Sopenharmony_ci		}
83362306a36Sopenharmony_ci	}
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci	read_lock_bh(&idev->lock);
83662306a36Sopenharmony_ci	list_for_each_entry(ifa, &idev->addr_list, if_list) {
83762306a36Sopenharmony_ci		if (ifa->flags&IFA_F_TENTATIVE)
83862306a36Sopenharmony_ci			continue;
83962306a36Sopenharmony_ci		list_add_tail(&ifa->if_list_aux, &tmp_addr_list);
84062306a36Sopenharmony_ci	}
84162306a36Sopenharmony_ci	read_unlock_bh(&idev->lock);
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_ci	while (!list_empty(&tmp_addr_list)) {
84462306a36Sopenharmony_ci		ifa = list_first_entry(&tmp_addr_list,
84562306a36Sopenharmony_ci				       struct inet6_ifaddr, if_list_aux);
84662306a36Sopenharmony_ci		list_del(&ifa->if_list_aux);
84762306a36Sopenharmony_ci		if (idev->cnf.forwarding)
84862306a36Sopenharmony_ci			addrconf_join_anycast(ifa);
84962306a36Sopenharmony_ci		else
85062306a36Sopenharmony_ci			addrconf_leave_anycast(ifa);
85162306a36Sopenharmony_ci	}
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ci	inet6_netconf_notify_devconf(dev_net(dev), RTM_NEWNETCONF,
85462306a36Sopenharmony_ci				     NETCONFA_FORWARDING,
85562306a36Sopenharmony_ci				     dev->ifindex, &idev->cnf);
85662306a36Sopenharmony_ci}
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_cistatic void addrconf_forward_change(struct net *net, __s32 newf)
86062306a36Sopenharmony_ci{
86162306a36Sopenharmony_ci	struct net_device *dev;
86262306a36Sopenharmony_ci	struct inet6_dev *idev;
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci	for_each_netdev(net, dev) {
86562306a36Sopenharmony_ci		idev = __in6_dev_get(dev);
86662306a36Sopenharmony_ci		if (idev) {
86762306a36Sopenharmony_ci			int changed = (!idev->cnf.forwarding) ^ (!newf);
86862306a36Sopenharmony_ci			idev->cnf.forwarding = newf;
86962306a36Sopenharmony_ci			if (changed)
87062306a36Sopenharmony_ci				dev_forward_change(idev);
87162306a36Sopenharmony_ci		}
87262306a36Sopenharmony_ci	}
87362306a36Sopenharmony_ci}
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_cistatic int addrconf_fixup_forwarding(struct ctl_table *table, int *p, int newf)
87662306a36Sopenharmony_ci{
87762306a36Sopenharmony_ci	struct net *net;
87862306a36Sopenharmony_ci	int old;
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_ci	if (!rtnl_trylock())
88162306a36Sopenharmony_ci		return restart_syscall();
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci	net = (struct net *)table->extra2;
88462306a36Sopenharmony_ci	old = *p;
88562306a36Sopenharmony_ci	*p = newf;
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_ci	if (p == &net->ipv6.devconf_dflt->forwarding) {
88862306a36Sopenharmony_ci		if ((!newf) ^ (!old))
88962306a36Sopenharmony_ci			inet6_netconf_notify_devconf(net, RTM_NEWNETCONF,
89062306a36Sopenharmony_ci						     NETCONFA_FORWARDING,
89162306a36Sopenharmony_ci						     NETCONFA_IFINDEX_DEFAULT,
89262306a36Sopenharmony_ci						     net->ipv6.devconf_dflt);
89362306a36Sopenharmony_ci		rtnl_unlock();
89462306a36Sopenharmony_ci		return 0;
89562306a36Sopenharmony_ci	}
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	if (p == &net->ipv6.devconf_all->forwarding) {
89862306a36Sopenharmony_ci		int old_dflt = net->ipv6.devconf_dflt->forwarding;
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci		net->ipv6.devconf_dflt->forwarding = newf;
90162306a36Sopenharmony_ci		if ((!newf) ^ (!old_dflt))
90262306a36Sopenharmony_ci			inet6_netconf_notify_devconf(net, RTM_NEWNETCONF,
90362306a36Sopenharmony_ci						     NETCONFA_FORWARDING,
90462306a36Sopenharmony_ci						     NETCONFA_IFINDEX_DEFAULT,
90562306a36Sopenharmony_ci						     net->ipv6.devconf_dflt);
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci		addrconf_forward_change(net, newf);
90862306a36Sopenharmony_ci		if ((!newf) ^ (!old))
90962306a36Sopenharmony_ci			inet6_netconf_notify_devconf(net, RTM_NEWNETCONF,
91062306a36Sopenharmony_ci						     NETCONFA_FORWARDING,
91162306a36Sopenharmony_ci						     NETCONFA_IFINDEX_ALL,
91262306a36Sopenharmony_ci						     net->ipv6.devconf_all);
91362306a36Sopenharmony_ci	} else if ((!newf) ^ (!old))
91462306a36Sopenharmony_ci		dev_forward_change((struct inet6_dev *)table->extra1);
91562306a36Sopenharmony_ci	rtnl_unlock();
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci	if (newf)
91862306a36Sopenharmony_ci		rt6_purge_dflt_routers(net);
91962306a36Sopenharmony_ci	return 1;
92062306a36Sopenharmony_ci}
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_cistatic void addrconf_linkdown_change(struct net *net, __s32 newf)
92362306a36Sopenharmony_ci{
92462306a36Sopenharmony_ci	struct net_device *dev;
92562306a36Sopenharmony_ci	struct inet6_dev *idev;
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_ci	for_each_netdev(net, dev) {
92862306a36Sopenharmony_ci		idev = __in6_dev_get(dev);
92962306a36Sopenharmony_ci		if (idev) {
93062306a36Sopenharmony_ci			int changed = (!idev->cnf.ignore_routes_with_linkdown) ^ (!newf);
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ci			idev->cnf.ignore_routes_with_linkdown = newf;
93362306a36Sopenharmony_ci			if (changed)
93462306a36Sopenharmony_ci				inet6_netconf_notify_devconf(dev_net(dev),
93562306a36Sopenharmony_ci							     RTM_NEWNETCONF,
93662306a36Sopenharmony_ci							     NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN,
93762306a36Sopenharmony_ci							     dev->ifindex,
93862306a36Sopenharmony_ci							     &idev->cnf);
93962306a36Sopenharmony_ci		}
94062306a36Sopenharmony_ci	}
94162306a36Sopenharmony_ci}
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_cistatic int addrconf_fixup_linkdown(struct ctl_table *table, int *p, int newf)
94462306a36Sopenharmony_ci{
94562306a36Sopenharmony_ci	struct net *net;
94662306a36Sopenharmony_ci	int old;
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_ci	if (!rtnl_trylock())
94962306a36Sopenharmony_ci		return restart_syscall();
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_ci	net = (struct net *)table->extra2;
95262306a36Sopenharmony_ci	old = *p;
95362306a36Sopenharmony_ci	*p = newf;
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci	if (p == &net->ipv6.devconf_dflt->ignore_routes_with_linkdown) {
95662306a36Sopenharmony_ci		if ((!newf) ^ (!old))
95762306a36Sopenharmony_ci			inet6_netconf_notify_devconf(net,
95862306a36Sopenharmony_ci						     RTM_NEWNETCONF,
95962306a36Sopenharmony_ci						     NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN,
96062306a36Sopenharmony_ci						     NETCONFA_IFINDEX_DEFAULT,
96162306a36Sopenharmony_ci						     net->ipv6.devconf_dflt);
96262306a36Sopenharmony_ci		rtnl_unlock();
96362306a36Sopenharmony_ci		return 0;
96462306a36Sopenharmony_ci	}
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_ci	if (p == &net->ipv6.devconf_all->ignore_routes_with_linkdown) {
96762306a36Sopenharmony_ci		net->ipv6.devconf_dflt->ignore_routes_with_linkdown = newf;
96862306a36Sopenharmony_ci		addrconf_linkdown_change(net, newf);
96962306a36Sopenharmony_ci		if ((!newf) ^ (!old))
97062306a36Sopenharmony_ci			inet6_netconf_notify_devconf(net,
97162306a36Sopenharmony_ci						     RTM_NEWNETCONF,
97262306a36Sopenharmony_ci						     NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN,
97362306a36Sopenharmony_ci						     NETCONFA_IFINDEX_ALL,
97462306a36Sopenharmony_ci						     net->ipv6.devconf_all);
97562306a36Sopenharmony_ci	}
97662306a36Sopenharmony_ci	rtnl_unlock();
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_ci	return 1;
97962306a36Sopenharmony_ci}
98062306a36Sopenharmony_ci
98162306a36Sopenharmony_ci#endif
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_ci/* Nobody refers to this ifaddr, destroy it */
98462306a36Sopenharmony_civoid inet6_ifa_finish_destroy(struct inet6_ifaddr *ifp)
98562306a36Sopenharmony_ci{
98662306a36Sopenharmony_ci	WARN_ON(!hlist_unhashed(&ifp->addr_lst));
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci#ifdef NET_REFCNT_DEBUG
98962306a36Sopenharmony_ci	pr_debug("%s\n", __func__);
99062306a36Sopenharmony_ci#endif
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_ci	in6_dev_put(ifp->idev);
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ci	if (cancel_delayed_work(&ifp->dad_work))
99562306a36Sopenharmony_ci		pr_notice("delayed DAD work was pending while freeing ifa=%p\n",
99662306a36Sopenharmony_ci			  ifp);
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci	if (ifp->state != INET6_IFADDR_STATE_DEAD) {
99962306a36Sopenharmony_ci		pr_warn("Freeing alive inet6 address %p\n", ifp);
100062306a36Sopenharmony_ci		return;
100162306a36Sopenharmony_ci	}
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_ci	kfree_rcu(ifp, rcu);
100462306a36Sopenharmony_ci}
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_cistatic void
100762306a36Sopenharmony_ciipv6_link_dev_addr(struct inet6_dev *idev, struct inet6_ifaddr *ifp)
100862306a36Sopenharmony_ci{
100962306a36Sopenharmony_ci	struct list_head *p;
101062306a36Sopenharmony_ci	int ifp_scope = ipv6_addr_src_scope(&ifp->addr);
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_ci	/*
101362306a36Sopenharmony_ci	 * Each device address list is sorted in order of scope -
101462306a36Sopenharmony_ci	 * global before linklocal.
101562306a36Sopenharmony_ci	 */
101662306a36Sopenharmony_ci	list_for_each(p, &idev->addr_list) {
101762306a36Sopenharmony_ci		struct inet6_ifaddr *ifa
101862306a36Sopenharmony_ci			= list_entry(p, struct inet6_ifaddr, if_list);
101962306a36Sopenharmony_ci		if (ifp_scope >= ipv6_addr_src_scope(&ifa->addr))
102062306a36Sopenharmony_ci			break;
102162306a36Sopenharmony_ci	}
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci	list_add_tail_rcu(&ifp->if_list, p);
102462306a36Sopenharmony_ci}
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_cistatic u32 inet6_addr_hash(const struct net *net, const struct in6_addr *addr)
102762306a36Sopenharmony_ci{
102862306a36Sopenharmony_ci	u32 val = ipv6_addr_hash(addr) ^ net_hash_mix(net);
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_ci	return hash_32(val, IN6_ADDR_HSIZE_SHIFT);
103162306a36Sopenharmony_ci}
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_cistatic bool ipv6_chk_same_addr(struct net *net, const struct in6_addr *addr,
103462306a36Sopenharmony_ci			       struct net_device *dev, unsigned int hash)
103562306a36Sopenharmony_ci{
103662306a36Sopenharmony_ci	struct inet6_ifaddr *ifp;
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci	hlist_for_each_entry(ifp, &net->ipv6.inet6_addr_lst[hash], addr_lst) {
103962306a36Sopenharmony_ci		if (ipv6_addr_equal(&ifp->addr, addr)) {
104062306a36Sopenharmony_ci			if (!dev || ifp->idev->dev == dev)
104162306a36Sopenharmony_ci				return true;
104262306a36Sopenharmony_ci		}
104362306a36Sopenharmony_ci	}
104462306a36Sopenharmony_ci	return false;
104562306a36Sopenharmony_ci}
104662306a36Sopenharmony_ci
104762306a36Sopenharmony_cistatic int ipv6_add_addr_hash(struct net_device *dev, struct inet6_ifaddr *ifa)
104862306a36Sopenharmony_ci{
104962306a36Sopenharmony_ci	struct net *net = dev_net(dev);
105062306a36Sopenharmony_ci	unsigned int hash = inet6_addr_hash(net, &ifa->addr);
105162306a36Sopenharmony_ci	int err = 0;
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_ci	spin_lock_bh(&net->ipv6.addrconf_hash_lock);
105462306a36Sopenharmony_ci
105562306a36Sopenharmony_ci	/* Ignore adding duplicate addresses on an interface */
105662306a36Sopenharmony_ci	if (ipv6_chk_same_addr(net, &ifa->addr, dev, hash)) {
105762306a36Sopenharmony_ci		netdev_dbg(dev, "ipv6_add_addr: already assigned\n");
105862306a36Sopenharmony_ci		err = -EEXIST;
105962306a36Sopenharmony_ci	} else {
106062306a36Sopenharmony_ci		hlist_add_head_rcu(&ifa->addr_lst, &net->ipv6.inet6_addr_lst[hash]);
106162306a36Sopenharmony_ci	}
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_ci	spin_unlock_bh(&net->ipv6.addrconf_hash_lock);
106462306a36Sopenharmony_ci
106562306a36Sopenharmony_ci	return err;
106662306a36Sopenharmony_ci}
106762306a36Sopenharmony_ci
106862306a36Sopenharmony_ci/* On success it returns ifp with increased reference count */
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_cistatic struct inet6_ifaddr *
107162306a36Sopenharmony_ciipv6_add_addr(struct inet6_dev *idev, struct ifa6_config *cfg,
107262306a36Sopenharmony_ci	      bool can_block, struct netlink_ext_ack *extack)
107362306a36Sopenharmony_ci{
107462306a36Sopenharmony_ci	gfp_t gfp_flags = can_block ? GFP_KERNEL : GFP_ATOMIC;
107562306a36Sopenharmony_ci	int addr_type = ipv6_addr_type(cfg->pfx);
107662306a36Sopenharmony_ci	struct net *net = dev_net(idev->dev);
107762306a36Sopenharmony_ci	struct inet6_ifaddr *ifa = NULL;
107862306a36Sopenharmony_ci	struct fib6_info *f6i = NULL;
107962306a36Sopenharmony_ci	int err = 0;
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_ci	if (addr_type == IPV6_ADDR_ANY) {
108262306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Invalid address");
108362306a36Sopenharmony_ci		return ERR_PTR(-EADDRNOTAVAIL);
108462306a36Sopenharmony_ci	} else if (addr_type & IPV6_ADDR_MULTICAST &&
108562306a36Sopenharmony_ci		   !(cfg->ifa_flags & IFA_F_MCAUTOJOIN)) {
108662306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Cannot assign multicast address without \"IFA_F_MCAUTOJOIN\" flag");
108762306a36Sopenharmony_ci		return ERR_PTR(-EADDRNOTAVAIL);
108862306a36Sopenharmony_ci	} else if (!(idev->dev->flags & IFF_LOOPBACK) &&
108962306a36Sopenharmony_ci		   !netif_is_l3_master(idev->dev) &&
109062306a36Sopenharmony_ci		   addr_type & IPV6_ADDR_LOOPBACK) {
109162306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Cannot assign loopback address on this device");
109262306a36Sopenharmony_ci		return ERR_PTR(-EADDRNOTAVAIL);
109362306a36Sopenharmony_ci	}
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci	if (idev->dead) {
109662306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "device is going away");
109762306a36Sopenharmony_ci		err = -ENODEV;
109862306a36Sopenharmony_ci		goto out;
109962306a36Sopenharmony_ci	}
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_ci	if (idev->cnf.disable_ipv6) {
110262306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "IPv6 is disabled on this device");
110362306a36Sopenharmony_ci		err = -EACCES;
110462306a36Sopenharmony_ci		goto out;
110562306a36Sopenharmony_ci	}
110662306a36Sopenharmony_ci
110762306a36Sopenharmony_ci	/* validator notifier needs to be blocking;
110862306a36Sopenharmony_ci	 * do not call in atomic context
110962306a36Sopenharmony_ci	 */
111062306a36Sopenharmony_ci	if (can_block) {
111162306a36Sopenharmony_ci		struct in6_validator_info i6vi = {
111262306a36Sopenharmony_ci			.i6vi_addr = *cfg->pfx,
111362306a36Sopenharmony_ci			.i6vi_dev = idev,
111462306a36Sopenharmony_ci			.extack = extack,
111562306a36Sopenharmony_ci		};
111662306a36Sopenharmony_ci
111762306a36Sopenharmony_ci		err = inet6addr_validator_notifier_call_chain(NETDEV_UP, &i6vi);
111862306a36Sopenharmony_ci		err = notifier_to_errno(err);
111962306a36Sopenharmony_ci		if (err < 0)
112062306a36Sopenharmony_ci			goto out;
112162306a36Sopenharmony_ci	}
112262306a36Sopenharmony_ci
112362306a36Sopenharmony_ci	ifa = kzalloc(sizeof(*ifa), gfp_flags | __GFP_ACCOUNT);
112462306a36Sopenharmony_ci	if (!ifa) {
112562306a36Sopenharmony_ci		err = -ENOBUFS;
112662306a36Sopenharmony_ci		goto out;
112762306a36Sopenharmony_ci	}
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_ci	f6i = addrconf_f6i_alloc(net, idev, cfg->pfx, false, gfp_flags, extack);
113062306a36Sopenharmony_ci	if (IS_ERR(f6i)) {
113162306a36Sopenharmony_ci		err = PTR_ERR(f6i);
113262306a36Sopenharmony_ci		f6i = NULL;
113362306a36Sopenharmony_ci		goto out;
113462306a36Sopenharmony_ci	}
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_ci	neigh_parms_data_state_setall(idev->nd_parms);
113762306a36Sopenharmony_ci
113862306a36Sopenharmony_ci	ifa->addr = *cfg->pfx;
113962306a36Sopenharmony_ci	if (cfg->peer_pfx)
114062306a36Sopenharmony_ci		ifa->peer_addr = *cfg->peer_pfx;
114162306a36Sopenharmony_ci
114262306a36Sopenharmony_ci	spin_lock_init(&ifa->lock);
114362306a36Sopenharmony_ci	INIT_DELAYED_WORK(&ifa->dad_work, addrconf_dad_work);
114462306a36Sopenharmony_ci	INIT_HLIST_NODE(&ifa->addr_lst);
114562306a36Sopenharmony_ci	ifa->scope = cfg->scope;
114662306a36Sopenharmony_ci	ifa->prefix_len = cfg->plen;
114762306a36Sopenharmony_ci	ifa->rt_priority = cfg->rt_priority;
114862306a36Sopenharmony_ci	ifa->flags = cfg->ifa_flags;
114962306a36Sopenharmony_ci	ifa->ifa_proto = cfg->ifa_proto;
115062306a36Sopenharmony_ci	/* No need to add the TENTATIVE flag for addresses with NODAD */
115162306a36Sopenharmony_ci	if (!(cfg->ifa_flags & IFA_F_NODAD))
115262306a36Sopenharmony_ci		ifa->flags |= IFA_F_TENTATIVE;
115362306a36Sopenharmony_ci	ifa->valid_lft = cfg->valid_lft;
115462306a36Sopenharmony_ci	ifa->prefered_lft = cfg->preferred_lft;
115562306a36Sopenharmony_ci	ifa->cstamp = ifa->tstamp = jiffies;
115662306a36Sopenharmony_ci	ifa->tokenized = false;
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_ci	ifa->rt = f6i;
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_ci	ifa->idev = idev;
116162306a36Sopenharmony_ci	in6_dev_hold(idev);
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ci	/* For caller */
116462306a36Sopenharmony_ci	refcount_set(&ifa->refcnt, 1);
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_ci	rcu_read_lock();
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_ci	err = ipv6_add_addr_hash(idev->dev, ifa);
116962306a36Sopenharmony_ci	if (err < 0) {
117062306a36Sopenharmony_ci		rcu_read_unlock();
117162306a36Sopenharmony_ci		goto out;
117262306a36Sopenharmony_ci	}
117362306a36Sopenharmony_ci
117462306a36Sopenharmony_ci	write_lock_bh(&idev->lock);
117562306a36Sopenharmony_ci
117662306a36Sopenharmony_ci	/* Add to inet6_dev unicast addr list. */
117762306a36Sopenharmony_ci	ipv6_link_dev_addr(idev, ifa);
117862306a36Sopenharmony_ci
117962306a36Sopenharmony_ci	if (ifa->flags&IFA_F_TEMPORARY) {
118062306a36Sopenharmony_ci		list_add(&ifa->tmp_list, &idev->tempaddr_list);
118162306a36Sopenharmony_ci		in6_ifa_hold(ifa);
118262306a36Sopenharmony_ci	}
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_ci	in6_ifa_hold(ifa);
118562306a36Sopenharmony_ci	write_unlock_bh(&idev->lock);
118662306a36Sopenharmony_ci
118762306a36Sopenharmony_ci	rcu_read_unlock();
118862306a36Sopenharmony_ci
118962306a36Sopenharmony_ci	inet6addr_notifier_call_chain(NETDEV_UP, ifa);
119062306a36Sopenharmony_ciout:
119162306a36Sopenharmony_ci	if (unlikely(err < 0)) {
119262306a36Sopenharmony_ci		fib6_info_release(f6i);
119362306a36Sopenharmony_ci
119462306a36Sopenharmony_ci		if (ifa) {
119562306a36Sopenharmony_ci			if (ifa->idev)
119662306a36Sopenharmony_ci				in6_dev_put(ifa->idev);
119762306a36Sopenharmony_ci			kfree(ifa);
119862306a36Sopenharmony_ci		}
119962306a36Sopenharmony_ci		ifa = ERR_PTR(err);
120062306a36Sopenharmony_ci	}
120162306a36Sopenharmony_ci
120262306a36Sopenharmony_ci	return ifa;
120362306a36Sopenharmony_ci}
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_cienum cleanup_prefix_rt_t {
120662306a36Sopenharmony_ci	CLEANUP_PREFIX_RT_NOP,    /* no cleanup action for prefix route */
120762306a36Sopenharmony_ci	CLEANUP_PREFIX_RT_DEL,    /* delete the prefix route */
120862306a36Sopenharmony_ci	CLEANUP_PREFIX_RT_EXPIRE, /* update the lifetime of the prefix route */
120962306a36Sopenharmony_ci};
121062306a36Sopenharmony_ci
121162306a36Sopenharmony_ci/*
121262306a36Sopenharmony_ci * Check, whether the prefix for ifp would still need a prefix route
121362306a36Sopenharmony_ci * after deleting ifp. The function returns one of the CLEANUP_PREFIX_RT_*
121462306a36Sopenharmony_ci * constants.
121562306a36Sopenharmony_ci *
121662306a36Sopenharmony_ci * 1) we don't purge prefix if address was not permanent.
121762306a36Sopenharmony_ci *    prefix is managed by its own lifetime.
121862306a36Sopenharmony_ci * 2) we also don't purge, if the address was IFA_F_NOPREFIXROUTE.
121962306a36Sopenharmony_ci * 3) if there are no addresses, delete prefix.
122062306a36Sopenharmony_ci * 4) if there are still other permanent address(es),
122162306a36Sopenharmony_ci *    corresponding prefix is still permanent.
122262306a36Sopenharmony_ci * 5) if there are still other addresses with IFA_F_NOPREFIXROUTE,
122362306a36Sopenharmony_ci *    don't purge the prefix, assume user space is managing it.
122462306a36Sopenharmony_ci * 6) otherwise, update prefix lifetime to the
122562306a36Sopenharmony_ci *    longest valid lifetime among the corresponding
122662306a36Sopenharmony_ci *    addresses on the device.
122762306a36Sopenharmony_ci *    Note: subsequent RA will update lifetime.
122862306a36Sopenharmony_ci **/
122962306a36Sopenharmony_cistatic enum cleanup_prefix_rt_t
123062306a36Sopenharmony_cicheck_cleanup_prefix_route(struct inet6_ifaddr *ifp, unsigned long *expires)
123162306a36Sopenharmony_ci{
123262306a36Sopenharmony_ci	struct inet6_ifaddr *ifa;
123362306a36Sopenharmony_ci	struct inet6_dev *idev = ifp->idev;
123462306a36Sopenharmony_ci	unsigned long lifetime;
123562306a36Sopenharmony_ci	enum cleanup_prefix_rt_t action = CLEANUP_PREFIX_RT_DEL;
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_ci	*expires = jiffies;
123862306a36Sopenharmony_ci
123962306a36Sopenharmony_ci	list_for_each_entry(ifa, &idev->addr_list, if_list) {
124062306a36Sopenharmony_ci		if (ifa == ifp)
124162306a36Sopenharmony_ci			continue;
124262306a36Sopenharmony_ci		if (ifa->prefix_len != ifp->prefix_len ||
124362306a36Sopenharmony_ci		    !ipv6_prefix_equal(&ifa->addr, &ifp->addr,
124462306a36Sopenharmony_ci				       ifp->prefix_len))
124562306a36Sopenharmony_ci			continue;
124662306a36Sopenharmony_ci		if (ifa->flags & (IFA_F_PERMANENT | IFA_F_NOPREFIXROUTE))
124762306a36Sopenharmony_ci			return CLEANUP_PREFIX_RT_NOP;
124862306a36Sopenharmony_ci
124962306a36Sopenharmony_ci		action = CLEANUP_PREFIX_RT_EXPIRE;
125062306a36Sopenharmony_ci
125162306a36Sopenharmony_ci		spin_lock(&ifa->lock);
125262306a36Sopenharmony_ci
125362306a36Sopenharmony_ci		lifetime = addrconf_timeout_fixup(ifa->valid_lft, HZ);
125462306a36Sopenharmony_ci		/*
125562306a36Sopenharmony_ci		 * Note: Because this address is
125662306a36Sopenharmony_ci		 * not permanent, lifetime <
125762306a36Sopenharmony_ci		 * LONG_MAX / HZ here.
125862306a36Sopenharmony_ci		 */
125962306a36Sopenharmony_ci		if (time_before(*expires, ifa->tstamp + lifetime * HZ))
126062306a36Sopenharmony_ci			*expires = ifa->tstamp + lifetime * HZ;
126162306a36Sopenharmony_ci		spin_unlock(&ifa->lock);
126262306a36Sopenharmony_ci	}
126362306a36Sopenharmony_ci
126462306a36Sopenharmony_ci	return action;
126562306a36Sopenharmony_ci}
126662306a36Sopenharmony_ci
126762306a36Sopenharmony_cistatic void
126862306a36Sopenharmony_cicleanup_prefix_route(struct inet6_ifaddr *ifp, unsigned long expires,
126962306a36Sopenharmony_ci		     bool del_rt, bool del_peer)
127062306a36Sopenharmony_ci{
127162306a36Sopenharmony_ci	struct fib6_info *f6i;
127262306a36Sopenharmony_ci
127362306a36Sopenharmony_ci	f6i = addrconf_get_prefix_route(del_peer ? &ifp->peer_addr : &ifp->addr,
127462306a36Sopenharmony_ci					ifp->prefix_len,
127562306a36Sopenharmony_ci					ifp->idev->dev, 0, RTF_DEFAULT, true);
127662306a36Sopenharmony_ci	if (f6i) {
127762306a36Sopenharmony_ci		if (del_rt)
127862306a36Sopenharmony_ci			ip6_del_rt(dev_net(ifp->idev->dev), f6i, false);
127962306a36Sopenharmony_ci		else {
128062306a36Sopenharmony_ci			if (!(f6i->fib6_flags & RTF_EXPIRES))
128162306a36Sopenharmony_ci				fib6_set_expires(f6i, expires);
128262306a36Sopenharmony_ci			fib6_info_release(f6i);
128362306a36Sopenharmony_ci		}
128462306a36Sopenharmony_ci	}
128562306a36Sopenharmony_ci}
128662306a36Sopenharmony_ci
128762306a36Sopenharmony_ci
128862306a36Sopenharmony_ci/* This function wants to get referenced ifp and releases it before return */
128962306a36Sopenharmony_ci
129062306a36Sopenharmony_cistatic void ipv6_del_addr(struct inet6_ifaddr *ifp)
129162306a36Sopenharmony_ci{
129262306a36Sopenharmony_ci	enum cleanup_prefix_rt_t action = CLEANUP_PREFIX_RT_NOP;
129362306a36Sopenharmony_ci	struct net *net = dev_net(ifp->idev->dev);
129462306a36Sopenharmony_ci	unsigned long expires;
129562306a36Sopenharmony_ci	int state;
129662306a36Sopenharmony_ci
129762306a36Sopenharmony_ci	ASSERT_RTNL();
129862306a36Sopenharmony_ci
129962306a36Sopenharmony_ci	spin_lock_bh(&ifp->lock);
130062306a36Sopenharmony_ci	state = ifp->state;
130162306a36Sopenharmony_ci	ifp->state = INET6_IFADDR_STATE_DEAD;
130262306a36Sopenharmony_ci	spin_unlock_bh(&ifp->lock);
130362306a36Sopenharmony_ci
130462306a36Sopenharmony_ci	if (state == INET6_IFADDR_STATE_DEAD)
130562306a36Sopenharmony_ci		goto out;
130662306a36Sopenharmony_ci
130762306a36Sopenharmony_ci	spin_lock_bh(&net->ipv6.addrconf_hash_lock);
130862306a36Sopenharmony_ci	hlist_del_init_rcu(&ifp->addr_lst);
130962306a36Sopenharmony_ci	spin_unlock_bh(&net->ipv6.addrconf_hash_lock);
131062306a36Sopenharmony_ci
131162306a36Sopenharmony_ci	write_lock_bh(&ifp->idev->lock);
131262306a36Sopenharmony_ci
131362306a36Sopenharmony_ci	if (ifp->flags&IFA_F_TEMPORARY) {
131462306a36Sopenharmony_ci		list_del(&ifp->tmp_list);
131562306a36Sopenharmony_ci		if (ifp->ifpub) {
131662306a36Sopenharmony_ci			in6_ifa_put(ifp->ifpub);
131762306a36Sopenharmony_ci			ifp->ifpub = NULL;
131862306a36Sopenharmony_ci		}
131962306a36Sopenharmony_ci		__in6_ifa_put(ifp);
132062306a36Sopenharmony_ci	}
132162306a36Sopenharmony_ci
132262306a36Sopenharmony_ci	if (ifp->flags & IFA_F_PERMANENT && !(ifp->flags & IFA_F_NOPREFIXROUTE))
132362306a36Sopenharmony_ci		action = check_cleanup_prefix_route(ifp, &expires);
132462306a36Sopenharmony_ci
132562306a36Sopenharmony_ci	list_del_rcu(&ifp->if_list);
132662306a36Sopenharmony_ci	__in6_ifa_put(ifp);
132762306a36Sopenharmony_ci
132862306a36Sopenharmony_ci	write_unlock_bh(&ifp->idev->lock);
132962306a36Sopenharmony_ci
133062306a36Sopenharmony_ci	addrconf_del_dad_work(ifp);
133162306a36Sopenharmony_ci
133262306a36Sopenharmony_ci	ipv6_ifa_notify(RTM_DELADDR, ifp);
133362306a36Sopenharmony_ci
133462306a36Sopenharmony_ci	inet6addr_notifier_call_chain(NETDEV_DOWN, ifp);
133562306a36Sopenharmony_ci
133662306a36Sopenharmony_ci	if (action != CLEANUP_PREFIX_RT_NOP) {
133762306a36Sopenharmony_ci		cleanup_prefix_route(ifp, expires,
133862306a36Sopenharmony_ci			action == CLEANUP_PREFIX_RT_DEL, false);
133962306a36Sopenharmony_ci	}
134062306a36Sopenharmony_ci
134162306a36Sopenharmony_ci	/* clean up prefsrc entries */
134262306a36Sopenharmony_ci	rt6_remove_prefsrc(ifp);
134362306a36Sopenharmony_ciout:
134462306a36Sopenharmony_ci	in6_ifa_put(ifp);
134562306a36Sopenharmony_ci}
134662306a36Sopenharmony_ci
134762306a36Sopenharmony_cistatic int ipv6_create_tempaddr(struct inet6_ifaddr *ifp, bool block)
134862306a36Sopenharmony_ci{
134962306a36Sopenharmony_ci	struct inet6_dev *idev = ifp->idev;
135062306a36Sopenharmony_ci	unsigned long tmp_tstamp, age;
135162306a36Sopenharmony_ci	unsigned long regen_advance;
135262306a36Sopenharmony_ci	unsigned long now = jiffies;
135362306a36Sopenharmony_ci	s32 cnf_temp_preferred_lft;
135462306a36Sopenharmony_ci	struct inet6_ifaddr *ift;
135562306a36Sopenharmony_ci	struct ifa6_config cfg;
135662306a36Sopenharmony_ci	long max_desync_factor;
135762306a36Sopenharmony_ci	struct in6_addr addr;
135862306a36Sopenharmony_ci	int ret = 0;
135962306a36Sopenharmony_ci
136062306a36Sopenharmony_ci	write_lock_bh(&idev->lock);
136162306a36Sopenharmony_ci
136262306a36Sopenharmony_ciretry:
136362306a36Sopenharmony_ci	in6_dev_hold(idev);
136462306a36Sopenharmony_ci	if (idev->cnf.use_tempaddr <= 0) {
136562306a36Sopenharmony_ci		write_unlock_bh(&idev->lock);
136662306a36Sopenharmony_ci		pr_info("%s: use_tempaddr is disabled\n", __func__);
136762306a36Sopenharmony_ci		in6_dev_put(idev);
136862306a36Sopenharmony_ci		ret = -1;
136962306a36Sopenharmony_ci		goto out;
137062306a36Sopenharmony_ci	}
137162306a36Sopenharmony_ci	spin_lock_bh(&ifp->lock);
137262306a36Sopenharmony_ci	if (ifp->regen_count++ >= idev->cnf.regen_max_retry) {
137362306a36Sopenharmony_ci		idev->cnf.use_tempaddr = -1;	/*XXX*/
137462306a36Sopenharmony_ci		spin_unlock_bh(&ifp->lock);
137562306a36Sopenharmony_ci		write_unlock_bh(&idev->lock);
137662306a36Sopenharmony_ci		pr_warn("%s: regeneration time exceeded - disabled temporary address support\n",
137762306a36Sopenharmony_ci			__func__);
137862306a36Sopenharmony_ci		in6_dev_put(idev);
137962306a36Sopenharmony_ci		ret = -1;
138062306a36Sopenharmony_ci		goto out;
138162306a36Sopenharmony_ci	}
138262306a36Sopenharmony_ci	in6_ifa_hold(ifp);
138362306a36Sopenharmony_ci	memcpy(addr.s6_addr, ifp->addr.s6_addr, 8);
138462306a36Sopenharmony_ci	ipv6_gen_rnd_iid(&addr);
138562306a36Sopenharmony_ci
138662306a36Sopenharmony_ci	age = (now - ifp->tstamp) / HZ;
138762306a36Sopenharmony_ci
138862306a36Sopenharmony_ci	regen_advance = idev->cnf.regen_max_retry *
138962306a36Sopenharmony_ci			idev->cnf.dad_transmits *
139062306a36Sopenharmony_ci			max(NEIGH_VAR(idev->nd_parms, RETRANS_TIME), HZ/100) / HZ;
139162306a36Sopenharmony_ci
139262306a36Sopenharmony_ci	/* recalculate max_desync_factor each time and update
139362306a36Sopenharmony_ci	 * idev->desync_factor if it's larger
139462306a36Sopenharmony_ci	 */
139562306a36Sopenharmony_ci	cnf_temp_preferred_lft = READ_ONCE(idev->cnf.temp_prefered_lft);
139662306a36Sopenharmony_ci	max_desync_factor = min_t(long,
139762306a36Sopenharmony_ci				  idev->cnf.max_desync_factor,
139862306a36Sopenharmony_ci				  cnf_temp_preferred_lft - regen_advance);
139962306a36Sopenharmony_ci
140062306a36Sopenharmony_ci	if (unlikely(idev->desync_factor > max_desync_factor)) {
140162306a36Sopenharmony_ci		if (max_desync_factor > 0) {
140262306a36Sopenharmony_ci			get_random_bytes(&idev->desync_factor,
140362306a36Sopenharmony_ci					 sizeof(idev->desync_factor));
140462306a36Sopenharmony_ci			idev->desync_factor %= max_desync_factor;
140562306a36Sopenharmony_ci		} else {
140662306a36Sopenharmony_ci			idev->desync_factor = 0;
140762306a36Sopenharmony_ci		}
140862306a36Sopenharmony_ci	}
140962306a36Sopenharmony_ci
141062306a36Sopenharmony_ci	memset(&cfg, 0, sizeof(cfg));
141162306a36Sopenharmony_ci	cfg.valid_lft = min_t(__u32, ifp->valid_lft,
141262306a36Sopenharmony_ci			      idev->cnf.temp_valid_lft + age);
141362306a36Sopenharmony_ci	cfg.preferred_lft = cnf_temp_preferred_lft + age - idev->desync_factor;
141462306a36Sopenharmony_ci	cfg.preferred_lft = min_t(__u32, ifp->prefered_lft, cfg.preferred_lft);
141562306a36Sopenharmony_ci
141662306a36Sopenharmony_ci	cfg.plen = ifp->prefix_len;
141762306a36Sopenharmony_ci	tmp_tstamp = ifp->tstamp;
141862306a36Sopenharmony_ci	spin_unlock_bh(&ifp->lock);
141962306a36Sopenharmony_ci
142062306a36Sopenharmony_ci	write_unlock_bh(&idev->lock);
142162306a36Sopenharmony_ci
142262306a36Sopenharmony_ci	/* A temporary address is created only if this calculated Preferred
142362306a36Sopenharmony_ci	 * Lifetime is greater than REGEN_ADVANCE time units.  In particular,
142462306a36Sopenharmony_ci	 * an implementation must not create a temporary address with a zero
142562306a36Sopenharmony_ci	 * Preferred Lifetime.
142662306a36Sopenharmony_ci	 * Use age calculation as in addrconf_verify to avoid unnecessary
142762306a36Sopenharmony_ci	 * temporary addresses being generated.
142862306a36Sopenharmony_ci	 */
142962306a36Sopenharmony_ci	age = (now - tmp_tstamp + ADDRCONF_TIMER_FUZZ_MINUS) / HZ;
143062306a36Sopenharmony_ci	if (cfg.preferred_lft <= regen_advance + age) {
143162306a36Sopenharmony_ci		in6_ifa_put(ifp);
143262306a36Sopenharmony_ci		in6_dev_put(idev);
143362306a36Sopenharmony_ci		ret = -1;
143462306a36Sopenharmony_ci		goto out;
143562306a36Sopenharmony_ci	}
143662306a36Sopenharmony_ci
143762306a36Sopenharmony_ci	cfg.ifa_flags = IFA_F_TEMPORARY;
143862306a36Sopenharmony_ci	/* set in addrconf_prefix_rcv() */
143962306a36Sopenharmony_ci	if (ifp->flags & IFA_F_OPTIMISTIC)
144062306a36Sopenharmony_ci		cfg.ifa_flags |= IFA_F_OPTIMISTIC;
144162306a36Sopenharmony_ci
144262306a36Sopenharmony_ci	cfg.pfx = &addr;
144362306a36Sopenharmony_ci	cfg.scope = ipv6_addr_scope(cfg.pfx);
144462306a36Sopenharmony_ci
144562306a36Sopenharmony_ci	ift = ipv6_add_addr(idev, &cfg, block, NULL);
144662306a36Sopenharmony_ci	if (IS_ERR(ift)) {
144762306a36Sopenharmony_ci		in6_ifa_put(ifp);
144862306a36Sopenharmony_ci		in6_dev_put(idev);
144962306a36Sopenharmony_ci		pr_info("%s: retry temporary address regeneration\n", __func__);
145062306a36Sopenharmony_ci		write_lock_bh(&idev->lock);
145162306a36Sopenharmony_ci		goto retry;
145262306a36Sopenharmony_ci	}
145362306a36Sopenharmony_ci
145462306a36Sopenharmony_ci	spin_lock_bh(&ift->lock);
145562306a36Sopenharmony_ci	ift->ifpub = ifp;
145662306a36Sopenharmony_ci	ift->cstamp = now;
145762306a36Sopenharmony_ci	ift->tstamp = tmp_tstamp;
145862306a36Sopenharmony_ci	spin_unlock_bh(&ift->lock);
145962306a36Sopenharmony_ci
146062306a36Sopenharmony_ci	addrconf_dad_start(ift);
146162306a36Sopenharmony_ci	in6_ifa_put(ift);
146262306a36Sopenharmony_ci	in6_dev_put(idev);
146362306a36Sopenharmony_ciout:
146462306a36Sopenharmony_ci	return ret;
146562306a36Sopenharmony_ci}
146662306a36Sopenharmony_ci
146762306a36Sopenharmony_ci/*
146862306a36Sopenharmony_ci *	Choose an appropriate source address (RFC3484)
146962306a36Sopenharmony_ci */
147062306a36Sopenharmony_cienum {
147162306a36Sopenharmony_ci	IPV6_SADDR_RULE_INIT = 0,
147262306a36Sopenharmony_ci	IPV6_SADDR_RULE_LOCAL,
147362306a36Sopenharmony_ci	IPV6_SADDR_RULE_SCOPE,
147462306a36Sopenharmony_ci	IPV6_SADDR_RULE_PREFERRED,
147562306a36Sopenharmony_ci#ifdef CONFIG_IPV6_MIP6
147662306a36Sopenharmony_ci	IPV6_SADDR_RULE_HOA,
147762306a36Sopenharmony_ci#endif
147862306a36Sopenharmony_ci	IPV6_SADDR_RULE_OIF,
147962306a36Sopenharmony_ci	IPV6_SADDR_RULE_LABEL,
148062306a36Sopenharmony_ci	IPV6_SADDR_RULE_PRIVACY,
148162306a36Sopenharmony_ci	IPV6_SADDR_RULE_ORCHID,
148262306a36Sopenharmony_ci	IPV6_SADDR_RULE_PREFIX,
148362306a36Sopenharmony_ci#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
148462306a36Sopenharmony_ci	IPV6_SADDR_RULE_NOT_OPTIMISTIC,
148562306a36Sopenharmony_ci#endif
148662306a36Sopenharmony_ci	IPV6_SADDR_RULE_MAX
148762306a36Sopenharmony_ci};
148862306a36Sopenharmony_ci
148962306a36Sopenharmony_cistruct ipv6_saddr_score {
149062306a36Sopenharmony_ci	int			rule;
149162306a36Sopenharmony_ci	int			addr_type;
149262306a36Sopenharmony_ci	struct inet6_ifaddr	*ifa;
149362306a36Sopenharmony_ci	DECLARE_BITMAP(scorebits, IPV6_SADDR_RULE_MAX);
149462306a36Sopenharmony_ci	int			scopedist;
149562306a36Sopenharmony_ci	int			matchlen;
149662306a36Sopenharmony_ci};
149762306a36Sopenharmony_ci
149862306a36Sopenharmony_cistruct ipv6_saddr_dst {
149962306a36Sopenharmony_ci	const struct in6_addr *addr;
150062306a36Sopenharmony_ci	int ifindex;
150162306a36Sopenharmony_ci	int scope;
150262306a36Sopenharmony_ci	int label;
150362306a36Sopenharmony_ci	unsigned int prefs;
150462306a36Sopenharmony_ci};
150562306a36Sopenharmony_ci
150662306a36Sopenharmony_cistatic inline int ipv6_saddr_preferred(int type)
150762306a36Sopenharmony_ci{
150862306a36Sopenharmony_ci	if (type & (IPV6_ADDR_MAPPED|IPV6_ADDR_COMPATv4|IPV6_ADDR_LOOPBACK))
150962306a36Sopenharmony_ci		return 1;
151062306a36Sopenharmony_ci	return 0;
151162306a36Sopenharmony_ci}
151262306a36Sopenharmony_ci
151362306a36Sopenharmony_cistatic bool ipv6_use_optimistic_addr(struct net *net,
151462306a36Sopenharmony_ci				     struct inet6_dev *idev)
151562306a36Sopenharmony_ci{
151662306a36Sopenharmony_ci#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
151762306a36Sopenharmony_ci	if (!idev)
151862306a36Sopenharmony_ci		return false;
151962306a36Sopenharmony_ci	if (!net->ipv6.devconf_all->optimistic_dad && !idev->cnf.optimistic_dad)
152062306a36Sopenharmony_ci		return false;
152162306a36Sopenharmony_ci	if (!net->ipv6.devconf_all->use_optimistic && !idev->cnf.use_optimistic)
152262306a36Sopenharmony_ci		return false;
152362306a36Sopenharmony_ci
152462306a36Sopenharmony_ci	return true;
152562306a36Sopenharmony_ci#else
152662306a36Sopenharmony_ci	return false;
152762306a36Sopenharmony_ci#endif
152862306a36Sopenharmony_ci}
152962306a36Sopenharmony_ci
153062306a36Sopenharmony_cistatic bool ipv6_allow_optimistic_dad(struct net *net,
153162306a36Sopenharmony_ci				      struct inet6_dev *idev)
153262306a36Sopenharmony_ci{
153362306a36Sopenharmony_ci#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
153462306a36Sopenharmony_ci	if (!idev)
153562306a36Sopenharmony_ci		return false;
153662306a36Sopenharmony_ci	if (!net->ipv6.devconf_all->optimistic_dad && !idev->cnf.optimistic_dad)
153762306a36Sopenharmony_ci		return false;
153862306a36Sopenharmony_ci
153962306a36Sopenharmony_ci	return true;
154062306a36Sopenharmony_ci#else
154162306a36Sopenharmony_ci	return false;
154262306a36Sopenharmony_ci#endif
154362306a36Sopenharmony_ci}
154462306a36Sopenharmony_ci
154562306a36Sopenharmony_cistatic int ipv6_get_saddr_eval(struct net *net,
154662306a36Sopenharmony_ci			       struct ipv6_saddr_score *score,
154762306a36Sopenharmony_ci			       struct ipv6_saddr_dst *dst,
154862306a36Sopenharmony_ci			       int i)
154962306a36Sopenharmony_ci{
155062306a36Sopenharmony_ci	int ret;
155162306a36Sopenharmony_ci
155262306a36Sopenharmony_ci	if (i <= score->rule) {
155362306a36Sopenharmony_ci		switch (i) {
155462306a36Sopenharmony_ci		case IPV6_SADDR_RULE_SCOPE:
155562306a36Sopenharmony_ci			ret = score->scopedist;
155662306a36Sopenharmony_ci			break;
155762306a36Sopenharmony_ci		case IPV6_SADDR_RULE_PREFIX:
155862306a36Sopenharmony_ci			ret = score->matchlen;
155962306a36Sopenharmony_ci			break;
156062306a36Sopenharmony_ci		default:
156162306a36Sopenharmony_ci			ret = !!test_bit(i, score->scorebits);
156262306a36Sopenharmony_ci		}
156362306a36Sopenharmony_ci		goto out;
156462306a36Sopenharmony_ci	}
156562306a36Sopenharmony_ci
156662306a36Sopenharmony_ci	switch (i) {
156762306a36Sopenharmony_ci	case IPV6_SADDR_RULE_INIT:
156862306a36Sopenharmony_ci		/* Rule 0: remember if hiscore is not ready yet */
156962306a36Sopenharmony_ci		ret = !!score->ifa;
157062306a36Sopenharmony_ci		break;
157162306a36Sopenharmony_ci	case IPV6_SADDR_RULE_LOCAL:
157262306a36Sopenharmony_ci		/* Rule 1: Prefer same address */
157362306a36Sopenharmony_ci		ret = ipv6_addr_equal(&score->ifa->addr, dst->addr);
157462306a36Sopenharmony_ci		break;
157562306a36Sopenharmony_ci	case IPV6_SADDR_RULE_SCOPE:
157662306a36Sopenharmony_ci		/* Rule 2: Prefer appropriate scope
157762306a36Sopenharmony_ci		 *
157862306a36Sopenharmony_ci		 *      ret
157962306a36Sopenharmony_ci		 *       ^
158062306a36Sopenharmony_ci		 *    -1 |  d 15
158162306a36Sopenharmony_ci		 *    ---+--+-+---> scope
158262306a36Sopenharmony_ci		 *       |
158362306a36Sopenharmony_ci		 *       |             d is scope of the destination.
158462306a36Sopenharmony_ci		 *  B-d  |  \
158562306a36Sopenharmony_ci		 *       |   \      <- smaller scope is better if
158662306a36Sopenharmony_ci		 *  B-15 |    \        if scope is enough for destination.
158762306a36Sopenharmony_ci		 *       |             ret = B - scope (-1 <= scope >= d <= 15).
158862306a36Sopenharmony_ci		 * d-C-1 | /
158962306a36Sopenharmony_ci		 *       |/         <- greater is better
159062306a36Sopenharmony_ci		 *   -C  /             if scope is not enough for destination.
159162306a36Sopenharmony_ci		 *      /|             ret = scope - C (-1 <= d < scope <= 15).
159262306a36Sopenharmony_ci		 *
159362306a36Sopenharmony_ci		 * d - C - 1 < B -15 (for all -1 <= d <= 15).
159462306a36Sopenharmony_ci		 * C > d + 14 - B >= 15 + 14 - B = 29 - B.
159562306a36Sopenharmony_ci		 * Assume B = 0 and we get C > 29.
159662306a36Sopenharmony_ci		 */
159762306a36Sopenharmony_ci		ret = __ipv6_addr_src_scope(score->addr_type);
159862306a36Sopenharmony_ci		if (ret >= dst->scope)
159962306a36Sopenharmony_ci			ret = -ret;
160062306a36Sopenharmony_ci		else
160162306a36Sopenharmony_ci			ret -= 128;	/* 30 is enough */
160262306a36Sopenharmony_ci		score->scopedist = ret;
160362306a36Sopenharmony_ci		break;
160462306a36Sopenharmony_ci	case IPV6_SADDR_RULE_PREFERRED:
160562306a36Sopenharmony_ci	    {
160662306a36Sopenharmony_ci		/* Rule 3: Avoid deprecated and optimistic addresses */
160762306a36Sopenharmony_ci		u8 avoid = IFA_F_DEPRECATED;
160862306a36Sopenharmony_ci
160962306a36Sopenharmony_ci		if (!ipv6_use_optimistic_addr(net, score->ifa->idev))
161062306a36Sopenharmony_ci			avoid |= IFA_F_OPTIMISTIC;
161162306a36Sopenharmony_ci		ret = ipv6_saddr_preferred(score->addr_type) ||
161262306a36Sopenharmony_ci		      !(score->ifa->flags & avoid);
161362306a36Sopenharmony_ci		break;
161462306a36Sopenharmony_ci	    }
161562306a36Sopenharmony_ci#ifdef CONFIG_IPV6_MIP6
161662306a36Sopenharmony_ci	case IPV6_SADDR_RULE_HOA:
161762306a36Sopenharmony_ci	    {
161862306a36Sopenharmony_ci		/* Rule 4: Prefer home address */
161962306a36Sopenharmony_ci		int prefhome = !(dst->prefs & IPV6_PREFER_SRC_COA);
162062306a36Sopenharmony_ci		ret = !(score->ifa->flags & IFA_F_HOMEADDRESS) ^ prefhome;
162162306a36Sopenharmony_ci		break;
162262306a36Sopenharmony_ci	    }
162362306a36Sopenharmony_ci#endif
162462306a36Sopenharmony_ci	case IPV6_SADDR_RULE_OIF:
162562306a36Sopenharmony_ci		/* Rule 5: Prefer outgoing interface */
162662306a36Sopenharmony_ci		ret = (!dst->ifindex ||
162762306a36Sopenharmony_ci		       dst->ifindex == score->ifa->idev->dev->ifindex);
162862306a36Sopenharmony_ci		break;
162962306a36Sopenharmony_ci	case IPV6_SADDR_RULE_LABEL:
163062306a36Sopenharmony_ci		/* Rule 6: Prefer matching label */
163162306a36Sopenharmony_ci		ret = ipv6_addr_label(net,
163262306a36Sopenharmony_ci				      &score->ifa->addr, score->addr_type,
163362306a36Sopenharmony_ci				      score->ifa->idev->dev->ifindex) == dst->label;
163462306a36Sopenharmony_ci		break;
163562306a36Sopenharmony_ci	case IPV6_SADDR_RULE_PRIVACY:
163662306a36Sopenharmony_ci	    {
163762306a36Sopenharmony_ci		/* Rule 7: Prefer public address
163862306a36Sopenharmony_ci		 * Note: prefer temporary address if use_tempaddr >= 2
163962306a36Sopenharmony_ci		 */
164062306a36Sopenharmony_ci		int preftmp = dst->prefs & (IPV6_PREFER_SRC_PUBLIC|IPV6_PREFER_SRC_TMP) ?
164162306a36Sopenharmony_ci				!!(dst->prefs & IPV6_PREFER_SRC_TMP) :
164262306a36Sopenharmony_ci				score->ifa->idev->cnf.use_tempaddr >= 2;
164362306a36Sopenharmony_ci		ret = (!(score->ifa->flags & IFA_F_TEMPORARY)) ^ preftmp;
164462306a36Sopenharmony_ci		break;
164562306a36Sopenharmony_ci	    }
164662306a36Sopenharmony_ci	case IPV6_SADDR_RULE_ORCHID:
164762306a36Sopenharmony_ci		/* Rule 8-: Prefer ORCHID vs ORCHID or
164862306a36Sopenharmony_ci		 *	    non-ORCHID vs non-ORCHID
164962306a36Sopenharmony_ci		 */
165062306a36Sopenharmony_ci		ret = !(ipv6_addr_orchid(&score->ifa->addr) ^
165162306a36Sopenharmony_ci			ipv6_addr_orchid(dst->addr));
165262306a36Sopenharmony_ci		break;
165362306a36Sopenharmony_ci	case IPV6_SADDR_RULE_PREFIX:
165462306a36Sopenharmony_ci		/* Rule 8: Use longest matching prefix */
165562306a36Sopenharmony_ci		ret = ipv6_addr_diff(&score->ifa->addr, dst->addr);
165662306a36Sopenharmony_ci		if (ret > score->ifa->prefix_len)
165762306a36Sopenharmony_ci			ret = score->ifa->prefix_len;
165862306a36Sopenharmony_ci		score->matchlen = ret;
165962306a36Sopenharmony_ci		break;
166062306a36Sopenharmony_ci#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
166162306a36Sopenharmony_ci	case IPV6_SADDR_RULE_NOT_OPTIMISTIC:
166262306a36Sopenharmony_ci		/* Optimistic addresses still have lower precedence than other
166362306a36Sopenharmony_ci		 * preferred addresses.
166462306a36Sopenharmony_ci		 */
166562306a36Sopenharmony_ci		ret = !(score->ifa->flags & IFA_F_OPTIMISTIC);
166662306a36Sopenharmony_ci		break;
166762306a36Sopenharmony_ci#endif
166862306a36Sopenharmony_ci	default:
166962306a36Sopenharmony_ci		ret = 0;
167062306a36Sopenharmony_ci	}
167162306a36Sopenharmony_ci
167262306a36Sopenharmony_ci	if (ret)
167362306a36Sopenharmony_ci		__set_bit(i, score->scorebits);
167462306a36Sopenharmony_ci	score->rule = i;
167562306a36Sopenharmony_ciout:
167662306a36Sopenharmony_ci	return ret;
167762306a36Sopenharmony_ci}
167862306a36Sopenharmony_ci
167962306a36Sopenharmony_cistatic int __ipv6_dev_get_saddr(struct net *net,
168062306a36Sopenharmony_ci				struct ipv6_saddr_dst *dst,
168162306a36Sopenharmony_ci				struct inet6_dev *idev,
168262306a36Sopenharmony_ci				struct ipv6_saddr_score *scores,
168362306a36Sopenharmony_ci				int hiscore_idx)
168462306a36Sopenharmony_ci{
168562306a36Sopenharmony_ci	struct ipv6_saddr_score *score = &scores[1 - hiscore_idx], *hiscore = &scores[hiscore_idx];
168662306a36Sopenharmony_ci
168762306a36Sopenharmony_ci	list_for_each_entry_rcu(score->ifa, &idev->addr_list, if_list) {
168862306a36Sopenharmony_ci		int i;
168962306a36Sopenharmony_ci
169062306a36Sopenharmony_ci		/*
169162306a36Sopenharmony_ci		 * - Tentative Address (RFC2462 section 5.4)
169262306a36Sopenharmony_ci		 *  - A tentative address is not considered
169362306a36Sopenharmony_ci		 *    "assigned to an interface" in the traditional
169462306a36Sopenharmony_ci		 *    sense, unless it is also flagged as optimistic.
169562306a36Sopenharmony_ci		 * - Candidate Source Address (section 4)
169662306a36Sopenharmony_ci		 *  - In any case, anycast addresses, multicast
169762306a36Sopenharmony_ci		 *    addresses, and the unspecified address MUST
169862306a36Sopenharmony_ci		 *    NOT be included in a candidate set.
169962306a36Sopenharmony_ci		 */
170062306a36Sopenharmony_ci		if ((score->ifa->flags & IFA_F_TENTATIVE) &&
170162306a36Sopenharmony_ci		    (!(score->ifa->flags & IFA_F_OPTIMISTIC)))
170262306a36Sopenharmony_ci			continue;
170362306a36Sopenharmony_ci
170462306a36Sopenharmony_ci		score->addr_type = __ipv6_addr_type(&score->ifa->addr);
170562306a36Sopenharmony_ci
170662306a36Sopenharmony_ci		if (unlikely(score->addr_type == IPV6_ADDR_ANY ||
170762306a36Sopenharmony_ci			     score->addr_type & IPV6_ADDR_MULTICAST)) {
170862306a36Sopenharmony_ci			net_dbg_ratelimited("ADDRCONF: unspecified / multicast address assigned as unicast address on %s",
170962306a36Sopenharmony_ci					    idev->dev->name);
171062306a36Sopenharmony_ci			continue;
171162306a36Sopenharmony_ci		}
171262306a36Sopenharmony_ci
171362306a36Sopenharmony_ci		score->rule = -1;
171462306a36Sopenharmony_ci		bitmap_zero(score->scorebits, IPV6_SADDR_RULE_MAX);
171562306a36Sopenharmony_ci
171662306a36Sopenharmony_ci		for (i = 0; i < IPV6_SADDR_RULE_MAX; i++) {
171762306a36Sopenharmony_ci			int minihiscore, miniscore;
171862306a36Sopenharmony_ci
171962306a36Sopenharmony_ci			minihiscore = ipv6_get_saddr_eval(net, hiscore, dst, i);
172062306a36Sopenharmony_ci			miniscore = ipv6_get_saddr_eval(net, score, dst, i);
172162306a36Sopenharmony_ci
172262306a36Sopenharmony_ci			if (minihiscore > miniscore) {
172362306a36Sopenharmony_ci				if (i == IPV6_SADDR_RULE_SCOPE &&
172462306a36Sopenharmony_ci				    score->scopedist > 0) {
172562306a36Sopenharmony_ci					/*
172662306a36Sopenharmony_ci					 * special case:
172762306a36Sopenharmony_ci					 * each remaining entry
172862306a36Sopenharmony_ci					 * has too small (not enough)
172962306a36Sopenharmony_ci					 * scope, because ifa entries
173062306a36Sopenharmony_ci					 * are sorted by their scope
173162306a36Sopenharmony_ci					 * values.
173262306a36Sopenharmony_ci					 */
173362306a36Sopenharmony_ci					goto out;
173462306a36Sopenharmony_ci				}
173562306a36Sopenharmony_ci				break;
173662306a36Sopenharmony_ci			} else if (minihiscore < miniscore) {
173762306a36Sopenharmony_ci				swap(hiscore, score);
173862306a36Sopenharmony_ci				hiscore_idx = 1 - hiscore_idx;
173962306a36Sopenharmony_ci
174062306a36Sopenharmony_ci				/* restore our iterator */
174162306a36Sopenharmony_ci				score->ifa = hiscore->ifa;
174262306a36Sopenharmony_ci
174362306a36Sopenharmony_ci				break;
174462306a36Sopenharmony_ci			}
174562306a36Sopenharmony_ci		}
174662306a36Sopenharmony_ci	}
174762306a36Sopenharmony_ciout:
174862306a36Sopenharmony_ci	return hiscore_idx;
174962306a36Sopenharmony_ci}
175062306a36Sopenharmony_ci
175162306a36Sopenharmony_cistatic int ipv6_get_saddr_master(struct net *net,
175262306a36Sopenharmony_ci				 const struct net_device *dst_dev,
175362306a36Sopenharmony_ci				 const struct net_device *master,
175462306a36Sopenharmony_ci				 struct ipv6_saddr_dst *dst,
175562306a36Sopenharmony_ci				 struct ipv6_saddr_score *scores,
175662306a36Sopenharmony_ci				 int hiscore_idx)
175762306a36Sopenharmony_ci{
175862306a36Sopenharmony_ci	struct inet6_dev *idev;
175962306a36Sopenharmony_ci
176062306a36Sopenharmony_ci	idev = __in6_dev_get(dst_dev);
176162306a36Sopenharmony_ci	if (idev)
176262306a36Sopenharmony_ci		hiscore_idx = __ipv6_dev_get_saddr(net, dst, idev,
176362306a36Sopenharmony_ci						   scores, hiscore_idx);
176462306a36Sopenharmony_ci
176562306a36Sopenharmony_ci	idev = __in6_dev_get(master);
176662306a36Sopenharmony_ci	if (idev)
176762306a36Sopenharmony_ci		hiscore_idx = __ipv6_dev_get_saddr(net, dst, idev,
176862306a36Sopenharmony_ci						   scores, hiscore_idx);
176962306a36Sopenharmony_ci
177062306a36Sopenharmony_ci	return hiscore_idx;
177162306a36Sopenharmony_ci}
177262306a36Sopenharmony_ci
177362306a36Sopenharmony_ciint ipv6_dev_get_saddr(struct net *net, const struct net_device *dst_dev,
177462306a36Sopenharmony_ci		       const struct in6_addr *daddr, unsigned int prefs,
177562306a36Sopenharmony_ci		       struct in6_addr *saddr)
177662306a36Sopenharmony_ci{
177762306a36Sopenharmony_ci	struct ipv6_saddr_score scores[2], *hiscore;
177862306a36Sopenharmony_ci	struct ipv6_saddr_dst dst;
177962306a36Sopenharmony_ci	struct inet6_dev *idev;
178062306a36Sopenharmony_ci	struct net_device *dev;
178162306a36Sopenharmony_ci	int dst_type;
178262306a36Sopenharmony_ci	bool use_oif_addr = false;
178362306a36Sopenharmony_ci	int hiscore_idx = 0;
178462306a36Sopenharmony_ci	int ret = 0;
178562306a36Sopenharmony_ci
178662306a36Sopenharmony_ci	dst_type = __ipv6_addr_type(daddr);
178762306a36Sopenharmony_ci	dst.addr = daddr;
178862306a36Sopenharmony_ci	dst.ifindex = dst_dev ? dst_dev->ifindex : 0;
178962306a36Sopenharmony_ci	dst.scope = __ipv6_addr_src_scope(dst_type);
179062306a36Sopenharmony_ci	dst.label = ipv6_addr_label(net, daddr, dst_type, dst.ifindex);
179162306a36Sopenharmony_ci	dst.prefs = prefs;
179262306a36Sopenharmony_ci
179362306a36Sopenharmony_ci	scores[hiscore_idx].rule = -1;
179462306a36Sopenharmony_ci	scores[hiscore_idx].ifa = NULL;
179562306a36Sopenharmony_ci
179662306a36Sopenharmony_ci	rcu_read_lock();
179762306a36Sopenharmony_ci
179862306a36Sopenharmony_ci	/* Candidate Source Address (section 4)
179962306a36Sopenharmony_ci	 *  - multicast and link-local destination address,
180062306a36Sopenharmony_ci	 *    the set of candidate source address MUST only
180162306a36Sopenharmony_ci	 *    include addresses assigned to interfaces
180262306a36Sopenharmony_ci	 *    belonging to the same link as the outgoing
180362306a36Sopenharmony_ci	 *    interface.
180462306a36Sopenharmony_ci	 * (- For site-local destination addresses, the
180562306a36Sopenharmony_ci	 *    set of candidate source addresses MUST only
180662306a36Sopenharmony_ci	 *    include addresses assigned to interfaces
180762306a36Sopenharmony_ci	 *    belonging to the same site as the outgoing
180862306a36Sopenharmony_ci	 *    interface.)
180962306a36Sopenharmony_ci	 *  - "It is RECOMMENDED that the candidate source addresses
181062306a36Sopenharmony_ci	 *    be the set of unicast addresses assigned to the
181162306a36Sopenharmony_ci	 *    interface that will be used to send to the destination
181262306a36Sopenharmony_ci	 *    (the 'outgoing' interface)." (RFC 6724)
181362306a36Sopenharmony_ci	 */
181462306a36Sopenharmony_ci	if (dst_dev) {
181562306a36Sopenharmony_ci		idev = __in6_dev_get(dst_dev);
181662306a36Sopenharmony_ci		if ((dst_type & IPV6_ADDR_MULTICAST) ||
181762306a36Sopenharmony_ci		    dst.scope <= IPV6_ADDR_SCOPE_LINKLOCAL ||
181862306a36Sopenharmony_ci		    (idev && idev->cnf.use_oif_addrs_only)) {
181962306a36Sopenharmony_ci			use_oif_addr = true;
182062306a36Sopenharmony_ci		}
182162306a36Sopenharmony_ci	}
182262306a36Sopenharmony_ci
182362306a36Sopenharmony_ci	if (use_oif_addr) {
182462306a36Sopenharmony_ci		if (idev)
182562306a36Sopenharmony_ci			hiscore_idx = __ipv6_dev_get_saddr(net, &dst, idev, scores, hiscore_idx);
182662306a36Sopenharmony_ci	} else {
182762306a36Sopenharmony_ci		const struct net_device *master;
182862306a36Sopenharmony_ci		int master_idx = 0;
182962306a36Sopenharmony_ci
183062306a36Sopenharmony_ci		/* if dst_dev exists and is enslaved to an L3 device, then
183162306a36Sopenharmony_ci		 * prefer addresses from dst_dev and then the master over
183262306a36Sopenharmony_ci		 * any other enslaved devices in the L3 domain.
183362306a36Sopenharmony_ci		 */
183462306a36Sopenharmony_ci		master = l3mdev_master_dev_rcu(dst_dev);
183562306a36Sopenharmony_ci		if (master) {
183662306a36Sopenharmony_ci			master_idx = master->ifindex;
183762306a36Sopenharmony_ci
183862306a36Sopenharmony_ci			hiscore_idx = ipv6_get_saddr_master(net, dst_dev,
183962306a36Sopenharmony_ci							    master, &dst,
184062306a36Sopenharmony_ci							    scores, hiscore_idx);
184162306a36Sopenharmony_ci
184262306a36Sopenharmony_ci			if (scores[hiscore_idx].ifa)
184362306a36Sopenharmony_ci				goto out;
184462306a36Sopenharmony_ci		}
184562306a36Sopenharmony_ci
184662306a36Sopenharmony_ci		for_each_netdev_rcu(net, dev) {
184762306a36Sopenharmony_ci			/* only consider addresses on devices in the
184862306a36Sopenharmony_ci			 * same L3 domain
184962306a36Sopenharmony_ci			 */
185062306a36Sopenharmony_ci			if (l3mdev_master_ifindex_rcu(dev) != master_idx)
185162306a36Sopenharmony_ci				continue;
185262306a36Sopenharmony_ci			idev = __in6_dev_get(dev);
185362306a36Sopenharmony_ci			if (!idev)
185462306a36Sopenharmony_ci				continue;
185562306a36Sopenharmony_ci			hiscore_idx = __ipv6_dev_get_saddr(net, &dst, idev, scores, hiscore_idx);
185662306a36Sopenharmony_ci		}
185762306a36Sopenharmony_ci	}
185862306a36Sopenharmony_ci
185962306a36Sopenharmony_ciout:
186062306a36Sopenharmony_ci	hiscore = &scores[hiscore_idx];
186162306a36Sopenharmony_ci	if (!hiscore->ifa)
186262306a36Sopenharmony_ci		ret = -EADDRNOTAVAIL;
186362306a36Sopenharmony_ci	else
186462306a36Sopenharmony_ci		*saddr = hiscore->ifa->addr;
186562306a36Sopenharmony_ci
186662306a36Sopenharmony_ci	rcu_read_unlock();
186762306a36Sopenharmony_ci	return ret;
186862306a36Sopenharmony_ci}
186962306a36Sopenharmony_ciEXPORT_SYMBOL(ipv6_dev_get_saddr);
187062306a36Sopenharmony_ci
187162306a36Sopenharmony_cistatic int __ipv6_get_lladdr(struct inet6_dev *idev, struct in6_addr *addr,
187262306a36Sopenharmony_ci			      u32 banned_flags)
187362306a36Sopenharmony_ci{
187462306a36Sopenharmony_ci	struct inet6_ifaddr *ifp;
187562306a36Sopenharmony_ci	int err = -EADDRNOTAVAIL;
187662306a36Sopenharmony_ci
187762306a36Sopenharmony_ci	list_for_each_entry_reverse(ifp, &idev->addr_list, if_list) {
187862306a36Sopenharmony_ci		if (ifp->scope > IFA_LINK)
187962306a36Sopenharmony_ci			break;
188062306a36Sopenharmony_ci		if (ifp->scope == IFA_LINK &&
188162306a36Sopenharmony_ci		    !(ifp->flags & banned_flags)) {
188262306a36Sopenharmony_ci			*addr = ifp->addr;
188362306a36Sopenharmony_ci			err = 0;
188462306a36Sopenharmony_ci			break;
188562306a36Sopenharmony_ci		}
188662306a36Sopenharmony_ci	}
188762306a36Sopenharmony_ci	return err;
188862306a36Sopenharmony_ci}
188962306a36Sopenharmony_ci
189062306a36Sopenharmony_ciint ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr,
189162306a36Sopenharmony_ci		    u32 banned_flags)
189262306a36Sopenharmony_ci{
189362306a36Sopenharmony_ci	struct inet6_dev *idev;
189462306a36Sopenharmony_ci	int err = -EADDRNOTAVAIL;
189562306a36Sopenharmony_ci
189662306a36Sopenharmony_ci	rcu_read_lock();
189762306a36Sopenharmony_ci	idev = __in6_dev_get(dev);
189862306a36Sopenharmony_ci	if (idev) {
189962306a36Sopenharmony_ci		read_lock_bh(&idev->lock);
190062306a36Sopenharmony_ci		err = __ipv6_get_lladdr(idev, addr, banned_flags);
190162306a36Sopenharmony_ci		read_unlock_bh(&idev->lock);
190262306a36Sopenharmony_ci	}
190362306a36Sopenharmony_ci	rcu_read_unlock();
190462306a36Sopenharmony_ci	return err;
190562306a36Sopenharmony_ci}
190662306a36Sopenharmony_ci
190762306a36Sopenharmony_cistatic int ipv6_count_addresses(const struct inet6_dev *idev)
190862306a36Sopenharmony_ci{
190962306a36Sopenharmony_ci	const struct inet6_ifaddr *ifp;
191062306a36Sopenharmony_ci	int cnt = 0;
191162306a36Sopenharmony_ci
191262306a36Sopenharmony_ci	rcu_read_lock();
191362306a36Sopenharmony_ci	list_for_each_entry_rcu(ifp, &idev->addr_list, if_list)
191462306a36Sopenharmony_ci		cnt++;
191562306a36Sopenharmony_ci	rcu_read_unlock();
191662306a36Sopenharmony_ci	return cnt;
191762306a36Sopenharmony_ci}
191862306a36Sopenharmony_ci
191962306a36Sopenharmony_ciint ipv6_chk_addr(struct net *net, const struct in6_addr *addr,
192062306a36Sopenharmony_ci		  const struct net_device *dev, int strict)
192162306a36Sopenharmony_ci{
192262306a36Sopenharmony_ci	return ipv6_chk_addr_and_flags(net, addr, dev, !dev,
192362306a36Sopenharmony_ci				       strict, IFA_F_TENTATIVE);
192462306a36Sopenharmony_ci}
192562306a36Sopenharmony_ciEXPORT_SYMBOL(ipv6_chk_addr);
192662306a36Sopenharmony_ci
192762306a36Sopenharmony_ci/* device argument is used to find the L3 domain of interest. If
192862306a36Sopenharmony_ci * skip_dev_check is set, then the ifp device is not checked against
192962306a36Sopenharmony_ci * the passed in dev argument. So the 2 cases for addresses checks are:
193062306a36Sopenharmony_ci *   1. does the address exist in the L3 domain that dev is part of
193162306a36Sopenharmony_ci *      (skip_dev_check = true), or
193262306a36Sopenharmony_ci *
193362306a36Sopenharmony_ci *   2. does the address exist on the specific device
193462306a36Sopenharmony_ci *      (skip_dev_check = false)
193562306a36Sopenharmony_ci */
193662306a36Sopenharmony_cistatic struct net_device *
193762306a36Sopenharmony_ci__ipv6_chk_addr_and_flags(struct net *net, const struct in6_addr *addr,
193862306a36Sopenharmony_ci			  const struct net_device *dev, bool skip_dev_check,
193962306a36Sopenharmony_ci			  int strict, u32 banned_flags)
194062306a36Sopenharmony_ci{
194162306a36Sopenharmony_ci	unsigned int hash = inet6_addr_hash(net, addr);
194262306a36Sopenharmony_ci	struct net_device *l3mdev, *ndev;
194362306a36Sopenharmony_ci	struct inet6_ifaddr *ifp;
194462306a36Sopenharmony_ci	u32 ifp_flags;
194562306a36Sopenharmony_ci
194662306a36Sopenharmony_ci	rcu_read_lock();
194762306a36Sopenharmony_ci
194862306a36Sopenharmony_ci	l3mdev = l3mdev_master_dev_rcu(dev);
194962306a36Sopenharmony_ci	if (skip_dev_check)
195062306a36Sopenharmony_ci		dev = NULL;
195162306a36Sopenharmony_ci
195262306a36Sopenharmony_ci	hlist_for_each_entry_rcu(ifp, &net->ipv6.inet6_addr_lst[hash], addr_lst) {
195362306a36Sopenharmony_ci		ndev = ifp->idev->dev;
195462306a36Sopenharmony_ci
195562306a36Sopenharmony_ci		if (l3mdev_master_dev_rcu(ndev) != l3mdev)
195662306a36Sopenharmony_ci			continue;
195762306a36Sopenharmony_ci
195862306a36Sopenharmony_ci		/* Decouple optimistic from tentative for evaluation here.
195962306a36Sopenharmony_ci		 * Ban optimistic addresses explicitly, when required.
196062306a36Sopenharmony_ci		 */
196162306a36Sopenharmony_ci		ifp_flags = (ifp->flags&IFA_F_OPTIMISTIC)
196262306a36Sopenharmony_ci			    ? (ifp->flags&~IFA_F_TENTATIVE)
196362306a36Sopenharmony_ci			    : ifp->flags;
196462306a36Sopenharmony_ci		if (ipv6_addr_equal(&ifp->addr, addr) &&
196562306a36Sopenharmony_ci		    !(ifp_flags&banned_flags) &&
196662306a36Sopenharmony_ci		    (!dev || ndev == dev ||
196762306a36Sopenharmony_ci		     !(ifp->scope&(IFA_LINK|IFA_HOST) || strict))) {
196862306a36Sopenharmony_ci			rcu_read_unlock();
196962306a36Sopenharmony_ci			return ndev;
197062306a36Sopenharmony_ci		}
197162306a36Sopenharmony_ci	}
197262306a36Sopenharmony_ci
197362306a36Sopenharmony_ci	rcu_read_unlock();
197462306a36Sopenharmony_ci	return NULL;
197562306a36Sopenharmony_ci}
197662306a36Sopenharmony_ci
197762306a36Sopenharmony_ciint ipv6_chk_addr_and_flags(struct net *net, const struct in6_addr *addr,
197862306a36Sopenharmony_ci			    const struct net_device *dev, bool skip_dev_check,
197962306a36Sopenharmony_ci			    int strict, u32 banned_flags)
198062306a36Sopenharmony_ci{
198162306a36Sopenharmony_ci	return __ipv6_chk_addr_and_flags(net, addr, dev, skip_dev_check,
198262306a36Sopenharmony_ci					 strict, banned_flags) ? 1 : 0;
198362306a36Sopenharmony_ci}
198462306a36Sopenharmony_ciEXPORT_SYMBOL(ipv6_chk_addr_and_flags);
198562306a36Sopenharmony_ci
198662306a36Sopenharmony_ci
198762306a36Sopenharmony_ci/* Compares an address/prefix_len with addresses on device @dev.
198862306a36Sopenharmony_ci * If one is found it returns true.
198962306a36Sopenharmony_ci */
199062306a36Sopenharmony_cibool ipv6_chk_custom_prefix(const struct in6_addr *addr,
199162306a36Sopenharmony_ci	const unsigned int prefix_len, struct net_device *dev)
199262306a36Sopenharmony_ci{
199362306a36Sopenharmony_ci	const struct inet6_ifaddr *ifa;
199462306a36Sopenharmony_ci	const struct inet6_dev *idev;
199562306a36Sopenharmony_ci	bool ret = false;
199662306a36Sopenharmony_ci
199762306a36Sopenharmony_ci	rcu_read_lock();
199862306a36Sopenharmony_ci	idev = __in6_dev_get(dev);
199962306a36Sopenharmony_ci	if (idev) {
200062306a36Sopenharmony_ci		list_for_each_entry_rcu(ifa, &idev->addr_list, if_list) {
200162306a36Sopenharmony_ci			ret = ipv6_prefix_equal(addr, &ifa->addr, prefix_len);
200262306a36Sopenharmony_ci			if (ret)
200362306a36Sopenharmony_ci				break;
200462306a36Sopenharmony_ci		}
200562306a36Sopenharmony_ci	}
200662306a36Sopenharmony_ci	rcu_read_unlock();
200762306a36Sopenharmony_ci
200862306a36Sopenharmony_ci	return ret;
200962306a36Sopenharmony_ci}
201062306a36Sopenharmony_ciEXPORT_SYMBOL(ipv6_chk_custom_prefix);
201162306a36Sopenharmony_ci
201262306a36Sopenharmony_ciint ipv6_chk_prefix(const struct in6_addr *addr, struct net_device *dev)
201362306a36Sopenharmony_ci{
201462306a36Sopenharmony_ci	const struct inet6_ifaddr *ifa;
201562306a36Sopenharmony_ci	const struct inet6_dev *idev;
201662306a36Sopenharmony_ci	int	onlink;
201762306a36Sopenharmony_ci
201862306a36Sopenharmony_ci	onlink = 0;
201962306a36Sopenharmony_ci	rcu_read_lock();
202062306a36Sopenharmony_ci	idev = __in6_dev_get(dev);
202162306a36Sopenharmony_ci	if (idev) {
202262306a36Sopenharmony_ci		list_for_each_entry_rcu(ifa, &idev->addr_list, if_list) {
202362306a36Sopenharmony_ci			onlink = ipv6_prefix_equal(addr, &ifa->addr,
202462306a36Sopenharmony_ci						   ifa->prefix_len);
202562306a36Sopenharmony_ci			if (onlink)
202662306a36Sopenharmony_ci				break;
202762306a36Sopenharmony_ci		}
202862306a36Sopenharmony_ci	}
202962306a36Sopenharmony_ci	rcu_read_unlock();
203062306a36Sopenharmony_ci	return onlink;
203162306a36Sopenharmony_ci}
203262306a36Sopenharmony_ciEXPORT_SYMBOL(ipv6_chk_prefix);
203362306a36Sopenharmony_ci
203462306a36Sopenharmony_ci/**
203562306a36Sopenharmony_ci * ipv6_dev_find - find the first device with a given source address.
203662306a36Sopenharmony_ci * @net: the net namespace
203762306a36Sopenharmony_ci * @addr: the source address
203862306a36Sopenharmony_ci * @dev: used to find the L3 domain of interest
203962306a36Sopenharmony_ci *
204062306a36Sopenharmony_ci * The caller should be protected by RCU, or RTNL.
204162306a36Sopenharmony_ci */
204262306a36Sopenharmony_cistruct net_device *ipv6_dev_find(struct net *net, const struct in6_addr *addr,
204362306a36Sopenharmony_ci				 struct net_device *dev)
204462306a36Sopenharmony_ci{
204562306a36Sopenharmony_ci	return __ipv6_chk_addr_and_flags(net, addr, dev, !dev, 1,
204662306a36Sopenharmony_ci					 IFA_F_TENTATIVE);
204762306a36Sopenharmony_ci}
204862306a36Sopenharmony_ciEXPORT_SYMBOL(ipv6_dev_find);
204962306a36Sopenharmony_ci
205062306a36Sopenharmony_cistruct inet6_ifaddr *ipv6_get_ifaddr(struct net *net, const struct in6_addr *addr,
205162306a36Sopenharmony_ci				     struct net_device *dev, int strict)
205262306a36Sopenharmony_ci{
205362306a36Sopenharmony_ci	unsigned int hash = inet6_addr_hash(net, addr);
205462306a36Sopenharmony_ci	struct inet6_ifaddr *ifp, *result = NULL;
205562306a36Sopenharmony_ci
205662306a36Sopenharmony_ci	rcu_read_lock();
205762306a36Sopenharmony_ci	hlist_for_each_entry_rcu(ifp, &net->ipv6.inet6_addr_lst[hash], addr_lst) {
205862306a36Sopenharmony_ci		if (ipv6_addr_equal(&ifp->addr, addr)) {
205962306a36Sopenharmony_ci			if (!dev || ifp->idev->dev == dev ||
206062306a36Sopenharmony_ci			    !(ifp->scope&(IFA_LINK|IFA_HOST) || strict)) {
206162306a36Sopenharmony_ci				result = ifp;
206262306a36Sopenharmony_ci				in6_ifa_hold(ifp);
206362306a36Sopenharmony_ci				break;
206462306a36Sopenharmony_ci			}
206562306a36Sopenharmony_ci		}
206662306a36Sopenharmony_ci	}
206762306a36Sopenharmony_ci	rcu_read_unlock();
206862306a36Sopenharmony_ci
206962306a36Sopenharmony_ci	return result;
207062306a36Sopenharmony_ci}
207162306a36Sopenharmony_ci
207262306a36Sopenharmony_ci/* Gets referenced address, destroys ifaddr */
207362306a36Sopenharmony_ci
207462306a36Sopenharmony_cistatic void addrconf_dad_stop(struct inet6_ifaddr *ifp, int dad_failed)
207562306a36Sopenharmony_ci{
207662306a36Sopenharmony_ci	if (dad_failed)
207762306a36Sopenharmony_ci		ifp->flags |= IFA_F_DADFAILED;
207862306a36Sopenharmony_ci
207962306a36Sopenharmony_ci	if (ifp->flags&IFA_F_TEMPORARY) {
208062306a36Sopenharmony_ci		struct inet6_ifaddr *ifpub;
208162306a36Sopenharmony_ci		spin_lock_bh(&ifp->lock);
208262306a36Sopenharmony_ci		ifpub = ifp->ifpub;
208362306a36Sopenharmony_ci		if (ifpub) {
208462306a36Sopenharmony_ci			in6_ifa_hold(ifpub);
208562306a36Sopenharmony_ci			spin_unlock_bh(&ifp->lock);
208662306a36Sopenharmony_ci			ipv6_create_tempaddr(ifpub, true);
208762306a36Sopenharmony_ci			in6_ifa_put(ifpub);
208862306a36Sopenharmony_ci		} else {
208962306a36Sopenharmony_ci			spin_unlock_bh(&ifp->lock);
209062306a36Sopenharmony_ci		}
209162306a36Sopenharmony_ci		ipv6_del_addr(ifp);
209262306a36Sopenharmony_ci	} else if (ifp->flags&IFA_F_PERMANENT || !dad_failed) {
209362306a36Sopenharmony_ci		spin_lock_bh(&ifp->lock);
209462306a36Sopenharmony_ci		addrconf_del_dad_work(ifp);
209562306a36Sopenharmony_ci		ifp->flags |= IFA_F_TENTATIVE;
209662306a36Sopenharmony_ci		if (dad_failed)
209762306a36Sopenharmony_ci			ifp->flags &= ~IFA_F_OPTIMISTIC;
209862306a36Sopenharmony_ci		spin_unlock_bh(&ifp->lock);
209962306a36Sopenharmony_ci		if (dad_failed)
210062306a36Sopenharmony_ci			ipv6_ifa_notify(0, ifp);
210162306a36Sopenharmony_ci		in6_ifa_put(ifp);
210262306a36Sopenharmony_ci	} else {
210362306a36Sopenharmony_ci		ipv6_del_addr(ifp);
210462306a36Sopenharmony_ci	}
210562306a36Sopenharmony_ci}
210662306a36Sopenharmony_ci
210762306a36Sopenharmony_cistatic int addrconf_dad_end(struct inet6_ifaddr *ifp)
210862306a36Sopenharmony_ci{
210962306a36Sopenharmony_ci	int err = -ENOENT;
211062306a36Sopenharmony_ci
211162306a36Sopenharmony_ci	spin_lock_bh(&ifp->lock);
211262306a36Sopenharmony_ci	if (ifp->state == INET6_IFADDR_STATE_DAD) {
211362306a36Sopenharmony_ci		ifp->state = INET6_IFADDR_STATE_POSTDAD;
211462306a36Sopenharmony_ci		err = 0;
211562306a36Sopenharmony_ci	}
211662306a36Sopenharmony_ci	spin_unlock_bh(&ifp->lock);
211762306a36Sopenharmony_ci
211862306a36Sopenharmony_ci	return err;
211962306a36Sopenharmony_ci}
212062306a36Sopenharmony_ci
212162306a36Sopenharmony_civoid addrconf_dad_failure(struct sk_buff *skb, struct inet6_ifaddr *ifp)
212262306a36Sopenharmony_ci{
212362306a36Sopenharmony_ci	struct inet6_dev *idev = ifp->idev;
212462306a36Sopenharmony_ci	struct net *net = dev_net(idev->dev);
212562306a36Sopenharmony_ci
212662306a36Sopenharmony_ci	if (addrconf_dad_end(ifp)) {
212762306a36Sopenharmony_ci		in6_ifa_put(ifp);
212862306a36Sopenharmony_ci		return;
212962306a36Sopenharmony_ci	}
213062306a36Sopenharmony_ci
213162306a36Sopenharmony_ci	net_info_ratelimited("%s: IPv6 duplicate address %pI6c used by %pM detected!\n",
213262306a36Sopenharmony_ci			     ifp->idev->dev->name, &ifp->addr, eth_hdr(skb)->h_source);
213362306a36Sopenharmony_ci
213462306a36Sopenharmony_ci	spin_lock_bh(&ifp->lock);
213562306a36Sopenharmony_ci
213662306a36Sopenharmony_ci	if (ifp->flags & IFA_F_STABLE_PRIVACY) {
213762306a36Sopenharmony_ci		struct in6_addr new_addr;
213862306a36Sopenharmony_ci		struct inet6_ifaddr *ifp2;
213962306a36Sopenharmony_ci		int retries = ifp->stable_privacy_retry + 1;
214062306a36Sopenharmony_ci		struct ifa6_config cfg = {
214162306a36Sopenharmony_ci			.pfx = &new_addr,
214262306a36Sopenharmony_ci			.plen = ifp->prefix_len,
214362306a36Sopenharmony_ci			.ifa_flags = ifp->flags,
214462306a36Sopenharmony_ci			.valid_lft = ifp->valid_lft,
214562306a36Sopenharmony_ci			.preferred_lft = ifp->prefered_lft,
214662306a36Sopenharmony_ci			.scope = ifp->scope,
214762306a36Sopenharmony_ci		};
214862306a36Sopenharmony_ci
214962306a36Sopenharmony_ci		if (retries > net->ipv6.sysctl.idgen_retries) {
215062306a36Sopenharmony_ci			net_info_ratelimited("%s: privacy stable address generation failed because of DAD conflicts!\n",
215162306a36Sopenharmony_ci					     ifp->idev->dev->name);
215262306a36Sopenharmony_ci			goto errdad;
215362306a36Sopenharmony_ci		}
215462306a36Sopenharmony_ci
215562306a36Sopenharmony_ci		new_addr = ifp->addr;
215662306a36Sopenharmony_ci		if (ipv6_generate_stable_address(&new_addr, retries,
215762306a36Sopenharmony_ci						 idev))
215862306a36Sopenharmony_ci			goto errdad;
215962306a36Sopenharmony_ci
216062306a36Sopenharmony_ci		spin_unlock_bh(&ifp->lock);
216162306a36Sopenharmony_ci
216262306a36Sopenharmony_ci		if (idev->cnf.max_addresses &&
216362306a36Sopenharmony_ci		    ipv6_count_addresses(idev) >=
216462306a36Sopenharmony_ci		    idev->cnf.max_addresses)
216562306a36Sopenharmony_ci			goto lock_errdad;
216662306a36Sopenharmony_ci
216762306a36Sopenharmony_ci		net_info_ratelimited("%s: generating new stable privacy address because of DAD conflict\n",
216862306a36Sopenharmony_ci				     ifp->idev->dev->name);
216962306a36Sopenharmony_ci
217062306a36Sopenharmony_ci		ifp2 = ipv6_add_addr(idev, &cfg, false, NULL);
217162306a36Sopenharmony_ci		if (IS_ERR(ifp2))
217262306a36Sopenharmony_ci			goto lock_errdad;
217362306a36Sopenharmony_ci
217462306a36Sopenharmony_ci		spin_lock_bh(&ifp2->lock);
217562306a36Sopenharmony_ci		ifp2->stable_privacy_retry = retries;
217662306a36Sopenharmony_ci		ifp2->state = INET6_IFADDR_STATE_PREDAD;
217762306a36Sopenharmony_ci		spin_unlock_bh(&ifp2->lock);
217862306a36Sopenharmony_ci
217962306a36Sopenharmony_ci		addrconf_mod_dad_work(ifp2, net->ipv6.sysctl.idgen_delay);
218062306a36Sopenharmony_ci		in6_ifa_put(ifp2);
218162306a36Sopenharmony_cilock_errdad:
218262306a36Sopenharmony_ci		spin_lock_bh(&ifp->lock);
218362306a36Sopenharmony_ci	}
218462306a36Sopenharmony_ci
218562306a36Sopenharmony_cierrdad:
218662306a36Sopenharmony_ci	/* transition from _POSTDAD to _ERRDAD */
218762306a36Sopenharmony_ci	ifp->state = INET6_IFADDR_STATE_ERRDAD;
218862306a36Sopenharmony_ci	spin_unlock_bh(&ifp->lock);
218962306a36Sopenharmony_ci
219062306a36Sopenharmony_ci	addrconf_mod_dad_work(ifp, 0);
219162306a36Sopenharmony_ci	in6_ifa_put(ifp);
219262306a36Sopenharmony_ci}
219362306a36Sopenharmony_ci
219462306a36Sopenharmony_ci/* Join to solicited addr multicast group.
219562306a36Sopenharmony_ci * caller must hold RTNL */
219662306a36Sopenharmony_civoid addrconf_join_solict(struct net_device *dev, const struct in6_addr *addr)
219762306a36Sopenharmony_ci{
219862306a36Sopenharmony_ci	struct in6_addr maddr;
219962306a36Sopenharmony_ci
220062306a36Sopenharmony_ci	if (dev->flags&(IFF_LOOPBACK|IFF_NOARP))
220162306a36Sopenharmony_ci		return;
220262306a36Sopenharmony_ci
220362306a36Sopenharmony_ci	addrconf_addr_solict_mult(addr, &maddr);
220462306a36Sopenharmony_ci	ipv6_dev_mc_inc(dev, &maddr);
220562306a36Sopenharmony_ci}
220662306a36Sopenharmony_ci
220762306a36Sopenharmony_ci/* caller must hold RTNL */
220862306a36Sopenharmony_civoid addrconf_leave_solict(struct inet6_dev *idev, const struct in6_addr *addr)
220962306a36Sopenharmony_ci{
221062306a36Sopenharmony_ci	struct in6_addr maddr;
221162306a36Sopenharmony_ci
221262306a36Sopenharmony_ci	if (idev->dev->flags&(IFF_LOOPBACK|IFF_NOARP))
221362306a36Sopenharmony_ci		return;
221462306a36Sopenharmony_ci
221562306a36Sopenharmony_ci	addrconf_addr_solict_mult(addr, &maddr);
221662306a36Sopenharmony_ci	__ipv6_dev_mc_dec(idev, &maddr);
221762306a36Sopenharmony_ci}
221862306a36Sopenharmony_ci
221962306a36Sopenharmony_ci/* caller must hold RTNL */
222062306a36Sopenharmony_cistatic void addrconf_join_anycast(struct inet6_ifaddr *ifp)
222162306a36Sopenharmony_ci{
222262306a36Sopenharmony_ci	struct in6_addr addr;
222362306a36Sopenharmony_ci
222462306a36Sopenharmony_ci	if (ifp->prefix_len >= 127) /* RFC 6164 */
222562306a36Sopenharmony_ci		return;
222662306a36Sopenharmony_ci	ipv6_addr_prefix(&addr, &ifp->addr, ifp->prefix_len);
222762306a36Sopenharmony_ci	if (ipv6_addr_any(&addr))
222862306a36Sopenharmony_ci		return;
222962306a36Sopenharmony_ci	__ipv6_dev_ac_inc(ifp->idev, &addr);
223062306a36Sopenharmony_ci}
223162306a36Sopenharmony_ci
223262306a36Sopenharmony_ci/* caller must hold RTNL */
223362306a36Sopenharmony_cistatic void addrconf_leave_anycast(struct inet6_ifaddr *ifp)
223462306a36Sopenharmony_ci{
223562306a36Sopenharmony_ci	struct in6_addr addr;
223662306a36Sopenharmony_ci
223762306a36Sopenharmony_ci	if (ifp->prefix_len >= 127) /* RFC 6164 */
223862306a36Sopenharmony_ci		return;
223962306a36Sopenharmony_ci	ipv6_addr_prefix(&addr, &ifp->addr, ifp->prefix_len);
224062306a36Sopenharmony_ci	if (ipv6_addr_any(&addr))
224162306a36Sopenharmony_ci		return;
224262306a36Sopenharmony_ci	__ipv6_dev_ac_dec(ifp->idev, &addr);
224362306a36Sopenharmony_ci}
224462306a36Sopenharmony_ci
224562306a36Sopenharmony_cistatic int addrconf_ifid_6lowpan(u8 *eui, struct net_device *dev)
224662306a36Sopenharmony_ci{
224762306a36Sopenharmony_ci	switch (dev->addr_len) {
224862306a36Sopenharmony_ci	case ETH_ALEN:
224962306a36Sopenharmony_ci		memcpy(eui, dev->dev_addr, 3);
225062306a36Sopenharmony_ci		eui[3] = 0xFF;
225162306a36Sopenharmony_ci		eui[4] = 0xFE;
225262306a36Sopenharmony_ci		memcpy(eui + 5, dev->dev_addr + 3, 3);
225362306a36Sopenharmony_ci		break;
225462306a36Sopenharmony_ci	case EUI64_ADDR_LEN:
225562306a36Sopenharmony_ci		memcpy(eui, dev->dev_addr, EUI64_ADDR_LEN);
225662306a36Sopenharmony_ci		eui[0] ^= 2;
225762306a36Sopenharmony_ci		break;
225862306a36Sopenharmony_ci	default:
225962306a36Sopenharmony_ci		return -1;
226062306a36Sopenharmony_ci	}
226162306a36Sopenharmony_ci
226262306a36Sopenharmony_ci	return 0;
226362306a36Sopenharmony_ci}
226462306a36Sopenharmony_ci
226562306a36Sopenharmony_cistatic int addrconf_ifid_ieee1394(u8 *eui, struct net_device *dev)
226662306a36Sopenharmony_ci{
226762306a36Sopenharmony_ci	const union fwnet_hwaddr *ha;
226862306a36Sopenharmony_ci
226962306a36Sopenharmony_ci	if (dev->addr_len != FWNET_ALEN)
227062306a36Sopenharmony_ci		return -1;
227162306a36Sopenharmony_ci
227262306a36Sopenharmony_ci	ha = (const union fwnet_hwaddr *)dev->dev_addr;
227362306a36Sopenharmony_ci
227462306a36Sopenharmony_ci	memcpy(eui, &ha->uc.uniq_id, sizeof(ha->uc.uniq_id));
227562306a36Sopenharmony_ci	eui[0] ^= 2;
227662306a36Sopenharmony_ci	return 0;
227762306a36Sopenharmony_ci}
227862306a36Sopenharmony_ci
227962306a36Sopenharmony_cistatic int addrconf_ifid_arcnet(u8 *eui, struct net_device *dev)
228062306a36Sopenharmony_ci{
228162306a36Sopenharmony_ci	/* XXX: inherit EUI-64 from other interface -- yoshfuji */
228262306a36Sopenharmony_ci	if (dev->addr_len != ARCNET_ALEN)
228362306a36Sopenharmony_ci		return -1;
228462306a36Sopenharmony_ci	memset(eui, 0, 7);
228562306a36Sopenharmony_ci	eui[7] = *(u8 *)dev->dev_addr;
228662306a36Sopenharmony_ci	return 0;
228762306a36Sopenharmony_ci}
228862306a36Sopenharmony_ci
228962306a36Sopenharmony_cistatic int addrconf_ifid_infiniband(u8 *eui, struct net_device *dev)
229062306a36Sopenharmony_ci{
229162306a36Sopenharmony_ci	if (dev->addr_len != INFINIBAND_ALEN)
229262306a36Sopenharmony_ci		return -1;
229362306a36Sopenharmony_ci	memcpy(eui, dev->dev_addr + 12, 8);
229462306a36Sopenharmony_ci	eui[0] |= 2;
229562306a36Sopenharmony_ci	return 0;
229662306a36Sopenharmony_ci}
229762306a36Sopenharmony_ci
229862306a36Sopenharmony_cistatic int __ipv6_isatap_ifid(u8 *eui, __be32 addr)
229962306a36Sopenharmony_ci{
230062306a36Sopenharmony_ci	if (addr == 0)
230162306a36Sopenharmony_ci		return -1;
230262306a36Sopenharmony_ci	eui[0] = (ipv4_is_zeronet(addr) || ipv4_is_private_10(addr) ||
230362306a36Sopenharmony_ci		  ipv4_is_loopback(addr) || ipv4_is_linklocal_169(addr) ||
230462306a36Sopenharmony_ci		  ipv4_is_private_172(addr) || ipv4_is_test_192(addr) ||
230562306a36Sopenharmony_ci		  ipv4_is_anycast_6to4(addr) || ipv4_is_private_192(addr) ||
230662306a36Sopenharmony_ci		  ipv4_is_test_198(addr) || ipv4_is_multicast(addr) ||
230762306a36Sopenharmony_ci		  ipv4_is_lbcast(addr)) ? 0x00 : 0x02;
230862306a36Sopenharmony_ci	eui[1] = 0;
230962306a36Sopenharmony_ci	eui[2] = 0x5E;
231062306a36Sopenharmony_ci	eui[3] = 0xFE;
231162306a36Sopenharmony_ci	memcpy(eui + 4, &addr, 4);
231262306a36Sopenharmony_ci	return 0;
231362306a36Sopenharmony_ci}
231462306a36Sopenharmony_ci
231562306a36Sopenharmony_cistatic int addrconf_ifid_sit(u8 *eui, struct net_device *dev)
231662306a36Sopenharmony_ci{
231762306a36Sopenharmony_ci	if (dev->priv_flags & IFF_ISATAP)
231862306a36Sopenharmony_ci		return __ipv6_isatap_ifid(eui, *(__be32 *)dev->dev_addr);
231962306a36Sopenharmony_ci	return -1;
232062306a36Sopenharmony_ci}
232162306a36Sopenharmony_ci
232262306a36Sopenharmony_cistatic int addrconf_ifid_gre(u8 *eui, struct net_device *dev)
232362306a36Sopenharmony_ci{
232462306a36Sopenharmony_ci	return __ipv6_isatap_ifid(eui, *(__be32 *)dev->dev_addr);
232562306a36Sopenharmony_ci}
232662306a36Sopenharmony_ci
232762306a36Sopenharmony_cistatic int addrconf_ifid_ip6tnl(u8 *eui, struct net_device *dev)
232862306a36Sopenharmony_ci{
232962306a36Sopenharmony_ci	memcpy(eui, dev->perm_addr, 3);
233062306a36Sopenharmony_ci	memcpy(eui + 5, dev->perm_addr + 3, 3);
233162306a36Sopenharmony_ci	eui[3] = 0xFF;
233262306a36Sopenharmony_ci	eui[4] = 0xFE;
233362306a36Sopenharmony_ci	eui[0] ^= 2;
233462306a36Sopenharmony_ci	return 0;
233562306a36Sopenharmony_ci}
233662306a36Sopenharmony_ci
233762306a36Sopenharmony_cistatic int ipv6_generate_eui64(u8 *eui, struct net_device *dev)
233862306a36Sopenharmony_ci{
233962306a36Sopenharmony_ci	switch (dev->type) {
234062306a36Sopenharmony_ci	case ARPHRD_ETHER:
234162306a36Sopenharmony_ci	case ARPHRD_FDDI:
234262306a36Sopenharmony_ci		return addrconf_ifid_eui48(eui, dev);
234362306a36Sopenharmony_ci	case ARPHRD_ARCNET:
234462306a36Sopenharmony_ci		return addrconf_ifid_arcnet(eui, dev);
234562306a36Sopenharmony_ci	case ARPHRD_INFINIBAND:
234662306a36Sopenharmony_ci		return addrconf_ifid_infiniband(eui, dev);
234762306a36Sopenharmony_ci	case ARPHRD_SIT:
234862306a36Sopenharmony_ci		return addrconf_ifid_sit(eui, dev);
234962306a36Sopenharmony_ci	case ARPHRD_IPGRE:
235062306a36Sopenharmony_ci	case ARPHRD_TUNNEL:
235162306a36Sopenharmony_ci		return addrconf_ifid_gre(eui, dev);
235262306a36Sopenharmony_ci	case ARPHRD_6LOWPAN:
235362306a36Sopenharmony_ci		return addrconf_ifid_6lowpan(eui, dev);
235462306a36Sopenharmony_ci	case ARPHRD_IEEE1394:
235562306a36Sopenharmony_ci		return addrconf_ifid_ieee1394(eui, dev);
235662306a36Sopenharmony_ci	case ARPHRD_TUNNEL6:
235762306a36Sopenharmony_ci	case ARPHRD_IP6GRE:
235862306a36Sopenharmony_ci	case ARPHRD_RAWIP:
235962306a36Sopenharmony_ci		return addrconf_ifid_ip6tnl(eui, dev);
236062306a36Sopenharmony_ci	}
236162306a36Sopenharmony_ci	return -1;
236262306a36Sopenharmony_ci}
236362306a36Sopenharmony_ci
236462306a36Sopenharmony_cistatic int ipv6_inherit_eui64(u8 *eui, struct inet6_dev *idev)
236562306a36Sopenharmony_ci{
236662306a36Sopenharmony_ci	int err = -1;
236762306a36Sopenharmony_ci	struct inet6_ifaddr *ifp;
236862306a36Sopenharmony_ci
236962306a36Sopenharmony_ci	read_lock_bh(&idev->lock);
237062306a36Sopenharmony_ci	list_for_each_entry_reverse(ifp, &idev->addr_list, if_list) {
237162306a36Sopenharmony_ci		if (ifp->scope > IFA_LINK)
237262306a36Sopenharmony_ci			break;
237362306a36Sopenharmony_ci		if (ifp->scope == IFA_LINK && !(ifp->flags&IFA_F_TENTATIVE)) {
237462306a36Sopenharmony_ci			memcpy(eui, ifp->addr.s6_addr+8, 8);
237562306a36Sopenharmony_ci			err = 0;
237662306a36Sopenharmony_ci			break;
237762306a36Sopenharmony_ci		}
237862306a36Sopenharmony_ci	}
237962306a36Sopenharmony_ci	read_unlock_bh(&idev->lock);
238062306a36Sopenharmony_ci	return err;
238162306a36Sopenharmony_ci}
238262306a36Sopenharmony_ci
238362306a36Sopenharmony_ci/* Generation of a randomized Interface Identifier
238462306a36Sopenharmony_ci * draft-ietf-6man-rfc4941bis, Section 3.3.1
238562306a36Sopenharmony_ci */
238662306a36Sopenharmony_ci
238762306a36Sopenharmony_cistatic void ipv6_gen_rnd_iid(struct in6_addr *addr)
238862306a36Sopenharmony_ci{
238962306a36Sopenharmony_ciregen:
239062306a36Sopenharmony_ci	get_random_bytes(&addr->s6_addr[8], 8);
239162306a36Sopenharmony_ci
239262306a36Sopenharmony_ci	/* <draft-ietf-6man-rfc4941bis-08.txt>, Section 3.3.1:
239362306a36Sopenharmony_ci	 * check if generated address is not inappropriate:
239462306a36Sopenharmony_ci	 *
239562306a36Sopenharmony_ci	 * - Reserved IPv6 Interface Identifiers
239662306a36Sopenharmony_ci	 * - XXX: already assigned to an address on the device
239762306a36Sopenharmony_ci	 */
239862306a36Sopenharmony_ci
239962306a36Sopenharmony_ci	/* Subnet-router anycast: 0000:0000:0000:0000 */
240062306a36Sopenharmony_ci	if (!(addr->s6_addr32[2] | addr->s6_addr32[3]))
240162306a36Sopenharmony_ci		goto regen;
240262306a36Sopenharmony_ci
240362306a36Sopenharmony_ci	/* IANA Ethernet block: 0200:5EFF:FE00:0000-0200:5EFF:FE00:5212
240462306a36Sopenharmony_ci	 * Proxy Mobile IPv6:   0200:5EFF:FE00:5213
240562306a36Sopenharmony_ci	 * IANA Ethernet block: 0200:5EFF:FE00:5214-0200:5EFF:FEFF:FFFF
240662306a36Sopenharmony_ci	 */
240762306a36Sopenharmony_ci	if (ntohl(addr->s6_addr32[2]) == 0x02005eff &&
240862306a36Sopenharmony_ci	    (ntohl(addr->s6_addr32[3]) & 0Xff000000) == 0xfe000000)
240962306a36Sopenharmony_ci		goto regen;
241062306a36Sopenharmony_ci
241162306a36Sopenharmony_ci	/* Reserved subnet anycast addresses */
241262306a36Sopenharmony_ci	if (ntohl(addr->s6_addr32[2]) == 0xfdffffff &&
241362306a36Sopenharmony_ci	    ntohl(addr->s6_addr32[3]) >= 0Xffffff80)
241462306a36Sopenharmony_ci		goto regen;
241562306a36Sopenharmony_ci}
241662306a36Sopenharmony_ci
241762306a36Sopenharmony_ci/*
241862306a36Sopenharmony_ci *	Add prefix route.
241962306a36Sopenharmony_ci */
242062306a36Sopenharmony_ci
242162306a36Sopenharmony_cistatic void
242262306a36Sopenharmony_ciaddrconf_prefix_route(struct in6_addr *pfx, int plen, u32 metric,
242362306a36Sopenharmony_ci		      struct net_device *dev, unsigned long expires,
242462306a36Sopenharmony_ci		      u32 flags, gfp_t gfp_flags)
242562306a36Sopenharmony_ci{
242662306a36Sopenharmony_ci	struct fib6_config cfg = {
242762306a36Sopenharmony_ci		.fc_table = l3mdev_fib_table(dev) ? : RT6_TABLE_PREFIX,
242862306a36Sopenharmony_ci		.fc_metric = metric ? : IP6_RT_PRIO_ADDRCONF,
242962306a36Sopenharmony_ci		.fc_ifindex = dev->ifindex,
243062306a36Sopenharmony_ci		.fc_expires = expires,
243162306a36Sopenharmony_ci		.fc_dst_len = plen,
243262306a36Sopenharmony_ci		.fc_flags = RTF_UP | flags,
243362306a36Sopenharmony_ci		.fc_nlinfo.nl_net = dev_net(dev),
243462306a36Sopenharmony_ci		.fc_protocol = RTPROT_KERNEL,
243562306a36Sopenharmony_ci		.fc_type = RTN_UNICAST,
243662306a36Sopenharmony_ci	};
243762306a36Sopenharmony_ci
243862306a36Sopenharmony_ci	cfg.fc_dst = *pfx;
243962306a36Sopenharmony_ci
244062306a36Sopenharmony_ci	/* Prevent useless cloning on PtP SIT.
244162306a36Sopenharmony_ci	   This thing is done here expecting that the whole
244262306a36Sopenharmony_ci	   class of non-broadcast devices need not cloning.
244362306a36Sopenharmony_ci	 */
244462306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6_SIT)
244562306a36Sopenharmony_ci	if (dev->type == ARPHRD_SIT && (dev->flags & IFF_POINTOPOINT))
244662306a36Sopenharmony_ci		cfg.fc_flags |= RTF_NONEXTHOP;
244762306a36Sopenharmony_ci#endif
244862306a36Sopenharmony_ci
244962306a36Sopenharmony_ci	ip6_route_add(&cfg, gfp_flags, NULL);
245062306a36Sopenharmony_ci}
245162306a36Sopenharmony_ci
245262306a36Sopenharmony_ci
245362306a36Sopenharmony_cistatic struct fib6_info *addrconf_get_prefix_route(const struct in6_addr *pfx,
245462306a36Sopenharmony_ci						  int plen,
245562306a36Sopenharmony_ci						  const struct net_device *dev,
245662306a36Sopenharmony_ci						  u32 flags, u32 noflags,
245762306a36Sopenharmony_ci						  bool no_gw)
245862306a36Sopenharmony_ci{
245962306a36Sopenharmony_ci	struct fib6_node *fn;
246062306a36Sopenharmony_ci	struct fib6_info *rt = NULL;
246162306a36Sopenharmony_ci	struct fib6_table *table;
246262306a36Sopenharmony_ci	u32 tb_id = l3mdev_fib_table(dev) ? : RT6_TABLE_PREFIX;
246362306a36Sopenharmony_ci
246462306a36Sopenharmony_ci	table = fib6_get_table(dev_net(dev), tb_id);
246562306a36Sopenharmony_ci	if (!table)
246662306a36Sopenharmony_ci		return NULL;
246762306a36Sopenharmony_ci
246862306a36Sopenharmony_ci	rcu_read_lock();
246962306a36Sopenharmony_ci	fn = fib6_locate(&table->tb6_root, pfx, plen, NULL, 0, true);
247062306a36Sopenharmony_ci	if (!fn)
247162306a36Sopenharmony_ci		goto out;
247262306a36Sopenharmony_ci
247362306a36Sopenharmony_ci	for_each_fib6_node_rt_rcu(fn) {
247462306a36Sopenharmony_ci		/* prefix routes only use builtin fib6_nh */
247562306a36Sopenharmony_ci		if (rt->nh)
247662306a36Sopenharmony_ci			continue;
247762306a36Sopenharmony_ci
247862306a36Sopenharmony_ci		if (rt->fib6_nh->fib_nh_dev->ifindex != dev->ifindex)
247962306a36Sopenharmony_ci			continue;
248062306a36Sopenharmony_ci		if (no_gw && rt->fib6_nh->fib_nh_gw_family)
248162306a36Sopenharmony_ci			continue;
248262306a36Sopenharmony_ci		if ((rt->fib6_flags & flags) != flags)
248362306a36Sopenharmony_ci			continue;
248462306a36Sopenharmony_ci		if ((rt->fib6_flags & noflags) != 0)
248562306a36Sopenharmony_ci			continue;
248662306a36Sopenharmony_ci		if (!fib6_info_hold_safe(rt))
248762306a36Sopenharmony_ci			continue;
248862306a36Sopenharmony_ci		break;
248962306a36Sopenharmony_ci	}
249062306a36Sopenharmony_ciout:
249162306a36Sopenharmony_ci	rcu_read_unlock();
249262306a36Sopenharmony_ci	return rt;
249362306a36Sopenharmony_ci}
249462306a36Sopenharmony_ci
249562306a36Sopenharmony_ci
249662306a36Sopenharmony_ci/* Create "default" multicast route to the interface */
249762306a36Sopenharmony_ci
249862306a36Sopenharmony_cistatic void addrconf_add_mroute(struct net_device *dev)
249962306a36Sopenharmony_ci{
250062306a36Sopenharmony_ci	struct fib6_config cfg = {
250162306a36Sopenharmony_ci		.fc_table = l3mdev_fib_table(dev) ? : RT6_TABLE_LOCAL,
250262306a36Sopenharmony_ci		.fc_metric = IP6_RT_PRIO_ADDRCONF,
250362306a36Sopenharmony_ci		.fc_ifindex = dev->ifindex,
250462306a36Sopenharmony_ci		.fc_dst_len = 8,
250562306a36Sopenharmony_ci		.fc_flags = RTF_UP,
250662306a36Sopenharmony_ci		.fc_type = RTN_MULTICAST,
250762306a36Sopenharmony_ci		.fc_nlinfo.nl_net = dev_net(dev),
250862306a36Sopenharmony_ci		.fc_protocol = RTPROT_KERNEL,
250962306a36Sopenharmony_ci	};
251062306a36Sopenharmony_ci
251162306a36Sopenharmony_ci	ipv6_addr_set(&cfg.fc_dst, htonl(0xFF000000), 0, 0, 0);
251262306a36Sopenharmony_ci
251362306a36Sopenharmony_ci	ip6_route_add(&cfg, GFP_KERNEL, NULL);
251462306a36Sopenharmony_ci}
251562306a36Sopenharmony_ci
251662306a36Sopenharmony_cistatic struct inet6_dev *addrconf_add_dev(struct net_device *dev)
251762306a36Sopenharmony_ci{
251862306a36Sopenharmony_ci	struct inet6_dev *idev;
251962306a36Sopenharmony_ci
252062306a36Sopenharmony_ci	ASSERT_RTNL();
252162306a36Sopenharmony_ci
252262306a36Sopenharmony_ci	idev = ipv6_find_idev(dev);
252362306a36Sopenharmony_ci	if (IS_ERR(idev))
252462306a36Sopenharmony_ci		return idev;
252562306a36Sopenharmony_ci
252662306a36Sopenharmony_ci	if (idev->cnf.disable_ipv6)
252762306a36Sopenharmony_ci		return ERR_PTR(-EACCES);
252862306a36Sopenharmony_ci
252962306a36Sopenharmony_ci	/* Add default multicast route */
253062306a36Sopenharmony_ci	if (!(dev->flags & IFF_LOOPBACK) && !netif_is_l3_master(dev))
253162306a36Sopenharmony_ci		addrconf_add_mroute(dev);
253262306a36Sopenharmony_ci
253362306a36Sopenharmony_ci	return idev;
253462306a36Sopenharmony_ci}
253562306a36Sopenharmony_ci
253662306a36Sopenharmony_cistatic void manage_tempaddrs(struct inet6_dev *idev,
253762306a36Sopenharmony_ci			     struct inet6_ifaddr *ifp,
253862306a36Sopenharmony_ci			     __u32 valid_lft, __u32 prefered_lft,
253962306a36Sopenharmony_ci			     bool create, unsigned long now)
254062306a36Sopenharmony_ci{
254162306a36Sopenharmony_ci	u32 flags;
254262306a36Sopenharmony_ci	struct inet6_ifaddr *ift;
254362306a36Sopenharmony_ci
254462306a36Sopenharmony_ci	read_lock_bh(&idev->lock);
254562306a36Sopenharmony_ci	/* update all temporary addresses in the list */
254662306a36Sopenharmony_ci	list_for_each_entry(ift, &idev->tempaddr_list, tmp_list) {
254762306a36Sopenharmony_ci		int age, max_valid, max_prefered;
254862306a36Sopenharmony_ci
254962306a36Sopenharmony_ci		if (ifp != ift->ifpub)
255062306a36Sopenharmony_ci			continue;
255162306a36Sopenharmony_ci
255262306a36Sopenharmony_ci		/* RFC 4941 section 3.3:
255362306a36Sopenharmony_ci		 * If a received option will extend the lifetime of a public
255462306a36Sopenharmony_ci		 * address, the lifetimes of temporary addresses should
255562306a36Sopenharmony_ci		 * be extended, subject to the overall constraint that no
255662306a36Sopenharmony_ci		 * temporary addresses should ever remain "valid" or "preferred"
255762306a36Sopenharmony_ci		 * for a time longer than (TEMP_VALID_LIFETIME) or
255862306a36Sopenharmony_ci		 * (TEMP_PREFERRED_LIFETIME - DESYNC_FACTOR), respectively.
255962306a36Sopenharmony_ci		 */
256062306a36Sopenharmony_ci		age = (now - ift->cstamp) / HZ;
256162306a36Sopenharmony_ci		max_valid = idev->cnf.temp_valid_lft - age;
256262306a36Sopenharmony_ci		if (max_valid < 0)
256362306a36Sopenharmony_ci			max_valid = 0;
256462306a36Sopenharmony_ci
256562306a36Sopenharmony_ci		max_prefered = idev->cnf.temp_prefered_lft -
256662306a36Sopenharmony_ci			       idev->desync_factor - age;
256762306a36Sopenharmony_ci		if (max_prefered < 0)
256862306a36Sopenharmony_ci			max_prefered = 0;
256962306a36Sopenharmony_ci
257062306a36Sopenharmony_ci		if (valid_lft > max_valid)
257162306a36Sopenharmony_ci			valid_lft = max_valid;
257262306a36Sopenharmony_ci
257362306a36Sopenharmony_ci		if (prefered_lft > max_prefered)
257462306a36Sopenharmony_ci			prefered_lft = max_prefered;
257562306a36Sopenharmony_ci
257662306a36Sopenharmony_ci		spin_lock(&ift->lock);
257762306a36Sopenharmony_ci		flags = ift->flags;
257862306a36Sopenharmony_ci		ift->valid_lft = valid_lft;
257962306a36Sopenharmony_ci		ift->prefered_lft = prefered_lft;
258062306a36Sopenharmony_ci		ift->tstamp = now;
258162306a36Sopenharmony_ci		if (prefered_lft > 0)
258262306a36Sopenharmony_ci			ift->flags &= ~IFA_F_DEPRECATED;
258362306a36Sopenharmony_ci
258462306a36Sopenharmony_ci		spin_unlock(&ift->lock);
258562306a36Sopenharmony_ci		if (!(flags&IFA_F_TENTATIVE))
258662306a36Sopenharmony_ci			ipv6_ifa_notify(0, ift);
258762306a36Sopenharmony_ci	}
258862306a36Sopenharmony_ci
258962306a36Sopenharmony_ci	/* Also create a temporary address if it's enabled but no temporary
259062306a36Sopenharmony_ci	 * address currently exists.
259162306a36Sopenharmony_ci	 * However, we get called with valid_lft == 0, prefered_lft == 0, create == false
259262306a36Sopenharmony_ci	 * as part of cleanup (ie. deleting the mngtmpaddr).
259362306a36Sopenharmony_ci	 * We don't want that to result in creating a new temporary ip address.
259462306a36Sopenharmony_ci	 */
259562306a36Sopenharmony_ci	if (list_empty(&idev->tempaddr_list) && (valid_lft || prefered_lft))
259662306a36Sopenharmony_ci		create = true;
259762306a36Sopenharmony_ci
259862306a36Sopenharmony_ci	if (create && idev->cnf.use_tempaddr > 0) {
259962306a36Sopenharmony_ci		/* When a new public address is created as described
260062306a36Sopenharmony_ci		 * in [ADDRCONF], also create a new temporary address.
260162306a36Sopenharmony_ci		 */
260262306a36Sopenharmony_ci		read_unlock_bh(&idev->lock);
260362306a36Sopenharmony_ci		ipv6_create_tempaddr(ifp, false);
260462306a36Sopenharmony_ci	} else {
260562306a36Sopenharmony_ci		read_unlock_bh(&idev->lock);
260662306a36Sopenharmony_ci	}
260762306a36Sopenharmony_ci}
260862306a36Sopenharmony_ci
260962306a36Sopenharmony_cistatic bool is_addr_mode_generate_stable(struct inet6_dev *idev)
261062306a36Sopenharmony_ci{
261162306a36Sopenharmony_ci	return idev->cnf.addr_gen_mode == IN6_ADDR_GEN_MODE_STABLE_PRIVACY ||
261262306a36Sopenharmony_ci	       idev->cnf.addr_gen_mode == IN6_ADDR_GEN_MODE_RANDOM;
261362306a36Sopenharmony_ci}
261462306a36Sopenharmony_ci
261562306a36Sopenharmony_ciint addrconf_prefix_rcv_add_addr(struct net *net, struct net_device *dev,
261662306a36Sopenharmony_ci				 const struct prefix_info *pinfo,
261762306a36Sopenharmony_ci				 struct inet6_dev *in6_dev,
261862306a36Sopenharmony_ci				 const struct in6_addr *addr, int addr_type,
261962306a36Sopenharmony_ci				 u32 addr_flags, bool sllao, bool tokenized,
262062306a36Sopenharmony_ci				 __u32 valid_lft, u32 prefered_lft)
262162306a36Sopenharmony_ci{
262262306a36Sopenharmony_ci	struct inet6_ifaddr *ifp = ipv6_get_ifaddr(net, addr, dev, 1);
262362306a36Sopenharmony_ci	int create = 0, update_lft = 0;
262462306a36Sopenharmony_ci
262562306a36Sopenharmony_ci	if (!ifp && valid_lft) {
262662306a36Sopenharmony_ci		int max_addresses = in6_dev->cnf.max_addresses;
262762306a36Sopenharmony_ci		struct ifa6_config cfg = {
262862306a36Sopenharmony_ci			.pfx = addr,
262962306a36Sopenharmony_ci			.plen = pinfo->prefix_len,
263062306a36Sopenharmony_ci			.ifa_flags = addr_flags,
263162306a36Sopenharmony_ci			.valid_lft = valid_lft,
263262306a36Sopenharmony_ci			.preferred_lft = prefered_lft,
263362306a36Sopenharmony_ci			.scope = addr_type & IPV6_ADDR_SCOPE_MASK,
263462306a36Sopenharmony_ci			.ifa_proto = IFAPROT_KERNEL_RA
263562306a36Sopenharmony_ci		};
263662306a36Sopenharmony_ci
263762306a36Sopenharmony_ci#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
263862306a36Sopenharmony_ci		if ((net->ipv6.devconf_all->optimistic_dad ||
263962306a36Sopenharmony_ci		     in6_dev->cnf.optimistic_dad) &&
264062306a36Sopenharmony_ci		    !net->ipv6.devconf_all->forwarding && sllao)
264162306a36Sopenharmony_ci			cfg.ifa_flags |= IFA_F_OPTIMISTIC;
264262306a36Sopenharmony_ci#endif
264362306a36Sopenharmony_ci
264462306a36Sopenharmony_ci		/* Do not allow to create too much of autoconfigured
264562306a36Sopenharmony_ci		 * addresses; this would be too easy way to crash kernel.
264662306a36Sopenharmony_ci		 */
264762306a36Sopenharmony_ci		if (!max_addresses ||
264862306a36Sopenharmony_ci		    ipv6_count_addresses(in6_dev) < max_addresses)
264962306a36Sopenharmony_ci			ifp = ipv6_add_addr(in6_dev, &cfg, false, NULL);
265062306a36Sopenharmony_ci
265162306a36Sopenharmony_ci		if (IS_ERR_OR_NULL(ifp))
265262306a36Sopenharmony_ci			return -1;
265362306a36Sopenharmony_ci
265462306a36Sopenharmony_ci		create = 1;
265562306a36Sopenharmony_ci		spin_lock_bh(&ifp->lock);
265662306a36Sopenharmony_ci		ifp->flags |= IFA_F_MANAGETEMPADDR;
265762306a36Sopenharmony_ci		ifp->cstamp = jiffies;
265862306a36Sopenharmony_ci		ifp->tokenized = tokenized;
265962306a36Sopenharmony_ci		spin_unlock_bh(&ifp->lock);
266062306a36Sopenharmony_ci		addrconf_dad_start(ifp);
266162306a36Sopenharmony_ci	}
266262306a36Sopenharmony_ci
266362306a36Sopenharmony_ci	if (ifp) {
266462306a36Sopenharmony_ci		u32 flags;
266562306a36Sopenharmony_ci		unsigned long now;
266662306a36Sopenharmony_ci		u32 stored_lft;
266762306a36Sopenharmony_ci
266862306a36Sopenharmony_ci		/* update lifetime (RFC2462 5.5.3 e) */
266962306a36Sopenharmony_ci		spin_lock_bh(&ifp->lock);
267062306a36Sopenharmony_ci		now = jiffies;
267162306a36Sopenharmony_ci		if (ifp->valid_lft > (now - ifp->tstamp) / HZ)
267262306a36Sopenharmony_ci			stored_lft = ifp->valid_lft - (now - ifp->tstamp) / HZ;
267362306a36Sopenharmony_ci		else
267462306a36Sopenharmony_ci			stored_lft = 0;
267562306a36Sopenharmony_ci		if (!create && stored_lft) {
267662306a36Sopenharmony_ci			const u32 minimum_lft = min_t(u32,
267762306a36Sopenharmony_ci				stored_lft, MIN_VALID_LIFETIME);
267862306a36Sopenharmony_ci			valid_lft = max(valid_lft, minimum_lft);
267962306a36Sopenharmony_ci
268062306a36Sopenharmony_ci			/* RFC4862 Section 5.5.3e:
268162306a36Sopenharmony_ci			 * "Note that the preferred lifetime of the
268262306a36Sopenharmony_ci			 *  corresponding address is always reset to
268362306a36Sopenharmony_ci			 *  the Preferred Lifetime in the received
268462306a36Sopenharmony_ci			 *  Prefix Information option, regardless of
268562306a36Sopenharmony_ci			 *  whether the valid lifetime is also reset or
268662306a36Sopenharmony_ci			 *  ignored."
268762306a36Sopenharmony_ci			 *
268862306a36Sopenharmony_ci			 * So we should always update prefered_lft here.
268962306a36Sopenharmony_ci			 */
269062306a36Sopenharmony_ci			update_lft = 1;
269162306a36Sopenharmony_ci		}
269262306a36Sopenharmony_ci
269362306a36Sopenharmony_ci		if (update_lft) {
269462306a36Sopenharmony_ci			ifp->valid_lft = valid_lft;
269562306a36Sopenharmony_ci			ifp->prefered_lft = prefered_lft;
269662306a36Sopenharmony_ci			ifp->tstamp = now;
269762306a36Sopenharmony_ci			flags = ifp->flags;
269862306a36Sopenharmony_ci			ifp->flags &= ~IFA_F_DEPRECATED;
269962306a36Sopenharmony_ci			spin_unlock_bh(&ifp->lock);
270062306a36Sopenharmony_ci
270162306a36Sopenharmony_ci			if (!(flags&IFA_F_TENTATIVE))
270262306a36Sopenharmony_ci				ipv6_ifa_notify(0, ifp);
270362306a36Sopenharmony_ci		} else
270462306a36Sopenharmony_ci			spin_unlock_bh(&ifp->lock);
270562306a36Sopenharmony_ci
270662306a36Sopenharmony_ci		manage_tempaddrs(in6_dev, ifp, valid_lft, prefered_lft,
270762306a36Sopenharmony_ci				 create, now);
270862306a36Sopenharmony_ci
270962306a36Sopenharmony_ci		in6_ifa_put(ifp);
271062306a36Sopenharmony_ci		addrconf_verify(net);
271162306a36Sopenharmony_ci	}
271262306a36Sopenharmony_ci
271362306a36Sopenharmony_ci	return 0;
271462306a36Sopenharmony_ci}
271562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(addrconf_prefix_rcv_add_addr);
271662306a36Sopenharmony_ci
271762306a36Sopenharmony_civoid addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao)
271862306a36Sopenharmony_ci{
271962306a36Sopenharmony_ci	struct prefix_info *pinfo;
272062306a36Sopenharmony_ci	__u32 valid_lft;
272162306a36Sopenharmony_ci	__u32 prefered_lft;
272262306a36Sopenharmony_ci	int addr_type, err;
272362306a36Sopenharmony_ci	u32 addr_flags = 0;
272462306a36Sopenharmony_ci	struct inet6_dev *in6_dev;
272562306a36Sopenharmony_ci	struct net *net = dev_net(dev);
272662306a36Sopenharmony_ci
272762306a36Sopenharmony_ci	pinfo = (struct prefix_info *) opt;
272862306a36Sopenharmony_ci
272962306a36Sopenharmony_ci	if (len < sizeof(struct prefix_info)) {
273062306a36Sopenharmony_ci		netdev_dbg(dev, "addrconf: prefix option too short\n");
273162306a36Sopenharmony_ci		return;
273262306a36Sopenharmony_ci	}
273362306a36Sopenharmony_ci
273462306a36Sopenharmony_ci	/*
273562306a36Sopenharmony_ci	 *	Validation checks ([ADDRCONF], page 19)
273662306a36Sopenharmony_ci	 */
273762306a36Sopenharmony_ci
273862306a36Sopenharmony_ci	addr_type = ipv6_addr_type(&pinfo->prefix);
273962306a36Sopenharmony_ci
274062306a36Sopenharmony_ci	if (addr_type & (IPV6_ADDR_MULTICAST|IPV6_ADDR_LINKLOCAL))
274162306a36Sopenharmony_ci		return;
274262306a36Sopenharmony_ci
274362306a36Sopenharmony_ci	valid_lft = ntohl(pinfo->valid);
274462306a36Sopenharmony_ci	prefered_lft = ntohl(pinfo->prefered);
274562306a36Sopenharmony_ci
274662306a36Sopenharmony_ci	if (prefered_lft > valid_lft) {
274762306a36Sopenharmony_ci		net_warn_ratelimited("addrconf: prefix option has invalid lifetime\n");
274862306a36Sopenharmony_ci		return;
274962306a36Sopenharmony_ci	}
275062306a36Sopenharmony_ci
275162306a36Sopenharmony_ci	in6_dev = in6_dev_get(dev);
275262306a36Sopenharmony_ci
275362306a36Sopenharmony_ci	if (!in6_dev) {
275462306a36Sopenharmony_ci		net_dbg_ratelimited("addrconf: device %s not configured\n",
275562306a36Sopenharmony_ci				    dev->name);
275662306a36Sopenharmony_ci		return;
275762306a36Sopenharmony_ci	}
275862306a36Sopenharmony_ci
275962306a36Sopenharmony_ci	if (valid_lft != 0 && valid_lft < in6_dev->cnf.accept_ra_min_lft)
276062306a36Sopenharmony_ci		goto put;
276162306a36Sopenharmony_ci
276262306a36Sopenharmony_ci	/*
276362306a36Sopenharmony_ci	 *	Two things going on here:
276462306a36Sopenharmony_ci	 *	1) Add routes for on-link prefixes
276562306a36Sopenharmony_ci	 *	2) Configure prefixes with the auto flag set
276662306a36Sopenharmony_ci	 */
276762306a36Sopenharmony_ci
276862306a36Sopenharmony_ci	if (pinfo->onlink) {
276962306a36Sopenharmony_ci		struct fib6_info *rt;
277062306a36Sopenharmony_ci		unsigned long rt_expires;
277162306a36Sopenharmony_ci
277262306a36Sopenharmony_ci		/* Avoid arithmetic overflow. Really, we could
277362306a36Sopenharmony_ci		 * save rt_expires in seconds, likely valid_lft,
277462306a36Sopenharmony_ci		 * but it would require division in fib gc, that it
277562306a36Sopenharmony_ci		 * not good.
277662306a36Sopenharmony_ci		 */
277762306a36Sopenharmony_ci		if (HZ > USER_HZ)
277862306a36Sopenharmony_ci			rt_expires = addrconf_timeout_fixup(valid_lft, HZ);
277962306a36Sopenharmony_ci		else
278062306a36Sopenharmony_ci			rt_expires = addrconf_timeout_fixup(valid_lft, USER_HZ);
278162306a36Sopenharmony_ci
278262306a36Sopenharmony_ci		if (addrconf_finite_timeout(rt_expires))
278362306a36Sopenharmony_ci			rt_expires *= HZ;
278462306a36Sopenharmony_ci
278562306a36Sopenharmony_ci		rt = addrconf_get_prefix_route(&pinfo->prefix,
278662306a36Sopenharmony_ci					       pinfo->prefix_len,
278762306a36Sopenharmony_ci					       dev,
278862306a36Sopenharmony_ci					       RTF_ADDRCONF | RTF_PREFIX_RT,
278962306a36Sopenharmony_ci					       RTF_DEFAULT, true);
279062306a36Sopenharmony_ci
279162306a36Sopenharmony_ci		if (rt) {
279262306a36Sopenharmony_ci			/* Autoconf prefix route */
279362306a36Sopenharmony_ci			if (valid_lft == 0) {
279462306a36Sopenharmony_ci				ip6_del_rt(net, rt, false);
279562306a36Sopenharmony_ci				rt = NULL;
279662306a36Sopenharmony_ci			} else if (addrconf_finite_timeout(rt_expires)) {
279762306a36Sopenharmony_ci				/* not infinity */
279862306a36Sopenharmony_ci				fib6_set_expires(rt, jiffies + rt_expires);
279962306a36Sopenharmony_ci			} else {
280062306a36Sopenharmony_ci				fib6_clean_expires(rt);
280162306a36Sopenharmony_ci			}
280262306a36Sopenharmony_ci		} else if (valid_lft) {
280362306a36Sopenharmony_ci			clock_t expires = 0;
280462306a36Sopenharmony_ci			int flags = RTF_ADDRCONF | RTF_PREFIX_RT;
280562306a36Sopenharmony_ci			if (addrconf_finite_timeout(rt_expires)) {
280662306a36Sopenharmony_ci				/* not infinity */
280762306a36Sopenharmony_ci				flags |= RTF_EXPIRES;
280862306a36Sopenharmony_ci				expires = jiffies_to_clock_t(rt_expires);
280962306a36Sopenharmony_ci			}
281062306a36Sopenharmony_ci			addrconf_prefix_route(&pinfo->prefix, pinfo->prefix_len,
281162306a36Sopenharmony_ci					      0, dev, expires, flags,
281262306a36Sopenharmony_ci					      GFP_ATOMIC);
281362306a36Sopenharmony_ci		}
281462306a36Sopenharmony_ci		fib6_info_release(rt);
281562306a36Sopenharmony_ci	}
281662306a36Sopenharmony_ci
281762306a36Sopenharmony_ci	/* Try to figure out our local address for this prefix */
281862306a36Sopenharmony_ci
281962306a36Sopenharmony_ci	if (pinfo->autoconf && in6_dev->cnf.autoconf) {
282062306a36Sopenharmony_ci		struct in6_addr addr;
282162306a36Sopenharmony_ci		bool tokenized = false, dev_addr_generated = false;
282262306a36Sopenharmony_ci
282362306a36Sopenharmony_ci		if (pinfo->prefix_len == 64) {
282462306a36Sopenharmony_ci			memcpy(&addr, &pinfo->prefix, 8);
282562306a36Sopenharmony_ci
282662306a36Sopenharmony_ci			if (!ipv6_addr_any(&in6_dev->token)) {
282762306a36Sopenharmony_ci				read_lock_bh(&in6_dev->lock);
282862306a36Sopenharmony_ci				memcpy(addr.s6_addr + 8,
282962306a36Sopenharmony_ci				       in6_dev->token.s6_addr + 8, 8);
283062306a36Sopenharmony_ci				read_unlock_bh(&in6_dev->lock);
283162306a36Sopenharmony_ci				tokenized = true;
283262306a36Sopenharmony_ci			} else if (is_addr_mode_generate_stable(in6_dev) &&
283362306a36Sopenharmony_ci				   !ipv6_generate_stable_address(&addr, 0,
283462306a36Sopenharmony_ci								 in6_dev)) {
283562306a36Sopenharmony_ci				addr_flags |= IFA_F_STABLE_PRIVACY;
283662306a36Sopenharmony_ci				goto ok;
283762306a36Sopenharmony_ci			} else if (ipv6_generate_eui64(addr.s6_addr + 8, dev) &&
283862306a36Sopenharmony_ci				   ipv6_inherit_eui64(addr.s6_addr + 8, in6_dev)) {
283962306a36Sopenharmony_ci				goto put;
284062306a36Sopenharmony_ci			} else {
284162306a36Sopenharmony_ci				dev_addr_generated = true;
284262306a36Sopenharmony_ci			}
284362306a36Sopenharmony_ci			goto ok;
284462306a36Sopenharmony_ci		}
284562306a36Sopenharmony_ci		net_dbg_ratelimited("IPv6 addrconf: prefix with wrong length %d\n",
284662306a36Sopenharmony_ci				    pinfo->prefix_len);
284762306a36Sopenharmony_ci		goto put;
284862306a36Sopenharmony_ci
284962306a36Sopenharmony_ciok:
285062306a36Sopenharmony_ci		err = addrconf_prefix_rcv_add_addr(net, dev, pinfo, in6_dev,
285162306a36Sopenharmony_ci						   &addr, addr_type,
285262306a36Sopenharmony_ci						   addr_flags, sllao,
285362306a36Sopenharmony_ci						   tokenized, valid_lft,
285462306a36Sopenharmony_ci						   prefered_lft);
285562306a36Sopenharmony_ci		if (err)
285662306a36Sopenharmony_ci			goto put;
285762306a36Sopenharmony_ci
285862306a36Sopenharmony_ci		/* Ignore error case here because previous prefix add addr was
285962306a36Sopenharmony_ci		 * successful which will be notified.
286062306a36Sopenharmony_ci		 */
286162306a36Sopenharmony_ci		ndisc_ops_prefix_rcv_add_addr(net, dev, pinfo, in6_dev, &addr,
286262306a36Sopenharmony_ci					      addr_type, addr_flags, sllao,
286362306a36Sopenharmony_ci					      tokenized, valid_lft,
286462306a36Sopenharmony_ci					      prefered_lft,
286562306a36Sopenharmony_ci					      dev_addr_generated);
286662306a36Sopenharmony_ci	}
286762306a36Sopenharmony_ci	inet6_prefix_notify(RTM_NEWPREFIX, in6_dev, pinfo);
286862306a36Sopenharmony_ciput:
286962306a36Sopenharmony_ci	in6_dev_put(in6_dev);
287062306a36Sopenharmony_ci}
287162306a36Sopenharmony_ci
287262306a36Sopenharmony_cistatic int addrconf_set_sit_dstaddr(struct net *net, struct net_device *dev,
287362306a36Sopenharmony_ci		struct in6_ifreq *ireq)
287462306a36Sopenharmony_ci{
287562306a36Sopenharmony_ci	struct ip_tunnel_parm p = { };
287662306a36Sopenharmony_ci	int err;
287762306a36Sopenharmony_ci
287862306a36Sopenharmony_ci	if (!(ipv6_addr_type(&ireq->ifr6_addr) & IPV6_ADDR_COMPATv4))
287962306a36Sopenharmony_ci		return -EADDRNOTAVAIL;
288062306a36Sopenharmony_ci
288162306a36Sopenharmony_ci	p.iph.daddr = ireq->ifr6_addr.s6_addr32[3];
288262306a36Sopenharmony_ci	p.iph.version = 4;
288362306a36Sopenharmony_ci	p.iph.ihl = 5;
288462306a36Sopenharmony_ci	p.iph.protocol = IPPROTO_IPV6;
288562306a36Sopenharmony_ci	p.iph.ttl = 64;
288662306a36Sopenharmony_ci
288762306a36Sopenharmony_ci	if (!dev->netdev_ops->ndo_tunnel_ctl)
288862306a36Sopenharmony_ci		return -EOPNOTSUPP;
288962306a36Sopenharmony_ci	err = dev->netdev_ops->ndo_tunnel_ctl(dev, &p, SIOCADDTUNNEL);
289062306a36Sopenharmony_ci	if (err)
289162306a36Sopenharmony_ci		return err;
289262306a36Sopenharmony_ci
289362306a36Sopenharmony_ci	dev = __dev_get_by_name(net, p.name);
289462306a36Sopenharmony_ci	if (!dev)
289562306a36Sopenharmony_ci		return -ENOBUFS;
289662306a36Sopenharmony_ci	return dev_open(dev, NULL);
289762306a36Sopenharmony_ci}
289862306a36Sopenharmony_ci
289962306a36Sopenharmony_ci/*
290062306a36Sopenharmony_ci *	Set destination address.
290162306a36Sopenharmony_ci *	Special case for SIT interfaces where we create a new "virtual"
290262306a36Sopenharmony_ci *	device.
290362306a36Sopenharmony_ci */
290462306a36Sopenharmony_ciint addrconf_set_dstaddr(struct net *net, void __user *arg)
290562306a36Sopenharmony_ci{
290662306a36Sopenharmony_ci	struct net_device *dev;
290762306a36Sopenharmony_ci	struct in6_ifreq ireq;
290862306a36Sopenharmony_ci	int err = -ENODEV;
290962306a36Sopenharmony_ci
291062306a36Sopenharmony_ci	if (!IS_ENABLED(CONFIG_IPV6_SIT))
291162306a36Sopenharmony_ci		return -ENODEV;
291262306a36Sopenharmony_ci	if (copy_from_user(&ireq, arg, sizeof(struct in6_ifreq)))
291362306a36Sopenharmony_ci		return -EFAULT;
291462306a36Sopenharmony_ci
291562306a36Sopenharmony_ci	rtnl_lock();
291662306a36Sopenharmony_ci	dev = __dev_get_by_index(net, ireq.ifr6_ifindex);
291762306a36Sopenharmony_ci	if (dev && dev->type == ARPHRD_SIT)
291862306a36Sopenharmony_ci		err = addrconf_set_sit_dstaddr(net, dev, &ireq);
291962306a36Sopenharmony_ci	rtnl_unlock();
292062306a36Sopenharmony_ci	return err;
292162306a36Sopenharmony_ci}
292262306a36Sopenharmony_ci
292362306a36Sopenharmony_cistatic int ipv6_mc_config(struct sock *sk, bool join,
292462306a36Sopenharmony_ci			  const struct in6_addr *addr, int ifindex)
292562306a36Sopenharmony_ci{
292662306a36Sopenharmony_ci	int ret;
292762306a36Sopenharmony_ci
292862306a36Sopenharmony_ci	ASSERT_RTNL();
292962306a36Sopenharmony_ci
293062306a36Sopenharmony_ci	lock_sock(sk);
293162306a36Sopenharmony_ci	if (join)
293262306a36Sopenharmony_ci		ret = ipv6_sock_mc_join(sk, ifindex, addr);
293362306a36Sopenharmony_ci	else
293462306a36Sopenharmony_ci		ret = ipv6_sock_mc_drop(sk, ifindex, addr);
293562306a36Sopenharmony_ci	release_sock(sk);
293662306a36Sopenharmony_ci
293762306a36Sopenharmony_ci	return ret;
293862306a36Sopenharmony_ci}
293962306a36Sopenharmony_ci
294062306a36Sopenharmony_ci/*
294162306a36Sopenharmony_ci *	Manual configuration of address on an interface
294262306a36Sopenharmony_ci */
294362306a36Sopenharmony_cistatic int inet6_addr_add(struct net *net, int ifindex,
294462306a36Sopenharmony_ci			  struct ifa6_config *cfg,
294562306a36Sopenharmony_ci			  struct netlink_ext_ack *extack)
294662306a36Sopenharmony_ci{
294762306a36Sopenharmony_ci	struct inet6_ifaddr *ifp;
294862306a36Sopenharmony_ci	struct inet6_dev *idev;
294962306a36Sopenharmony_ci	struct net_device *dev;
295062306a36Sopenharmony_ci	unsigned long timeout;
295162306a36Sopenharmony_ci	clock_t expires;
295262306a36Sopenharmony_ci	u32 flags;
295362306a36Sopenharmony_ci
295462306a36Sopenharmony_ci	ASSERT_RTNL();
295562306a36Sopenharmony_ci
295662306a36Sopenharmony_ci	if (cfg->plen > 128) {
295762306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Invalid prefix length");
295862306a36Sopenharmony_ci		return -EINVAL;
295962306a36Sopenharmony_ci	}
296062306a36Sopenharmony_ci
296162306a36Sopenharmony_ci	/* check the lifetime */
296262306a36Sopenharmony_ci	if (!cfg->valid_lft || cfg->preferred_lft > cfg->valid_lft) {
296362306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "address lifetime invalid");
296462306a36Sopenharmony_ci		return -EINVAL;
296562306a36Sopenharmony_ci	}
296662306a36Sopenharmony_ci
296762306a36Sopenharmony_ci	if (cfg->ifa_flags & IFA_F_MANAGETEMPADDR && cfg->plen != 64) {
296862306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "address with \"mngtmpaddr\" flag must have a prefix length of 64");
296962306a36Sopenharmony_ci		return -EINVAL;
297062306a36Sopenharmony_ci	}
297162306a36Sopenharmony_ci
297262306a36Sopenharmony_ci	dev = __dev_get_by_index(net, ifindex);
297362306a36Sopenharmony_ci	if (!dev)
297462306a36Sopenharmony_ci		return -ENODEV;
297562306a36Sopenharmony_ci
297662306a36Sopenharmony_ci	idev = addrconf_add_dev(dev);
297762306a36Sopenharmony_ci	if (IS_ERR(idev)) {
297862306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "IPv6 is disabled on this device");
297962306a36Sopenharmony_ci		return PTR_ERR(idev);
298062306a36Sopenharmony_ci	}
298162306a36Sopenharmony_ci
298262306a36Sopenharmony_ci	if (cfg->ifa_flags & IFA_F_MCAUTOJOIN) {
298362306a36Sopenharmony_ci		int ret = ipv6_mc_config(net->ipv6.mc_autojoin_sk,
298462306a36Sopenharmony_ci					 true, cfg->pfx, ifindex);
298562306a36Sopenharmony_ci
298662306a36Sopenharmony_ci		if (ret < 0) {
298762306a36Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "Multicast auto join failed");
298862306a36Sopenharmony_ci			return ret;
298962306a36Sopenharmony_ci		}
299062306a36Sopenharmony_ci	}
299162306a36Sopenharmony_ci
299262306a36Sopenharmony_ci	cfg->scope = ipv6_addr_scope(cfg->pfx);
299362306a36Sopenharmony_ci
299462306a36Sopenharmony_ci	timeout = addrconf_timeout_fixup(cfg->valid_lft, HZ);
299562306a36Sopenharmony_ci	if (addrconf_finite_timeout(timeout)) {
299662306a36Sopenharmony_ci		expires = jiffies_to_clock_t(timeout * HZ);
299762306a36Sopenharmony_ci		cfg->valid_lft = timeout;
299862306a36Sopenharmony_ci		flags = RTF_EXPIRES;
299962306a36Sopenharmony_ci	} else {
300062306a36Sopenharmony_ci		expires = 0;
300162306a36Sopenharmony_ci		flags = 0;
300262306a36Sopenharmony_ci		cfg->ifa_flags |= IFA_F_PERMANENT;
300362306a36Sopenharmony_ci	}
300462306a36Sopenharmony_ci
300562306a36Sopenharmony_ci	timeout = addrconf_timeout_fixup(cfg->preferred_lft, HZ);
300662306a36Sopenharmony_ci	if (addrconf_finite_timeout(timeout)) {
300762306a36Sopenharmony_ci		if (timeout == 0)
300862306a36Sopenharmony_ci			cfg->ifa_flags |= IFA_F_DEPRECATED;
300962306a36Sopenharmony_ci		cfg->preferred_lft = timeout;
301062306a36Sopenharmony_ci	}
301162306a36Sopenharmony_ci
301262306a36Sopenharmony_ci	ifp = ipv6_add_addr(idev, cfg, true, extack);
301362306a36Sopenharmony_ci	if (!IS_ERR(ifp)) {
301462306a36Sopenharmony_ci		if (!(cfg->ifa_flags & IFA_F_NOPREFIXROUTE)) {
301562306a36Sopenharmony_ci			addrconf_prefix_route(&ifp->addr, ifp->prefix_len,
301662306a36Sopenharmony_ci					      ifp->rt_priority, dev, expires,
301762306a36Sopenharmony_ci					      flags, GFP_KERNEL);
301862306a36Sopenharmony_ci		}
301962306a36Sopenharmony_ci
302062306a36Sopenharmony_ci		/* Send a netlink notification if DAD is enabled and
302162306a36Sopenharmony_ci		 * optimistic flag is not set
302262306a36Sopenharmony_ci		 */
302362306a36Sopenharmony_ci		if (!(ifp->flags & (IFA_F_OPTIMISTIC | IFA_F_NODAD)))
302462306a36Sopenharmony_ci			ipv6_ifa_notify(0, ifp);
302562306a36Sopenharmony_ci		/*
302662306a36Sopenharmony_ci		 * Note that section 3.1 of RFC 4429 indicates
302762306a36Sopenharmony_ci		 * that the Optimistic flag should not be set for
302862306a36Sopenharmony_ci		 * manually configured addresses
302962306a36Sopenharmony_ci		 */
303062306a36Sopenharmony_ci		addrconf_dad_start(ifp);
303162306a36Sopenharmony_ci		if (cfg->ifa_flags & IFA_F_MANAGETEMPADDR)
303262306a36Sopenharmony_ci			manage_tempaddrs(idev, ifp, cfg->valid_lft,
303362306a36Sopenharmony_ci					 cfg->preferred_lft, true, jiffies);
303462306a36Sopenharmony_ci		in6_ifa_put(ifp);
303562306a36Sopenharmony_ci		addrconf_verify_rtnl(net);
303662306a36Sopenharmony_ci		return 0;
303762306a36Sopenharmony_ci	} else if (cfg->ifa_flags & IFA_F_MCAUTOJOIN) {
303862306a36Sopenharmony_ci		ipv6_mc_config(net->ipv6.mc_autojoin_sk, false,
303962306a36Sopenharmony_ci			       cfg->pfx, ifindex);
304062306a36Sopenharmony_ci	}
304162306a36Sopenharmony_ci
304262306a36Sopenharmony_ci	return PTR_ERR(ifp);
304362306a36Sopenharmony_ci}
304462306a36Sopenharmony_ci
304562306a36Sopenharmony_cistatic int inet6_addr_del(struct net *net, int ifindex, u32 ifa_flags,
304662306a36Sopenharmony_ci			  const struct in6_addr *pfx, unsigned int plen,
304762306a36Sopenharmony_ci			  struct netlink_ext_ack *extack)
304862306a36Sopenharmony_ci{
304962306a36Sopenharmony_ci	struct inet6_ifaddr *ifp;
305062306a36Sopenharmony_ci	struct inet6_dev *idev;
305162306a36Sopenharmony_ci	struct net_device *dev;
305262306a36Sopenharmony_ci
305362306a36Sopenharmony_ci	if (plen > 128) {
305462306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Invalid prefix length");
305562306a36Sopenharmony_ci		return -EINVAL;
305662306a36Sopenharmony_ci	}
305762306a36Sopenharmony_ci
305862306a36Sopenharmony_ci	dev = __dev_get_by_index(net, ifindex);
305962306a36Sopenharmony_ci	if (!dev) {
306062306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Unable to find the interface");
306162306a36Sopenharmony_ci		return -ENODEV;
306262306a36Sopenharmony_ci	}
306362306a36Sopenharmony_ci
306462306a36Sopenharmony_ci	idev = __in6_dev_get(dev);
306562306a36Sopenharmony_ci	if (!idev) {
306662306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "IPv6 is disabled on this device");
306762306a36Sopenharmony_ci		return -ENXIO;
306862306a36Sopenharmony_ci	}
306962306a36Sopenharmony_ci
307062306a36Sopenharmony_ci	read_lock_bh(&idev->lock);
307162306a36Sopenharmony_ci	list_for_each_entry(ifp, &idev->addr_list, if_list) {
307262306a36Sopenharmony_ci		if (ifp->prefix_len == plen &&
307362306a36Sopenharmony_ci		    ipv6_addr_equal(pfx, &ifp->addr)) {
307462306a36Sopenharmony_ci			in6_ifa_hold(ifp);
307562306a36Sopenharmony_ci			read_unlock_bh(&idev->lock);
307662306a36Sopenharmony_ci
307762306a36Sopenharmony_ci			if (!(ifp->flags & IFA_F_TEMPORARY) &&
307862306a36Sopenharmony_ci			    (ifa_flags & IFA_F_MANAGETEMPADDR))
307962306a36Sopenharmony_ci				manage_tempaddrs(idev, ifp, 0, 0, false,
308062306a36Sopenharmony_ci						 jiffies);
308162306a36Sopenharmony_ci			ipv6_del_addr(ifp);
308262306a36Sopenharmony_ci			addrconf_verify_rtnl(net);
308362306a36Sopenharmony_ci			if (ipv6_addr_is_multicast(pfx)) {
308462306a36Sopenharmony_ci				ipv6_mc_config(net->ipv6.mc_autojoin_sk,
308562306a36Sopenharmony_ci					       false, pfx, dev->ifindex);
308662306a36Sopenharmony_ci			}
308762306a36Sopenharmony_ci			return 0;
308862306a36Sopenharmony_ci		}
308962306a36Sopenharmony_ci	}
309062306a36Sopenharmony_ci	read_unlock_bh(&idev->lock);
309162306a36Sopenharmony_ci
309262306a36Sopenharmony_ci	NL_SET_ERR_MSG_MOD(extack, "address not found");
309362306a36Sopenharmony_ci	return -EADDRNOTAVAIL;
309462306a36Sopenharmony_ci}
309562306a36Sopenharmony_ci
309662306a36Sopenharmony_ci
309762306a36Sopenharmony_ciint addrconf_add_ifaddr(struct net *net, void __user *arg)
309862306a36Sopenharmony_ci{
309962306a36Sopenharmony_ci	struct ifa6_config cfg = {
310062306a36Sopenharmony_ci		.ifa_flags = IFA_F_PERMANENT,
310162306a36Sopenharmony_ci		.preferred_lft = INFINITY_LIFE_TIME,
310262306a36Sopenharmony_ci		.valid_lft = INFINITY_LIFE_TIME,
310362306a36Sopenharmony_ci	};
310462306a36Sopenharmony_ci	struct in6_ifreq ireq;
310562306a36Sopenharmony_ci	int err;
310662306a36Sopenharmony_ci
310762306a36Sopenharmony_ci	if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
310862306a36Sopenharmony_ci		return -EPERM;
310962306a36Sopenharmony_ci
311062306a36Sopenharmony_ci	if (copy_from_user(&ireq, arg, sizeof(struct in6_ifreq)))
311162306a36Sopenharmony_ci		return -EFAULT;
311262306a36Sopenharmony_ci
311362306a36Sopenharmony_ci	cfg.pfx = &ireq.ifr6_addr;
311462306a36Sopenharmony_ci	cfg.plen = ireq.ifr6_prefixlen;
311562306a36Sopenharmony_ci
311662306a36Sopenharmony_ci	rtnl_lock();
311762306a36Sopenharmony_ci	err = inet6_addr_add(net, ireq.ifr6_ifindex, &cfg, NULL);
311862306a36Sopenharmony_ci	rtnl_unlock();
311962306a36Sopenharmony_ci	return err;
312062306a36Sopenharmony_ci}
312162306a36Sopenharmony_ci
312262306a36Sopenharmony_ciint addrconf_del_ifaddr(struct net *net, void __user *arg)
312362306a36Sopenharmony_ci{
312462306a36Sopenharmony_ci	struct in6_ifreq ireq;
312562306a36Sopenharmony_ci	int err;
312662306a36Sopenharmony_ci
312762306a36Sopenharmony_ci	if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
312862306a36Sopenharmony_ci		return -EPERM;
312962306a36Sopenharmony_ci
313062306a36Sopenharmony_ci	if (copy_from_user(&ireq, arg, sizeof(struct in6_ifreq)))
313162306a36Sopenharmony_ci		return -EFAULT;
313262306a36Sopenharmony_ci
313362306a36Sopenharmony_ci	rtnl_lock();
313462306a36Sopenharmony_ci	err = inet6_addr_del(net, ireq.ifr6_ifindex, 0, &ireq.ifr6_addr,
313562306a36Sopenharmony_ci			     ireq.ifr6_prefixlen, NULL);
313662306a36Sopenharmony_ci	rtnl_unlock();
313762306a36Sopenharmony_ci	return err;
313862306a36Sopenharmony_ci}
313962306a36Sopenharmony_ci
314062306a36Sopenharmony_cistatic void add_addr(struct inet6_dev *idev, const struct in6_addr *addr,
314162306a36Sopenharmony_ci		     int plen, int scope, u8 proto)
314262306a36Sopenharmony_ci{
314362306a36Sopenharmony_ci	struct inet6_ifaddr *ifp;
314462306a36Sopenharmony_ci	struct ifa6_config cfg = {
314562306a36Sopenharmony_ci		.pfx = addr,
314662306a36Sopenharmony_ci		.plen = plen,
314762306a36Sopenharmony_ci		.ifa_flags = IFA_F_PERMANENT,
314862306a36Sopenharmony_ci		.valid_lft = INFINITY_LIFE_TIME,
314962306a36Sopenharmony_ci		.preferred_lft = INFINITY_LIFE_TIME,
315062306a36Sopenharmony_ci		.scope = scope,
315162306a36Sopenharmony_ci		.ifa_proto = proto
315262306a36Sopenharmony_ci	};
315362306a36Sopenharmony_ci
315462306a36Sopenharmony_ci	ifp = ipv6_add_addr(idev, &cfg, true, NULL);
315562306a36Sopenharmony_ci	if (!IS_ERR(ifp)) {
315662306a36Sopenharmony_ci		spin_lock_bh(&ifp->lock);
315762306a36Sopenharmony_ci		ifp->flags &= ~IFA_F_TENTATIVE;
315862306a36Sopenharmony_ci		spin_unlock_bh(&ifp->lock);
315962306a36Sopenharmony_ci		rt_genid_bump_ipv6(dev_net(idev->dev));
316062306a36Sopenharmony_ci		ipv6_ifa_notify(RTM_NEWADDR, ifp);
316162306a36Sopenharmony_ci		in6_ifa_put(ifp);
316262306a36Sopenharmony_ci	}
316362306a36Sopenharmony_ci}
316462306a36Sopenharmony_ci
316562306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6_SIT) || IS_ENABLED(CONFIG_NET_IPGRE) || IS_ENABLED(CONFIG_IPV6_GRE)
316662306a36Sopenharmony_cistatic void add_v4_addrs(struct inet6_dev *idev)
316762306a36Sopenharmony_ci{
316862306a36Sopenharmony_ci	struct in6_addr addr;
316962306a36Sopenharmony_ci	struct net_device *dev;
317062306a36Sopenharmony_ci	struct net *net = dev_net(idev->dev);
317162306a36Sopenharmony_ci	int scope, plen, offset = 0;
317262306a36Sopenharmony_ci	u32 pflags = 0;
317362306a36Sopenharmony_ci
317462306a36Sopenharmony_ci	ASSERT_RTNL();
317562306a36Sopenharmony_ci
317662306a36Sopenharmony_ci	memset(&addr, 0, sizeof(struct in6_addr));
317762306a36Sopenharmony_ci	/* in case of IP6GRE the dev_addr is an IPv6 and therefore we use only the last 4 bytes */
317862306a36Sopenharmony_ci	if (idev->dev->addr_len == sizeof(struct in6_addr))
317962306a36Sopenharmony_ci		offset = sizeof(struct in6_addr) - 4;
318062306a36Sopenharmony_ci	memcpy(&addr.s6_addr32[3], idev->dev->dev_addr + offset, 4);
318162306a36Sopenharmony_ci
318262306a36Sopenharmony_ci	if (!(idev->dev->flags & IFF_POINTOPOINT) && idev->dev->type == ARPHRD_SIT) {
318362306a36Sopenharmony_ci		scope = IPV6_ADDR_COMPATv4;
318462306a36Sopenharmony_ci		plen = 96;
318562306a36Sopenharmony_ci		pflags |= RTF_NONEXTHOP;
318662306a36Sopenharmony_ci	} else {
318762306a36Sopenharmony_ci		if (idev->cnf.addr_gen_mode == IN6_ADDR_GEN_MODE_NONE)
318862306a36Sopenharmony_ci			return;
318962306a36Sopenharmony_ci
319062306a36Sopenharmony_ci		addr.s6_addr32[0] = htonl(0xfe800000);
319162306a36Sopenharmony_ci		scope = IFA_LINK;
319262306a36Sopenharmony_ci		plen = 64;
319362306a36Sopenharmony_ci	}
319462306a36Sopenharmony_ci
319562306a36Sopenharmony_ci	if (addr.s6_addr32[3]) {
319662306a36Sopenharmony_ci		add_addr(idev, &addr, plen, scope, IFAPROT_UNSPEC);
319762306a36Sopenharmony_ci		addrconf_prefix_route(&addr, plen, 0, idev->dev, 0, pflags,
319862306a36Sopenharmony_ci				      GFP_KERNEL);
319962306a36Sopenharmony_ci		return;
320062306a36Sopenharmony_ci	}
320162306a36Sopenharmony_ci
320262306a36Sopenharmony_ci	for_each_netdev(net, dev) {
320362306a36Sopenharmony_ci		struct in_device *in_dev = __in_dev_get_rtnl(dev);
320462306a36Sopenharmony_ci		if (in_dev && (dev->flags & IFF_UP)) {
320562306a36Sopenharmony_ci			struct in_ifaddr *ifa;
320662306a36Sopenharmony_ci			int flag = scope;
320762306a36Sopenharmony_ci
320862306a36Sopenharmony_ci			in_dev_for_each_ifa_rtnl(ifa, in_dev) {
320962306a36Sopenharmony_ci				addr.s6_addr32[3] = ifa->ifa_local;
321062306a36Sopenharmony_ci
321162306a36Sopenharmony_ci				if (ifa->ifa_scope == RT_SCOPE_LINK)
321262306a36Sopenharmony_ci					continue;
321362306a36Sopenharmony_ci				if (ifa->ifa_scope >= RT_SCOPE_HOST) {
321462306a36Sopenharmony_ci					if (idev->dev->flags&IFF_POINTOPOINT)
321562306a36Sopenharmony_ci						continue;
321662306a36Sopenharmony_ci					flag |= IFA_HOST;
321762306a36Sopenharmony_ci				}
321862306a36Sopenharmony_ci
321962306a36Sopenharmony_ci				add_addr(idev, &addr, plen, flag,
322062306a36Sopenharmony_ci					 IFAPROT_UNSPEC);
322162306a36Sopenharmony_ci				addrconf_prefix_route(&addr, plen, 0, idev->dev,
322262306a36Sopenharmony_ci						      0, pflags, GFP_KERNEL);
322362306a36Sopenharmony_ci			}
322462306a36Sopenharmony_ci		}
322562306a36Sopenharmony_ci	}
322662306a36Sopenharmony_ci}
322762306a36Sopenharmony_ci#endif
322862306a36Sopenharmony_ci
322962306a36Sopenharmony_cistatic void init_loopback(struct net_device *dev)
323062306a36Sopenharmony_ci{
323162306a36Sopenharmony_ci	struct inet6_dev  *idev;
323262306a36Sopenharmony_ci
323362306a36Sopenharmony_ci	/* ::1 */
323462306a36Sopenharmony_ci
323562306a36Sopenharmony_ci	ASSERT_RTNL();
323662306a36Sopenharmony_ci
323762306a36Sopenharmony_ci	idev = ipv6_find_idev(dev);
323862306a36Sopenharmony_ci	if (IS_ERR(idev)) {
323962306a36Sopenharmony_ci		pr_debug("%s: add_dev failed\n", __func__);
324062306a36Sopenharmony_ci		return;
324162306a36Sopenharmony_ci	}
324262306a36Sopenharmony_ci
324362306a36Sopenharmony_ci	add_addr(idev, &in6addr_loopback, 128, IFA_HOST, IFAPROT_KERNEL_LO);
324462306a36Sopenharmony_ci}
324562306a36Sopenharmony_ci
324662306a36Sopenharmony_civoid addrconf_add_linklocal(struct inet6_dev *idev,
324762306a36Sopenharmony_ci			    const struct in6_addr *addr, u32 flags)
324862306a36Sopenharmony_ci{
324962306a36Sopenharmony_ci	struct ifa6_config cfg = {
325062306a36Sopenharmony_ci		.pfx = addr,
325162306a36Sopenharmony_ci		.plen = 64,
325262306a36Sopenharmony_ci		.ifa_flags = flags | IFA_F_PERMANENT,
325362306a36Sopenharmony_ci		.valid_lft = INFINITY_LIFE_TIME,
325462306a36Sopenharmony_ci		.preferred_lft = INFINITY_LIFE_TIME,
325562306a36Sopenharmony_ci		.scope = IFA_LINK,
325662306a36Sopenharmony_ci		.ifa_proto = IFAPROT_KERNEL_LL
325762306a36Sopenharmony_ci	};
325862306a36Sopenharmony_ci	struct inet6_ifaddr *ifp;
325962306a36Sopenharmony_ci
326062306a36Sopenharmony_ci#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
326162306a36Sopenharmony_ci	if ((dev_net(idev->dev)->ipv6.devconf_all->optimistic_dad ||
326262306a36Sopenharmony_ci	     idev->cnf.optimistic_dad) &&
326362306a36Sopenharmony_ci	    !dev_net(idev->dev)->ipv6.devconf_all->forwarding)
326462306a36Sopenharmony_ci		cfg.ifa_flags |= IFA_F_OPTIMISTIC;
326562306a36Sopenharmony_ci#endif
326662306a36Sopenharmony_ci
326762306a36Sopenharmony_ci	ifp = ipv6_add_addr(idev, &cfg, true, NULL);
326862306a36Sopenharmony_ci	if (!IS_ERR(ifp)) {
326962306a36Sopenharmony_ci		addrconf_prefix_route(&ifp->addr, ifp->prefix_len, 0, idev->dev,
327062306a36Sopenharmony_ci				      0, 0, GFP_ATOMIC);
327162306a36Sopenharmony_ci		addrconf_dad_start(ifp);
327262306a36Sopenharmony_ci		in6_ifa_put(ifp);
327362306a36Sopenharmony_ci	}
327462306a36Sopenharmony_ci}
327562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(addrconf_add_linklocal);
327662306a36Sopenharmony_ci
327762306a36Sopenharmony_cistatic bool ipv6_reserved_interfaceid(struct in6_addr address)
327862306a36Sopenharmony_ci{
327962306a36Sopenharmony_ci	if ((address.s6_addr32[2] | address.s6_addr32[3]) == 0)
328062306a36Sopenharmony_ci		return true;
328162306a36Sopenharmony_ci
328262306a36Sopenharmony_ci	if (address.s6_addr32[2] == htonl(0x02005eff) &&
328362306a36Sopenharmony_ci	    ((address.s6_addr32[3] & htonl(0xfe000000)) == htonl(0xfe000000)))
328462306a36Sopenharmony_ci		return true;
328562306a36Sopenharmony_ci
328662306a36Sopenharmony_ci	if (address.s6_addr32[2] == htonl(0xfdffffff) &&
328762306a36Sopenharmony_ci	    ((address.s6_addr32[3] & htonl(0xffffff80)) == htonl(0xffffff80)))
328862306a36Sopenharmony_ci		return true;
328962306a36Sopenharmony_ci
329062306a36Sopenharmony_ci	return false;
329162306a36Sopenharmony_ci}
329262306a36Sopenharmony_ci
329362306a36Sopenharmony_cistatic int ipv6_generate_stable_address(struct in6_addr *address,
329462306a36Sopenharmony_ci					u8 dad_count,
329562306a36Sopenharmony_ci					const struct inet6_dev *idev)
329662306a36Sopenharmony_ci{
329762306a36Sopenharmony_ci	static DEFINE_SPINLOCK(lock);
329862306a36Sopenharmony_ci	static __u32 digest[SHA1_DIGEST_WORDS];
329962306a36Sopenharmony_ci	static __u32 workspace[SHA1_WORKSPACE_WORDS];
330062306a36Sopenharmony_ci
330162306a36Sopenharmony_ci	static union {
330262306a36Sopenharmony_ci		char __data[SHA1_BLOCK_SIZE];
330362306a36Sopenharmony_ci		struct {
330462306a36Sopenharmony_ci			struct in6_addr secret;
330562306a36Sopenharmony_ci			__be32 prefix[2];
330662306a36Sopenharmony_ci			unsigned char hwaddr[MAX_ADDR_LEN];
330762306a36Sopenharmony_ci			u8 dad_count;
330862306a36Sopenharmony_ci		} __packed;
330962306a36Sopenharmony_ci	} data;
331062306a36Sopenharmony_ci
331162306a36Sopenharmony_ci	struct in6_addr secret;
331262306a36Sopenharmony_ci	struct in6_addr temp;
331362306a36Sopenharmony_ci	struct net *net = dev_net(idev->dev);
331462306a36Sopenharmony_ci
331562306a36Sopenharmony_ci	BUILD_BUG_ON(sizeof(data.__data) != sizeof(data));
331662306a36Sopenharmony_ci
331762306a36Sopenharmony_ci	if (idev->cnf.stable_secret.initialized)
331862306a36Sopenharmony_ci		secret = idev->cnf.stable_secret.secret;
331962306a36Sopenharmony_ci	else if (net->ipv6.devconf_dflt->stable_secret.initialized)
332062306a36Sopenharmony_ci		secret = net->ipv6.devconf_dflt->stable_secret.secret;
332162306a36Sopenharmony_ci	else
332262306a36Sopenharmony_ci		return -1;
332362306a36Sopenharmony_ci
332462306a36Sopenharmony_ciretry:
332562306a36Sopenharmony_ci	spin_lock_bh(&lock);
332662306a36Sopenharmony_ci
332762306a36Sopenharmony_ci	sha1_init(digest);
332862306a36Sopenharmony_ci	memset(&data, 0, sizeof(data));
332962306a36Sopenharmony_ci	memset(workspace, 0, sizeof(workspace));
333062306a36Sopenharmony_ci	memcpy(data.hwaddr, idev->dev->perm_addr, idev->dev->addr_len);
333162306a36Sopenharmony_ci	data.prefix[0] = address->s6_addr32[0];
333262306a36Sopenharmony_ci	data.prefix[1] = address->s6_addr32[1];
333362306a36Sopenharmony_ci	data.secret = secret;
333462306a36Sopenharmony_ci	data.dad_count = dad_count;
333562306a36Sopenharmony_ci
333662306a36Sopenharmony_ci	sha1_transform(digest, data.__data, workspace);
333762306a36Sopenharmony_ci
333862306a36Sopenharmony_ci	temp = *address;
333962306a36Sopenharmony_ci	temp.s6_addr32[2] = (__force __be32)digest[0];
334062306a36Sopenharmony_ci	temp.s6_addr32[3] = (__force __be32)digest[1];
334162306a36Sopenharmony_ci
334262306a36Sopenharmony_ci	spin_unlock_bh(&lock);
334362306a36Sopenharmony_ci
334462306a36Sopenharmony_ci	if (ipv6_reserved_interfaceid(temp)) {
334562306a36Sopenharmony_ci		dad_count++;
334662306a36Sopenharmony_ci		if (dad_count > dev_net(idev->dev)->ipv6.sysctl.idgen_retries)
334762306a36Sopenharmony_ci			return -1;
334862306a36Sopenharmony_ci		goto retry;
334962306a36Sopenharmony_ci	}
335062306a36Sopenharmony_ci
335162306a36Sopenharmony_ci	*address = temp;
335262306a36Sopenharmony_ci	return 0;
335362306a36Sopenharmony_ci}
335462306a36Sopenharmony_ci
335562306a36Sopenharmony_cistatic void ipv6_gen_mode_random_init(struct inet6_dev *idev)
335662306a36Sopenharmony_ci{
335762306a36Sopenharmony_ci	struct ipv6_stable_secret *s = &idev->cnf.stable_secret;
335862306a36Sopenharmony_ci
335962306a36Sopenharmony_ci	if (s->initialized)
336062306a36Sopenharmony_ci		return;
336162306a36Sopenharmony_ci	s = &idev->cnf.stable_secret;
336262306a36Sopenharmony_ci	get_random_bytes(&s->secret, sizeof(s->secret));
336362306a36Sopenharmony_ci	s->initialized = true;
336462306a36Sopenharmony_ci}
336562306a36Sopenharmony_ci
336662306a36Sopenharmony_cistatic void addrconf_addr_gen(struct inet6_dev *idev, bool prefix_route)
336762306a36Sopenharmony_ci{
336862306a36Sopenharmony_ci	struct in6_addr addr;
336962306a36Sopenharmony_ci
337062306a36Sopenharmony_ci	/* no link local addresses on L3 master devices */
337162306a36Sopenharmony_ci	if (netif_is_l3_master(idev->dev))
337262306a36Sopenharmony_ci		return;
337362306a36Sopenharmony_ci
337462306a36Sopenharmony_ci	/* no link local addresses on devices flagged as slaves */
337562306a36Sopenharmony_ci	if (idev->dev->priv_flags & IFF_NO_ADDRCONF)
337662306a36Sopenharmony_ci		return;
337762306a36Sopenharmony_ci
337862306a36Sopenharmony_ci	ipv6_addr_set(&addr, htonl(0xFE800000), 0, 0, 0);
337962306a36Sopenharmony_ci
338062306a36Sopenharmony_ci	switch (idev->cnf.addr_gen_mode) {
338162306a36Sopenharmony_ci	case IN6_ADDR_GEN_MODE_RANDOM:
338262306a36Sopenharmony_ci		ipv6_gen_mode_random_init(idev);
338362306a36Sopenharmony_ci		fallthrough;
338462306a36Sopenharmony_ci	case IN6_ADDR_GEN_MODE_STABLE_PRIVACY:
338562306a36Sopenharmony_ci		if (!ipv6_generate_stable_address(&addr, 0, idev))
338662306a36Sopenharmony_ci			addrconf_add_linklocal(idev, &addr,
338762306a36Sopenharmony_ci					       IFA_F_STABLE_PRIVACY);
338862306a36Sopenharmony_ci		else if (prefix_route)
338962306a36Sopenharmony_ci			addrconf_prefix_route(&addr, 64, 0, idev->dev,
339062306a36Sopenharmony_ci					      0, 0, GFP_KERNEL);
339162306a36Sopenharmony_ci		break;
339262306a36Sopenharmony_ci	case IN6_ADDR_GEN_MODE_EUI64:
339362306a36Sopenharmony_ci		/* addrconf_add_linklocal also adds a prefix_route and we
339462306a36Sopenharmony_ci		 * only need to care about prefix routes if ipv6_generate_eui64
339562306a36Sopenharmony_ci		 * couldn't generate one.
339662306a36Sopenharmony_ci		 */
339762306a36Sopenharmony_ci		if (ipv6_generate_eui64(addr.s6_addr + 8, idev->dev) == 0)
339862306a36Sopenharmony_ci			addrconf_add_linklocal(idev, &addr, 0);
339962306a36Sopenharmony_ci		else if (prefix_route)
340062306a36Sopenharmony_ci			addrconf_prefix_route(&addr, 64, 0, idev->dev,
340162306a36Sopenharmony_ci					      0, 0, GFP_KERNEL);
340262306a36Sopenharmony_ci		break;
340362306a36Sopenharmony_ci	case IN6_ADDR_GEN_MODE_NONE:
340462306a36Sopenharmony_ci	default:
340562306a36Sopenharmony_ci		/* will not add any link local address */
340662306a36Sopenharmony_ci		break;
340762306a36Sopenharmony_ci	}
340862306a36Sopenharmony_ci}
340962306a36Sopenharmony_ci
341062306a36Sopenharmony_cistatic void addrconf_dev_config(struct net_device *dev)
341162306a36Sopenharmony_ci{
341262306a36Sopenharmony_ci	struct inet6_dev *idev;
341362306a36Sopenharmony_ci
341462306a36Sopenharmony_ci	ASSERT_RTNL();
341562306a36Sopenharmony_ci
341662306a36Sopenharmony_ci	if ((dev->type != ARPHRD_ETHER) &&
341762306a36Sopenharmony_ci	    (dev->type != ARPHRD_FDDI) &&
341862306a36Sopenharmony_ci	    (dev->type != ARPHRD_ARCNET) &&
341962306a36Sopenharmony_ci	    (dev->type != ARPHRD_INFINIBAND) &&
342062306a36Sopenharmony_ci	    (dev->type != ARPHRD_IEEE1394) &&
342162306a36Sopenharmony_ci	    (dev->type != ARPHRD_TUNNEL6) &&
342262306a36Sopenharmony_ci	    (dev->type != ARPHRD_6LOWPAN) &&
342362306a36Sopenharmony_ci	    (dev->type != ARPHRD_TUNNEL) &&
342462306a36Sopenharmony_ci	    (dev->type != ARPHRD_NONE) &&
342562306a36Sopenharmony_ci	    (dev->type != ARPHRD_RAWIP)) {
342662306a36Sopenharmony_ci		/* Alas, we support only Ethernet autoconfiguration. */
342762306a36Sopenharmony_ci		idev = __in6_dev_get(dev);
342862306a36Sopenharmony_ci		if (!IS_ERR_OR_NULL(idev) && dev->flags & IFF_UP &&
342962306a36Sopenharmony_ci		    dev->flags & IFF_MULTICAST)
343062306a36Sopenharmony_ci			ipv6_mc_up(idev);
343162306a36Sopenharmony_ci		return;
343262306a36Sopenharmony_ci	}
343362306a36Sopenharmony_ci
343462306a36Sopenharmony_ci	idev = addrconf_add_dev(dev);
343562306a36Sopenharmony_ci	if (IS_ERR(idev))
343662306a36Sopenharmony_ci		return;
343762306a36Sopenharmony_ci
343862306a36Sopenharmony_ci	/* this device type has no EUI support */
343962306a36Sopenharmony_ci	if (dev->type == ARPHRD_NONE &&
344062306a36Sopenharmony_ci	    idev->cnf.addr_gen_mode == IN6_ADDR_GEN_MODE_EUI64)
344162306a36Sopenharmony_ci		idev->cnf.addr_gen_mode = IN6_ADDR_GEN_MODE_RANDOM;
344262306a36Sopenharmony_ci
344362306a36Sopenharmony_ci	addrconf_addr_gen(idev, false);
344462306a36Sopenharmony_ci}
344562306a36Sopenharmony_ci
344662306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6_SIT)
344762306a36Sopenharmony_cistatic void addrconf_sit_config(struct net_device *dev)
344862306a36Sopenharmony_ci{
344962306a36Sopenharmony_ci	struct inet6_dev *idev;
345062306a36Sopenharmony_ci
345162306a36Sopenharmony_ci	ASSERT_RTNL();
345262306a36Sopenharmony_ci
345362306a36Sopenharmony_ci	/*
345462306a36Sopenharmony_ci	 * Configure the tunnel with one of our IPv4
345562306a36Sopenharmony_ci	 * addresses... we should configure all of
345662306a36Sopenharmony_ci	 * our v4 addrs in the tunnel
345762306a36Sopenharmony_ci	 */
345862306a36Sopenharmony_ci
345962306a36Sopenharmony_ci	idev = ipv6_find_idev(dev);
346062306a36Sopenharmony_ci	if (IS_ERR(idev)) {
346162306a36Sopenharmony_ci		pr_debug("%s: add_dev failed\n", __func__);
346262306a36Sopenharmony_ci		return;
346362306a36Sopenharmony_ci	}
346462306a36Sopenharmony_ci
346562306a36Sopenharmony_ci	if (dev->priv_flags & IFF_ISATAP) {
346662306a36Sopenharmony_ci		addrconf_addr_gen(idev, false);
346762306a36Sopenharmony_ci		return;
346862306a36Sopenharmony_ci	}
346962306a36Sopenharmony_ci
347062306a36Sopenharmony_ci	add_v4_addrs(idev);
347162306a36Sopenharmony_ci
347262306a36Sopenharmony_ci	if (dev->flags&IFF_POINTOPOINT)
347362306a36Sopenharmony_ci		addrconf_add_mroute(dev);
347462306a36Sopenharmony_ci}
347562306a36Sopenharmony_ci#endif
347662306a36Sopenharmony_ci
347762306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_NET_IPGRE) || IS_ENABLED(CONFIG_IPV6_GRE)
347862306a36Sopenharmony_cistatic void addrconf_gre_config(struct net_device *dev)
347962306a36Sopenharmony_ci{
348062306a36Sopenharmony_ci	struct inet6_dev *idev;
348162306a36Sopenharmony_ci
348262306a36Sopenharmony_ci	ASSERT_RTNL();
348362306a36Sopenharmony_ci
348462306a36Sopenharmony_ci	idev = ipv6_find_idev(dev);
348562306a36Sopenharmony_ci	if (IS_ERR(idev)) {
348662306a36Sopenharmony_ci		pr_debug("%s: add_dev failed\n", __func__);
348762306a36Sopenharmony_ci		return;
348862306a36Sopenharmony_ci	}
348962306a36Sopenharmony_ci
349062306a36Sopenharmony_ci	if (dev->type == ARPHRD_ETHER) {
349162306a36Sopenharmony_ci		addrconf_addr_gen(idev, true);
349262306a36Sopenharmony_ci		return;
349362306a36Sopenharmony_ci	}
349462306a36Sopenharmony_ci
349562306a36Sopenharmony_ci	add_v4_addrs(idev);
349662306a36Sopenharmony_ci
349762306a36Sopenharmony_ci	if (dev->flags & IFF_POINTOPOINT)
349862306a36Sopenharmony_ci		addrconf_add_mroute(dev);
349962306a36Sopenharmony_ci}
350062306a36Sopenharmony_ci#endif
350162306a36Sopenharmony_ci
350262306a36Sopenharmony_cistatic void addrconf_init_auto_addrs(struct net_device *dev)
350362306a36Sopenharmony_ci{
350462306a36Sopenharmony_ci	switch (dev->type) {
350562306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6_SIT)
350662306a36Sopenharmony_ci	case ARPHRD_SIT:
350762306a36Sopenharmony_ci		addrconf_sit_config(dev);
350862306a36Sopenharmony_ci		break;
350962306a36Sopenharmony_ci#endif
351062306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_NET_IPGRE) || IS_ENABLED(CONFIG_IPV6_GRE)
351162306a36Sopenharmony_ci	case ARPHRD_IP6GRE:
351262306a36Sopenharmony_ci	case ARPHRD_IPGRE:
351362306a36Sopenharmony_ci		addrconf_gre_config(dev);
351462306a36Sopenharmony_ci		break;
351562306a36Sopenharmony_ci#endif
351662306a36Sopenharmony_ci	case ARPHRD_LOOPBACK:
351762306a36Sopenharmony_ci		init_loopback(dev);
351862306a36Sopenharmony_ci		break;
351962306a36Sopenharmony_ci
352062306a36Sopenharmony_ci	default:
352162306a36Sopenharmony_ci		addrconf_dev_config(dev);
352262306a36Sopenharmony_ci		break;
352362306a36Sopenharmony_ci	}
352462306a36Sopenharmony_ci}
352562306a36Sopenharmony_ci
352662306a36Sopenharmony_cistatic int fixup_permanent_addr(struct net *net,
352762306a36Sopenharmony_ci				struct inet6_dev *idev,
352862306a36Sopenharmony_ci				struct inet6_ifaddr *ifp)
352962306a36Sopenharmony_ci{
353062306a36Sopenharmony_ci	/* !fib6_node means the host route was removed from the
353162306a36Sopenharmony_ci	 * FIB, for example, if 'lo' device is taken down. In that
353262306a36Sopenharmony_ci	 * case regenerate the host route.
353362306a36Sopenharmony_ci	 */
353462306a36Sopenharmony_ci	if (!ifp->rt || !ifp->rt->fib6_node) {
353562306a36Sopenharmony_ci		struct fib6_info *f6i, *prev;
353662306a36Sopenharmony_ci
353762306a36Sopenharmony_ci		f6i = addrconf_f6i_alloc(net, idev, &ifp->addr, false,
353862306a36Sopenharmony_ci					 GFP_ATOMIC, NULL);
353962306a36Sopenharmony_ci		if (IS_ERR(f6i))
354062306a36Sopenharmony_ci			return PTR_ERR(f6i);
354162306a36Sopenharmony_ci
354262306a36Sopenharmony_ci		/* ifp->rt can be accessed outside of rtnl */
354362306a36Sopenharmony_ci		spin_lock(&ifp->lock);
354462306a36Sopenharmony_ci		prev = ifp->rt;
354562306a36Sopenharmony_ci		ifp->rt = f6i;
354662306a36Sopenharmony_ci		spin_unlock(&ifp->lock);
354762306a36Sopenharmony_ci
354862306a36Sopenharmony_ci		fib6_info_release(prev);
354962306a36Sopenharmony_ci	}
355062306a36Sopenharmony_ci
355162306a36Sopenharmony_ci	if (!(ifp->flags & IFA_F_NOPREFIXROUTE)) {
355262306a36Sopenharmony_ci		addrconf_prefix_route(&ifp->addr, ifp->prefix_len,
355362306a36Sopenharmony_ci				      ifp->rt_priority, idev->dev, 0, 0,
355462306a36Sopenharmony_ci				      GFP_ATOMIC);
355562306a36Sopenharmony_ci	}
355662306a36Sopenharmony_ci
355762306a36Sopenharmony_ci	if (ifp->state == INET6_IFADDR_STATE_PREDAD)
355862306a36Sopenharmony_ci		addrconf_dad_start(ifp);
355962306a36Sopenharmony_ci
356062306a36Sopenharmony_ci	return 0;
356162306a36Sopenharmony_ci}
356262306a36Sopenharmony_ci
356362306a36Sopenharmony_cistatic void addrconf_permanent_addr(struct net *net, struct net_device *dev)
356462306a36Sopenharmony_ci{
356562306a36Sopenharmony_ci	struct inet6_ifaddr *ifp, *tmp;
356662306a36Sopenharmony_ci	struct inet6_dev *idev;
356762306a36Sopenharmony_ci
356862306a36Sopenharmony_ci	idev = __in6_dev_get(dev);
356962306a36Sopenharmony_ci	if (!idev)
357062306a36Sopenharmony_ci		return;
357162306a36Sopenharmony_ci
357262306a36Sopenharmony_ci	write_lock_bh(&idev->lock);
357362306a36Sopenharmony_ci
357462306a36Sopenharmony_ci	list_for_each_entry_safe(ifp, tmp, &idev->addr_list, if_list) {
357562306a36Sopenharmony_ci		if ((ifp->flags & IFA_F_PERMANENT) &&
357662306a36Sopenharmony_ci		    fixup_permanent_addr(net, idev, ifp) < 0) {
357762306a36Sopenharmony_ci			write_unlock_bh(&idev->lock);
357862306a36Sopenharmony_ci			in6_ifa_hold(ifp);
357962306a36Sopenharmony_ci			ipv6_del_addr(ifp);
358062306a36Sopenharmony_ci			write_lock_bh(&idev->lock);
358162306a36Sopenharmony_ci
358262306a36Sopenharmony_ci			net_info_ratelimited("%s: Failed to add prefix route for address %pI6c; dropping\n",
358362306a36Sopenharmony_ci					     idev->dev->name, &ifp->addr);
358462306a36Sopenharmony_ci		}
358562306a36Sopenharmony_ci	}
358662306a36Sopenharmony_ci
358762306a36Sopenharmony_ci	write_unlock_bh(&idev->lock);
358862306a36Sopenharmony_ci}
358962306a36Sopenharmony_ci
359062306a36Sopenharmony_cistatic int addrconf_notify(struct notifier_block *this, unsigned long event,
359162306a36Sopenharmony_ci			   void *ptr)
359262306a36Sopenharmony_ci{
359362306a36Sopenharmony_ci	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
359462306a36Sopenharmony_ci	struct netdev_notifier_change_info *change_info;
359562306a36Sopenharmony_ci	struct netdev_notifier_changeupper_info *info;
359662306a36Sopenharmony_ci	struct inet6_dev *idev = __in6_dev_get(dev);
359762306a36Sopenharmony_ci	struct net *net = dev_net(dev);
359862306a36Sopenharmony_ci	int run_pending = 0;
359962306a36Sopenharmony_ci	int err;
360062306a36Sopenharmony_ci
360162306a36Sopenharmony_ci	switch (event) {
360262306a36Sopenharmony_ci	case NETDEV_REGISTER:
360362306a36Sopenharmony_ci		if (!idev && dev->mtu >= IPV6_MIN_MTU) {
360462306a36Sopenharmony_ci			idev = ipv6_add_dev(dev);
360562306a36Sopenharmony_ci			if (IS_ERR(idev))
360662306a36Sopenharmony_ci				return notifier_from_errno(PTR_ERR(idev));
360762306a36Sopenharmony_ci		}
360862306a36Sopenharmony_ci		break;
360962306a36Sopenharmony_ci
361062306a36Sopenharmony_ci	case NETDEV_CHANGEMTU:
361162306a36Sopenharmony_ci		/* if MTU under IPV6_MIN_MTU stop IPv6 on this interface. */
361262306a36Sopenharmony_ci		if (dev->mtu < IPV6_MIN_MTU) {
361362306a36Sopenharmony_ci			addrconf_ifdown(dev, dev != net->loopback_dev);
361462306a36Sopenharmony_ci			break;
361562306a36Sopenharmony_ci		}
361662306a36Sopenharmony_ci
361762306a36Sopenharmony_ci		if (idev) {
361862306a36Sopenharmony_ci			rt6_mtu_change(dev, dev->mtu);
361962306a36Sopenharmony_ci			idev->cnf.mtu6 = dev->mtu;
362062306a36Sopenharmony_ci			break;
362162306a36Sopenharmony_ci		}
362262306a36Sopenharmony_ci
362362306a36Sopenharmony_ci		/* allocate new idev */
362462306a36Sopenharmony_ci		idev = ipv6_add_dev(dev);
362562306a36Sopenharmony_ci		if (IS_ERR(idev))
362662306a36Sopenharmony_ci			break;
362762306a36Sopenharmony_ci
362862306a36Sopenharmony_ci		/* device is still not ready */
362962306a36Sopenharmony_ci		if (!(idev->if_flags & IF_READY))
363062306a36Sopenharmony_ci			break;
363162306a36Sopenharmony_ci
363262306a36Sopenharmony_ci		run_pending = 1;
363362306a36Sopenharmony_ci		fallthrough;
363462306a36Sopenharmony_ci	case NETDEV_UP:
363562306a36Sopenharmony_ci	case NETDEV_CHANGE:
363662306a36Sopenharmony_ci		if (idev && idev->cnf.disable_ipv6)
363762306a36Sopenharmony_ci			break;
363862306a36Sopenharmony_ci
363962306a36Sopenharmony_ci		if (dev->priv_flags & IFF_NO_ADDRCONF) {
364062306a36Sopenharmony_ci			if (event == NETDEV_UP && !IS_ERR_OR_NULL(idev) &&
364162306a36Sopenharmony_ci			    dev->flags & IFF_UP && dev->flags & IFF_MULTICAST)
364262306a36Sopenharmony_ci				ipv6_mc_up(idev);
364362306a36Sopenharmony_ci			break;
364462306a36Sopenharmony_ci		}
364562306a36Sopenharmony_ci
364662306a36Sopenharmony_ci		if (event == NETDEV_UP) {
364762306a36Sopenharmony_ci			/* restore routes for permanent addresses */
364862306a36Sopenharmony_ci			addrconf_permanent_addr(net, dev);
364962306a36Sopenharmony_ci
365062306a36Sopenharmony_ci			if (!addrconf_link_ready(dev)) {
365162306a36Sopenharmony_ci				/* device is not ready yet. */
365262306a36Sopenharmony_ci				pr_debug("ADDRCONF(NETDEV_UP): %s: link is not ready\n",
365362306a36Sopenharmony_ci					 dev->name);
365462306a36Sopenharmony_ci				break;
365562306a36Sopenharmony_ci			}
365662306a36Sopenharmony_ci
365762306a36Sopenharmony_ci			if (!idev && dev->mtu >= IPV6_MIN_MTU)
365862306a36Sopenharmony_ci				idev = ipv6_add_dev(dev);
365962306a36Sopenharmony_ci
366062306a36Sopenharmony_ci			if (!IS_ERR_OR_NULL(idev)) {
366162306a36Sopenharmony_ci				idev->if_flags |= IF_READY;
366262306a36Sopenharmony_ci				run_pending = 1;
366362306a36Sopenharmony_ci			}
366462306a36Sopenharmony_ci		} else if (event == NETDEV_CHANGE) {
366562306a36Sopenharmony_ci			if (!addrconf_link_ready(dev)) {
366662306a36Sopenharmony_ci				/* device is still not ready. */
366762306a36Sopenharmony_ci				rt6_sync_down_dev(dev, event);
366862306a36Sopenharmony_ci				break;
366962306a36Sopenharmony_ci			}
367062306a36Sopenharmony_ci
367162306a36Sopenharmony_ci			if (!IS_ERR_OR_NULL(idev)) {
367262306a36Sopenharmony_ci				if (idev->if_flags & IF_READY) {
367362306a36Sopenharmony_ci					/* device is already configured -
367462306a36Sopenharmony_ci					 * but resend MLD reports, we might
367562306a36Sopenharmony_ci					 * have roamed and need to update
367662306a36Sopenharmony_ci					 * multicast snooping switches
367762306a36Sopenharmony_ci					 */
367862306a36Sopenharmony_ci					ipv6_mc_up(idev);
367962306a36Sopenharmony_ci					change_info = ptr;
368062306a36Sopenharmony_ci					if (change_info->flags_changed & IFF_NOARP)
368162306a36Sopenharmony_ci						addrconf_dad_run(idev, true);
368262306a36Sopenharmony_ci					rt6_sync_up(dev, RTNH_F_LINKDOWN);
368362306a36Sopenharmony_ci					break;
368462306a36Sopenharmony_ci				}
368562306a36Sopenharmony_ci				idev->if_flags |= IF_READY;
368662306a36Sopenharmony_ci			}
368762306a36Sopenharmony_ci
368862306a36Sopenharmony_ci			pr_debug("ADDRCONF(NETDEV_CHANGE): %s: link becomes ready\n",
368962306a36Sopenharmony_ci				 dev->name);
369062306a36Sopenharmony_ci
369162306a36Sopenharmony_ci			run_pending = 1;
369262306a36Sopenharmony_ci		}
369362306a36Sopenharmony_ci
369462306a36Sopenharmony_ci		addrconf_init_auto_addrs(dev);
369562306a36Sopenharmony_ci
369662306a36Sopenharmony_ci		if (!IS_ERR_OR_NULL(idev)) {
369762306a36Sopenharmony_ci			if (run_pending)
369862306a36Sopenharmony_ci				addrconf_dad_run(idev, false);
369962306a36Sopenharmony_ci
370062306a36Sopenharmony_ci			/* Device has an address by now */
370162306a36Sopenharmony_ci			rt6_sync_up(dev, RTNH_F_DEAD);
370262306a36Sopenharmony_ci
370362306a36Sopenharmony_ci			/*
370462306a36Sopenharmony_ci			 * If the MTU changed during the interface down,
370562306a36Sopenharmony_ci			 * when the interface up, the changed MTU must be
370662306a36Sopenharmony_ci			 * reflected in the idev as well as routers.
370762306a36Sopenharmony_ci			 */
370862306a36Sopenharmony_ci			if (idev->cnf.mtu6 != dev->mtu &&
370962306a36Sopenharmony_ci			    dev->mtu >= IPV6_MIN_MTU) {
371062306a36Sopenharmony_ci				rt6_mtu_change(dev, dev->mtu);
371162306a36Sopenharmony_ci				idev->cnf.mtu6 = dev->mtu;
371262306a36Sopenharmony_ci			}
371362306a36Sopenharmony_ci			idev->tstamp = jiffies;
371462306a36Sopenharmony_ci			inet6_ifinfo_notify(RTM_NEWLINK, idev);
371562306a36Sopenharmony_ci
371662306a36Sopenharmony_ci			/*
371762306a36Sopenharmony_ci			 * If the changed mtu during down is lower than
371862306a36Sopenharmony_ci			 * IPV6_MIN_MTU stop IPv6 on this interface.
371962306a36Sopenharmony_ci			 */
372062306a36Sopenharmony_ci			if (dev->mtu < IPV6_MIN_MTU)
372162306a36Sopenharmony_ci				addrconf_ifdown(dev, dev != net->loopback_dev);
372262306a36Sopenharmony_ci		}
372362306a36Sopenharmony_ci		break;
372462306a36Sopenharmony_ci
372562306a36Sopenharmony_ci	case NETDEV_DOWN:
372662306a36Sopenharmony_ci	case NETDEV_UNREGISTER:
372762306a36Sopenharmony_ci		/*
372862306a36Sopenharmony_ci		 *	Remove all addresses from this interface.
372962306a36Sopenharmony_ci		 */
373062306a36Sopenharmony_ci		addrconf_ifdown(dev, event != NETDEV_DOWN);
373162306a36Sopenharmony_ci		break;
373262306a36Sopenharmony_ci
373362306a36Sopenharmony_ci	case NETDEV_CHANGENAME:
373462306a36Sopenharmony_ci		if (idev) {
373562306a36Sopenharmony_ci			snmp6_unregister_dev(idev);
373662306a36Sopenharmony_ci			addrconf_sysctl_unregister(idev);
373762306a36Sopenharmony_ci			err = addrconf_sysctl_register(idev);
373862306a36Sopenharmony_ci			if (err)
373962306a36Sopenharmony_ci				return notifier_from_errno(err);
374062306a36Sopenharmony_ci			err = snmp6_register_dev(idev);
374162306a36Sopenharmony_ci			if (err) {
374262306a36Sopenharmony_ci				addrconf_sysctl_unregister(idev);
374362306a36Sopenharmony_ci				return notifier_from_errno(err);
374462306a36Sopenharmony_ci			}
374562306a36Sopenharmony_ci		}
374662306a36Sopenharmony_ci		break;
374762306a36Sopenharmony_ci
374862306a36Sopenharmony_ci	case NETDEV_PRE_TYPE_CHANGE:
374962306a36Sopenharmony_ci	case NETDEV_POST_TYPE_CHANGE:
375062306a36Sopenharmony_ci		if (idev)
375162306a36Sopenharmony_ci			addrconf_type_change(dev, event);
375262306a36Sopenharmony_ci		break;
375362306a36Sopenharmony_ci
375462306a36Sopenharmony_ci	case NETDEV_CHANGEUPPER:
375562306a36Sopenharmony_ci		info = ptr;
375662306a36Sopenharmony_ci
375762306a36Sopenharmony_ci		/* flush all routes if dev is linked to or unlinked from
375862306a36Sopenharmony_ci		 * an L3 master device (e.g., VRF)
375962306a36Sopenharmony_ci		 */
376062306a36Sopenharmony_ci		if (info->upper_dev && netif_is_l3_master(info->upper_dev))
376162306a36Sopenharmony_ci			addrconf_ifdown(dev, false);
376262306a36Sopenharmony_ci	}
376362306a36Sopenharmony_ci
376462306a36Sopenharmony_ci	return NOTIFY_OK;
376562306a36Sopenharmony_ci}
376662306a36Sopenharmony_ci
376762306a36Sopenharmony_ci/*
376862306a36Sopenharmony_ci *	addrconf module should be notified of a device going up
376962306a36Sopenharmony_ci */
377062306a36Sopenharmony_cistatic struct notifier_block ipv6_dev_notf = {
377162306a36Sopenharmony_ci	.notifier_call = addrconf_notify,
377262306a36Sopenharmony_ci	.priority = ADDRCONF_NOTIFY_PRIORITY,
377362306a36Sopenharmony_ci};
377462306a36Sopenharmony_ci
377562306a36Sopenharmony_cistatic void addrconf_type_change(struct net_device *dev, unsigned long event)
377662306a36Sopenharmony_ci{
377762306a36Sopenharmony_ci	struct inet6_dev *idev;
377862306a36Sopenharmony_ci	ASSERT_RTNL();
377962306a36Sopenharmony_ci
378062306a36Sopenharmony_ci	idev = __in6_dev_get(dev);
378162306a36Sopenharmony_ci
378262306a36Sopenharmony_ci	if (event == NETDEV_POST_TYPE_CHANGE)
378362306a36Sopenharmony_ci		ipv6_mc_remap(idev);
378462306a36Sopenharmony_ci	else if (event == NETDEV_PRE_TYPE_CHANGE)
378562306a36Sopenharmony_ci		ipv6_mc_unmap(idev);
378662306a36Sopenharmony_ci}
378762306a36Sopenharmony_ci
378862306a36Sopenharmony_cistatic bool addr_is_local(const struct in6_addr *addr)
378962306a36Sopenharmony_ci{
379062306a36Sopenharmony_ci	return ipv6_addr_type(addr) &
379162306a36Sopenharmony_ci		(IPV6_ADDR_LINKLOCAL | IPV6_ADDR_LOOPBACK);
379262306a36Sopenharmony_ci}
379362306a36Sopenharmony_ci
379462306a36Sopenharmony_cistatic int addrconf_ifdown(struct net_device *dev, bool unregister)
379562306a36Sopenharmony_ci{
379662306a36Sopenharmony_ci	unsigned long event = unregister ? NETDEV_UNREGISTER : NETDEV_DOWN;
379762306a36Sopenharmony_ci	struct net *net = dev_net(dev);
379862306a36Sopenharmony_ci	struct inet6_dev *idev;
379962306a36Sopenharmony_ci	struct inet6_ifaddr *ifa;
380062306a36Sopenharmony_ci	LIST_HEAD(tmp_addr_list);
380162306a36Sopenharmony_ci	bool keep_addr = false;
380262306a36Sopenharmony_ci	bool was_ready;
380362306a36Sopenharmony_ci	int state, i;
380462306a36Sopenharmony_ci
380562306a36Sopenharmony_ci	ASSERT_RTNL();
380662306a36Sopenharmony_ci
380762306a36Sopenharmony_ci	rt6_disable_ip(dev, event);
380862306a36Sopenharmony_ci
380962306a36Sopenharmony_ci	idev = __in6_dev_get(dev);
381062306a36Sopenharmony_ci	if (!idev)
381162306a36Sopenharmony_ci		return -ENODEV;
381262306a36Sopenharmony_ci
381362306a36Sopenharmony_ci	/*
381462306a36Sopenharmony_ci	 * Step 1: remove reference to ipv6 device from parent device.
381562306a36Sopenharmony_ci	 *	   Do not dev_put!
381662306a36Sopenharmony_ci	 */
381762306a36Sopenharmony_ci	if (unregister) {
381862306a36Sopenharmony_ci		idev->dead = 1;
381962306a36Sopenharmony_ci
382062306a36Sopenharmony_ci		/* protected by rtnl_lock */
382162306a36Sopenharmony_ci		RCU_INIT_POINTER(dev->ip6_ptr, NULL);
382262306a36Sopenharmony_ci
382362306a36Sopenharmony_ci		/* Step 1.5: remove snmp6 entry */
382462306a36Sopenharmony_ci		snmp6_unregister_dev(idev);
382562306a36Sopenharmony_ci
382662306a36Sopenharmony_ci	}
382762306a36Sopenharmony_ci
382862306a36Sopenharmony_ci	/* combine the user config with event to determine if permanent
382962306a36Sopenharmony_ci	 * addresses are to be removed from address hash table
383062306a36Sopenharmony_ci	 */
383162306a36Sopenharmony_ci	if (!unregister && !idev->cnf.disable_ipv6) {
383262306a36Sopenharmony_ci		/* aggregate the system setting and interface setting */
383362306a36Sopenharmony_ci		int _keep_addr = net->ipv6.devconf_all->keep_addr_on_down;
383462306a36Sopenharmony_ci
383562306a36Sopenharmony_ci		if (!_keep_addr)
383662306a36Sopenharmony_ci			_keep_addr = idev->cnf.keep_addr_on_down;
383762306a36Sopenharmony_ci
383862306a36Sopenharmony_ci		keep_addr = (_keep_addr > 0);
383962306a36Sopenharmony_ci	}
384062306a36Sopenharmony_ci
384162306a36Sopenharmony_ci	/* Step 2: clear hash table */
384262306a36Sopenharmony_ci	for (i = 0; i < IN6_ADDR_HSIZE; i++) {
384362306a36Sopenharmony_ci		struct hlist_head *h = &net->ipv6.inet6_addr_lst[i];
384462306a36Sopenharmony_ci
384562306a36Sopenharmony_ci		spin_lock_bh(&net->ipv6.addrconf_hash_lock);
384662306a36Sopenharmony_cirestart:
384762306a36Sopenharmony_ci		hlist_for_each_entry_rcu(ifa, h, addr_lst) {
384862306a36Sopenharmony_ci			if (ifa->idev == idev) {
384962306a36Sopenharmony_ci				addrconf_del_dad_work(ifa);
385062306a36Sopenharmony_ci				/* combined flag + permanent flag decide if
385162306a36Sopenharmony_ci				 * address is retained on a down event
385262306a36Sopenharmony_ci				 */
385362306a36Sopenharmony_ci				if (!keep_addr ||
385462306a36Sopenharmony_ci				    !(ifa->flags & IFA_F_PERMANENT) ||
385562306a36Sopenharmony_ci				    addr_is_local(&ifa->addr)) {
385662306a36Sopenharmony_ci					hlist_del_init_rcu(&ifa->addr_lst);
385762306a36Sopenharmony_ci					goto restart;
385862306a36Sopenharmony_ci				}
385962306a36Sopenharmony_ci			}
386062306a36Sopenharmony_ci		}
386162306a36Sopenharmony_ci		spin_unlock_bh(&net->ipv6.addrconf_hash_lock);
386262306a36Sopenharmony_ci	}
386362306a36Sopenharmony_ci
386462306a36Sopenharmony_ci	write_lock_bh(&idev->lock);
386562306a36Sopenharmony_ci
386662306a36Sopenharmony_ci	addrconf_del_rs_timer(idev);
386762306a36Sopenharmony_ci
386862306a36Sopenharmony_ci	/* Step 2: clear flags for stateless addrconf, repeated down
386962306a36Sopenharmony_ci	 *         detection
387062306a36Sopenharmony_ci	 */
387162306a36Sopenharmony_ci	was_ready = idev->if_flags & IF_READY;
387262306a36Sopenharmony_ci	if (!unregister)
387362306a36Sopenharmony_ci		idev->if_flags &= ~(IF_RS_SENT|IF_RA_RCVD|IF_READY);
387462306a36Sopenharmony_ci
387562306a36Sopenharmony_ci	/* Step 3: clear tempaddr list */
387662306a36Sopenharmony_ci	while (!list_empty(&idev->tempaddr_list)) {
387762306a36Sopenharmony_ci		ifa = list_first_entry(&idev->tempaddr_list,
387862306a36Sopenharmony_ci				       struct inet6_ifaddr, tmp_list);
387962306a36Sopenharmony_ci		list_del(&ifa->tmp_list);
388062306a36Sopenharmony_ci		write_unlock_bh(&idev->lock);
388162306a36Sopenharmony_ci		spin_lock_bh(&ifa->lock);
388262306a36Sopenharmony_ci
388362306a36Sopenharmony_ci		if (ifa->ifpub) {
388462306a36Sopenharmony_ci			in6_ifa_put(ifa->ifpub);
388562306a36Sopenharmony_ci			ifa->ifpub = NULL;
388662306a36Sopenharmony_ci		}
388762306a36Sopenharmony_ci		spin_unlock_bh(&ifa->lock);
388862306a36Sopenharmony_ci		in6_ifa_put(ifa);
388962306a36Sopenharmony_ci		write_lock_bh(&idev->lock);
389062306a36Sopenharmony_ci	}
389162306a36Sopenharmony_ci
389262306a36Sopenharmony_ci	list_for_each_entry(ifa, &idev->addr_list, if_list)
389362306a36Sopenharmony_ci		list_add_tail(&ifa->if_list_aux, &tmp_addr_list);
389462306a36Sopenharmony_ci	write_unlock_bh(&idev->lock);
389562306a36Sopenharmony_ci
389662306a36Sopenharmony_ci	while (!list_empty(&tmp_addr_list)) {
389762306a36Sopenharmony_ci		struct fib6_info *rt = NULL;
389862306a36Sopenharmony_ci		bool keep;
389962306a36Sopenharmony_ci
390062306a36Sopenharmony_ci		ifa = list_first_entry(&tmp_addr_list,
390162306a36Sopenharmony_ci				       struct inet6_ifaddr, if_list_aux);
390262306a36Sopenharmony_ci		list_del(&ifa->if_list_aux);
390362306a36Sopenharmony_ci
390462306a36Sopenharmony_ci		addrconf_del_dad_work(ifa);
390562306a36Sopenharmony_ci
390662306a36Sopenharmony_ci		keep = keep_addr && (ifa->flags & IFA_F_PERMANENT) &&
390762306a36Sopenharmony_ci			!addr_is_local(&ifa->addr);
390862306a36Sopenharmony_ci
390962306a36Sopenharmony_ci		spin_lock_bh(&ifa->lock);
391062306a36Sopenharmony_ci
391162306a36Sopenharmony_ci		if (keep) {
391262306a36Sopenharmony_ci			/* set state to skip the notifier below */
391362306a36Sopenharmony_ci			state = INET6_IFADDR_STATE_DEAD;
391462306a36Sopenharmony_ci			ifa->state = INET6_IFADDR_STATE_PREDAD;
391562306a36Sopenharmony_ci			if (!(ifa->flags & IFA_F_NODAD))
391662306a36Sopenharmony_ci				ifa->flags |= IFA_F_TENTATIVE;
391762306a36Sopenharmony_ci
391862306a36Sopenharmony_ci			rt = ifa->rt;
391962306a36Sopenharmony_ci			ifa->rt = NULL;
392062306a36Sopenharmony_ci		} else {
392162306a36Sopenharmony_ci			state = ifa->state;
392262306a36Sopenharmony_ci			ifa->state = INET6_IFADDR_STATE_DEAD;
392362306a36Sopenharmony_ci		}
392462306a36Sopenharmony_ci
392562306a36Sopenharmony_ci		spin_unlock_bh(&ifa->lock);
392662306a36Sopenharmony_ci
392762306a36Sopenharmony_ci		if (rt)
392862306a36Sopenharmony_ci			ip6_del_rt(net, rt, false);
392962306a36Sopenharmony_ci
393062306a36Sopenharmony_ci		if (state != INET6_IFADDR_STATE_DEAD) {
393162306a36Sopenharmony_ci			__ipv6_ifa_notify(RTM_DELADDR, ifa);
393262306a36Sopenharmony_ci			inet6addr_notifier_call_chain(NETDEV_DOWN, ifa);
393362306a36Sopenharmony_ci		} else {
393462306a36Sopenharmony_ci			if (idev->cnf.forwarding)
393562306a36Sopenharmony_ci				addrconf_leave_anycast(ifa);
393662306a36Sopenharmony_ci			addrconf_leave_solict(ifa->idev, &ifa->addr);
393762306a36Sopenharmony_ci		}
393862306a36Sopenharmony_ci
393962306a36Sopenharmony_ci		if (!keep) {
394062306a36Sopenharmony_ci			write_lock_bh(&idev->lock);
394162306a36Sopenharmony_ci			list_del_rcu(&ifa->if_list);
394262306a36Sopenharmony_ci			write_unlock_bh(&idev->lock);
394362306a36Sopenharmony_ci			in6_ifa_put(ifa);
394462306a36Sopenharmony_ci		}
394562306a36Sopenharmony_ci	}
394662306a36Sopenharmony_ci
394762306a36Sopenharmony_ci	/* Step 5: Discard anycast and multicast list */
394862306a36Sopenharmony_ci	if (unregister) {
394962306a36Sopenharmony_ci		ipv6_ac_destroy_dev(idev);
395062306a36Sopenharmony_ci		ipv6_mc_destroy_dev(idev);
395162306a36Sopenharmony_ci	} else if (was_ready) {
395262306a36Sopenharmony_ci		ipv6_mc_down(idev);
395362306a36Sopenharmony_ci	}
395462306a36Sopenharmony_ci
395562306a36Sopenharmony_ci	idev->tstamp = jiffies;
395662306a36Sopenharmony_ci	idev->ra_mtu = 0;
395762306a36Sopenharmony_ci
395862306a36Sopenharmony_ci	/* Last: Shot the device (if unregistered) */
395962306a36Sopenharmony_ci	if (unregister) {
396062306a36Sopenharmony_ci		addrconf_sysctl_unregister(idev);
396162306a36Sopenharmony_ci		neigh_parms_release(&nd_tbl, idev->nd_parms);
396262306a36Sopenharmony_ci		neigh_ifdown(&nd_tbl, dev);
396362306a36Sopenharmony_ci		in6_dev_put(idev);
396462306a36Sopenharmony_ci	}
396562306a36Sopenharmony_ci	return 0;
396662306a36Sopenharmony_ci}
396762306a36Sopenharmony_ci
396862306a36Sopenharmony_cistatic void addrconf_rs_timer(struct timer_list *t)
396962306a36Sopenharmony_ci{
397062306a36Sopenharmony_ci	struct inet6_dev *idev = from_timer(idev, t, rs_timer);
397162306a36Sopenharmony_ci	struct net_device *dev = idev->dev;
397262306a36Sopenharmony_ci	struct in6_addr lladdr;
397362306a36Sopenharmony_ci
397462306a36Sopenharmony_ci	write_lock(&idev->lock);
397562306a36Sopenharmony_ci	if (idev->dead || !(idev->if_flags & IF_READY))
397662306a36Sopenharmony_ci		goto out;
397762306a36Sopenharmony_ci
397862306a36Sopenharmony_ci	if (!ipv6_accept_ra(idev))
397962306a36Sopenharmony_ci		goto out;
398062306a36Sopenharmony_ci
398162306a36Sopenharmony_ci	/* Announcement received after solicitation was sent */
398262306a36Sopenharmony_ci	if (idev->if_flags & IF_RA_RCVD)
398362306a36Sopenharmony_ci		goto out;
398462306a36Sopenharmony_ci
398562306a36Sopenharmony_ci	if (idev->rs_probes++ < idev->cnf.rtr_solicits || idev->cnf.rtr_solicits < 0) {
398662306a36Sopenharmony_ci		write_unlock(&idev->lock);
398762306a36Sopenharmony_ci		if (!ipv6_get_lladdr(dev, &lladdr, IFA_F_TENTATIVE))
398862306a36Sopenharmony_ci			ndisc_send_rs(dev, &lladdr,
398962306a36Sopenharmony_ci				      &in6addr_linklocal_allrouters);
399062306a36Sopenharmony_ci		else
399162306a36Sopenharmony_ci			goto put;
399262306a36Sopenharmony_ci
399362306a36Sopenharmony_ci		write_lock(&idev->lock);
399462306a36Sopenharmony_ci		idev->rs_interval = rfc3315_s14_backoff_update(
399562306a36Sopenharmony_ci			idev->rs_interval, idev->cnf.rtr_solicit_max_interval);
399662306a36Sopenharmony_ci		/* The wait after the last probe can be shorter */
399762306a36Sopenharmony_ci		addrconf_mod_rs_timer(idev, (idev->rs_probes ==
399862306a36Sopenharmony_ci					     idev->cnf.rtr_solicits) ?
399962306a36Sopenharmony_ci				      idev->cnf.rtr_solicit_delay :
400062306a36Sopenharmony_ci				      idev->rs_interval);
400162306a36Sopenharmony_ci	} else {
400262306a36Sopenharmony_ci		/*
400362306a36Sopenharmony_ci		 * Note: we do not support deprecated "all on-link"
400462306a36Sopenharmony_ci		 * assumption any longer.
400562306a36Sopenharmony_ci		 */
400662306a36Sopenharmony_ci		pr_debug("%s: no IPv6 routers present\n", idev->dev->name);
400762306a36Sopenharmony_ci	}
400862306a36Sopenharmony_ci
400962306a36Sopenharmony_ciout:
401062306a36Sopenharmony_ci	write_unlock(&idev->lock);
401162306a36Sopenharmony_ciput:
401262306a36Sopenharmony_ci	in6_dev_put(idev);
401362306a36Sopenharmony_ci}
401462306a36Sopenharmony_ci
401562306a36Sopenharmony_ci/*
401662306a36Sopenharmony_ci *	Duplicate Address Detection
401762306a36Sopenharmony_ci */
401862306a36Sopenharmony_cistatic void addrconf_dad_kick(struct inet6_ifaddr *ifp)
401962306a36Sopenharmony_ci{
402062306a36Sopenharmony_ci	unsigned long rand_num;
402162306a36Sopenharmony_ci	struct inet6_dev *idev = ifp->idev;
402262306a36Sopenharmony_ci	u64 nonce;
402362306a36Sopenharmony_ci
402462306a36Sopenharmony_ci	if (ifp->flags & IFA_F_OPTIMISTIC)
402562306a36Sopenharmony_ci		rand_num = 0;
402662306a36Sopenharmony_ci	else
402762306a36Sopenharmony_ci		rand_num = get_random_u32_below(idev->cnf.rtr_solicit_delay ? : 1);
402862306a36Sopenharmony_ci
402962306a36Sopenharmony_ci	nonce = 0;
403062306a36Sopenharmony_ci	if (idev->cnf.enhanced_dad ||
403162306a36Sopenharmony_ci	    dev_net(idev->dev)->ipv6.devconf_all->enhanced_dad) {
403262306a36Sopenharmony_ci		do
403362306a36Sopenharmony_ci			get_random_bytes(&nonce, 6);
403462306a36Sopenharmony_ci		while (nonce == 0);
403562306a36Sopenharmony_ci	}
403662306a36Sopenharmony_ci	ifp->dad_nonce = nonce;
403762306a36Sopenharmony_ci	ifp->dad_probes = idev->cnf.dad_transmits;
403862306a36Sopenharmony_ci	addrconf_mod_dad_work(ifp, rand_num);
403962306a36Sopenharmony_ci}
404062306a36Sopenharmony_ci
404162306a36Sopenharmony_cistatic void addrconf_dad_begin(struct inet6_ifaddr *ifp)
404262306a36Sopenharmony_ci{
404362306a36Sopenharmony_ci	struct inet6_dev *idev = ifp->idev;
404462306a36Sopenharmony_ci	struct net_device *dev = idev->dev;
404562306a36Sopenharmony_ci	bool bump_id, notify = false;
404662306a36Sopenharmony_ci	struct net *net;
404762306a36Sopenharmony_ci
404862306a36Sopenharmony_ci	addrconf_join_solict(dev, &ifp->addr);
404962306a36Sopenharmony_ci
405062306a36Sopenharmony_ci	read_lock_bh(&idev->lock);
405162306a36Sopenharmony_ci	spin_lock(&ifp->lock);
405262306a36Sopenharmony_ci	if (ifp->state == INET6_IFADDR_STATE_DEAD)
405362306a36Sopenharmony_ci		goto out;
405462306a36Sopenharmony_ci
405562306a36Sopenharmony_ci	net = dev_net(dev);
405662306a36Sopenharmony_ci	if (dev->flags&(IFF_NOARP|IFF_LOOPBACK) ||
405762306a36Sopenharmony_ci	    (net->ipv6.devconf_all->accept_dad < 1 &&
405862306a36Sopenharmony_ci	     idev->cnf.accept_dad < 1) ||
405962306a36Sopenharmony_ci	    !(ifp->flags&IFA_F_TENTATIVE) ||
406062306a36Sopenharmony_ci	    ifp->flags & IFA_F_NODAD) {
406162306a36Sopenharmony_ci		bool send_na = false;
406262306a36Sopenharmony_ci
406362306a36Sopenharmony_ci		if (ifp->flags & IFA_F_TENTATIVE &&
406462306a36Sopenharmony_ci		    !(ifp->flags & IFA_F_OPTIMISTIC))
406562306a36Sopenharmony_ci			send_na = true;
406662306a36Sopenharmony_ci		bump_id = ifp->flags & IFA_F_TENTATIVE;
406762306a36Sopenharmony_ci		ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC|IFA_F_DADFAILED);
406862306a36Sopenharmony_ci		spin_unlock(&ifp->lock);
406962306a36Sopenharmony_ci		read_unlock_bh(&idev->lock);
407062306a36Sopenharmony_ci
407162306a36Sopenharmony_ci		addrconf_dad_completed(ifp, bump_id, send_na);
407262306a36Sopenharmony_ci		return;
407362306a36Sopenharmony_ci	}
407462306a36Sopenharmony_ci
407562306a36Sopenharmony_ci	if (!(idev->if_flags & IF_READY)) {
407662306a36Sopenharmony_ci		spin_unlock(&ifp->lock);
407762306a36Sopenharmony_ci		read_unlock_bh(&idev->lock);
407862306a36Sopenharmony_ci		/*
407962306a36Sopenharmony_ci		 * If the device is not ready:
408062306a36Sopenharmony_ci		 * - keep it tentative if it is a permanent address.
408162306a36Sopenharmony_ci		 * - otherwise, kill it.
408262306a36Sopenharmony_ci		 */
408362306a36Sopenharmony_ci		in6_ifa_hold(ifp);
408462306a36Sopenharmony_ci		addrconf_dad_stop(ifp, 0);
408562306a36Sopenharmony_ci		return;
408662306a36Sopenharmony_ci	}
408762306a36Sopenharmony_ci
408862306a36Sopenharmony_ci	/*
408962306a36Sopenharmony_ci	 * Optimistic nodes can start receiving
409062306a36Sopenharmony_ci	 * Frames right away
409162306a36Sopenharmony_ci	 */
409262306a36Sopenharmony_ci	if (ifp->flags & IFA_F_OPTIMISTIC) {
409362306a36Sopenharmony_ci		ip6_ins_rt(net, ifp->rt);
409462306a36Sopenharmony_ci		if (ipv6_use_optimistic_addr(net, idev)) {
409562306a36Sopenharmony_ci			/* Because optimistic nodes can use this address,
409662306a36Sopenharmony_ci			 * notify listeners. If DAD fails, RTM_DELADDR is sent.
409762306a36Sopenharmony_ci			 */
409862306a36Sopenharmony_ci			notify = true;
409962306a36Sopenharmony_ci		}
410062306a36Sopenharmony_ci	}
410162306a36Sopenharmony_ci
410262306a36Sopenharmony_ci	addrconf_dad_kick(ifp);
410362306a36Sopenharmony_ciout:
410462306a36Sopenharmony_ci	spin_unlock(&ifp->lock);
410562306a36Sopenharmony_ci	read_unlock_bh(&idev->lock);
410662306a36Sopenharmony_ci	if (notify)
410762306a36Sopenharmony_ci		ipv6_ifa_notify(RTM_NEWADDR, ifp);
410862306a36Sopenharmony_ci}
410962306a36Sopenharmony_ci
411062306a36Sopenharmony_cistatic void addrconf_dad_start(struct inet6_ifaddr *ifp)
411162306a36Sopenharmony_ci{
411262306a36Sopenharmony_ci	bool begin_dad = false;
411362306a36Sopenharmony_ci
411462306a36Sopenharmony_ci	spin_lock_bh(&ifp->lock);
411562306a36Sopenharmony_ci	if (ifp->state != INET6_IFADDR_STATE_DEAD) {
411662306a36Sopenharmony_ci		ifp->state = INET6_IFADDR_STATE_PREDAD;
411762306a36Sopenharmony_ci		begin_dad = true;
411862306a36Sopenharmony_ci	}
411962306a36Sopenharmony_ci	spin_unlock_bh(&ifp->lock);
412062306a36Sopenharmony_ci
412162306a36Sopenharmony_ci	if (begin_dad)
412262306a36Sopenharmony_ci		addrconf_mod_dad_work(ifp, 0);
412362306a36Sopenharmony_ci}
412462306a36Sopenharmony_ci
412562306a36Sopenharmony_cistatic void addrconf_dad_work(struct work_struct *w)
412662306a36Sopenharmony_ci{
412762306a36Sopenharmony_ci	struct inet6_ifaddr *ifp = container_of(to_delayed_work(w),
412862306a36Sopenharmony_ci						struct inet6_ifaddr,
412962306a36Sopenharmony_ci						dad_work);
413062306a36Sopenharmony_ci	struct inet6_dev *idev = ifp->idev;
413162306a36Sopenharmony_ci	bool bump_id, disable_ipv6 = false;
413262306a36Sopenharmony_ci	struct in6_addr mcaddr;
413362306a36Sopenharmony_ci
413462306a36Sopenharmony_ci	enum {
413562306a36Sopenharmony_ci		DAD_PROCESS,
413662306a36Sopenharmony_ci		DAD_BEGIN,
413762306a36Sopenharmony_ci		DAD_ABORT,
413862306a36Sopenharmony_ci	} action = DAD_PROCESS;
413962306a36Sopenharmony_ci
414062306a36Sopenharmony_ci	rtnl_lock();
414162306a36Sopenharmony_ci
414262306a36Sopenharmony_ci	spin_lock_bh(&ifp->lock);
414362306a36Sopenharmony_ci	if (ifp->state == INET6_IFADDR_STATE_PREDAD) {
414462306a36Sopenharmony_ci		action = DAD_BEGIN;
414562306a36Sopenharmony_ci		ifp->state = INET6_IFADDR_STATE_DAD;
414662306a36Sopenharmony_ci	} else if (ifp->state == INET6_IFADDR_STATE_ERRDAD) {
414762306a36Sopenharmony_ci		action = DAD_ABORT;
414862306a36Sopenharmony_ci		ifp->state = INET6_IFADDR_STATE_POSTDAD;
414962306a36Sopenharmony_ci
415062306a36Sopenharmony_ci		if ((dev_net(idev->dev)->ipv6.devconf_all->accept_dad > 1 ||
415162306a36Sopenharmony_ci		     idev->cnf.accept_dad > 1) &&
415262306a36Sopenharmony_ci		    !idev->cnf.disable_ipv6 &&
415362306a36Sopenharmony_ci		    !(ifp->flags & IFA_F_STABLE_PRIVACY)) {
415462306a36Sopenharmony_ci			struct in6_addr addr;
415562306a36Sopenharmony_ci
415662306a36Sopenharmony_ci			addr.s6_addr32[0] = htonl(0xfe800000);
415762306a36Sopenharmony_ci			addr.s6_addr32[1] = 0;
415862306a36Sopenharmony_ci
415962306a36Sopenharmony_ci			if (!ipv6_generate_eui64(addr.s6_addr + 8, idev->dev) &&
416062306a36Sopenharmony_ci			    ipv6_addr_equal(&ifp->addr, &addr)) {
416162306a36Sopenharmony_ci				/* DAD failed for link-local based on MAC */
416262306a36Sopenharmony_ci				idev->cnf.disable_ipv6 = 1;
416362306a36Sopenharmony_ci
416462306a36Sopenharmony_ci				pr_info("%s: IPv6 being disabled!\n",
416562306a36Sopenharmony_ci					ifp->idev->dev->name);
416662306a36Sopenharmony_ci				disable_ipv6 = true;
416762306a36Sopenharmony_ci			}
416862306a36Sopenharmony_ci		}
416962306a36Sopenharmony_ci	}
417062306a36Sopenharmony_ci	spin_unlock_bh(&ifp->lock);
417162306a36Sopenharmony_ci
417262306a36Sopenharmony_ci	if (action == DAD_BEGIN) {
417362306a36Sopenharmony_ci		addrconf_dad_begin(ifp);
417462306a36Sopenharmony_ci		goto out;
417562306a36Sopenharmony_ci	} else if (action == DAD_ABORT) {
417662306a36Sopenharmony_ci		in6_ifa_hold(ifp);
417762306a36Sopenharmony_ci		addrconf_dad_stop(ifp, 1);
417862306a36Sopenharmony_ci		if (disable_ipv6)
417962306a36Sopenharmony_ci			addrconf_ifdown(idev->dev, false);
418062306a36Sopenharmony_ci		goto out;
418162306a36Sopenharmony_ci	}
418262306a36Sopenharmony_ci
418362306a36Sopenharmony_ci	if (!ifp->dad_probes && addrconf_dad_end(ifp))
418462306a36Sopenharmony_ci		goto out;
418562306a36Sopenharmony_ci
418662306a36Sopenharmony_ci	write_lock_bh(&idev->lock);
418762306a36Sopenharmony_ci	if (idev->dead || !(idev->if_flags & IF_READY)) {
418862306a36Sopenharmony_ci		write_unlock_bh(&idev->lock);
418962306a36Sopenharmony_ci		goto out;
419062306a36Sopenharmony_ci	}
419162306a36Sopenharmony_ci
419262306a36Sopenharmony_ci	spin_lock(&ifp->lock);
419362306a36Sopenharmony_ci	if (ifp->state == INET6_IFADDR_STATE_DEAD) {
419462306a36Sopenharmony_ci		spin_unlock(&ifp->lock);
419562306a36Sopenharmony_ci		write_unlock_bh(&idev->lock);
419662306a36Sopenharmony_ci		goto out;
419762306a36Sopenharmony_ci	}
419862306a36Sopenharmony_ci
419962306a36Sopenharmony_ci	if (ifp->dad_probes == 0) {
420062306a36Sopenharmony_ci		bool send_na = false;
420162306a36Sopenharmony_ci
420262306a36Sopenharmony_ci		/*
420362306a36Sopenharmony_ci		 * DAD was successful
420462306a36Sopenharmony_ci		 */
420562306a36Sopenharmony_ci
420662306a36Sopenharmony_ci		if (ifp->flags & IFA_F_TENTATIVE &&
420762306a36Sopenharmony_ci		    !(ifp->flags & IFA_F_OPTIMISTIC))
420862306a36Sopenharmony_ci			send_na = true;
420962306a36Sopenharmony_ci		bump_id = ifp->flags & IFA_F_TENTATIVE;
421062306a36Sopenharmony_ci		ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC|IFA_F_DADFAILED);
421162306a36Sopenharmony_ci		spin_unlock(&ifp->lock);
421262306a36Sopenharmony_ci		write_unlock_bh(&idev->lock);
421362306a36Sopenharmony_ci
421462306a36Sopenharmony_ci		addrconf_dad_completed(ifp, bump_id, send_na);
421562306a36Sopenharmony_ci
421662306a36Sopenharmony_ci		goto out;
421762306a36Sopenharmony_ci	}
421862306a36Sopenharmony_ci
421962306a36Sopenharmony_ci	ifp->dad_probes--;
422062306a36Sopenharmony_ci	addrconf_mod_dad_work(ifp,
422162306a36Sopenharmony_ci			      max(NEIGH_VAR(ifp->idev->nd_parms, RETRANS_TIME),
422262306a36Sopenharmony_ci				  HZ/100));
422362306a36Sopenharmony_ci	spin_unlock(&ifp->lock);
422462306a36Sopenharmony_ci	write_unlock_bh(&idev->lock);
422562306a36Sopenharmony_ci
422662306a36Sopenharmony_ci	/* send a neighbour solicitation for our addr */
422762306a36Sopenharmony_ci	addrconf_addr_solict_mult(&ifp->addr, &mcaddr);
422862306a36Sopenharmony_ci	ndisc_send_ns(ifp->idev->dev, &ifp->addr, &mcaddr, &in6addr_any,
422962306a36Sopenharmony_ci		      ifp->dad_nonce);
423062306a36Sopenharmony_ciout:
423162306a36Sopenharmony_ci	in6_ifa_put(ifp);
423262306a36Sopenharmony_ci	rtnl_unlock();
423362306a36Sopenharmony_ci}
423462306a36Sopenharmony_ci
423562306a36Sopenharmony_ci/* ifp->idev must be at least read locked */
423662306a36Sopenharmony_cistatic bool ipv6_lonely_lladdr(struct inet6_ifaddr *ifp)
423762306a36Sopenharmony_ci{
423862306a36Sopenharmony_ci	struct inet6_ifaddr *ifpiter;
423962306a36Sopenharmony_ci	struct inet6_dev *idev = ifp->idev;
424062306a36Sopenharmony_ci
424162306a36Sopenharmony_ci	list_for_each_entry_reverse(ifpiter, &idev->addr_list, if_list) {
424262306a36Sopenharmony_ci		if (ifpiter->scope > IFA_LINK)
424362306a36Sopenharmony_ci			break;
424462306a36Sopenharmony_ci		if (ifp != ifpiter && ifpiter->scope == IFA_LINK &&
424562306a36Sopenharmony_ci		    (ifpiter->flags & (IFA_F_PERMANENT|IFA_F_TENTATIVE|
424662306a36Sopenharmony_ci				       IFA_F_OPTIMISTIC|IFA_F_DADFAILED)) ==
424762306a36Sopenharmony_ci		    IFA_F_PERMANENT)
424862306a36Sopenharmony_ci			return false;
424962306a36Sopenharmony_ci	}
425062306a36Sopenharmony_ci	return true;
425162306a36Sopenharmony_ci}
425262306a36Sopenharmony_ci
425362306a36Sopenharmony_cistatic void addrconf_dad_completed(struct inet6_ifaddr *ifp, bool bump_id,
425462306a36Sopenharmony_ci				   bool send_na)
425562306a36Sopenharmony_ci{
425662306a36Sopenharmony_ci	struct net_device *dev = ifp->idev->dev;
425762306a36Sopenharmony_ci	struct in6_addr lladdr;
425862306a36Sopenharmony_ci	bool send_rs, send_mld;
425962306a36Sopenharmony_ci
426062306a36Sopenharmony_ci	addrconf_del_dad_work(ifp);
426162306a36Sopenharmony_ci
426262306a36Sopenharmony_ci	/*
426362306a36Sopenharmony_ci	 *	Configure the address for reception. Now it is valid.
426462306a36Sopenharmony_ci	 */
426562306a36Sopenharmony_ci
426662306a36Sopenharmony_ci	ipv6_ifa_notify(RTM_NEWADDR, ifp);
426762306a36Sopenharmony_ci
426862306a36Sopenharmony_ci	/* If added prefix is link local and we are prepared to process
426962306a36Sopenharmony_ci	   router advertisements, start sending router solicitations.
427062306a36Sopenharmony_ci	 */
427162306a36Sopenharmony_ci
427262306a36Sopenharmony_ci	read_lock_bh(&ifp->idev->lock);
427362306a36Sopenharmony_ci	send_mld = ifp->scope == IFA_LINK && ipv6_lonely_lladdr(ifp);
427462306a36Sopenharmony_ci	send_rs = send_mld &&
427562306a36Sopenharmony_ci		  ipv6_accept_ra(ifp->idev) &&
427662306a36Sopenharmony_ci		  ifp->idev->cnf.rtr_solicits != 0 &&
427762306a36Sopenharmony_ci		  (dev->flags & IFF_LOOPBACK) == 0 &&
427862306a36Sopenharmony_ci		  (dev->type != ARPHRD_TUNNEL) &&
427962306a36Sopenharmony_ci		  !netif_is_team_port(dev);
428062306a36Sopenharmony_ci	read_unlock_bh(&ifp->idev->lock);
428162306a36Sopenharmony_ci
428262306a36Sopenharmony_ci	/* While dad is in progress mld report's source address is in6_addrany.
428362306a36Sopenharmony_ci	 * Resend with proper ll now.
428462306a36Sopenharmony_ci	 */
428562306a36Sopenharmony_ci	if (send_mld)
428662306a36Sopenharmony_ci		ipv6_mc_dad_complete(ifp->idev);
428762306a36Sopenharmony_ci
428862306a36Sopenharmony_ci	/* send unsolicited NA if enabled */
428962306a36Sopenharmony_ci	if (send_na &&
429062306a36Sopenharmony_ci	    (ifp->idev->cnf.ndisc_notify ||
429162306a36Sopenharmony_ci	     dev_net(dev)->ipv6.devconf_all->ndisc_notify)) {
429262306a36Sopenharmony_ci		ndisc_send_na(dev, &in6addr_linklocal_allnodes, &ifp->addr,
429362306a36Sopenharmony_ci			      /*router=*/ !!ifp->idev->cnf.forwarding,
429462306a36Sopenharmony_ci			      /*solicited=*/ false, /*override=*/ true,
429562306a36Sopenharmony_ci			      /*inc_opt=*/ true);
429662306a36Sopenharmony_ci	}
429762306a36Sopenharmony_ci
429862306a36Sopenharmony_ci	if (send_rs) {
429962306a36Sopenharmony_ci		/*
430062306a36Sopenharmony_ci		 *	If a host as already performed a random delay
430162306a36Sopenharmony_ci		 *	[...] as part of DAD [...] there is no need
430262306a36Sopenharmony_ci		 *	to delay again before sending the first RS
430362306a36Sopenharmony_ci		 */
430462306a36Sopenharmony_ci		if (ipv6_get_lladdr(dev, &lladdr, IFA_F_TENTATIVE))
430562306a36Sopenharmony_ci			return;
430662306a36Sopenharmony_ci		ndisc_send_rs(dev, &lladdr, &in6addr_linklocal_allrouters);
430762306a36Sopenharmony_ci
430862306a36Sopenharmony_ci		write_lock_bh(&ifp->idev->lock);
430962306a36Sopenharmony_ci		spin_lock(&ifp->lock);
431062306a36Sopenharmony_ci		ifp->idev->rs_interval = rfc3315_s14_backoff_init(
431162306a36Sopenharmony_ci			ifp->idev->cnf.rtr_solicit_interval);
431262306a36Sopenharmony_ci		ifp->idev->rs_probes = 1;
431362306a36Sopenharmony_ci		ifp->idev->if_flags |= IF_RS_SENT;
431462306a36Sopenharmony_ci		addrconf_mod_rs_timer(ifp->idev, ifp->idev->rs_interval);
431562306a36Sopenharmony_ci		spin_unlock(&ifp->lock);
431662306a36Sopenharmony_ci		write_unlock_bh(&ifp->idev->lock);
431762306a36Sopenharmony_ci	}
431862306a36Sopenharmony_ci
431962306a36Sopenharmony_ci	if (bump_id)
432062306a36Sopenharmony_ci		rt_genid_bump_ipv6(dev_net(dev));
432162306a36Sopenharmony_ci
432262306a36Sopenharmony_ci	/* Make sure that a new temporary address will be created
432362306a36Sopenharmony_ci	 * before this temporary address becomes deprecated.
432462306a36Sopenharmony_ci	 */
432562306a36Sopenharmony_ci	if (ifp->flags & IFA_F_TEMPORARY)
432662306a36Sopenharmony_ci		addrconf_verify_rtnl(dev_net(dev));
432762306a36Sopenharmony_ci}
432862306a36Sopenharmony_ci
432962306a36Sopenharmony_cistatic void addrconf_dad_run(struct inet6_dev *idev, bool restart)
433062306a36Sopenharmony_ci{
433162306a36Sopenharmony_ci	struct inet6_ifaddr *ifp;
433262306a36Sopenharmony_ci
433362306a36Sopenharmony_ci	read_lock_bh(&idev->lock);
433462306a36Sopenharmony_ci	list_for_each_entry(ifp, &idev->addr_list, if_list) {
433562306a36Sopenharmony_ci		spin_lock(&ifp->lock);
433662306a36Sopenharmony_ci		if ((ifp->flags & IFA_F_TENTATIVE &&
433762306a36Sopenharmony_ci		     ifp->state == INET6_IFADDR_STATE_DAD) || restart) {
433862306a36Sopenharmony_ci			if (restart)
433962306a36Sopenharmony_ci				ifp->state = INET6_IFADDR_STATE_PREDAD;
434062306a36Sopenharmony_ci			addrconf_dad_kick(ifp);
434162306a36Sopenharmony_ci		}
434262306a36Sopenharmony_ci		spin_unlock(&ifp->lock);
434362306a36Sopenharmony_ci	}
434462306a36Sopenharmony_ci	read_unlock_bh(&idev->lock);
434562306a36Sopenharmony_ci}
434662306a36Sopenharmony_ci
434762306a36Sopenharmony_ci#ifdef CONFIG_PROC_FS
434862306a36Sopenharmony_cistruct if6_iter_state {
434962306a36Sopenharmony_ci	struct seq_net_private p;
435062306a36Sopenharmony_ci	int bucket;
435162306a36Sopenharmony_ci	int offset;
435262306a36Sopenharmony_ci};
435362306a36Sopenharmony_ci
435462306a36Sopenharmony_cistatic struct inet6_ifaddr *if6_get_first(struct seq_file *seq, loff_t pos)
435562306a36Sopenharmony_ci{
435662306a36Sopenharmony_ci	struct if6_iter_state *state = seq->private;
435762306a36Sopenharmony_ci	struct net *net = seq_file_net(seq);
435862306a36Sopenharmony_ci	struct inet6_ifaddr *ifa = NULL;
435962306a36Sopenharmony_ci	int p = 0;
436062306a36Sopenharmony_ci
436162306a36Sopenharmony_ci	/* initial bucket if pos is 0 */
436262306a36Sopenharmony_ci	if (pos == 0) {
436362306a36Sopenharmony_ci		state->bucket = 0;
436462306a36Sopenharmony_ci		state->offset = 0;
436562306a36Sopenharmony_ci	}
436662306a36Sopenharmony_ci
436762306a36Sopenharmony_ci	for (; state->bucket < IN6_ADDR_HSIZE; ++state->bucket) {
436862306a36Sopenharmony_ci		hlist_for_each_entry_rcu(ifa, &net->ipv6.inet6_addr_lst[state->bucket],
436962306a36Sopenharmony_ci					 addr_lst) {
437062306a36Sopenharmony_ci			/* sync with offset */
437162306a36Sopenharmony_ci			if (p < state->offset) {
437262306a36Sopenharmony_ci				p++;
437362306a36Sopenharmony_ci				continue;
437462306a36Sopenharmony_ci			}
437562306a36Sopenharmony_ci			return ifa;
437662306a36Sopenharmony_ci		}
437762306a36Sopenharmony_ci
437862306a36Sopenharmony_ci		/* prepare for next bucket */
437962306a36Sopenharmony_ci		state->offset = 0;
438062306a36Sopenharmony_ci		p = 0;
438162306a36Sopenharmony_ci	}
438262306a36Sopenharmony_ci	return NULL;
438362306a36Sopenharmony_ci}
438462306a36Sopenharmony_ci
438562306a36Sopenharmony_cistatic struct inet6_ifaddr *if6_get_next(struct seq_file *seq,
438662306a36Sopenharmony_ci					 struct inet6_ifaddr *ifa)
438762306a36Sopenharmony_ci{
438862306a36Sopenharmony_ci	struct if6_iter_state *state = seq->private;
438962306a36Sopenharmony_ci	struct net *net = seq_file_net(seq);
439062306a36Sopenharmony_ci
439162306a36Sopenharmony_ci	hlist_for_each_entry_continue_rcu(ifa, addr_lst) {
439262306a36Sopenharmony_ci		state->offset++;
439362306a36Sopenharmony_ci		return ifa;
439462306a36Sopenharmony_ci	}
439562306a36Sopenharmony_ci
439662306a36Sopenharmony_ci	state->offset = 0;
439762306a36Sopenharmony_ci	while (++state->bucket < IN6_ADDR_HSIZE) {
439862306a36Sopenharmony_ci		hlist_for_each_entry_rcu(ifa,
439962306a36Sopenharmony_ci				     &net->ipv6.inet6_addr_lst[state->bucket], addr_lst) {
440062306a36Sopenharmony_ci			return ifa;
440162306a36Sopenharmony_ci		}
440262306a36Sopenharmony_ci	}
440362306a36Sopenharmony_ci
440462306a36Sopenharmony_ci	return NULL;
440562306a36Sopenharmony_ci}
440662306a36Sopenharmony_ci
440762306a36Sopenharmony_cistatic void *if6_seq_start(struct seq_file *seq, loff_t *pos)
440862306a36Sopenharmony_ci	__acquires(rcu)
440962306a36Sopenharmony_ci{
441062306a36Sopenharmony_ci	rcu_read_lock();
441162306a36Sopenharmony_ci	return if6_get_first(seq, *pos);
441262306a36Sopenharmony_ci}
441362306a36Sopenharmony_ci
441462306a36Sopenharmony_cistatic void *if6_seq_next(struct seq_file *seq, void *v, loff_t *pos)
441562306a36Sopenharmony_ci{
441662306a36Sopenharmony_ci	struct inet6_ifaddr *ifa;
441762306a36Sopenharmony_ci
441862306a36Sopenharmony_ci	ifa = if6_get_next(seq, v);
441962306a36Sopenharmony_ci	++*pos;
442062306a36Sopenharmony_ci	return ifa;
442162306a36Sopenharmony_ci}
442262306a36Sopenharmony_ci
442362306a36Sopenharmony_cistatic void if6_seq_stop(struct seq_file *seq, void *v)
442462306a36Sopenharmony_ci	__releases(rcu)
442562306a36Sopenharmony_ci{
442662306a36Sopenharmony_ci	rcu_read_unlock();
442762306a36Sopenharmony_ci}
442862306a36Sopenharmony_ci
442962306a36Sopenharmony_cistatic int if6_seq_show(struct seq_file *seq, void *v)
443062306a36Sopenharmony_ci{
443162306a36Sopenharmony_ci	struct inet6_ifaddr *ifp = (struct inet6_ifaddr *)v;
443262306a36Sopenharmony_ci	seq_printf(seq, "%pi6 %02x %02x %02x %02x %8s\n",
443362306a36Sopenharmony_ci		   &ifp->addr,
443462306a36Sopenharmony_ci		   ifp->idev->dev->ifindex,
443562306a36Sopenharmony_ci		   ifp->prefix_len,
443662306a36Sopenharmony_ci		   ifp->scope,
443762306a36Sopenharmony_ci		   (u8) ifp->flags,
443862306a36Sopenharmony_ci		   ifp->idev->dev->name);
443962306a36Sopenharmony_ci	return 0;
444062306a36Sopenharmony_ci}
444162306a36Sopenharmony_ci
444262306a36Sopenharmony_cistatic const struct seq_operations if6_seq_ops = {
444362306a36Sopenharmony_ci	.start	= if6_seq_start,
444462306a36Sopenharmony_ci	.next	= if6_seq_next,
444562306a36Sopenharmony_ci	.show	= if6_seq_show,
444662306a36Sopenharmony_ci	.stop	= if6_seq_stop,
444762306a36Sopenharmony_ci};
444862306a36Sopenharmony_ci
444962306a36Sopenharmony_cistatic int __net_init if6_proc_net_init(struct net *net)
445062306a36Sopenharmony_ci{
445162306a36Sopenharmony_ci	if (!proc_create_net("if_inet6", 0444, net->proc_net, &if6_seq_ops,
445262306a36Sopenharmony_ci			sizeof(struct if6_iter_state)))
445362306a36Sopenharmony_ci		return -ENOMEM;
445462306a36Sopenharmony_ci	return 0;
445562306a36Sopenharmony_ci}
445662306a36Sopenharmony_ci
445762306a36Sopenharmony_cistatic void __net_exit if6_proc_net_exit(struct net *net)
445862306a36Sopenharmony_ci{
445962306a36Sopenharmony_ci	remove_proc_entry("if_inet6", net->proc_net);
446062306a36Sopenharmony_ci}
446162306a36Sopenharmony_ci
446262306a36Sopenharmony_cistatic struct pernet_operations if6_proc_net_ops = {
446362306a36Sopenharmony_ci	.init = if6_proc_net_init,
446462306a36Sopenharmony_ci	.exit = if6_proc_net_exit,
446562306a36Sopenharmony_ci};
446662306a36Sopenharmony_ci
446762306a36Sopenharmony_ciint __init if6_proc_init(void)
446862306a36Sopenharmony_ci{
446962306a36Sopenharmony_ci	return register_pernet_subsys(&if6_proc_net_ops);
447062306a36Sopenharmony_ci}
447162306a36Sopenharmony_ci
447262306a36Sopenharmony_civoid if6_proc_exit(void)
447362306a36Sopenharmony_ci{
447462306a36Sopenharmony_ci	unregister_pernet_subsys(&if6_proc_net_ops);
447562306a36Sopenharmony_ci}
447662306a36Sopenharmony_ci#endif	/* CONFIG_PROC_FS */
447762306a36Sopenharmony_ci
447862306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6_MIP6)
447962306a36Sopenharmony_ci/* Check if address is a home address configured on any interface. */
448062306a36Sopenharmony_ciint ipv6_chk_home_addr(struct net *net, const struct in6_addr *addr)
448162306a36Sopenharmony_ci{
448262306a36Sopenharmony_ci	unsigned int hash = inet6_addr_hash(net, addr);
448362306a36Sopenharmony_ci	struct inet6_ifaddr *ifp = NULL;
448462306a36Sopenharmony_ci	int ret = 0;
448562306a36Sopenharmony_ci
448662306a36Sopenharmony_ci	rcu_read_lock();
448762306a36Sopenharmony_ci	hlist_for_each_entry_rcu(ifp, &net->ipv6.inet6_addr_lst[hash], addr_lst) {
448862306a36Sopenharmony_ci		if (ipv6_addr_equal(&ifp->addr, addr) &&
448962306a36Sopenharmony_ci		    (ifp->flags & IFA_F_HOMEADDRESS)) {
449062306a36Sopenharmony_ci			ret = 1;
449162306a36Sopenharmony_ci			break;
449262306a36Sopenharmony_ci		}
449362306a36Sopenharmony_ci	}
449462306a36Sopenharmony_ci	rcu_read_unlock();
449562306a36Sopenharmony_ci	return ret;
449662306a36Sopenharmony_ci}
449762306a36Sopenharmony_ci#endif
449862306a36Sopenharmony_ci
449962306a36Sopenharmony_ci/* RFC6554 has some algorithm to avoid loops in segment routing by
450062306a36Sopenharmony_ci * checking if the segments contains any of a local interface address.
450162306a36Sopenharmony_ci *
450262306a36Sopenharmony_ci * Quote:
450362306a36Sopenharmony_ci *
450462306a36Sopenharmony_ci * To detect loops in the SRH, a router MUST determine if the SRH
450562306a36Sopenharmony_ci * includes multiple addresses assigned to any interface on that router.
450662306a36Sopenharmony_ci * If such addresses appear more than once and are separated by at least
450762306a36Sopenharmony_ci * one address not assigned to that router.
450862306a36Sopenharmony_ci */
450962306a36Sopenharmony_ciint ipv6_chk_rpl_srh_loop(struct net *net, const struct in6_addr *segs,
451062306a36Sopenharmony_ci			  unsigned char nsegs)
451162306a36Sopenharmony_ci{
451262306a36Sopenharmony_ci	const struct in6_addr *addr;
451362306a36Sopenharmony_ci	int i, ret = 0, found = 0;
451462306a36Sopenharmony_ci	struct inet6_ifaddr *ifp;
451562306a36Sopenharmony_ci	bool separated = false;
451662306a36Sopenharmony_ci	unsigned int hash;
451762306a36Sopenharmony_ci	bool hash_found;
451862306a36Sopenharmony_ci
451962306a36Sopenharmony_ci	rcu_read_lock();
452062306a36Sopenharmony_ci	for (i = 0; i < nsegs; i++) {
452162306a36Sopenharmony_ci		addr = &segs[i];
452262306a36Sopenharmony_ci		hash = inet6_addr_hash(net, addr);
452362306a36Sopenharmony_ci
452462306a36Sopenharmony_ci		hash_found = false;
452562306a36Sopenharmony_ci		hlist_for_each_entry_rcu(ifp, &net->ipv6.inet6_addr_lst[hash], addr_lst) {
452662306a36Sopenharmony_ci
452762306a36Sopenharmony_ci			if (ipv6_addr_equal(&ifp->addr, addr)) {
452862306a36Sopenharmony_ci				hash_found = true;
452962306a36Sopenharmony_ci				break;
453062306a36Sopenharmony_ci			}
453162306a36Sopenharmony_ci		}
453262306a36Sopenharmony_ci
453362306a36Sopenharmony_ci		if (hash_found) {
453462306a36Sopenharmony_ci			if (found > 1 && separated) {
453562306a36Sopenharmony_ci				ret = 1;
453662306a36Sopenharmony_ci				break;
453762306a36Sopenharmony_ci			}
453862306a36Sopenharmony_ci
453962306a36Sopenharmony_ci			separated = false;
454062306a36Sopenharmony_ci			found++;
454162306a36Sopenharmony_ci		} else {
454262306a36Sopenharmony_ci			separated = true;
454362306a36Sopenharmony_ci		}
454462306a36Sopenharmony_ci	}
454562306a36Sopenharmony_ci	rcu_read_unlock();
454662306a36Sopenharmony_ci
454762306a36Sopenharmony_ci	return ret;
454862306a36Sopenharmony_ci}
454962306a36Sopenharmony_ci
455062306a36Sopenharmony_ci/*
455162306a36Sopenharmony_ci *	Periodic address status verification
455262306a36Sopenharmony_ci */
455362306a36Sopenharmony_ci
455462306a36Sopenharmony_cistatic void addrconf_verify_rtnl(struct net *net)
455562306a36Sopenharmony_ci{
455662306a36Sopenharmony_ci	unsigned long now, next, next_sec, next_sched;
455762306a36Sopenharmony_ci	struct inet6_ifaddr *ifp;
455862306a36Sopenharmony_ci	int i;
455962306a36Sopenharmony_ci
456062306a36Sopenharmony_ci	ASSERT_RTNL();
456162306a36Sopenharmony_ci
456262306a36Sopenharmony_ci	rcu_read_lock_bh();
456362306a36Sopenharmony_ci	now = jiffies;
456462306a36Sopenharmony_ci	next = round_jiffies_up(now + ADDR_CHECK_FREQUENCY);
456562306a36Sopenharmony_ci
456662306a36Sopenharmony_ci	cancel_delayed_work(&net->ipv6.addr_chk_work);
456762306a36Sopenharmony_ci
456862306a36Sopenharmony_ci	for (i = 0; i < IN6_ADDR_HSIZE; i++) {
456962306a36Sopenharmony_cirestart:
457062306a36Sopenharmony_ci		hlist_for_each_entry_rcu_bh(ifp, &net->ipv6.inet6_addr_lst[i], addr_lst) {
457162306a36Sopenharmony_ci			unsigned long age;
457262306a36Sopenharmony_ci
457362306a36Sopenharmony_ci			/* When setting preferred_lft to a value not zero or
457462306a36Sopenharmony_ci			 * infinity, while valid_lft is infinity
457562306a36Sopenharmony_ci			 * IFA_F_PERMANENT has a non-infinity life time.
457662306a36Sopenharmony_ci			 */
457762306a36Sopenharmony_ci			if ((ifp->flags & IFA_F_PERMANENT) &&
457862306a36Sopenharmony_ci			    (ifp->prefered_lft == INFINITY_LIFE_TIME))
457962306a36Sopenharmony_ci				continue;
458062306a36Sopenharmony_ci
458162306a36Sopenharmony_ci			spin_lock(&ifp->lock);
458262306a36Sopenharmony_ci			/* We try to batch several events at once. */
458362306a36Sopenharmony_ci			age = (now - ifp->tstamp + ADDRCONF_TIMER_FUZZ_MINUS) / HZ;
458462306a36Sopenharmony_ci
458562306a36Sopenharmony_ci			if ((ifp->flags&IFA_F_TEMPORARY) &&
458662306a36Sopenharmony_ci			    !(ifp->flags&IFA_F_TENTATIVE) &&
458762306a36Sopenharmony_ci			    ifp->prefered_lft != INFINITY_LIFE_TIME &&
458862306a36Sopenharmony_ci			    !ifp->regen_count && ifp->ifpub) {
458962306a36Sopenharmony_ci				/* This is a non-regenerated temporary addr. */
459062306a36Sopenharmony_ci
459162306a36Sopenharmony_ci				unsigned long regen_advance = ifp->idev->cnf.regen_max_retry *
459262306a36Sopenharmony_ci					ifp->idev->cnf.dad_transmits *
459362306a36Sopenharmony_ci					max(NEIGH_VAR(ifp->idev->nd_parms, RETRANS_TIME), HZ/100) / HZ;
459462306a36Sopenharmony_ci
459562306a36Sopenharmony_ci				if (age + regen_advance >= ifp->prefered_lft) {
459662306a36Sopenharmony_ci					struct inet6_ifaddr *ifpub = ifp->ifpub;
459762306a36Sopenharmony_ci					if (time_before(ifp->tstamp + ifp->prefered_lft * HZ, next))
459862306a36Sopenharmony_ci						next = ifp->tstamp + ifp->prefered_lft * HZ;
459962306a36Sopenharmony_ci
460062306a36Sopenharmony_ci					ifp->regen_count++;
460162306a36Sopenharmony_ci					in6_ifa_hold(ifp);
460262306a36Sopenharmony_ci					in6_ifa_hold(ifpub);
460362306a36Sopenharmony_ci					spin_unlock(&ifp->lock);
460462306a36Sopenharmony_ci
460562306a36Sopenharmony_ci					spin_lock(&ifpub->lock);
460662306a36Sopenharmony_ci					ifpub->regen_count = 0;
460762306a36Sopenharmony_ci					spin_unlock(&ifpub->lock);
460862306a36Sopenharmony_ci					rcu_read_unlock_bh();
460962306a36Sopenharmony_ci					ipv6_create_tempaddr(ifpub, true);
461062306a36Sopenharmony_ci					in6_ifa_put(ifpub);
461162306a36Sopenharmony_ci					in6_ifa_put(ifp);
461262306a36Sopenharmony_ci					rcu_read_lock_bh();
461362306a36Sopenharmony_ci					goto restart;
461462306a36Sopenharmony_ci				} else if (time_before(ifp->tstamp + ifp->prefered_lft * HZ - regen_advance * HZ, next))
461562306a36Sopenharmony_ci					next = ifp->tstamp + ifp->prefered_lft * HZ - regen_advance * HZ;
461662306a36Sopenharmony_ci			}
461762306a36Sopenharmony_ci
461862306a36Sopenharmony_ci			if (ifp->valid_lft != INFINITY_LIFE_TIME &&
461962306a36Sopenharmony_ci			    age >= ifp->valid_lft) {
462062306a36Sopenharmony_ci				spin_unlock(&ifp->lock);
462162306a36Sopenharmony_ci				in6_ifa_hold(ifp);
462262306a36Sopenharmony_ci				rcu_read_unlock_bh();
462362306a36Sopenharmony_ci				ipv6_del_addr(ifp);
462462306a36Sopenharmony_ci				rcu_read_lock_bh();
462562306a36Sopenharmony_ci				goto restart;
462662306a36Sopenharmony_ci			} else if (ifp->prefered_lft == INFINITY_LIFE_TIME) {
462762306a36Sopenharmony_ci				spin_unlock(&ifp->lock);
462862306a36Sopenharmony_ci				continue;
462962306a36Sopenharmony_ci			} else if (age >= ifp->prefered_lft) {
463062306a36Sopenharmony_ci				/* jiffies - ifp->tstamp > age >= ifp->prefered_lft */
463162306a36Sopenharmony_ci				int deprecate = 0;
463262306a36Sopenharmony_ci
463362306a36Sopenharmony_ci				if (!(ifp->flags&IFA_F_DEPRECATED)) {
463462306a36Sopenharmony_ci					deprecate = 1;
463562306a36Sopenharmony_ci					ifp->flags |= IFA_F_DEPRECATED;
463662306a36Sopenharmony_ci				}
463762306a36Sopenharmony_ci
463862306a36Sopenharmony_ci				if ((ifp->valid_lft != INFINITY_LIFE_TIME) &&
463962306a36Sopenharmony_ci				    (time_before(ifp->tstamp + ifp->valid_lft * HZ, next)))
464062306a36Sopenharmony_ci					next = ifp->tstamp + ifp->valid_lft * HZ;
464162306a36Sopenharmony_ci
464262306a36Sopenharmony_ci				spin_unlock(&ifp->lock);
464362306a36Sopenharmony_ci
464462306a36Sopenharmony_ci				if (deprecate) {
464562306a36Sopenharmony_ci					in6_ifa_hold(ifp);
464662306a36Sopenharmony_ci
464762306a36Sopenharmony_ci					ipv6_ifa_notify(0, ifp);
464862306a36Sopenharmony_ci					in6_ifa_put(ifp);
464962306a36Sopenharmony_ci					goto restart;
465062306a36Sopenharmony_ci				}
465162306a36Sopenharmony_ci			} else {
465262306a36Sopenharmony_ci				/* ifp->prefered_lft <= ifp->valid_lft */
465362306a36Sopenharmony_ci				if (time_before(ifp->tstamp + ifp->prefered_lft * HZ, next))
465462306a36Sopenharmony_ci					next = ifp->tstamp + ifp->prefered_lft * HZ;
465562306a36Sopenharmony_ci				spin_unlock(&ifp->lock);
465662306a36Sopenharmony_ci			}
465762306a36Sopenharmony_ci		}
465862306a36Sopenharmony_ci	}
465962306a36Sopenharmony_ci
466062306a36Sopenharmony_ci	next_sec = round_jiffies_up(next);
466162306a36Sopenharmony_ci	next_sched = next;
466262306a36Sopenharmony_ci
466362306a36Sopenharmony_ci	/* If rounded timeout is accurate enough, accept it. */
466462306a36Sopenharmony_ci	if (time_before(next_sec, next + ADDRCONF_TIMER_FUZZ))
466562306a36Sopenharmony_ci		next_sched = next_sec;
466662306a36Sopenharmony_ci
466762306a36Sopenharmony_ci	/* And minimum interval is ADDRCONF_TIMER_FUZZ_MAX. */
466862306a36Sopenharmony_ci	if (time_before(next_sched, jiffies + ADDRCONF_TIMER_FUZZ_MAX))
466962306a36Sopenharmony_ci		next_sched = jiffies + ADDRCONF_TIMER_FUZZ_MAX;
467062306a36Sopenharmony_ci
467162306a36Sopenharmony_ci	pr_debug("now = %lu, schedule = %lu, rounded schedule = %lu => %lu\n",
467262306a36Sopenharmony_ci		 now, next, next_sec, next_sched);
467362306a36Sopenharmony_ci	mod_delayed_work(addrconf_wq, &net->ipv6.addr_chk_work, next_sched - now);
467462306a36Sopenharmony_ci	rcu_read_unlock_bh();
467562306a36Sopenharmony_ci}
467662306a36Sopenharmony_ci
467762306a36Sopenharmony_cistatic void addrconf_verify_work(struct work_struct *w)
467862306a36Sopenharmony_ci{
467962306a36Sopenharmony_ci	struct net *net = container_of(to_delayed_work(w), struct net,
468062306a36Sopenharmony_ci				       ipv6.addr_chk_work);
468162306a36Sopenharmony_ci
468262306a36Sopenharmony_ci	rtnl_lock();
468362306a36Sopenharmony_ci	addrconf_verify_rtnl(net);
468462306a36Sopenharmony_ci	rtnl_unlock();
468562306a36Sopenharmony_ci}
468662306a36Sopenharmony_ci
468762306a36Sopenharmony_cistatic void addrconf_verify(struct net *net)
468862306a36Sopenharmony_ci{
468962306a36Sopenharmony_ci	mod_delayed_work(addrconf_wq, &net->ipv6.addr_chk_work, 0);
469062306a36Sopenharmony_ci}
469162306a36Sopenharmony_ci
469262306a36Sopenharmony_cistatic struct in6_addr *extract_addr(struct nlattr *addr, struct nlattr *local,
469362306a36Sopenharmony_ci				     struct in6_addr **peer_pfx)
469462306a36Sopenharmony_ci{
469562306a36Sopenharmony_ci	struct in6_addr *pfx = NULL;
469662306a36Sopenharmony_ci
469762306a36Sopenharmony_ci	*peer_pfx = NULL;
469862306a36Sopenharmony_ci
469962306a36Sopenharmony_ci	if (addr)
470062306a36Sopenharmony_ci		pfx = nla_data(addr);
470162306a36Sopenharmony_ci
470262306a36Sopenharmony_ci	if (local) {
470362306a36Sopenharmony_ci		if (pfx && nla_memcmp(local, pfx, sizeof(*pfx)))
470462306a36Sopenharmony_ci			*peer_pfx = pfx;
470562306a36Sopenharmony_ci		pfx = nla_data(local);
470662306a36Sopenharmony_ci	}
470762306a36Sopenharmony_ci
470862306a36Sopenharmony_ci	return pfx;
470962306a36Sopenharmony_ci}
471062306a36Sopenharmony_ci
471162306a36Sopenharmony_cistatic const struct nla_policy ifa_ipv6_policy[IFA_MAX+1] = {
471262306a36Sopenharmony_ci	[IFA_ADDRESS]		= { .len = sizeof(struct in6_addr) },
471362306a36Sopenharmony_ci	[IFA_LOCAL]		= { .len = sizeof(struct in6_addr) },
471462306a36Sopenharmony_ci	[IFA_CACHEINFO]		= { .len = sizeof(struct ifa_cacheinfo) },
471562306a36Sopenharmony_ci	[IFA_FLAGS]		= { .len = sizeof(u32) },
471662306a36Sopenharmony_ci	[IFA_RT_PRIORITY]	= { .len = sizeof(u32) },
471762306a36Sopenharmony_ci	[IFA_TARGET_NETNSID]	= { .type = NLA_S32 },
471862306a36Sopenharmony_ci	[IFA_PROTO]		= { .type = NLA_U8 },
471962306a36Sopenharmony_ci};
472062306a36Sopenharmony_ci
472162306a36Sopenharmony_cistatic int
472262306a36Sopenharmony_ciinet6_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh,
472362306a36Sopenharmony_ci		  struct netlink_ext_ack *extack)
472462306a36Sopenharmony_ci{
472562306a36Sopenharmony_ci	struct net *net = sock_net(skb->sk);
472662306a36Sopenharmony_ci	struct ifaddrmsg *ifm;
472762306a36Sopenharmony_ci	struct nlattr *tb[IFA_MAX+1];
472862306a36Sopenharmony_ci	struct in6_addr *pfx, *peer_pfx;
472962306a36Sopenharmony_ci	u32 ifa_flags;
473062306a36Sopenharmony_ci	int err;
473162306a36Sopenharmony_ci
473262306a36Sopenharmony_ci	err = nlmsg_parse_deprecated(nlh, sizeof(*ifm), tb, IFA_MAX,
473362306a36Sopenharmony_ci				     ifa_ipv6_policy, extack);
473462306a36Sopenharmony_ci	if (err < 0)
473562306a36Sopenharmony_ci		return err;
473662306a36Sopenharmony_ci
473762306a36Sopenharmony_ci	ifm = nlmsg_data(nlh);
473862306a36Sopenharmony_ci	pfx = extract_addr(tb[IFA_ADDRESS], tb[IFA_LOCAL], &peer_pfx);
473962306a36Sopenharmony_ci	if (!pfx)
474062306a36Sopenharmony_ci		return -EINVAL;
474162306a36Sopenharmony_ci
474262306a36Sopenharmony_ci	ifa_flags = tb[IFA_FLAGS] ? nla_get_u32(tb[IFA_FLAGS]) : ifm->ifa_flags;
474362306a36Sopenharmony_ci
474462306a36Sopenharmony_ci	/* We ignore other flags so far. */
474562306a36Sopenharmony_ci	ifa_flags &= IFA_F_MANAGETEMPADDR;
474662306a36Sopenharmony_ci
474762306a36Sopenharmony_ci	return inet6_addr_del(net, ifm->ifa_index, ifa_flags, pfx,
474862306a36Sopenharmony_ci			      ifm->ifa_prefixlen, extack);
474962306a36Sopenharmony_ci}
475062306a36Sopenharmony_ci
475162306a36Sopenharmony_cistatic int modify_prefix_route(struct inet6_ifaddr *ifp,
475262306a36Sopenharmony_ci			       unsigned long expires, u32 flags,
475362306a36Sopenharmony_ci			       bool modify_peer)
475462306a36Sopenharmony_ci{
475562306a36Sopenharmony_ci	struct fib6_info *f6i;
475662306a36Sopenharmony_ci	u32 prio;
475762306a36Sopenharmony_ci
475862306a36Sopenharmony_ci	f6i = addrconf_get_prefix_route(modify_peer ? &ifp->peer_addr : &ifp->addr,
475962306a36Sopenharmony_ci					ifp->prefix_len,
476062306a36Sopenharmony_ci					ifp->idev->dev, 0, RTF_DEFAULT, true);
476162306a36Sopenharmony_ci	if (!f6i)
476262306a36Sopenharmony_ci		return -ENOENT;
476362306a36Sopenharmony_ci
476462306a36Sopenharmony_ci	prio = ifp->rt_priority ? : IP6_RT_PRIO_ADDRCONF;
476562306a36Sopenharmony_ci	if (f6i->fib6_metric != prio) {
476662306a36Sopenharmony_ci		/* delete old one */
476762306a36Sopenharmony_ci		ip6_del_rt(dev_net(ifp->idev->dev), f6i, false);
476862306a36Sopenharmony_ci
476962306a36Sopenharmony_ci		/* add new one */
477062306a36Sopenharmony_ci		addrconf_prefix_route(modify_peer ? &ifp->peer_addr : &ifp->addr,
477162306a36Sopenharmony_ci				      ifp->prefix_len,
477262306a36Sopenharmony_ci				      ifp->rt_priority, ifp->idev->dev,
477362306a36Sopenharmony_ci				      expires, flags, GFP_KERNEL);
477462306a36Sopenharmony_ci	} else {
477562306a36Sopenharmony_ci		if (!expires)
477662306a36Sopenharmony_ci			fib6_clean_expires(f6i);
477762306a36Sopenharmony_ci		else
477862306a36Sopenharmony_ci			fib6_set_expires(f6i, expires);
477962306a36Sopenharmony_ci
478062306a36Sopenharmony_ci		fib6_info_release(f6i);
478162306a36Sopenharmony_ci	}
478262306a36Sopenharmony_ci
478362306a36Sopenharmony_ci	return 0;
478462306a36Sopenharmony_ci}
478562306a36Sopenharmony_ci
478662306a36Sopenharmony_cistatic int inet6_addr_modify(struct net *net, struct inet6_ifaddr *ifp,
478762306a36Sopenharmony_ci			     struct ifa6_config *cfg)
478862306a36Sopenharmony_ci{
478962306a36Sopenharmony_ci	u32 flags;
479062306a36Sopenharmony_ci	clock_t expires;
479162306a36Sopenharmony_ci	unsigned long timeout;
479262306a36Sopenharmony_ci	bool was_managetempaddr;
479362306a36Sopenharmony_ci	bool had_prefixroute;
479462306a36Sopenharmony_ci	bool new_peer = false;
479562306a36Sopenharmony_ci
479662306a36Sopenharmony_ci	ASSERT_RTNL();
479762306a36Sopenharmony_ci
479862306a36Sopenharmony_ci	if (!cfg->valid_lft || cfg->preferred_lft > cfg->valid_lft)
479962306a36Sopenharmony_ci		return -EINVAL;
480062306a36Sopenharmony_ci
480162306a36Sopenharmony_ci	if (cfg->ifa_flags & IFA_F_MANAGETEMPADDR &&
480262306a36Sopenharmony_ci	    (ifp->flags & IFA_F_TEMPORARY || ifp->prefix_len != 64))
480362306a36Sopenharmony_ci		return -EINVAL;
480462306a36Sopenharmony_ci
480562306a36Sopenharmony_ci	if (!(ifp->flags & IFA_F_TENTATIVE) || ifp->flags & IFA_F_DADFAILED)
480662306a36Sopenharmony_ci		cfg->ifa_flags &= ~IFA_F_OPTIMISTIC;
480762306a36Sopenharmony_ci
480862306a36Sopenharmony_ci	timeout = addrconf_timeout_fixup(cfg->valid_lft, HZ);
480962306a36Sopenharmony_ci	if (addrconf_finite_timeout(timeout)) {
481062306a36Sopenharmony_ci		expires = jiffies_to_clock_t(timeout * HZ);
481162306a36Sopenharmony_ci		cfg->valid_lft = timeout;
481262306a36Sopenharmony_ci		flags = RTF_EXPIRES;
481362306a36Sopenharmony_ci	} else {
481462306a36Sopenharmony_ci		expires = 0;
481562306a36Sopenharmony_ci		flags = 0;
481662306a36Sopenharmony_ci		cfg->ifa_flags |= IFA_F_PERMANENT;
481762306a36Sopenharmony_ci	}
481862306a36Sopenharmony_ci
481962306a36Sopenharmony_ci	timeout = addrconf_timeout_fixup(cfg->preferred_lft, HZ);
482062306a36Sopenharmony_ci	if (addrconf_finite_timeout(timeout)) {
482162306a36Sopenharmony_ci		if (timeout == 0)
482262306a36Sopenharmony_ci			cfg->ifa_flags |= IFA_F_DEPRECATED;
482362306a36Sopenharmony_ci		cfg->preferred_lft = timeout;
482462306a36Sopenharmony_ci	}
482562306a36Sopenharmony_ci
482662306a36Sopenharmony_ci	if (cfg->peer_pfx &&
482762306a36Sopenharmony_ci	    memcmp(&ifp->peer_addr, cfg->peer_pfx, sizeof(struct in6_addr))) {
482862306a36Sopenharmony_ci		if (!ipv6_addr_any(&ifp->peer_addr))
482962306a36Sopenharmony_ci			cleanup_prefix_route(ifp, expires, true, true);
483062306a36Sopenharmony_ci		new_peer = true;
483162306a36Sopenharmony_ci	}
483262306a36Sopenharmony_ci
483362306a36Sopenharmony_ci	spin_lock_bh(&ifp->lock);
483462306a36Sopenharmony_ci	was_managetempaddr = ifp->flags & IFA_F_MANAGETEMPADDR;
483562306a36Sopenharmony_ci	had_prefixroute = ifp->flags & IFA_F_PERMANENT &&
483662306a36Sopenharmony_ci			  !(ifp->flags & IFA_F_NOPREFIXROUTE);
483762306a36Sopenharmony_ci	ifp->flags &= ~(IFA_F_DEPRECATED | IFA_F_PERMANENT | IFA_F_NODAD |
483862306a36Sopenharmony_ci			IFA_F_HOMEADDRESS | IFA_F_MANAGETEMPADDR |
483962306a36Sopenharmony_ci			IFA_F_NOPREFIXROUTE);
484062306a36Sopenharmony_ci	ifp->flags |= cfg->ifa_flags;
484162306a36Sopenharmony_ci	ifp->tstamp = jiffies;
484262306a36Sopenharmony_ci	ifp->valid_lft = cfg->valid_lft;
484362306a36Sopenharmony_ci	ifp->prefered_lft = cfg->preferred_lft;
484462306a36Sopenharmony_ci	ifp->ifa_proto = cfg->ifa_proto;
484562306a36Sopenharmony_ci
484662306a36Sopenharmony_ci	if (cfg->rt_priority && cfg->rt_priority != ifp->rt_priority)
484762306a36Sopenharmony_ci		ifp->rt_priority = cfg->rt_priority;
484862306a36Sopenharmony_ci
484962306a36Sopenharmony_ci	if (new_peer)
485062306a36Sopenharmony_ci		ifp->peer_addr = *cfg->peer_pfx;
485162306a36Sopenharmony_ci
485262306a36Sopenharmony_ci	spin_unlock_bh(&ifp->lock);
485362306a36Sopenharmony_ci	if (!(ifp->flags&IFA_F_TENTATIVE))
485462306a36Sopenharmony_ci		ipv6_ifa_notify(0, ifp);
485562306a36Sopenharmony_ci
485662306a36Sopenharmony_ci	if (!(cfg->ifa_flags & IFA_F_NOPREFIXROUTE)) {
485762306a36Sopenharmony_ci		int rc = -ENOENT;
485862306a36Sopenharmony_ci
485962306a36Sopenharmony_ci		if (had_prefixroute)
486062306a36Sopenharmony_ci			rc = modify_prefix_route(ifp, expires, flags, false);
486162306a36Sopenharmony_ci
486262306a36Sopenharmony_ci		/* prefix route could have been deleted; if so restore it */
486362306a36Sopenharmony_ci		if (rc == -ENOENT) {
486462306a36Sopenharmony_ci			addrconf_prefix_route(&ifp->addr, ifp->prefix_len,
486562306a36Sopenharmony_ci					      ifp->rt_priority, ifp->idev->dev,
486662306a36Sopenharmony_ci					      expires, flags, GFP_KERNEL);
486762306a36Sopenharmony_ci		}
486862306a36Sopenharmony_ci
486962306a36Sopenharmony_ci		if (had_prefixroute && !ipv6_addr_any(&ifp->peer_addr))
487062306a36Sopenharmony_ci			rc = modify_prefix_route(ifp, expires, flags, true);
487162306a36Sopenharmony_ci
487262306a36Sopenharmony_ci		if (rc == -ENOENT && !ipv6_addr_any(&ifp->peer_addr)) {
487362306a36Sopenharmony_ci			addrconf_prefix_route(&ifp->peer_addr, ifp->prefix_len,
487462306a36Sopenharmony_ci					      ifp->rt_priority, ifp->idev->dev,
487562306a36Sopenharmony_ci					      expires, flags, GFP_KERNEL);
487662306a36Sopenharmony_ci		}
487762306a36Sopenharmony_ci	} else if (had_prefixroute) {
487862306a36Sopenharmony_ci		enum cleanup_prefix_rt_t action;
487962306a36Sopenharmony_ci		unsigned long rt_expires;
488062306a36Sopenharmony_ci
488162306a36Sopenharmony_ci		write_lock_bh(&ifp->idev->lock);
488262306a36Sopenharmony_ci		action = check_cleanup_prefix_route(ifp, &rt_expires);
488362306a36Sopenharmony_ci		write_unlock_bh(&ifp->idev->lock);
488462306a36Sopenharmony_ci
488562306a36Sopenharmony_ci		if (action != CLEANUP_PREFIX_RT_NOP) {
488662306a36Sopenharmony_ci			cleanup_prefix_route(ifp, rt_expires,
488762306a36Sopenharmony_ci				action == CLEANUP_PREFIX_RT_DEL, false);
488862306a36Sopenharmony_ci		}
488962306a36Sopenharmony_ci	}
489062306a36Sopenharmony_ci
489162306a36Sopenharmony_ci	if (was_managetempaddr || ifp->flags & IFA_F_MANAGETEMPADDR) {
489262306a36Sopenharmony_ci		if (was_managetempaddr &&
489362306a36Sopenharmony_ci		    !(ifp->flags & IFA_F_MANAGETEMPADDR)) {
489462306a36Sopenharmony_ci			cfg->valid_lft = 0;
489562306a36Sopenharmony_ci			cfg->preferred_lft = 0;
489662306a36Sopenharmony_ci		}
489762306a36Sopenharmony_ci		manage_tempaddrs(ifp->idev, ifp, cfg->valid_lft,
489862306a36Sopenharmony_ci				 cfg->preferred_lft, !was_managetempaddr,
489962306a36Sopenharmony_ci				 jiffies);
490062306a36Sopenharmony_ci	}
490162306a36Sopenharmony_ci
490262306a36Sopenharmony_ci	addrconf_verify_rtnl(net);
490362306a36Sopenharmony_ci
490462306a36Sopenharmony_ci	return 0;
490562306a36Sopenharmony_ci}
490662306a36Sopenharmony_ci
490762306a36Sopenharmony_cistatic int
490862306a36Sopenharmony_ciinet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh,
490962306a36Sopenharmony_ci		  struct netlink_ext_ack *extack)
491062306a36Sopenharmony_ci{
491162306a36Sopenharmony_ci	struct net *net = sock_net(skb->sk);
491262306a36Sopenharmony_ci	struct ifaddrmsg *ifm;
491362306a36Sopenharmony_ci	struct nlattr *tb[IFA_MAX+1];
491462306a36Sopenharmony_ci	struct in6_addr *peer_pfx;
491562306a36Sopenharmony_ci	struct inet6_ifaddr *ifa;
491662306a36Sopenharmony_ci	struct net_device *dev;
491762306a36Sopenharmony_ci	struct inet6_dev *idev;
491862306a36Sopenharmony_ci	struct ifa6_config cfg;
491962306a36Sopenharmony_ci	int err;
492062306a36Sopenharmony_ci
492162306a36Sopenharmony_ci	err = nlmsg_parse_deprecated(nlh, sizeof(*ifm), tb, IFA_MAX,
492262306a36Sopenharmony_ci				     ifa_ipv6_policy, extack);
492362306a36Sopenharmony_ci	if (err < 0)
492462306a36Sopenharmony_ci		return err;
492562306a36Sopenharmony_ci
492662306a36Sopenharmony_ci	memset(&cfg, 0, sizeof(cfg));
492762306a36Sopenharmony_ci
492862306a36Sopenharmony_ci	ifm = nlmsg_data(nlh);
492962306a36Sopenharmony_ci	cfg.pfx = extract_addr(tb[IFA_ADDRESS], tb[IFA_LOCAL], &peer_pfx);
493062306a36Sopenharmony_ci	if (!cfg.pfx)
493162306a36Sopenharmony_ci		return -EINVAL;
493262306a36Sopenharmony_ci
493362306a36Sopenharmony_ci	cfg.peer_pfx = peer_pfx;
493462306a36Sopenharmony_ci	cfg.plen = ifm->ifa_prefixlen;
493562306a36Sopenharmony_ci	if (tb[IFA_RT_PRIORITY])
493662306a36Sopenharmony_ci		cfg.rt_priority = nla_get_u32(tb[IFA_RT_PRIORITY]);
493762306a36Sopenharmony_ci
493862306a36Sopenharmony_ci	if (tb[IFA_PROTO])
493962306a36Sopenharmony_ci		cfg.ifa_proto = nla_get_u8(tb[IFA_PROTO]);
494062306a36Sopenharmony_ci
494162306a36Sopenharmony_ci	cfg.valid_lft = INFINITY_LIFE_TIME;
494262306a36Sopenharmony_ci	cfg.preferred_lft = INFINITY_LIFE_TIME;
494362306a36Sopenharmony_ci
494462306a36Sopenharmony_ci	if (tb[IFA_CACHEINFO]) {
494562306a36Sopenharmony_ci		struct ifa_cacheinfo *ci;
494662306a36Sopenharmony_ci
494762306a36Sopenharmony_ci		ci = nla_data(tb[IFA_CACHEINFO]);
494862306a36Sopenharmony_ci		cfg.valid_lft = ci->ifa_valid;
494962306a36Sopenharmony_ci		cfg.preferred_lft = ci->ifa_prefered;
495062306a36Sopenharmony_ci	}
495162306a36Sopenharmony_ci
495262306a36Sopenharmony_ci	dev =  __dev_get_by_index(net, ifm->ifa_index);
495362306a36Sopenharmony_ci	if (!dev) {
495462306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Unable to find the interface");
495562306a36Sopenharmony_ci		return -ENODEV;
495662306a36Sopenharmony_ci	}
495762306a36Sopenharmony_ci
495862306a36Sopenharmony_ci	if (tb[IFA_FLAGS])
495962306a36Sopenharmony_ci		cfg.ifa_flags = nla_get_u32(tb[IFA_FLAGS]);
496062306a36Sopenharmony_ci	else
496162306a36Sopenharmony_ci		cfg.ifa_flags = ifm->ifa_flags;
496262306a36Sopenharmony_ci
496362306a36Sopenharmony_ci	/* We ignore other flags so far. */
496462306a36Sopenharmony_ci	cfg.ifa_flags &= IFA_F_NODAD | IFA_F_HOMEADDRESS |
496562306a36Sopenharmony_ci			 IFA_F_MANAGETEMPADDR | IFA_F_NOPREFIXROUTE |
496662306a36Sopenharmony_ci			 IFA_F_MCAUTOJOIN | IFA_F_OPTIMISTIC;
496762306a36Sopenharmony_ci
496862306a36Sopenharmony_ci	idev = ipv6_find_idev(dev);
496962306a36Sopenharmony_ci	if (IS_ERR(idev))
497062306a36Sopenharmony_ci		return PTR_ERR(idev);
497162306a36Sopenharmony_ci
497262306a36Sopenharmony_ci	if (!ipv6_allow_optimistic_dad(net, idev))
497362306a36Sopenharmony_ci		cfg.ifa_flags &= ~IFA_F_OPTIMISTIC;
497462306a36Sopenharmony_ci
497562306a36Sopenharmony_ci	if (cfg.ifa_flags & IFA_F_NODAD &&
497662306a36Sopenharmony_ci	    cfg.ifa_flags & IFA_F_OPTIMISTIC) {
497762306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "IFA_F_NODAD and IFA_F_OPTIMISTIC are mutually exclusive");
497862306a36Sopenharmony_ci		return -EINVAL;
497962306a36Sopenharmony_ci	}
498062306a36Sopenharmony_ci
498162306a36Sopenharmony_ci	ifa = ipv6_get_ifaddr(net, cfg.pfx, dev, 1);
498262306a36Sopenharmony_ci	if (!ifa) {
498362306a36Sopenharmony_ci		/*
498462306a36Sopenharmony_ci		 * It would be best to check for !NLM_F_CREATE here but
498562306a36Sopenharmony_ci		 * userspace already relies on not having to provide this.
498662306a36Sopenharmony_ci		 */
498762306a36Sopenharmony_ci		return inet6_addr_add(net, ifm->ifa_index, &cfg, extack);
498862306a36Sopenharmony_ci	}
498962306a36Sopenharmony_ci
499062306a36Sopenharmony_ci	if (nlh->nlmsg_flags & NLM_F_EXCL ||
499162306a36Sopenharmony_ci	    !(nlh->nlmsg_flags & NLM_F_REPLACE)) {
499262306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "address already assigned");
499362306a36Sopenharmony_ci		err = -EEXIST;
499462306a36Sopenharmony_ci	} else {
499562306a36Sopenharmony_ci		err = inet6_addr_modify(net, ifa, &cfg);
499662306a36Sopenharmony_ci	}
499762306a36Sopenharmony_ci
499862306a36Sopenharmony_ci	in6_ifa_put(ifa);
499962306a36Sopenharmony_ci
500062306a36Sopenharmony_ci	return err;
500162306a36Sopenharmony_ci}
500262306a36Sopenharmony_ci
500362306a36Sopenharmony_cistatic void put_ifaddrmsg(struct nlmsghdr *nlh, u8 prefixlen, u32 flags,
500462306a36Sopenharmony_ci			  u8 scope, int ifindex)
500562306a36Sopenharmony_ci{
500662306a36Sopenharmony_ci	struct ifaddrmsg *ifm;
500762306a36Sopenharmony_ci
500862306a36Sopenharmony_ci	ifm = nlmsg_data(nlh);
500962306a36Sopenharmony_ci	ifm->ifa_family = AF_INET6;
501062306a36Sopenharmony_ci	ifm->ifa_prefixlen = prefixlen;
501162306a36Sopenharmony_ci	ifm->ifa_flags = flags;
501262306a36Sopenharmony_ci	ifm->ifa_scope = scope;
501362306a36Sopenharmony_ci	ifm->ifa_index = ifindex;
501462306a36Sopenharmony_ci}
501562306a36Sopenharmony_ci
501662306a36Sopenharmony_cistatic int put_cacheinfo(struct sk_buff *skb, unsigned long cstamp,
501762306a36Sopenharmony_ci			 unsigned long tstamp, u32 preferred, u32 valid)
501862306a36Sopenharmony_ci{
501962306a36Sopenharmony_ci	struct ifa_cacheinfo ci;
502062306a36Sopenharmony_ci
502162306a36Sopenharmony_ci	ci.cstamp = cstamp_delta(cstamp);
502262306a36Sopenharmony_ci	ci.tstamp = cstamp_delta(tstamp);
502362306a36Sopenharmony_ci	ci.ifa_prefered = preferred;
502462306a36Sopenharmony_ci	ci.ifa_valid = valid;
502562306a36Sopenharmony_ci
502662306a36Sopenharmony_ci	return nla_put(skb, IFA_CACHEINFO, sizeof(ci), &ci);
502762306a36Sopenharmony_ci}
502862306a36Sopenharmony_ci
502962306a36Sopenharmony_cistatic inline int rt_scope(int ifa_scope)
503062306a36Sopenharmony_ci{
503162306a36Sopenharmony_ci	if (ifa_scope & IFA_HOST)
503262306a36Sopenharmony_ci		return RT_SCOPE_HOST;
503362306a36Sopenharmony_ci	else if (ifa_scope & IFA_LINK)
503462306a36Sopenharmony_ci		return RT_SCOPE_LINK;
503562306a36Sopenharmony_ci	else if (ifa_scope & IFA_SITE)
503662306a36Sopenharmony_ci		return RT_SCOPE_SITE;
503762306a36Sopenharmony_ci	else
503862306a36Sopenharmony_ci		return RT_SCOPE_UNIVERSE;
503962306a36Sopenharmony_ci}
504062306a36Sopenharmony_ci
504162306a36Sopenharmony_cistatic inline int inet6_ifaddr_msgsize(void)
504262306a36Sopenharmony_ci{
504362306a36Sopenharmony_ci	return NLMSG_ALIGN(sizeof(struct ifaddrmsg))
504462306a36Sopenharmony_ci	       + nla_total_size(16) /* IFA_LOCAL */
504562306a36Sopenharmony_ci	       + nla_total_size(16) /* IFA_ADDRESS */
504662306a36Sopenharmony_ci	       + nla_total_size(sizeof(struct ifa_cacheinfo))
504762306a36Sopenharmony_ci	       + nla_total_size(4)  /* IFA_FLAGS */
504862306a36Sopenharmony_ci	       + nla_total_size(1)  /* IFA_PROTO */
504962306a36Sopenharmony_ci	       + nla_total_size(4)  /* IFA_RT_PRIORITY */;
505062306a36Sopenharmony_ci}
505162306a36Sopenharmony_ci
505262306a36Sopenharmony_cienum addr_type_t {
505362306a36Sopenharmony_ci	UNICAST_ADDR,
505462306a36Sopenharmony_ci	MULTICAST_ADDR,
505562306a36Sopenharmony_ci	ANYCAST_ADDR,
505662306a36Sopenharmony_ci};
505762306a36Sopenharmony_ci
505862306a36Sopenharmony_cistruct inet6_fill_args {
505962306a36Sopenharmony_ci	u32 portid;
506062306a36Sopenharmony_ci	u32 seq;
506162306a36Sopenharmony_ci	int event;
506262306a36Sopenharmony_ci	unsigned int flags;
506362306a36Sopenharmony_ci	int netnsid;
506462306a36Sopenharmony_ci	int ifindex;
506562306a36Sopenharmony_ci	enum addr_type_t type;
506662306a36Sopenharmony_ci};
506762306a36Sopenharmony_ci
506862306a36Sopenharmony_cistatic int inet6_fill_ifaddr(struct sk_buff *skb, struct inet6_ifaddr *ifa,
506962306a36Sopenharmony_ci			     struct inet6_fill_args *args)
507062306a36Sopenharmony_ci{
507162306a36Sopenharmony_ci	struct nlmsghdr  *nlh;
507262306a36Sopenharmony_ci	u32 preferred, valid;
507362306a36Sopenharmony_ci
507462306a36Sopenharmony_ci	nlh = nlmsg_put(skb, args->portid, args->seq, args->event,
507562306a36Sopenharmony_ci			sizeof(struct ifaddrmsg), args->flags);
507662306a36Sopenharmony_ci	if (!nlh)
507762306a36Sopenharmony_ci		return -EMSGSIZE;
507862306a36Sopenharmony_ci
507962306a36Sopenharmony_ci	put_ifaddrmsg(nlh, ifa->prefix_len, ifa->flags, rt_scope(ifa->scope),
508062306a36Sopenharmony_ci		      ifa->idev->dev->ifindex);
508162306a36Sopenharmony_ci
508262306a36Sopenharmony_ci	if (args->netnsid >= 0 &&
508362306a36Sopenharmony_ci	    nla_put_s32(skb, IFA_TARGET_NETNSID, args->netnsid))
508462306a36Sopenharmony_ci		goto error;
508562306a36Sopenharmony_ci
508662306a36Sopenharmony_ci	spin_lock_bh(&ifa->lock);
508762306a36Sopenharmony_ci	if (!((ifa->flags&IFA_F_PERMANENT) &&
508862306a36Sopenharmony_ci	      (ifa->prefered_lft == INFINITY_LIFE_TIME))) {
508962306a36Sopenharmony_ci		preferred = ifa->prefered_lft;
509062306a36Sopenharmony_ci		valid = ifa->valid_lft;
509162306a36Sopenharmony_ci		if (preferred != INFINITY_LIFE_TIME) {
509262306a36Sopenharmony_ci			long tval = (jiffies - ifa->tstamp)/HZ;
509362306a36Sopenharmony_ci			if (preferred > tval)
509462306a36Sopenharmony_ci				preferred -= tval;
509562306a36Sopenharmony_ci			else
509662306a36Sopenharmony_ci				preferred = 0;
509762306a36Sopenharmony_ci			if (valid != INFINITY_LIFE_TIME) {
509862306a36Sopenharmony_ci				if (valid > tval)
509962306a36Sopenharmony_ci					valid -= tval;
510062306a36Sopenharmony_ci				else
510162306a36Sopenharmony_ci					valid = 0;
510262306a36Sopenharmony_ci			}
510362306a36Sopenharmony_ci		}
510462306a36Sopenharmony_ci	} else {
510562306a36Sopenharmony_ci		preferred = INFINITY_LIFE_TIME;
510662306a36Sopenharmony_ci		valid = INFINITY_LIFE_TIME;
510762306a36Sopenharmony_ci	}
510862306a36Sopenharmony_ci	spin_unlock_bh(&ifa->lock);
510962306a36Sopenharmony_ci
511062306a36Sopenharmony_ci	if (!ipv6_addr_any(&ifa->peer_addr)) {
511162306a36Sopenharmony_ci		if (nla_put_in6_addr(skb, IFA_LOCAL, &ifa->addr) < 0 ||
511262306a36Sopenharmony_ci		    nla_put_in6_addr(skb, IFA_ADDRESS, &ifa->peer_addr) < 0)
511362306a36Sopenharmony_ci			goto error;
511462306a36Sopenharmony_ci	} else
511562306a36Sopenharmony_ci		if (nla_put_in6_addr(skb, IFA_ADDRESS, &ifa->addr) < 0)
511662306a36Sopenharmony_ci			goto error;
511762306a36Sopenharmony_ci
511862306a36Sopenharmony_ci	if (ifa->rt_priority &&
511962306a36Sopenharmony_ci	    nla_put_u32(skb, IFA_RT_PRIORITY, ifa->rt_priority))
512062306a36Sopenharmony_ci		goto error;
512162306a36Sopenharmony_ci
512262306a36Sopenharmony_ci	if (put_cacheinfo(skb, ifa->cstamp, ifa->tstamp, preferred, valid) < 0)
512362306a36Sopenharmony_ci		goto error;
512462306a36Sopenharmony_ci
512562306a36Sopenharmony_ci	if (nla_put_u32(skb, IFA_FLAGS, ifa->flags) < 0)
512662306a36Sopenharmony_ci		goto error;
512762306a36Sopenharmony_ci
512862306a36Sopenharmony_ci	if (ifa->ifa_proto &&
512962306a36Sopenharmony_ci	    nla_put_u8(skb, IFA_PROTO, ifa->ifa_proto))
513062306a36Sopenharmony_ci		goto error;
513162306a36Sopenharmony_ci
513262306a36Sopenharmony_ci	nlmsg_end(skb, nlh);
513362306a36Sopenharmony_ci	return 0;
513462306a36Sopenharmony_ci
513562306a36Sopenharmony_cierror:
513662306a36Sopenharmony_ci	nlmsg_cancel(skb, nlh);
513762306a36Sopenharmony_ci	return -EMSGSIZE;
513862306a36Sopenharmony_ci}
513962306a36Sopenharmony_ci
514062306a36Sopenharmony_cistatic int inet6_fill_ifmcaddr(struct sk_buff *skb, struct ifmcaddr6 *ifmca,
514162306a36Sopenharmony_ci			       struct inet6_fill_args *args)
514262306a36Sopenharmony_ci{
514362306a36Sopenharmony_ci	struct nlmsghdr  *nlh;
514462306a36Sopenharmony_ci	u8 scope = RT_SCOPE_UNIVERSE;
514562306a36Sopenharmony_ci	int ifindex = ifmca->idev->dev->ifindex;
514662306a36Sopenharmony_ci
514762306a36Sopenharmony_ci	if (ipv6_addr_scope(&ifmca->mca_addr) & IFA_SITE)
514862306a36Sopenharmony_ci		scope = RT_SCOPE_SITE;
514962306a36Sopenharmony_ci
515062306a36Sopenharmony_ci	nlh = nlmsg_put(skb, args->portid, args->seq, args->event,
515162306a36Sopenharmony_ci			sizeof(struct ifaddrmsg), args->flags);
515262306a36Sopenharmony_ci	if (!nlh)
515362306a36Sopenharmony_ci		return -EMSGSIZE;
515462306a36Sopenharmony_ci
515562306a36Sopenharmony_ci	if (args->netnsid >= 0 &&
515662306a36Sopenharmony_ci	    nla_put_s32(skb, IFA_TARGET_NETNSID, args->netnsid)) {
515762306a36Sopenharmony_ci		nlmsg_cancel(skb, nlh);
515862306a36Sopenharmony_ci		return -EMSGSIZE;
515962306a36Sopenharmony_ci	}
516062306a36Sopenharmony_ci
516162306a36Sopenharmony_ci	put_ifaddrmsg(nlh, 128, IFA_F_PERMANENT, scope, ifindex);
516262306a36Sopenharmony_ci	if (nla_put_in6_addr(skb, IFA_MULTICAST, &ifmca->mca_addr) < 0 ||
516362306a36Sopenharmony_ci	    put_cacheinfo(skb, ifmca->mca_cstamp, ifmca->mca_tstamp,
516462306a36Sopenharmony_ci			  INFINITY_LIFE_TIME, INFINITY_LIFE_TIME) < 0) {
516562306a36Sopenharmony_ci		nlmsg_cancel(skb, nlh);
516662306a36Sopenharmony_ci		return -EMSGSIZE;
516762306a36Sopenharmony_ci	}
516862306a36Sopenharmony_ci
516962306a36Sopenharmony_ci	nlmsg_end(skb, nlh);
517062306a36Sopenharmony_ci	return 0;
517162306a36Sopenharmony_ci}
517262306a36Sopenharmony_ci
517362306a36Sopenharmony_cistatic int inet6_fill_ifacaddr(struct sk_buff *skb, struct ifacaddr6 *ifaca,
517462306a36Sopenharmony_ci			       struct inet6_fill_args *args)
517562306a36Sopenharmony_ci{
517662306a36Sopenharmony_ci	struct net_device *dev = fib6_info_nh_dev(ifaca->aca_rt);
517762306a36Sopenharmony_ci	int ifindex = dev ? dev->ifindex : 1;
517862306a36Sopenharmony_ci	struct nlmsghdr  *nlh;
517962306a36Sopenharmony_ci	u8 scope = RT_SCOPE_UNIVERSE;
518062306a36Sopenharmony_ci
518162306a36Sopenharmony_ci	if (ipv6_addr_scope(&ifaca->aca_addr) & IFA_SITE)
518262306a36Sopenharmony_ci		scope = RT_SCOPE_SITE;
518362306a36Sopenharmony_ci
518462306a36Sopenharmony_ci	nlh = nlmsg_put(skb, args->portid, args->seq, args->event,
518562306a36Sopenharmony_ci			sizeof(struct ifaddrmsg), args->flags);
518662306a36Sopenharmony_ci	if (!nlh)
518762306a36Sopenharmony_ci		return -EMSGSIZE;
518862306a36Sopenharmony_ci
518962306a36Sopenharmony_ci	if (args->netnsid >= 0 &&
519062306a36Sopenharmony_ci	    nla_put_s32(skb, IFA_TARGET_NETNSID, args->netnsid)) {
519162306a36Sopenharmony_ci		nlmsg_cancel(skb, nlh);
519262306a36Sopenharmony_ci		return -EMSGSIZE;
519362306a36Sopenharmony_ci	}
519462306a36Sopenharmony_ci
519562306a36Sopenharmony_ci	put_ifaddrmsg(nlh, 128, IFA_F_PERMANENT, scope, ifindex);
519662306a36Sopenharmony_ci	if (nla_put_in6_addr(skb, IFA_ANYCAST, &ifaca->aca_addr) < 0 ||
519762306a36Sopenharmony_ci	    put_cacheinfo(skb, ifaca->aca_cstamp, ifaca->aca_tstamp,
519862306a36Sopenharmony_ci			  INFINITY_LIFE_TIME, INFINITY_LIFE_TIME) < 0) {
519962306a36Sopenharmony_ci		nlmsg_cancel(skb, nlh);
520062306a36Sopenharmony_ci		return -EMSGSIZE;
520162306a36Sopenharmony_ci	}
520262306a36Sopenharmony_ci
520362306a36Sopenharmony_ci	nlmsg_end(skb, nlh);
520462306a36Sopenharmony_ci	return 0;
520562306a36Sopenharmony_ci}
520662306a36Sopenharmony_ci
520762306a36Sopenharmony_ci/* called with rcu_read_lock() */
520862306a36Sopenharmony_cistatic int in6_dump_addrs(struct inet6_dev *idev, struct sk_buff *skb,
520962306a36Sopenharmony_ci			  struct netlink_callback *cb, int s_ip_idx,
521062306a36Sopenharmony_ci			  struct inet6_fill_args *fillargs)
521162306a36Sopenharmony_ci{
521262306a36Sopenharmony_ci	struct ifmcaddr6 *ifmca;
521362306a36Sopenharmony_ci	struct ifacaddr6 *ifaca;
521462306a36Sopenharmony_ci	int ip_idx = 0;
521562306a36Sopenharmony_ci	int err = 1;
521662306a36Sopenharmony_ci
521762306a36Sopenharmony_ci	read_lock_bh(&idev->lock);
521862306a36Sopenharmony_ci	switch (fillargs->type) {
521962306a36Sopenharmony_ci	case UNICAST_ADDR: {
522062306a36Sopenharmony_ci		struct inet6_ifaddr *ifa;
522162306a36Sopenharmony_ci		fillargs->event = RTM_NEWADDR;
522262306a36Sopenharmony_ci
522362306a36Sopenharmony_ci		/* unicast address incl. temp addr */
522462306a36Sopenharmony_ci		list_for_each_entry(ifa, &idev->addr_list, if_list) {
522562306a36Sopenharmony_ci			if (ip_idx < s_ip_idx)
522662306a36Sopenharmony_ci				goto next;
522762306a36Sopenharmony_ci			err = inet6_fill_ifaddr(skb, ifa, fillargs);
522862306a36Sopenharmony_ci			if (err < 0)
522962306a36Sopenharmony_ci				break;
523062306a36Sopenharmony_ci			nl_dump_check_consistent(cb, nlmsg_hdr(skb));
523162306a36Sopenharmony_cinext:
523262306a36Sopenharmony_ci			ip_idx++;
523362306a36Sopenharmony_ci		}
523462306a36Sopenharmony_ci		break;
523562306a36Sopenharmony_ci	}
523662306a36Sopenharmony_ci	case MULTICAST_ADDR:
523762306a36Sopenharmony_ci		read_unlock_bh(&idev->lock);
523862306a36Sopenharmony_ci		fillargs->event = RTM_GETMULTICAST;
523962306a36Sopenharmony_ci
524062306a36Sopenharmony_ci		/* multicast address */
524162306a36Sopenharmony_ci		for (ifmca = rtnl_dereference(idev->mc_list);
524262306a36Sopenharmony_ci		     ifmca;
524362306a36Sopenharmony_ci		     ifmca = rtnl_dereference(ifmca->next), ip_idx++) {
524462306a36Sopenharmony_ci			if (ip_idx < s_ip_idx)
524562306a36Sopenharmony_ci				continue;
524662306a36Sopenharmony_ci			err = inet6_fill_ifmcaddr(skb, ifmca, fillargs);
524762306a36Sopenharmony_ci			if (err < 0)
524862306a36Sopenharmony_ci				break;
524962306a36Sopenharmony_ci		}
525062306a36Sopenharmony_ci		read_lock_bh(&idev->lock);
525162306a36Sopenharmony_ci		break;
525262306a36Sopenharmony_ci	case ANYCAST_ADDR:
525362306a36Sopenharmony_ci		fillargs->event = RTM_GETANYCAST;
525462306a36Sopenharmony_ci		/* anycast address */
525562306a36Sopenharmony_ci		for (ifaca = idev->ac_list; ifaca;
525662306a36Sopenharmony_ci		     ifaca = ifaca->aca_next, ip_idx++) {
525762306a36Sopenharmony_ci			if (ip_idx < s_ip_idx)
525862306a36Sopenharmony_ci				continue;
525962306a36Sopenharmony_ci			err = inet6_fill_ifacaddr(skb, ifaca, fillargs);
526062306a36Sopenharmony_ci			if (err < 0)
526162306a36Sopenharmony_ci				break;
526262306a36Sopenharmony_ci		}
526362306a36Sopenharmony_ci		break;
526462306a36Sopenharmony_ci	default:
526562306a36Sopenharmony_ci		break;
526662306a36Sopenharmony_ci	}
526762306a36Sopenharmony_ci	read_unlock_bh(&idev->lock);
526862306a36Sopenharmony_ci	cb->args[2] = ip_idx;
526962306a36Sopenharmony_ci	return err;
527062306a36Sopenharmony_ci}
527162306a36Sopenharmony_ci
527262306a36Sopenharmony_cistatic int inet6_valid_dump_ifaddr_req(const struct nlmsghdr *nlh,
527362306a36Sopenharmony_ci				       struct inet6_fill_args *fillargs,
527462306a36Sopenharmony_ci				       struct net **tgt_net, struct sock *sk,
527562306a36Sopenharmony_ci				       struct netlink_callback *cb)
527662306a36Sopenharmony_ci{
527762306a36Sopenharmony_ci	struct netlink_ext_ack *extack = cb->extack;
527862306a36Sopenharmony_ci	struct nlattr *tb[IFA_MAX+1];
527962306a36Sopenharmony_ci	struct ifaddrmsg *ifm;
528062306a36Sopenharmony_ci	int err, i;
528162306a36Sopenharmony_ci
528262306a36Sopenharmony_ci	if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ifm))) {
528362306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Invalid header for address dump request");
528462306a36Sopenharmony_ci		return -EINVAL;
528562306a36Sopenharmony_ci	}
528662306a36Sopenharmony_ci
528762306a36Sopenharmony_ci	ifm = nlmsg_data(nlh);
528862306a36Sopenharmony_ci	if (ifm->ifa_prefixlen || ifm->ifa_flags || ifm->ifa_scope) {
528962306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Invalid values in header for address dump request");
529062306a36Sopenharmony_ci		return -EINVAL;
529162306a36Sopenharmony_ci	}
529262306a36Sopenharmony_ci
529362306a36Sopenharmony_ci	fillargs->ifindex = ifm->ifa_index;
529462306a36Sopenharmony_ci	if (fillargs->ifindex) {
529562306a36Sopenharmony_ci		cb->answer_flags |= NLM_F_DUMP_FILTERED;
529662306a36Sopenharmony_ci		fillargs->flags |= NLM_F_DUMP_FILTERED;
529762306a36Sopenharmony_ci	}
529862306a36Sopenharmony_ci
529962306a36Sopenharmony_ci	err = nlmsg_parse_deprecated_strict(nlh, sizeof(*ifm), tb, IFA_MAX,
530062306a36Sopenharmony_ci					    ifa_ipv6_policy, extack);
530162306a36Sopenharmony_ci	if (err < 0)
530262306a36Sopenharmony_ci		return err;
530362306a36Sopenharmony_ci
530462306a36Sopenharmony_ci	for (i = 0; i <= IFA_MAX; ++i) {
530562306a36Sopenharmony_ci		if (!tb[i])
530662306a36Sopenharmony_ci			continue;
530762306a36Sopenharmony_ci
530862306a36Sopenharmony_ci		if (i == IFA_TARGET_NETNSID) {
530962306a36Sopenharmony_ci			struct net *net;
531062306a36Sopenharmony_ci
531162306a36Sopenharmony_ci			fillargs->netnsid = nla_get_s32(tb[i]);
531262306a36Sopenharmony_ci			net = rtnl_get_net_ns_capable(sk, fillargs->netnsid);
531362306a36Sopenharmony_ci			if (IS_ERR(net)) {
531462306a36Sopenharmony_ci				fillargs->netnsid = -1;
531562306a36Sopenharmony_ci				NL_SET_ERR_MSG_MOD(extack, "Invalid target network namespace id");
531662306a36Sopenharmony_ci				return PTR_ERR(net);
531762306a36Sopenharmony_ci			}
531862306a36Sopenharmony_ci			*tgt_net = net;
531962306a36Sopenharmony_ci		} else {
532062306a36Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "Unsupported attribute in dump request");
532162306a36Sopenharmony_ci			return -EINVAL;
532262306a36Sopenharmony_ci		}
532362306a36Sopenharmony_ci	}
532462306a36Sopenharmony_ci
532562306a36Sopenharmony_ci	return 0;
532662306a36Sopenharmony_ci}
532762306a36Sopenharmony_ci
532862306a36Sopenharmony_cistatic int inet6_dump_addr(struct sk_buff *skb, struct netlink_callback *cb,
532962306a36Sopenharmony_ci			   enum addr_type_t type)
533062306a36Sopenharmony_ci{
533162306a36Sopenharmony_ci	const struct nlmsghdr *nlh = cb->nlh;
533262306a36Sopenharmony_ci	struct inet6_fill_args fillargs = {
533362306a36Sopenharmony_ci		.portid = NETLINK_CB(cb->skb).portid,
533462306a36Sopenharmony_ci		.seq = cb->nlh->nlmsg_seq,
533562306a36Sopenharmony_ci		.flags = NLM_F_MULTI,
533662306a36Sopenharmony_ci		.netnsid = -1,
533762306a36Sopenharmony_ci		.type = type,
533862306a36Sopenharmony_ci	};
533962306a36Sopenharmony_ci	struct net *tgt_net = sock_net(skb->sk);
534062306a36Sopenharmony_ci	int idx, s_idx, s_ip_idx;
534162306a36Sopenharmony_ci	int h, s_h;
534262306a36Sopenharmony_ci	struct net_device *dev;
534362306a36Sopenharmony_ci	struct inet6_dev *idev;
534462306a36Sopenharmony_ci	struct hlist_head *head;
534562306a36Sopenharmony_ci	int err = 0;
534662306a36Sopenharmony_ci
534762306a36Sopenharmony_ci	s_h = cb->args[0];
534862306a36Sopenharmony_ci	s_idx = idx = cb->args[1];
534962306a36Sopenharmony_ci	s_ip_idx = cb->args[2];
535062306a36Sopenharmony_ci
535162306a36Sopenharmony_ci	if (cb->strict_check) {
535262306a36Sopenharmony_ci		err = inet6_valid_dump_ifaddr_req(nlh, &fillargs, &tgt_net,
535362306a36Sopenharmony_ci						  skb->sk, cb);
535462306a36Sopenharmony_ci		if (err < 0)
535562306a36Sopenharmony_ci			goto put_tgt_net;
535662306a36Sopenharmony_ci
535762306a36Sopenharmony_ci		err = 0;
535862306a36Sopenharmony_ci		if (fillargs.ifindex) {
535962306a36Sopenharmony_ci			dev = __dev_get_by_index(tgt_net, fillargs.ifindex);
536062306a36Sopenharmony_ci			if (!dev) {
536162306a36Sopenharmony_ci				err = -ENODEV;
536262306a36Sopenharmony_ci				goto put_tgt_net;
536362306a36Sopenharmony_ci			}
536462306a36Sopenharmony_ci			idev = __in6_dev_get(dev);
536562306a36Sopenharmony_ci			if (idev) {
536662306a36Sopenharmony_ci				err = in6_dump_addrs(idev, skb, cb, s_ip_idx,
536762306a36Sopenharmony_ci						     &fillargs);
536862306a36Sopenharmony_ci				if (err > 0)
536962306a36Sopenharmony_ci					err = 0;
537062306a36Sopenharmony_ci			}
537162306a36Sopenharmony_ci			goto put_tgt_net;
537262306a36Sopenharmony_ci		}
537362306a36Sopenharmony_ci	}
537462306a36Sopenharmony_ci
537562306a36Sopenharmony_ci	rcu_read_lock();
537662306a36Sopenharmony_ci	cb->seq = inet6_base_seq(tgt_net);
537762306a36Sopenharmony_ci	for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
537862306a36Sopenharmony_ci		idx = 0;
537962306a36Sopenharmony_ci		head = &tgt_net->dev_index_head[h];
538062306a36Sopenharmony_ci		hlist_for_each_entry_rcu(dev, head, index_hlist) {
538162306a36Sopenharmony_ci			if (idx < s_idx)
538262306a36Sopenharmony_ci				goto cont;
538362306a36Sopenharmony_ci			if (h > s_h || idx > s_idx)
538462306a36Sopenharmony_ci				s_ip_idx = 0;
538562306a36Sopenharmony_ci			idev = __in6_dev_get(dev);
538662306a36Sopenharmony_ci			if (!idev)
538762306a36Sopenharmony_ci				goto cont;
538862306a36Sopenharmony_ci
538962306a36Sopenharmony_ci			if (in6_dump_addrs(idev, skb, cb, s_ip_idx,
539062306a36Sopenharmony_ci					   &fillargs) < 0)
539162306a36Sopenharmony_ci				goto done;
539262306a36Sopenharmony_cicont:
539362306a36Sopenharmony_ci			idx++;
539462306a36Sopenharmony_ci		}
539562306a36Sopenharmony_ci	}
539662306a36Sopenharmony_cidone:
539762306a36Sopenharmony_ci	rcu_read_unlock();
539862306a36Sopenharmony_ci	cb->args[0] = h;
539962306a36Sopenharmony_ci	cb->args[1] = idx;
540062306a36Sopenharmony_ciput_tgt_net:
540162306a36Sopenharmony_ci	if (fillargs.netnsid >= 0)
540262306a36Sopenharmony_ci		put_net(tgt_net);
540362306a36Sopenharmony_ci
540462306a36Sopenharmony_ci	return skb->len ? : err;
540562306a36Sopenharmony_ci}
540662306a36Sopenharmony_ci
540762306a36Sopenharmony_cistatic int inet6_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb)
540862306a36Sopenharmony_ci{
540962306a36Sopenharmony_ci	enum addr_type_t type = UNICAST_ADDR;
541062306a36Sopenharmony_ci
541162306a36Sopenharmony_ci	return inet6_dump_addr(skb, cb, type);
541262306a36Sopenharmony_ci}
541362306a36Sopenharmony_ci
541462306a36Sopenharmony_cistatic int inet6_dump_ifmcaddr(struct sk_buff *skb, struct netlink_callback *cb)
541562306a36Sopenharmony_ci{
541662306a36Sopenharmony_ci	enum addr_type_t type = MULTICAST_ADDR;
541762306a36Sopenharmony_ci
541862306a36Sopenharmony_ci	return inet6_dump_addr(skb, cb, type);
541962306a36Sopenharmony_ci}
542062306a36Sopenharmony_ci
542162306a36Sopenharmony_ci
542262306a36Sopenharmony_cistatic int inet6_dump_ifacaddr(struct sk_buff *skb, struct netlink_callback *cb)
542362306a36Sopenharmony_ci{
542462306a36Sopenharmony_ci	enum addr_type_t type = ANYCAST_ADDR;
542562306a36Sopenharmony_ci
542662306a36Sopenharmony_ci	return inet6_dump_addr(skb, cb, type);
542762306a36Sopenharmony_ci}
542862306a36Sopenharmony_ci
542962306a36Sopenharmony_cistatic int inet6_rtm_valid_getaddr_req(struct sk_buff *skb,
543062306a36Sopenharmony_ci				       const struct nlmsghdr *nlh,
543162306a36Sopenharmony_ci				       struct nlattr **tb,
543262306a36Sopenharmony_ci				       struct netlink_ext_ack *extack)
543362306a36Sopenharmony_ci{
543462306a36Sopenharmony_ci	struct ifaddrmsg *ifm;
543562306a36Sopenharmony_ci	int i, err;
543662306a36Sopenharmony_ci
543762306a36Sopenharmony_ci	if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ifm))) {
543862306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Invalid header for get address request");
543962306a36Sopenharmony_ci		return -EINVAL;
544062306a36Sopenharmony_ci	}
544162306a36Sopenharmony_ci
544262306a36Sopenharmony_ci	if (!netlink_strict_get_check(skb))
544362306a36Sopenharmony_ci		return nlmsg_parse_deprecated(nlh, sizeof(*ifm), tb, IFA_MAX,
544462306a36Sopenharmony_ci					      ifa_ipv6_policy, extack);
544562306a36Sopenharmony_ci
544662306a36Sopenharmony_ci	ifm = nlmsg_data(nlh);
544762306a36Sopenharmony_ci	if (ifm->ifa_prefixlen || ifm->ifa_flags || ifm->ifa_scope) {
544862306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Invalid values in header for get address request");
544962306a36Sopenharmony_ci		return -EINVAL;
545062306a36Sopenharmony_ci	}
545162306a36Sopenharmony_ci
545262306a36Sopenharmony_ci	err = nlmsg_parse_deprecated_strict(nlh, sizeof(*ifm), tb, IFA_MAX,
545362306a36Sopenharmony_ci					    ifa_ipv6_policy, extack);
545462306a36Sopenharmony_ci	if (err)
545562306a36Sopenharmony_ci		return err;
545662306a36Sopenharmony_ci
545762306a36Sopenharmony_ci	for (i = 0; i <= IFA_MAX; i++) {
545862306a36Sopenharmony_ci		if (!tb[i])
545962306a36Sopenharmony_ci			continue;
546062306a36Sopenharmony_ci
546162306a36Sopenharmony_ci		switch (i) {
546262306a36Sopenharmony_ci		case IFA_TARGET_NETNSID:
546362306a36Sopenharmony_ci		case IFA_ADDRESS:
546462306a36Sopenharmony_ci		case IFA_LOCAL:
546562306a36Sopenharmony_ci			break;
546662306a36Sopenharmony_ci		default:
546762306a36Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "Unsupported attribute in get address request");
546862306a36Sopenharmony_ci			return -EINVAL;
546962306a36Sopenharmony_ci		}
547062306a36Sopenharmony_ci	}
547162306a36Sopenharmony_ci
547262306a36Sopenharmony_ci	return 0;
547362306a36Sopenharmony_ci}
547462306a36Sopenharmony_ci
547562306a36Sopenharmony_cistatic int inet6_rtm_getaddr(struct sk_buff *in_skb, struct nlmsghdr *nlh,
547662306a36Sopenharmony_ci			     struct netlink_ext_ack *extack)
547762306a36Sopenharmony_ci{
547862306a36Sopenharmony_ci	struct net *tgt_net = sock_net(in_skb->sk);
547962306a36Sopenharmony_ci	struct inet6_fill_args fillargs = {
548062306a36Sopenharmony_ci		.portid = NETLINK_CB(in_skb).portid,
548162306a36Sopenharmony_ci		.seq = nlh->nlmsg_seq,
548262306a36Sopenharmony_ci		.event = RTM_NEWADDR,
548362306a36Sopenharmony_ci		.flags = 0,
548462306a36Sopenharmony_ci		.netnsid = -1,
548562306a36Sopenharmony_ci	};
548662306a36Sopenharmony_ci	struct ifaddrmsg *ifm;
548762306a36Sopenharmony_ci	struct nlattr *tb[IFA_MAX+1];
548862306a36Sopenharmony_ci	struct in6_addr *addr = NULL, *peer;
548962306a36Sopenharmony_ci	struct net_device *dev = NULL;
549062306a36Sopenharmony_ci	struct inet6_ifaddr *ifa;
549162306a36Sopenharmony_ci	struct sk_buff *skb;
549262306a36Sopenharmony_ci	int err;
549362306a36Sopenharmony_ci
549462306a36Sopenharmony_ci	err = inet6_rtm_valid_getaddr_req(in_skb, nlh, tb, extack);
549562306a36Sopenharmony_ci	if (err < 0)
549662306a36Sopenharmony_ci		return err;
549762306a36Sopenharmony_ci
549862306a36Sopenharmony_ci	if (tb[IFA_TARGET_NETNSID]) {
549962306a36Sopenharmony_ci		fillargs.netnsid = nla_get_s32(tb[IFA_TARGET_NETNSID]);
550062306a36Sopenharmony_ci
550162306a36Sopenharmony_ci		tgt_net = rtnl_get_net_ns_capable(NETLINK_CB(in_skb).sk,
550262306a36Sopenharmony_ci						  fillargs.netnsid);
550362306a36Sopenharmony_ci		if (IS_ERR(tgt_net))
550462306a36Sopenharmony_ci			return PTR_ERR(tgt_net);
550562306a36Sopenharmony_ci	}
550662306a36Sopenharmony_ci
550762306a36Sopenharmony_ci	addr = extract_addr(tb[IFA_ADDRESS], tb[IFA_LOCAL], &peer);
550862306a36Sopenharmony_ci	if (!addr) {
550962306a36Sopenharmony_ci		err = -EINVAL;
551062306a36Sopenharmony_ci		goto errout;
551162306a36Sopenharmony_ci	}
551262306a36Sopenharmony_ci	ifm = nlmsg_data(nlh);
551362306a36Sopenharmony_ci	if (ifm->ifa_index)
551462306a36Sopenharmony_ci		dev = dev_get_by_index(tgt_net, ifm->ifa_index);
551562306a36Sopenharmony_ci
551662306a36Sopenharmony_ci	ifa = ipv6_get_ifaddr(tgt_net, addr, dev, 1);
551762306a36Sopenharmony_ci	if (!ifa) {
551862306a36Sopenharmony_ci		err = -EADDRNOTAVAIL;
551962306a36Sopenharmony_ci		goto errout;
552062306a36Sopenharmony_ci	}
552162306a36Sopenharmony_ci
552262306a36Sopenharmony_ci	skb = nlmsg_new(inet6_ifaddr_msgsize(), GFP_KERNEL);
552362306a36Sopenharmony_ci	if (!skb) {
552462306a36Sopenharmony_ci		err = -ENOBUFS;
552562306a36Sopenharmony_ci		goto errout_ifa;
552662306a36Sopenharmony_ci	}
552762306a36Sopenharmony_ci
552862306a36Sopenharmony_ci	err = inet6_fill_ifaddr(skb, ifa, &fillargs);
552962306a36Sopenharmony_ci	if (err < 0) {
553062306a36Sopenharmony_ci		/* -EMSGSIZE implies BUG in inet6_ifaddr_msgsize() */
553162306a36Sopenharmony_ci		WARN_ON(err == -EMSGSIZE);
553262306a36Sopenharmony_ci		kfree_skb(skb);
553362306a36Sopenharmony_ci		goto errout_ifa;
553462306a36Sopenharmony_ci	}
553562306a36Sopenharmony_ci	err = rtnl_unicast(skb, tgt_net, NETLINK_CB(in_skb).portid);
553662306a36Sopenharmony_cierrout_ifa:
553762306a36Sopenharmony_ci	in6_ifa_put(ifa);
553862306a36Sopenharmony_cierrout:
553962306a36Sopenharmony_ci	dev_put(dev);
554062306a36Sopenharmony_ci	if (fillargs.netnsid >= 0)
554162306a36Sopenharmony_ci		put_net(tgt_net);
554262306a36Sopenharmony_ci
554362306a36Sopenharmony_ci	return err;
554462306a36Sopenharmony_ci}
554562306a36Sopenharmony_ci
554662306a36Sopenharmony_cistatic void inet6_ifa_notify(int event, struct inet6_ifaddr *ifa)
554762306a36Sopenharmony_ci{
554862306a36Sopenharmony_ci	struct sk_buff *skb;
554962306a36Sopenharmony_ci	struct net *net = dev_net(ifa->idev->dev);
555062306a36Sopenharmony_ci	struct inet6_fill_args fillargs = {
555162306a36Sopenharmony_ci		.portid = 0,
555262306a36Sopenharmony_ci		.seq = 0,
555362306a36Sopenharmony_ci		.event = event,
555462306a36Sopenharmony_ci		.flags = 0,
555562306a36Sopenharmony_ci		.netnsid = -1,
555662306a36Sopenharmony_ci	};
555762306a36Sopenharmony_ci	int err = -ENOBUFS;
555862306a36Sopenharmony_ci
555962306a36Sopenharmony_ci	skb = nlmsg_new(inet6_ifaddr_msgsize(), GFP_ATOMIC);
556062306a36Sopenharmony_ci	if (!skb)
556162306a36Sopenharmony_ci		goto errout;
556262306a36Sopenharmony_ci
556362306a36Sopenharmony_ci	err = inet6_fill_ifaddr(skb, ifa, &fillargs);
556462306a36Sopenharmony_ci	if (err < 0) {
556562306a36Sopenharmony_ci		/* -EMSGSIZE implies BUG in inet6_ifaddr_msgsize() */
556662306a36Sopenharmony_ci		WARN_ON(err == -EMSGSIZE);
556762306a36Sopenharmony_ci		kfree_skb(skb);
556862306a36Sopenharmony_ci		goto errout;
556962306a36Sopenharmony_ci	}
557062306a36Sopenharmony_ci	rtnl_notify(skb, net, 0, RTNLGRP_IPV6_IFADDR, NULL, GFP_ATOMIC);
557162306a36Sopenharmony_ci	return;
557262306a36Sopenharmony_cierrout:
557362306a36Sopenharmony_ci	if (err < 0)
557462306a36Sopenharmony_ci		rtnl_set_sk_err(net, RTNLGRP_IPV6_IFADDR, err);
557562306a36Sopenharmony_ci}
557662306a36Sopenharmony_ci
557762306a36Sopenharmony_cistatic inline void ipv6_store_devconf(struct ipv6_devconf *cnf,
557862306a36Sopenharmony_ci				__s32 *array, int bytes)
557962306a36Sopenharmony_ci{
558062306a36Sopenharmony_ci	BUG_ON(bytes < (DEVCONF_MAX * 4));
558162306a36Sopenharmony_ci
558262306a36Sopenharmony_ci	memset(array, 0, bytes);
558362306a36Sopenharmony_ci	array[DEVCONF_FORWARDING] = cnf->forwarding;
558462306a36Sopenharmony_ci	array[DEVCONF_HOPLIMIT] = cnf->hop_limit;
558562306a36Sopenharmony_ci	array[DEVCONF_MTU6] = cnf->mtu6;
558662306a36Sopenharmony_ci	array[DEVCONF_ACCEPT_RA] = cnf->accept_ra;
558762306a36Sopenharmony_ci	array[DEVCONF_ACCEPT_REDIRECTS] = cnf->accept_redirects;
558862306a36Sopenharmony_ci	array[DEVCONF_AUTOCONF] = cnf->autoconf;
558962306a36Sopenharmony_ci	array[DEVCONF_DAD_TRANSMITS] = cnf->dad_transmits;
559062306a36Sopenharmony_ci	array[DEVCONF_RTR_SOLICITS] = cnf->rtr_solicits;
559162306a36Sopenharmony_ci	array[DEVCONF_RTR_SOLICIT_INTERVAL] =
559262306a36Sopenharmony_ci		jiffies_to_msecs(cnf->rtr_solicit_interval);
559362306a36Sopenharmony_ci	array[DEVCONF_RTR_SOLICIT_MAX_INTERVAL] =
559462306a36Sopenharmony_ci		jiffies_to_msecs(cnf->rtr_solicit_max_interval);
559562306a36Sopenharmony_ci	array[DEVCONF_RTR_SOLICIT_DELAY] =
559662306a36Sopenharmony_ci		jiffies_to_msecs(cnf->rtr_solicit_delay);
559762306a36Sopenharmony_ci	array[DEVCONF_FORCE_MLD_VERSION] = cnf->force_mld_version;
559862306a36Sopenharmony_ci	array[DEVCONF_MLDV1_UNSOLICITED_REPORT_INTERVAL] =
559962306a36Sopenharmony_ci		jiffies_to_msecs(cnf->mldv1_unsolicited_report_interval);
560062306a36Sopenharmony_ci	array[DEVCONF_MLDV2_UNSOLICITED_REPORT_INTERVAL] =
560162306a36Sopenharmony_ci		jiffies_to_msecs(cnf->mldv2_unsolicited_report_interval);
560262306a36Sopenharmony_ci	array[DEVCONF_USE_TEMPADDR] = cnf->use_tempaddr;
560362306a36Sopenharmony_ci	array[DEVCONF_TEMP_VALID_LFT] = cnf->temp_valid_lft;
560462306a36Sopenharmony_ci	array[DEVCONF_TEMP_PREFERED_LFT] = cnf->temp_prefered_lft;
560562306a36Sopenharmony_ci	array[DEVCONF_REGEN_MAX_RETRY] = cnf->regen_max_retry;
560662306a36Sopenharmony_ci	array[DEVCONF_MAX_DESYNC_FACTOR] = cnf->max_desync_factor;
560762306a36Sopenharmony_ci	array[DEVCONF_MAX_ADDRESSES] = cnf->max_addresses;
560862306a36Sopenharmony_ci	array[DEVCONF_ACCEPT_RA_DEFRTR] = cnf->accept_ra_defrtr;
560962306a36Sopenharmony_ci	array[DEVCONF_RA_DEFRTR_METRIC] = cnf->ra_defrtr_metric;
561062306a36Sopenharmony_ci	array[DEVCONF_ACCEPT_RA_MIN_HOP_LIMIT] = cnf->accept_ra_min_hop_limit;
561162306a36Sopenharmony_ci	array[DEVCONF_ACCEPT_RA_PINFO] = cnf->accept_ra_pinfo;
561262306a36Sopenharmony_ci#ifdef CONFIG_IPV6_ROUTER_PREF
561362306a36Sopenharmony_ci	array[DEVCONF_ACCEPT_RA_RTR_PREF] = cnf->accept_ra_rtr_pref;
561462306a36Sopenharmony_ci	array[DEVCONF_RTR_PROBE_INTERVAL] =
561562306a36Sopenharmony_ci		jiffies_to_msecs(cnf->rtr_probe_interval);
561662306a36Sopenharmony_ci#ifdef CONFIG_IPV6_ROUTE_INFO
561762306a36Sopenharmony_ci	array[DEVCONF_ACCEPT_RA_RT_INFO_MIN_PLEN] = cnf->accept_ra_rt_info_min_plen;
561862306a36Sopenharmony_ci	array[DEVCONF_ACCEPT_RA_RT_INFO_MAX_PLEN] = cnf->accept_ra_rt_info_max_plen;
561962306a36Sopenharmony_ci#endif
562062306a36Sopenharmony_ci#endif
562162306a36Sopenharmony_ci	array[DEVCONF_PROXY_NDP] = cnf->proxy_ndp;
562262306a36Sopenharmony_ci	array[DEVCONF_ACCEPT_SOURCE_ROUTE] = cnf->accept_source_route;
562362306a36Sopenharmony_ci#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
562462306a36Sopenharmony_ci	array[DEVCONF_OPTIMISTIC_DAD] = cnf->optimistic_dad;
562562306a36Sopenharmony_ci	array[DEVCONF_USE_OPTIMISTIC] = cnf->use_optimistic;
562662306a36Sopenharmony_ci#endif
562762306a36Sopenharmony_ci#ifdef CONFIG_IPV6_MROUTE
562862306a36Sopenharmony_ci	array[DEVCONF_MC_FORWARDING] = atomic_read(&cnf->mc_forwarding);
562962306a36Sopenharmony_ci#endif
563062306a36Sopenharmony_ci	array[DEVCONF_DISABLE_IPV6] = cnf->disable_ipv6;
563162306a36Sopenharmony_ci	array[DEVCONF_ACCEPT_DAD] = cnf->accept_dad;
563262306a36Sopenharmony_ci	array[DEVCONF_FORCE_TLLAO] = cnf->force_tllao;
563362306a36Sopenharmony_ci	array[DEVCONF_NDISC_NOTIFY] = cnf->ndisc_notify;
563462306a36Sopenharmony_ci	array[DEVCONF_SUPPRESS_FRAG_NDISC] = cnf->suppress_frag_ndisc;
563562306a36Sopenharmony_ci	array[DEVCONF_ACCEPT_RA_FROM_LOCAL] = cnf->accept_ra_from_local;
563662306a36Sopenharmony_ci	array[DEVCONF_ACCEPT_RA_MTU] = cnf->accept_ra_mtu;
563762306a36Sopenharmony_ci	array[DEVCONF_IGNORE_ROUTES_WITH_LINKDOWN] = cnf->ignore_routes_with_linkdown;
563862306a36Sopenharmony_ci	/* we omit DEVCONF_STABLE_SECRET for now */
563962306a36Sopenharmony_ci	array[DEVCONF_USE_OIF_ADDRS_ONLY] = cnf->use_oif_addrs_only;
564062306a36Sopenharmony_ci	array[DEVCONF_DROP_UNICAST_IN_L2_MULTICAST] = cnf->drop_unicast_in_l2_multicast;
564162306a36Sopenharmony_ci	array[DEVCONF_DROP_UNSOLICITED_NA] = cnf->drop_unsolicited_na;
564262306a36Sopenharmony_ci	array[DEVCONF_KEEP_ADDR_ON_DOWN] = cnf->keep_addr_on_down;
564362306a36Sopenharmony_ci	array[DEVCONF_SEG6_ENABLED] = cnf->seg6_enabled;
564462306a36Sopenharmony_ci#ifdef CONFIG_IPV6_SEG6_HMAC
564562306a36Sopenharmony_ci	array[DEVCONF_SEG6_REQUIRE_HMAC] = cnf->seg6_require_hmac;
564662306a36Sopenharmony_ci#endif
564762306a36Sopenharmony_ci	array[DEVCONF_ENHANCED_DAD] = cnf->enhanced_dad;
564862306a36Sopenharmony_ci	array[DEVCONF_ADDR_GEN_MODE] = cnf->addr_gen_mode;
564962306a36Sopenharmony_ci	array[DEVCONF_DISABLE_POLICY] = cnf->disable_policy;
565062306a36Sopenharmony_ci	array[DEVCONF_NDISC_TCLASS] = cnf->ndisc_tclass;
565162306a36Sopenharmony_ci	array[DEVCONF_RPL_SEG_ENABLED] = cnf->rpl_seg_enabled;
565262306a36Sopenharmony_ci	array[DEVCONF_IOAM6_ENABLED] = cnf->ioam6_enabled;
565362306a36Sopenharmony_ci	array[DEVCONF_IOAM6_ID] = cnf->ioam6_id;
565462306a36Sopenharmony_ci	array[DEVCONF_IOAM6_ID_WIDE] = cnf->ioam6_id_wide;
565562306a36Sopenharmony_ci	array[DEVCONF_NDISC_EVICT_NOCARRIER] = cnf->ndisc_evict_nocarrier;
565662306a36Sopenharmony_ci	array[DEVCONF_ACCEPT_UNTRACKED_NA] = cnf->accept_untracked_na;
565762306a36Sopenharmony_ci	array[DEVCONF_ACCEPT_RA_MIN_LFT] = cnf->accept_ra_min_lft;
565862306a36Sopenharmony_ci}
565962306a36Sopenharmony_ci
566062306a36Sopenharmony_cistatic inline size_t inet6_ifla6_size(void)
566162306a36Sopenharmony_ci{
566262306a36Sopenharmony_ci	return nla_total_size(4) /* IFLA_INET6_FLAGS */
566362306a36Sopenharmony_ci	     + nla_total_size(sizeof(struct ifla_cacheinfo))
566462306a36Sopenharmony_ci	     + nla_total_size(DEVCONF_MAX * 4) /* IFLA_INET6_CONF */
566562306a36Sopenharmony_ci	     + nla_total_size(IPSTATS_MIB_MAX * 8) /* IFLA_INET6_STATS */
566662306a36Sopenharmony_ci	     + nla_total_size(ICMP6_MIB_MAX * 8) /* IFLA_INET6_ICMP6STATS */
566762306a36Sopenharmony_ci	     + nla_total_size(sizeof(struct in6_addr)) /* IFLA_INET6_TOKEN */
566862306a36Sopenharmony_ci	     + nla_total_size(1) /* IFLA_INET6_ADDR_GEN_MODE */
566962306a36Sopenharmony_ci	     + nla_total_size(4) /* IFLA_INET6_RA_MTU */
567062306a36Sopenharmony_ci	     + 0;
567162306a36Sopenharmony_ci}
567262306a36Sopenharmony_ci
567362306a36Sopenharmony_cistatic inline size_t inet6_if_nlmsg_size(void)
567462306a36Sopenharmony_ci{
567562306a36Sopenharmony_ci	return NLMSG_ALIGN(sizeof(struct ifinfomsg))
567662306a36Sopenharmony_ci	       + nla_total_size(IFNAMSIZ) /* IFLA_IFNAME */
567762306a36Sopenharmony_ci	       + nla_total_size(MAX_ADDR_LEN) /* IFLA_ADDRESS */
567862306a36Sopenharmony_ci	       + nla_total_size(4) /* IFLA_MTU */
567962306a36Sopenharmony_ci	       + nla_total_size(4) /* IFLA_LINK */
568062306a36Sopenharmony_ci	       + nla_total_size(1) /* IFLA_OPERSTATE */
568162306a36Sopenharmony_ci	       + nla_total_size(inet6_ifla6_size()); /* IFLA_PROTINFO */
568262306a36Sopenharmony_ci}
568362306a36Sopenharmony_ci
568462306a36Sopenharmony_cistatic inline void __snmp6_fill_statsdev(u64 *stats, atomic_long_t *mib,
568562306a36Sopenharmony_ci					int bytes)
568662306a36Sopenharmony_ci{
568762306a36Sopenharmony_ci	int i;
568862306a36Sopenharmony_ci	int pad = bytes - sizeof(u64) * ICMP6_MIB_MAX;
568962306a36Sopenharmony_ci	BUG_ON(pad < 0);
569062306a36Sopenharmony_ci
569162306a36Sopenharmony_ci	/* Use put_unaligned() because stats may not be aligned for u64. */
569262306a36Sopenharmony_ci	put_unaligned(ICMP6_MIB_MAX, &stats[0]);
569362306a36Sopenharmony_ci	for (i = 1; i < ICMP6_MIB_MAX; i++)
569462306a36Sopenharmony_ci		put_unaligned(atomic_long_read(&mib[i]), &stats[i]);
569562306a36Sopenharmony_ci
569662306a36Sopenharmony_ci	memset(&stats[ICMP6_MIB_MAX], 0, pad);
569762306a36Sopenharmony_ci}
569862306a36Sopenharmony_ci
569962306a36Sopenharmony_cistatic inline void __snmp6_fill_stats64(u64 *stats, void __percpu *mib,
570062306a36Sopenharmony_ci					int bytes, size_t syncpoff)
570162306a36Sopenharmony_ci{
570262306a36Sopenharmony_ci	int i, c;
570362306a36Sopenharmony_ci	u64 buff[IPSTATS_MIB_MAX];
570462306a36Sopenharmony_ci	int pad = bytes - sizeof(u64) * IPSTATS_MIB_MAX;
570562306a36Sopenharmony_ci
570662306a36Sopenharmony_ci	BUG_ON(pad < 0);
570762306a36Sopenharmony_ci
570862306a36Sopenharmony_ci	memset(buff, 0, sizeof(buff));
570962306a36Sopenharmony_ci	buff[0] = IPSTATS_MIB_MAX;
571062306a36Sopenharmony_ci
571162306a36Sopenharmony_ci	for_each_possible_cpu(c) {
571262306a36Sopenharmony_ci		for (i = 1; i < IPSTATS_MIB_MAX; i++)
571362306a36Sopenharmony_ci			buff[i] += snmp_get_cpu_field64(mib, c, i, syncpoff);
571462306a36Sopenharmony_ci	}
571562306a36Sopenharmony_ci
571662306a36Sopenharmony_ci	memcpy(stats, buff, IPSTATS_MIB_MAX * sizeof(u64));
571762306a36Sopenharmony_ci	memset(&stats[IPSTATS_MIB_MAX], 0, pad);
571862306a36Sopenharmony_ci}
571962306a36Sopenharmony_ci
572062306a36Sopenharmony_cistatic void snmp6_fill_stats(u64 *stats, struct inet6_dev *idev, int attrtype,
572162306a36Sopenharmony_ci			     int bytes)
572262306a36Sopenharmony_ci{
572362306a36Sopenharmony_ci	switch (attrtype) {
572462306a36Sopenharmony_ci	case IFLA_INET6_STATS:
572562306a36Sopenharmony_ci		__snmp6_fill_stats64(stats, idev->stats.ipv6, bytes,
572662306a36Sopenharmony_ci				     offsetof(struct ipstats_mib, syncp));
572762306a36Sopenharmony_ci		break;
572862306a36Sopenharmony_ci	case IFLA_INET6_ICMP6STATS:
572962306a36Sopenharmony_ci		__snmp6_fill_statsdev(stats, idev->stats.icmpv6dev->mibs, bytes);
573062306a36Sopenharmony_ci		break;
573162306a36Sopenharmony_ci	}
573262306a36Sopenharmony_ci}
573362306a36Sopenharmony_ci
573462306a36Sopenharmony_cistatic int inet6_fill_ifla6_attrs(struct sk_buff *skb, struct inet6_dev *idev,
573562306a36Sopenharmony_ci				  u32 ext_filter_mask)
573662306a36Sopenharmony_ci{
573762306a36Sopenharmony_ci	struct nlattr *nla;
573862306a36Sopenharmony_ci	struct ifla_cacheinfo ci;
573962306a36Sopenharmony_ci
574062306a36Sopenharmony_ci	if (nla_put_u32(skb, IFLA_INET6_FLAGS, idev->if_flags))
574162306a36Sopenharmony_ci		goto nla_put_failure;
574262306a36Sopenharmony_ci	ci.max_reasm_len = IPV6_MAXPLEN;
574362306a36Sopenharmony_ci	ci.tstamp = cstamp_delta(idev->tstamp);
574462306a36Sopenharmony_ci	ci.reachable_time = jiffies_to_msecs(idev->nd_parms->reachable_time);
574562306a36Sopenharmony_ci	ci.retrans_time = jiffies_to_msecs(NEIGH_VAR(idev->nd_parms, RETRANS_TIME));
574662306a36Sopenharmony_ci	if (nla_put(skb, IFLA_INET6_CACHEINFO, sizeof(ci), &ci))
574762306a36Sopenharmony_ci		goto nla_put_failure;
574862306a36Sopenharmony_ci	nla = nla_reserve(skb, IFLA_INET6_CONF, DEVCONF_MAX * sizeof(s32));
574962306a36Sopenharmony_ci	if (!nla)
575062306a36Sopenharmony_ci		goto nla_put_failure;
575162306a36Sopenharmony_ci	ipv6_store_devconf(&idev->cnf, nla_data(nla), nla_len(nla));
575262306a36Sopenharmony_ci
575362306a36Sopenharmony_ci	/* XXX - MC not implemented */
575462306a36Sopenharmony_ci
575562306a36Sopenharmony_ci	if (ext_filter_mask & RTEXT_FILTER_SKIP_STATS)
575662306a36Sopenharmony_ci		return 0;
575762306a36Sopenharmony_ci
575862306a36Sopenharmony_ci	nla = nla_reserve(skb, IFLA_INET6_STATS, IPSTATS_MIB_MAX * sizeof(u64));
575962306a36Sopenharmony_ci	if (!nla)
576062306a36Sopenharmony_ci		goto nla_put_failure;
576162306a36Sopenharmony_ci	snmp6_fill_stats(nla_data(nla), idev, IFLA_INET6_STATS, nla_len(nla));
576262306a36Sopenharmony_ci
576362306a36Sopenharmony_ci	nla = nla_reserve(skb, IFLA_INET6_ICMP6STATS, ICMP6_MIB_MAX * sizeof(u64));
576462306a36Sopenharmony_ci	if (!nla)
576562306a36Sopenharmony_ci		goto nla_put_failure;
576662306a36Sopenharmony_ci	snmp6_fill_stats(nla_data(nla), idev, IFLA_INET6_ICMP6STATS, nla_len(nla));
576762306a36Sopenharmony_ci
576862306a36Sopenharmony_ci	nla = nla_reserve(skb, IFLA_INET6_TOKEN, sizeof(struct in6_addr));
576962306a36Sopenharmony_ci	if (!nla)
577062306a36Sopenharmony_ci		goto nla_put_failure;
577162306a36Sopenharmony_ci	read_lock_bh(&idev->lock);
577262306a36Sopenharmony_ci	memcpy(nla_data(nla), idev->token.s6_addr, nla_len(nla));
577362306a36Sopenharmony_ci	read_unlock_bh(&idev->lock);
577462306a36Sopenharmony_ci
577562306a36Sopenharmony_ci	if (nla_put_u8(skb, IFLA_INET6_ADDR_GEN_MODE, idev->cnf.addr_gen_mode))
577662306a36Sopenharmony_ci		goto nla_put_failure;
577762306a36Sopenharmony_ci
577862306a36Sopenharmony_ci	if (idev->ra_mtu &&
577962306a36Sopenharmony_ci	    nla_put_u32(skb, IFLA_INET6_RA_MTU, idev->ra_mtu))
578062306a36Sopenharmony_ci		goto nla_put_failure;
578162306a36Sopenharmony_ci
578262306a36Sopenharmony_ci	return 0;
578362306a36Sopenharmony_ci
578462306a36Sopenharmony_cinla_put_failure:
578562306a36Sopenharmony_ci	return -EMSGSIZE;
578662306a36Sopenharmony_ci}
578762306a36Sopenharmony_ci
578862306a36Sopenharmony_cistatic size_t inet6_get_link_af_size(const struct net_device *dev,
578962306a36Sopenharmony_ci				     u32 ext_filter_mask)
579062306a36Sopenharmony_ci{
579162306a36Sopenharmony_ci	if (!__in6_dev_get(dev))
579262306a36Sopenharmony_ci		return 0;
579362306a36Sopenharmony_ci
579462306a36Sopenharmony_ci	return inet6_ifla6_size();
579562306a36Sopenharmony_ci}
579662306a36Sopenharmony_ci
579762306a36Sopenharmony_cistatic int inet6_fill_link_af(struct sk_buff *skb, const struct net_device *dev,
579862306a36Sopenharmony_ci			      u32 ext_filter_mask)
579962306a36Sopenharmony_ci{
580062306a36Sopenharmony_ci	struct inet6_dev *idev = __in6_dev_get(dev);
580162306a36Sopenharmony_ci
580262306a36Sopenharmony_ci	if (!idev)
580362306a36Sopenharmony_ci		return -ENODATA;
580462306a36Sopenharmony_ci
580562306a36Sopenharmony_ci	if (inet6_fill_ifla6_attrs(skb, idev, ext_filter_mask) < 0)
580662306a36Sopenharmony_ci		return -EMSGSIZE;
580762306a36Sopenharmony_ci
580862306a36Sopenharmony_ci	return 0;
580962306a36Sopenharmony_ci}
581062306a36Sopenharmony_ci
581162306a36Sopenharmony_cistatic int inet6_set_iftoken(struct inet6_dev *idev, struct in6_addr *token,
581262306a36Sopenharmony_ci			     struct netlink_ext_ack *extack)
581362306a36Sopenharmony_ci{
581462306a36Sopenharmony_ci	struct inet6_ifaddr *ifp;
581562306a36Sopenharmony_ci	struct net_device *dev = idev->dev;
581662306a36Sopenharmony_ci	bool clear_token, update_rs = false;
581762306a36Sopenharmony_ci	struct in6_addr ll_addr;
581862306a36Sopenharmony_ci
581962306a36Sopenharmony_ci	ASSERT_RTNL();
582062306a36Sopenharmony_ci
582162306a36Sopenharmony_ci	if (!token)
582262306a36Sopenharmony_ci		return -EINVAL;
582362306a36Sopenharmony_ci
582462306a36Sopenharmony_ci	if (dev->flags & IFF_LOOPBACK) {
582562306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Device is loopback");
582662306a36Sopenharmony_ci		return -EINVAL;
582762306a36Sopenharmony_ci	}
582862306a36Sopenharmony_ci
582962306a36Sopenharmony_ci	if (dev->flags & IFF_NOARP) {
583062306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack,
583162306a36Sopenharmony_ci				   "Device does not do neighbour discovery");
583262306a36Sopenharmony_ci		return -EINVAL;
583362306a36Sopenharmony_ci	}
583462306a36Sopenharmony_ci
583562306a36Sopenharmony_ci	if (!ipv6_accept_ra(idev)) {
583662306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack,
583762306a36Sopenharmony_ci				   "Router advertisement is disabled on device");
583862306a36Sopenharmony_ci		return -EINVAL;
583962306a36Sopenharmony_ci	}
584062306a36Sopenharmony_ci
584162306a36Sopenharmony_ci	if (idev->cnf.rtr_solicits == 0) {
584262306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack,
584362306a36Sopenharmony_ci			       "Router solicitation is disabled on device");
584462306a36Sopenharmony_ci		return -EINVAL;
584562306a36Sopenharmony_ci	}
584662306a36Sopenharmony_ci
584762306a36Sopenharmony_ci	write_lock_bh(&idev->lock);
584862306a36Sopenharmony_ci
584962306a36Sopenharmony_ci	BUILD_BUG_ON(sizeof(token->s6_addr) != 16);
585062306a36Sopenharmony_ci	memcpy(idev->token.s6_addr + 8, token->s6_addr + 8, 8);
585162306a36Sopenharmony_ci
585262306a36Sopenharmony_ci	write_unlock_bh(&idev->lock);
585362306a36Sopenharmony_ci
585462306a36Sopenharmony_ci	clear_token = ipv6_addr_any(token);
585562306a36Sopenharmony_ci	if (clear_token)
585662306a36Sopenharmony_ci		goto update_lft;
585762306a36Sopenharmony_ci
585862306a36Sopenharmony_ci	if (!idev->dead && (idev->if_flags & IF_READY) &&
585962306a36Sopenharmony_ci	    !ipv6_get_lladdr(dev, &ll_addr, IFA_F_TENTATIVE |
586062306a36Sopenharmony_ci			     IFA_F_OPTIMISTIC)) {
586162306a36Sopenharmony_ci		/* If we're not ready, then normal ifup will take care
586262306a36Sopenharmony_ci		 * of this. Otherwise, we need to request our rs here.
586362306a36Sopenharmony_ci		 */
586462306a36Sopenharmony_ci		ndisc_send_rs(dev, &ll_addr, &in6addr_linklocal_allrouters);
586562306a36Sopenharmony_ci		update_rs = true;
586662306a36Sopenharmony_ci	}
586762306a36Sopenharmony_ci
586862306a36Sopenharmony_ciupdate_lft:
586962306a36Sopenharmony_ci	write_lock_bh(&idev->lock);
587062306a36Sopenharmony_ci
587162306a36Sopenharmony_ci	if (update_rs) {
587262306a36Sopenharmony_ci		idev->if_flags |= IF_RS_SENT;
587362306a36Sopenharmony_ci		idev->rs_interval = rfc3315_s14_backoff_init(
587462306a36Sopenharmony_ci			idev->cnf.rtr_solicit_interval);
587562306a36Sopenharmony_ci		idev->rs_probes = 1;
587662306a36Sopenharmony_ci		addrconf_mod_rs_timer(idev, idev->rs_interval);
587762306a36Sopenharmony_ci	}
587862306a36Sopenharmony_ci
587962306a36Sopenharmony_ci	/* Well, that's kinda nasty ... */
588062306a36Sopenharmony_ci	list_for_each_entry(ifp, &idev->addr_list, if_list) {
588162306a36Sopenharmony_ci		spin_lock(&ifp->lock);
588262306a36Sopenharmony_ci		if (ifp->tokenized) {
588362306a36Sopenharmony_ci			ifp->valid_lft = 0;
588462306a36Sopenharmony_ci			ifp->prefered_lft = 0;
588562306a36Sopenharmony_ci		}
588662306a36Sopenharmony_ci		spin_unlock(&ifp->lock);
588762306a36Sopenharmony_ci	}
588862306a36Sopenharmony_ci
588962306a36Sopenharmony_ci	write_unlock_bh(&idev->lock);
589062306a36Sopenharmony_ci	inet6_ifinfo_notify(RTM_NEWLINK, idev);
589162306a36Sopenharmony_ci	addrconf_verify_rtnl(dev_net(dev));
589262306a36Sopenharmony_ci	return 0;
589362306a36Sopenharmony_ci}
589462306a36Sopenharmony_ci
589562306a36Sopenharmony_cistatic const struct nla_policy inet6_af_policy[IFLA_INET6_MAX + 1] = {
589662306a36Sopenharmony_ci	[IFLA_INET6_ADDR_GEN_MODE]	= { .type = NLA_U8 },
589762306a36Sopenharmony_ci	[IFLA_INET6_TOKEN]		= { .len = sizeof(struct in6_addr) },
589862306a36Sopenharmony_ci	[IFLA_INET6_RA_MTU]		= { .type = NLA_REJECT,
589962306a36Sopenharmony_ci					    .reject_message =
590062306a36Sopenharmony_ci						"IFLA_INET6_RA_MTU can not be set" },
590162306a36Sopenharmony_ci};
590262306a36Sopenharmony_ci
590362306a36Sopenharmony_cistatic int check_addr_gen_mode(int mode)
590462306a36Sopenharmony_ci{
590562306a36Sopenharmony_ci	if (mode != IN6_ADDR_GEN_MODE_EUI64 &&
590662306a36Sopenharmony_ci	    mode != IN6_ADDR_GEN_MODE_NONE &&
590762306a36Sopenharmony_ci	    mode != IN6_ADDR_GEN_MODE_STABLE_PRIVACY &&
590862306a36Sopenharmony_ci	    mode != IN6_ADDR_GEN_MODE_RANDOM)
590962306a36Sopenharmony_ci		return -EINVAL;
591062306a36Sopenharmony_ci	return 1;
591162306a36Sopenharmony_ci}
591262306a36Sopenharmony_ci
591362306a36Sopenharmony_cistatic int check_stable_privacy(struct inet6_dev *idev, struct net *net,
591462306a36Sopenharmony_ci				int mode)
591562306a36Sopenharmony_ci{
591662306a36Sopenharmony_ci	if (mode == IN6_ADDR_GEN_MODE_STABLE_PRIVACY &&
591762306a36Sopenharmony_ci	    !idev->cnf.stable_secret.initialized &&
591862306a36Sopenharmony_ci	    !net->ipv6.devconf_dflt->stable_secret.initialized)
591962306a36Sopenharmony_ci		return -EINVAL;
592062306a36Sopenharmony_ci	return 1;
592162306a36Sopenharmony_ci}
592262306a36Sopenharmony_ci
592362306a36Sopenharmony_cistatic int inet6_validate_link_af(const struct net_device *dev,
592462306a36Sopenharmony_ci				  const struct nlattr *nla,
592562306a36Sopenharmony_ci				  struct netlink_ext_ack *extack)
592662306a36Sopenharmony_ci{
592762306a36Sopenharmony_ci	struct nlattr *tb[IFLA_INET6_MAX + 1];
592862306a36Sopenharmony_ci	struct inet6_dev *idev = NULL;
592962306a36Sopenharmony_ci	int err;
593062306a36Sopenharmony_ci
593162306a36Sopenharmony_ci	if (dev) {
593262306a36Sopenharmony_ci		idev = __in6_dev_get(dev);
593362306a36Sopenharmony_ci		if (!idev)
593462306a36Sopenharmony_ci			return -EAFNOSUPPORT;
593562306a36Sopenharmony_ci	}
593662306a36Sopenharmony_ci
593762306a36Sopenharmony_ci	err = nla_parse_nested_deprecated(tb, IFLA_INET6_MAX, nla,
593862306a36Sopenharmony_ci					  inet6_af_policy, extack);
593962306a36Sopenharmony_ci	if (err)
594062306a36Sopenharmony_ci		return err;
594162306a36Sopenharmony_ci
594262306a36Sopenharmony_ci	if (!tb[IFLA_INET6_TOKEN] && !tb[IFLA_INET6_ADDR_GEN_MODE])
594362306a36Sopenharmony_ci		return -EINVAL;
594462306a36Sopenharmony_ci
594562306a36Sopenharmony_ci	if (tb[IFLA_INET6_ADDR_GEN_MODE]) {
594662306a36Sopenharmony_ci		u8 mode = nla_get_u8(tb[IFLA_INET6_ADDR_GEN_MODE]);
594762306a36Sopenharmony_ci
594862306a36Sopenharmony_ci		if (check_addr_gen_mode(mode) < 0)
594962306a36Sopenharmony_ci			return -EINVAL;
595062306a36Sopenharmony_ci		if (dev && check_stable_privacy(idev, dev_net(dev), mode) < 0)
595162306a36Sopenharmony_ci			return -EINVAL;
595262306a36Sopenharmony_ci	}
595362306a36Sopenharmony_ci
595462306a36Sopenharmony_ci	return 0;
595562306a36Sopenharmony_ci}
595662306a36Sopenharmony_ci
595762306a36Sopenharmony_cistatic int inet6_set_link_af(struct net_device *dev, const struct nlattr *nla,
595862306a36Sopenharmony_ci			     struct netlink_ext_ack *extack)
595962306a36Sopenharmony_ci{
596062306a36Sopenharmony_ci	struct inet6_dev *idev = __in6_dev_get(dev);
596162306a36Sopenharmony_ci	struct nlattr *tb[IFLA_INET6_MAX + 1];
596262306a36Sopenharmony_ci	int err;
596362306a36Sopenharmony_ci
596462306a36Sopenharmony_ci	if (!idev)
596562306a36Sopenharmony_ci		return -EAFNOSUPPORT;
596662306a36Sopenharmony_ci
596762306a36Sopenharmony_ci	if (nla_parse_nested_deprecated(tb, IFLA_INET6_MAX, nla, NULL, NULL) < 0)
596862306a36Sopenharmony_ci		return -EINVAL;
596962306a36Sopenharmony_ci
597062306a36Sopenharmony_ci	if (tb[IFLA_INET6_TOKEN]) {
597162306a36Sopenharmony_ci		err = inet6_set_iftoken(idev, nla_data(tb[IFLA_INET6_TOKEN]),
597262306a36Sopenharmony_ci					extack);
597362306a36Sopenharmony_ci		if (err)
597462306a36Sopenharmony_ci			return err;
597562306a36Sopenharmony_ci	}
597662306a36Sopenharmony_ci
597762306a36Sopenharmony_ci	if (tb[IFLA_INET6_ADDR_GEN_MODE]) {
597862306a36Sopenharmony_ci		u8 mode = nla_get_u8(tb[IFLA_INET6_ADDR_GEN_MODE]);
597962306a36Sopenharmony_ci
598062306a36Sopenharmony_ci		idev->cnf.addr_gen_mode = mode;
598162306a36Sopenharmony_ci	}
598262306a36Sopenharmony_ci
598362306a36Sopenharmony_ci	return 0;
598462306a36Sopenharmony_ci}
598562306a36Sopenharmony_ci
598662306a36Sopenharmony_cistatic int inet6_fill_ifinfo(struct sk_buff *skb, struct inet6_dev *idev,
598762306a36Sopenharmony_ci			     u32 portid, u32 seq, int event, unsigned int flags)
598862306a36Sopenharmony_ci{
598962306a36Sopenharmony_ci	struct net_device *dev = idev->dev;
599062306a36Sopenharmony_ci	struct ifinfomsg *hdr;
599162306a36Sopenharmony_ci	struct nlmsghdr *nlh;
599262306a36Sopenharmony_ci	void *protoinfo;
599362306a36Sopenharmony_ci
599462306a36Sopenharmony_ci	nlh = nlmsg_put(skb, portid, seq, event, sizeof(*hdr), flags);
599562306a36Sopenharmony_ci	if (!nlh)
599662306a36Sopenharmony_ci		return -EMSGSIZE;
599762306a36Sopenharmony_ci
599862306a36Sopenharmony_ci	hdr = nlmsg_data(nlh);
599962306a36Sopenharmony_ci	hdr->ifi_family = AF_INET6;
600062306a36Sopenharmony_ci	hdr->__ifi_pad = 0;
600162306a36Sopenharmony_ci	hdr->ifi_type = dev->type;
600262306a36Sopenharmony_ci	hdr->ifi_index = dev->ifindex;
600362306a36Sopenharmony_ci	hdr->ifi_flags = dev_get_flags(dev);
600462306a36Sopenharmony_ci	hdr->ifi_change = 0;
600562306a36Sopenharmony_ci
600662306a36Sopenharmony_ci	if (nla_put_string(skb, IFLA_IFNAME, dev->name) ||
600762306a36Sopenharmony_ci	    (dev->addr_len &&
600862306a36Sopenharmony_ci	     nla_put(skb, IFLA_ADDRESS, dev->addr_len, dev->dev_addr)) ||
600962306a36Sopenharmony_ci	    nla_put_u32(skb, IFLA_MTU, dev->mtu) ||
601062306a36Sopenharmony_ci	    (dev->ifindex != dev_get_iflink(dev) &&
601162306a36Sopenharmony_ci	     nla_put_u32(skb, IFLA_LINK, dev_get_iflink(dev))) ||
601262306a36Sopenharmony_ci	    nla_put_u8(skb, IFLA_OPERSTATE,
601362306a36Sopenharmony_ci		       netif_running(dev) ? dev->operstate : IF_OPER_DOWN))
601462306a36Sopenharmony_ci		goto nla_put_failure;
601562306a36Sopenharmony_ci	protoinfo = nla_nest_start_noflag(skb, IFLA_PROTINFO);
601662306a36Sopenharmony_ci	if (!protoinfo)
601762306a36Sopenharmony_ci		goto nla_put_failure;
601862306a36Sopenharmony_ci
601962306a36Sopenharmony_ci	if (inet6_fill_ifla6_attrs(skb, idev, 0) < 0)
602062306a36Sopenharmony_ci		goto nla_put_failure;
602162306a36Sopenharmony_ci
602262306a36Sopenharmony_ci	nla_nest_end(skb, protoinfo);
602362306a36Sopenharmony_ci	nlmsg_end(skb, nlh);
602462306a36Sopenharmony_ci	return 0;
602562306a36Sopenharmony_ci
602662306a36Sopenharmony_cinla_put_failure:
602762306a36Sopenharmony_ci	nlmsg_cancel(skb, nlh);
602862306a36Sopenharmony_ci	return -EMSGSIZE;
602962306a36Sopenharmony_ci}
603062306a36Sopenharmony_ci
603162306a36Sopenharmony_cistatic int inet6_valid_dump_ifinfo(const struct nlmsghdr *nlh,
603262306a36Sopenharmony_ci				   struct netlink_ext_ack *extack)
603362306a36Sopenharmony_ci{
603462306a36Sopenharmony_ci	struct ifinfomsg *ifm;
603562306a36Sopenharmony_ci
603662306a36Sopenharmony_ci	if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ifm))) {
603762306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Invalid header for link dump request");
603862306a36Sopenharmony_ci		return -EINVAL;
603962306a36Sopenharmony_ci	}
604062306a36Sopenharmony_ci
604162306a36Sopenharmony_ci	if (nlmsg_attrlen(nlh, sizeof(*ifm))) {
604262306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Invalid data after header");
604362306a36Sopenharmony_ci		return -EINVAL;
604462306a36Sopenharmony_ci	}
604562306a36Sopenharmony_ci
604662306a36Sopenharmony_ci	ifm = nlmsg_data(nlh);
604762306a36Sopenharmony_ci	if (ifm->__ifi_pad || ifm->ifi_type || ifm->ifi_flags ||
604862306a36Sopenharmony_ci	    ifm->ifi_change || ifm->ifi_index) {
604962306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Invalid values in header for dump request");
605062306a36Sopenharmony_ci		return -EINVAL;
605162306a36Sopenharmony_ci	}
605262306a36Sopenharmony_ci
605362306a36Sopenharmony_ci	return 0;
605462306a36Sopenharmony_ci}
605562306a36Sopenharmony_ci
605662306a36Sopenharmony_cistatic int inet6_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
605762306a36Sopenharmony_ci{
605862306a36Sopenharmony_ci	struct net *net = sock_net(skb->sk);
605962306a36Sopenharmony_ci	int h, s_h;
606062306a36Sopenharmony_ci	int idx = 0, s_idx;
606162306a36Sopenharmony_ci	struct net_device *dev;
606262306a36Sopenharmony_ci	struct inet6_dev *idev;
606362306a36Sopenharmony_ci	struct hlist_head *head;
606462306a36Sopenharmony_ci
606562306a36Sopenharmony_ci	/* only requests using strict checking can pass data to
606662306a36Sopenharmony_ci	 * influence the dump
606762306a36Sopenharmony_ci	 */
606862306a36Sopenharmony_ci	if (cb->strict_check) {
606962306a36Sopenharmony_ci		int err = inet6_valid_dump_ifinfo(cb->nlh, cb->extack);
607062306a36Sopenharmony_ci
607162306a36Sopenharmony_ci		if (err < 0)
607262306a36Sopenharmony_ci			return err;
607362306a36Sopenharmony_ci	}
607462306a36Sopenharmony_ci
607562306a36Sopenharmony_ci	s_h = cb->args[0];
607662306a36Sopenharmony_ci	s_idx = cb->args[1];
607762306a36Sopenharmony_ci
607862306a36Sopenharmony_ci	rcu_read_lock();
607962306a36Sopenharmony_ci	for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
608062306a36Sopenharmony_ci		idx = 0;
608162306a36Sopenharmony_ci		head = &net->dev_index_head[h];
608262306a36Sopenharmony_ci		hlist_for_each_entry_rcu(dev, head, index_hlist) {
608362306a36Sopenharmony_ci			if (idx < s_idx)
608462306a36Sopenharmony_ci				goto cont;
608562306a36Sopenharmony_ci			idev = __in6_dev_get(dev);
608662306a36Sopenharmony_ci			if (!idev)
608762306a36Sopenharmony_ci				goto cont;
608862306a36Sopenharmony_ci			if (inet6_fill_ifinfo(skb, idev,
608962306a36Sopenharmony_ci					      NETLINK_CB(cb->skb).portid,
609062306a36Sopenharmony_ci					      cb->nlh->nlmsg_seq,
609162306a36Sopenharmony_ci					      RTM_NEWLINK, NLM_F_MULTI) < 0)
609262306a36Sopenharmony_ci				goto out;
609362306a36Sopenharmony_cicont:
609462306a36Sopenharmony_ci			idx++;
609562306a36Sopenharmony_ci		}
609662306a36Sopenharmony_ci	}
609762306a36Sopenharmony_ciout:
609862306a36Sopenharmony_ci	rcu_read_unlock();
609962306a36Sopenharmony_ci	cb->args[1] = idx;
610062306a36Sopenharmony_ci	cb->args[0] = h;
610162306a36Sopenharmony_ci
610262306a36Sopenharmony_ci	return skb->len;
610362306a36Sopenharmony_ci}
610462306a36Sopenharmony_ci
610562306a36Sopenharmony_civoid inet6_ifinfo_notify(int event, struct inet6_dev *idev)
610662306a36Sopenharmony_ci{
610762306a36Sopenharmony_ci	struct sk_buff *skb;
610862306a36Sopenharmony_ci	struct net *net = dev_net(idev->dev);
610962306a36Sopenharmony_ci	int err = -ENOBUFS;
611062306a36Sopenharmony_ci
611162306a36Sopenharmony_ci	skb = nlmsg_new(inet6_if_nlmsg_size(), GFP_ATOMIC);
611262306a36Sopenharmony_ci	if (!skb)
611362306a36Sopenharmony_ci		goto errout;
611462306a36Sopenharmony_ci
611562306a36Sopenharmony_ci	err = inet6_fill_ifinfo(skb, idev, 0, 0, event, 0);
611662306a36Sopenharmony_ci	if (err < 0) {
611762306a36Sopenharmony_ci		/* -EMSGSIZE implies BUG in inet6_if_nlmsg_size() */
611862306a36Sopenharmony_ci		WARN_ON(err == -EMSGSIZE);
611962306a36Sopenharmony_ci		kfree_skb(skb);
612062306a36Sopenharmony_ci		goto errout;
612162306a36Sopenharmony_ci	}
612262306a36Sopenharmony_ci	rtnl_notify(skb, net, 0, RTNLGRP_IPV6_IFINFO, NULL, GFP_ATOMIC);
612362306a36Sopenharmony_ci	return;
612462306a36Sopenharmony_cierrout:
612562306a36Sopenharmony_ci	if (err < 0)
612662306a36Sopenharmony_ci		rtnl_set_sk_err(net, RTNLGRP_IPV6_IFINFO, err);
612762306a36Sopenharmony_ci}
612862306a36Sopenharmony_ci
612962306a36Sopenharmony_cistatic inline size_t inet6_prefix_nlmsg_size(void)
613062306a36Sopenharmony_ci{
613162306a36Sopenharmony_ci	return NLMSG_ALIGN(sizeof(struct prefixmsg))
613262306a36Sopenharmony_ci	       + nla_total_size(sizeof(struct in6_addr))
613362306a36Sopenharmony_ci	       + nla_total_size(sizeof(struct prefix_cacheinfo));
613462306a36Sopenharmony_ci}
613562306a36Sopenharmony_ci
613662306a36Sopenharmony_cistatic int inet6_fill_prefix(struct sk_buff *skb, struct inet6_dev *idev,
613762306a36Sopenharmony_ci			     struct prefix_info *pinfo, u32 portid, u32 seq,
613862306a36Sopenharmony_ci			     int event, unsigned int flags)
613962306a36Sopenharmony_ci{
614062306a36Sopenharmony_ci	struct prefixmsg *pmsg;
614162306a36Sopenharmony_ci	struct nlmsghdr *nlh;
614262306a36Sopenharmony_ci	struct prefix_cacheinfo	ci;
614362306a36Sopenharmony_ci
614462306a36Sopenharmony_ci	nlh = nlmsg_put(skb, portid, seq, event, sizeof(*pmsg), flags);
614562306a36Sopenharmony_ci	if (!nlh)
614662306a36Sopenharmony_ci		return -EMSGSIZE;
614762306a36Sopenharmony_ci
614862306a36Sopenharmony_ci	pmsg = nlmsg_data(nlh);
614962306a36Sopenharmony_ci	pmsg->prefix_family = AF_INET6;
615062306a36Sopenharmony_ci	pmsg->prefix_pad1 = 0;
615162306a36Sopenharmony_ci	pmsg->prefix_pad2 = 0;
615262306a36Sopenharmony_ci	pmsg->prefix_ifindex = idev->dev->ifindex;
615362306a36Sopenharmony_ci	pmsg->prefix_len = pinfo->prefix_len;
615462306a36Sopenharmony_ci	pmsg->prefix_type = pinfo->type;
615562306a36Sopenharmony_ci	pmsg->prefix_pad3 = 0;
615662306a36Sopenharmony_ci	pmsg->prefix_flags = pinfo->flags;
615762306a36Sopenharmony_ci
615862306a36Sopenharmony_ci	if (nla_put(skb, PREFIX_ADDRESS, sizeof(pinfo->prefix), &pinfo->prefix))
615962306a36Sopenharmony_ci		goto nla_put_failure;
616062306a36Sopenharmony_ci	ci.preferred_time = ntohl(pinfo->prefered);
616162306a36Sopenharmony_ci	ci.valid_time = ntohl(pinfo->valid);
616262306a36Sopenharmony_ci	if (nla_put(skb, PREFIX_CACHEINFO, sizeof(ci), &ci))
616362306a36Sopenharmony_ci		goto nla_put_failure;
616462306a36Sopenharmony_ci	nlmsg_end(skb, nlh);
616562306a36Sopenharmony_ci	return 0;
616662306a36Sopenharmony_ci
616762306a36Sopenharmony_cinla_put_failure:
616862306a36Sopenharmony_ci	nlmsg_cancel(skb, nlh);
616962306a36Sopenharmony_ci	return -EMSGSIZE;
617062306a36Sopenharmony_ci}
617162306a36Sopenharmony_ci
617262306a36Sopenharmony_cistatic void inet6_prefix_notify(int event, struct inet6_dev *idev,
617362306a36Sopenharmony_ci			 struct prefix_info *pinfo)
617462306a36Sopenharmony_ci{
617562306a36Sopenharmony_ci	struct sk_buff *skb;
617662306a36Sopenharmony_ci	struct net *net = dev_net(idev->dev);
617762306a36Sopenharmony_ci	int err = -ENOBUFS;
617862306a36Sopenharmony_ci
617962306a36Sopenharmony_ci	skb = nlmsg_new(inet6_prefix_nlmsg_size(), GFP_ATOMIC);
618062306a36Sopenharmony_ci	if (!skb)
618162306a36Sopenharmony_ci		goto errout;
618262306a36Sopenharmony_ci
618362306a36Sopenharmony_ci	err = inet6_fill_prefix(skb, idev, pinfo, 0, 0, event, 0);
618462306a36Sopenharmony_ci	if (err < 0) {
618562306a36Sopenharmony_ci		/* -EMSGSIZE implies BUG in inet6_prefix_nlmsg_size() */
618662306a36Sopenharmony_ci		WARN_ON(err == -EMSGSIZE);
618762306a36Sopenharmony_ci		kfree_skb(skb);
618862306a36Sopenharmony_ci		goto errout;
618962306a36Sopenharmony_ci	}
619062306a36Sopenharmony_ci	rtnl_notify(skb, net, 0, RTNLGRP_IPV6_PREFIX, NULL, GFP_ATOMIC);
619162306a36Sopenharmony_ci	return;
619262306a36Sopenharmony_cierrout:
619362306a36Sopenharmony_ci	if (err < 0)
619462306a36Sopenharmony_ci		rtnl_set_sk_err(net, RTNLGRP_IPV6_PREFIX, err);
619562306a36Sopenharmony_ci}
619662306a36Sopenharmony_ci
619762306a36Sopenharmony_cistatic void __ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp)
619862306a36Sopenharmony_ci{
619962306a36Sopenharmony_ci	struct net *net = dev_net(ifp->idev->dev);
620062306a36Sopenharmony_ci
620162306a36Sopenharmony_ci	if (event)
620262306a36Sopenharmony_ci		ASSERT_RTNL();
620362306a36Sopenharmony_ci
620462306a36Sopenharmony_ci	inet6_ifa_notify(event ? : RTM_NEWADDR, ifp);
620562306a36Sopenharmony_ci
620662306a36Sopenharmony_ci	switch (event) {
620762306a36Sopenharmony_ci	case RTM_NEWADDR:
620862306a36Sopenharmony_ci		/*
620962306a36Sopenharmony_ci		 * If the address was optimistic we inserted the route at the
621062306a36Sopenharmony_ci		 * start of our DAD process, so we don't need to do it again.
621162306a36Sopenharmony_ci		 * If the device was taken down in the middle of the DAD
621262306a36Sopenharmony_ci		 * cycle there is a race where we could get here without a
621362306a36Sopenharmony_ci		 * host route, so nothing to insert. That will be fixed when
621462306a36Sopenharmony_ci		 * the device is brought up.
621562306a36Sopenharmony_ci		 */
621662306a36Sopenharmony_ci		if (ifp->rt && !rcu_access_pointer(ifp->rt->fib6_node)) {
621762306a36Sopenharmony_ci			ip6_ins_rt(net, ifp->rt);
621862306a36Sopenharmony_ci		} else if (!ifp->rt && (ifp->idev->dev->flags & IFF_UP)) {
621962306a36Sopenharmony_ci			pr_warn("BUG: Address %pI6c on device %s is missing its host route.\n",
622062306a36Sopenharmony_ci				&ifp->addr, ifp->idev->dev->name);
622162306a36Sopenharmony_ci		}
622262306a36Sopenharmony_ci
622362306a36Sopenharmony_ci		if (ifp->idev->cnf.forwarding)
622462306a36Sopenharmony_ci			addrconf_join_anycast(ifp);
622562306a36Sopenharmony_ci		if (!ipv6_addr_any(&ifp->peer_addr))
622662306a36Sopenharmony_ci			addrconf_prefix_route(&ifp->peer_addr, 128,
622762306a36Sopenharmony_ci					      ifp->rt_priority, ifp->idev->dev,
622862306a36Sopenharmony_ci					      0, 0, GFP_ATOMIC);
622962306a36Sopenharmony_ci		break;
623062306a36Sopenharmony_ci	case RTM_DELADDR:
623162306a36Sopenharmony_ci		if (ifp->idev->cnf.forwarding)
623262306a36Sopenharmony_ci			addrconf_leave_anycast(ifp);
623362306a36Sopenharmony_ci		addrconf_leave_solict(ifp->idev, &ifp->addr);
623462306a36Sopenharmony_ci		if (!ipv6_addr_any(&ifp->peer_addr)) {
623562306a36Sopenharmony_ci			struct fib6_info *rt;
623662306a36Sopenharmony_ci
623762306a36Sopenharmony_ci			rt = addrconf_get_prefix_route(&ifp->peer_addr, 128,
623862306a36Sopenharmony_ci						       ifp->idev->dev, 0, 0,
623962306a36Sopenharmony_ci						       false);
624062306a36Sopenharmony_ci			if (rt)
624162306a36Sopenharmony_ci				ip6_del_rt(net, rt, false);
624262306a36Sopenharmony_ci		}
624362306a36Sopenharmony_ci		if (ifp->rt) {
624462306a36Sopenharmony_ci			ip6_del_rt(net, ifp->rt, false);
624562306a36Sopenharmony_ci			ifp->rt = NULL;
624662306a36Sopenharmony_ci		}
624762306a36Sopenharmony_ci		rt_genid_bump_ipv6(net);
624862306a36Sopenharmony_ci		break;
624962306a36Sopenharmony_ci	}
625062306a36Sopenharmony_ci	atomic_inc(&net->ipv6.dev_addr_genid);
625162306a36Sopenharmony_ci}
625262306a36Sopenharmony_ci
625362306a36Sopenharmony_cistatic void ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp)
625462306a36Sopenharmony_ci{
625562306a36Sopenharmony_ci	if (likely(ifp->idev->dead == 0))
625662306a36Sopenharmony_ci		__ipv6_ifa_notify(event, ifp);
625762306a36Sopenharmony_ci}
625862306a36Sopenharmony_ci
625962306a36Sopenharmony_ci#ifdef CONFIG_SYSCTL
626062306a36Sopenharmony_ci
626162306a36Sopenharmony_cistatic int addrconf_sysctl_forward(struct ctl_table *ctl, int write,
626262306a36Sopenharmony_ci		void *buffer, size_t *lenp, loff_t *ppos)
626362306a36Sopenharmony_ci{
626462306a36Sopenharmony_ci	int *valp = ctl->data;
626562306a36Sopenharmony_ci	int val = *valp;
626662306a36Sopenharmony_ci	loff_t pos = *ppos;
626762306a36Sopenharmony_ci	struct ctl_table lctl;
626862306a36Sopenharmony_ci	int ret;
626962306a36Sopenharmony_ci
627062306a36Sopenharmony_ci	/*
627162306a36Sopenharmony_ci	 * ctl->data points to idev->cnf.forwarding, we should
627262306a36Sopenharmony_ci	 * not modify it until we get the rtnl lock.
627362306a36Sopenharmony_ci	 */
627462306a36Sopenharmony_ci	lctl = *ctl;
627562306a36Sopenharmony_ci	lctl.data = &val;
627662306a36Sopenharmony_ci
627762306a36Sopenharmony_ci	ret = proc_dointvec(&lctl, write, buffer, lenp, ppos);
627862306a36Sopenharmony_ci
627962306a36Sopenharmony_ci	if (write)
628062306a36Sopenharmony_ci		ret = addrconf_fixup_forwarding(ctl, valp, val);
628162306a36Sopenharmony_ci	if (ret)
628262306a36Sopenharmony_ci		*ppos = pos;
628362306a36Sopenharmony_ci	return ret;
628462306a36Sopenharmony_ci}
628562306a36Sopenharmony_ci
628662306a36Sopenharmony_cistatic int addrconf_sysctl_mtu(struct ctl_table *ctl, int write,
628762306a36Sopenharmony_ci		void *buffer, size_t *lenp, loff_t *ppos)
628862306a36Sopenharmony_ci{
628962306a36Sopenharmony_ci	struct inet6_dev *idev = ctl->extra1;
629062306a36Sopenharmony_ci	int min_mtu = IPV6_MIN_MTU;
629162306a36Sopenharmony_ci	struct ctl_table lctl;
629262306a36Sopenharmony_ci
629362306a36Sopenharmony_ci	lctl = *ctl;
629462306a36Sopenharmony_ci	lctl.extra1 = &min_mtu;
629562306a36Sopenharmony_ci	lctl.extra2 = idev ? &idev->dev->mtu : NULL;
629662306a36Sopenharmony_ci
629762306a36Sopenharmony_ci	return proc_dointvec_minmax(&lctl, write, buffer, lenp, ppos);
629862306a36Sopenharmony_ci}
629962306a36Sopenharmony_ci
630062306a36Sopenharmony_cistatic void dev_disable_change(struct inet6_dev *idev)
630162306a36Sopenharmony_ci{
630262306a36Sopenharmony_ci	struct netdev_notifier_info info;
630362306a36Sopenharmony_ci
630462306a36Sopenharmony_ci	if (!idev || !idev->dev)
630562306a36Sopenharmony_ci		return;
630662306a36Sopenharmony_ci
630762306a36Sopenharmony_ci	netdev_notifier_info_init(&info, idev->dev);
630862306a36Sopenharmony_ci	if (idev->cnf.disable_ipv6)
630962306a36Sopenharmony_ci		addrconf_notify(NULL, NETDEV_DOWN, &info);
631062306a36Sopenharmony_ci	else
631162306a36Sopenharmony_ci		addrconf_notify(NULL, NETDEV_UP, &info);
631262306a36Sopenharmony_ci}
631362306a36Sopenharmony_ci
631462306a36Sopenharmony_cistatic void addrconf_disable_change(struct net *net, __s32 newf)
631562306a36Sopenharmony_ci{
631662306a36Sopenharmony_ci	struct net_device *dev;
631762306a36Sopenharmony_ci	struct inet6_dev *idev;
631862306a36Sopenharmony_ci
631962306a36Sopenharmony_ci	for_each_netdev(net, dev) {
632062306a36Sopenharmony_ci		idev = __in6_dev_get(dev);
632162306a36Sopenharmony_ci		if (idev) {
632262306a36Sopenharmony_ci			int changed = (!idev->cnf.disable_ipv6) ^ (!newf);
632362306a36Sopenharmony_ci			idev->cnf.disable_ipv6 = newf;
632462306a36Sopenharmony_ci			if (changed)
632562306a36Sopenharmony_ci				dev_disable_change(idev);
632662306a36Sopenharmony_ci		}
632762306a36Sopenharmony_ci	}
632862306a36Sopenharmony_ci}
632962306a36Sopenharmony_ci
633062306a36Sopenharmony_cistatic int addrconf_disable_ipv6(struct ctl_table *table, int *p, int newf)
633162306a36Sopenharmony_ci{
633262306a36Sopenharmony_ci	struct net *net;
633362306a36Sopenharmony_ci	int old;
633462306a36Sopenharmony_ci
633562306a36Sopenharmony_ci	if (!rtnl_trylock())
633662306a36Sopenharmony_ci		return restart_syscall();
633762306a36Sopenharmony_ci
633862306a36Sopenharmony_ci	net = (struct net *)table->extra2;
633962306a36Sopenharmony_ci	old = *p;
634062306a36Sopenharmony_ci	*p = newf;
634162306a36Sopenharmony_ci
634262306a36Sopenharmony_ci	if (p == &net->ipv6.devconf_dflt->disable_ipv6) {
634362306a36Sopenharmony_ci		rtnl_unlock();
634462306a36Sopenharmony_ci		return 0;
634562306a36Sopenharmony_ci	}
634662306a36Sopenharmony_ci
634762306a36Sopenharmony_ci	if (p == &net->ipv6.devconf_all->disable_ipv6) {
634862306a36Sopenharmony_ci		net->ipv6.devconf_dflt->disable_ipv6 = newf;
634962306a36Sopenharmony_ci		addrconf_disable_change(net, newf);
635062306a36Sopenharmony_ci	} else if ((!newf) ^ (!old))
635162306a36Sopenharmony_ci		dev_disable_change((struct inet6_dev *)table->extra1);
635262306a36Sopenharmony_ci
635362306a36Sopenharmony_ci	rtnl_unlock();
635462306a36Sopenharmony_ci	return 0;
635562306a36Sopenharmony_ci}
635662306a36Sopenharmony_ci
635762306a36Sopenharmony_cistatic int addrconf_sysctl_disable(struct ctl_table *ctl, int write,
635862306a36Sopenharmony_ci		void *buffer, size_t *lenp, loff_t *ppos)
635962306a36Sopenharmony_ci{
636062306a36Sopenharmony_ci	int *valp = ctl->data;
636162306a36Sopenharmony_ci	int val = *valp;
636262306a36Sopenharmony_ci	loff_t pos = *ppos;
636362306a36Sopenharmony_ci	struct ctl_table lctl;
636462306a36Sopenharmony_ci	int ret;
636562306a36Sopenharmony_ci
636662306a36Sopenharmony_ci	/*
636762306a36Sopenharmony_ci	 * ctl->data points to idev->cnf.disable_ipv6, we should
636862306a36Sopenharmony_ci	 * not modify it until we get the rtnl lock.
636962306a36Sopenharmony_ci	 */
637062306a36Sopenharmony_ci	lctl = *ctl;
637162306a36Sopenharmony_ci	lctl.data = &val;
637262306a36Sopenharmony_ci
637362306a36Sopenharmony_ci	ret = proc_dointvec(&lctl, write, buffer, lenp, ppos);
637462306a36Sopenharmony_ci
637562306a36Sopenharmony_ci	if (write)
637662306a36Sopenharmony_ci		ret = addrconf_disable_ipv6(ctl, valp, val);
637762306a36Sopenharmony_ci	if (ret)
637862306a36Sopenharmony_ci		*ppos = pos;
637962306a36Sopenharmony_ci	return ret;
638062306a36Sopenharmony_ci}
638162306a36Sopenharmony_ci
638262306a36Sopenharmony_cistatic int addrconf_sysctl_proxy_ndp(struct ctl_table *ctl, int write,
638362306a36Sopenharmony_ci		void *buffer, size_t *lenp, loff_t *ppos)
638462306a36Sopenharmony_ci{
638562306a36Sopenharmony_ci	int *valp = ctl->data;
638662306a36Sopenharmony_ci	int ret;
638762306a36Sopenharmony_ci	int old, new;
638862306a36Sopenharmony_ci
638962306a36Sopenharmony_ci	old = *valp;
639062306a36Sopenharmony_ci	ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
639162306a36Sopenharmony_ci	new = *valp;
639262306a36Sopenharmony_ci
639362306a36Sopenharmony_ci	if (write && old != new) {
639462306a36Sopenharmony_ci		struct net *net = ctl->extra2;
639562306a36Sopenharmony_ci
639662306a36Sopenharmony_ci		if (!rtnl_trylock())
639762306a36Sopenharmony_ci			return restart_syscall();
639862306a36Sopenharmony_ci
639962306a36Sopenharmony_ci		if (valp == &net->ipv6.devconf_dflt->proxy_ndp)
640062306a36Sopenharmony_ci			inet6_netconf_notify_devconf(net, RTM_NEWNETCONF,
640162306a36Sopenharmony_ci						     NETCONFA_PROXY_NEIGH,
640262306a36Sopenharmony_ci						     NETCONFA_IFINDEX_DEFAULT,
640362306a36Sopenharmony_ci						     net->ipv6.devconf_dflt);
640462306a36Sopenharmony_ci		else if (valp == &net->ipv6.devconf_all->proxy_ndp)
640562306a36Sopenharmony_ci			inet6_netconf_notify_devconf(net, RTM_NEWNETCONF,
640662306a36Sopenharmony_ci						     NETCONFA_PROXY_NEIGH,
640762306a36Sopenharmony_ci						     NETCONFA_IFINDEX_ALL,
640862306a36Sopenharmony_ci						     net->ipv6.devconf_all);
640962306a36Sopenharmony_ci		else {
641062306a36Sopenharmony_ci			struct inet6_dev *idev = ctl->extra1;
641162306a36Sopenharmony_ci
641262306a36Sopenharmony_ci			inet6_netconf_notify_devconf(net, RTM_NEWNETCONF,
641362306a36Sopenharmony_ci						     NETCONFA_PROXY_NEIGH,
641462306a36Sopenharmony_ci						     idev->dev->ifindex,
641562306a36Sopenharmony_ci						     &idev->cnf);
641662306a36Sopenharmony_ci		}
641762306a36Sopenharmony_ci		rtnl_unlock();
641862306a36Sopenharmony_ci	}
641962306a36Sopenharmony_ci
642062306a36Sopenharmony_ci	return ret;
642162306a36Sopenharmony_ci}
642262306a36Sopenharmony_ci
642362306a36Sopenharmony_cistatic int addrconf_sysctl_addr_gen_mode(struct ctl_table *ctl, int write,
642462306a36Sopenharmony_ci					 void *buffer, size_t *lenp,
642562306a36Sopenharmony_ci					 loff_t *ppos)
642662306a36Sopenharmony_ci{
642762306a36Sopenharmony_ci	int ret = 0;
642862306a36Sopenharmony_ci	u32 new_val;
642962306a36Sopenharmony_ci	struct inet6_dev *idev = (struct inet6_dev *)ctl->extra1;
643062306a36Sopenharmony_ci	struct net *net = (struct net *)ctl->extra2;
643162306a36Sopenharmony_ci	struct ctl_table tmp = {
643262306a36Sopenharmony_ci		.data = &new_val,
643362306a36Sopenharmony_ci		.maxlen = sizeof(new_val),
643462306a36Sopenharmony_ci		.mode = ctl->mode,
643562306a36Sopenharmony_ci	};
643662306a36Sopenharmony_ci
643762306a36Sopenharmony_ci	if (!rtnl_trylock())
643862306a36Sopenharmony_ci		return restart_syscall();
643962306a36Sopenharmony_ci
644062306a36Sopenharmony_ci	new_val = *((u32 *)ctl->data);
644162306a36Sopenharmony_ci
644262306a36Sopenharmony_ci	ret = proc_douintvec(&tmp, write, buffer, lenp, ppos);
644362306a36Sopenharmony_ci	if (ret != 0)
644462306a36Sopenharmony_ci		goto out;
644562306a36Sopenharmony_ci
644662306a36Sopenharmony_ci	if (write) {
644762306a36Sopenharmony_ci		if (check_addr_gen_mode(new_val) < 0) {
644862306a36Sopenharmony_ci			ret = -EINVAL;
644962306a36Sopenharmony_ci			goto out;
645062306a36Sopenharmony_ci		}
645162306a36Sopenharmony_ci
645262306a36Sopenharmony_ci		if (idev) {
645362306a36Sopenharmony_ci			if (check_stable_privacy(idev, net, new_val) < 0) {
645462306a36Sopenharmony_ci				ret = -EINVAL;
645562306a36Sopenharmony_ci				goto out;
645662306a36Sopenharmony_ci			}
645762306a36Sopenharmony_ci
645862306a36Sopenharmony_ci			if (idev->cnf.addr_gen_mode != new_val) {
645962306a36Sopenharmony_ci				idev->cnf.addr_gen_mode = new_val;
646062306a36Sopenharmony_ci				addrconf_init_auto_addrs(idev->dev);
646162306a36Sopenharmony_ci			}
646262306a36Sopenharmony_ci		} else if (&net->ipv6.devconf_all->addr_gen_mode == ctl->data) {
646362306a36Sopenharmony_ci			struct net_device *dev;
646462306a36Sopenharmony_ci
646562306a36Sopenharmony_ci			net->ipv6.devconf_dflt->addr_gen_mode = new_val;
646662306a36Sopenharmony_ci			for_each_netdev(net, dev) {
646762306a36Sopenharmony_ci				idev = __in6_dev_get(dev);
646862306a36Sopenharmony_ci				if (idev &&
646962306a36Sopenharmony_ci				    idev->cnf.addr_gen_mode != new_val) {
647062306a36Sopenharmony_ci					idev->cnf.addr_gen_mode = new_val;
647162306a36Sopenharmony_ci					addrconf_init_auto_addrs(idev->dev);
647262306a36Sopenharmony_ci				}
647362306a36Sopenharmony_ci			}
647462306a36Sopenharmony_ci		}
647562306a36Sopenharmony_ci
647662306a36Sopenharmony_ci		*((u32 *)ctl->data) = new_val;
647762306a36Sopenharmony_ci	}
647862306a36Sopenharmony_ci
647962306a36Sopenharmony_ciout:
648062306a36Sopenharmony_ci	rtnl_unlock();
648162306a36Sopenharmony_ci
648262306a36Sopenharmony_ci	return ret;
648362306a36Sopenharmony_ci}
648462306a36Sopenharmony_ci
648562306a36Sopenharmony_cistatic int addrconf_sysctl_stable_secret(struct ctl_table *ctl, int write,
648662306a36Sopenharmony_ci					 void *buffer, size_t *lenp,
648762306a36Sopenharmony_ci					 loff_t *ppos)
648862306a36Sopenharmony_ci{
648962306a36Sopenharmony_ci	int err;
649062306a36Sopenharmony_ci	struct in6_addr addr;
649162306a36Sopenharmony_ci	char str[IPV6_MAX_STRLEN];
649262306a36Sopenharmony_ci	struct ctl_table lctl = *ctl;
649362306a36Sopenharmony_ci	struct net *net = ctl->extra2;
649462306a36Sopenharmony_ci	struct ipv6_stable_secret *secret = ctl->data;
649562306a36Sopenharmony_ci
649662306a36Sopenharmony_ci	if (&net->ipv6.devconf_all->stable_secret == ctl->data)
649762306a36Sopenharmony_ci		return -EIO;
649862306a36Sopenharmony_ci
649962306a36Sopenharmony_ci	lctl.maxlen = IPV6_MAX_STRLEN;
650062306a36Sopenharmony_ci	lctl.data = str;
650162306a36Sopenharmony_ci
650262306a36Sopenharmony_ci	if (!rtnl_trylock())
650362306a36Sopenharmony_ci		return restart_syscall();
650462306a36Sopenharmony_ci
650562306a36Sopenharmony_ci	if (!write && !secret->initialized) {
650662306a36Sopenharmony_ci		err = -EIO;
650762306a36Sopenharmony_ci		goto out;
650862306a36Sopenharmony_ci	}
650962306a36Sopenharmony_ci
651062306a36Sopenharmony_ci	err = snprintf(str, sizeof(str), "%pI6", &secret->secret);
651162306a36Sopenharmony_ci	if (err >= sizeof(str)) {
651262306a36Sopenharmony_ci		err = -EIO;
651362306a36Sopenharmony_ci		goto out;
651462306a36Sopenharmony_ci	}
651562306a36Sopenharmony_ci
651662306a36Sopenharmony_ci	err = proc_dostring(&lctl, write, buffer, lenp, ppos);
651762306a36Sopenharmony_ci	if (err || !write)
651862306a36Sopenharmony_ci		goto out;
651962306a36Sopenharmony_ci
652062306a36Sopenharmony_ci	if (in6_pton(str, -1, addr.in6_u.u6_addr8, -1, NULL) != 1) {
652162306a36Sopenharmony_ci		err = -EIO;
652262306a36Sopenharmony_ci		goto out;
652362306a36Sopenharmony_ci	}
652462306a36Sopenharmony_ci
652562306a36Sopenharmony_ci	secret->initialized = true;
652662306a36Sopenharmony_ci	secret->secret = addr;
652762306a36Sopenharmony_ci
652862306a36Sopenharmony_ci	if (&net->ipv6.devconf_dflt->stable_secret == ctl->data) {
652962306a36Sopenharmony_ci		struct net_device *dev;
653062306a36Sopenharmony_ci
653162306a36Sopenharmony_ci		for_each_netdev(net, dev) {
653262306a36Sopenharmony_ci			struct inet6_dev *idev = __in6_dev_get(dev);
653362306a36Sopenharmony_ci
653462306a36Sopenharmony_ci			if (idev) {
653562306a36Sopenharmony_ci				idev->cnf.addr_gen_mode =
653662306a36Sopenharmony_ci					IN6_ADDR_GEN_MODE_STABLE_PRIVACY;
653762306a36Sopenharmony_ci			}
653862306a36Sopenharmony_ci		}
653962306a36Sopenharmony_ci	} else {
654062306a36Sopenharmony_ci		struct inet6_dev *idev = ctl->extra1;
654162306a36Sopenharmony_ci
654262306a36Sopenharmony_ci		idev->cnf.addr_gen_mode = IN6_ADDR_GEN_MODE_STABLE_PRIVACY;
654362306a36Sopenharmony_ci	}
654462306a36Sopenharmony_ci
654562306a36Sopenharmony_ciout:
654662306a36Sopenharmony_ci	rtnl_unlock();
654762306a36Sopenharmony_ci
654862306a36Sopenharmony_ci	return err;
654962306a36Sopenharmony_ci}
655062306a36Sopenharmony_ci
655162306a36Sopenharmony_cistatic
655262306a36Sopenharmony_ciint addrconf_sysctl_ignore_routes_with_linkdown(struct ctl_table *ctl,
655362306a36Sopenharmony_ci						int write, void *buffer,
655462306a36Sopenharmony_ci						size_t *lenp,
655562306a36Sopenharmony_ci						loff_t *ppos)
655662306a36Sopenharmony_ci{
655762306a36Sopenharmony_ci	int *valp = ctl->data;
655862306a36Sopenharmony_ci	int val = *valp;
655962306a36Sopenharmony_ci	loff_t pos = *ppos;
656062306a36Sopenharmony_ci	struct ctl_table lctl;
656162306a36Sopenharmony_ci	int ret;
656262306a36Sopenharmony_ci
656362306a36Sopenharmony_ci	/* ctl->data points to idev->cnf.ignore_routes_when_linkdown
656462306a36Sopenharmony_ci	 * we should not modify it until we get the rtnl lock.
656562306a36Sopenharmony_ci	 */
656662306a36Sopenharmony_ci	lctl = *ctl;
656762306a36Sopenharmony_ci	lctl.data = &val;
656862306a36Sopenharmony_ci
656962306a36Sopenharmony_ci	ret = proc_dointvec(&lctl, write, buffer, lenp, ppos);
657062306a36Sopenharmony_ci
657162306a36Sopenharmony_ci	if (write)
657262306a36Sopenharmony_ci		ret = addrconf_fixup_linkdown(ctl, valp, val);
657362306a36Sopenharmony_ci	if (ret)
657462306a36Sopenharmony_ci		*ppos = pos;
657562306a36Sopenharmony_ci	return ret;
657662306a36Sopenharmony_ci}
657762306a36Sopenharmony_ci
657862306a36Sopenharmony_cistatic
657962306a36Sopenharmony_civoid addrconf_set_nopolicy(struct rt6_info *rt, int action)
658062306a36Sopenharmony_ci{
658162306a36Sopenharmony_ci	if (rt) {
658262306a36Sopenharmony_ci		if (action)
658362306a36Sopenharmony_ci			rt->dst.flags |= DST_NOPOLICY;
658462306a36Sopenharmony_ci		else
658562306a36Sopenharmony_ci			rt->dst.flags &= ~DST_NOPOLICY;
658662306a36Sopenharmony_ci	}
658762306a36Sopenharmony_ci}
658862306a36Sopenharmony_ci
658962306a36Sopenharmony_cistatic
659062306a36Sopenharmony_civoid addrconf_disable_policy_idev(struct inet6_dev *idev, int val)
659162306a36Sopenharmony_ci{
659262306a36Sopenharmony_ci	struct inet6_ifaddr *ifa;
659362306a36Sopenharmony_ci
659462306a36Sopenharmony_ci	read_lock_bh(&idev->lock);
659562306a36Sopenharmony_ci	list_for_each_entry(ifa, &idev->addr_list, if_list) {
659662306a36Sopenharmony_ci		spin_lock(&ifa->lock);
659762306a36Sopenharmony_ci		if (ifa->rt) {
659862306a36Sopenharmony_ci			/* host routes only use builtin fib6_nh */
659962306a36Sopenharmony_ci			struct fib6_nh *nh = ifa->rt->fib6_nh;
660062306a36Sopenharmony_ci			int cpu;
660162306a36Sopenharmony_ci
660262306a36Sopenharmony_ci			rcu_read_lock();
660362306a36Sopenharmony_ci			ifa->rt->dst_nopolicy = val ? true : false;
660462306a36Sopenharmony_ci			if (nh->rt6i_pcpu) {
660562306a36Sopenharmony_ci				for_each_possible_cpu(cpu) {
660662306a36Sopenharmony_ci					struct rt6_info **rtp;
660762306a36Sopenharmony_ci
660862306a36Sopenharmony_ci					rtp = per_cpu_ptr(nh->rt6i_pcpu, cpu);
660962306a36Sopenharmony_ci					addrconf_set_nopolicy(*rtp, val);
661062306a36Sopenharmony_ci				}
661162306a36Sopenharmony_ci			}
661262306a36Sopenharmony_ci			rcu_read_unlock();
661362306a36Sopenharmony_ci		}
661462306a36Sopenharmony_ci		spin_unlock(&ifa->lock);
661562306a36Sopenharmony_ci	}
661662306a36Sopenharmony_ci	read_unlock_bh(&idev->lock);
661762306a36Sopenharmony_ci}
661862306a36Sopenharmony_ci
661962306a36Sopenharmony_cistatic
662062306a36Sopenharmony_ciint addrconf_disable_policy(struct ctl_table *ctl, int *valp, int val)
662162306a36Sopenharmony_ci{
662262306a36Sopenharmony_ci	struct inet6_dev *idev;
662362306a36Sopenharmony_ci	struct net *net;
662462306a36Sopenharmony_ci
662562306a36Sopenharmony_ci	if (!rtnl_trylock())
662662306a36Sopenharmony_ci		return restart_syscall();
662762306a36Sopenharmony_ci
662862306a36Sopenharmony_ci	*valp = val;
662962306a36Sopenharmony_ci
663062306a36Sopenharmony_ci	net = (struct net *)ctl->extra2;
663162306a36Sopenharmony_ci	if (valp == &net->ipv6.devconf_dflt->disable_policy) {
663262306a36Sopenharmony_ci		rtnl_unlock();
663362306a36Sopenharmony_ci		return 0;
663462306a36Sopenharmony_ci	}
663562306a36Sopenharmony_ci
663662306a36Sopenharmony_ci	if (valp == &net->ipv6.devconf_all->disable_policy)  {
663762306a36Sopenharmony_ci		struct net_device *dev;
663862306a36Sopenharmony_ci
663962306a36Sopenharmony_ci		for_each_netdev(net, dev) {
664062306a36Sopenharmony_ci			idev = __in6_dev_get(dev);
664162306a36Sopenharmony_ci			if (idev)
664262306a36Sopenharmony_ci				addrconf_disable_policy_idev(idev, val);
664362306a36Sopenharmony_ci		}
664462306a36Sopenharmony_ci	} else {
664562306a36Sopenharmony_ci		idev = (struct inet6_dev *)ctl->extra1;
664662306a36Sopenharmony_ci		addrconf_disable_policy_idev(idev, val);
664762306a36Sopenharmony_ci	}
664862306a36Sopenharmony_ci
664962306a36Sopenharmony_ci	rtnl_unlock();
665062306a36Sopenharmony_ci	return 0;
665162306a36Sopenharmony_ci}
665262306a36Sopenharmony_ci
665362306a36Sopenharmony_cistatic int addrconf_sysctl_disable_policy(struct ctl_table *ctl, int write,
665462306a36Sopenharmony_ci				   void *buffer, size_t *lenp, loff_t *ppos)
665562306a36Sopenharmony_ci{
665662306a36Sopenharmony_ci	int *valp = ctl->data;
665762306a36Sopenharmony_ci	int val = *valp;
665862306a36Sopenharmony_ci	loff_t pos = *ppos;
665962306a36Sopenharmony_ci	struct ctl_table lctl;
666062306a36Sopenharmony_ci	int ret;
666162306a36Sopenharmony_ci
666262306a36Sopenharmony_ci	lctl = *ctl;
666362306a36Sopenharmony_ci	lctl.data = &val;
666462306a36Sopenharmony_ci	ret = proc_dointvec(&lctl, write, buffer, lenp, ppos);
666562306a36Sopenharmony_ci
666662306a36Sopenharmony_ci	if (write && (*valp != val))
666762306a36Sopenharmony_ci		ret = addrconf_disable_policy(ctl, valp, val);
666862306a36Sopenharmony_ci
666962306a36Sopenharmony_ci	if (ret)
667062306a36Sopenharmony_ci		*ppos = pos;
667162306a36Sopenharmony_ci
667262306a36Sopenharmony_ci	return ret;
667362306a36Sopenharmony_ci}
667462306a36Sopenharmony_ci
667562306a36Sopenharmony_cistatic int minus_one = -1;
667662306a36Sopenharmony_cistatic const int two_five_five = 255;
667762306a36Sopenharmony_cistatic u32 ioam6_if_id_max = U16_MAX;
667862306a36Sopenharmony_ci
667962306a36Sopenharmony_cistatic const struct ctl_table addrconf_sysctl[] = {
668062306a36Sopenharmony_ci	{
668162306a36Sopenharmony_ci		.procname	= "forwarding",
668262306a36Sopenharmony_ci		.data		= &ipv6_devconf.forwarding,
668362306a36Sopenharmony_ci		.maxlen		= sizeof(int),
668462306a36Sopenharmony_ci		.mode		= 0644,
668562306a36Sopenharmony_ci		.proc_handler	= addrconf_sysctl_forward,
668662306a36Sopenharmony_ci	},
668762306a36Sopenharmony_ci	{
668862306a36Sopenharmony_ci		.procname	= "hop_limit",
668962306a36Sopenharmony_ci		.data		= &ipv6_devconf.hop_limit,
669062306a36Sopenharmony_ci		.maxlen		= sizeof(int),
669162306a36Sopenharmony_ci		.mode		= 0644,
669262306a36Sopenharmony_ci		.proc_handler	= proc_dointvec_minmax,
669362306a36Sopenharmony_ci		.extra1		= (void *)SYSCTL_ONE,
669462306a36Sopenharmony_ci		.extra2		= (void *)&two_five_five,
669562306a36Sopenharmony_ci	},
669662306a36Sopenharmony_ci	{
669762306a36Sopenharmony_ci		.procname	= "mtu",
669862306a36Sopenharmony_ci		.data		= &ipv6_devconf.mtu6,
669962306a36Sopenharmony_ci		.maxlen		= sizeof(int),
670062306a36Sopenharmony_ci		.mode		= 0644,
670162306a36Sopenharmony_ci		.proc_handler	= addrconf_sysctl_mtu,
670262306a36Sopenharmony_ci	},
670362306a36Sopenharmony_ci	{
670462306a36Sopenharmony_ci		.procname	= "accept_ra",
670562306a36Sopenharmony_ci		.data		= &ipv6_devconf.accept_ra,
670662306a36Sopenharmony_ci		.maxlen		= sizeof(int),
670762306a36Sopenharmony_ci		.mode		= 0644,
670862306a36Sopenharmony_ci		.proc_handler	= proc_dointvec,
670962306a36Sopenharmony_ci	},
671062306a36Sopenharmony_ci	{
671162306a36Sopenharmony_ci		.procname	= "accept_redirects",
671262306a36Sopenharmony_ci		.data		= &ipv6_devconf.accept_redirects,
671362306a36Sopenharmony_ci		.maxlen		= sizeof(int),
671462306a36Sopenharmony_ci		.mode		= 0644,
671562306a36Sopenharmony_ci		.proc_handler	= proc_dointvec,
671662306a36Sopenharmony_ci	},
671762306a36Sopenharmony_ci	{
671862306a36Sopenharmony_ci		.procname	= "autoconf",
671962306a36Sopenharmony_ci		.data		= &ipv6_devconf.autoconf,
672062306a36Sopenharmony_ci		.maxlen		= sizeof(int),
672162306a36Sopenharmony_ci		.mode		= 0644,
672262306a36Sopenharmony_ci		.proc_handler	= proc_dointvec,
672362306a36Sopenharmony_ci	},
672462306a36Sopenharmony_ci	{
672562306a36Sopenharmony_ci		.procname	= "dad_transmits",
672662306a36Sopenharmony_ci		.data		= &ipv6_devconf.dad_transmits,
672762306a36Sopenharmony_ci		.maxlen		= sizeof(int),
672862306a36Sopenharmony_ci		.mode		= 0644,
672962306a36Sopenharmony_ci		.proc_handler	= proc_dointvec,
673062306a36Sopenharmony_ci	},
673162306a36Sopenharmony_ci	{
673262306a36Sopenharmony_ci		.procname	= "router_solicitations",
673362306a36Sopenharmony_ci		.data		= &ipv6_devconf.rtr_solicits,
673462306a36Sopenharmony_ci		.maxlen		= sizeof(int),
673562306a36Sopenharmony_ci		.mode		= 0644,
673662306a36Sopenharmony_ci		.proc_handler	= proc_dointvec_minmax,
673762306a36Sopenharmony_ci		.extra1		= &minus_one,
673862306a36Sopenharmony_ci	},
673962306a36Sopenharmony_ci	{
674062306a36Sopenharmony_ci		.procname	= "router_solicitation_interval",
674162306a36Sopenharmony_ci		.data		= &ipv6_devconf.rtr_solicit_interval,
674262306a36Sopenharmony_ci		.maxlen		= sizeof(int),
674362306a36Sopenharmony_ci		.mode		= 0644,
674462306a36Sopenharmony_ci		.proc_handler	= proc_dointvec_jiffies,
674562306a36Sopenharmony_ci	},
674662306a36Sopenharmony_ci	{
674762306a36Sopenharmony_ci		.procname	= "router_solicitation_max_interval",
674862306a36Sopenharmony_ci		.data		= &ipv6_devconf.rtr_solicit_max_interval,
674962306a36Sopenharmony_ci		.maxlen		= sizeof(int),
675062306a36Sopenharmony_ci		.mode		= 0644,
675162306a36Sopenharmony_ci		.proc_handler	= proc_dointvec_jiffies,
675262306a36Sopenharmony_ci	},
675362306a36Sopenharmony_ci	{
675462306a36Sopenharmony_ci		.procname	= "router_solicitation_delay",
675562306a36Sopenharmony_ci		.data		= &ipv6_devconf.rtr_solicit_delay,
675662306a36Sopenharmony_ci		.maxlen		= sizeof(int),
675762306a36Sopenharmony_ci		.mode		= 0644,
675862306a36Sopenharmony_ci		.proc_handler	= proc_dointvec_jiffies,
675962306a36Sopenharmony_ci	},
676062306a36Sopenharmony_ci	{
676162306a36Sopenharmony_ci		.procname	= "force_mld_version",
676262306a36Sopenharmony_ci		.data		= &ipv6_devconf.force_mld_version,
676362306a36Sopenharmony_ci		.maxlen		= sizeof(int),
676462306a36Sopenharmony_ci		.mode		= 0644,
676562306a36Sopenharmony_ci		.proc_handler	= proc_dointvec,
676662306a36Sopenharmony_ci	},
676762306a36Sopenharmony_ci	{
676862306a36Sopenharmony_ci		.procname	= "mldv1_unsolicited_report_interval",
676962306a36Sopenharmony_ci		.data		=
677062306a36Sopenharmony_ci			&ipv6_devconf.mldv1_unsolicited_report_interval,
677162306a36Sopenharmony_ci		.maxlen		= sizeof(int),
677262306a36Sopenharmony_ci		.mode		= 0644,
677362306a36Sopenharmony_ci		.proc_handler	= proc_dointvec_ms_jiffies,
677462306a36Sopenharmony_ci	},
677562306a36Sopenharmony_ci	{
677662306a36Sopenharmony_ci		.procname	= "mldv2_unsolicited_report_interval",
677762306a36Sopenharmony_ci		.data		=
677862306a36Sopenharmony_ci			&ipv6_devconf.mldv2_unsolicited_report_interval,
677962306a36Sopenharmony_ci		.maxlen		= sizeof(int),
678062306a36Sopenharmony_ci		.mode		= 0644,
678162306a36Sopenharmony_ci		.proc_handler	= proc_dointvec_ms_jiffies,
678262306a36Sopenharmony_ci	},
678362306a36Sopenharmony_ci	{
678462306a36Sopenharmony_ci		.procname	= "use_tempaddr",
678562306a36Sopenharmony_ci		.data		= &ipv6_devconf.use_tempaddr,
678662306a36Sopenharmony_ci		.maxlen		= sizeof(int),
678762306a36Sopenharmony_ci		.mode		= 0644,
678862306a36Sopenharmony_ci		.proc_handler	= proc_dointvec,
678962306a36Sopenharmony_ci	},
679062306a36Sopenharmony_ci	{
679162306a36Sopenharmony_ci		.procname	= "temp_valid_lft",
679262306a36Sopenharmony_ci		.data		= &ipv6_devconf.temp_valid_lft,
679362306a36Sopenharmony_ci		.maxlen		= sizeof(int),
679462306a36Sopenharmony_ci		.mode		= 0644,
679562306a36Sopenharmony_ci		.proc_handler	= proc_dointvec,
679662306a36Sopenharmony_ci	},
679762306a36Sopenharmony_ci	{
679862306a36Sopenharmony_ci		.procname	= "temp_prefered_lft",
679962306a36Sopenharmony_ci		.data		= &ipv6_devconf.temp_prefered_lft,
680062306a36Sopenharmony_ci		.maxlen		= sizeof(int),
680162306a36Sopenharmony_ci		.mode		= 0644,
680262306a36Sopenharmony_ci		.proc_handler	= proc_dointvec,
680362306a36Sopenharmony_ci	},
680462306a36Sopenharmony_ci	{
680562306a36Sopenharmony_ci		.procname	= "regen_max_retry",
680662306a36Sopenharmony_ci		.data		= &ipv6_devconf.regen_max_retry,
680762306a36Sopenharmony_ci		.maxlen		= sizeof(int),
680862306a36Sopenharmony_ci		.mode		= 0644,
680962306a36Sopenharmony_ci		.proc_handler	= proc_dointvec,
681062306a36Sopenharmony_ci	},
681162306a36Sopenharmony_ci	{
681262306a36Sopenharmony_ci		.procname	= "max_desync_factor",
681362306a36Sopenharmony_ci		.data		= &ipv6_devconf.max_desync_factor,
681462306a36Sopenharmony_ci		.maxlen		= sizeof(int),
681562306a36Sopenharmony_ci		.mode		= 0644,
681662306a36Sopenharmony_ci		.proc_handler	= proc_dointvec,
681762306a36Sopenharmony_ci	},
681862306a36Sopenharmony_ci	{
681962306a36Sopenharmony_ci		.procname	= "max_addresses",
682062306a36Sopenharmony_ci		.data		= &ipv6_devconf.max_addresses,
682162306a36Sopenharmony_ci		.maxlen		= sizeof(int),
682262306a36Sopenharmony_ci		.mode		= 0644,
682362306a36Sopenharmony_ci		.proc_handler	= proc_dointvec,
682462306a36Sopenharmony_ci	},
682562306a36Sopenharmony_ci	{
682662306a36Sopenharmony_ci		.procname	= "accept_ra_defrtr",
682762306a36Sopenharmony_ci		.data		= &ipv6_devconf.accept_ra_defrtr,
682862306a36Sopenharmony_ci		.maxlen		= sizeof(int),
682962306a36Sopenharmony_ci		.mode		= 0644,
683062306a36Sopenharmony_ci		.proc_handler	= proc_dointvec,
683162306a36Sopenharmony_ci	},
683262306a36Sopenharmony_ci	{
683362306a36Sopenharmony_ci		.procname	= "ra_defrtr_metric",
683462306a36Sopenharmony_ci		.data		= &ipv6_devconf.ra_defrtr_metric,
683562306a36Sopenharmony_ci		.maxlen		= sizeof(u32),
683662306a36Sopenharmony_ci		.mode		= 0644,
683762306a36Sopenharmony_ci		.proc_handler	= proc_douintvec_minmax,
683862306a36Sopenharmony_ci		.extra1		= (void *)SYSCTL_ONE,
683962306a36Sopenharmony_ci	},
684062306a36Sopenharmony_ci	{
684162306a36Sopenharmony_ci		.procname	= "accept_ra_min_hop_limit",
684262306a36Sopenharmony_ci		.data		= &ipv6_devconf.accept_ra_min_hop_limit,
684362306a36Sopenharmony_ci		.maxlen		= sizeof(int),
684462306a36Sopenharmony_ci		.mode		= 0644,
684562306a36Sopenharmony_ci		.proc_handler	= proc_dointvec,
684662306a36Sopenharmony_ci	},
684762306a36Sopenharmony_ci	{
684862306a36Sopenharmony_ci		.procname	= "accept_ra_min_lft",
684962306a36Sopenharmony_ci		.data		= &ipv6_devconf.accept_ra_min_lft,
685062306a36Sopenharmony_ci		.maxlen		= sizeof(int),
685162306a36Sopenharmony_ci		.mode		= 0644,
685262306a36Sopenharmony_ci		.proc_handler	= proc_dointvec,
685362306a36Sopenharmony_ci	},
685462306a36Sopenharmony_ci	{
685562306a36Sopenharmony_ci		.procname	= "accept_ra_pinfo",
685662306a36Sopenharmony_ci		.data		= &ipv6_devconf.accept_ra_pinfo,
685762306a36Sopenharmony_ci		.maxlen		= sizeof(int),
685862306a36Sopenharmony_ci		.mode		= 0644,
685962306a36Sopenharmony_ci		.proc_handler	= proc_dointvec,
686062306a36Sopenharmony_ci	},
686162306a36Sopenharmony_ci#ifdef CONFIG_IPV6_ROUTER_PREF
686262306a36Sopenharmony_ci	{
686362306a36Sopenharmony_ci		.procname	= "accept_ra_rtr_pref",
686462306a36Sopenharmony_ci		.data		= &ipv6_devconf.accept_ra_rtr_pref,
686562306a36Sopenharmony_ci		.maxlen		= sizeof(int),
686662306a36Sopenharmony_ci		.mode		= 0644,
686762306a36Sopenharmony_ci		.proc_handler	= proc_dointvec,
686862306a36Sopenharmony_ci	},
686962306a36Sopenharmony_ci	{
687062306a36Sopenharmony_ci		.procname	= "router_probe_interval",
687162306a36Sopenharmony_ci		.data		= &ipv6_devconf.rtr_probe_interval,
687262306a36Sopenharmony_ci		.maxlen		= sizeof(int),
687362306a36Sopenharmony_ci		.mode		= 0644,
687462306a36Sopenharmony_ci		.proc_handler	= proc_dointvec_jiffies,
687562306a36Sopenharmony_ci	},
687662306a36Sopenharmony_ci#ifdef CONFIG_IPV6_ROUTE_INFO
687762306a36Sopenharmony_ci	{
687862306a36Sopenharmony_ci		.procname	= "accept_ra_rt_info_min_plen",
687962306a36Sopenharmony_ci		.data		= &ipv6_devconf.accept_ra_rt_info_min_plen,
688062306a36Sopenharmony_ci		.maxlen		= sizeof(int),
688162306a36Sopenharmony_ci		.mode		= 0644,
688262306a36Sopenharmony_ci		.proc_handler	= proc_dointvec,
688362306a36Sopenharmony_ci	},
688462306a36Sopenharmony_ci	{
688562306a36Sopenharmony_ci		.procname	= "accept_ra_rt_info_max_plen",
688662306a36Sopenharmony_ci		.data		= &ipv6_devconf.accept_ra_rt_info_max_plen,
688762306a36Sopenharmony_ci		.maxlen		= sizeof(int),
688862306a36Sopenharmony_ci		.mode		= 0644,
688962306a36Sopenharmony_ci		.proc_handler	= proc_dointvec,
689062306a36Sopenharmony_ci	},
689162306a36Sopenharmony_ci#endif
689262306a36Sopenharmony_ci#endif
689362306a36Sopenharmony_ci	{
689462306a36Sopenharmony_ci		.procname	= "proxy_ndp",
689562306a36Sopenharmony_ci		.data		= &ipv6_devconf.proxy_ndp,
689662306a36Sopenharmony_ci		.maxlen		= sizeof(int),
689762306a36Sopenharmony_ci		.mode		= 0644,
689862306a36Sopenharmony_ci		.proc_handler	= addrconf_sysctl_proxy_ndp,
689962306a36Sopenharmony_ci	},
690062306a36Sopenharmony_ci	{
690162306a36Sopenharmony_ci		.procname	= "accept_source_route",
690262306a36Sopenharmony_ci		.data		= &ipv6_devconf.accept_source_route,
690362306a36Sopenharmony_ci		.maxlen		= sizeof(int),
690462306a36Sopenharmony_ci		.mode		= 0644,
690562306a36Sopenharmony_ci		.proc_handler	= proc_dointvec,
690662306a36Sopenharmony_ci	},
690762306a36Sopenharmony_ci#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
690862306a36Sopenharmony_ci	{
690962306a36Sopenharmony_ci		.procname	= "optimistic_dad",
691062306a36Sopenharmony_ci		.data		= &ipv6_devconf.optimistic_dad,
691162306a36Sopenharmony_ci		.maxlen		= sizeof(int),
691262306a36Sopenharmony_ci		.mode		= 0644,
691362306a36Sopenharmony_ci		.proc_handler   = proc_dointvec,
691462306a36Sopenharmony_ci	},
691562306a36Sopenharmony_ci	{
691662306a36Sopenharmony_ci		.procname	= "use_optimistic",
691762306a36Sopenharmony_ci		.data		= &ipv6_devconf.use_optimistic,
691862306a36Sopenharmony_ci		.maxlen		= sizeof(int),
691962306a36Sopenharmony_ci		.mode		= 0644,
692062306a36Sopenharmony_ci		.proc_handler	= proc_dointvec,
692162306a36Sopenharmony_ci	},
692262306a36Sopenharmony_ci#endif
692362306a36Sopenharmony_ci#ifdef CONFIG_IPV6_MROUTE
692462306a36Sopenharmony_ci	{
692562306a36Sopenharmony_ci		.procname	= "mc_forwarding",
692662306a36Sopenharmony_ci		.data		= &ipv6_devconf.mc_forwarding,
692762306a36Sopenharmony_ci		.maxlen		= sizeof(int),
692862306a36Sopenharmony_ci		.mode		= 0444,
692962306a36Sopenharmony_ci		.proc_handler	= proc_dointvec,
693062306a36Sopenharmony_ci	},
693162306a36Sopenharmony_ci#endif
693262306a36Sopenharmony_ci	{
693362306a36Sopenharmony_ci		.procname	= "disable_ipv6",
693462306a36Sopenharmony_ci		.data		= &ipv6_devconf.disable_ipv6,
693562306a36Sopenharmony_ci		.maxlen		= sizeof(int),
693662306a36Sopenharmony_ci		.mode		= 0644,
693762306a36Sopenharmony_ci		.proc_handler	= addrconf_sysctl_disable,
693862306a36Sopenharmony_ci	},
693962306a36Sopenharmony_ci	{
694062306a36Sopenharmony_ci		.procname	= "accept_dad",
694162306a36Sopenharmony_ci		.data		= &ipv6_devconf.accept_dad,
694262306a36Sopenharmony_ci		.maxlen		= sizeof(int),
694362306a36Sopenharmony_ci		.mode		= 0644,
694462306a36Sopenharmony_ci		.proc_handler	= proc_dointvec,
694562306a36Sopenharmony_ci	},
694662306a36Sopenharmony_ci	{
694762306a36Sopenharmony_ci		.procname	= "force_tllao",
694862306a36Sopenharmony_ci		.data		= &ipv6_devconf.force_tllao,
694962306a36Sopenharmony_ci		.maxlen		= sizeof(int),
695062306a36Sopenharmony_ci		.mode		= 0644,
695162306a36Sopenharmony_ci		.proc_handler	= proc_dointvec
695262306a36Sopenharmony_ci	},
695362306a36Sopenharmony_ci	{
695462306a36Sopenharmony_ci		.procname	= "ndisc_notify",
695562306a36Sopenharmony_ci		.data		= &ipv6_devconf.ndisc_notify,
695662306a36Sopenharmony_ci		.maxlen		= sizeof(int),
695762306a36Sopenharmony_ci		.mode		= 0644,
695862306a36Sopenharmony_ci		.proc_handler	= proc_dointvec
695962306a36Sopenharmony_ci	},
696062306a36Sopenharmony_ci	{
696162306a36Sopenharmony_ci		.procname	= "suppress_frag_ndisc",
696262306a36Sopenharmony_ci		.data		= &ipv6_devconf.suppress_frag_ndisc,
696362306a36Sopenharmony_ci		.maxlen		= sizeof(int),
696462306a36Sopenharmony_ci		.mode		= 0644,
696562306a36Sopenharmony_ci		.proc_handler	= proc_dointvec
696662306a36Sopenharmony_ci	},
696762306a36Sopenharmony_ci	{
696862306a36Sopenharmony_ci		.procname	= "accept_ra_from_local",
696962306a36Sopenharmony_ci		.data		= &ipv6_devconf.accept_ra_from_local,
697062306a36Sopenharmony_ci		.maxlen		= sizeof(int),
697162306a36Sopenharmony_ci		.mode		= 0644,
697262306a36Sopenharmony_ci		.proc_handler	= proc_dointvec,
697362306a36Sopenharmony_ci	},
697462306a36Sopenharmony_ci	{
697562306a36Sopenharmony_ci		.procname	= "accept_ra_mtu",
697662306a36Sopenharmony_ci		.data		= &ipv6_devconf.accept_ra_mtu,
697762306a36Sopenharmony_ci		.maxlen		= sizeof(int),
697862306a36Sopenharmony_ci		.mode		= 0644,
697962306a36Sopenharmony_ci		.proc_handler	= proc_dointvec,
698062306a36Sopenharmony_ci	},
698162306a36Sopenharmony_ci	{
698262306a36Sopenharmony_ci		.procname	= "stable_secret",
698362306a36Sopenharmony_ci		.data		= &ipv6_devconf.stable_secret,
698462306a36Sopenharmony_ci		.maxlen		= IPV6_MAX_STRLEN,
698562306a36Sopenharmony_ci		.mode		= 0600,
698662306a36Sopenharmony_ci		.proc_handler	= addrconf_sysctl_stable_secret,
698762306a36Sopenharmony_ci	},
698862306a36Sopenharmony_ci	{
698962306a36Sopenharmony_ci		.procname	= "use_oif_addrs_only",
699062306a36Sopenharmony_ci		.data		= &ipv6_devconf.use_oif_addrs_only,
699162306a36Sopenharmony_ci		.maxlen		= sizeof(int),
699262306a36Sopenharmony_ci		.mode		= 0644,
699362306a36Sopenharmony_ci		.proc_handler	= proc_dointvec,
699462306a36Sopenharmony_ci	},
699562306a36Sopenharmony_ci	{
699662306a36Sopenharmony_ci		.procname	= "ignore_routes_with_linkdown",
699762306a36Sopenharmony_ci		.data		= &ipv6_devconf.ignore_routes_with_linkdown,
699862306a36Sopenharmony_ci		.maxlen		= sizeof(int),
699962306a36Sopenharmony_ci		.mode		= 0644,
700062306a36Sopenharmony_ci		.proc_handler	= addrconf_sysctl_ignore_routes_with_linkdown,
700162306a36Sopenharmony_ci	},
700262306a36Sopenharmony_ci	{
700362306a36Sopenharmony_ci		.procname	= "drop_unicast_in_l2_multicast",
700462306a36Sopenharmony_ci		.data		= &ipv6_devconf.drop_unicast_in_l2_multicast,
700562306a36Sopenharmony_ci		.maxlen		= sizeof(int),
700662306a36Sopenharmony_ci		.mode		= 0644,
700762306a36Sopenharmony_ci		.proc_handler	= proc_dointvec,
700862306a36Sopenharmony_ci	},
700962306a36Sopenharmony_ci	{
701062306a36Sopenharmony_ci		.procname	= "drop_unsolicited_na",
701162306a36Sopenharmony_ci		.data		= &ipv6_devconf.drop_unsolicited_na,
701262306a36Sopenharmony_ci		.maxlen		= sizeof(int),
701362306a36Sopenharmony_ci		.mode		= 0644,
701462306a36Sopenharmony_ci		.proc_handler	= proc_dointvec,
701562306a36Sopenharmony_ci	},
701662306a36Sopenharmony_ci	{
701762306a36Sopenharmony_ci		.procname	= "keep_addr_on_down",
701862306a36Sopenharmony_ci		.data		= &ipv6_devconf.keep_addr_on_down,
701962306a36Sopenharmony_ci		.maxlen		= sizeof(int),
702062306a36Sopenharmony_ci		.mode		= 0644,
702162306a36Sopenharmony_ci		.proc_handler	= proc_dointvec,
702262306a36Sopenharmony_ci
702362306a36Sopenharmony_ci	},
702462306a36Sopenharmony_ci	{
702562306a36Sopenharmony_ci		.procname	= "seg6_enabled",
702662306a36Sopenharmony_ci		.data		= &ipv6_devconf.seg6_enabled,
702762306a36Sopenharmony_ci		.maxlen		= sizeof(int),
702862306a36Sopenharmony_ci		.mode		= 0644,
702962306a36Sopenharmony_ci		.proc_handler	= proc_dointvec,
703062306a36Sopenharmony_ci	},
703162306a36Sopenharmony_ci#ifdef CONFIG_IPV6_SEG6_HMAC
703262306a36Sopenharmony_ci	{
703362306a36Sopenharmony_ci		.procname	= "seg6_require_hmac",
703462306a36Sopenharmony_ci		.data		= &ipv6_devconf.seg6_require_hmac,
703562306a36Sopenharmony_ci		.maxlen		= sizeof(int),
703662306a36Sopenharmony_ci		.mode		= 0644,
703762306a36Sopenharmony_ci		.proc_handler	= proc_dointvec,
703862306a36Sopenharmony_ci	},
703962306a36Sopenharmony_ci#endif
704062306a36Sopenharmony_ci	{
704162306a36Sopenharmony_ci		.procname       = "enhanced_dad",
704262306a36Sopenharmony_ci		.data           = &ipv6_devconf.enhanced_dad,
704362306a36Sopenharmony_ci		.maxlen         = sizeof(int),
704462306a36Sopenharmony_ci		.mode           = 0644,
704562306a36Sopenharmony_ci		.proc_handler   = proc_dointvec,
704662306a36Sopenharmony_ci	},
704762306a36Sopenharmony_ci	{
704862306a36Sopenharmony_ci		.procname	= "addr_gen_mode",
704962306a36Sopenharmony_ci		.data		= &ipv6_devconf.addr_gen_mode,
705062306a36Sopenharmony_ci		.maxlen		= sizeof(int),
705162306a36Sopenharmony_ci		.mode		= 0644,
705262306a36Sopenharmony_ci		.proc_handler	= addrconf_sysctl_addr_gen_mode,
705362306a36Sopenharmony_ci	},
705462306a36Sopenharmony_ci	{
705562306a36Sopenharmony_ci		.procname       = "disable_policy",
705662306a36Sopenharmony_ci		.data           = &ipv6_devconf.disable_policy,
705762306a36Sopenharmony_ci		.maxlen         = sizeof(int),
705862306a36Sopenharmony_ci		.mode           = 0644,
705962306a36Sopenharmony_ci		.proc_handler   = addrconf_sysctl_disable_policy,
706062306a36Sopenharmony_ci	},
706162306a36Sopenharmony_ci	{
706262306a36Sopenharmony_ci		.procname	= "ndisc_tclass",
706362306a36Sopenharmony_ci		.data		= &ipv6_devconf.ndisc_tclass,
706462306a36Sopenharmony_ci		.maxlen		= sizeof(int),
706562306a36Sopenharmony_ci		.mode		= 0644,
706662306a36Sopenharmony_ci		.proc_handler	= proc_dointvec_minmax,
706762306a36Sopenharmony_ci		.extra1		= (void *)SYSCTL_ZERO,
706862306a36Sopenharmony_ci		.extra2		= (void *)&two_five_five,
706962306a36Sopenharmony_ci	},
707062306a36Sopenharmony_ci	{
707162306a36Sopenharmony_ci		.procname	= "rpl_seg_enabled",
707262306a36Sopenharmony_ci		.data		= &ipv6_devconf.rpl_seg_enabled,
707362306a36Sopenharmony_ci		.maxlen		= sizeof(int),
707462306a36Sopenharmony_ci		.mode		= 0644,
707562306a36Sopenharmony_ci		.proc_handler	= proc_dointvec,
707662306a36Sopenharmony_ci	},
707762306a36Sopenharmony_ci	{
707862306a36Sopenharmony_ci		.procname	= "ioam6_enabled",
707962306a36Sopenharmony_ci		.data		= &ipv6_devconf.ioam6_enabled,
708062306a36Sopenharmony_ci		.maxlen		= sizeof(u8),
708162306a36Sopenharmony_ci		.mode		= 0644,
708262306a36Sopenharmony_ci		.proc_handler	= proc_dou8vec_minmax,
708362306a36Sopenharmony_ci		.extra1		= (void *)SYSCTL_ZERO,
708462306a36Sopenharmony_ci		.extra2		= (void *)SYSCTL_ONE,
708562306a36Sopenharmony_ci	},
708662306a36Sopenharmony_ci	{
708762306a36Sopenharmony_ci		.procname	= "ioam6_id",
708862306a36Sopenharmony_ci		.data		= &ipv6_devconf.ioam6_id,
708962306a36Sopenharmony_ci		.maxlen		= sizeof(u32),
709062306a36Sopenharmony_ci		.mode		= 0644,
709162306a36Sopenharmony_ci		.proc_handler	= proc_douintvec_minmax,
709262306a36Sopenharmony_ci		.extra1		= (void *)SYSCTL_ZERO,
709362306a36Sopenharmony_ci		.extra2		= (void *)&ioam6_if_id_max,
709462306a36Sopenharmony_ci	},
709562306a36Sopenharmony_ci	{
709662306a36Sopenharmony_ci		.procname	= "ioam6_id_wide",
709762306a36Sopenharmony_ci		.data		= &ipv6_devconf.ioam6_id_wide,
709862306a36Sopenharmony_ci		.maxlen		= sizeof(u32),
709962306a36Sopenharmony_ci		.mode		= 0644,
710062306a36Sopenharmony_ci		.proc_handler	= proc_douintvec,
710162306a36Sopenharmony_ci	},
710262306a36Sopenharmony_ci	{
710362306a36Sopenharmony_ci		.procname	= "ndisc_evict_nocarrier",
710462306a36Sopenharmony_ci		.data		= &ipv6_devconf.ndisc_evict_nocarrier,
710562306a36Sopenharmony_ci		.maxlen		= sizeof(u8),
710662306a36Sopenharmony_ci		.mode		= 0644,
710762306a36Sopenharmony_ci		.proc_handler	= proc_dou8vec_minmax,
710862306a36Sopenharmony_ci		.extra1		= (void *)SYSCTL_ZERO,
710962306a36Sopenharmony_ci		.extra2		= (void *)SYSCTL_ONE,
711062306a36Sopenharmony_ci	},
711162306a36Sopenharmony_ci	{
711262306a36Sopenharmony_ci		.procname	= "accept_untracked_na",
711362306a36Sopenharmony_ci		.data		= &ipv6_devconf.accept_untracked_na,
711462306a36Sopenharmony_ci		.maxlen		= sizeof(int),
711562306a36Sopenharmony_ci		.mode		= 0644,
711662306a36Sopenharmony_ci		.proc_handler	= proc_dointvec_minmax,
711762306a36Sopenharmony_ci		.extra1		= SYSCTL_ZERO,
711862306a36Sopenharmony_ci		.extra2		= SYSCTL_TWO,
711962306a36Sopenharmony_ci	},
712062306a36Sopenharmony_ci	{
712162306a36Sopenharmony_ci		/* sentinel */
712262306a36Sopenharmony_ci	}
712362306a36Sopenharmony_ci};
712462306a36Sopenharmony_ci
712562306a36Sopenharmony_cistatic int __addrconf_sysctl_register(struct net *net, char *dev_name,
712662306a36Sopenharmony_ci		struct inet6_dev *idev, struct ipv6_devconf *p)
712762306a36Sopenharmony_ci{
712862306a36Sopenharmony_ci	int i, ifindex;
712962306a36Sopenharmony_ci	struct ctl_table *table;
713062306a36Sopenharmony_ci	char path[sizeof("net/ipv6/conf/") + IFNAMSIZ];
713162306a36Sopenharmony_ci
713262306a36Sopenharmony_ci	table = kmemdup(addrconf_sysctl, sizeof(addrconf_sysctl), GFP_KERNEL_ACCOUNT);
713362306a36Sopenharmony_ci	if (!table)
713462306a36Sopenharmony_ci		goto out;
713562306a36Sopenharmony_ci
713662306a36Sopenharmony_ci	for (i = 0; table[i].data; i++) {
713762306a36Sopenharmony_ci		table[i].data += (char *)p - (char *)&ipv6_devconf;
713862306a36Sopenharmony_ci		/* If one of these is already set, then it is not safe to
713962306a36Sopenharmony_ci		 * overwrite either of them: this makes proc_dointvec_minmax
714062306a36Sopenharmony_ci		 * usable.
714162306a36Sopenharmony_ci		 */
714262306a36Sopenharmony_ci		if (!table[i].extra1 && !table[i].extra2) {
714362306a36Sopenharmony_ci			table[i].extra1 = idev; /* embedded; no ref */
714462306a36Sopenharmony_ci			table[i].extra2 = net;
714562306a36Sopenharmony_ci		}
714662306a36Sopenharmony_ci	}
714762306a36Sopenharmony_ci
714862306a36Sopenharmony_ci	snprintf(path, sizeof(path), "net/ipv6/conf/%s", dev_name);
714962306a36Sopenharmony_ci
715062306a36Sopenharmony_ci	p->sysctl_header = register_net_sysctl_sz(net, path, table,
715162306a36Sopenharmony_ci						  ARRAY_SIZE(addrconf_sysctl));
715262306a36Sopenharmony_ci	if (!p->sysctl_header)
715362306a36Sopenharmony_ci		goto free;
715462306a36Sopenharmony_ci
715562306a36Sopenharmony_ci	if (!strcmp(dev_name, "all"))
715662306a36Sopenharmony_ci		ifindex = NETCONFA_IFINDEX_ALL;
715762306a36Sopenharmony_ci	else if (!strcmp(dev_name, "default"))
715862306a36Sopenharmony_ci		ifindex = NETCONFA_IFINDEX_DEFAULT;
715962306a36Sopenharmony_ci	else
716062306a36Sopenharmony_ci		ifindex = idev->dev->ifindex;
716162306a36Sopenharmony_ci	inet6_netconf_notify_devconf(net, RTM_NEWNETCONF, NETCONFA_ALL,
716262306a36Sopenharmony_ci				     ifindex, p);
716362306a36Sopenharmony_ci	return 0;
716462306a36Sopenharmony_ci
716562306a36Sopenharmony_cifree:
716662306a36Sopenharmony_ci	kfree(table);
716762306a36Sopenharmony_ciout:
716862306a36Sopenharmony_ci	return -ENOBUFS;
716962306a36Sopenharmony_ci}
717062306a36Sopenharmony_ci
717162306a36Sopenharmony_cistatic void __addrconf_sysctl_unregister(struct net *net,
717262306a36Sopenharmony_ci					 struct ipv6_devconf *p, int ifindex)
717362306a36Sopenharmony_ci{
717462306a36Sopenharmony_ci	struct ctl_table *table;
717562306a36Sopenharmony_ci
717662306a36Sopenharmony_ci	if (!p->sysctl_header)
717762306a36Sopenharmony_ci		return;
717862306a36Sopenharmony_ci
717962306a36Sopenharmony_ci	table = p->sysctl_header->ctl_table_arg;
718062306a36Sopenharmony_ci	unregister_net_sysctl_table(p->sysctl_header);
718162306a36Sopenharmony_ci	p->sysctl_header = NULL;
718262306a36Sopenharmony_ci	kfree(table);
718362306a36Sopenharmony_ci
718462306a36Sopenharmony_ci	inet6_netconf_notify_devconf(net, RTM_DELNETCONF, 0, ifindex, NULL);
718562306a36Sopenharmony_ci}
718662306a36Sopenharmony_ci
718762306a36Sopenharmony_cistatic int addrconf_sysctl_register(struct inet6_dev *idev)
718862306a36Sopenharmony_ci{
718962306a36Sopenharmony_ci	int err;
719062306a36Sopenharmony_ci
719162306a36Sopenharmony_ci	if (!sysctl_dev_name_is_allowed(idev->dev->name))
719262306a36Sopenharmony_ci		return -EINVAL;
719362306a36Sopenharmony_ci
719462306a36Sopenharmony_ci	err = neigh_sysctl_register(idev->dev, idev->nd_parms,
719562306a36Sopenharmony_ci				    &ndisc_ifinfo_sysctl_change);
719662306a36Sopenharmony_ci	if (err)
719762306a36Sopenharmony_ci		return err;
719862306a36Sopenharmony_ci	err = __addrconf_sysctl_register(dev_net(idev->dev), idev->dev->name,
719962306a36Sopenharmony_ci					 idev, &idev->cnf);
720062306a36Sopenharmony_ci	if (err)
720162306a36Sopenharmony_ci		neigh_sysctl_unregister(idev->nd_parms);
720262306a36Sopenharmony_ci
720362306a36Sopenharmony_ci	return err;
720462306a36Sopenharmony_ci}
720562306a36Sopenharmony_ci
720662306a36Sopenharmony_cistatic void addrconf_sysctl_unregister(struct inet6_dev *idev)
720762306a36Sopenharmony_ci{
720862306a36Sopenharmony_ci	__addrconf_sysctl_unregister(dev_net(idev->dev), &idev->cnf,
720962306a36Sopenharmony_ci				     idev->dev->ifindex);
721062306a36Sopenharmony_ci	neigh_sysctl_unregister(idev->nd_parms);
721162306a36Sopenharmony_ci}
721262306a36Sopenharmony_ci
721362306a36Sopenharmony_ci
721462306a36Sopenharmony_ci#endif
721562306a36Sopenharmony_ci
721662306a36Sopenharmony_cistatic int __net_init addrconf_init_net(struct net *net)
721762306a36Sopenharmony_ci{
721862306a36Sopenharmony_ci	int err = -ENOMEM;
721962306a36Sopenharmony_ci	struct ipv6_devconf *all, *dflt;
722062306a36Sopenharmony_ci
722162306a36Sopenharmony_ci	spin_lock_init(&net->ipv6.addrconf_hash_lock);
722262306a36Sopenharmony_ci	INIT_DEFERRABLE_WORK(&net->ipv6.addr_chk_work, addrconf_verify_work);
722362306a36Sopenharmony_ci	net->ipv6.inet6_addr_lst = kcalloc(IN6_ADDR_HSIZE,
722462306a36Sopenharmony_ci					   sizeof(struct hlist_head),
722562306a36Sopenharmony_ci					   GFP_KERNEL);
722662306a36Sopenharmony_ci	if (!net->ipv6.inet6_addr_lst)
722762306a36Sopenharmony_ci		goto err_alloc_addr;
722862306a36Sopenharmony_ci
722962306a36Sopenharmony_ci	all = kmemdup(&ipv6_devconf, sizeof(ipv6_devconf), GFP_KERNEL);
723062306a36Sopenharmony_ci	if (!all)
723162306a36Sopenharmony_ci		goto err_alloc_all;
723262306a36Sopenharmony_ci
723362306a36Sopenharmony_ci	dflt = kmemdup(&ipv6_devconf_dflt, sizeof(ipv6_devconf_dflt), GFP_KERNEL);
723462306a36Sopenharmony_ci	if (!dflt)
723562306a36Sopenharmony_ci		goto err_alloc_dflt;
723662306a36Sopenharmony_ci
723762306a36Sopenharmony_ci	if (!net_eq(net, &init_net)) {
723862306a36Sopenharmony_ci		switch (net_inherit_devconf()) {
723962306a36Sopenharmony_ci		case 1:  /* copy from init_net */
724062306a36Sopenharmony_ci			memcpy(all, init_net.ipv6.devconf_all,
724162306a36Sopenharmony_ci			       sizeof(ipv6_devconf));
724262306a36Sopenharmony_ci			memcpy(dflt, init_net.ipv6.devconf_dflt,
724362306a36Sopenharmony_ci			       sizeof(ipv6_devconf_dflt));
724462306a36Sopenharmony_ci			break;
724562306a36Sopenharmony_ci		case 3: /* copy from the current netns */
724662306a36Sopenharmony_ci			memcpy(all, current->nsproxy->net_ns->ipv6.devconf_all,
724762306a36Sopenharmony_ci			       sizeof(ipv6_devconf));
724862306a36Sopenharmony_ci			memcpy(dflt,
724962306a36Sopenharmony_ci			       current->nsproxy->net_ns->ipv6.devconf_dflt,
725062306a36Sopenharmony_ci			       sizeof(ipv6_devconf_dflt));
725162306a36Sopenharmony_ci			break;
725262306a36Sopenharmony_ci		case 0:
725362306a36Sopenharmony_ci		case 2:
725462306a36Sopenharmony_ci			/* use compiled values */
725562306a36Sopenharmony_ci			break;
725662306a36Sopenharmony_ci		}
725762306a36Sopenharmony_ci	}
725862306a36Sopenharmony_ci
725962306a36Sopenharmony_ci	/* these will be inherited by all namespaces */
726062306a36Sopenharmony_ci	dflt->autoconf = ipv6_defaults.autoconf;
726162306a36Sopenharmony_ci	dflt->disable_ipv6 = ipv6_defaults.disable_ipv6;
726262306a36Sopenharmony_ci
726362306a36Sopenharmony_ci	dflt->stable_secret.initialized = false;
726462306a36Sopenharmony_ci	all->stable_secret.initialized = false;
726562306a36Sopenharmony_ci
726662306a36Sopenharmony_ci	net->ipv6.devconf_all = all;
726762306a36Sopenharmony_ci	net->ipv6.devconf_dflt = dflt;
726862306a36Sopenharmony_ci
726962306a36Sopenharmony_ci#ifdef CONFIG_SYSCTL
727062306a36Sopenharmony_ci	err = __addrconf_sysctl_register(net, "all", NULL, all);
727162306a36Sopenharmony_ci	if (err < 0)
727262306a36Sopenharmony_ci		goto err_reg_all;
727362306a36Sopenharmony_ci
727462306a36Sopenharmony_ci	err = __addrconf_sysctl_register(net, "default", NULL, dflt);
727562306a36Sopenharmony_ci	if (err < 0)
727662306a36Sopenharmony_ci		goto err_reg_dflt;
727762306a36Sopenharmony_ci#endif
727862306a36Sopenharmony_ci	return 0;
727962306a36Sopenharmony_ci
728062306a36Sopenharmony_ci#ifdef CONFIG_SYSCTL
728162306a36Sopenharmony_cierr_reg_dflt:
728262306a36Sopenharmony_ci	__addrconf_sysctl_unregister(net, all, NETCONFA_IFINDEX_ALL);
728362306a36Sopenharmony_cierr_reg_all:
728462306a36Sopenharmony_ci	kfree(dflt);
728562306a36Sopenharmony_ci	net->ipv6.devconf_dflt = NULL;
728662306a36Sopenharmony_ci#endif
728762306a36Sopenharmony_cierr_alloc_dflt:
728862306a36Sopenharmony_ci	kfree(all);
728962306a36Sopenharmony_ci	net->ipv6.devconf_all = NULL;
729062306a36Sopenharmony_cierr_alloc_all:
729162306a36Sopenharmony_ci	kfree(net->ipv6.inet6_addr_lst);
729262306a36Sopenharmony_cierr_alloc_addr:
729362306a36Sopenharmony_ci	return err;
729462306a36Sopenharmony_ci}
729562306a36Sopenharmony_ci
729662306a36Sopenharmony_cistatic void __net_exit addrconf_exit_net(struct net *net)
729762306a36Sopenharmony_ci{
729862306a36Sopenharmony_ci	int i;
729962306a36Sopenharmony_ci
730062306a36Sopenharmony_ci#ifdef CONFIG_SYSCTL
730162306a36Sopenharmony_ci	__addrconf_sysctl_unregister(net, net->ipv6.devconf_dflt,
730262306a36Sopenharmony_ci				     NETCONFA_IFINDEX_DEFAULT);
730362306a36Sopenharmony_ci	__addrconf_sysctl_unregister(net, net->ipv6.devconf_all,
730462306a36Sopenharmony_ci				     NETCONFA_IFINDEX_ALL);
730562306a36Sopenharmony_ci#endif
730662306a36Sopenharmony_ci	kfree(net->ipv6.devconf_dflt);
730762306a36Sopenharmony_ci	net->ipv6.devconf_dflt = NULL;
730862306a36Sopenharmony_ci	kfree(net->ipv6.devconf_all);
730962306a36Sopenharmony_ci	net->ipv6.devconf_all = NULL;
731062306a36Sopenharmony_ci
731162306a36Sopenharmony_ci	cancel_delayed_work_sync(&net->ipv6.addr_chk_work);
731262306a36Sopenharmony_ci	/*
731362306a36Sopenharmony_ci	 *	Check hash table, then free it.
731462306a36Sopenharmony_ci	 */
731562306a36Sopenharmony_ci	for (i = 0; i < IN6_ADDR_HSIZE; i++)
731662306a36Sopenharmony_ci		WARN_ON_ONCE(!hlist_empty(&net->ipv6.inet6_addr_lst[i]));
731762306a36Sopenharmony_ci
731862306a36Sopenharmony_ci	kfree(net->ipv6.inet6_addr_lst);
731962306a36Sopenharmony_ci	net->ipv6.inet6_addr_lst = NULL;
732062306a36Sopenharmony_ci}
732162306a36Sopenharmony_ci
732262306a36Sopenharmony_cistatic struct pernet_operations addrconf_ops = {
732362306a36Sopenharmony_ci	.init = addrconf_init_net,
732462306a36Sopenharmony_ci	.exit = addrconf_exit_net,
732562306a36Sopenharmony_ci};
732662306a36Sopenharmony_ci
732762306a36Sopenharmony_cistatic struct rtnl_af_ops inet6_ops __read_mostly = {
732862306a36Sopenharmony_ci	.family		  = AF_INET6,
732962306a36Sopenharmony_ci	.fill_link_af	  = inet6_fill_link_af,
733062306a36Sopenharmony_ci	.get_link_af_size = inet6_get_link_af_size,
733162306a36Sopenharmony_ci	.validate_link_af = inet6_validate_link_af,
733262306a36Sopenharmony_ci	.set_link_af	  = inet6_set_link_af,
733362306a36Sopenharmony_ci};
733462306a36Sopenharmony_ci
733562306a36Sopenharmony_ci/*
733662306a36Sopenharmony_ci *	Init / cleanup code
733762306a36Sopenharmony_ci */
733862306a36Sopenharmony_ci
733962306a36Sopenharmony_ciint __init addrconf_init(void)
734062306a36Sopenharmony_ci{
734162306a36Sopenharmony_ci	struct inet6_dev *idev;
734262306a36Sopenharmony_ci	int err;
734362306a36Sopenharmony_ci
734462306a36Sopenharmony_ci	err = ipv6_addr_label_init();
734562306a36Sopenharmony_ci	if (err < 0) {
734662306a36Sopenharmony_ci		pr_crit("%s: cannot initialize default policy table: %d\n",
734762306a36Sopenharmony_ci			__func__, err);
734862306a36Sopenharmony_ci		goto out;
734962306a36Sopenharmony_ci	}
735062306a36Sopenharmony_ci
735162306a36Sopenharmony_ci	err = register_pernet_subsys(&addrconf_ops);
735262306a36Sopenharmony_ci	if (err < 0)
735362306a36Sopenharmony_ci		goto out_addrlabel;
735462306a36Sopenharmony_ci
735562306a36Sopenharmony_ci	addrconf_wq = create_workqueue("ipv6_addrconf");
735662306a36Sopenharmony_ci	if (!addrconf_wq) {
735762306a36Sopenharmony_ci		err = -ENOMEM;
735862306a36Sopenharmony_ci		goto out_nowq;
735962306a36Sopenharmony_ci	}
736062306a36Sopenharmony_ci
736162306a36Sopenharmony_ci	rtnl_lock();
736262306a36Sopenharmony_ci	idev = ipv6_add_dev(blackhole_netdev);
736362306a36Sopenharmony_ci	rtnl_unlock();
736462306a36Sopenharmony_ci	if (IS_ERR(idev)) {
736562306a36Sopenharmony_ci		err = PTR_ERR(idev);
736662306a36Sopenharmony_ci		goto errlo;
736762306a36Sopenharmony_ci	}
736862306a36Sopenharmony_ci
736962306a36Sopenharmony_ci	ip6_route_init_special_entries();
737062306a36Sopenharmony_ci
737162306a36Sopenharmony_ci	register_netdevice_notifier(&ipv6_dev_notf);
737262306a36Sopenharmony_ci
737362306a36Sopenharmony_ci	addrconf_verify(&init_net);
737462306a36Sopenharmony_ci
737562306a36Sopenharmony_ci	rtnl_af_register(&inet6_ops);
737662306a36Sopenharmony_ci
737762306a36Sopenharmony_ci	err = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_GETLINK,
737862306a36Sopenharmony_ci				   NULL, inet6_dump_ifinfo, 0);
737962306a36Sopenharmony_ci	if (err < 0)
738062306a36Sopenharmony_ci		goto errout;
738162306a36Sopenharmony_ci
738262306a36Sopenharmony_ci	err = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_NEWADDR,
738362306a36Sopenharmony_ci				   inet6_rtm_newaddr, NULL, 0);
738462306a36Sopenharmony_ci	if (err < 0)
738562306a36Sopenharmony_ci		goto errout;
738662306a36Sopenharmony_ci	err = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_DELADDR,
738762306a36Sopenharmony_ci				   inet6_rtm_deladdr, NULL, 0);
738862306a36Sopenharmony_ci	if (err < 0)
738962306a36Sopenharmony_ci		goto errout;
739062306a36Sopenharmony_ci	err = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_GETADDR,
739162306a36Sopenharmony_ci				   inet6_rtm_getaddr, inet6_dump_ifaddr,
739262306a36Sopenharmony_ci				   RTNL_FLAG_DOIT_UNLOCKED);
739362306a36Sopenharmony_ci	if (err < 0)
739462306a36Sopenharmony_ci		goto errout;
739562306a36Sopenharmony_ci	err = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_GETMULTICAST,
739662306a36Sopenharmony_ci				   NULL, inet6_dump_ifmcaddr, 0);
739762306a36Sopenharmony_ci	if (err < 0)
739862306a36Sopenharmony_ci		goto errout;
739962306a36Sopenharmony_ci	err = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_GETANYCAST,
740062306a36Sopenharmony_ci				   NULL, inet6_dump_ifacaddr, 0);
740162306a36Sopenharmony_ci	if (err < 0)
740262306a36Sopenharmony_ci		goto errout;
740362306a36Sopenharmony_ci	err = rtnl_register_module(THIS_MODULE, PF_INET6, RTM_GETNETCONF,
740462306a36Sopenharmony_ci				   inet6_netconf_get_devconf,
740562306a36Sopenharmony_ci				   inet6_netconf_dump_devconf,
740662306a36Sopenharmony_ci				   RTNL_FLAG_DOIT_UNLOCKED);
740762306a36Sopenharmony_ci	if (err < 0)
740862306a36Sopenharmony_ci		goto errout;
740962306a36Sopenharmony_ci	err = ipv6_addr_label_rtnl_register();
741062306a36Sopenharmony_ci	if (err < 0)
741162306a36Sopenharmony_ci		goto errout;
741262306a36Sopenharmony_ci
741362306a36Sopenharmony_ci	return 0;
741462306a36Sopenharmony_cierrout:
741562306a36Sopenharmony_ci	rtnl_unregister_all(PF_INET6);
741662306a36Sopenharmony_ci	rtnl_af_unregister(&inet6_ops);
741762306a36Sopenharmony_ci	unregister_netdevice_notifier(&ipv6_dev_notf);
741862306a36Sopenharmony_cierrlo:
741962306a36Sopenharmony_ci	destroy_workqueue(addrconf_wq);
742062306a36Sopenharmony_ciout_nowq:
742162306a36Sopenharmony_ci	unregister_pernet_subsys(&addrconf_ops);
742262306a36Sopenharmony_ciout_addrlabel:
742362306a36Sopenharmony_ci	ipv6_addr_label_cleanup();
742462306a36Sopenharmony_ciout:
742562306a36Sopenharmony_ci	return err;
742662306a36Sopenharmony_ci}
742762306a36Sopenharmony_ci
742862306a36Sopenharmony_civoid addrconf_cleanup(void)
742962306a36Sopenharmony_ci{
743062306a36Sopenharmony_ci	struct net_device *dev;
743162306a36Sopenharmony_ci
743262306a36Sopenharmony_ci	unregister_netdevice_notifier(&ipv6_dev_notf);
743362306a36Sopenharmony_ci	unregister_pernet_subsys(&addrconf_ops);
743462306a36Sopenharmony_ci	ipv6_addr_label_cleanup();
743562306a36Sopenharmony_ci
743662306a36Sopenharmony_ci	rtnl_af_unregister(&inet6_ops);
743762306a36Sopenharmony_ci
743862306a36Sopenharmony_ci	rtnl_lock();
743962306a36Sopenharmony_ci
744062306a36Sopenharmony_ci	/* clean dev list */
744162306a36Sopenharmony_ci	for_each_netdev(&init_net, dev) {
744262306a36Sopenharmony_ci		if (__in6_dev_get(dev) == NULL)
744362306a36Sopenharmony_ci			continue;
744462306a36Sopenharmony_ci		addrconf_ifdown(dev, true);
744562306a36Sopenharmony_ci	}
744662306a36Sopenharmony_ci	addrconf_ifdown(init_net.loopback_dev, true);
744762306a36Sopenharmony_ci
744862306a36Sopenharmony_ci	rtnl_unlock();
744962306a36Sopenharmony_ci
745062306a36Sopenharmony_ci	destroy_workqueue(addrconf_wq);
745162306a36Sopenharmony_ci}
7452