addrconf.c 145 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1
2
3
4
5
/*
 *	IPv6 Address [auto]configuration
 *	Linux INET6 implementation
 *
 *	Authors:
6
 *	Pedro Roque		<roque@di.fc.ul.pt>
Linus Torvalds's avatar
Linus Torvalds committed
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
 *	Alexey Kuznetsov	<kuznet@ms2.inr.ac.ru>
 *
 *	This program is free software; you can redistribute it and/or
 *      modify it under the terms of the GNU General Public License
 *      as published by the Free Software Foundation; either version
 *      2 of the License, or (at your option) any later version.
 */

/*
 *	Changes:
 *
 *	Janos Farkas			:	delete timer on ifdown
 *	<chexum@bankinf.banki.hu>
 *	Andi Kleen			:	kill double kfree on module
 *						unload.
 *	Maciej W. Rozycki		:	FDDI support
 *	sekiya@USAGI			:	Don't send too many RS
 *						packets.
 *	yoshfuji@USAGI			:       Fixed interval between DAD
 *						packets.
 *	YOSHIFUJI Hideaki @USAGI	:	improved accuracy of
 *						address validation timer.
 *	YOSHIFUJI Hideaki @USAGI	:	Privacy Extensions (RFC3041)
 *						support.
 *	Yuji SEKIYA @USAGI		:	Don't assign a same IPv6
 *						address on a same interface.
 *	YOSHIFUJI Hideaki @USAGI	:	ARCnet support
 *	YOSHIFUJI Hideaki @USAGI	:	convert /proc/net/if_inet6 to
 *						seq_file.
36
37
38
 *	YOSHIFUJI Hideaki @USAGI	:	improved source address
 *						selection; consider scope,
 *						status etc.
Linus Torvalds's avatar
Linus Torvalds committed
39
40
 */

41
42
#define pr_fmt(fmt) "IPv6: " fmt

Linus Torvalds's avatar
Linus Torvalds committed
43
44
#include <linux/errno.h>
#include <linux/types.h>
45
#include <linux/kernel.h>
Linus Torvalds's avatar
Linus Torvalds committed
46
47
48
#include <linux/socket.h>
#include <linux/sockios.h>
#include <linux/net.h>
49
#include <linux/inet.h>
Linus Torvalds's avatar
Linus Torvalds committed
50
51
#include <linux/in6.h>
#include <linux/netdevice.h>
52
#include <linux/if_addr.h>
Linus Torvalds's avatar
Linus Torvalds committed
53
54
55
56
57
58
#include <linux/if_arp.h>
#include <linux/if_arcnet.h>
#include <linux/if_infiniband.h>
#include <linux/route.h>
#include <linux/inetdevice.h>
#include <linux/init.h>
59
#include <linux/slab.h>
Linus Torvalds's avatar
Linus Torvalds committed
60
61
62
#ifdef CONFIG_SYSCTL
#include <linux/sysctl.h>
#endif
63
#include <linux/capability.h>
Linus Torvalds's avatar
Linus Torvalds committed
64
65
#include <linux/delay.h>
#include <linux/notifier.h>
66
#include <linux/string.h>
67
#include <linux/hash.h>
Linus Torvalds's avatar
Linus Torvalds committed
68

69
#include <net/net_namespace.h>
Linus Torvalds's avatar
Linus Torvalds committed
70
71
72
#include <net/sock.h>
#include <net/snmp.h>

73
#include <net/6lowpan.h>
74
#include <net/firewire.h>
Linus Torvalds's avatar
Linus Torvalds committed
75
76
77
78
79
80
81
#include <net/ipv6.h>
#include <net/protocol.h>
#include <net/ndisc.h>
#include <net/ip6_route.h>
#include <net/addrconf.h>
#include <net/tcp.h>
#include <net/ip.h>
82
#include <net/netlink.h>
83
#include <net/pkt_sched.h>
84
#include <net/l3mdev.h>
Linus Torvalds's avatar
Linus Torvalds committed
85
86
#include <linux/if_tunnel.h>
#include <linux/rtnetlink.h>
87
#include <linux/netconf.h>
Linus Torvalds's avatar
Linus Torvalds committed
88
#include <linux/random.h>
Stephen Hemminger's avatar
Stephen Hemminger committed
89
#include <linux/uaccess.h>
90
#include <asm/unaligned.h>
Linus Torvalds's avatar
Linus Torvalds committed
91
92
93

#include <linux/proc_fs.h>
#include <linux/seq_file.h>
94
#include <linux/export.h>
Linus Torvalds's avatar
Linus Torvalds committed
95
96
97
98
99

/* Set to 3 to get tracing... */
#define ACONF_DEBUG 2

#if ACONF_DEBUG >= 3
100
#define ADBG(fmt, ...) printk(fmt, ##__VA_ARGS__)
Linus Torvalds's avatar
Linus Torvalds committed
101
#else
102
#define ADBG(fmt, ...) do { if (0) printk(fmt, ##__VA_ARGS__); } while (0)
Linus Torvalds's avatar
Linus Torvalds committed
103
104
105
#endif

#define	INFINITY_LIFE_TIME	0xFFFFFFFF
106

107
108
109
#define IPV6_MAX_STRLEN \
	sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")

110
111
112
113
static inline u32 cstamp_delta(unsigned long cstamp)
{
	return (cstamp - INITIAL_JIFFIES) * 100UL / HZ;
}
Linus Torvalds's avatar
Linus Torvalds committed
114
115

#ifdef CONFIG_SYSCTL
116
static int addrconf_sysctl_register(struct inet6_dev *idev);
117
118
static void addrconf_sysctl_unregister(struct inet6_dev *idev);
#else
119
static inline int addrconf_sysctl_register(struct inet6_dev *idev)
120
{
121
	return 0;
122
123
124
125
126
}

static inline void addrconf_sysctl_unregister(struct inet6_dev *idev)
{
}
Linus Torvalds's avatar
Linus Torvalds committed
127
128
#endif

Sorin Dumitru's avatar
Sorin Dumitru committed
129
130
static void __ipv6_regen_rndid(struct inet6_dev *idev);
static void __ipv6_try_regen_rndid(struct inet6_dev *idev, struct in6_addr *tmpaddr);
Linus Torvalds's avatar
Linus Torvalds committed
131
132
static void ipv6_regen_rndid(unsigned long data);

