blob: ff36194a71aa7dd0fe0f707cff80afd83e40102e [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * Neighbour Discovery for IPv6
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09003 * Linux INET6 implementation
Linus Torvalds1da177e2005-04-16 15:20:36 -07004 *
5 * Authors:
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09006 * Pedro Roque <roque@di.fc.ul.pt>
Linus Torvalds1da177e2005-04-16 15:20:36 -07007 * Mike Shaver <shaver@ingenia.com>
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version
12 * 2 of the License, or (at your option) any later version.
13 */
14
15/*
16 * Changes:
17 *
Alexey I. Froloffe35f30c2012-04-06 05:50:58 +000018 * Alexey I. Froloff : RFC6106 (DNSSL) support
Pierre Ynard31910572007-10-10 21:22:05 -070019 * Pierre Ynard : export userland ND options
20 * through netlink (RDNSS support)
Linus Torvalds1da177e2005-04-16 15:20:36 -070021 * Lars Fenneberg : fixed MTU setting on receipt
22 * of an RA.
Linus Torvalds1da177e2005-04-16 15:20:36 -070023 * Janos Farkas : kmalloc failure checks
24 * Alexey Kuznetsov : state machine reworked
25 * and moved to net/core.
26 * Pekka Savola : RFC2461 validation
27 * YOSHIFUJI Hideaki @USAGI : Verify ND options properly
28 */
29
Joe Perches675418d2012-05-16 19:28:38 +000030#define pr_fmt(fmt) "ICMPv6: " fmt
Linus Torvalds1da177e2005-04-16 15:20:36 -070031
32#include <linux/module.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070033#include <linux/errno.h>
34#include <linux/types.h>
35#include <linux/socket.h>
36#include <linux/sockios.h>
37#include <linux/sched.h>
38#include <linux/net.h>
39#include <linux/in6.h>
40#include <linux/route.h>
41#include <linux/init.h>
42#include <linux/rcupdate.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090043#include <linux/slab.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070044#ifdef CONFIG_SYSCTL
45#include <linux/sysctl.h>
46#endif
47
Thomas Graf18237302006-08-04 23:04:54 -070048#include <linux/if_addr.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070049#include <linux/if_arp.h>
50#include <linux/ipv6.h>
51#include <linux/icmpv6.h>
52#include <linux/jhash.h>
53
54#include <net/sock.h>
55#include <net/snmp.h>
56
57#include <net/ipv6.h>
58#include <net/protocol.h>
59#include <net/ndisc.h>
60#include <net/ip6_route.h>
61#include <net/addrconf.h>
62#include <net/icmp.h>
63
Pierre Ynard31910572007-10-10 21:22:05 -070064#include <net/netlink.h>
65#include <linux/rtnetlink.h>
66
Linus Torvalds1da177e2005-04-16 15:20:36 -070067#include <net/flow.h>
68#include <net/ip6_checksum.h>
Denis V. Lunev1ed85162008-04-03 14:31:03 -070069#include <net/inet_common.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070070#include <linux/proc_fs.h>
71
72#include <linux/netfilter.h>
73#include <linux/netfilter_ipv6.h>
74
Joe Perches675418d2012-05-16 19:28:38 +000075/* Set to 3 to get tracing... */
76#define ND_DEBUG 1
77
78#define ND_PRINTK(val, level, fmt, ...) \
79do { \
80 if (val <= ND_DEBUG) \
81 net_##level##_ratelimited(fmt, ##__VA_ARGS__); \
82} while (0)
83
Eric Dumazetd6bf7812010-10-04 06:15:44 +000084static u32 ndisc_hash(const void *pkey,
85 const struct net_device *dev,
David S. Miller2c2aba62011-12-28 15:06:58 -050086 __u32 *hash_rnd);
Linus Torvalds1da177e2005-04-16 15:20:36 -070087static int ndisc_constructor(struct neighbour *neigh);
88static void ndisc_solicit(struct neighbour *neigh, struct sk_buff *skb);
89static void ndisc_error_report(struct neighbour *neigh, struct sk_buff *skb);
90static int pndisc_constructor(struct pneigh_entry *n);
91static void pndisc_destructor(struct pneigh_entry *n);
92static void pndisc_redo(struct sk_buff *skb);
93
Stephen Hemminger89d69d22009-09-01 11:13:19 +000094static const struct neigh_ops ndisc_generic_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -070095 .family = AF_INET6,
96 .solicit = ndisc_solicit,
97 .error_report = ndisc_error_report,
98 .output = neigh_resolve_output,
99 .connected_output = neigh_connected_output,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100};
101
Stephen Hemminger89d69d22009-09-01 11:13:19 +0000102static const struct neigh_ops ndisc_hh_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700103 .family = AF_INET6,
104 .solicit = ndisc_solicit,
105 .error_report = ndisc_error_report,
106 .output = neigh_resolve_output,
107 .connected_output = neigh_resolve_output,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108};
109
110
Stephen Hemminger89d69d22009-09-01 11:13:19 +0000111static const struct neigh_ops ndisc_direct_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700112 .family = AF_INET6,
David S. Miller8f40b162011-07-17 13:34:11 -0700113 .output = neigh_direct_output,
114 .connected_output = neigh_direct_output,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700115};
116
117struct neigh_table nd_tbl = {
118 .family = AF_INET6,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700119 .key_len = sizeof(struct in6_addr),
120 .hash = ndisc_hash,
121 .constructor = ndisc_constructor,
122 .pconstructor = pndisc_constructor,
123 .pdestructor = pndisc_destructor,
124 .proxy_redo = pndisc_redo,
125 .id = "ndisc_cache",
126 .parms = {
Shan Weib6720832010-12-01 18:05:12 +0000127 .tbl = &nd_tbl,
128 .base_reachable_time = ND_REACHABLE_TIME,
129 .retrans_time = ND_RETRANS_TIMER,
130 .gc_staletime = 60 * HZ,
131 .reachable_time = ND_REACHABLE_TIME,
132 .delay_probe_time = 5 * HZ,
Eric Dumazet8b5c1712011-11-09 12:07:14 +0000133 .queue_len_bytes = 64*1024,
Shan Weib6720832010-12-01 18:05:12 +0000134 .ucast_probes = 3,
135 .mcast_probes = 3,
136 .anycast_delay = 1 * HZ,
137 .proxy_delay = (8 * HZ) / 10,
138 .proxy_qlen = 64,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700139 },
140 .gc_interval = 30 * HZ,
141 .gc_thresh1 = 128,
142 .gc_thresh2 = 512,
143 .gc_thresh3 = 1024,
144};
145
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146static inline int ndisc_opt_addr_space(struct net_device *dev)
147{
148 return NDISC_OPT_SPACE(dev->addr_len + ndisc_addr_option_pad(dev->type));
149}
150
151static u8 *ndisc_fill_addr_option(u8 *opt, int type, void *data, int data_len,
152 unsigned short addr_type)
153{
154 int space = NDISC_OPT_SPACE(data_len);
155 int pad = ndisc_addr_option_pad(addr_type);
156
157 opt[0] = type;
158 opt[1] = space>>3;
159
160 memset(opt + 2, 0, pad);
161 opt += pad;
162 space -= pad;
163
164 memcpy(opt+2, data, data_len);
165 data_len += 2;
166 opt += data_len;
167 if ((space -= data_len) > 0)
168 memset(opt, 0, space);
169 return opt + space;
170}
171
172static struct nd_opt_hdr *ndisc_next_option(struct nd_opt_hdr *cur,
173 struct nd_opt_hdr *end)
174{
175 int type;
176 if (!cur || !end || cur >= end)
177 return NULL;
178 type = cur->nd_opt_type;
179 do {
180 cur = ((void *)cur) + (cur->nd_opt_len << 3);
181 } while(cur < end && cur->nd_opt_type != type);
Eric Dumazeta02cec22010-09-22 20:43:57 +0000182 return cur <= end && cur->nd_opt_type == type ? cur : NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183}
184
Pierre Ynard31910572007-10-10 21:22:05 -0700185static inline int ndisc_is_useropt(struct nd_opt_hdr *opt)
186{
Alexey I. Froloffe35f30c2012-04-06 05:50:58 +0000187 return opt->nd_opt_type == ND_OPT_RDNSS ||
188 opt->nd_opt_type == ND_OPT_DNSSL;
Pierre Ynard31910572007-10-10 21:22:05 -0700189}
190
191static struct nd_opt_hdr *ndisc_next_useropt(struct nd_opt_hdr *cur,
192 struct nd_opt_hdr *end)
193{
194 if (!cur || !end || cur >= end)
195 return NULL;
196 do {
197 cur = ((void *)cur) + (cur->nd_opt_len << 3);
198 } while(cur < end && !ndisc_is_useropt(cur));
Eric Dumazeta02cec22010-09-22 20:43:57 +0000199 return cur <= end && ndisc_is_useropt(cur) ? cur : NULL;
Pierre Ynard31910572007-10-10 21:22:05 -0700200}
201
David S. Miller30f2a5f2012-07-11 23:26:46 -0700202struct ndisc_options *ndisc_parse_options(u8 *opt, int opt_len,
203 struct ndisc_options *ndopts)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700204{
205 struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)opt;
206
207 if (!nd_opt || opt_len < 0 || !ndopts)
208 return NULL;
209 memset(ndopts, 0, sizeof(*ndopts));
210 while (opt_len) {
211 int l;
212 if (opt_len < sizeof(struct nd_opt_hdr))
213 return NULL;
214 l = nd_opt->nd_opt_len << 3;
215 if (opt_len < l || l == 0)
216 return NULL;
217 switch (nd_opt->nd_opt_type) {
218 case ND_OPT_SOURCE_LL_ADDR:
219 case ND_OPT_TARGET_LL_ADDR:
220 case ND_OPT_MTU:
221 case ND_OPT_REDIRECT_HDR:
222 if (ndopts->nd_opt_array[nd_opt->nd_opt_type]) {
Joe Perches675418d2012-05-16 19:28:38 +0000223 ND_PRINTK(2, warn,
224 "%s: duplicated ND6 option found: type=%d\n",
225 __func__, nd_opt->nd_opt_type);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700226 } else {
227 ndopts->nd_opt_array[nd_opt->nd_opt_type] = nd_opt;
228 }
229 break;
230 case ND_OPT_PREFIX_INFO:
231 ndopts->nd_opts_pi_end = nd_opt;
Stephen Hemmingercfcabdc2007-10-09 01:59:42 -0700232 if (!ndopts->nd_opt_array[nd_opt->nd_opt_type])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700233 ndopts->nd_opt_array[nd_opt->nd_opt_type] = nd_opt;
234 break;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800235#ifdef CONFIG_IPV6_ROUTE_INFO
236 case ND_OPT_ROUTE_INFO:
237 ndopts->nd_opts_ri_end = nd_opt;
238 if (!ndopts->nd_opts_ri)
239 ndopts->nd_opts_ri = nd_opt;
240 break;
241#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700242 default:
Pierre Ynard31910572007-10-10 21:22:05 -0700243 if (ndisc_is_useropt(nd_opt)) {
244 ndopts->nd_useropts_end = nd_opt;
245 if (!ndopts->nd_useropts)
246 ndopts->nd_useropts = nd_opt;
247 } else {
248 /*
249 * Unknown options must be silently ignored,
250 * to accommodate future extension to the
251 * protocol.
252 */
Joe Perches675418d2012-05-16 19:28:38 +0000253 ND_PRINTK(2, notice,
254 "%s: ignored unsupported option; type=%d, len=%d\n",
255 __func__,
256 nd_opt->nd_opt_type,
257 nd_opt->nd_opt_len);
Pierre Ynard31910572007-10-10 21:22:05 -0700258 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700259 }
260 opt_len -= l;
261 nd_opt = ((void *)nd_opt) + l;
262 }
263 return ndopts;
264}
265
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000266int ndisc_mc_map(const struct in6_addr *addr, char *buf, struct net_device *dev, int dir)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700267{
268 switch (dev->type) {
269 case ARPHRD_ETHER:
270 case ARPHRD_IEEE802: /* Not sure. Check it later. --ANK */
271 case ARPHRD_FDDI:
272 ipv6_eth_mc_map(addr, buf);
273 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700274 case ARPHRD_ARCNET:
275 ipv6_arcnet_mc_map(addr, buf);
276 return 0;
277 case ARPHRD_INFINIBAND:
Rolf Manderscheida9e527e2007-12-10 13:38:41 -0700278 ipv6_ib_mc_map(addr, dev->broadcast, buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279 return 0;
Timo Teräs93ca3bb2011-03-28 22:40:53 +0000280 case ARPHRD_IPGRE:
281 return ipv6_ipgre_mc_map(addr, dev->broadcast, buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282 default:
283 if (dir) {
284 memcpy(buf, dev->broadcast, dev->addr_len);
285 return 0;
286 }
287 }
288 return -EINVAL;
289}
290
YOSHIFUJI Hideaki71590392007-02-22 22:05:40 +0900291EXPORT_SYMBOL(ndisc_mc_map);
292
Eric Dumazetd6bf7812010-10-04 06:15:44 +0000293static u32 ndisc_hash(const void *pkey,
294 const struct net_device *dev,
David S. Miller2c2aba62011-12-28 15:06:58 -0500295 __u32 *hash_rnd)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296{
David S. Miller2c2aba62011-12-28 15:06:58 -0500297 return ndisc_hashfn(pkey, dev, hash_rnd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298}
299
300static int ndisc_constructor(struct neighbour *neigh)
301{
302 struct in6_addr *addr = (struct in6_addr*)&neigh->primary_key;
303 struct net_device *dev = neigh->dev;
304 struct inet6_dev *in6_dev;
305 struct neigh_parms *parms;
Eric Dumazeta50feda2012-05-18 18:57:34 +0000306 bool is_multicast = ipv6_addr_is_multicast(addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700307
Linus Torvalds1da177e2005-04-16 15:20:36 -0700308 in6_dev = in6_dev_get(dev);
309 if (in6_dev == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700310 return -EINVAL;
311 }
312
313 parms = in6_dev->nd_parms;
314 __neigh_parms_put(neigh->parms);
315 neigh->parms = neigh_parms_clone(parms);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700316
317 neigh->type = is_multicast ? RTN_MULTICAST : RTN_UNICAST;
Stephen Hemminger3b04ddd2007-10-09 01:40:57 -0700318 if (!dev->header_ops) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700319 neigh->nud_state = NUD_NOARP;
320 neigh->ops = &ndisc_direct_ops;
David S. Miller8f40b162011-07-17 13:34:11 -0700321 neigh->output = neigh_direct_output;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322 } else {
323 if (is_multicast) {
324 neigh->nud_state = NUD_NOARP;
325 ndisc_mc_map(addr, neigh->ha, dev, 1);
326 } else if (dev->flags&(IFF_NOARP|IFF_LOOPBACK)) {
327 neigh->nud_state = NUD_NOARP;
328 memcpy(neigh->ha, dev->dev_addr, dev->addr_len);
329 if (dev->flags&IFF_LOOPBACK)
330 neigh->type = RTN_LOCAL;
331 } else if (dev->flags&IFF_POINTOPOINT) {
332 neigh->nud_state = NUD_NOARP;
333 memcpy(neigh->ha, dev->broadcast, dev->addr_len);
334 }
Stephen Hemminger3b04ddd2007-10-09 01:40:57 -0700335 if (dev->header_ops->cache)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336 neigh->ops = &ndisc_hh_ops;
337 else
338 neigh->ops = &ndisc_generic_ops;
339 if (neigh->nud_state&NUD_VALID)
340 neigh->output = neigh->ops->connected_output;
341 else
342 neigh->output = neigh->ops->output;
343 }
344 in6_dev_put(in6_dev);
345 return 0;
346}
347
348static int pndisc_constructor(struct pneigh_entry *n)
349{
350 struct in6_addr *addr = (struct in6_addr*)&n->key;
351 struct in6_addr maddr;
352 struct net_device *dev = n->dev;
353
354 if (dev == NULL || __in6_dev_get(dev) == NULL)
355 return -EINVAL;
356 addrconf_addr_solict_mult(addr, &maddr);
357 ipv6_dev_mc_inc(dev, &maddr);
358 return 0;
359}
360
361static void pndisc_destructor(struct pneigh_entry *n)
362{
363 struct in6_addr *addr = (struct in6_addr*)&n->key;
364 struct in6_addr maddr;
365 struct net_device *dev = n->dev;
366
367 if (dev == NULL || __in6_dev_get(dev) == NULL)
368 return;
369 addrconf_addr_solict_mult(addr, &maddr);
370 ipv6_dev_mc_dec(dev, &maddr);
371}
372
Brian Haley305d5522008-11-04 17:51:14 -0800373struct sk_buff *ndisc_build_skb(struct net_device *dev,
374 const struct in6_addr *daddr,
375 const struct in6_addr *saddr,
376 struct icmp6hdr *icmp6h,
377 const struct in6_addr *target,
378 int llinfo)
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900379{
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900380 struct net *net = dev_net(dev);
Daniel Lezcano1762f7e2008-03-07 11:15:34 -0800381 struct sock *sk = net->ipv6.ndisc_sk;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900382 struct sk_buff *skb;
383 struct icmp6hdr *hdr;
Herbert Xua7ae1992011-11-18 02:20:04 +0000384 int hlen = LL_RESERVED_SPACE(dev);
385 int tlen = dev->needed_tailroom;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900386 int len;
387 int err;
Brian Haley305d5522008-11-04 17:51:14 -0800388 u8 *opt;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900389
390 if (!dev->addr_len)
391 llinfo = 0;
392
393 len = sizeof(struct icmp6hdr) + (target ? sizeof(*target) : 0);
394 if (llinfo)
395 len += ndisc_opt_addr_space(dev);
396
397 skb = sock_alloc_send_skb(sk,
398 (MAX_HEADER + sizeof(struct ipv6hdr) +
Herbert Xua7ae1992011-11-18 02:20:04 +0000399 len + hlen + tlen),
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900400 1, &err);
401 if (!skb) {
Joe Perches675418d2012-05-16 19:28:38 +0000402 ND_PRINTK(0, err, "ND: %s failed to allocate an skb, err=%d\n",
403 __func__, err);
Brian Haley305d5522008-11-04 17:51:14 -0800404 return NULL;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900405 }
406
Herbert Xua7ae1992011-11-18 02:20:04 +0000407 skb_reserve(skb, hlen);
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900408 ip6_nd_hdr(sk, skb, dev, saddr, daddr, IPPROTO_ICMPV6, len);
409
410 skb->transport_header = skb->tail;
411 skb_put(skb, len);
412
413 hdr = (struct icmp6hdr *)skb_transport_header(skb);
414 memcpy(hdr, icmp6h, sizeof(*hdr));
415
416 opt = skb_transport_header(skb) + sizeof(struct icmp6hdr);
417 if (target) {
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +0000418 *(struct in6_addr *)opt = *target;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900419 opt += sizeof(*target);
420 }
421
422 if (llinfo)
423 ndisc_fill_addr_option(opt, llinfo, dev->dev_addr,
424 dev->addr_len, dev->type);
425
426 hdr->icmp6_cksum = csum_ipv6_magic(saddr, daddr, len,
427 IPPROTO_ICMPV6,
Joe Perches07f07572008-11-19 15:44:53 -0800428 csum_partial(hdr,
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900429 len, 0));
430
Brian Haley305d5522008-11-04 17:51:14 -0800431 return skb;
432}
433
434EXPORT_SYMBOL(ndisc_build_skb);
435
436void ndisc_send_skb(struct sk_buff *skb,
437 struct net_device *dev,
438 struct neighbour *neigh,
439 const struct in6_addr *daddr,
440 const struct in6_addr *saddr,
441 struct icmp6hdr *icmp6h)
442{
David S. Miller4c9483b2011-03-12 16:22:43 -0500443 struct flowi6 fl6;
Brian Haley305d5522008-11-04 17:51:14 -0800444 struct dst_entry *dst;
445 struct net *net = dev_net(dev);
446 struct sock *sk = net->ipv6.ndisc_sk;
447 struct inet6_dev *idev;
448 int err;
449 u8 type;
450
451 type = icmp6h->icmp6_type;
452
David S. Miller4c9483b2011-03-12 16:22:43 -0500453 icmpv6_flow_init(sk, &fl6, type, saddr, daddr, dev->ifindex);
David S. Miller87a11572011-12-06 17:04:13 -0500454 dst = icmp6_dst_alloc(dev, neigh, &fl6);
David S. Miller452edd52011-03-02 13:27:41 -0800455 if (IS_ERR(dst)) {
Brian Haley305d5522008-11-04 17:51:14 -0800456 kfree_skb(skb);
457 return;
458 }
459
Eric Dumazetadf30902009-06-02 05:19:30 +0000460 skb_dst_set(skb, dst);
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900461
Eric Dumazetcfdf7642011-07-27 21:13:03 +0000462 rcu_read_lock();
463 idev = __in6_dev_get(dst->dev);
Neil Hormanedf391f2009-04-27 02:45:02 -0700464 IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUT, skb->len);
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900465
Jan Engelhardtb2e0b382010-03-23 04:09:07 +0100466 err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, skb, NULL, dst->dev,
Patrick McHardy6e23ae22007-11-19 18:53:30 -0800467 dst_output);
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900468 if (!err) {
Denis V. Lunev5c5d2442008-10-08 10:33:50 -0700469 ICMP6MSGOUT_INC_STATS(net, idev, type);
Denis V. Luneva862f6a2008-10-08 10:33:06 -0700470 ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS);
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900471 }
472
Eric Dumazetcfdf7642011-07-27 21:13:03 +0000473 rcu_read_unlock();
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900474}
475
Brian Haley305d5522008-11-04 17:51:14 -0800476EXPORT_SYMBOL(ndisc_send_skb);
477
478/*
479 * Send a Neighbour Discover packet
480 */
481static void __ndisc_send(struct net_device *dev,
482 struct neighbour *neigh,
483 const struct in6_addr *daddr,
484 const struct in6_addr *saddr,
485 struct icmp6hdr *icmp6h, const struct in6_addr *target,
486 int llinfo)
487{
488 struct sk_buff *skb;
489
490 skb = ndisc_build_skb(dev, daddr, saddr, icmp6h, target, llinfo);
491 if (!skb)
492 return;
493
494 ndisc_send_skb(skb, dev, neigh, daddr, saddr, icmp6h);
495}
496
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +0900498 const struct in6_addr *daddr,
499 const struct in6_addr *solicited_addr,
500 int router, int solicited, int override, int inc_opt)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501{
502 struct in6_addr tmpaddr;
503 struct inet6_ifaddr *ifp;
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +0900504 const struct in6_addr *src_addr;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900505 struct icmp6hdr icmp6h = {
506 .icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT,
507 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700508
509 /* for anycast or proxy, solicited_addr != src_addr */
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900510 ifp = ipv6_get_ifaddr(dev_net(dev), solicited_addr, dev, 1);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900511 if (ifp) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700512 src_addr = solicited_addr;
Neil Horman95c385b2007-04-25 17:08:10 -0700513 if (ifp->flags & IFA_F_OPTIMISTIC)
514 override = 0;
stephen hemminger9f888162010-06-21 11:00:13 +0000515 inc_opt |= ifp->idev->cnf.force_tllao;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700516 in6_ifa_put(ifp);
517 } else {
Brian Haley191cd582008-08-14 15:33:21 -0700518 if (ipv6_dev_get_saddr(dev_net(dev), dev, daddr,
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900519 inet6_sk(dev_net(dev)->ipv6.ndisc_sk)->srcprefs,
YOSHIFUJI Hideaki7cbca672008-03-25 09:37:42 +0900520 &tmpaddr))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700521 return;
522 src_addr = &tmpaddr;
523 }
524
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900525 icmp6h.icmp6_router = router;
526 icmp6h.icmp6_solicited = solicited;
527 icmp6h.icmp6_override = override;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700528
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900529 __ndisc_send(dev, neigh, daddr, src_addr,
530 &icmp6h, solicited_addr,
David L Stevens14878f72007-09-16 16:52:35 -0700531 inc_opt ? ND_OPT_TARGET_LL_ADDR : 0);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900532}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700533
Ben Hutchingsf47b9462011-04-15 13:46:02 +0000534static void ndisc_send_unsol_na(struct net_device *dev)
535{
536 struct inet6_dev *idev;
537 struct inet6_ifaddr *ifa;
538 struct in6_addr mcaddr;
539
540 idev = in6_dev_get(dev);
541 if (!idev)
542 return;
543
544 read_lock_bh(&idev->lock);
545 list_for_each_entry(ifa, &idev->addr_list, if_list) {
546 addrconf_addr_solict_mult(&ifa->addr, &mcaddr);
547 ndisc_send_na(dev, NULL, &mcaddr, &ifa->addr,
548 /*router=*/ !!idev->cnf.forwarding,
549 /*solicited=*/ false, /*override=*/ true,
550 /*inc_opt=*/ true);
551 }
552 read_unlock_bh(&idev->lock);
553
554 in6_dev_put(idev);
555}
556
Linus Torvalds1da177e2005-04-16 15:20:36 -0700557void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +0900558 const struct in6_addr *solicit,
559 const struct in6_addr *daddr, const struct in6_addr *saddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700560{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700561 struct in6_addr addr_buf;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900562 struct icmp6hdr icmp6h = {
563 .icmp6_type = NDISC_NEIGHBOUR_SOLICITATION,
564 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565
566 if (saddr == NULL) {
Neil Horman95c385b2007-04-25 17:08:10 -0700567 if (ipv6_get_lladdr(dev, &addr_buf,
568 (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569 return;
570 saddr = &addr_buf;
571 }
572
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900573 __ndisc_send(dev, neigh, daddr, saddr,
574 &icmp6h, solicit,
David L Stevens14878f72007-09-16 16:52:35 -0700575 !ipv6_addr_any(saddr) ? ND_OPT_SOURCE_LL_ADDR : 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700576}
577
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +0900578void ndisc_send_rs(struct net_device *dev, const struct in6_addr *saddr,
579 const struct in6_addr *daddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700580{
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900581 struct icmp6hdr icmp6h = {
582 .icmp6_type = NDISC_ROUTER_SOLICITATION,
583 };
Neil Horman95c385b2007-04-25 17:08:10 -0700584 int send_sllao = dev->addr_len;
Neil Horman95c385b2007-04-25 17:08:10 -0700585
586#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
587 /*
588 * According to section 2.2 of RFC 4429, we must not
589 * send router solicitations with a sllao from
590 * optimistic addresses, but we may send the solicitation
591 * if we don't include the sllao. So here we check
592 * if our address is optimistic, and if so, we
Joe Perchesbea85192007-12-20 14:01:35 -0800593 * suppress the inclusion of the sllao.
Neil Horman95c385b2007-04-25 17:08:10 -0700594 */
595 if (send_sllao) {
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900596 struct inet6_ifaddr *ifp = ipv6_get_ifaddr(dev_net(dev), saddr,
Daniel Lezcano1cab3da2008-01-10 22:44:09 -0800597 dev, 1);
Neil Horman95c385b2007-04-25 17:08:10 -0700598 if (ifp) {
599 if (ifp->flags & IFA_F_OPTIMISTIC) {
YOSHIFUJI Hideakica043562007-02-28 23:13:20 +0900600 send_sllao = 0;
Neil Horman95c385b2007-04-25 17:08:10 -0700601 }
YOSHIFUJI Hideakica043562007-02-28 23:13:20 +0900602 in6_ifa_put(ifp);
Neil Horman95c385b2007-04-25 17:08:10 -0700603 } else {
604 send_sllao = 0;
605 }
606 }
607#endif
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900608 __ndisc_send(dev, NULL, daddr, saddr,
609 &icmp6h, NULL,
David L Stevens14878f72007-09-16 16:52:35 -0700610 send_sllao ? ND_OPT_SOURCE_LL_ADDR : 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700611}
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900612
Linus Torvalds1da177e2005-04-16 15:20:36 -0700613
614static void ndisc_error_report(struct neighbour *neigh, struct sk_buff *skb)
615{
616 /*
617 * "The sender MUST return an ICMP
618 * destination unreachable"
619 */
620 dst_link_failure(skb);
621 kfree_skb(skb);
622}
623
624/* Called with locked neigh: either read or both */
625
626static void ndisc_solicit(struct neighbour *neigh, struct sk_buff *skb)
627{
628 struct in6_addr *saddr = NULL;
629 struct in6_addr mcaddr;
630 struct net_device *dev = neigh->dev;
631 struct in6_addr *target = (struct in6_addr *)&neigh->primary_key;
632 int probes = atomic_read(&neigh->probes);
633
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900634 if (skb && ipv6_chk_addr(dev_net(dev), &ipv6_hdr(skb)->saddr, dev, 1))
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -0700635 saddr = &ipv6_hdr(skb)->saddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700636
637 if ((probes -= neigh->parms->ucast_probes) < 0) {
638 if (!(neigh->nud_state & NUD_VALID)) {
Joe Perches675418d2012-05-16 19:28:38 +0000639 ND_PRINTK(1, dbg,
640 "%s: trying to ucast probe in NUD_INVALID: %pI6\n",
641 __func__, target);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700642 }
643 ndisc_send_ns(dev, neigh, target, target, saddr);
644 } else if ((probes -= neigh->parms->app_probes) < 0) {
645#ifdef CONFIG_ARPD
646 neigh_app_ns(neigh);
647#endif
648 } else {
649 addrconf_addr_solict_mult(target, &mcaddr);
650 ndisc_send_ns(dev, NULL, target, &mcaddr, saddr);
651 }
652}
653
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900654static int pndisc_is_router(const void *pkey,
655 struct net_device *dev)
Pavel Emelyanovfa86d322008-03-24 14:48:59 -0700656{
657 struct pneigh_entry *n;
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900658 int ret = -1;
Pavel Emelyanovfa86d322008-03-24 14:48:59 -0700659
660 read_lock_bh(&nd_tbl.lock);
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900661 n = __pneigh_lookup(&nd_tbl, dev_net(dev), pkey, dev);
662 if (n)
663 ret = !!(n->flags & NTF_ROUTER);
Pavel Emelyanovfa86d322008-03-24 14:48:59 -0700664 read_unlock_bh(&nd_tbl.lock);
665
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900666 return ret;
Pavel Emelyanovfa86d322008-03-24 14:48:59 -0700667}
668
Linus Torvalds1da177e2005-04-16 15:20:36 -0700669static void ndisc_recv_ns(struct sk_buff *skb)
670{
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -0700671 struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb);
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000672 const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
673 const struct in6_addr *daddr = &ipv6_hdr(skb)->daddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700674 u8 *lladdr = NULL;
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -0700675 u32 ndoptlen = skb->tail - (skb->transport_header +
676 offsetof(struct nd_msg, opt));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700677 struct ndisc_options ndopts;
678 struct net_device *dev = skb->dev;
679 struct inet6_ifaddr *ifp;
680 struct inet6_dev *idev = NULL;
681 struct neighbour *neigh;
682 int dad = ipv6_addr_any(saddr);
Eric Dumazeta50feda2012-05-18 18:57:34 +0000683 bool inc;
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900684 int is_router = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700685
686 if (ipv6_addr_is_multicast(&msg->target)) {
Joe Perches675418d2012-05-16 19:28:38 +0000687 ND_PRINTK(2, warn, "NS: multicast target address\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700688 return;
689 }
690
691 /*
692 * RFC2461 7.1.1:
693 * DAD has to be destined for solicited node multicast address.
694 */
695 if (dad &&
696 !(daddr->s6_addr32[0] == htonl(0xff020000) &&
697 daddr->s6_addr32[1] == htonl(0x00000000) &&
698 daddr->s6_addr32[2] == htonl(0x00000001) &&
699 daddr->s6_addr [12] == 0xff )) {
Joe Perches675418d2012-05-16 19:28:38 +0000700 ND_PRINTK(2, warn, "NS: bad DAD packet (wrong destination)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700701 return;
702 }
703
704 if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts)) {
Joe Perches675418d2012-05-16 19:28:38 +0000705 ND_PRINTK(2, warn, "NS: invalid ND options\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700706 return;
707 }
708
709 if (ndopts.nd_opts_src_lladdr) {
710 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr, dev);
711 if (!lladdr) {
Joe Perches675418d2012-05-16 19:28:38 +0000712 ND_PRINTK(2, warn,
713 "NS: invalid link-layer address length\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700714 return;
715 }
716
717 /* RFC2461 7.1.1:
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900718 * If the IP source address is the unspecified address,
719 * there MUST NOT be source link-layer address option
Linus Torvalds1da177e2005-04-16 15:20:36 -0700720 * in the message.
721 */
722 if (dad) {
Joe Perches675418d2012-05-16 19:28:38 +0000723 ND_PRINTK(2, warn,
724 "NS: bad DAD packet (link-layer address option)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700725 return;
726 }
727 }
728
729 inc = ipv6_addr_is_multicast(daddr);
730
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900731 ifp = ipv6_get_ifaddr(dev_net(dev), &msg->target, dev, 1);
Daniel Lezcanoa18bc692008-03-07 11:14:49 -0800732 if (ifp) {
Neil Horman95c385b2007-04-25 17:08:10 -0700733
734 if (ifp->flags & (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)) {
735 if (dad) {
Neil Horman95c385b2007-04-25 17:08:10 -0700736 /*
737 * We are colliding with another node
738 * who is doing DAD
739 * so fail our DAD process
740 */
741 addrconf_dad_failure(ifp);
Denis V. Lunev9e3be4b2007-09-11 11:04:49 +0200742 return;
Neil Horman95c385b2007-04-25 17:08:10 -0700743 } else {
744 /*
745 * This is not a dad solicitation.
746 * If we are an optimistic node,
747 * we should respond.
748 * Otherwise, we should ignore it.
749 */
750 if (!(ifp->flags & IFA_F_OPTIMISTIC))
751 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700752 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700753 }
754
755 idev = ifp->idev;
756 } else {
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700757 struct net *net = dev_net(dev);
758
Linus Torvalds1da177e2005-04-16 15:20:36 -0700759 idev = in6_dev_get(dev);
760 if (!idev) {
761 /* XXX: count this drop? */
762 return;
763 }
764
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700765 if (ipv6_chk_acast_addr(net, dev, &msg->target) ||
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900766 (idev->cnf.forwarding &&
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700767 (net->ipv6.devconf_all->proxy_ndp || idev->cnf.proxy_ndp) &&
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900768 (is_router = pndisc_is_router(&msg->target, dev)) >= 0)) {
Patrick McHardya61bbcf2005-08-14 17:24:31 -0700769 if (!(NEIGH_CB(skb)->flags & LOCALLY_ENQUEUED) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700770 skb->pkt_type != PACKET_HOST &&
771 inc != 0 &&
772 idev->nd_parms->proxy_delay != 0) {
773 /*
774 * for anycast or proxy,
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900775 * sender should delay its response
776 * by a random time between 0 and
Linus Torvalds1da177e2005-04-16 15:20:36 -0700777 * MAX_ANYCAST_DELAY_TIME seconds.
778 * (RFC2461) -- yoshfuji
779 */
780 struct sk_buff *n = skb_clone(skb, GFP_ATOMIC);
781 if (n)
782 pneigh_enqueue(&nd_tbl, idev->nd_parms, n);
783 goto out;
784 }
785 } else
786 goto out;
787 }
788
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900789 if (is_router < 0)
790 is_router = !!idev->cnf.forwarding;
Ville Nuorvala62dd9312006-09-22 14:43:19 -0700791
Linus Torvalds1da177e2005-04-16 15:20:36 -0700792 if (dad) {
YOSHIFUJI Hideakif3ee4012008-04-10 15:42:11 +0900793 ndisc_send_na(dev, NULL, &in6addr_linklocal_allnodes, &msg->target,
Ville Nuorvala62dd9312006-09-22 14:43:19 -0700794 is_router, 0, (ifp != NULL), 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700795 goto out;
796 }
797
798 if (inc)
799 NEIGH_CACHE_STAT_INC(&nd_tbl, rcv_probes_mcast);
800 else
801 NEIGH_CACHE_STAT_INC(&nd_tbl, rcv_probes_ucast);
802
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900803 /*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700804 * update / create cache entry
805 * for the source address
806 */
807 neigh = __neigh_lookup(&nd_tbl, saddr, dev,
808 !inc || lladdr || !dev->addr_len);
809 if (neigh)
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900810 neigh_update(neigh, lladdr, NUD_STALE,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700811 NEIGH_UPDATE_F_WEAK_OVERRIDE|
812 NEIGH_UPDATE_F_OVERRIDE);
Stephen Hemminger3b04ddd2007-10-09 01:40:57 -0700813 if (neigh || !dev->header_ops) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700814 ndisc_send_na(dev, neigh, saddr, &msg->target,
Ville Nuorvala62dd9312006-09-22 14:43:19 -0700815 is_router,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816 1, (ifp != NULL && inc), inc);
817 if (neigh)
818 neigh_release(neigh);
819 }
820
821out:
822 if (ifp)
823 in6_ifa_put(ifp);
824 else
825 in6_dev_put(idev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700826}
827
828static void ndisc_recv_na(struct sk_buff *skb)
829{
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -0700830 struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb);
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000831 const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
832 const struct in6_addr *daddr = &ipv6_hdr(skb)->daddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700833 u8 *lladdr = NULL;
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -0700834 u32 ndoptlen = skb->tail - (skb->transport_header +
835 offsetof(struct nd_msg, opt));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700836 struct ndisc_options ndopts;
837 struct net_device *dev = skb->dev;
838 struct inet6_ifaddr *ifp;
839 struct neighbour *neigh;
840
841 if (skb->len < sizeof(struct nd_msg)) {
Joe Perches675418d2012-05-16 19:28:38 +0000842 ND_PRINTK(2, warn, "NA: packet too short\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700843 return;
844 }
845
846 if (ipv6_addr_is_multicast(&msg->target)) {
Joe Perches675418d2012-05-16 19:28:38 +0000847 ND_PRINTK(2, warn, "NA: target address is multicast\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700848 return;
849 }
850
851 if (ipv6_addr_is_multicast(daddr) &&
852 msg->icmph.icmp6_solicited) {
Joe Perches675418d2012-05-16 19:28:38 +0000853 ND_PRINTK(2, warn, "NA: solicited NA is multicasted\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700854 return;
855 }
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900856
Linus Torvalds1da177e2005-04-16 15:20:36 -0700857 if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts)) {
Joe Perches675418d2012-05-16 19:28:38 +0000858 ND_PRINTK(2, warn, "NS: invalid ND option\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700859 return;
860 }
861 if (ndopts.nd_opts_tgt_lladdr) {
862 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr, dev);
863 if (!lladdr) {
Joe Perches675418d2012-05-16 19:28:38 +0000864 ND_PRINTK(2, warn,
865 "NA: invalid link-layer address length\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700866 return;
867 }
868 }
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900869 ifp = ipv6_get_ifaddr(dev_net(dev), &msg->target, dev, 1);
Daniel Lezcanoa18bc692008-03-07 11:14:49 -0800870 if (ifp) {
Daniel Walterbd015922011-04-13 21:09:25 +0000871 if (skb->pkt_type != PACKET_LOOPBACK
872 && (ifp->flags & IFA_F_TENTATIVE)) {
873 addrconf_dad_failure(ifp);
874 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700875 }
876 /* What should we make now? The advertisement
877 is invalid, but ndisc specs say nothing
878 about it. It could be misconfiguration, or
879 an smart proxy agent tries to help us :-)
Jan Sembera24fc7b82008-12-09 15:48:32 -0800880
881 We should not print the error if NA has been
882 received from loopback - it is just our own
883 unsolicited advertisement.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700884 */
Jan Sembera24fc7b82008-12-09 15:48:32 -0800885 if (skb->pkt_type != PACKET_LOOPBACK)
Joe Perches675418d2012-05-16 19:28:38 +0000886 ND_PRINTK(1, warn,
887 "NA: someone advertises our address %pI6 on %s!\n",
888 &ifp->addr, ifp->idev->dev->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700889 in6_ifa_put(ifp);
890 return;
891 }
892 neigh = neigh_lookup(&nd_tbl, &msg->target, dev);
893
894 if (neigh) {
895 u8 old_flags = neigh->flags;
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700896 struct net *net = dev_net(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700897
898 if (neigh->nud_state & NUD_FAILED)
899 goto out;
900
Ville Nuorvala5f3e6e92006-09-22 14:42:46 -0700901 /*
902 * Don't update the neighbor cache entry on a proxy NA from
903 * ourselves because either the proxied node is off link or it
904 * has already sent a NA to us.
905 */
906 if (lladdr && !memcmp(lladdr, dev->dev_addr, dev->addr_len) &&
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700907 net->ipv6.devconf_all->forwarding && net->ipv6.devconf_all->proxy_ndp &&
908 pneigh_lookup(&nd_tbl, net, &msg->target, dev, 0)) {
YOSHIFUJI Hideakifbea49e2006-09-22 14:43:49 -0700909 /* XXX: idev->cnf.prixy_ndp */
Ville Nuorvala5f3e6e92006-09-22 14:42:46 -0700910 goto out;
YOSHIFUJI Hideakifbea49e2006-09-22 14:43:49 -0700911 }
Ville Nuorvala5f3e6e92006-09-22 14:42:46 -0700912
Linus Torvalds1da177e2005-04-16 15:20:36 -0700913 neigh_update(neigh, lladdr,
914 msg->icmph.icmp6_solicited ? NUD_REACHABLE : NUD_STALE,
915 NEIGH_UPDATE_F_WEAK_OVERRIDE|
916 (msg->icmph.icmp6_override ? NEIGH_UPDATE_F_OVERRIDE : 0)|
917 NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
918 (msg->icmph.icmp6_router ? NEIGH_UPDATE_F_ISROUTER : 0));
919
920 if ((old_flags & ~neigh->flags) & NTF_ROUTER) {
921 /*
922 * Change: router to host
923 */
924 struct rt6_info *rt;
925 rt = rt6_get_dflt_router(saddr, dev);
926 if (rt)
Thomas Grafe0a1ad732006-08-22 00:00:21 -0700927 ip6_del_rt(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700928 }
929
930out:
931 neigh_release(neigh);
932 }
933}
934
935static void ndisc_recv_rs(struct sk_buff *skb)
936{
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -0700937 struct rs_msg *rs_msg = (struct rs_msg *)skb_transport_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700938 unsigned long ndoptlen = skb->len - sizeof(*rs_msg);
939 struct neighbour *neigh;
940 struct inet6_dev *idev;
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000941 const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700942 struct ndisc_options ndopts;
943 u8 *lladdr = NULL;
944
945 if (skb->len < sizeof(*rs_msg))
946 return;
947
Eric Dumazetcfdf7642011-07-27 21:13:03 +0000948 idev = __in6_dev_get(skb->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700949 if (!idev) {
Joe Perches675418d2012-05-16 19:28:38 +0000950 ND_PRINTK(1, err, "RS: can't find in6 device\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700951 return;
952 }
953
954 /* Don't accept RS if we're not in router mode */
955 if (!idev->cnf.forwarding)
956 goto out;
957
958 /*
959 * Don't update NCE if src = ::;
960 * this implies that the source node has no ip address assigned yet.
961 */
962 if (ipv6_addr_any(saddr))
963 goto out;
964
965 /* Parse ND options */
966 if (!ndisc_parse_options(rs_msg->opt, ndoptlen, &ndopts)) {
Joe Perches675418d2012-05-16 19:28:38 +0000967 ND_PRINTK(2, notice, "NS: invalid ND option, ignored\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700968 goto out;
969 }
970
971 if (ndopts.nd_opts_src_lladdr) {
972 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr,
973 skb->dev);
974 if (!lladdr)
975 goto out;
976 }
977
978 neigh = __neigh_lookup(&nd_tbl, saddr, skb->dev, 1);
979 if (neigh) {
980 neigh_update(neigh, lladdr, NUD_STALE,
981 NEIGH_UPDATE_F_WEAK_OVERRIDE|
982 NEIGH_UPDATE_F_OVERRIDE|
983 NEIGH_UPDATE_F_OVERRIDE_ISROUTER);
984 neigh_release(neigh);
985 }
986out:
Eric Dumazetcfdf7642011-07-27 21:13:03 +0000987 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700988}
989
Pierre Ynard31910572007-10-10 21:22:05 -0700990static void ndisc_ra_useropt(struct sk_buff *ra, struct nd_opt_hdr *opt)
991{
992 struct icmp6hdr *icmp6h = (struct icmp6hdr *)skb_transport_header(ra);
993 struct sk_buff *skb;
994 struct nlmsghdr *nlh;
995 struct nduseroptmsg *ndmsg;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900996 struct net *net = dev_net(ra->dev);
Pierre Ynard31910572007-10-10 21:22:05 -0700997 int err;
998 int base_size = NLMSG_ALIGN(sizeof(struct nduseroptmsg)
999 + (opt->nd_opt_len << 3));
1000 size_t msg_size = base_size + nla_total_size(sizeof(struct in6_addr));
1001
1002 skb = nlmsg_new(msg_size, GFP_ATOMIC);
1003 if (skb == NULL) {
1004 err = -ENOBUFS;
1005 goto errout;
1006 }
1007
1008 nlh = nlmsg_put(skb, 0, 0, RTM_NEWNDUSEROPT, base_size, 0);
1009 if (nlh == NULL) {
1010 goto nla_put_failure;
1011 }
1012
1013 ndmsg = nlmsg_data(nlh);
1014 ndmsg->nduseropt_family = AF_INET6;
Pierre Ynarddbb2ed22007-11-12 17:58:35 -08001015 ndmsg->nduseropt_ifindex = ra->dev->ifindex;
Pierre Ynard31910572007-10-10 21:22:05 -07001016 ndmsg->nduseropt_icmp_type = icmp6h->icmp6_type;
1017 ndmsg->nduseropt_icmp_code = icmp6h->icmp6_code;
1018 ndmsg->nduseropt_opts_len = opt->nd_opt_len << 3;
1019
1020 memcpy(ndmsg + 1, opt, opt->nd_opt_len << 3);
1021
David S. Millerc78679e2012-04-01 20:27:33 -04001022 if (nla_put(skb, NDUSEROPT_SRCADDR, sizeof(struct in6_addr),
1023 &ipv6_hdr(ra)->saddr))
1024 goto nla_put_failure;
Pierre Ynard31910572007-10-10 21:22:05 -07001025 nlmsg_end(skb, nlh);
1026
Pablo Neira Ayuso1ce85fe2009-02-24 23:18:28 -08001027 rtnl_notify(skb, net, 0, RTNLGRP_ND_USEROPT, NULL, GFP_ATOMIC);
Pierre Ynard31910572007-10-10 21:22:05 -07001028 return;
1029
1030nla_put_failure:
1031 nlmsg_free(skb);
1032 err = -EMSGSIZE;
1033errout:
Daniel Lezcanoa18bc692008-03-07 11:14:49 -08001034 rtnl_set_sk_err(net, RTNLGRP_ND_USEROPT, err);
Pierre Ynard31910572007-10-10 21:22:05 -07001035}
1036
Thomas Graf65e9b622010-09-03 02:59:14 +00001037static inline int accept_ra(struct inet6_dev *in6_dev)
1038{
1039 /*
1040 * If forwarding is enabled, RA are not accepted unless the special
1041 * hybrid mode (accept_ra=2) is enabled.
1042 */
1043 if (in6_dev->cnf.forwarding && in6_dev->cnf.accept_ra < 2)
1044 return 0;
1045
1046 return in6_dev->cnf.accept_ra;
1047}
1048
Linus Torvalds1da177e2005-04-16 15:20:36 -07001049static void ndisc_router_discovery(struct sk_buff *skb)
1050{
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -07001051 struct ra_msg *ra_msg = (struct ra_msg *)skb_transport_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001052 struct neighbour *neigh = NULL;
1053 struct inet6_dev *in6_dev;
YOSHIFUJI Hideaki65f5c7c2006-03-20 16:55:08 -08001054 struct rt6_info *rt = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001055 int lifetime;
1056 struct ndisc_options ndopts;
1057 int optlen;
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001058 unsigned int pref = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001059
1060 __u8 * opt = (__u8 *)(ra_msg + 1);
1061
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -07001062 optlen = (skb->tail - skb->transport_header) - sizeof(struct ra_msg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001063
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001064 if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) {
Joe Perches675418d2012-05-16 19:28:38 +00001065 ND_PRINTK(2, warn, "RA: source address is not link-local\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001066 return;
1067 }
1068 if (optlen < 0) {
Joe Perches675418d2012-05-16 19:28:38 +00001069 ND_PRINTK(2, warn, "RA: packet too short\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001070 return;
1071 }
1072
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001073#ifdef CONFIG_IPV6_NDISC_NODETYPE
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001074 if (skb->ndisc_nodetype == NDISC_NODETYPE_HOST) {
Joe Perches675418d2012-05-16 19:28:38 +00001075 ND_PRINTK(2, warn, "RA: from host or unauthorized router\n");
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001076 return;
1077 }
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001078#endif
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001079
Linus Torvalds1da177e2005-04-16 15:20:36 -07001080 /*
1081 * set the RA_RECV flag in the interface
1082 */
1083
Eric Dumazetcfdf7642011-07-27 21:13:03 +00001084 in6_dev = __in6_dev_get(skb->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001085 if (in6_dev == NULL) {
Joe Perches675418d2012-05-16 19:28:38 +00001086 ND_PRINTK(0, err, "RA: can't find inet6 device for %s\n",
1087 skb->dev->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001088 return;
1089 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001090
1091 if (!ndisc_parse_options(opt, optlen, &ndopts)) {
Joe Perches675418d2012-05-16 19:28:38 +00001092 ND_PRINTK(2, warn, "RA: invalid ND options\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001093 return;
1094 }
1095
Thomas Graf65e9b622010-09-03 02:59:14 +00001096 if (!accept_ra(in6_dev))
David Ward31ce8c72009-08-29 00:04:09 -07001097 goto skip_linkparms;
1098
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001099#ifdef CONFIG_IPV6_NDISC_NODETYPE
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001100 /* skip link-specific parameters from interior routers */
1101 if (skb->ndisc_nodetype == NDISC_NODETYPE_NODEFAULT)
1102 goto skip_linkparms;
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001103#endif
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001104
Linus Torvalds1da177e2005-04-16 15:20:36 -07001105 if (in6_dev->if_flags & IF_RS_SENT) {
1106 /*
1107 * flag that an RA was received after an RS was sent
1108 * out on this interface.
1109 */
1110 in6_dev->if_flags |= IF_RA_RCVD;
1111 }
1112
1113 /*
1114 * Remember the managed/otherconf flags from most recently
1115 * received RA message (RFC 2462) -- yoshfuji
1116 */
1117 in6_dev->if_flags = (in6_dev->if_flags & ~(IF_RA_MANAGED |
1118 IF_RA_OTHERCONF)) |
1119 (ra_msg->icmph.icmp6_addrconf_managed ?
1120 IF_RA_MANAGED : 0) |
1121 (ra_msg->icmph.icmp6_addrconf_other ?
1122 IF_RA_OTHERCONF : 0);
1123
YOSHIFUJI Hideaki65f5c7c2006-03-20 16:55:08 -08001124 if (!in6_dev->cnf.accept_ra_defrtr)
1125 goto skip_defrtr;
1126
Andreas Hofmeister9f562202011-10-24 19:13:15 -04001127 if (ipv6_chk_addr(dev_net(in6_dev->dev), &ipv6_hdr(skb)->saddr, NULL, 0))
1128 goto skip_defrtr;
1129
Linus Torvalds1da177e2005-04-16 15:20:36 -07001130 lifetime = ntohs(ra_msg->icmph.icmp6_rt_lifetime);
1131
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001132#ifdef CONFIG_IPV6_ROUTER_PREF
1133 pref = ra_msg->icmph.icmp6_router_pref;
1134 /* 10b is handled as if it were 00b (medium) */
YOSHIFUJI Hideaki930d6ff2006-03-20 17:05:30 -08001135 if (pref == ICMPV6_ROUTER_PREF_INVALID ||
YOSHIFUJI Hideaki6d5b78c2007-06-22 16:07:04 -07001136 !in6_dev->cnf.accept_ra_rtr_pref)
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001137 pref = ICMPV6_ROUTER_PREF_MEDIUM;
1138#endif
1139
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001140 rt = rt6_get_dflt_router(&ipv6_hdr(skb)->saddr, skb->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001141
David S. Millereb857182012-01-27 15:07:56 -08001142 if (rt) {
1143 neigh = dst_neigh_lookup(&rt->dst, &ipv6_hdr(skb)->saddr);
1144 if (!neigh) {
Joe Perches675418d2012-05-16 19:28:38 +00001145 ND_PRINTK(0, err,
1146 "RA: %s got default router without neighbour\n",
1147 __func__);
David S. Millereb857182012-01-27 15:07:56 -08001148 dst_release(&rt->dst);
1149 return;
1150 }
1151 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001152 if (rt && lifetime == 0) {
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001153 ip6_del_rt(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001154 rt = NULL;
1155 }
1156
1157 if (rt == NULL && lifetime) {
Joe Perches675418d2012-05-16 19:28:38 +00001158 ND_PRINTK(3, dbg, "RA: adding default router\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001159
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001160 rt = rt6_add_dflt_router(&ipv6_hdr(skb)->saddr, skb->dev, pref);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001161 if (rt == NULL) {
Joe Perches675418d2012-05-16 19:28:38 +00001162 ND_PRINTK(0, err,
1163 "RA: %s failed to add default route\n",
1164 __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001165 return;
1166 }
1167
David S. Millereb857182012-01-27 15:07:56 -08001168 neigh = dst_neigh_lookup(&rt->dst, &ipv6_hdr(skb)->saddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001169 if (neigh == NULL) {
Joe Perches675418d2012-05-16 19:28:38 +00001170 ND_PRINTK(0, err,
1171 "RA: %s got default router without neighbour\n",
1172 __func__);
Changli Gaod8d1f302010-06-10 23:31:35 -07001173 dst_release(&rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001174 return;
1175 }
1176 neigh->flags |= NTF_ROUTER;
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001177 } else if (rt) {
Pedro Ribeiro22441cf2008-10-15 15:47:49 -07001178 rt->rt6i_flags = (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001179 }
1180
1181 if (rt)
Gao feng1716a962012-04-06 00:13:10 +00001182 rt6_set_expires(rt, jiffies + (HZ * lifetime));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001183 if (ra_msg->icmph.icmp6_hop_limit) {
1184 in6_dev->cnf.hop_limit = ra_msg->icmph.icmp6_hop_limit;
1185 if (rt)
David S. Millerdefb3512010-12-08 21:16:57 -08001186 dst_metric_set(&rt->dst, RTAX_HOPLIMIT,
1187 ra_msg->icmph.icmp6_hop_limit);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001188 }
1189
YOSHIFUJI Hideaki65f5c7c2006-03-20 16:55:08 -08001190skip_defrtr:
1191
Linus Torvalds1da177e2005-04-16 15:20:36 -07001192 /*
1193 * Update Reachable Time and Retrans Timer
1194 */
1195
1196 if (in6_dev->nd_parms) {
1197 unsigned long rtime = ntohl(ra_msg->retrans_timer);
1198
1199 if (rtime && rtime/1000 < MAX_SCHEDULE_TIMEOUT/HZ) {
1200 rtime = (rtime*HZ)/1000;
1201 if (rtime < HZ/10)
1202 rtime = HZ/10;
1203 in6_dev->nd_parms->retrans_time = rtime;
1204 in6_dev->tstamp = jiffies;
1205 inet6_ifinfo_notify(RTM_NEWLINK, in6_dev);
1206 }
1207
1208 rtime = ntohl(ra_msg->reachable_time);
1209 if (rtime && rtime/1000 < MAX_SCHEDULE_TIMEOUT/(3*HZ)) {
1210 rtime = (rtime*HZ)/1000;
1211
1212 if (rtime < HZ/10)
1213 rtime = HZ/10;
1214
1215 if (rtime != in6_dev->nd_parms->base_reachable_time) {
1216 in6_dev->nd_parms->base_reachable_time = rtime;
1217 in6_dev->nd_parms->gc_staletime = 3 * rtime;
1218 in6_dev->nd_parms->reachable_time = neigh_rand_reach_time(rtime);
1219 in6_dev->tstamp = jiffies;
1220 inet6_ifinfo_notify(RTM_NEWLINK, in6_dev);
1221 }
1222 }
1223 }
1224
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001225skip_linkparms:
1226
Linus Torvalds1da177e2005-04-16 15:20:36 -07001227 /*
1228 * Process options.
1229 */
1230
1231 if (!neigh)
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001232 neigh = __neigh_lookup(&nd_tbl, &ipv6_hdr(skb)->saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001233 skb->dev, 1);
1234 if (neigh) {
1235 u8 *lladdr = NULL;
1236 if (ndopts.nd_opts_src_lladdr) {
1237 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr,
1238 skb->dev);
1239 if (!lladdr) {
Joe Perches675418d2012-05-16 19:28:38 +00001240 ND_PRINTK(2, warn,
1241 "RA: invalid link-layer address length\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001242 goto out;
1243 }
1244 }
1245 neigh_update(neigh, lladdr, NUD_STALE,
1246 NEIGH_UPDATE_F_WEAK_OVERRIDE|
1247 NEIGH_UPDATE_F_OVERRIDE|
1248 NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
1249 NEIGH_UPDATE_F_ISROUTER);
1250 }
1251
Thomas Graf65e9b622010-09-03 02:59:14 +00001252 if (!accept_ra(in6_dev))
David Ward31ce8c72009-08-29 00:04:09 -07001253 goto out;
1254
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001255#ifdef CONFIG_IPV6_ROUTE_INFO
Andreas Hofmeister9f562202011-10-24 19:13:15 -04001256 if (ipv6_chk_addr(dev_net(in6_dev->dev), &ipv6_hdr(skb)->saddr, NULL, 0))
1257 goto skip_routeinfo;
1258
YOSHIFUJI Hideaki09c884d2006-03-20 17:07:03 -08001259 if (in6_dev->cnf.accept_ra_rtr_pref && ndopts.nd_opts_ri) {
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001260 struct nd_opt_hdr *p;
1261 for (p = ndopts.nd_opts_ri;
1262 p;
1263 p = ndisc_next_option(p, ndopts.nd_opts_ri_end)) {
YOSHIFUJI Hideaki6294e002008-03-15 23:56:52 -04001264 struct route_info *ri = (struct route_info *)p;
1265#ifdef CONFIG_IPV6_NDISC_NODETYPE
1266 if (skb->ndisc_nodetype == NDISC_NODETYPE_NODEFAULT &&
1267 ri->prefix_len == 0)
1268 continue;
1269#endif
1270 if (ri->prefix_len > in6_dev->cnf.accept_ra_rt_info_max_plen)
YOSHIFUJI Hideaki09c884d2006-03-20 17:07:03 -08001271 continue;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001272 rt6_route_rcv(skb->dev, (u8*)p, (p->nd_opt_len) << 3,
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001273 &ipv6_hdr(skb)->saddr);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001274 }
1275 }
Andreas Hofmeister9f562202011-10-24 19:13:15 -04001276
1277skip_routeinfo:
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001278#endif
1279
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001280#ifdef CONFIG_IPV6_NDISC_NODETYPE
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001281 /* skip link-specific ndopts from interior routers */
1282 if (skb->ndisc_nodetype == NDISC_NODETYPE_NODEFAULT)
1283 goto out;
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001284#endif
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001285
YOSHIFUJI Hideakic4fd30e2006-03-20 16:55:26 -08001286 if (in6_dev->cnf.accept_ra_pinfo && ndopts.nd_opts_pi) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001287 struct nd_opt_hdr *p;
1288 for (p = ndopts.nd_opts_pi;
1289 p;
1290 p = ndisc_next_option(p, ndopts.nd_opts_pi_end)) {
Neil Hormane6bff992012-01-04 10:49:15 +00001291 addrconf_prefix_rcv(skb->dev, (u8 *)p,
1292 (p->nd_opt_len) << 3,
1293 ndopts.nd_opts_src_lladdr != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001294 }
1295 }
1296
1297 if (ndopts.nd_opts_mtu) {
Al Viroe69a4ad2006-11-14 20:56:00 -08001298 __be32 n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001299 u32 mtu;
1300
Al Viroe69a4ad2006-11-14 20:56:00 -08001301 memcpy(&n, ((u8*)(ndopts.nd_opts_mtu+1))+2, sizeof(mtu));
1302 mtu = ntohl(n);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001303
1304 if (mtu < IPV6_MIN_MTU || mtu > skb->dev->mtu) {
Joe Perches675418d2012-05-16 19:28:38 +00001305 ND_PRINTK(2, warn, "RA: invalid mtu: %d\n", mtu);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001306 } else if (in6_dev->cnf.mtu6 != mtu) {
1307 in6_dev->cnf.mtu6 = mtu;
1308
1309 if (rt)
David S. Millerdefb3512010-12-08 21:16:57 -08001310 dst_metric_set(&rt->dst, RTAX_MTU, mtu);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001311
1312 rt6_mtu_change(skb->dev, mtu);
1313 }
1314 }
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001315
Pierre Ynard31910572007-10-10 21:22:05 -07001316 if (ndopts.nd_useropts) {
YOSHIFUJI Hideaki61cf46ad2008-01-22 17:32:53 +09001317 struct nd_opt_hdr *p;
1318 for (p = ndopts.nd_useropts;
1319 p;
1320 p = ndisc_next_useropt(p, ndopts.nd_useropts_end)) {
1321 ndisc_ra_useropt(skb, p);
Pierre Ynard31910572007-10-10 21:22:05 -07001322 }
1323 }
1324
Linus Torvalds1da177e2005-04-16 15:20:36 -07001325 if (ndopts.nd_opts_tgt_lladdr || ndopts.nd_opts_rh) {
Joe Perches675418d2012-05-16 19:28:38 +00001326 ND_PRINTK(2, warn, "RA: invalid RA options\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001327 }
1328out:
1329 if (rt)
Changli Gaod8d1f302010-06-10 23:31:35 -07001330 dst_release(&rt->dst);
David S. Millereb857182012-01-27 15:07:56 -08001331 if (neigh)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001332 neigh_release(neigh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001333}
1334
1335static void ndisc_redirect_rcv(struct sk_buff *skb)
1336{
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001337#ifdef CONFIG_IPV6_NDISC_NODETYPE
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001338 switch (skb->ndisc_nodetype) {
1339 case NDISC_NODETYPE_HOST:
1340 case NDISC_NODETYPE_NODEFAULT:
Joe Perches675418d2012-05-16 19:28:38 +00001341 ND_PRINTK(2, warn,
1342 "Redirect: from host or unauthorized router\n");
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001343 return;
1344 }
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001345#endif
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001346
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001347 if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) {
Joe Perches675418d2012-05-16 19:28:38 +00001348 ND_PRINTK(2, warn,
1349 "Redirect: source address is not link-local\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001350 return;
1351 }
1352
David S. Millerb94f1c02012-07-12 00:33:37 -07001353 icmpv6_notify(skb, NDISC_REDIRECT, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001354}
1355
David S. Miller49919692012-01-27 15:30:48 -08001356void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001357{
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001358 struct net_device *dev = skb->dev;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001359 struct net *net = dev_net(dev);
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001360 struct sock *sk = net->ipv6.ndisc_sk;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001361 int len = sizeof(struct icmp6hdr) + 2 * sizeof(struct in6_addr);
David S. Millerfbfe95a2012-06-08 23:24:18 -07001362 struct inet_peer *peer;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001363 struct sk_buff *buff;
1364 struct icmp6hdr *icmph;
1365 struct in6_addr saddr_buf;
1366 struct in6_addr *addrp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001367 struct rt6_info *rt;
1368 struct dst_entry *dst;
1369 struct inet6_dev *idev;
David S. Miller4c9483b2011-03-12 16:22:43 -05001370 struct flowi6 fl6;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001371 u8 *opt;
Herbert Xua7ae1992011-11-18 02:20:04 +00001372 int hlen, tlen;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001373 int rd_len;
1374 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001375 u8 ha_buf[MAX_ADDR_LEN], *ha = NULL;
David S. Miller1d861aa2012-07-10 03:58:16 -07001376 bool ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001377
Neil Horman95c385b2007-04-25 17:08:10 -07001378 if (ipv6_get_lladdr(dev, &saddr_buf, IFA_F_TENTATIVE)) {
Joe Perches675418d2012-05-16 19:28:38 +00001379 ND_PRINTK(2, warn, "Redirect: no link-local address on %s\n",
1380 dev->name);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001381 return;
1382 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001383
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001384 if (!ipv6_addr_equal(&ipv6_hdr(skb)->daddr, target) &&
Brian Haleybf0b48d2007-10-08 00:12:05 -07001385 ipv6_addr_type(target) != (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) {
Joe Perches675418d2012-05-16 19:28:38 +00001386 ND_PRINTK(2, warn,
1387 "Redirect: target address is not link-local unicast\n");
Li Yewang29556522007-01-30 14:33:20 -08001388 return;
1389 }
1390
David S. Miller4c9483b2011-03-12 16:22:43 -05001391 icmpv6_flow_init(sk, &fl6, NDISC_REDIRECT,
YOSHIFUJI Hideaki95e41e92007-12-06 15:43:30 -08001392 &saddr_buf, &ipv6_hdr(skb)->saddr, dev->ifindex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001393
David S. Miller4c9483b2011-03-12 16:22:43 -05001394 dst = ip6_route_output(net, NULL, &fl6);
RongQing.Li5095d642012-02-21 22:10:49 +00001395 if (dst->error) {
1396 dst_release(dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001397 return;
RongQing.Li5095d642012-02-21 22:10:49 +00001398 }
David S. Miller4c9483b2011-03-12 16:22:43 -05001399 dst = xfrm_lookup(net, dst, flowi6_to_flowi(&fl6), NULL, 0);
David S. Miller452edd52011-03-02 13:27:41 -08001400 if (IS_ERR(dst))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001401 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001402
1403 rt = (struct rt6_info *) dst;
1404
1405 if (rt->rt6i_flags & RTF_GATEWAY) {
Joe Perches675418d2012-05-16 19:28:38 +00001406 ND_PRINTK(2, warn,
1407 "Redirect: destination is not a neighbour\n");
Ilpo Järvinend73f0802009-02-06 23:47:37 -08001408 goto release;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001409 }
David S. Miller1d861aa2012-07-10 03:58:16 -07001410 peer = inet_getpeer_v6(net->ipv6.peers, &rt->rt6i_dst.addr, 1);
1411 ret = inet_peer_xrlim_allow(peer, 1*HZ);
1412 if (peer)
1413 inet_putpeer(peer);
1414 if (!ret)
Ilpo Järvinend73f0802009-02-06 23:47:37 -08001415 goto release;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001416
1417 if (dev->addr_len) {
David S. Miller49919692012-01-27 15:30:48 -08001418 struct neighbour *neigh = dst_neigh_lookup(skb_dst(skb), target);
1419 if (!neigh) {
Joe Perches675418d2012-05-16 19:28:38 +00001420 ND_PRINTK(2, warn,
1421 "Redirect: no neigh for target address\n");
David S. Miller49919692012-01-27 15:30:48 -08001422 goto release;
1423 }
1424
Linus Torvalds1da177e2005-04-16 15:20:36 -07001425 read_lock_bh(&neigh->lock);
1426 if (neigh->nud_state & NUD_VALID) {
1427 memcpy(ha_buf, neigh->ha, dev->addr_len);
1428 read_unlock_bh(&neigh->lock);
1429 ha = ha_buf;
1430 len += ndisc_opt_addr_space(dev);
1431 } else
1432 read_unlock_bh(&neigh->lock);
David S. Miller49919692012-01-27 15:30:48 -08001433
1434 neigh_release(neigh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001435 }
1436
1437 rd_len = min_t(unsigned int,
1438 IPV6_MIN_MTU-sizeof(struct ipv6hdr)-len, skb->len + 8);
1439 rd_len &= ~0x7;
1440 len += rd_len;
1441
Herbert Xua7ae1992011-11-18 02:20:04 +00001442 hlen = LL_RESERVED_SPACE(dev);
1443 tlen = dev->needed_tailroom;
David S. Millerd54a81d2006-12-02 21:00:06 -08001444 buff = sock_alloc_send_skb(sk,
1445 (MAX_HEADER + sizeof(struct ipv6hdr) +
Herbert Xua7ae1992011-11-18 02:20:04 +00001446 len + hlen + tlen),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001447 1, &err);
1448 if (buff == NULL) {
Joe Perches675418d2012-05-16 19:28:38 +00001449 ND_PRINTK(0, err,
1450 "Redirect: %s failed to allocate an skb, err=%d\n",
1451 __func__, err);
Ilpo Järvinend73f0802009-02-06 23:47:37 -08001452 goto release;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001453 }
1454
Herbert Xua7ae1992011-11-18 02:20:04 +00001455 skb_reserve(buff, hlen);
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001456 ip6_nd_hdr(sk, buff, dev, &saddr_buf, &ipv6_hdr(skb)->saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001457 IPPROTO_ICMPV6, len);
1458
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -07001459 skb_set_transport_header(buff, skb_tail_pointer(buff) - buff->data);
Arnaldo Carvalho de Melod10ba342007-03-14 21:05:37 -03001460 skb_put(buff, len);
1461 icmph = icmp6_hdr(buff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001462
1463 memset(icmph, 0, sizeof(struct icmp6hdr));
1464 icmph->icmp6_type = NDISC_REDIRECT;
1465
1466 /*
1467 * copy target and destination addresses
1468 */
1469
1470 addrp = (struct in6_addr *)(icmph + 1);
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001471 *addrp = *target;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001472 addrp++;
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001473 *addrp = ipv6_hdr(skb)->daddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001474
1475 opt = (u8*) (addrp + 1);
1476
1477 /*
1478 * include target_address option
1479 */
1480
1481 if (ha)
1482 opt = ndisc_fill_addr_option(opt, ND_OPT_TARGET_LL_ADDR, ha,
1483 dev->addr_len, dev->type);
1484
1485 /*
1486 * build redirect option and copy skb over to the new packet.
1487 */
1488
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001489 memset(opt, 0, 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001490 *(opt++) = ND_OPT_REDIRECT_HDR;
1491 *(opt++) = (rd_len >> 3);
1492 opt += 6;
1493
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001494 memcpy(opt, ipv6_hdr(skb), rd_len - 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001495
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001496 icmph->icmp6_cksum = csum_ipv6_magic(&saddr_buf, &ipv6_hdr(skb)->saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001497 len, IPPROTO_ICMPV6,
Joe Perches07f07572008-11-19 15:44:53 -08001498 csum_partial(icmph, len, 0));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001499
Eric Dumazetadf30902009-06-02 05:19:30 +00001500 skb_dst_set(buff, dst);
Eric Dumazetcfdf7642011-07-27 21:13:03 +00001501 rcu_read_lock();
1502 idev = __in6_dev_get(dst->dev);
Neil Hormanedf391f2009-04-27 02:45:02 -07001503 IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUT, skb->len);
Jan Engelhardtb2e0b382010-03-23 04:09:07 +01001504 err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, buff, NULL, dst->dev,
Patrick McHardy6e23ae22007-11-19 18:53:30 -08001505 dst_output);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001506 if (!err) {
Denis V. Lunev5c5d2442008-10-08 10:33:50 -07001507 ICMP6MSGOUT_INC_STATS(net, idev, NDISC_REDIRECT);
Denis V. Luneva862f6a2008-10-08 10:33:06 -07001508 ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001509 }
1510
Eric Dumazetcfdf7642011-07-27 21:13:03 +00001511 rcu_read_unlock();
Ilpo Järvinend73f0802009-02-06 23:47:37 -08001512 return;
1513
1514release:
1515 dst_release(dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001516}
1517
1518static void pndisc_redo(struct sk_buff *skb)
1519{
YOSHIFUJI Hideaki140e26fc2005-10-05 12:11:41 -07001520 ndisc_recv_ns(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001521 kfree_skb(skb);
1522}
1523
1524int ndisc_rcv(struct sk_buff *skb)
1525{
1526 struct nd_msg *msg;
1527
1528 if (!pskb_may_pull(skb, skb->len))
1529 return 0;
1530
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -07001531 msg = (struct nd_msg *)skb_transport_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001532
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -07001533 __skb_push(skb, skb->data - skb_transport_header(skb));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001534
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001535 if (ipv6_hdr(skb)->hop_limit != 255) {
Joe Perches675418d2012-05-16 19:28:38 +00001536 ND_PRINTK(2, warn, "NDISC: invalid hop-limit: %d\n",
1537 ipv6_hdr(skb)->hop_limit);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001538 return 0;
1539 }
1540
1541 if (msg->icmph.icmp6_code != 0) {
Joe Perches675418d2012-05-16 19:28:38 +00001542 ND_PRINTK(2, warn, "NDISC: invalid ICMPv6 code: %d\n",
1543 msg->icmph.icmp6_code);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001544 return 0;
1545 }
1546
Patrick McHardya61bbcf2005-08-14 17:24:31 -07001547 memset(NEIGH_CB(skb), 0, sizeof(struct neighbour_cb));
1548
Linus Torvalds1da177e2005-04-16 15:20:36 -07001549 switch (msg->icmph.icmp6_type) {
1550 case NDISC_NEIGHBOUR_SOLICITATION:
1551 ndisc_recv_ns(skb);
1552 break;
1553
1554 case NDISC_NEIGHBOUR_ADVERTISEMENT:
1555 ndisc_recv_na(skb);
1556 break;
1557
1558 case NDISC_ROUTER_SOLICITATION:
1559 ndisc_recv_rs(skb);
1560 break;
1561
1562 case NDISC_ROUTER_ADVERTISEMENT:
1563 ndisc_router_discovery(skb);
1564 break;
1565
1566 case NDISC_REDIRECT:
1567 ndisc_redirect_rcv(skb);
1568 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -07001569 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001570
1571 return 0;
1572}
1573
1574static int ndisc_netdev_event(struct notifier_block *this, unsigned long event, void *ptr)
1575{
1576 struct net_device *dev = ptr;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001577 struct net *net = dev_net(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001578
1579 switch (event) {
1580 case NETDEV_CHANGEADDR:
1581 neigh_changeaddr(&nd_tbl, dev);
Daniel Lezcano5b7c9312008-03-03 23:28:58 -08001582 fib6_run_gc(~0UL, net);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001583 break;
1584 case NETDEV_DOWN:
1585 neigh_ifdown(&nd_tbl, dev);
Daniel Lezcano5b7c9312008-03-03 23:28:58 -08001586 fib6_run_gc(~0UL, net);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001587 break;
Ben Hutchingsf47b9462011-04-15 13:46:02 +00001588 case NETDEV_NOTIFY_PEERS:
1589 ndisc_send_unsol_na(dev);
1590 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001591 default:
1592 break;
1593 }
1594
1595 return NOTIFY_DONE;
1596}
1597
1598static struct notifier_block ndisc_netdev_notifier = {
1599 .notifier_call = ndisc_netdev_event,
1600};
1601
1602#ifdef CONFIG_SYSCTL
1603static void ndisc_warn_deprecated_sysctl(struct ctl_table *ctl,
1604 const char *func, const char *dev_name)
1605{
1606 static char warncomm[TASK_COMM_LEN];
1607 static int warned;
1608 if (strcmp(warncomm, current->comm) && warned < 5) {
1609 strcpy(warncomm, current->comm);
Joe Perchesf3213832012-05-15 14:11:53 +00001610 pr_warn("process `%s' is using deprecated sysctl (%s) net.ipv6.neigh.%s.%s - use net.ipv6.neigh.%s.%s_ms instead\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001611 warncomm, func,
1612 dev_name, ctl->procname,
1613 dev_name, ctl->procname);
1614 warned++;
1615 }
1616}
1617
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001618int ndisc_ifinfo_sysctl_change(struct ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *ppos)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001619{
1620 struct net_device *dev = ctl->extra1;
1621 struct inet6_dev *idev;
1622 int ret;
1623
Eric W. Biedermand12af672007-10-18 03:05:25 -07001624 if ((strcmp(ctl->procname, "retrans_time") == 0) ||
1625 (strcmp(ctl->procname, "base_reachable_time") == 0))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001626 ndisc_warn_deprecated_sysctl(ctl, "syscall", dev ? dev->name : "default");
1627
Eric W. Biedermand12af672007-10-18 03:05:25 -07001628 if (strcmp(ctl->procname, "retrans_time") == 0)
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001629 ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
Eric W. Biedermand12af672007-10-18 03:05:25 -07001630
1631 else if (strcmp(ctl->procname, "base_reachable_time") == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001632 ret = proc_dointvec_jiffies(ctl, write,
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001633 buffer, lenp, ppos);
Eric W. Biedermand12af672007-10-18 03:05:25 -07001634
1635 else if ((strcmp(ctl->procname, "retrans_time_ms") == 0) ||
YOSHIFUJI Hideakiad02ac12007-10-29 01:32:23 -07001636 (strcmp(ctl->procname, "base_reachable_time_ms") == 0))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001637 ret = proc_dointvec_ms_jiffies(ctl, write,
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001638 buffer, lenp, ppos);
Eric W. Biedermand12af672007-10-18 03:05:25 -07001639 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001640 ret = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001641
1642 if (write && ret == 0 && dev && (idev = in6_dev_get(dev)) != NULL) {
Eric W. Biedermand12af672007-10-18 03:05:25 -07001643 if (ctl->data == &idev->nd_parms->base_reachable_time)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001644 idev->nd_parms->reachable_time = neigh_rand_reach_time(idev->nd_parms->base_reachable_time);
1645 idev->tstamp = jiffies;
1646 inet6_ifinfo_notify(RTM_NEWLINK, idev);
1647 in6_dev_put(idev);
1648 }
1649 return ret;
1650}
1651
Linus Torvalds1da177e2005-04-16 15:20:36 -07001652
1653#endif
1654
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00001655static int __net_init ndisc_net_init(struct net *net)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001656{
1657 struct ipv6_pinfo *np;
1658 struct sock *sk;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001659 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001660
Denis V. Lunev1ed85162008-04-03 14:31:03 -07001661 err = inet_ctl_sock_create(&sk, PF_INET6,
1662 SOCK_RAW, IPPROTO_ICMPV6, net);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001663 if (err < 0) {
Joe Perches675418d2012-05-16 19:28:38 +00001664 ND_PRINTK(0, err,
1665 "NDISC: Failed to initialize the control socket (err %d)\n",
1666 err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001667 return err;
1668 }
1669
Denis V. Lunev1ed85162008-04-03 14:31:03 -07001670 net->ipv6.ndisc_sk = sk;
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001671
Linus Torvalds1da177e2005-04-16 15:20:36 -07001672 np = inet6_sk(sk);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001673 np->hop_limit = 255;
1674 /* Do not loopback ndisc messages */
1675 np->mc_loop = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001676
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001677 return 0;
1678}
1679
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00001680static void __net_exit ndisc_net_exit(struct net *net)
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001681{
Denis V. Lunev1ed85162008-04-03 14:31:03 -07001682 inet_ctl_sock_destroy(net->ipv6.ndisc_sk);
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001683}
1684
1685static struct pernet_operations ndisc_net_ops = {
1686 .init = ndisc_net_init,
1687 .exit = ndisc_net_exit,
1688};
1689
1690int __init ndisc_init(void)
1691{
1692 int err;
1693
1694 err = register_pernet_subsys(&ndisc_net_ops);
1695 if (err)
1696 return err;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001697 /*
1698 * Initialize the neighbour table
1699 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001700 neigh_table_init(&nd_tbl);
1701
1702#ifdef CONFIG_SYSCTL
Eric W. Biederman54716e32010-02-14 03:27:03 +00001703 err = neigh_sysctl_register(NULL, &nd_tbl.parms, "ipv6",
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001704 &ndisc_ifinfo_sysctl_change);
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001705 if (err)
1706 goto out_unregister_pernet;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001707#endif
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001708 err = register_netdevice_notifier(&ndisc_netdev_notifier);
1709 if (err)
1710 goto out_unregister_sysctl;
1711out:
1712 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001713
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001714out_unregister_sysctl:
1715#ifdef CONFIG_SYSCTL
1716 neigh_sysctl_unregister(&nd_tbl.parms);
1717out_unregister_pernet:
1718#endif
1719 unregister_pernet_subsys(&ndisc_net_ops);
1720 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001721}
1722
1723void ndisc_cleanup(void)
1724{
Dmitry Mishin36f73d02006-11-03 16:08:19 -08001725 unregister_netdevice_notifier(&ndisc_netdev_notifier);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001726#ifdef CONFIG_SYSCTL
1727 neigh_sysctl_unregister(&nd_tbl.parms);
1728#endif
1729 neigh_table_clear(&nd_tbl);
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001730 unregister_pernet_subsys(&ndisc_net_ops);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001731}