blob: db782d2f7cc2cd7ad037ee4de068ee1365731950 [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 *
Pierre Ynard31910572007-10-10 21:22:05 -070018 * Pierre Ynard : export userland ND options
19 * through netlink (RDNSS support)
Linus Torvalds1da177e2005-04-16 15:20:36 -070020 * Lars Fenneberg : fixed MTU setting on receipt
21 * of an RA.
Linus Torvalds1da177e2005-04-16 15:20:36 -070022 * Janos Farkas : kmalloc failure checks
23 * Alexey Kuznetsov : state machine reworked
24 * and moved to net/core.
25 * Pekka Savola : RFC2461 validation
26 * YOSHIFUJI Hideaki @USAGI : Verify ND options properly
27 */
28
29/* Set to 3 to get tracing... */
30#define ND_DEBUG 1
31
32#define ND_PRINTK(fmt, args...) do { if (net_ratelimit()) { printk(fmt, ## args); } } while(0)
33#define ND_NOPRINTK(x...) do { ; } while(0)
34#define ND_PRINTK0 ND_PRINTK
35#define ND_PRINTK1 ND_NOPRINTK
36#define ND_PRINTK2 ND_NOPRINTK
37#define ND_PRINTK3 ND_NOPRINTK
38#if ND_DEBUG >= 1
39#undef ND_PRINTK1
40#define ND_PRINTK1 ND_PRINTK
41#endif
42#if ND_DEBUG >= 2
43#undef ND_PRINTK2
44#define ND_PRINTK2 ND_PRINTK
45#endif
46#if ND_DEBUG >= 3
47#undef ND_PRINTK3
48#define ND_PRINTK3 ND_PRINTK
49#endif
50
51#include <linux/module.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070052#include <linux/errno.h>
53#include <linux/types.h>
54#include <linux/socket.h>
55#include <linux/sockios.h>
56#include <linux/sched.h>
57#include <linux/net.h>
58#include <linux/in6.h>
59#include <linux/route.h>
60#include <linux/init.h>
61#include <linux/rcupdate.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090062#include <linux/slab.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070063#ifdef CONFIG_SYSCTL
64#include <linux/sysctl.h>
65#endif
66
Thomas Graf18237302006-08-04 23:04:54 -070067#include <linux/if_addr.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070068#include <linux/if_arp.h>
69#include <linux/ipv6.h>
70#include <linux/icmpv6.h>
71#include <linux/jhash.h>
72
73#include <net/sock.h>
74#include <net/snmp.h>
75
76#include <net/ipv6.h>
77#include <net/protocol.h>
78#include <net/ndisc.h>
79#include <net/ip6_route.h>
80#include <net/addrconf.h>
81#include <net/icmp.h>
82
Pierre Ynard31910572007-10-10 21:22:05 -070083#include <net/netlink.h>
84#include <linux/rtnetlink.h>
85
Linus Torvalds1da177e2005-04-16 15:20:36 -070086#include <net/flow.h>
87#include <net/ip6_checksum.h>
Denis V. Lunev1ed85162008-04-03 14:31:03 -070088#include <net/inet_common.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070089#include <linux/proc_fs.h>
90
91#include <linux/netfilter.h>
92#include <linux/netfilter_ipv6.h>
93
Eric Dumazetd6bf7812010-10-04 06:15:44 +000094static u32 ndisc_hash(const void *pkey,
95 const struct net_device *dev,
96 __u32 rnd);
Linus Torvalds1da177e2005-04-16 15:20:36 -070097static int ndisc_constructor(struct neighbour *neigh);
98static void ndisc_solicit(struct neighbour *neigh, struct sk_buff *skb);
99static void ndisc_error_report(struct neighbour *neigh, struct sk_buff *skb);
100static int pndisc_constructor(struct pneigh_entry *n);
101static void pndisc_destructor(struct pneigh_entry *n);
102static void pndisc_redo(struct sk_buff *skb);
103
Stephen Hemminger89d69d22009-09-01 11:13:19 +0000104static const struct neigh_ops ndisc_generic_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700105 .family = AF_INET6,
106 .solicit = ndisc_solicit,
107 .error_report = ndisc_error_report,
108 .output = neigh_resolve_output,
109 .connected_output = neigh_connected_output,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700110 .queue_xmit = dev_queue_xmit,
111};
112
Stephen Hemminger89d69d22009-09-01 11:13:19 +0000113static const struct neigh_ops ndisc_hh_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700114 .family = AF_INET6,
115 .solicit = ndisc_solicit,
116 .error_report = ndisc_error_report,
117 .output = neigh_resolve_output,
118 .connected_output = neigh_resolve_output,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700119 .queue_xmit = dev_queue_xmit,
120};
121
122
Stephen Hemminger89d69d22009-09-01 11:13:19 +0000123static const struct neigh_ops ndisc_direct_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700124 .family = AF_INET6,
125 .output = dev_queue_xmit,
126 .connected_output = dev_queue_xmit,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127 .queue_xmit = dev_queue_xmit,
128};
129
130struct neigh_table nd_tbl = {
131 .family = AF_INET6,
132 .entry_size = sizeof(struct neighbour) + sizeof(struct in6_addr),
133 .key_len = sizeof(struct in6_addr),
134 .hash = ndisc_hash,
135 .constructor = ndisc_constructor,
136 .pconstructor = pndisc_constructor,
137 .pdestructor = pndisc_destructor,
138 .proxy_redo = pndisc_redo,
139 .id = "ndisc_cache",
140 .parms = {
Shan Weib6720832010-12-01 18:05:12 +0000141 .tbl = &nd_tbl,
142 .base_reachable_time = ND_REACHABLE_TIME,
143 .retrans_time = ND_RETRANS_TIMER,
144 .gc_staletime = 60 * HZ,
145 .reachable_time = ND_REACHABLE_TIME,
146 .delay_probe_time = 5 * HZ,
147 .queue_len = 3,
148 .ucast_probes = 3,
149 .mcast_probes = 3,
150 .anycast_delay = 1 * HZ,
151 .proxy_delay = (8 * HZ) / 10,
152 .proxy_qlen = 64,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700153 },
154 .gc_interval = 30 * HZ,
155 .gc_thresh1 = 128,
156 .gc_thresh2 = 512,
157 .gc_thresh3 = 1024,
158};
159
160/* ND options */
161struct ndisc_options {
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800162 struct nd_opt_hdr *nd_opt_array[__ND_OPT_ARRAY_MAX];
163#ifdef CONFIG_IPV6_ROUTE_INFO
164 struct nd_opt_hdr *nd_opts_ri;
165 struct nd_opt_hdr *nd_opts_ri_end;
166#endif
Pierre Ynard31910572007-10-10 21:22:05 -0700167 struct nd_opt_hdr *nd_useropts;
168 struct nd_opt_hdr *nd_useropts_end;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700169};
170
171#define nd_opts_src_lladdr nd_opt_array[ND_OPT_SOURCE_LL_ADDR]
172#define nd_opts_tgt_lladdr nd_opt_array[ND_OPT_TARGET_LL_ADDR]
173#define nd_opts_pi nd_opt_array[ND_OPT_PREFIX_INFO]
174#define nd_opts_pi_end nd_opt_array[__ND_OPT_PREFIX_INFO_END]
175#define nd_opts_rh nd_opt_array[ND_OPT_REDIRECT_HDR]
176#define nd_opts_mtu nd_opt_array[ND_OPT_MTU]
177
178#define NDISC_OPT_SPACE(len) (((len)+2+7)&~7)
179
180/*
181 * Return the padding between the option length and the start of the
182 * link addr. Currently only IP-over-InfiniBand needs this, although
183 * if RFC 3831 IPv6-over-Fibre Channel is ever implemented it may
184 * also need a pad of 2.
185 */
186static int ndisc_addr_option_pad(unsigned short type)
187{
188 switch (type) {
189 case ARPHRD_INFINIBAND: return 2;
190 default: return 0;
191 }
192}
193
194static inline int ndisc_opt_addr_space(struct net_device *dev)
195{
196 return NDISC_OPT_SPACE(dev->addr_len + ndisc_addr_option_pad(dev->type));
197}
198
199static u8 *ndisc_fill_addr_option(u8 *opt, int type, void *data, int data_len,
200 unsigned short addr_type)
201{
202 int space = NDISC_OPT_SPACE(data_len);
203 int pad = ndisc_addr_option_pad(addr_type);
204
205 opt[0] = type;
206 opt[1] = space>>3;
207
208 memset(opt + 2, 0, pad);
209 opt += pad;
210 space -= pad;
211
212 memcpy(opt+2, data, data_len);
213 data_len += 2;
214 opt += data_len;
215 if ((space -= data_len) > 0)
216 memset(opt, 0, space);
217 return opt + space;
218}
219
220static struct nd_opt_hdr *ndisc_next_option(struct nd_opt_hdr *cur,
221 struct nd_opt_hdr *end)
222{
223 int type;
224 if (!cur || !end || cur >= end)
225 return NULL;
226 type = cur->nd_opt_type;
227 do {
228 cur = ((void *)cur) + (cur->nd_opt_len << 3);
229 } while(cur < end && cur->nd_opt_type != type);
Eric Dumazeta02cec22010-09-22 20:43:57 +0000230 return cur <= end && cur->nd_opt_type == type ? cur : NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700231}
232
Pierre Ynard31910572007-10-10 21:22:05 -0700233static inline int ndisc_is_useropt(struct nd_opt_hdr *opt)
234{
Eric Dumazeta02cec22010-09-22 20:43:57 +0000235 return opt->nd_opt_type == ND_OPT_RDNSS;
Pierre Ynard31910572007-10-10 21:22:05 -0700236}
237
238static struct nd_opt_hdr *ndisc_next_useropt(struct nd_opt_hdr *cur,
239 struct nd_opt_hdr *end)
240{
241 if (!cur || !end || cur >= end)
242 return NULL;
243 do {
244 cur = ((void *)cur) + (cur->nd_opt_len << 3);
245 } while(cur < end && !ndisc_is_useropt(cur));
Eric Dumazeta02cec22010-09-22 20:43:57 +0000246 return cur <= end && ndisc_is_useropt(cur) ? cur : NULL;
Pierre Ynard31910572007-10-10 21:22:05 -0700247}
248
Linus Torvalds1da177e2005-04-16 15:20:36 -0700249static struct ndisc_options *ndisc_parse_options(u8 *opt, int opt_len,
250 struct ndisc_options *ndopts)
251{
252 struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)opt;
253
254 if (!nd_opt || opt_len < 0 || !ndopts)
255 return NULL;
256 memset(ndopts, 0, sizeof(*ndopts));
257 while (opt_len) {
258 int l;
259 if (opt_len < sizeof(struct nd_opt_hdr))
260 return NULL;
261 l = nd_opt->nd_opt_len << 3;
262 if (opt_len < l || l == 0)
263 return NULL;
264 switch (nd_opt->nd_opt_type) {
265 case ND_OPT_SOURCE_LL_ADDR:
266 case ND_OPT_TARGET_LL_ADDR:
267 case ND_OPT_MTU:
268 case ND_OPT_REDIRECT_HDR:
269 if (ndopts->nd_opt_array[nd_opt->nd_opt_type]) {
270 ND_PRINTK2(KERN_WARNING
271 "%s(): duplicated ND6 option found: type=%d\n",
Harvey Harrison0dc47872008-03-05 20:47:47 -0800272 __func__,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700273 nd_opt->nd_opt_type);
274 } else {
275 ndopts->nd_opt_array[nd_opt->nd_opt_type] = nd_opt;
276 }
277 break;
278 case ND_OPT_PREFIX_INFO:
279 ndopts->nd_opts_pi_end = nd_opt;
Stephen Hemmingercfcabdc2007-10-09 01:59:42 -0700280 if (!ndopts->nd_opt_array[nd_opt->nd_opt_type])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281 ndopts->nd_opt_array[nd_opt->nd_opt_type] = nd_opt;
282 break;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800283#ifdef CONFIG_IPV6_ROUTE_INFO
284 case ND_OPT_ROUTE_INFO:
285 ndopts->nd_opts_ri_end = nd_opt;
286 if (!ndopts->nd_opts_ri)
287 ndopts->nd_opts_ri = nd_opt;
288 break;
289#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290 default:
Pierre Ynard31910572007-10-10 21:22:05 -0700291 if (ndisc_is_useropt(nd_opt)) {
292 ndopts->nd_useropts_end = nd_opt;
293 if (!ndopts->nd_useropts)
294 ndopts->nd_useropts = nd_opt;
295 } else {
296 /*
297 * Unknown options must be silently ignored,
298 * to accommodate future extension to the
299 * protocol.
300 */
301 ND_PRINTK2(KERN_NOTICE
302 "%s(): ignored unsupported option; type=%d, len=%d\n",
Harvey Harrison0dc47872008-03-05 20:47:47 -0800303 __func__,
Pierre Ynard31910572007-10-10 21:22:05 -0700304 nd_opt->nd_opt_type, nd_opt->nd_opt_len);
305 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700306 }
307 opt_len -= l;
308 nd_opt = ((void *)nd_opt) + l;
309 }
310 return ndopts;
311}
312
313static inline u8 *ndisc_opt_addr_data(struct nd_opt_hdr *p,
314 struct net_device *dev)
315{
316 u8 *lladdr = (u8 *)(p + 1);
317 int lladdrlen = p->nd_opt_len << 3;
318 int prepad = ndisc_addr_option_pad(dev->type);
319 if (lladdrlen != NDISC_OPT_SPACE(dev->addr_len + prepad))
320 return NULL;
Eric Dumazeta02cec22010-09-22 20:43:57 +0000321 return lladdr + prepad;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322}
323
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000324int ndisc_mc_map(const struct in6_addr *addr, char *buf, struct net_device *dev, int dir)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325{
326 switch (dev->type) {
327 case ARPHRD_ETHER:
328 case ARPHRD_IEEE802: /* Not sure. Check it later. --ANK */
329 case ARPHRD_FDDI:
330 ipv6_eth_mc_map(addr, buf);
331 return 0;
332 case ARPHRD_IEEE802_TR:
333 ipv6_tr_mc_map(addr,buf);
334 return 0;
335 case ARPHRD_ARCNET:
336 ipv6_arcnet_mc_map(addr, buf);
337 return 0;
338 case ARPHRD_INFINIBAND:
Rolf Manderscheida9e527e2007-12-10 13:38:41 -0700339 ipv6_ib_mc_map(addr, dev->broadcast, buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340 return 0;
Timo Teräs93ca3bb2011-03-28 22:40:53 +0000341 case ARPHRD_IPGRE:
342 return ipv6_ipgre_mc_map(addr, dev->broadcast, buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700343 default:
344 if (dir) {
345 memcpy(buf, dev->broadcast, dev->addr_len);
346 return 0;
347 }
348 }
349 return -EINVAL;
350}
351
YOSHIFUJI Hideaki71590392007-02-22 22:05:40 +0900352EXPORT_SYMBOL(ndisc_mc_map);
353
Eric Dumazetd6bf7812010-10-04 06:15:44 +0000354static u32 ndisc_hash(const void *pkey,
355 const struct net_device *dev,
356 __u32 hash_rnd)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700357{
358 const u32 *p32 = pkey;
359 u32 addr_hash, i;
360
361 addr_hash = 0;
362 for (i = 0; i < (sizeof(struct in6_addr) / sizeof(u32)); i++)
363 addr_hash ^= *p32++;
364
Eric Dumazetd6bf7812010-10-04 06:15:44 +0000365 return jhash_2words(addr_hash, dev->ifindex, hash_rnd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700366}
367
368static int ndisc_constructor(struct neighbour *neigh)
369{
370 struct in6_addr *addr = (struct in6_addr*)&neigh->primary_key;
371 struct net_device *dev = neigh->dev;
372 struct inet6_dev *in6_dev;
373 struct neigh_parms *parms;
374 int is_multicast = ipv6_addr_is_multicast(addr);
375
376 rcu_read_lock();
377 in6_dev = in6_dev_get(dev);
378 if (in6_dev == NULL) {
379 rcu_read_unlock();
380 return -EINVAL;
381 }
382
383 parms = in6_dev->nd_parms;
384 __neigh_parms_put(neigh->parms);
385 neigh->parms = neigh_parms_clone(parms);
386 rcu_read_unlock();
387
388 neigh->type = is_multicast ? RTN_MULTICAST : RTN_UNICAST;
Stephen Hemminger3b04ddd2007-10-09 01:40:57 -0700389 if (!dev->header_ops) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700390 neigh->nud_state = NUD_NOARP;
391 neigh->ops = &ndisc_direct_ops;
392 neigh->output = neigh->ops->queue_xmit;
393 } else {
394 if (is_multicast) {
395 neigh->nud_state = NUD_NOARP;
396 ndisc_mc_map(addr, neigh->ha, dev, 1);
397 } else if (dev->flags&(IFF_NOARP|IFF_LOOPBACK)) {
398 neigh->nud_state = NUD_NOARP;
399 memcpy(neigh->ha, dev->dev_addr, dev->addr_len);
400 if (dev->flags&IFF_LOOPBACK)
401 neigh->type = RTN_LOCAL;
402 } else if (dev->flags&IFF_POINTOPOINT) {
403 neigh->nud_state = NUD_NOARP;
404 memcpy(neigh->ha, dev->broadcast, dev->addr_len);
405 }
Stephen Hemminger3b04ddd2007-10-09 01:40:57 -0700406 if (dev->header_ops->cache)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700407 neigh->ops = &ndisc_hh_ops;
408 else
409 neigh->ops = &ndisc_generic_ops;
410 if (neigh->nud_state&NUD_VALID)
411 neigh->output = neigh->ops->connected_output;
412 else
413 neigh->output = neigh->ops->output;
414 }
415 in6_dev_put(in6_dev);
416 return 0;
417}
418
419static int pndisc_constructor(struct pneigh_entry *n)
420{
421 struct in6_addr *addr = (struct in6_addr*)&n->key;
422 struct in6_addr maddr;
423 struct net_device *dev = n->dev;
424
425 if (dev == NULL || __in6_dev_get(dev) == NULL)
426 return -EINVAL;
427 addrconf_addr_solict_mult(addr, &maddr);
428 ipv6_dev_mc_inc(dev, &maddr);
429 return 0;
430}
431
432static void pndisc_destructor(struct pneigh_entry *n)
433{
434 struct in6_addr *addr = (struct in6_addr*)&n->key;
435 struct in6_addr maddr;
436 struct net_device *dev = n->dev;
437
438 if (dev == NULL || __in6_dev_get(dev) == NULL)
439 return;
440 addrconf_addr_solict_mult(addr, &maddr);
441 ipv6_dev_mc_dec(dev, &maddr);
442}
443
Brian Haley305d5522008-11-04 17:51:14 -0800444struct sk_buff *ndisc_build_skb(struct net_device *dev,
445 const struct in6_addr *daddr,
446 const struct in6_addr *saddr,
447 struct icmp6hdr *icmp6h,
448 const struct in6_addr *target,
449 int llinfo)
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900450{
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900451 struct net *net = dev_net(dev);
Daniel Lezcano1762f7e2008-03-07 11:15:34 -0800452 struct sock *sk = net->ipv6.ndisc_sk;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900453 struct sk_buff *skb;
454 struct icmp6hdr *hdr;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900455 int len;
456 int err;
Brian Haley305d5522008-11-04 17:51:14 -0800457 u8 *opt;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900458
459 if (!dev->addr_len)
460 llinfo = 0;
461
462 len = sizeof(struct icmp6hdr) + (target ? sizeof(*target) : 0);
463 if (llinfo)
464 len += ndisc_opt_addr_space(dev);
465
466 skb = sock_alloc_send_skb(sk,
467 (MAX_HEADER + sizeof(struct ipv6hdr) +
Johannes Bergf5184d22008-05-12 20:48:31 -0700468 len + LL_ALLOCATED_SPACE(dev)),
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900469 1, &err);
470 if (!skb) {
471 ND_PRINTK0(KERN_ERR
Brian Haleydae9de82009-06-02 00:20:26 -0700472 "ICMPv6 ND: %s() failed to allocate an skb, err=%d.\n",
473 __func__, err);
Brian Haley305d5522008-11-04 17:51:14 -0800474 return NULL;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900475 }
476
477 skb_reserve(skb, LL_RESERVED_SPACE(dev));
478 ip6_nd_hdr(sk, skb, dev, saddr, daddr, IPPROTO_ICMPV6, len);
479
480 skb->transport_header = skb->tail;
481 skb_put(skb, len);
482
483 hdr = (struct icmp6hdr *)skb_transport_header(skb);
484 memcpy(hdr, icmp6h, sizeof(*hdr));
485
486 opt = skb_transport_header(skb) + sizeof(struct icmp6hdr);
487 if (target) {
488 ipv6_addr_copy((struct in6_addr *)opt, target);
489 opt += sizeof(*target);
490 }
491
492 if (llinfo)
493 ndisc_fill_addr_option(opt, llinfo, dev->dev_addr,
494 dev->addr_len, dev->type);
495
496 hdr->icmp6_cksum = csum_ipv6_magic(saddr, daddr, len,
497 IPPROTO_ICMPV6,
Joe Perches07f07572008-11-19 15:44:53 -0800498 csum_partial(hdr,
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900499 len, 0));
500
Brian Haley305d5522008-11-04 17:51:14 -0800501 return skb;
502}
503
504EXPORT_SYMBOL(ndisc_build_skb);
505
506void ndisc_send_skb(struct sk_buff *skb,
507 struct net_device *dev,
508 struct neighbour *neigh,
509 const struct in6_addr *daddr,
510 const struct in6_addr *saddr,
511 struct icmp6hdr *icmp6h)
512{
David S. Miller4c9483b2011-03-12 16:22:43 -0500513 struct flowi6 fl6;
Brian Haley305d5522008-11-04 17:51:14 -0800514 struct dst_entry *dst;
515 struct net *net = dev_net(dev);
516 struct sock *sk = net->ipv6.ndisc_sk;
517 struct inet6_dev *idev;
518 int err;
519 u8 type;
520
521 type = icmp6h->icmp6_type;
522
David S. Miller4c9483b2011-03-12 16:22:43 -0500523 icmpv6_flow_init(sk, &fl6, type, saddr, daddr, dev->ifindex);
Brian Haley305d5522008-11-04 17:51:14 -0800524
525 dst = icmp6_dst_alloc(dev, neigh, daddr);
526 if (!dst) {
527 kfree_skb(skb);
528 return;
529 }
530
David S. Miller4c9483b2011-03-12 16:22:43 -0500531 dst = xfrm_lookup(net, dst, flowi6_to_flowi(&fl6), NULL, 0);
David S. Miller452edd52011-03-02 13:27:41 -0800532 if (IS_ERR(dst)) {
Brian Haley305d5522008-11-04 17:51:14 -0800533 kfree_skb(skb);
534 return;
535 }
536
Eric Dumazetadf30902009-06-02 05:19:30 +0000537 skb_dst_set(skb, dst);
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900538
539 idev = in6_dev_get(dst->dev);
Neil Hormanedf391f2009-04-27 02:45:02 -0700540 IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUT, skb->len);
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900541
Jan Engelhardtb2e0b382010-03-23 04:09:07 +0100542 err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, skb, NULL, dst->dev,
Patrick McHardy6e23ae22007-11-19 18:53:30 -0800543 dst_output);
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900544 if (!err) {
Denis V. Lunev5c5d2442008-10-08 10:33:50 -0700545 ICMP6MSGOUT_INC_STATS(net, idev, type);
Denis V. Luneva862f6a2008-10-08 10:33:06 -0700546 ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS);
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900547 }
548
549 if (likely(idev != NULL))
550 in6_dev_put(idev);
551}
552
Brian Haley305d5522008-11-04 17:51:14 -0800553EXPORT_SYMBOL(ndisc_send_skb);
554
555/*
556 * Send a Neighbour Discover packet
557 */
558static void __ndisc_send(struct net_device *dev,
559 struct neighbour *neigh,
560 const struct in6_addr *daddr,
561 const struct in6_addr *saddr,
562 struct icmp6hdr *icmp6h, const struct in6_addr *target,
563 int llinfo)
564{
565 struct sk_buff *skb;
566
567 skb = ndisc_build_skb(dev, daddr, saddr, icmp6h, target, llinfo);
568 if (!skb)
569 return;
570
571 ndisc_send_skb(skb, dev, neigh, daddr, saddr, icmp6h);
572}
573
Linus Torvalds1da177e2005-04-16 15:20:36 -0700574static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +0900575 const struct in6_addr *daddr,
576 const struct in6_addr *solicited_addr,
577 int router, int solicited, int override, int inc_opt)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578{
579 struct in6_addr tmpaddr;
580 struct inet6_ifaddr *ifp;
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +0900581 const struct in6_addr *src_addr;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900582 struct icmp6hdr icmp6h = {
583 .icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT,
584 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700585
586 /* for anycast or proxy, solicited_addr != src_addr */
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900587 ifp = ipv6_get_ifaddr(dev_net(dev), solicited_addr, dev, 1);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900588 if (ifp) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700589 src_addr = solicited_addr;
Neil Horman95c385b2007-04-25 17:08:10 -0700590 if (ifp->flags & IFA_F_OPTIMISTIC)
591 override = 0;
stephen hemminger9f888162010-06-21 11:00:13 +0000592 inc_opt |= ifp->idev->cnf.force_tllao;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700593 in6_ifa_put(ifp);
594 } else {
Brian Haley191cd582008-08-14 15:33:21 -0700595 if (ipv6_dev_get_saddr(dev_net(dev), dev, daddr,
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900596 inet6_sk(dev_net(dev)->ipv6.ndisc_sk)->srcprefs,
YOSHIFUJI Hideaki7cbca672008-03-25 09:37:42 +0900597 &tmpaddr))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700598 return;
599 src_addr = &tmpaddr;
600 }
601
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900602 icmp6h.icmp6_router = router;
603 icmp6h.icmp6_solicited = solicited;
604 icmp6h.icmp6_override = override;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700605
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900606 __ndisc_send(dev, neigh, daddr, src_addr,
607 &icmp6h, solicited_addr,
David L Stevens14878f72007-09-16 16:52:35 -0700608 inc_opt ? ND_OPT_TARGET_LL_ADDR : 0);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900609}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700610
Ben Hutchingsf47b9462011-04-15 13:46:02 +0000611static void ndisc_send_unsol_na(struct net_device *dev)
612{
613 struct inet6_dev *idev;
614 struct inet6_ifaddr *ifa;
615 struct in6_addr mcaddr;
616
617 idev = in6_dev_get(dev);
618 if (!idev)
619 return;
620
621 read_lock_bh(&idev->lock);
622 list_for_each_entry(ifa, &idev->addr_list, if_list) {
623 addrconf_addr_solict_mult(&ifa->addr, &mcaddr);
624 ndisc_send_na(dev, NULL, &mcaddr, &ifa->addr,
625 /*router=*/ !!idev->cnf.forwarding,
626 /*solicited=*/ false, /*override=*/ true,
627 /*inc_opt=*/ true);
628 }
629 read_unlock_bh(&idev->lock);
630
631 in6_dev_put(idev);
632}
633
Linus Torvalds1da177e2005-04-16 15:20:36 -0700634void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +0900635 const struct in6_addr *solicit,
636 const struct in6_addr *daddr, const struct in6_addr *saddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700637{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700638 struct in6_addr addr_buf;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900639 struct icmp6hdr icmp6h = {
640 .icmp6_type = NDISC_NEIGHBOUR_SOLICITATION,
641 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700642
643 if (saddr == NULL) {
Neil Horman95c385b2007-04-25 17:08:10 -0700644 if (ipv6_get_lladdr(dev, &addr_buf,
645 (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700646 return;
647 saddr = &addr_buf;
648 }
649
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900650 __ndisc_send(dev, neigh, daddr, saddr,
651 &icmp6h, solicit,
David L Stevens14878f72007-09-16 16:52:35 -0700652 !ipv6_addr_any(saddr) ? ND_OPT_SOURCE_LL_ADDR : 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700653}
654
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +0900655void ndisc_send_rs(struct net_device *dev, const struct in6_addr *saddr,
656 const struct in6_addr *daddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700657{
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900658 struct icmp6hdr icmp6h = {
659 .icmp6_type = NDISC_ROUTER_SOLICITATION,
660 };
Neil Horman95c385b2007-04-25 17:08:10 -0700661 int send_sllao = dev->addr_len;
Neil Horman95c385b2007-04-25 17:08:10 -0700662
663#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
664 /*
665 * According to section 2.2 of RFC 4429, we must not
666 * send router solicitations with a sllao from
667 * optimistic addresses, but we may send the solicitation
668 * if we don't include the sllao. So here we check
669 * if our address is optimistic, and if so, we
Joe Perchesbea85192007-12-20 14:01:35 -0800670 * suppress the inclusion of the sllao.
Neil Horman95c385b2007-04-25 17:08:10 -0700671 */
672 if (send_sllao) {
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900673 struct inet6_ifaddr *ifp = ipv6_get_ifaddr(dev_net(dev), saddr,
Daniel Lezcano1cab3da2008-01-10 22:44:09 -0800674 dev, 1);
Neil Horman95c385b2007-04-25 17:08:10 -0700675 if (ifp) {
676 if (ifp->flags & IFA_F_OPTIMISTIC) {
YOSHIFUJI Hideakica043562007-02-28 23:13:20 +0900677 send_sllao = 0;
Neil Horman95c385b2007-04-25 17:08:10 -0700678 }
YOSHIFUJI Hideakica043562007-02-28 23:13:20 +0900679 in6_ifa_put(ifp);
Neil Horman95c385b2007-04-25 17:08:10 -0700680 } else {
681 send_sllao = 0;
682 }
683 }
684#endif
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900685 __ndisc_send(dev, NULL, daddr, saddr,
686 &icmp6h, NULL,
David L Stevens14878f72007-09-16 16:52:35 -0700687 send_sllao ? ND_OPT_SOURCE_LL_ADDR : 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700688}
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900689
Linus Torvalds1da177e2005-04-16 15:20:36 -0700690
691static void ndisc_error_report(struct neighbour *neigh, struct sk_buff *skb)
692{
693 /*
694 * "The sender MUST return an ICMP
695 * destination unreachable"
696 */
697 dst_link_failure(skb);
698 kfree_skb(skb);
699}
700
701/* Called with locked neigh: either read or both */
702
703static void ndisc_solicit(struct neighbour *neigh, struct sk_buff *skb)
704{
705 struct in6_addr *saddr = NULL;
706 struct in6_addr mcaddr;
707 struct net_device *dev = neigh->dev;
708 struct in6_addr *target = (struct in6_addr *)&neigh->primary_key;
709 int probes = atomic_read(&neigh->probes);
710
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900711 if (skb && ipv6_chk_addr(dev_net(dev), &ipv6_hdr(skb)->saddr, dev, 1))
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -0700712 saddr = &ipv6_hdr(skb)->saddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700713
714 if ((probes -= neigh->parms->ucast_probes) < 0) {
715 if (!(neigh->nud_state & NUD_VALID)) {
Harvey Harrison5b095d9892008-10-29 12:52:50 -0700716 ND_PRINTK1(KERN_DEBUG "%s(): trying to ucast probe in NUD_INVALID: %pI6\n",
Harvey Harrison0c6ce782008-10-28 16:09:23 -0700717 __func__, target);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700718 }
719 ndisc_send_ns(dev, neigh, target, target, saddr);
720 } else if ((probes -= neigh->parms->app_probes) < 0) {
721#ifdef CONFIG_ARPD
722 neigh_app_ns(neigh);
723#endif
724 } else {
725 addrconf_addr_solict_mult(target, &mcaddr);
726 ndisc_send_ns(dev, NULL, target, &mcaddr, saddr);
727 }
728}
729
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900730static int pndisc_is_router(const void *pkey,
731 struct net_device *dev)
Pavel Emelyanovfa86d322008-03-24 14:48:59 -0700732{
733 struct pneigh_entry *n;
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900734 int ret = -1;
Pavel Emelyanovfa86d322008-03-24 14:48:59 -0700735
736 read_lock_bh(&nd_tbl.lock);
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900737 n = __pneigh_lookup(&nd_tbl, dev_net(dev), pkey, dev);
738 if (n)
739 ret = !!(n->flags & NTF_ROUTER);
Pavel Emelyanovfa86d322008-03-24 14:48:59 -0700740 read_unlock_bh(&nd_tbl.lock);
741
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900742 return ret;
Pavel Emelyanovfa86d322008-03-24 14:48:59 -0700743}
744
Linus Torvalds1da177e2005-04-16 15:20:36 -0700745static void ndisc_recv_ns(struct sk_buff *skb)
746{
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -0700747 struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb);
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000748 const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
749 const struct in6_addr *daddr = &ipv6_hdr(skb)->daddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700750 u8 *lladdr = NULL;
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -0700751 u32 ndoptlen = skb->tail - (skb->transport_header +
752 offsetof(struct nd_msg, opt));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700753 struct ndisc_options ndopts;
754 struct net_device *dev = skb->dev;
755 struct inet6_ifaddr *ifp;
756 struct inet6_dev *idev = NULL;
757 struct neighbour *neigh;
758 int dad = ipv6_addr_any(saddr);
759 int inc;
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900760 int is_router = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700761
762 if (ipv6_addr_is_multicast(&msg->target)) {
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900763 ND_PRINTK2(KERN_WARNING
Linus Torvalds1da177e2005-04-16 15:20:36 -0700764 "ICMPv6 NS: multicast target address");
765 return;
766 }
767
768 /*
769 * RFC2461 7.1.1:
770 * DAD has to be destined for solicited node multicast address.
771 */
772 if (dad &&
773 !(daddr->s6_addr32[0] == htonl(0xff020000) &&
774 daddr->s6_addr32[1] == htonl(0x00000000) &&
775 daddr->s6_addr32[2] == htonl(0x00000001) &&
776 daddr->s6_addr [12] == 0xff )) {
777 ND_PRINTK2(KERN_WARNING
778 "ICMPv6 NS: bad DAD packet (wrong destination)\n");
779 return;
780 }
781
782 if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts)) {
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900783 ND_PRINTK2(KERN_WARNING
Linus Torvalds1da177e2005-04-16 15:20:36 -0700784 "ICMPv6 NS: invalid ND options\n");
785 return;
786 }
787
788 if (ndopts.nd_opts_src_lladdr) {
789 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr, dev);
790 if (!lladdr) {
791 ND_PRINTK2(KERN_WARNING
792 "ICMPv6 NS: invalid link-layer address length\n");
793 return;
794 }
795
796 /* RFC2461 7.1.1:
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900797 * If the IP source address is the unspecified address,
798 * there MUST NOT be source link-layer address option
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799 * in the message.
800 */
801 if (dad) {
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900802 ND_PRINTK2(KERN_WARNING
Linus Torvalds1da177e2005-04-16 15:20:36 -0700803 "ICMPv6 NS: bad DAD packet (link-layer address option)\n");
804 return;
805 }
806 }
807
808 inc = ipv6_addr_is_multicast(daddr);
809
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900810 ifp = ipv6_get_ifaddr(dev_net(dev), &msg->target, dev, 1);
Daniel Lezcanoa18bc692008-03-07 11:14:49 -0800811 if (ifp) {
Neil Horman95c385b2007-04-25 17:08:10 -0700812
813 if (ifp->flags & (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)) {
814 if (dad) {
815 if (dev->type == ARPHRD_IEEE802_TR) {
Arnaldo Carvalho de Melo98e399f2007-03-19 15:33:04 -0700816 const unsigned char *sadr;
817 sadr = skb_mac_header(skb);
Neil Horman95c385b2007-04-25 17:08:10 -0700818 if (((sadr[8] ^ dev->dev_addr[0]) & 0x7f) == 0 &&
819 sadr[9] == dev->dev_addr[1] &&
820 sadr[10] == dev->dev_addr[2] &&
821 sadr[11] == dev->dev_addr[3] &&
822 sadr[12] == dev->dev_addr[4] &&
823 sadr[13] == dev->dev_addr[5]) {
824 /* looped-back to us */
825 goto out;
826 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700827 }
Neil Horman95c385b2007-04-25 17:08:10 -0700828
829 /*
830 * We are colliding with another node
831 * who is doing DAD
832 * so fail our DAD process
833 */
834 addrconf_dad_failure(ifp);
Denis V. Lunev9e3be4b2007-09-11 11:04:49 +0200835 return;
Neil Horman95c385b2007-04-25 17:08:10 -0700836 } else {
837 /*
838 * This is not a dad solicitation.
839 * If we are an optimistic node,
840 * we should respond.
841 * Otherwise, we should ignore it.
842 */
843 if (!(ifp->flags & IFA_F_OPTIMISTIC))
844 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700845 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700846 }
847
848 idev = ifp->idev;
849 } else {
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700850 struct net *net = dev_net(dev);
851
Linus Torvalds1da177e2005-04-16 15:20:36 -0700852 idev = in6_dev_get(dev);
853 if (!idev) {
854 /* XXX: count this drop? */
855 return;
856 }
857
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700858 if (ipv6_chk_acast_addr(net, dev, &msg->target) ||
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900859 (idev->cnf.forwarding &&
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700860 (net->ipv6.devconf_all->proxy_ndp || idev->cnf.proxy_ndp) &&
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900861 (is_router = pndisc_is_router(&msg->target, dev)) >= 0)) {
Patrick McHardya61bbcf2005-08-14 17:24:31 -0700862 if (!(NEIGH_CB(skb)->flags & LOCALLY_ENQUEUED) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700863 skb->pkt_type != PACKET_HOST &&
864 inc != 0 &&
865 idev->nd_parms->proxy_delay != 0) {
866 /*
867 * for anycast or proxy,
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900868 * sender should delay its response
869 * by a random time between 0 and
Linus Torvalds1da177e2005-04-16 15:20:36 -0700870 * MAX_ANYCAST_DELAY_TIME seconds.
871 * (RFC2461) -- yoshfuji
872 */
873 struct sk_buff *n = skb_clone(skb, GFP_ATOMIC);
874 if (n)
875 pneigh_enqueue(&nd_tbl, idev->nd_parms, n);
876 goto out;
877 }
878 } else
879 goto out;
880 }
881
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900882 if (is_router < 0)
883 is_router = !!idev->cnf.forwarding;
Ville Nuorvala62dd9312006-09-22 14:43:19 -0700884
Linus Torvalds1da177e2005-04-16 15:20:36 -0700885 if (dad) {
YOSHIFUJI Hideakif3ee4012008-04-10 15:42:11 +0900886 ndisc_send_na(dev, NULL, &in6addr_linklocal_allnodes, &msg->target,
Ville Nuorvala62dd9312006-09-22 14:43:19 -0700887 is_router, 0, (ifp != NULL), 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700888 goto out;
889 }
890
891 if (inc)
892 NEIGH_CACHE_STAT_INC(&nd_tbl, rcv_probes_mcast);
893 else
894 NEIGH_CACHE_STAT_INC(&nd_tbl, rcv_probes_ucast);
895
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900896 /*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700897 * update / create cache entry
898 * for the source address
899 */
900 neigh = __neigh_lookup(&nd_tbl, saddr, dev,
901 !inc || lladdr || !dev->addr_len);
902 if (neigh)
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900903 neigh_update(neigh, lladdr, NUD_STALE,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700904 NEIGH_UPDATE_F_WEAK_OVERRIDE|
905 NEIGH_UPDATE_F_OVERRIDE);
Stephen Hemminger3b04ddd2007-10-09 01:40:57 -0700906 if (neigh || !dev->header_ops) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700907 ndisc_send_na(dev, neigh, saddr, &msg->target,
Ville Nuorvala62dd9312006-09-22 14:43:19 -0700908 is_router,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700909 1, (ifp != NULL && inc), inc);
910 if (neigh)
911 neigh_release(neigh);
912 }
913
914out:
915 if (ifp)
916 in6_ifa_put(ifp);
917 else
918 in6_dev_put(idev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700919}
920
921static void ndisc_recv_na(struct sk_buff *skb)
922{
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -0700923 struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb);
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000924 const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
925 const struct in6_addr *daddr = &ipv6_hdr(skb)->daddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700926 u8 *lladdr = NULL;
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -0700927 u32 ndoptlen = skb->tail - (skb->transport_header +
928 offsetof(struct nd_msg, opt));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700929 struct ndisc_options ndopts;
930 struct net_device *dev = skb->dev;
931 struct inet6_ifaddr *ifp;
932 struct neighbour *neigh;
933
934 if (skb->len < sizeof(struct nd_msg)) {
935 ND_PRINTK2(KERN_WARNING
936 "ICMPv6 NA: packet too short\n");
937 return;
938 }
939
940 if (ipv6_addr_is_multicast(&msg->target)) {
941 ND_PRINTK2(KERN_WARNING
942 "ICMPv6 NA: target address is multicast.\n");
943 return;
944 }
945
946 if (ipv6_addr_is_multicast(daddr) &&
947 msg->icmph.icmp6_solicited) {
948 ND_PRINTK2(KERN_WARNING
949 "ICMPv6 NA: solicited NA is multicasted.\n");
950 return;
951 }
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900952
Linus Torvalds1da177e2005-04-16 15:20:36 -0700953 if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts)) {
954 ND_PRINTK2(KERN_WARNING
955 "ICMPv6 NS: invalid ND option\n");
956 return;
957 }
958 if (ndopts.nd_opts_tgt_lladdr) {
959 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr, dev);
960 if (!lladdr) {
961 ND_PRINTK2(KERN_WARNING
962 "ICMPv6 NA: invalid link-layer address length\n");
963 return;
964 }
965 }
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900966 ifp = ipv6_get_ifaddr(dev_net(dev), &msg->target, dev, 1);
Daniel Lezcanoa18bc692008-03-07 11:14:49 -0800967 if (ifp) {
Daniel Walterbd015922011-04-13 21:09:25 +0000968 if (skb->pkt_type != PACKET_LOOPBACK
969 && (ifp->flags & IFA_F_TENTATIVE)) {
970 addrconf_dad_failure(ifp);
971 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700972 }
973 /* What should we make now? The advertisement
974 is invalid, but ndisc specs say nothing
975 about it. It could be misconfiguration, or
976 an smart proxy agent tries to help us :-)
Jan Sembera24fc7b82008-12-09 15:48:32 -0800977
978 We should not print the error if NA has been
979 received from loopback - it is just our own
980 unsolicited advertisement.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700981 */
Jan Sembera24fc7b82008-12-09 15:48:32 -0800982 if (skb->pkt_type != PACKET_LOOPBACK)
983 ND_PRINTK1(KERN_WARNING
Jens Rosenbooma6fa3282009-08-12 22:16:04 +0000984 "ICMPv6 NA: someone advertises our address %pI6 on %s!\n",
985 &ifp->addr, ifp->idev->dev->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700986 in6_ifa_put(ifp);
987 return;
988 }
989 neigh = neigh_lookup(&nd_tbl, &msg->target, dev);
990
991 if (neigh) {
992 u8 old_flags = neigh->flags;
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700993 struct net *net = dev_net(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700994
995 if (neigh->nud_state & NUD_FAILED)
996 goto out;
997
Ville Nuorvala5f3e6e92006-09-22 14:42:46 -0700998 /*
999 * Don't update the neighbor cache entry on a proxy NA from
1000 * ourselves because either the proxied node is off link or it
1001 * has already sent a NA to us.
1002 */
1003 if (lladdr && !memcmp(lladdr, dev->dev_addr, dev->addr_len) &&
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -07001004 net->ipv6.devconf_all->forwarding && net->ipv6.devconf_all->proxy_ndp &&
1005 pneigh_lookup(&nd_tbl, net, &msg->target, dev, 0)) {
YOSHIFUJI Hideakifbea49e2006-09-22 14:43:49 -07001006 /* XXX: idev->cnf.prixy_ndp */
Ville Nuorvala5f3e6e92006-09-22 14:42:46 -07001007 goto out;
YOSHIFUJI Hideakifbea49e2006-09-22 14:43:49 -07001008 }
Ville Nuorvala5f3e6e92006-09-22 14:42:46 -07001009
Linus Torvalds1da177e2005-04-16 15:20:36 -07001010 neigh_update(neigh, lladdr,
1011 msg->icmph.icmp6_solicited ? NUD_REACHABLE : NUD_STALE,
1012 NEIGH_UPDATE_F_WEAK_OVERRIDE|
1013 (msg->icmph.icmp6_override ? NEIGH_UPDATE_F_OVERRIDE : 0)|
1014 NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
1015 (msg->icmph.icmp6_router ? NEIGH_UPDATE_F_ISROUTER : 0));
1016
1017 if ((old_flags & ~neigh->flags) & NTF_ROUTER) {
1018 /*
1019 * Change: router to host
1020 */
1021 struct rt6_info *rt;
1022 rt = rt6_get_dflt_router(saddr, dev);
1023 if (rt)
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001024 ip6_del_rt(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001025 }
1026
1027out:
1028 neigh_release(neigh);
1029 }
1030}
1031
1032static void ndisc_recv_rs(struct sk_buff *skb)
1033{
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -07001034 struct rs_msg *rs_msg = (struct rs_msg *)skb_transport_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001035 unsigned long ndoptlen = skb->len - sizeof(*rs_msg);
1036 struct neighbour *neigh;
1037 struct inet6_dev *idev;
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001038 const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001039 struct ndisc_options ndopts;
1040 u8 *lladdr = NULL;
1041
1042 if (skb->len < sizeof(*rs_msg))
1043 return;
1044
1045 idev = in6_dev_get(skb->dev);
1046 if (!idev) {
1047 if (net_ratelimit())
1048 ND_PRINTK1("ICMP6 RS: can't find in6 device\n");
1049 return;
1050 }
1051
1052 /* Don't accept RS if we're not in router mode */
1053 if (!idev->cnf.forwarding)
1054 goto out;
1055
1056 /*
1057 * Don't update NCE if src = ::;
1058 * this implies that the source node has no ip address assigned yet.
1059 */
1060 if (ipv6_addr_any(saddr))
1061 goto out;
1062
1063 /* Parse ND options */
1064 if (!ndisc_parse_options(rs_msg->opt, ndoptlen, &ndopts)) {
1065 if (net_ratelimit())
1066 ND_PRINTK2("ICMP6 NS: invalid ND option, ignored\n");
1067 goto out;
1068 }
1069
1070 if (ndopts.nd_opts_src_lladdr) {
1071 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr,
1072 skb->dev);
1073 if (!lladdr)
1074 goto out;
1075 }
1076
1077 neigh = __neigh_lookup(&nd_tbl, saddr, skb->dev, 1);
1078 if (neigh) {
1079 neigh_update(neigh, lladdr, NUD_STALE,
1080 NEIGH_UPDATE_F_WEAK_OVERRIDE|
1081 NEIGH_UPDATE_F_OVERRIDE|
1082 NEIGH_UPDATE_F_OVERRIDE_ISROUTER);
1083 neigh_release(neigh);
1084 }
1085out:
1086 in6_dev_put(idev);
1087}
1088
Pierre Ynard31910572007-10-10 21:22:05 -07001089static void ndisc_ra_useropt(struct sk_buff *ra, struct nd_opt_hdr *opt)
1090{
1091 struct icmp6hdr *icmp6h = (struct icmp6hdr *)skb_transport_header(ra);
1092 struct sk_buff *skb;
1093 struct nlmsghdr *nlh;
1094 struct nduseroptmsg *ndmsg;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001095 struct net *net = dev_net(ra->dev);
Pierre Ynard31910572007-10-10 21:22:05 -07001096 int err;
1097 int base_size = NLMSG_ALIGN(sizeof(struct nduseroptmsg)
1098 + (opt->nd_opt_len << 3));
1099 size_t msg_size = base_size + nla_total_size(sizeof(struct in6_addr));
1100
1101 skb = nlmsg_new(msg_size, GFP_ATOMIC);
1102 if (skb == NULL) {
1103 err = -ENOBUFS;
1104 goto errout;
1105 }
1106
1107 nlh = nlmsg_put(skb, 0, 0, RTM_NEWNDUSEROPT, base_size, 0);
1108 if (nlh == NULL) {
1109 goto nla_put_failure;
1110 }
1111
1112 ndmsg = nlmsg_data(nlh);
1113 ndmsg->nduseropt_family = AF_INET6;
Pierre Ynarddbb2ed22007-11-12 17:58:35 -08001114 ndmsg->nduseropt_ifindex = ra->dev->ifindex;
Pierre Ynard31910572007-10-10 21:22:05 -07001115 ndmsg->nduseropt_icmp_type = icmp6h->icmp6_type;
1116 ndmsg->nduseropt_icmp_code = icmp6h->icmp6_code;
1117 ndmsg->nduseropt_opts_len = opt->nd_opt_len << 3;
1118
1119 memcpy(ndmsg + 1, opt, opt->nd_opt_len << 3);
1120
1121 NLA_PUT(skb, NDUSEROPT_SRCADDR, sizeof(struct in6_addr),
1122 &ipv6_hdr(ra)->saddr);
1123 nlmsg_end(skb, nlh);
1124
Pablo Neira Ayuso1ce85fe2009-02-24 23:18:28 -08001125 rtnl_notify(skb, net, 0, RTNLGRP_ND_USEROPT, NULL, GFP_ATOMIC);
Pierre Ynard31910572007-10-10 21:22:05 -07001126 return;
1127
1128nla_put_failure:
1129 nlmsg_free(skb);
1130 err = -EMSGSIZE;
1131errout:
Daniel Lezcanoa18bc692008-03-07 11:14:49 -08001132 rtnl_set_sk_err(net, RTNLGRP_ND_USEROPT, err);
Pierre Ynard31910572007-10-10 21:22:05 -07001133}
1134
Thomas Graf65e9b622010-09-03 02:59:14 +00001135static inline int accept_ra(struct inet6_dev *in6_dev)
1136{
1137 /*
1138 * If forwarding is enabled, RA are not accepted unless the special
1139 * hybrid mode (accept_ra=2) is enabled.
1140 */
1141 if (in6_dev->cnf.forwarding && in6_dev->cnf.accept_ra < 2)
1142 return 0;
1143
1144 return in6_dev->cnf.accept_ra;
1145}
1146
Linus Torvalds1da177e2005-04-16 15:20:36 -07001147static void ndisc_router_discovery(struct sk_buff *skb)
1148{
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -07001149 struct ra_msg *ra_msg = (struct ra_msg *)skb_transport_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001150 struct neighbour *neigh = NULL;
1151 struct inet6_dev *in6_dev;
YOSHIFUJI Hideaki65f5c7c2006-03-20 16:55:08 -08001152 struct rt6_info *rt = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001153 int lifetime;
1154 struct ndisc_options ndopts;
1155 int optlen;
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001156 unsigned int pref = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001157
1158 __u8 * opt = (__u8 *)(ra_msg + 1);
1159
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -07001160 optlen = (skb->tail - skb->transport_header) - sizeof(struct ra_msg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001161
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001162 if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001163 ND_PRINTK2(KERN_WARNING
1164 "ICMPv6 RA: source address is not link-local.\n");
1165 return;
1166 }
1167 if (optlen < 0) {
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001168 ND_PRINTK2(KERN_WARNING
Linus Torvalds1da177e2005-04-16 15:20:36 -07001169 "ICMPv6 RA: packet too short\n");
1170 return;
1171 }
1172
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001173#ifdef CONFIG_IPV6_NDISC_NODETYPE
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001174 if (skb->ndisc_nodetype == NDISC_NODETYPE_HOST) {
1175 ND_PRINTK2(KERN_WARNING
1176 "ICMPv6 RA: from host or unauthorized router\n");
1177 return;
1178 }
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001179#endif
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001180
Linus Torvalds1da177e2005-04-16 15:20:36 -07001181 /*
1182 * set the RA_RECV flag in the interface
1183 */
1184
1185 in6_dev = in6_dev_get(skb->dev);
1186 if (in6_dev == NULL) {
1187 ND_PRINTK0(KERN_ERR
1188 "ICMPv6 RA: can't find inet6 device for %s.\n",
1189 skb->dev->name);
1190 return;
1191 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001192
1193 if (!ndisc_parse_options(opt, optlen, &ndopts)) {
1194 in6_dev_put(in6_dev);
1195 ND_PRINTK2(KERN_WARNING
1196 "ICMP6 RA: invalid ND options\n");
1197 return;
1198 }
1199
Thomas Graf65e9b622010-09-03 02:59:14 +00001200 if (!accept_ra(in6_dev))
David Ward31ce8c72009-08-29 00:04:09 -07001201 goto skip_linkparms;
1202
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001203#ifdef CONFIG_IPV6_NDISC_NODETYPE
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001204 /* skip link-specific parameters from interior routers */
1205 if (skb->ndisc_nodetype == NDISC_NODETYPE_NODEFAULT)
1206 goto skip_linkparms;
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001207#endif
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001208
Linus Torvalds1da177e2005-04-16 15:20:36 -07001209 if (in6_dev->if_flags & IF_RS_SENT) {
1210 /*
1211 * flag that an RA was received after an RS was sent
1212 * out on this interface.
1213 */
1214 in6_dev->if_flags |= IF_RA_RCVD;
1215 }
1216
1217 /*
1218 * Remember the managed/otherconf flags from most recently
1219 * received RA message (RFC 2462) -- yoshfuji
1220 */
1221 in6_dev->if_flags = (in6_dev->if_flags & ~(IF_RA_MANAGED |
1222 IF_RA_OTHERCONF)) |
1223 (ra_msg->icmph.icmp6_addrconf_managed ?
1224 IF_RA_MANAGED : 0) |
1225 (ra_msg->icmph.icmp6_addrconf_other ?
1226 IF_RA_OTHERCONF : 0);
1227
YOSHIFUJI Hideaki65f5c7c2006-03-20 16:55:08 -08001228 if (!in6_dev->cnf.accept_ra_defrtr)
1229 goto skip_defrtr;
1230
Linus Torvalds1da177e2005-04-16 15:20:36 -07001231 lifetime = ntohs(ra_msg->icmph.icmp6_rt_lifetime);
1232
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001233#ifdef CONFIG_IPV6_ROUTER_PREF
1234 pref = ra_msg->icmph.icmp6_router_pref;
1235 /* 10b is handled as if it were 00b (medium) */
YOSHIFUJI Hideaki930d6ff2006-03-20 17:05:30 -08001236 if (pref == ICMPV6_ROUTER_PREF_INVALID ||
YOSHIFUJI Hideaki6d5b78c2007-06-22 16:07:04 -07001237 !in6_dev->cnf.accept_ra_rtr_pref)
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001238 pref = ICMPV6_ROUTER_PREF_MEDIUM;
1239#endif
1240
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001241 rt = rt6_get_dflt_router(&ipv6_hdr(skb)->saddr, skb->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001242
1243 if (rt)
1244 neigh = rt->rt6i_nexthop;
1245
1246 if (rt && lifetime == 0) {
1247 neigh_clone(neigh);
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001248 ip6_del_rt(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001249 rt = NULL;
1250 }
1251
1252 if (rt == NULL && lifetime) {
1253 ND_PRINTK3(KERN_DEBUG
1254 "ICMPv6 RA: adding default router.\n");
1255
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001256 rt = rt6_add_dflt_router(&ipv6_hdr(skb)->saddr, skb->dev, pref);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001257 if (rt == NULL) {
1258 ND_PRINTK0(KERN_ERR
1259 "ICMPv6 RA: %s() failed to add default route.\n",
Harvey Harrison0dc47872008-03-05 20:47:47 -08001260 __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001261 in6_dev_put(in6_dev);
1262 return;
1263 }
1264
1265 neigh = rt->rt6i_nexthop;
1266 if (neigh == NULL) {
1267 ND_PRINTK0(KERN_ERR
1268 "ICMPv6 RA: %s() got default router without neighbour.\n",
Harvey Harrison0dc47872008-03-05 20:47:47 -08001269 __func__);
Changli Gaod8d1f302010-06-10 23:31:35 -07001270 dst_release(&rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001271 in6_dev_put(in6_dev);
1272 return;
1273 }
1274 neigh->flags |= NTF_ROUTER;
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001275 } else if (rt) {
Pedro Ribeiro22441cf2008-10-15 15:47:49 -07001276 rt->rt6i_flags = (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001277 }
1278
1279 if (rt)
1280 rt->rt6i_expires = jiffies + (HZ * lifetime);
1281
1282 if (ra_msg->icmph.icmp6_hop_limit) {
1283 in6_dev->cnf.hop_limit = ra_msg->icmph.icmp6_hop_limit;
1284 if (rt)
David S. Millerdefb3512010-12-08 21:16:57 -08001285 dst_metric_set(&rt->dst, RTAX_HOPLIMIT,
1286 ra_msg->icmph.icmp6_hop_limit);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001287 }
1288
YOSHIFUJI Hideaki65f5c7c2006-03-20 16:55:08 -08001289skip_defrtr:
1290
Linus Torvalds1da177e2005-04-16 15:20:36 -07001291 /*
1292 * Update Reachable Time and Retrans Timer
1293 */
1294
1295 if (in6_dev->nd_parms) {
1296 unsigned long rtime = ntohl(ra_msg->retrans_timer);
1297
1298 if (rtime && rtime/1000 < MAX_SCHEDULE_TIMEOUT/HZ) {
1299 rtime = (rtime*HZ)/1000;
1300 if (rtime < HZ/10)
1301 rtime = HZ/10;
1302 in6_dev->nd_parms->retrans_time = rtime;
1303 in6_dev->tstamp = jiffies;
1304 inet6_ifinfo_notify(RTM_NEWLINK, in6_dev);
1305 }
1306
1307 rtime = ntohl(ra_msg->reachable_time);
1308 if (rtime && rtime/1000 < MAX_SCHEDULE_TIMEOUT/(3*HZ)) {
1309 rtime = (rtime*HZ)/1000;
1310
1311 if (rtime < HZ/10)
1312 rtime = HZ/10;
1313
1314 if (rtime != in6_dev->nd_parms->base_reachable_time) {
1315 in6_dev->nd_parms->base_reachable_time = rtime;
1316 in6_dev->nd_parms->gc_staletime = 3 * rtime;
1317 in6_dev->nd_parms->reachable_time = neigh_rand_reach_time(rtime);
1318 in6_dev->tstamp = jiffies;
1319 inet6_ifinfo_notify(RTM_NEWLINK, in6_dev);
1320 }
1321 }
1322 }
1323
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001324skip_linkparms:
1325
Linus Torvalds1da177e2005-04-16 15:20:36 -07001326 /*
1327 * Process options.
1328 */
1329
1330 if (!neigh)
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001331 neigh = __neigh_lookup(&nd_tbl, &ipv6_hdr(skb)->saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001332 skb->dev, 1);
1333 if (neigh) {
1334 u8 *lladdr = NULL;
1335 if (ndopts.nd_opts_src_lladdr) {
1336 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr,
1337 skb->dev);
1338 if (!lladdr) {
1339 ND_PRINTK2(KERN_WARNING
1340 "ICMPv6 RA: invalid link-layer address length\n");
1341 goto out;
1342 }
1343 }
1344 neigh_update(neigh, lladdr, NUD_STALE,
1345 NEIGH_UPDATE_F_WEAK_OVERRIDE|
1346 NEIGH_UPDATE_F_OVERRIDE|
1347 NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
1348 NEIGH_UPDATE_F_ISROUTER);
1349 }
1350
Thomas Graf65e9b622010-09-03 02:59:14 +00001351 if (!accept_ra(in6_dev))
David Ward31ce8c72009-08-29 00:04:09 -07001352 goto out;
1353
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001354#ifdef CONFIG_IPV6_ROUTE_INFO
YOSHIFUJI Hideaki09c884d2006-03-20 17:07:03 -08001355 if (in6_dev->cnf.accept_ra_rtr_pref && ndopts.nd_opts_ri) {
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001356 struct nd_opt_hdr *p;
1357 for (p = ndopts.nd_opts_ri;
1358 p;
1359 p = ndisc_next_option(p, ndopts.nd_opts_ri_end)) {
YOSHIFUJI Hideaki6294e002008-03-15 23:56:52 -04001360 struct route_info *ri = (struct route_info *)p;
1361#ifdef CONFIG_IPV6_NDISC_NODETYPE
1362 if (skb->ndisc_nodetype == NDISC_NODETYPE_NODEFAULT &&
1363 ri->prefix_len == 0)
1364 continue;
1365#endif
1366 if (ri->prefix_len > in6_dev->cnf.accept_ra_rt_info_max_plen)
YOSHIFUJI Hideaki09c884d2006-03-20 17:07:03 -08001367 continue;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001368 rt6_route_rcv(skb->dev, (u8*)p, (p->nd_opt_len) << 3,
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001369 &ipv6_hdr(skb)->saddr);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001370 }
1371 }
1372#endif
1373
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001374#ifdef CONFIG_IPV6_NDISC_NODETYPE
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001375 /* skip link-specific ndopts from interior routers */
1376 if (skb->ndisc_nodetype == NDISC_NODETYPE_NODEFAULT)
1377 goto out;
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001378#endif
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001379
YOSHIFUJI Hideakic4fd30e2006-03-20 16:55:26 -08001380 if (in6_dev->cnf.accept_ra_pinfo && ndopts.nd_opts_pi) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001381 struct nd_opt_hdr *p;
1382 for (p = ndopts.nd_opts_pi;
1383 p;
1384 p = ndisc_next_option(p, ndopts.nd_opts_pi_end)) {
1385 addrconf_prefix_rcv(skb->dev, (u8*)p, (p->nd_opt_len) << 3);
1386 }
1387 }
1388
1389 if (ndopts.nd_opts_mtu) {
Al Viroe69a4ad2006-11-14 20:56:00 -08001390 __be32 n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001391 u32 mtu;
1392
Al Viroe69a4ad2006-11-14 20:56:00 -08001393 memcpy(&n, ((u8*)(ndopts.nd_opts_mtu+1))+2, sizeof(mtu));
1394 mtu = ntohl(n);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001395
1396 if (mtu < IPV6_MIN_MTU || mtu > skb->dev->mtu) {
1397 ND_PRINTK2(KERN_WARNING
1398 "ICMPv6 RA: invalid mtu: %d\n",
1399 mtu);
1400 } else if (in6_dev->cnf.mtu6 != mtu) {
1401 in6_dev->cnf.mtu6 = mtu;
1402
1403 if (rt)
David S. Millerdefb3512010-12-08 21:16:57 -08001404 dst_metric_set(&rt->dst, RTAX_MTU, mtu);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001405
1406 rt6_mtu_change(skb->dev, mtu);
1407 }
1408 }
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001409
Pierre Ynard31910572007-10-10 21:22:05 -07001410 if (ndopts.nd_useropts) {
YOSHIFUJI Hideaki61cf46a2008-01-22 17:32:53 +09001411 struct nd_opt_hdr *p;
1412 for (p = ndopts.nd_useropts;
1413 p;
1414 p = ndisc_next_useropt(p, ndopts.nd_useropts_end)) {
1415 ndisc_ra_useropt(skb, p);
Pierre Ynard31910572007-10-10 21:22:05 -07001416 }
1417 }
1418
Linus Torvalds1da177e2005-04-16 15:20:36 -07001419 if (ndopts.nd_opts_tgt_lladdr || ndopts.nd_opts_rh) {
1420 ND_PRINTK2(KERN_WARNING
1421 "ICMPv6 RA: invalid RA options");
1422 }
1423out:
1424 if (rt)
Changli Gaod8d1f302010-06-10 23:31:35 -07001425 dst_release(&rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001426 else if (neigh)
1427 neigh_release(neigh);
1428 in6_dev_put(in6_dev);
1429}
1430
1431static void ndisc_redirect_rcv(struct sk_buff *skb)
1432{
1433 struct inet6_dev *in6_dev;
1434 struct icmp6hdr *icmph;
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001435 const struct in6_addr *dest;
1436 const struct in6_addr *target; /* new first hop to destination */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001437 struct neighbour *neigh;
1438 int on_link = 0;
1439 struct ndisc_options ndopts;
1440 int optlen;
1441 u8 *lladdr = NULL;
1442
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001443#ifdef CONFIG_IPV6_NDISC_NODETYPE
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001444 switch (skb->ndisc_nodetype) {
1445 case NDISC_NODETYPE_HOST:
1446 case NDISC_NODETYPE_NODEFAULT:
1447 ND_PRINTK2(KERN_WARNING
1448 "ICMPv6 Redirect: from host or unauthorized router\n");
1449 return;
1450 }
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001451#endif
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001452
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001453 if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001454 ND_PRINTK2(KERN_WARNING
1455 "ICMPv6 Redirect: source address is not link-local.\n");
1456 return;
1457 }
1458
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -07001459 optlen = skb->tail - skb->transport_header;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001460 optlen -= sizeof(struct icmp6hdr) + 2 * sizeof(struct in6_addr);
1461
1462 if (optlen < 0) {
1463 ND_PRINTK2(KERN_WARNING
1464 "ICMPv6 Redirect: packet too short\n");
1465 return;
1466 }
1467
Arnaldo Carvalho de Melocc70ab22007-03-13 14:03:22 -03001468 icmph = icmp6_hdr(skb);
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001469 target = (const struct in6_addr *) (icmph + 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001470 dest = target + 1;
1471
1472 if (ipv6_addr_is_multicast(dest)) {
1473 ND_PRINTK2(KERN_WARNING
1474 "ICMPv6 Redirect: destination address is multicast.\n");
1475 return;
1476 }
1477
1478 if (ipv6_addr_equal(dest, target)) {
1479 on_link = 1;
Brian Haleybf0b48d2007-10-08 00:12:05 -07001480 } else if (ipv6_addr_type(target) !=
1481 (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) {
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001482 ND_PRINTK2(KERN_WARNING
Brian Haleybf0b48d2007-10-08 00:12:05 -07001483 "ICMPv6 Redirect: target address is not link-local unicast.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001484 return;
1485 }
1486
1487 in6_dev = in6_dev_get(skb->dev);
1488 if (!in6_dev)
1489 return;
1490 if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects) {
1491 in6_dev_put(in6_dev);
1492 return;
1493 }
1494
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001495 /* RFC2461 8.1:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001496 * The IP source address of the Redirect MUST be the same as the current
1497 * first-hop router for the specified ICMP Destination Address.
1498 */
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001499
Linus Torvalds1da177e2005-04-16 15:20:36 -07001500 if (!ndisc_parse_options((u8*)(dest + 1), optlen, &ndopts)) {
1501 ND_PRINTK2(KERN_WARNING
1502 "ICMPv6 Redirect: invalid ND options\n");
1503 in6_dev_put(in6_dev);
1504 return;
1505 }
1506 if (ndopts.nd_opts_tgt_lladdr) {
1507 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr,
1508 skb->dev);
1509 if (!lladdr) {
1510 ND_PRINTK2(KERN_WARNING
1511 "ICMPv6 Redirect: invalid link-layer address length\n");
1512 in6_dev_put(in6_dev);
1513 return;
1514 }
1515 }
1516
1517 neigh = __neigh_lookup(&nd_tbl, target, skb->dev, 1);
1518 if (neigh) {
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001519 rt6_redirect(dest, &ipv6_hdr(skb)->daddr,
1520 &ipv6_hdr(skb)->saddr, neigh, lladdr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001521 on_link);
1522 neigh_release(neigh);
1523 }
1524 in6_dev_put(in6_dev);
1525}
1526
1527void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +09001528 const struct in6_addr *target)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001529{
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001530 struct net_device *dev = skb->dev;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001531 struct net *net = dev_net(dev);
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001532 struct sock *sk = net->ipv6.ndisc_sk;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001533 int len = sizeof(struct icmp6hdr) + 2 * sizeof(struct in6_addr);
1534 struct sk_buff *buff;
1535 struct icmp6hdr *icmph;
1536 struct in6_addr saddr_buf;
1537 struct in6_addr *addrp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001538 struct rt6_info *rt;
1539 struct dst_entry *dst;
1540 struct inet6_dev *idev;
David S. Miller4c9483b2011-03-12 16:22:43 -05001541 struct flowi6 fl6;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001542 u8 *opt;
1543 int rd_len;
1544 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001545 u8 ha_buf[MAX_ADDR_LEN], *ha = NULL;
1546
Neil Horman95c385b2007-04-25 17:08:10 -07001547 if (ipv6_get_lladdr(dev, &saddr_buf, IFA_F_TENTATIVE)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001548 ND_PRINTK2(KERN_WARNING
1549 "ICMPv6 Redirect: no link-local address on %s\n",
1550 dev->name);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001551 return;
1552 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001553
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001554 if (!ipv6_addr_equal(&ipv6_hdr(skb)->daddr, target) &&
Brian Haleybf0b48d2007-10-08 00:12:05 -07001555 ipv6_addr_type(target) != (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) {
Li Yewang29556522007-01-30 14:33:20 -08001556 ND_PRINTK2(KERN_WARNING
Brian Haleybf0b48d2007-10-08 00:12:05 -07001557 "ICMPv6 Redirect: target address is not link-local unicast.\n");
Li Yewang29556522007-01-30 14:33:20 -08001558 return;
1559 }
1560
David S. Miller4c9483b2011-03-12 16:22:43 -05001561 icmpv6_flow_init(sk, &fl6, NDISC_REDIRECT,
YOSHIFUJI Hideaki95e41e92007-12-06 15:43:30 -08001562 &saddr_buf, &ipv6_hdr(skb)->saddr, dev->ifindex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001563
David S. Miller4c9483b2011-03-12 16:22:43 -05001564 dst = ip6_route_output(net, NULL, &fl6);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001565 if (dst == NULL)
1566 return;
1567
David S. Miller4c9483b2011-03-12 16:22:43 -05001568 dst = xfrm_lookup(net, dst, flowi6_to_flowi(&fl6), NULL, 0);
David S. Miller452edd52011-03-02 13:27:41 -08001569 if (IS_ERR(dst))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001570 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001571
1572 rt = (struct rt6_info *) dst;
1573
1574 if (rt->rt6i_flags & RTF_GATEWAY) {
1575 ND_PRINTK2(KERN_WARNING
1576 "ICMPv6 Redirect: destination is not a neighbour.\n");
Ilpo Järvinend73f0802009-02-06 23:47:37 -08001577 goto release;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001578 }
David S. Miller92d86822011-02-04 15:55:25 -08001579 if (!rt->rt6i_peer)
1580 rt6_bind_peer(rt, 1);
1581 if (inet_peer_xrlim_allow(rt->rt6i_peer, 1*HZ))
Ilpo Järvinend73f0802009-02-06 23:47:37 -08001582 goto release;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001583
1584 if (dev->addr_len) {
1585 read_lock_bh(&neigh->lock);
1586 if (neigh->nud_state & NUD_VALID) {
1587 memcpy(ha_buf, neigh->ha, dev->addr_len);
1588 read_unlock_bh(&neigh->lock);
1589 ha = ha_buf;
1590 len += ndisc_opt_addr_space(dev);
1591 } else
1592 read_unlock_bh(&neigh->lock);
1593 }
1594
1595 rd_len = min_t(unsigned int,
1596 IPV6_MIN_MTU-sizeof(struct ipv6hdr)-len, skb->len + 8);
1597 rd_len &= ~0x7;
1598 len += rd_len;
1599
David S. Millerd54a81d2006-12-02 21:00:06 -08001600 buff = sock_alloc_send_skb(sk,
1601 (MAX_HEADER + sizeof(struct ipv6hdr) +
Johannes Bergf5184d22008-05-12 20:48:31 -07001602 len + LL_ALLOCATED_SPACE(dev)),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001603 1, &err);
1604 if (buff == NULL) {
1605 ND_PRINTK0(KERN_ERR
Brian Haleydae9de82009-06-02 00:20:26 -07001606 "ICMPv6 Redirect: %s() failed to allocate an skb, err=%d.\n",
1607 __func__, err);
Ilpo Järvinend73f0802009-02-06 23:47:37 -08001608 goto release;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001609 }
1610
Linus Torvalds1da177e2005-04-16 15:20:36 -07001611 skb_reserve(buff, LL_RESERVED_SPACE(dev));
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001612 ip6_nd_hdr(sk, buff, dev, &saddr_buf, &ipv6_hdr(skb)->saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001613 IPPROTO_ICMPV6, len);
1614
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -07001615 skb_set_transport_header(buff, skb_tail_pointer(buff) - buff->data);
Arnaldo Carvalho de Melod10ba342007-03-14 21:05:37 -03001616 skb_put(buff, len);
1617 icmph = icmp6_hdr(buff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001618
1619 memset(icmph, 0, sizeof(struct icmp6hdr));
1620 icmph->icmp6_type = NDISC_REDIRECT;
1621
1622 /*
1623 * copy target and destination addresses
1624 */
1625
1626 addrp = (struct in6_addr *)(icmph + 1);
1627 ipv6_addr_copy(addrp, target);
1628 addrp++;
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001629 ipv6_addr_copy(addrp, &ipv6_hdr(skb)->daddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001630
1631 opt = (u8*) (addrp + 1);
1632
1633 /*
1634 * include target_address option
1635 */
1636
1637 if (ha)
1638 opt = ndisc_fill_addr_option(opt, ND_OPT_TARGET_LL_ADDR, ha,
1639 dev->addr_len, dev->type);
1640
1641 /*
1642 * build redirect option and copy skb over to the new packet.
1643 */
1644
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001645 memset(opt, 0, 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001646 *(opt++) = ND_OPT_REDIRECT_HDR;
1647 *(opt++) = (rd_len >> 3);
1648 opt += 6;
1649
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001650 memcpy(opt, ipv6_hdr(skb), rd_len - 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001651
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001652 icmph->icmp6_cksum = csum_ipv6_magic(&saddr_buf, &ipv6_hdr(skb)->saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001653 len, IPPROTO_ICMPV6,
Joe Perches07f07572008-11-19 15:44:53 -08001654 csum_partial(icmph, len, 0));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001655
Eric Dumazetadf30902009-06-02 05:19:30 +00001656 skb_dst_set(buff, dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001657 idev = in6_dev_get(dst->dev);
Neil Hormanedf391f2009-04-27 02:45:02 -07001658 IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUT, skb->len);
Jan Engelhardtb2e0b382010-03-23 04:09:07 +01001659 err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, buff, NULL, dst->dev,
Patrick McHardy6e23ae22007-11-19 18:53:30 -08001660 dst_output);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001661 if (!err) {
Denis V. Lunev5c5d2442008-10-08 10:33:50 -07001662 ICMP6MSGOUT_INC_STATS(net, idev, NDISC_REDIRECT);
Denis V. Luneva862f6a2008-10-08 10:33:06 -07001663 ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001664 }
1665
1666 if (likely(idev != NULL))
1667 in6_dev_put(idev);
Ilpo Järvinend73f0802009-02-06 23:47:37 -08001668 return;
1669
1670release:
1671 dst_release(dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001672}
1673
1674static void pndisc_redo(struct sk_buff *skb)
1675{
YOSHIFUJI Hideaki140e26fc2005-10-05 12:11:41 -07001676 ndisc_recv_ns(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001677 kfree_skb(skb);
1678}
1679
1680int ndisc_rcv(struct sk_buff *skb)
1681{
1682 struct nd_msg *msg;
1683
1684 if (!pskb_may_pull(skb, skb->len))
1685 return 0;
1686
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -07001687 msg = (struct nd_msg *)skb_transport_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001688
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -07001689 __skb_push(skb, skb->data - skb_transport_header(skb));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001690
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001691 if (ipv6_hdr(skb)->hop_limit != 255) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001692 ND_PRINTK2(KERN_WARNING
1693 "ICMPv6 NDISC: invalid hop-limit: %d\n",
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001694 ipv6_hdr(skb)->hop_limit);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001695 return 0;
1696 }
1697
1698 if (msg->icmph.icmp6_code != 0) {
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001699 ND_PRINTK2(KERN_WARNING
Linus Torvalds1da177e2005-04-16 15:20:36 -07001700 "ICMPv6 NDISC: invalid ICMPv6 code: %d\n",
1701 msg->icmph.icmp6_code);
1702 return 0;
1703 }
1704
Patrick McHardya61bbcf2005-08-14 17:24:31 -07001705 memset(NEIGH_CB(skb), 0, sizeof(struct neighbour_cb));
1706
Linus Torvalds1da177e2005-04-16 15:20:36 -07001707 switch (msg->icmph.icmp6_type) {
1708 case NDISC_NEIGHBOUR_SOLICITATION:
1709 ndisc_recv_ns(skb);
1710 break;
1711
1712 case NDISC_NEIGHBOUR_ADVERTISEMENT:
1713 ndisc_recv_na(skb);
1714 break;
1715
1716 case NDISC_ROUTER_SOLICITATION:
1717 ndisc_recv_rs(skb);
1718 break;
1719
1720 case NDISC_ROUTER_ADVERTISEMENT:
1721 ndisc_router_discovery(skb);
1722 break;
1723
1724 case NDISC_REDIRECT:
1725 ndisc_redirect_rcv(skb);
1726 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -07001727 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001728
1729 return 0;
1730}
1731
1732static int ndisc_netdev_event(struct notifier_block *this, unsigned long event, void *ptr)
1733{
1734 struct net_device *dev = ptr;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001735 struct net *net = dev_net(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001736
1737 switch (event) {
1738 case NETDEV_CHANGEADDR:
1739 neigh_changeaddr(&nd_tbl, dev);
Daniel Lezcano5b7c9312008-03-03 23:28:58 -08001740 fib6_run_gc(~0UL, net);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001741 break;
1742 case NETDEV_DOWN:
1743 neigh_ifdown(&nd_tbl, dev);
Daniel Lezcano5b7c9312008-03-03 23:28:58 -08001744 fib6_run_gc(~0UL, net);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001745 break;
Ben Hutchingsf47b9462011-04-15 13:46:02 +00001746 case NETDEV_NOTIFY_PEERS:
1747 ndisc_send_unsol_na(dev);
1748 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001749 default:
1750 break;
1751 }
1752
1753 return NOTIFY_DONE;
1754}
1755
1756static struct notifier_block ndisc_netdev_notifier = {
1757 .notifier_call = ndisc_netdev_event,
1758};
1759
1760#ifdef CONFIG_SYSCTL
1761static void ndisc_warn_deprecated_sysctl(struct ctl_table *ctl,
1762 const char *func, const char *dev_name)
1763{
1764 static char warncomm[TASK_COMM_LEN];
1765 static int warned;
1766 if (strcmp(warncomm, current->comm) && warned < 5) {
1767 strcpy(warncomm, current->comm);
1768 printk(KERN_WARNING
1769 "process `%s' is using deprecated sysctl (%s) "
1770 "net.ipv6.neigh.%s.%s; "
1771 "Use net.ipv6.neigh.%s.%s_ms "
1772 "instead.\n",
1773 warncomm, func,
1774 dev_name, ctl->procname,
1775 dev_name, ctl->procname);
1776 warned++;
1777 }
1778}
1779
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001780int 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 -07001781{
1782 struct net_device *dev = ctl->extra1;
1783 struct inet6_dev *idev;
1784 int ret;
1785
Eric W. Biedermand12af672007-10-18 03:05:25 -07001786 if ((strcmp(ctl->procname, "retrans_time") == 0) ||
1787 (strcmp(ctl->procname, "base_reachable_time") == 0))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001788 ndisc_warn_deprecated_sysctl(ctl, "syscall", dev ? dev->name : "default");
1789
Eric W. Biedermand12af672007-10-18 03:05:25 -07001790 if (strcmp(ctl->procname, "retrans_time") == 0)
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001791 ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
Eric W. Biedermand12af672007-10-18 03:05:25 -07001792
1793 else if (strcmp(ctl->procname, "base_reachable_time") == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001794 ret = proc_dointvec_jiffies(ctl, write,
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001795 buffer, lenp, ppos);
Eric W. Biedermand12af672007-10-18 03:05:25 -07001796
1797 else if ((strcmp(ctl->procname, "retrans_time_ms") == 0) ||
YOSHIFUJI Hideakiad02ac12007-10-29 01:32:23 -07001798 (strcmp(ctl->procname, "base_reachable_time_ms") == 0))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001799 ret = proc_dointvec_ms_jiffies(ctl, write,
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001800 buffer, lenp, ppos);
Eric W. Biedermand12af672007-10-18 03:05:25 -07001801 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001802 ret = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001803
1804 if (write && ret == 0 && dev && (idev = in6_dev_get(dev)) != NULL) {
Eric W. Biedermand12af672007-10-18 03:05:25 -07001805 if (ctl->data == &idev->nd_parms->base_reachable_time)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001806 idev->nd_parms->reachable_time = neigh_rand_reach_time(idev->nd_parms->base_reachable_time);
1807 idev->tstamp = jiffies;
1808 inet6_ifinfo_notify(RTM_NEWLINK, idev);
1809 in6_dev_put(idev);
1810 }
1811 return ret;
1812}
1813
Linus Torvalds1da177e2005-04-16 15:20:36 -07001814
1815#endif
1816
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00001817static int __net_init ndisc_net_init(struct net *net)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001818{
1819 struct ipv6_pinfo *np;
1820 struct sock *sk;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001821 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001822
Denis V. Lunev1ed85162008-04-03 14:31:03 -07001823 err = inet_ctl_sock_create(&sk, PF_INET6,
1824 SOCK_RAW, IPPROTO_ICMPV6, net);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001825 if (err < 0) {
1826 ND_PRINTK0(KERN_ERR
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001827 "ICMPv6 NDISC: Failed to initialize the control socket (err %d).\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001828 err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001829 return err;
1830 }
1831
Denis V. Lunev1ed85162008-04-03 14:31:03 -07001832 net->ipv6.ndisc_sk = sk;
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001833
Linus Torvalds1da177e2005-04-16 15:20:36 -07001834 np = inet6_sk(sk);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001835 np->hop_limit = 255;
1836 /* Do not loopback ndisc messages */
1837 np->mc_loop = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001838
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001839 return 0;
1840}
1841
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00001842static void __net_exit ndisc_net_exit(struct net *net)
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001843{
Denis V. Lunev1ed85162008-04-03 14:31:03 -07001844 inet_ctl_sock_destroy(net->ipv6.ndisc_sk);
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001845}
1846
1847static struct pernet_operations ndisc_net_ops = {
1848 .init = ndisc_net_init,
1849 .exit = ndisc_net_exit,
1850};
1851
1852int __init ndisc_init(void)
1853{
1854 int err;
1855
1856 err = register_pernet_subsys(&ndisc_net_ops);
1857 if (err)
1858 return err;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001859 /*
1860 * Initialize the neighbour table
1861 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001862 neigh_table_init(&nd_tbl);
1863
1864#ifdef CONFIG_SYSCTL
Eric W. Biederman54716e32010-02-14 03:27:03 +00001865 err = neigh_sysctl_register(NULL, &nd_tbl.parms, "ipv6",
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001866 &ndisc_ifinfo_sysctl_change);
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001867 if (err)
1868 goto out_unregister_pernet;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001869#endif
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001870 err = register_netdevice_notifier(&ndisc_netdev_notifier);
1871 if (err)
1872 goto out_unregister_sysctl;
1873out:
1874 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001875
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001876out_unregister_sysctl:
1877#ifdef CONFIG_SYSCTL
1878 neigh_sysctl_unregister(&nd_tbl.parms);
1879out_unregister_pernet:
1880#endif
1881 unregister_pernet_subsys(&ndisc_net_ops);
1882 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001883}
1884
1885void ndisc_cleanup(void)
1886{
Dmitry Mishin36f73d02006-11-03 16:08:19 -08001887 unregister_netdevice_notifier(&ndisc_netdev_notifier);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001888#ifdef CONFIG_SYSCTL
1889 neigh_sysctl_unregister(&nd_tbl.parms);
1890#endif
1891 neigh_table_clear(&nd_tbl);
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001892 unregister_pernet_subsys(&ndisc_net_ops);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001893}