133
static int ipv6_generate_eui64(u8 *eui, struct net_device *dev);
Linus Torvalds's avatar
Linus Torvalds committed
134
static int ipv6_count_addresses(struct inet6_dev *idev);
135
136
137
static int ipv6_generate_stable_address(struct in6_addr *addr,
					u8 dad_count,
					const struct inet6_dev *idev);
Linus Torvalds's avatar
Linus Torvalds committed
138
139
140
141

/*
 *	Configured unicast address hash table
 */
142
static struct hlist_head inet6_addr_lst[IN6_ADDR_HSIZE];
143
static DEFINE_SPINLOCK(addrconf_hash_lock);
Linus Torvalds's avatar
Linus Torvalds committed
144

145
146
147
static void addrconf_verify(void);
static void addrconf_verify_rtnl(void);
static void addrconf_verify_work(struct work_struct *);
Linus Torvalds's avatar
Linus Torvalds committed
148

149
150
static struct workqueue_struct *addrconf_wq;
static DECLARE_DELAYED_WORK(addr_chk_work, addrconf_verify_work);
Linus Torvalds's avatar
Linus Torvalds committed
151
152
153
154

static void addrconf_join_anycast(struct inet6_ifaddr *ifp);
static void addrconf_leave_anycast(struct inet6_ifaddr *ifp);

155
156
static void addrconf_type_change(struct net_device *dev,
				 unsigned long event);
Linus Torvalds's avatar
Linus Torvalds committed
157
158
static int addrconf_ifdown(struct net_device *dev, int how);

159
160
161
162
163
static struct rt6_info *addrconf_get_prefix_route(const struct in6_addr *pfx,
						  int plen,
						  const struct net_device *dev,
						  u32 flags, u32 noflags);

164
static void addrconf_dad_start(struct inet6_ifaddr *ifp);
165
static void addrconf_dad_work(struct work_struct *w);
Linus Torvalds's avatar
Linus Torvalds committed
166
static void addrconf_dad_completed(struct inet6_ifaddr *ifp);
167
static void addrconf_dad_run(struct inet6_dev *idev);
Linus Torvalds's avatar
Linus Torvalds committed
168
169
170
171
static void addrconf_rs_timer(unsigned long data);
static void __ipv6_ifa_notify(int event, struct inet6_ifaddr *ifa);
static void ipv6_ifa_notify(int event, struct inet6_ifaddr *ifa);

172
static void inet6_prefix_notify(int event, struct inet6_dev *idev,
Linus Torvalds's avatar
Linus Torvalds committed
173
				struct prefix_info *pinfo);
174
175
static bool ipv6_chk_same_addr(struct net *net, const struct in6_addr *addr,
			       struct net_device *dev);
Linus Torvalds's avatar
Linus Torvalds committed
176

177
static struct ipv6_devconf ipv6_devconf __read_mostly = {
Linus Torvalds's avatar
Linus Torvalds committed
178
179
180
181
182
183
184
	.forwarding		= 0,
	.hop_limit		= IPV6_DEFAULT_HOPLIMIT,
	.mtu6			= IPV6_MIN_MTU,
	.accept_ra		= 1,
	.accept_redirects	= 1,
	.autoconf		= 1,
	.force_mld_version	= 0,
185
186
	.mldv1_unsolicited_report_interval = 10 * HZ,
	.mldv2_unsolicited_report_interval = HZ,
Linus Torvalds's avatar
Linus Torvalds committed
187
188
189
190
	.dad_transmits		= 1,
	.rtr_solicits		= MAX_RTR_SOLICITATIONS,
	.rtr_solicit_interval	= RTR_SOLICITATION_INTERVAL,
	.rtr_solicit_delay	= MAX_RTR_SOLICITATION_DELAY,
191
	.use_tempaddr		= 0,
Linus Torvalds's avatar
Linus Torvalds committed
192
193
194
195
196
	.temp_valid_lft		= TEMP_VALID_LIFETIME,
	.temp_prefered_lft	= TEMP_PREFERRED_LIFETIME,
	.regen_max_retry	= REGEN_MAX_RETRY,
	.max_desync_factor	= MAX_DESYNC_FACTOR,
	.max_addresses		= IPV6_MAX_ADDRESSES,
197
	.accept_ra_defrtr	= 1,
198
	.accept_ra_from_local	= 0,
199
	.accept_ra_min_hop_limit= 1,
200
	.accept_ra_pinfo	= 1,
201
202
#ifdef CONFIG_IPV6_ROUTER_PREF
	.accept_ra_rtr_pref	= 1,
203
	.rtr_probe_interval	= 60 * HZ,
204
205
206
#ifdef CONFIG_IPV6_ROUTE_INFO
	.accept_ra_rt_info_max_plen = 0,
#endif
207
#endif
208
	.proxy_ndp		= 0,
209
	.accept_source_route	= 0,	/* we do not accept RH0 by default. */
210
	.disable_ipv6		= 0,
211
	.accept_dad		= 1,
212
	.suppress_frag_ndisc	= 1,
213
	.accept_ra_mtu		= 1,
214
215
	.stable_secret		= {
		.initialized = false,
216
217
	},
	.use_oif_addrs_only	= 0,
218
	.ignore_routes_with_linkdown = 0,
Linus Torvalds's avatar
Linus Torvalds committed
219
220
};

221
static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
Linus Torvalds's avatar
Linus Torvalds committed
222
223
224
225
226
227
	.forwarding		= 0,
	.hop_limit		= IPV6_DEFAULT_HOPLIMIT,
	.mtu6			= IPV6_MIN_MTU,
	.accept_ra		= 1,
	.accept_redirects	= 1,
	.autoconf		= 1,
228
229
230
	.force_mld_version	= 0,
	.mldv1_unsolicited_report_interval = 10 * HZ,
	.mldv2_unsolicited_report_interval = HZ,
Linus Torvalds's avatar
Linus Torvalds committed
231
232
233
234
235
236
237
238
239
240
	.dad_transmits		= 1,
	.rtr_solicits		= MAX_RTR_SOLICITATIONS,
	.rtr_solicit_interval	= RTR_SOLICITATION_INTERVAL,
	.rtr_solicit_delay	= MAX_RTR_SOLICITATION_DELAY,
	.use_tempaddr		= 0,
	.temp_valid_lft		= TEMP_VALID_LIFETIME,
	.temp_prefered_lft	= TEMP_PREFERRED_LIFETIME,
	.regen_max_retry	= REGEN_MAX_RETRY,
	.max_desync_factor	= MAX_DESYNC_FACTOR,
	.max_addresses		= IPV6_MAX_ADDRESSES,
241
	.accept_ra_defrtr	= 1,
242
	.accept_ra_from_local	= 0,
243
	.accept_ra_min_hop_limit= 1,
244
	.accept_ra_pinfo	= 1,
245
246
#ifdef CONFIG_IPV6_ROUTER_PREF
	.accept_ra_rtr_pref	= 1,
247
	.rtr_probe_interval	= 60 * HZ,
248
249
250
#ifdef CONFIG_IPV6_ROUTE_INFO
	.accept_ra_rt_info_max_plen = 0,
#endif
251
#endif
252
	.proxy_ndp		= 0,
253
	.accept_source_route	= 0,	/* we do not accept RH0 by default. */
254
	.disable_ipv6		= 0,
255
	.accept_dad		= 1,
256
	.suppress_frag_ndisc	= 1,
257
	.accept_ra_mtu		= 1,
258
259
260
	.stable_secret		= {
		.initialized = false,
	},
261
	.use_oif_addrs_only	= 0,
262
	.ignore_routes_with_linkdown = 0,
Linus Torvalds's avatar
Linus Torvalds committed
263
264
};

265
/* Check if a valid qdisc is available */
266
static inline bool addrconf_qdisc_ok(const struct net_device *dev)
267
{
268
	return !qdisc_tx_is_noop(dev);
269
270
}

271
static void addrconf_del_rs_timer(struct inet6_dev *idev)
Linus Torvalds's avatar
Linus Torvalds committed
272
{
273
274
275
276
	if (del_timer(&idev->rs_timer))
		__in6_dev_put(idev);
}

277
static void addrconf_del_dad_work(struct inet6_ifaddr *ifp)
278
{
279
	if (cancel_delayed_work(&ifp->dad_work))
Linus Torvalds's avatar
Linus Torvalds committed
280
281
282
		__in6_ifa_put(ifp);
}

283
284
285
286
287
288
289
static void addrconf_mod_rs_timer(struct inet6_dev *idev,
				  unsigned long when)
{
	if (!timer_pending(&idev->rs_timer))
		in6_dev_hold(idev);
	mod_timer(&idev->rs_timer, jiffies + when);
}
Linus Torvalds's avatar
Linus Torvalds committed
290

291
292
static void addrconf_mod_dad_work(struct inet6_ifaddr *ifp,
				   unsigned long delay)
Linus Torvalds's avatar
Linus Torvalds committed
293
{
294
	if (!delayed_work_pending(&ifp->dad_work))
Linus Torvalds's avatar
Linus Torvalds committed
295
		in6_ifa_hold(ifp);
296
	mod_delayed_work(addrconf_wq, &ifp->dad_work, delay);
Linus Torvalds's avatar
Linus Torvalds committed
297
298
}

299
300
static int snmp6_alloc_dev(struct inet6_dev *idev)
{
301
302
	int i;

WANG Cong's avatar
WANG Cong committed
303
304
	idev->stats.ipv6 = alloc_percpu(struct ipstats_mib);
	if (!idev->stats.ipv6)
305
		goto err_ip;
306
307
308

	for_each_possible_cpu(i) {
		struct ipstats_mib *addrconf_stats;
WANG Cong's avatar
WANG Cong committed
309
		addrconf_stats = per_cpu_ptr(idev->stats.ipv6, i);
310
311
312
313
		u64_stats_init(&addrconf_stats->syncp);
	}


314
315
316
	idev->stats.icmpv6dev = kzalloc(sizeof(struct icmpv6_mib_device),
					GFP_KERNEL);
	if (!idev->stats.icmpv6dev)
317
		goto err_icmp;
318
319
320
	idev->stats.icmpv6msgdev = kzalloc(sizeof(struct icmpv6msg_mib_device),
					   GFP_KERNEL);
	if (!idev->stats.icmpv6msgdev)
321
		goto err_icmpmsg;
322
323
324

	return 0;

325
err_icmpmsg:
326
	kfree(idev->stats.icmpv6dev);
327
err_icmp:
WANG Cong's avatar
WANG Cong committed
328
	free_percpu(idev->stats.ipv6);
329
err_ip:
330
	return -ENOMEM;
331
332
}

333
static struct inet6_dev *ipv6_add_dev(struct net_device *dev)
Linus Torvalds's avatar
Linus Torvalds committed
334
335
{
	struct inet6_dev *ndev;
336
	int err = -ENOMEM;
Linus Torvalds's avatar
Linus Torvalds committed
337
338
339
340

	ASSERT_RTNL();

	if (dev->mtu < IPV6_MIN_MTU)
341
		return ERR_PTR(-EINVAL);
Linus Torvalds's avatar
Linus Torvalds committed
342

343
	ndev = kzalloc(sizeof(struct inet6_dev), GFP_KERNEL);
344
	if (!ndev)
345
		return ERR_PTR(err);
346
347
348

	rwlock_init(&ndev->lock);
	ndev->dev = dev;
349
	INIT_LIST_HEAD(&ndev->addr_list);
350
351
	setup_timer(&ndev->rs_timer, addrconf_rs_timer,
		    (unsigned long)ndev);
352
	memcpy(&ndev->cnf, dev_net(dev)->ipv6.devconf_dflt, sizeof(ndev->cnf));
353
354
355
356
357
358

	if (ndev->cnf.stable_secret.initialized)
		ndev->addr_gen_mode = IN6_ADDR_GEN_MODE_STABLE_PRIVACY;
	else
		ndev->addr_gen_mode = IN6_ADDR_GEN_MODE_EUI64;

359
360
361
	ndev->cnf.mtu6 = dev->mtu;
	ndev->cnf.sysctl = NULL;
	ndev->nd_parms = neigh_parms_alloc(dev, &nd_tbl);
362
	if (!ndev->nd_parms) {
363
		kfree(ndev);
364
		return ERR_PTR(err);
365
	}
366
367
	if (ndev->cnf.forwarding)
		dev_disable_lro(dev);
368
369
	/* We refer to the device */
	dev_hold(dev);
Linus Torvalds's avatar
Linus Torvalds committed
370

371
	if (snmp6_alloc_dev(ndev) < 0) {
372
		ADBG(KERN_WARNING
373
			"%s: cannot allocate memory for statistics; dev=%s.\n",
374
			__func__, dev->name);
375
		neigh_parms_release(&nd_tbl, ndev->nd_parms);
Roy Li's avatar
Roy Li committed
376
377
		dev_put(dev);
		kfree(ndev);
378
		return ERR_PTR(err);
379
	}
Linus Torvalds's avatar
Linus Torvalds committed
380

381
	if (snmp6_register_dev(ndev) < 0) {
382
		ADBG(KERN_WARNING
383
			"%s: cannot create /proc/net/dev_snmp6/%s\n",
384
			__func__, dev->name);
385
		goto err_release;
386
387
388
389
390
391
	}

	/* One reference from device.  We must do this before
	 * we invoke __ipv6_regen_rndid().
	 */
	in6_dev_hold(ndev);
Linus Torvalds's avatar
Linus Torvalds committed
392

393
394
395
	if (dev->flags & (IFF_NOARP | IFF_LOOPBACK))
		ndev->cnf.accept_dad = -1;

Amerigo Wang's avatar
Amerigo Wang committed
396
#if IS_ENABLED(CONFIG_IPV6_SIT)
397
	if (dev->type == ARPHRD_SIT && (dev->priv_flags & IFF_ISATAP)) {
398
		pr_info("%s: Disabled Multicast RS\n", dev->name);
399
400
401
402
		ndev->cnf.rtr_solicits = 0;
	}
#endif

403
	INIT_LIST_HEAD(&ndev->tempaddr_list);
404
	setup_timer(&ndev->regen_timer, ipv6_regen_rndid, (unsigned long)ndev);
405
406
	if ((dev->flags&IFF_LOOPBACK) ||
	    dev->type == ARPHRD_TUNNEL ||
407
	    dev->type == ARPHRD_TUNNEL6 ||
408
409
	    dev->type == ARPHRD_SIT ||
	    dev->type == ARPHRD_NONE) {
410
411
412
413
414
		ndev->cnf.use_tempaddr = -1;
	} else {
		in6_dev_hold(ndev);
		ipv6_regen_rndid((unsigned long) ndev);
	}
415

416
	ndev->token = in6addr_any;
Linus Torvalds's avatar
Linus Torvalds committed
417

418
	if (netif_running(dev) && addrconf_qdisc_ok(dev))
419
420
		ndev->if_flags |= IF_READY;

421
422
	ipv6_mc_init_dev(ndev);
	ndev->tstamp = jiffies;
423
424
425
426
	err = addrconf_sysctl_register(ndev);
	if (err) {
		ipv6_mc_destroy_dev(ndev);
		del_timer(&ndev->regen_timer);
427
		snmp6_unregister_dev(ndev);
428
429
		goto err_release;
	}
430
	/* protected by rtnl_lock */
431
	rcu_assign_pointer(dev->ip6_ptr, ndev);
432

433
434
435
	/* Join interface-local all-node multicast group */
	ipv6_dev_mc_inc(dev, &in6addr_interfacelocal_allnodes);

436
	/* Join all-node multicast group */
437
	ipv6_dev_mc_inc(dev, &in6addr_linklocal_allnodes);
438

439
	/* Join all-router multicast group if forwarding is set */
Li Wei's avatar
Li Wei committed
440
	if (ndev->cnf.forwarding && (dev->flags & IFF_MULTICAST))
441
442
		ipv6_dev_mc_inc(dev, &in6addr_linklocal_allrouters);

Linus Torvalds's avatar
Linus Torvalds committed
443
	return ndev;
444
445
446
447
448
449

err_release:
	neigh_parms_release(&nd_tbl, ndev->nd_parms);
	ndev->dead = 1;
	in6_dev_finish_destroy(ndev);
	return ERR_PTR(err);
Linus Torvalds's avatar
Linus Torvalds committed
450
451
}

452
static struct inet6_dev *ipv6_find_idev(struct net_device *dev)
Linus Torvalds's avatar
Linus Torvalds committed
453
454
455
456
457
{
	struct inet6_dev *idev;

	ASSERT_RTNL();

Stephen Hemminger's avatar
Stephen Hemminger committed
458
459
460
	idev = __in6_dev_get(dev);
	if (!idev) {
		idev = ipv6_add_dev(dev);
461
		if (IS_ERR(idev))
Linus Torvalds's avatar
Linus Torvalds committed
462
463
			return NULL;
	}
464

Linus Torvalds's avatar
Linus Torvalds committed
465
466
467
468
469
	if (dev->flags&IFF_UP)
		ipv6_mc_up(idev);
	return idev;
}

470
471
472
473
474
static int inet6_netconf_msgsize_devconf(int type)
{
	int size =  NLMSG_ALIGN(sizeof(struct netconfmsg))
		    + nla_total_size(4);	/* NETCONFA_IFINDEX */

475
476
	/* type -1 is used for ALL */
	if (type == -1 || type == NETCONFA_FORWARDING)
477
		size += nla_total_size(4);
478
#ifdef CONFIG_IPV6_MROUTE
479
480
	if (type == -1 || type == NETCONFA_MC_FORWARDING)
		size += nla_total_size(4);
481
#endif
482
483
	if (type == -1 || type == NETCONFA_PROXY_NEIGH)
		size += nla_total_size(4);
484

485
486
487
	if (type == -1 || type == NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN)
		size += nla_total_size(4);

488
489
490
491
492
493
494
495
496
497
498
499
500
	return size;
}

static int inet6_netconf_fill_devconf(struct sk_buff *skb, int ifindex,
				      struct ipv6_devconf *devconf, u32 portid,
				      u32 seq, int event, unsigned int flags,
				      int type)
{
	struct nlmsghdr  *nlh;
	struct netconfmsg *ncm;

	nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct netconfmsg),
			flags);
501
	if (!nlh)
502
503
504
505
506
507
508
509
		return -EMSGSIZE;

	ncm = nlmsg_data(nlh);
	ncm->ncm_family = AF_INET6;

	if (nla_put_s32(skb, NETCONFA_IFINDEX, ifindex) < 0)
		goto nla_put_failure;

510
511
	/* type -1 is used for ALL */
	if ((type == -1 || type == NETCONFA_FORWARDING) &&
512
513
	    nla_put_s32(skb, NETCONFA_FORWARDING, devconf->forwarding) < 0)
		goto nla_put_failure;
514
#ifdef CONFIG_IPV6_MROUTE
515
516
517
518
	if ((type == -1 || type == NETCONFA_MC_FORWARDING) &&
	    nla_put_s32(skb, NETCONFA_MC_FORWARDING,
			devconf->mc_forwarding) < 0)
		goto nla_put_failure;
519
#endif
520
521
522
523
	if ((type == -1 || type == NETCONFA_PROXY_NEIGH) &&
	    nla_put_s32(skb, NETCONFA_PROXY_NEIGH, devconf->proxy_ndp) < 0)
		goto nla_put_failure;

524
525
526
527
528
	if ((type == -1 || type == NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN) &&
	    nla_put_s32(skb, NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN,
			devconf->ignore_routes_with_linkdown) < 0)
		goto nla_put_failure;

529
530
	nlmsg_end(skb, nlh);
	return 0;
531
532
533
534
535
536

nla_put_failure:
	nlmsg_cancel(skb, nlh);
	return -EMSGSIZE;
}

537
538
void inet6_netconf_notify_devconf(struct net *net, int type, int ifindex,
				  struct ipv6_devconf *devconf)
539
540
541
542
543
{
	struct sk_buff *skb;
	int err = -ENOBUFS;

	skb = nlmsg_new(inet6_netconf_msgsize_devconf(type), GFP_ATOMIC);
544
	if (!skb)
545
546
547
548
549
550
551
552
553
554
555
556
557
		goto errout;

	err = inet6_netconf_fill_devconf(skb, ifindex, devconf, 0, 0,
					 RTM_NEWNETCONF, 0, type);
	if (err < 0) {
		/* -EMSGSIZE implies BUG in inet6_netconf_msgsize_devconf() */
		WARN_ON(err == -EMSGSIZE);
		kfree_skb(skb);
		goto errout;
	}
	rtnl_notify(skb, net, 0, RTNLGRP_IPV6_NETCONF, NULL, GFP_ATOMIC);
	return;
errout:
558
	rtnl_set_sk_err(net, RTNLGRP_IPV6_NETCONF, err);
559
560
}

561
562
563
static const struct nla_policy devconf_ipv6_policy[NETCONFA_MAX+1] = {
	[NETCONFA_IFINDEX]	= { .len = sizeof(int) },
	[NETCONFA_FORWARDING]	= { .len = sizeof(int) },
564
	[NETCONFA_PROXY_NEIGH]	= { .len = sizeof(int) },
565
	[NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN]	= { .len = sizeof(int) },
566
567
568
};

static int inet6_netconf_get_devconf(struct sk_buff *in_skb,
569
				     struct nlmsghdr *nlh)
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
{
	struct net *net = sock_net(in_skb->sk);
	struct nlattr *tb[NETCONFA_MAX+1];
	struct netconfmsg *ncm;
	struct sk_buff *skb;
	struct ipv6_devconf *devconf;
	struct inet6_dev *in6_dev;
	struct net_device *dev;
	int ifindex;
	int err;

	err = nlmsg_parse(nlh, sizeof(*ncm), tb, NETCONFA_MAX,
			  devconf_ipv6_policy);
	if (err < 0)
		goto errout;

	err = EINVAL;
	if (!tb[NETCONFA_IFINDEX])
		goto errout;

	ifindex = nla_get_s32(tb[NETCONFA_IFINDEX]);
	switch (ifindex) {
	case NETCONFA_IFINDEX_ALL:
		devconf = net->ipv6.devconf_all;
		break;
	case NETCONFA_IFINDEX_DEFAULT:
		devconf = net->ipv6.devconf_dflt;
		break;
	default:
		dev = __dev_get_by_index(net, ifindex);
600
		if (!dev)
601
602
			goto errout;
		in6_dev = __in6_dev_get(dev);
603
		if (!in6_dev)
604
605
606
607
608
609
610
			goto errout;
		devconf = &in6_dev->cnf;
		break;
	}

	err = -ENOBUFS;
	skb = nlmsg_new(inet6_netconf_msgsize_devconf(-1), GFP_ATOMIC);
611
	if (!skb)
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
		goto errout;

	err = inet6_netconf_fill_devconf(skb, ifindex, devconf,
					 NETLINK_CB(in_skb).portid,
					 nlh->nlmsg_seq, RTM_NEWNETCONF, 0,
					 -1);
	if (err < 0) {
		/* -EMSGSIZE implies BUG in inet6_netconf_msgsize_devconf() */
		WARN_ON(err == -EMSGSIZE);
		kfree_skb(skb);
		goto errout;
	}
	err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
errout:
	return err;
}

629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
static int inet6_netconf_dump_devconf(struct sk_buff *skb,
				      struct netlink_callback *cb)
{
	struct net *net = sock_net(skb->sk);
	int h, s_h;
	int idx, s_idx;
	struct net_device *dev;
	struct inet6_dev *idev;
	struct hlist_head *head;

	s_h = cb->args[0];
	s_idx = idx = cb->args[1];

	for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
		idx = 0;
		head = &net->dev_index_head[h];
		rcu_read_lock();
646
647
		cb->seq = atomic_read(&net->ipv6.dev_addr_genid) ^
			  net->dev_base_seq;
648
649
650
651
652
653
654
655
656
657
658
659
660
		hlist_for_each_entry_rcu(dev, head, index_hlist) {
			if (idx < s_idx)
				goto cont;
			idev = __in6_dev_get(dev);
			if (!idev)
				goto cont;

			if (inet6_netconf_fill_devconf(skb, dev->ifindex,
						       &idev->cnf,
						       NETLINK_CB(cb->skb).portid,
						       cb->nlh->nlmsg_seq,
						       RTM_NEWNETCONF,
						       NLM_F_MULTI,
661
						       -1) < 0) {
662
663
664
				rcu_read_unlock();
				goto done;
			}
665
			nl_dump_check_consistent(cb, nlmsg_hdr(skb));
666
667
668
669
670
671
672
673
674
675
676
cont:
			idx++;
		}
		rcu_read_unlock();
	}
	if (h == NETDEV_HASHENTRIES) {
		if (inet6_netconf_fill_devconf(skb, NETCONFA_IFINDEX_ALL,
					       net->ipv6.devconf_all,
					       NETLINK_CB(cb->skb).portid,
					       cb->nlh->nlmsg_seq,
					       RTM_NEWNETCONF, NLM_F_MULTI,
677
					       -1) < 0)
678
679
680
681
682
683
684
685
686
687
			goto done;
		else
			h++;
	}
	if (h == NETDEV_HASHENTRIES + 1) {
		if (inet6_netconf_fill_devconf(skb, NETCONFA_IFINDEX_DEFAULT,
					       net->ipv6.devconf_dflt,
					       NETLINK_CB(cb->skb).portid,
					       cb->nlh->nlmsg_seq,
					       RTM_NEWNETCONF, NLM_F_MULTI,
688
					       -1) < 0)
689
690
691
692
693
694
695
696
697
698
699
			goto done;
		else
			h++;
	}
done:
	cb->args[0] = h;
	cb->args[1] = idx;

	return skb->len;
}

Linus Torvalds's avatar
Linus Torvalds committed
700
701
702
703
704
705
706
707
708
#ifdef CONFIG_SYSCTL
static void dev_forward_change(struct inet6_dev *idev)
{
	struct net_device *dev;
	struct inet6_ifaddr *ifa;

	if (!idev)
		return;
	dev = idev->dev;
709
710
	if (idev->cnf.forwarding)
		dev_disable_lro(dev);
711
	if (dev->flags & IFF_MULTICAST) {
712
		if (idev->cnf.forwarding) {
713
			ipv6_dev_mc_inc(dev, &in6addr_linklocal_allrouters);
714
715
716
			ipv6_dev_mc_inc(dev, &in6addr_interfacelocal_allrouters);
			ipv6_dev_mc_inc(dev, &in6addr_sitelocal_allrouters);
		} else {
717
			ipv6_dev_mc_dec(dev, &in6addr_linklocal_allrouters);
718
719
720
			ipv6_dev_mc_dec(dev, &in6addr_interfacelocal_allrouters);
			ipv6_dev_mc_dec(dev, &in6addr_sitelocal_allrouters);
		}
Linus Torvalds's avatar
Linus Torvalds committed
721
	}
722
723

	list_for_each_entry(ifa, &idev->addr_list, if_list) {
Michal Wrobel's avatar
Michal Wrobel committed
724
725
		if (ifa->flags&IFA_F_TENTATIVE)
			continue;
Linus Torvalds's avatar
Linus Torvalds committed
726
727
728
729
730
		if (idev->cnf.forwarding)
			addrconf_join_anycast(ifa);
		else
			addrconf_leave_anycast(ifa);
	}
731
732
	inet6_netconf_notify_devconf(dev_net(dev), NETCONFA_FORWARDING,
				     dev->ifindex, &idev->cnf);
Linus Torvalds's avatar
Linus Torvalds committed
733
734
735
}


736
static void addrconf_forward_change(struct net *net, __s32 newf)
Linus Torvalds's avatar
Linus Torvalds committed
737
738
739
740
{
	struct net_device *dev;
	struct inet6_dev *idev;

741
	for_each_netdev(net, dev) {
Linus Torvalds's avatar
Linus Torvalds committed
742
743
		idev = __in6_dev_get(dev);
		if (idev) {
744
745
			int changed = (!idev->cnf.forwarding) ^ (!newf);
			idev->cnf.forwarding = newf;
Linus Torvalds's avatar
Linus Torvalds committed
746
747
748
749
750
			if (changed)
				dev_forward_change(idev);
		}
	}
}
751

752
static int addrconf_fixup_forwarding(struct ctl_table *table, int *p, int newf)
753
{
754
	struct net *net;
755
756
757
758
	int old;

	if (!rtnl_trylock())
		return restart_syscall();
759
760

	net = (struct net *)table->extra2;
761
762
	old = *p;
	*p = newf;
763

764
	if (p == &net->ipv6.devconf_dflt->forwarding) {
765
766
767
768
		if ((!newf) ^ (!old))
			inet6_netconf_notify_devconf(net, NETCONFA_FORWARDING,
						     NETCONFA_IFINDEX_DEFAULT,
						     net->ipv6.devconf_dflt);
769
770
		rtnl_unlock();
		return 0;
771
	}
772

773
774
775
	if (p == &net->ipv6.devconf_all->forwarding) {
		net->ipv6.devconf_dflt->forwarding = newf;
		addrconf_forward_change(net, newf);
776
777
778
779
		if ((!newf) ^ (!old))
			inet6_netconf_notify_devconf(net, NETCONFA_FORWARDING,
						     NETCONFA_IFINDEX_ALL,
						     net->ipv6.devconf_all);
780
	} else if ((!newf) ^ (!old))
781
		dev_forward_change((struct inet6_dev *)table->extra1);
782
	rtnl_unlock();
783

784
	if (newf)
785
		rt6_purge_dflt_routers(net);
786
	return 1;
787
}
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844

static void addrconf_linkdown_change(struct net *net, __s32 newf)
{
	struct net_device *dev;
	struct inet6_dev *idev;

	for_each_netdev(net, dev) {
		idev = __in6_dev_get(dev);
		if (idev) {
			int changed = (!idev->cnf.ignore_routes_with_linkdown) ^ (!newf);

			idev->cnf.ignore_routes_with_linkdown = newf;
			if (changed)
				inet6_netconf_notify_devconf(dev_net(dev),
							     NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN,
							     dev->ifindex,
							     &idev->cnf);
		}
	}
}

static int addrconf_fixup_linkdown(struct ctl_table *table, int *p, int newf)
{
	struct net *net;
	int old;

	if (!rtnl_trylock())
		return restart_syscall();

	net = (struct net *)table->extra2;
	old = *p;
	*p = newf;

	if (p == &net->ipv6.devconf_dflt->ignore_routes_with_linkdown) {
		if ((!newf) ^ (!old))
			inet6_netconf_notify_devconf(net,
						     NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN,
						     NETCONFA_IFINDEX_DEFAULT,
						     net->ipv6.devconf_dflt);
		rtnl_unlock();
		return 0;
	}

	if (p == &net->ipv6.devconf_all->ignore_routes_with_linkdown) {
		net->ipv6.devconf_dflt->ignore_routes_with_linkdown = newf;
		addrconf_linkdown_change(net, newf);
		if ((!newf) ^ (!old))
			inet6_netconf_notify_devconf(net,
						     NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN,
						     NETCONFA_IFINDEX_ALL,
						     net->ipv6.devconf_all);
	}
	rtnl_unlock();

	return 1;
}

Linus Torvalds's avatar
Linus Torvalds committed
845
846
#endif

847
/* Nobody refers to this ifaddr, destroy it */
Linus Torvalds's avatar
Linus Torvalds committed
848
849
void inet6_ifa_finish_destroy(struct inet6_ifaddr *ifp)
{
850
	WARN_ON(!hlist_unhashed(&ifp->addr_lst));
851

Linus Torvalds's avatar
Linus Torvalds committed
852
#ifdef NET_REFCNT_DEBUG
853
	pr_debug("%s\n", __func__);
Linus Torvalds's avatar
Linus Torvalds committed
854
855
856
857
#endif

	in6_dev_put(ifp->idev);

858
859
860
	if (cancel_delayed_work(&ifp->dad_work))
		pr_notice("delayed DAD work was pending while freeing ifa=%p\n",
			  ifp);
Linus Torvalds's avatar
Linus Torvalds committed
861

862
	if (ifp->state != INET6_IFADDR_STATE_DEAD) {
863
		pr_warn("Freeing alive inet6 address %p\n", ifp);
Linus Torvalds's avatar
Linus Torvalds committed
864
865
		return;
	}
Amerigo Wang's avatar
Amerigo Wang committed
866
	ip6_rt_put(ifp->rt);
Linus Torvalds's avatar
Linus Torvalds committed
867

868
	kfree_rcu(ifp, rcu);
Linus Torvalds's avatar
Linus Torvalds committed
869
870
}

Brian Haley's avatar
Brian Haley committed
871
872
873
static void
ipv6_link_dev_addr(struct inet6_dev *idev, struct inet6_ifaddr *ifp)
{
874
	struct list_head *p;
875
	int ifp_scope = ipv6_addr_src_scope(&ifp->addr);
Brian Haley's avatar
Brian Haley committed
876
877
878
879
880

	/*
	 * Each device address list is sorted in order of scope -
	 * global before linklocal.
	 */
881
882
883
	list_for_each(p, &idev->addr_list) {
		struct inet6_ifaddr *ifa
			= list_entry(p, struct inet6_ifaddr, if_list);
884
		if (ifp_scope >= ipv6_addr_src_scope(&ifa->addr))
Brian Haley's avatar
Brian Haley committed
885
886
887
			break;
	}

888
	list_add_tail(&ifp->if_list, p);
Brian Haley's avatar
Brian Haley committed
889
890
}

891
static u32 inet6_addr_hash(const struct in6_addr *addr)
892
{
893
	return hash_32(ipv6_addr_hash(addr), IN6_ADDR_HSIZE_SHIFT);
894
895
}

Linus Torvalds's avatar
Linus Torvalds committed
896
897
898
/* On success it returns ifp with increased reference count */

static struct inet6_ifaddr *
899
900
ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr,
	      const struct in6_addr *peer_addr, int pfxlen,
901
	      int scope, u32 flags, u32 valid_lft, u32 prefered_lft)
Linus Torvalds's avatar
Linus Torvalds committed
902
903
904
{
	struct inet6_ifaddr *ifa = NULL;
	struct rt6_info *rt;
905
	unsigned int hash;
Linus Torvalds's avatar
Linus Torvalds committed
906
	int err = 0;
907
908
909
910
911
912
913
	int addr_type = ipv6_addr_type(addr);

	if (addr_type == IPV6_ADDR_ANY ||
	    addr_type & IPV6_ADDR_MULTICAST ||
	    (!(idev->dev->flags & IFF_LOOPBACK) &&
	     addr_type & IPV6_ADDR_LOOPBACK))
		return ERR_PTR(-EADDRNOTAVAIL);
Linus Torvalds's avatar
Linus Torvalds committed
914

915
	rcu_read_lock_bh();
Linus Torvalds's avatar
Linus Torvalds committed
916
917
918
919
920
	if (idev->dead) {
		err = -ENODEV;			/*XXX*/
		goto out2;
	}

921
	if (idev->cnf.disable_ipv6) {
922
923
924
925
		err = -EACCES;
		goto out2;
	}

926
	spin_lock(&addrconf_hash_lock);
Linus Torvalds's avatar
Linus Torvalds committed
927
928

	/* Ignore adding duplicate addresses on an interface */
929
	if (ipv6_chk_same_addr(dev_net(idev->dev), addr, idev->dev)) {
930
		ADBG("ipv6_add_addr: already assigned\n");
Linus Torvalds's avatar
Linus Torvalds committed
931
932
933
934
		err = -EEXIST;
		goto out;
	}

935
	ifa = kzalloc(sizeof(struct inet6_ifaddr), GFP_ATOMIC);
Linus Torvalds's avatar
Linus Torvalds committed
936

937
	if (!ifa) {
938
		ADBG("ipv6_add_addr: malloc failed\n");
Linus Torvalds's avatar
Linus Torvalds committed
939
940
941
942
		err = -ENOBUFS;
		goto out;
	}

943
	rt = addrconf_dst_alloc(idev, addr, false);
Linus Torvalds's avatar
Linus Torvalds committed
944
945
946
947
948
	if (IS_ERR(rt)) {
		err = PTR_ERR(rt);
		goto out;
	}

949
950
	neigh_parms_data_state_setall(idev->nd_parms);

Alexey Dobriyan's avatar
Alexey Dobriyan committed
951
	ifa->addr = *addr;
952
953
	if (peer_addr)
		ifa->peer_addr = *peer_addr;
Linus Torvalds's avatar
Linus Torvalds committed
954
955

	spin_lock_init(&ifa->lock);
956
	INIT_DELAYED_WORK(&ifa->dad_work, addrconf_dad_work);
957
	INIT_HLIST_NODE(&ifa->addr_lst);
Linus Torvalds's avatar
Linus Torvalds committed
958
959
960
	ifa->scope = scope;
	ifa->prefix_len = pfxlen;
	ifa->flags = flags | IFA_F_TENTATIVE;
961
962
	ifa->valid_lft = valid_lft;
	ifa->prefered_lft = prefered_lft;
Linus Torvalds's avatar
Linus Torvalds committed
963
	ifa->cstamp = ifa->tstamp = jiffies;
964
	ifa->tokenized = false;
Linus Torvalds's avatar
Linus Torvalds committed
965

966
967
	ifa->rt = rt;

Linus Torvalds's avatar
Linus Torvalds committed
968
969
970
971
972
973
	ifa->idev = idev;
	in6_dev_hold(idev);
	/* For caller */
	in6_ifa_hold(ifa);

	/* Add to big hash table */
974
	hash = inet6_addr_hash(addr);
Linus Torvalds's avatar
Linus Torvalds committed
975

976
977
	hlist_add_head_rcu(&ifa->addr_lst, &inet6_addr_lst[hash]);
	spin_unlock(&addrconf_hash_lock);
Linus Torvalds's avatar
Linus Torvalds committed
978
979
980

	write_lock(&idev->lock);
	/* Add to inet6_dev unicast addr list. */
Brian Haley's avatar
Brian Haley committed
981
	ipv6_link_dev_addr(idev, ifa);
Linus Torvalds's avatar
Linus Torvalds committed
982
983

	if (ifa->flags&IFA_F_TEMPORARY) {
984
		list_add(&ifa->tmp_list, &idev->tempaddr_list);
Linus Torvalds's avatar
Linus Torvalds committed
985
986
987
988
989
990
		in6_ifa_hold(ifa);
	}

	in6_ifa_hold(ifa);
	write_unlock(&idev->lock);
out2:
991
	rcu_read_unlock_bh();
Linus Torvalds's avatar
Linus Torvalds committed
992

993
	if (likely(err == 0))
994
		inet6addr_notifier_call_chain(NETDEV_UP, ifa);
Linus Torvalds's avatar
Linus Torvalds committed
995
996
997
998
999
1000
1001
	else {
		kfree(ifa);
		ifa = ERR_PTR(err);
	}

	return ifa;
out:
1002
	spin_unlock(&addrconf_hash_lock);
Linus Torvalds's avatar
Linus Torvalds committed
1003
1004
1005
	goto out2;
}

1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
enum cleanup_prefix_rt_t {
	CLEANUP_PREFIX_RT_NOP,    /* no cleanup action for prefix route */
	CLEANUP_PREFIX_RT_DEL,    /* delete the prefix route */
	CLEANUP_PREFIX_RT_EXPIRE, /* update the lifetime of the prefix route */
};

/*
 * Check, whether the prefix for ifp would still need a prefix route
 * after deleting ifp. The function returns one of the CLEANUP_PREFIX_RT_*
 * constants.
 *
 * 1) we don't purge prefix if address was not permanent.
 *    prefix is managed by its own lifetime.
 * 2) we also don't purge, if the address was IFA_F_NOPREFIXROUTE.
 * 3) if there are no addresses, delete prefix.
 * 4) if there are still other permanent address(es),
 *    corresponding prefix is still permanent.
 * 5) if there are still other addresses with IFA_F_NOPREFIXROUTE,
 *    don't purge the prefix, assume user space is managing it.
 * 6) otherwise, update prefix lifetime to the
 *    longest valid lifetime among the corresponding
 *    addresses on the device.
 *    Note: subsequent RA will update lifetime.
 **/
static enum cleanup_prefix_rt_t
check_cleanup_prefix_route(struct inet6_ifaddr *ifp, unsigned long *expires)
{
	struct inet6_ifaddr *ifa;
	struct inet6_dev *idev = ifp->idev;
	unsigned long lifetime;
	enum cleanup_prefix_rt_t action = CLEANUP_PREFIX_RT_DEL;

	*expires = jiffies;

	list_for_each_entry(ifa, &idev->addr_list, if_list) {
		if (ifa == ifp)
			continue;
		if (!ipv6_prefix_equal(&ifa->addr, &ifp->addr,
				       ifp->prefix_len))
			continue;
		if (ifa->flags & (IFA_F_PERMANENT | IFA_F_NOPREFIXROUTE))
			return CLEANUP_PREFIX_RT_NOP;

		action = CLEANUP_PREFIX_RT_EXPIRE;

		spin_lock(&ifa->lock);

		lifetime = addrconf_timeout_fixup(ifa->valid_lft, HZ);
		/*
		 * Note: Because this address is
		 * not permanent, lifetime <
		 * LONG_MAX / HZ here.
		 */
		if (time_before(*expires, ifa->tstamp + lifetime * HZ))
			*expires = ifa->tstamp + lifetime * HZ;
		spin_unlock(&ifa->lock);
	}

	return action;
}

static void
cleanup_prefix_route(struct inet6_ifaddr *ifp, unsigned long expires, bool del_rt)
{
	struct rt6_info *rt;

	rt = addrconf_get_prefix_route(&ifp->addr,
				       ifp->prefix_len,
				       ifp->idev->dev,
				       0, RTF_GATEWAY | RTF_DEFAULT);
	if (rt) {
		if (del_rt)
			ip6_del_rt(rt);
		else {
			if (!(rt->rt6i_flags & RTF_EXPIRES))
				rt6_set_expires(rt, expires);
			ip6_rt_put(rt);
		}
	}
}


Linus Torvalds's avatar
Linus Torvalds committed
1088
1089
1090
1091
/* This function wants to get referenced ifp and releases it before return */

static void ipv6_del_addr(struct inet6_ifaddr *ifp)
{
1092
	int state;
1093
1094
	enum cleanup_prefix_rt_t action = CLEANUP_PREFIX_RT_NOP;
	unsigned long expires;
Linus Torvalds's avatar
Linus Torvalds committed
1095

1096
1097
	ASSERT_RTNL();

1098
	spin_lock_bh(&ifp->lock);
1099
	state = ifp->state;
1100
	ifp