blob: a539b9ea53fdfbfb6f420788e8cac72577181c46 [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>
62#ifdef CONFIG_SYSCTL
63#include <linux/sysctl.h>
64#endif
65
Thomas Graf18237302006-08-04 23:04:54 -070066#include <linux/if_addr.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070067#include <linux/if_arp.h>
68#include <linux/ipv6.h>
69#include <linux/icmpv6.h>
70#include <linux/jhash.h>
71
72#include <net/sock.h>
73#include <net/snmp.h>
74
75#include <net/ipv6.h>
76#include <net/protocol.h>
77#include <net/ndisc.h>
78#include <net/ip6_route.h>
79#include <net/addrconf.h>
80#include <net/icmp.h>
81
Pierre Ynard31910572007-10-10 21:22:05 -070082#include <net/netlink.h>
83#include <linux/rtnetlink.h>
84
Linus Torvalds1da177e2005-04-16 15:20:36 -070085#include <net/flow.h>
86#include <net/ip6_checksum.h>
87#include <linux/proc_fs.h>
88
89#include <linux/netfilter.h>
90#include <linux/netfilter_ipv6.h>
91
92static struct socket *ndisc_socket;
93
94static u32 ndisc_hash(const void *pkey, const struct net_device *dev);
95static int ndisc_constructor(struct neighbour *neigh);
96static void ndisc_solicit(struct neighbour *neigh, struct sk_buff *skb);
97static void ndisc_error_report(struct neighbour *neigh, struct sk_buff *skb);
98static int pndisc_constructor(struct pneigh_entry *n);
99static void pndisc_destructor(struct pneigh_entry *n);
100static void pndisc_redo(struct sk_buff *skb);
101
102static struct neigh_ops ndisc_generic_ops = {
103 .family = AF_INET6,
104 .solicit = ndisc_solicit,
105 .error_report = ndisc_error_report,
106 .output = neigh_resolve_output,
107 .connected_output = neigh_connected_output,
108 .hh_output = dev_queue_xmit,
109 .queue_xmit = dev_queue_xmit,
110};
111
112static struct neigh_ops ndisc_hh_ops = {
113 .family = AF_INET6,
114 .solicit = ndisc_solicit,
115 .error_report = ndisc_error_report,
116 .output = neigh_resolve_output,
117 .connected_output = neigh_resolve_output,
118 .hh_output = dev_queue_xmit,
119 .queue_xmit = dev_queue_xmit,
120};
121
122
123static struct neigh_ops ndisc_direct_ops = {
124 .family = AF_INET6,
125 .output = dev_queue_xmit,
126 .connected_output = dev_queue_xmit,
127 .hh_output = dev_queue_xmit,
128 .queue_xmit = dev_queue_xmit,
129};
130
131struct neigh_table nd_tbl = {
132 .family = AF_INET6,
133 .entry_size = sizeof(struct neighbour) + sizeof(struct in6_addr),
134 .key_len = sizeof(struct in6_addr),
135 .hash = ndisc_hash,
136 .constructor = ndisc_constructor,
137 .pconstructor = pndisc_constructor,
138 .pdestructor = pndisc_destructor,
139 .proxy_redo = pndisc_redo,
140 .id = "ndisc_cache",
141 .parms = {
142 .tbl = &nd_tbl,
143 .base_reachable_time = 30 * HZ,
144 .retrans_time = 1 * HZ,
145 .gc_staletime = 60 * HZ,
146 .reachable_time = 30 * HZ,
147 .delay_probe_time = 5 * HZ,
148 .queue_len = 3,
149 .ucast_probes = 3,
150 .mcast_probes = 3,
151 .anycast_delay = 1 * HZ,
152 .proxy_delay = (8 * HZ) / 10,
153 .proxy_qlen = 64,
154 },
155 .gc_interval = 30 * HZ,
156 .gc_thresh1 = 128,
157 .gc_thresh2 = 512,
158 .gc_thresh3 = 1024,
159};
160
161/* ND options */
162struct ndisc_options {
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800163 struct nd_opt_hdr *nd_opt_array[__ND_OPT_ARRAY_MAX];
164#ifdef CONFIG_IPV6_ROUTE_INFO
165 struct nd_opt_hdr *nd_opts_ri;
166 struct nd_opt_hdr *nd_opts_ri_end;
167#endif
Pierre Ynard31910572007-10-10 21:22:05 -0700168 struct nd_opt_hdr *nd_useropts;
169 struct nd_opt_hdr *nd_useropts_end;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700170};
171
172#define nd_opts_src_lladdr nd_opt_array[ND_OPT_SOURCE_LL_ADDR]
173#define nd_opts_tgt_lladdr nd_opt_array[ND_OPT_TARGET_LL_ADDR]
174#define nd_opts_pi nd_opt_array[ND_OPT_PREFIX_INFO]
175#define nd_opts_pi_end nd_opt_array[__ND_OPT_PREFIX_INFO_END]
176#define nd_opts_rh nd_opt_array[ND_OPT_REDIRECT_HDR]
177#define nd_opts_mtu nd_opt_array[ND_OPT_MTU]
178
179#define NDISC_OPT_SPACE(len) (((len)+2+7)&~7)
180
181/*
182 * Return the padding between the option length and the start of the
183 * link addr. Currently only IP-over-InfiniBand needs this, although
184 * if RFC 3831 IPv6-over-Fibre Channel is ever implemented it may
185 * also need a pad of 2.
186 */
187static int ndisc_addr_option_pad(unsigned short type)
188{
189 switch (type) {
190 case ARPHRD_INFINIBAND: return 2;
191 default: return 0;
192 }
193}
194
195static inline int ndisc_opt_addr_space(struct net_device *dev)
196{
197 return NDISC_OPT_SPACE(dev->addr_len + ndisc_addr_option_pad(dev->type));
198}
199
200static u8 *ndisc_fill_addr_option(u8 *opt, int type, void *data, int data_len,
201 unsigned short addr_type)
202{
203 int space = NDISC_OPT_SPACE(data_len);
204 int pad = ndisc_addr_option_pad(addr_type);
205
206 opt[0] = type;
207 opt[1] = space>>3;
208
209 memset(opt + 2, 0, pad);
210 opt += pad;
211 space -= pad;
212
213 memcpy(opt+2, data, data_len);
214 data_len += 2;
215 opt += data_len;
216 if ((space -= data_len) > 0)
217 memset(opt, 0, space);
218 return opt + space;
219}
220
221static struct nd_opt_hdr *ndisc_next_option(struct nd_opt_hdr *cur,
222 struct nd_opt_hdr *end)
223{
224 int type;
225 if (!cur || !end || cur >= end)
226 return NULL;
227 type = cur->nd_opt_type;
228 do {
229 cur = ((void *)cur) + (cur->nd_opt_len << 3);
230 } while(cur < end && cur->nd_opt_type != type);
231 return (cur <= end && cur->nd_opt_type == type ? cur : NULL);
232}
233
Pierre Ynard31910572007-10-10 21:22:05 -0700234static inline int ndisc_is_useropt(struct nd_opt_hdr *opt)
235{
236 return (opt->nd_opt_type == ND_OPT_RDNSS);
237}
238
239static struct nd_opt_hdr *ndisc_next_useropt(struct nd_opt_hdr *cur,
240 struct nd_opt_hdr *end)
241{
242 if (!cur || !end || cur >= end)
243 return NULL;
244 do {
245 cur = ((void *)cur) + (cur->nd_opt_len << 3);
246 } while(cur < end && !ndisc_is_useropt(cur));
247 return (cur <= end && ndisc_is_useropt(cur) ? cur : NULL);
248}
249
Linus Torvalds1da177e2005-04-16 15:20:36 -0700250static struct ndisc_options *ndisc_parse_options(u8 *opt, int opt_len,
251 struct ndisc_options *ndopts)
252{
253 struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)opt;
254
255 if (!nd_opt || opt_len < 0 || !ndopts)
256 return NULL;
257 memset(ndopts, 0, sizeof(*ndopts));
258 while (opt_len) {
259 int l;
260 if (opt_len < sizeof(struct nd_opt_hdr))
261 return NULL;
262 l = nd_opt->nd_opt_len << 3;
263 if (opt_len < l || l == 0)
264 return NULL;
265 switch (nd_opt->nd_opt_type) {
266 case ND_OPT_SOURCE_LL_ADDR:
267 case ND_OPT_TARGET_LL_ADDR:
268 case ND_OPT_MTU:
269 case ND_OPT_REDIRECT_HDR:
270 if (ndopts->nd_opt_array[nd_opt->nd_opt_type]) {
271 ND_PRINTK2(KERN_WARNING
272 "%s(): duplicated ND6 option found: type=%d\n",
Harvey Harrison0dc47872008-03-05 20:47:47 -0800273 __func__,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700274 nd_opt->nd_opt_type);
275 } else {
276 ndopts->nd_opt_array[nd_opt->nd_opt_type] = nd_opt;
277 }
278 break;
279 case ND_OPT_PREFIX_INFO:
280 ndopts->nd_opts_pi_end = nd_opt;
Stephen Hemmingercfcabdc2007-10-09 01:59:42 -0700281 if (!ndopts->nd_opt_array[nd_opt->nd_opt_type])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282 ndopts->nd_opt_array[nd_opt->nd_opt_type] = nd_opt;
283 break;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800284#ifdef CONFIG_IPV6_ROUTE_INFO
285 case ND_OPT_ROUTE_INFO:
286 ndopts->nd_opts_ri_end = nd_opt;
287 if (!ndopts->nd_opts_ri)
288 ndopts->nd_opts_ri = nd_opt;
289 break;
290#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291 default:
Pierre Ynard31910572007-10-10 21:22:05 -0700292 if (ndisc_is_useropt(nd_opt)) {
293 ndopts->nd_useropts_end = nd_opt;
294 if (!ndopts->nd_useropts)
295 ndopts->nd_useropts = nd_opt;
296 } else {
297 /*
298 * Unknown options must be silently ignored,
299 * to accommodate future extension to the
300 * protocol.
301 */
302 ND_PRINTK2(KERN_NOTICE
303 "%s(): ignored unsupported option; type=%d, len=%d\n",
Harvey Harrison0dc47872008-03-05 20:47:47 -0800304 __func__,
Pierre Ynard31910572007-10-10 21:22:05 -0700305 nd_opt->nd_opt_type, nd_opt->nd_opt_len);
306 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700307 }
308 opt_len -= l;
309 nd_opt = ((void *)nd_opt) + l;
310 }
311 return ndopts;
312}
313
314static inline u8 *ndisc_opt_addr_data(struct nd_opt_hdr *p,
315 struct net_device *dev)
316{
317 u8 *lladdr = (u8 *)(p + 1);
318 int lladdrlen = p->nd_opt_len << 3;
319 int prepad = ndisc_addr_option_pad(dev->type);
320 if (lladdrlen != NDISC_OPT_SPACE(dev->addr_len + prepad))
321 return NULL;
322 return (lladdr + prepad);
323}
324
325int ndisc_mc_map(struct in6_addr *addr, char *buf, struct net_device *dev, int dir)
326{
327 switch (dev->type) {
328 case ARPHRD_ETHER:
329 case ARPHRD_IEEE802: /* Not sure. Check it later. --ANK */
330 case ARPHRD_FDDI:
331 ipv6_eth_mc_map(addr, buf);
332 return 0;
333 case ARPHRD_IEEE802_TR:
334 ipv6_tr_mc_map(addr,buf);
335 return 0;
336 case ARPHRD_ARCNET:
337 ipv6_arcnet_mc_map(addr, buf);
338 return 0;
339 case ARPHRD_INFINIBAND:
Rolf Manderscheida9e527e2007-12-10 13:38:41 -0700340 ipv6_ib_mc_map(addr, dev->broadcast, buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700341 return 0;
342 default:
343 if (dir) {
344 memcpy(buf, dev->broadcast, dev->addr_len);
345 return 0;
346 }
347 }
348 return -EINVAL;
349}
350
YOSHIFUJI Hideaki71590392007-02-22 22:05:40 +0900351EXPORT_SYMBOL(ndisc_mc_map);
352
Linus Torvalds1da177e2005-04-16 15:20:36 -0700353static u32 ndisc_hash(const void *pkey, const struct net_device *dev)
354{
355 const u32 *p32 = pkey;
356 u32 addr_hash, i;
357
358 addr_hash = 0;
359 for (i = 0; i < (sizeof(struct in6_addr) / sizeof(u32)); i++)
360 addr_hash ^= *p32++;
361
362 return jhash_2words(addr_hash, dev->ifindex, nd_tbl.hash_rnd);
363}
364
365static int ndisc_constructor(struct neighbour *neigh)
366{
367 struct in6_addr *addr = (struct in6_addr*)&neigh->primary_key;
368 struct net_device *dev = neigh->dev;
369 struct inet6_dev *in6_dev;
370 struct neigh_parms *parms;
371 int is_multicast = ipv6_addr_is_multicast(addr);
372
373 rcu_read_lock();
374 in6_dev = in6_dev_get(dev);
375 if (in6_dev == NULL) {
376 rcu_read_unlock();
377 return -EINVAL;
378 }
379
380 parms = in6_dev->nd_parms;
381 __neigh_parms_put(neigh->parms);
382 neigh->parms = neigh_parms_clone(parms);
383 rcu_read_unlock();
384
385 neigh->type = is_multicast ? RTN_MULTICAST : RTN_UNICAST;
Stephen Hemminger3b04ddd2007-10-09 01:40:57 -0700386 if (!dev->header_ops) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700387 neigh->nud_state = NUD_NOARP;
388 neigh->ops = &ndisc_direct_ops;
389 neigh->output = neigh->ops->queue_xmit;
390 } else {
391 if (is_multicast) {
392 neigh->nud_state = NUD_NOARP;
393 ndisc_mc_map(addr, neigh->ha, dev, 1);
394 } else if (dev->flags&(IFF_NOARP|IFF_LOOPBACK)) {
395 neigh->nud_state = NUD_NOARP;
396 memcpy(neigh->ha, dev->dev_addr, dev->addr_len);
397 if (dev->flags&IFF_LOOPBACK)
398 neigh->type = RTN_LOCAL;
399 } else if (dev->flags&IFF_POINTOPOINT) {
400 neigh->nud_state = NUD_NOARP;
401 memcpy(neigh->ha, dev->broadcast, dev->addr_len);
402 }
Stephen Hemminger3b04ddd2007-10-09 01:40:57 -0700403 if (dev->header_ops->cache)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404 neigh->ops = &ndisc_hh_ops;
405 else
406 neigh->ops = &ndisc_generic_ops;
407 if (neigh->nud_state&NUD_VALID)
408 neigh->output = neigh->ops->connected_output;
409 else
410 neigh->output = neigh->ops->output;
411 }
412 in6_dev_put(in6_dev);
413 return 0;
414}
415
416static int pndisc_constructor(struct pneigh_entry *n)
417{
418 struct in6_addr *addr = (struct in6_addr*)&n->key;
419 struct in6_addr maddr;
420 struct net_device *dev = n->dev;
421
422 if (dev == NULL || __in6_dev_get(dev) == NULL)
423 return -EINVAL;
424 addrconf_addr_solict_mult(addr, &maddr);
425 ipv6_dev_mc_inc(dev, &maddr);
426 return 0;
427}
428
429static void pndisc_destructor(struct pneigh_entry *n)
430{
431 struct in6_addr *addr = (struct in6_addr*)&n->key;
432 struct in6_addr maddr;
433 struct net_device *dev = n->dev;
434
435 if (dev == NULL || __in6_dev_get(dev) == NULL)
436 return;
437 addrconf_addr_solict_mult(addr, &maddr);
438 ipv6_dev_mc_dec(dev, &maddr);
439}
440
441/*
442 * Send a Neighbour Advertisement
443 */
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900444static void __ndisc_send(struct net_device *dev,
445 struct neighbour *neigh,
446 struct in6_addr *daddr, struct in6_addr *saddr,
447 struct icmp6hdr *icmp6h, struct in6_addr *target,
David L Stevens14878f72007-09-16 16:52:35 -0700448 int llinfo)
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900449{
450 struct flowi fl;
451 struct dst_entry *dst;
452 struct sock *sk = ndisc_socket->sk;
453 struct sk_buff *skb;
454 struct icmp6hdr *hdr;
455 struct inet6_dev *idev;
456 int len;
457 int err;
David L Stevens14878f72007-09-16 16:52:35 -0700458 u8 *opt, type;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900459
David L Stevens14878f72007-09-16 16:52:35 -0700460 type = icmp6h->icmp6_type;
461
YOSHIFUJI Hideaki95e41e92007-12-06 15:43:30 -0800462 icmpv6_flow_init(ndisc_socket->sk, &fl, type,
463 saddr, daddr, dev->ifindex);
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900464
YOSHIFUJI Hideaki3b009442007-12-06 16:11:48 -0800465 dst = icmp6_dst_alloc(dev, neigh, daddr);
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900466 if (!dst)
467 return;
468
469 err = xfrm_lookup(&dst, &fl, NULL, 0);
470 if (err < 0)
471 return;
472
473 if (!dev->addr_len)
474 llinfo = 0;
475
476 len = sizeof(struct icmp6hdr) + (target ? sizeof(*target) : 0);
477 if (llinfo)
478 len += ndisc_opt_addr_space(dev);
479
480 skb = sock_alloc_send_skb(sk,
481 (MAX_HEADER + sizeof(struct ipv6hdr) +
482 len + LL_RESERVED_SPACE(dev)),
483 1, &err);
484 if (!skb) {
485 ND_PRINTK0(KERN_ERR
486 "ICMPv6 ND: %s() failed to allocate an skb.\n",
Harvey Harrison0dc47872008-03-05 20:47:47 -0800487 __func__);
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900488 dst_release(dst);
489 return;
490 }
491
492 skb_reserve(skb, LL_RESERVED_SPACE(dev));
493 ip6_nd_hdr(sk, skb, dev, saddr, daddr, IPPROTO_ICMPV6, len);
494
495 skb->transport_header = skb->tail;
496 skb_put(skb, len);
497
498 hdr = (struct icmp6hdr *)skb_transport_header(skb);
499 memcpy(hdr, icmp6h, sizeof(*hdr));
500
501 opt = skb_transport_header(skb) + sizeof(struct icmp6hdr);
502 if (target) {
503 ipv6_addr_copy((struct in6_addr *)opt, target);
504 opt += sizeof(*target);
505 }
506
507 if (llinfo)
508 ndisc_fill_addr_option(opt, llinfo, dev->dev_addr,
509 dev->addr_len, dev->type);
510
511 hdr->icmp6_cksum = csum_ipv6_magic(saddr, daddr, len,
512 IPPROTO_ICMPV6,
513 csum_partial((__u8 *) hdr,
514 len, 0));
515
516 skb->dst = dst;
517
518 idev = in6_dev_get(dst->dev);
519 IP6_INC_STATS(idev, IPSTATS_MIB_OUTREQUESTS);
520
Patrick McHardy6e23ae22007-11-19 18:53:30 -0800521 err = NF_HOOK(PF_INET6, NF_INET_LOCAL_OUT, skb, NULL, dst->dev,
522 dst_output);
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900523 if (!err) {
David L Stevens14878f72007-09-16 16:52:35 -0700524 ICMP6MSGOUT_INC_STATS(idev, type);
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900525 ICMP6_INC_STATS(idev, ICMP6_MIB_OUTMSGS);
526 }
527
528 if (likely(idev != NULL))
529 in6_dev_put(idev);
530}
531
Linus Torvalds1da177e2005-04-16 15:20:36 -0700532static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
533 struct in6_addr *daddr, struct in6_addr *solicited_addr,
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900534 int router, int solicited, int override, int inc_opt)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535{
536 struct in6_addr tmpaddr;
537 struct inet6_ifaddr *ifp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538 struct in6_addr *src_addr;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900539 struct icmp6hdr icmp6h = {
540 .icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT,
541 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700542
543 /* for anycast or proxy, solicited_addr != src_addr */
Daniel Lezcanoa18bc692008-03-07 11:14:49 -0800544 ifp = ipv6_get_ifaddr(dev->nd_net, solicited_addr, dev, 1);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900545 if (ifp) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700546 src_addr = solicited_addr;
Neil Horman95c385b2007-04-25 17:08:10 -0700547 if (ifp->flags & IFA_F_OPTIMISTIC)
548 override = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549 in6_ifa_put(ifp);
550 } else {
551 if (ipv6_dev_get_saddr(dev, daddr, &tmpaddr))
552 return;
553 src_addr = &tmpaddr;
554 }
555
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900556 icmp6h.icmp6_router = router;
557 icmp6h.icmp6_solicited = solicited;
558 icmp6h.icmp6_override = override;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700559
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900560 __ndisc_send(dev, neigh, daddr, src_addr,
561 &icmp6h, solicited_addr,
David L Stevens14878f72007-09-16 16:52:35 -0700562 inc_opt ? ND_OPT_TARGET_LL_ADDR : 0);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900563}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564
565void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,
566 struct in6_addr *solicit,
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900567 struct in6_addr *daddr, struct in6_addr *saddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700568{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569 struct in6_addr addr_buf;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900570 struct icmp6hdr icmp6h = {
571 .icmp6_type = NDISC_NEIGHBOUR_SOLICITATION,
572 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700573
574 if (saddr == NULL) {
Neil Horman95c385b2007-04-25 17:08:10 -0700575 if (ipv6_get_lladdr(dev, &addr_buf,
576 (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700577 return;
578 saddr = &addr_buf;
579 }
580
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900581 __ndisc_send(dev, neigh, daddr, saddr,
582 &icmp6h, solicit,
David L Stevens14878f72007-09-16 16:52:35 -0700583 !ipv6_addr_any(saddr) ? ND_OPT_SOURCE_LL_ADDR : 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584}
585
586void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr,
587 struct in6_addr *daddr)
588{
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900589 struct icmp6hdr icmp6h = {
590 .icmp6_type = NDISC_ROUTER_SOLICITATION,
591 };
Neil Horman95c385b2007-04-25 17:08:10 -0700592 int send_sllao = dev->addr_len;
Neil Horman95c385b2007-04-25 17:08:10 -0700593
594#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
595 /*
596 * According to section 2.2 of RFC 4429, we must not
597 * send router solicitations with a sllao from
598 * optimistic addresses, but we may send the solicitation
599 * if we don't include the sllao. So here we check
600 * if our address is optimistic, and if so, we
Joe Perchesbea85192007-12-20 14:01:35 -0800601 * suppress the inclusion of the sllao.
Neil Horman95c385b2007-04-25 17:08:10 -0700602 */
603 if (send_sllao) {
Daniel Lezcanoa18bc692008-03-07 11:14:49 -0800604 struct inet6_ifaddr *ifp = ipv6_get_ifaddr(dev->nd_net, saddr,
Daniel Lezcano1cab3da2008-01-10 22:44:09 -0800605 dev, 1);
Neil Horman95c385b2007-04-25 17:08:10 -0700606 if (ifp) {
607 if (ifp->flags & IFA_F_OPTIMISTIC) {
YOSHIFUJI Hideakica043562007-02-28 23:13:20 +0900608 send_sllao = 0;
Neil Horman95c385b2007-04-25 17:08:10 -0700609 }
YOSHIFUJI Hideakica043562007-02-28 23:13:20 +0900610 in6_ifa_put(ifp);
Neil Horman95c385b2007-04-25 17:08:10 -0700611 } else {
612 send_sllao = 0;
613 }
614 }
615#endif
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900616 __ndisc_send(dev, NULL, daddr, saddr,
617 &icmp6h, NULL,
David L Stevens14878f72007-09-16 16:52:35 -0700618 send_sllao ? ND_OPT_SOURCE_LL_ADDR : 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700619}
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900620
Linus Torvalds1da177e2005-04-16 15:20:36 -0700621
622static void ndisc_error_report(struct neighbour *neigh, struct sk_buff *skb)
623{
624 /*
625 * "The sender MUST return an ICMP
626 * destination unreachable"
627 */
628 dst_link_failure(skb);
629 kfree_skb(skb);
630}
631
632/* Called with locked neigh: either read or both */
633
634static void ndisc_solicit(struct neighbour *neigh, struct sk_buff *skb)
635{
636 struct in6_addr *saddr = NULL;
637 struct in6_addr mcaddr;
638 struct net_device *dev = neigh->dev;
639 struct in6_addr *target = (struct in6_addr *)&neigh->primary_key;
640 int probes = atomic_read(&neigh->probes);
641
Daniel Lezcanoa18bc692008-03-07 11:14:49 -0800642 if (skb && ipv6_chk_addr(dev->nd_net, &ipv6_hdr(skb)->saddr, dev, 1))
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -0700643 saddr = &ipv6_hdr(skb)->saddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700644
645 if ((probes -= neigh->parms->ucast_probes) < 0) {
646 if (!(neigh->nud_state & NUD_VALID)) {
647 ND_PRINTK1(KERN_DEBUG
648 "%s(): trying to ucast probe in NUD_INVALID: "
Joe Perches46b86a22006-01-13 14:29:07 -0800649 NIP6_FMT "\n",
Harvey Harrison0dc47872008-03-05 20:47:47 -0800650 __func__,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700651 NIP6(*target));
652 }
653 ndisc_send_ns(dev, neigh, target, target, saddr);
654 } else if ((probes -= neigh->parms->app_probes) < 0) {
655#ifdef CONFIG_ARPD
656 neigh_app_ns(neigh);
657#endif
658 } else {
659 addrconf_addr_solict_mult(target, &mcaddr);
660 ndisc_send_ns(dev, NULL, target, &mcaddr, saddr);
661 }
662}
663
664static void ndisc_recv_ns(struct sk_buff *skb)
665{
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -0700666 struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb);
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -0700667 struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
668 struct in6_addr *daddr = &ipv6_hdr(skb)->daddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700669 u8 *lladdr = NULL;
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -0700670 u32 ndoptlen = skb->tail - (skb->transport_header +
671 offsetof(struct nd_msg, opt));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700672 struct ndisc_options ndopts;
673 struct net_device *dev = skb->dev;
674 struct inet6_ifaddr *ifp;
675 struct inet6_dev *idev = NULL;
676 struct neighbour *neigh;
Ville Nuorvala62dd9312006-09-22 14:43:19 -0700677 struct pneigh_entry *pneigh = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700678 int dad = ipv6_addr_any(saddr);
679 int inc;
Ville Nuorvala62dd9312006-09-22 14:43:19 -0700680 int is_router;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700681
682 if (ipv6_addr_is_multicast(&msg->target)) {
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900683 ND_PRINTK2(KERN_WARNING
Linus Torvalds1da177e2005-04-16 15:20:36 -0700684 "ICMPv6 NS: multicast target address");
685 return;
686 }
687
688 /*
689 * RFC2461 7.1.1:
690 * DAD has to be destined for solicited node multicast address.
691 */
692 if (dad &&
693 !(daddr->s6_addr32[0] == htonl(0xff020000) &&
694 daddr->s6_addr32[1] == htonl(0x00000000) &&
695 daddr->s6_addr32[2] == htonl(0x00000001) &&
696 daddr->s6_addr [12] == 0xff )) {
697 ND_PRINTK2(KERN_WARNING
698 "ICMPv6 NS: bad DAD packet (wrong destination)\n");
699 return;
700 }
701
702 if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts)) {
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900703 ND_PRINTK2(KERN_WARNING
Linus Torvalds1da177e2005-04-16 15:20:36 -0700704 "ICMPv6 NS: invalid ND options\n");
705 return;
706 }
707
708 if (ndopts.nd_opts_src_lladdr) {
709 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr, dev);
710 if (!lladdr) {
711 ND_PRINTK2(KERN_WARNING
712 "ICMPv6 NS: invalid link-layer address length\n");
713 return;
714 }
715
716 /* RFC2461 7.1.1:
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900717 * If the IP source address is the unspecified address,
718 * there MUST NOT be source link-layer address option
Linus Torvalds1da177e2005-04-16 15:20:36 -0700719 * in the message.
720 */
721 if (dad) {
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900722 ND_PRINTK2(KERN_WARNING
Linus Torvalds1da177e2005-04-16 15:20:36 -0700723 "ICMPv6 NS: bad DAD packet (link-layer address option)\n");
724 return;
725 }
726 }
727
728 inc = ipv6_addr_is_multicast(daddr);
729
Daniel Lezcanoa18bc692008-03-07 11:14:49 -0800730 ifp = ipv6_get_ifaddr(dev->nd_net, &msg->target, dev, 1);
731 if (ifp) {
Neil Horman95c385b2007-04-25 17:08:10 -0700732
733 if (ifp->flags & (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)) {
734 if (dad) {
735 if (dev->type == ARPHRD_IEEE802_TR) {
Arnaldo Carvalho de Melo98e399f2007-03-19 15:33:04 -0700736 const unsigned char *sadr;
737 sadr = skb_mac_header(skb);
Neil Horman95c385b2007-04-25 17:08:10 -0700738 if (((sadr[8] ^ dev->dev_addr[0]) & 0x7f) == 0 &&
739 sadr[9] == dev->dev_addr[1] &&
740 sadr[10] == dev->dev_addr[2] &&
741 sadr[11] == dev->dev_addr[3] &&
742 sadr[12] == dev->dev_addr[4] &&
743 sadr[13] == dev->dev_addr[5]) {
744 /* looped-back to us */
745 goto out;
746 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700747 }
Neil Horman95c385b2007-04-25 17:08:10 -0700748
749 /*
750 * We are colliding with another node
751 * who is doing DAD
752 * so fail our DAD process
753 */
754 addrconf_dad_failure(ifp);
Denis V. Lunev9e3be4b2007-09-11 11:04:49 +0200755 return;
Neil Horman95c385b2007-04-25 17:08:10 -0700756 } else {
757 /*
758 * This is not a dad solicitation.
759 * If we are an optimistic node,
760 * we should respond.
761 * Otherwise, we should ignore it.
762 */
763 if (!(ifp->flags & IFA_F_OPTIMISTIC))
764 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700765 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700766 }
767
768 idev = ifp->idev;
769 } else {
770 idev = in6_dev_get(dev);
771 if (!idev) {
772 /* XXX: count this drop? */
773 return;
774 }
775
776 if (ipv6_chk_acast_addr(dev, &msg->target) ||
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900777 (idev->cnf.forwarding &&
YOSHIFUJI Hideakifbea49e2006-09-22 14:43:49 -0700778 (ipv6_devconf.proxy_ndp || idev->cnf.proxy_ndp) &&
Daniel Lezcanoa18bc692008-03-07 11:14:49 -0800779 (pneigh = pneigh_lookup(&nd_tbl, dev->nd_net,
Ville Nuorvala62dd9312006-09-22 14:43:19 -0700780 &msg->target, dev, 0)) != NULL)) {
Patrick McHardya61bbcf2005-08-14 17:24:31 -0700781 if (!(NEIGH_CB(skb)->flags & LOCALLY_ENQUEUED) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700782 skb->pkt_type != PACKET_HOST &&
783 inc != 0 &&
784 idev->nd_parms->proxy_delay != 0) {
785 /*
786 * for anycast or proxy,
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900787 * sender should delay its response
788 * by a random time between 0 and
Linus Torvalds1da177e2005-04-16 15:20:36 -0700789 * MAX_ANYCAST_DELAY_TIME seconds.
790 * (RFC2461) -- yoshfuji
791 */
792 struct sk_buff *n = skb_clone(skb, GFP_ATOMIC);
793 if (n)
794 pneigh_enqueue(&nd_tbl, idev->nd_parms, n);
795 goto out;
796 }
797 } else
798 goto out;
799 }
800
YOSHIFUJI Hideakifc26d0a2006-09-22 14:44:53 -0700801 is_router = !!(pneigh ? pneigh->flags & NTF_ROUTER : idev->cnf.forwarding);
Ville Nuorvala62dd9312006-09-22 14:43:19 -0700802
Linus Torvalds1da177e2005-04-16 15:20:36 -0700803 if (dad) {
804 struct in6_addr maddr;
805
806 ipv6_addr_all_nodes(&maddr);
807 ndisc_send_na(dev, NULL, &maddr, &msg->target,
Ville Nuorvala62dd9312006-09-22 14:43:19 -0700808 is_router, 0, (ifp != NULL), 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700809 goto out;
810 }
811
812 if (inc)
813 NEIGH_CACHE_STAT_INC(&nd_tbl, rcv_probes_mcast);
814 else
815 NEIGH_CACHE_STAT_INC(&nd_tbl, rcv_probes_ucast);
816
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900817 /*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700818 * update / create cache entry
819 * for the source address
820 */
821 neigh = __neigh_lookup(&nd_tbl, saddr, dev,
822 !inc || lladdr || !dev->addr_len);
823 if (neigh)
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900824 neigh_update(neigh, lladdr, NUD_STALE,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700825 NEIGH_UPDATE_F_WEAK_OVERRIDE|
826 NEIGH_UPDATE_F_OVERRIDE);
Stephen Hemminger3b04ddd2007-10-09 01:40:57 -0700827 if (neigh || !dev->header_ops) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700828 ndisc_send_na(dev, neigh, saddr, &msg->target,
Ville Nuorvala62dd9312006-09-22 14:43:19 -0700829 is_router,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700830 1, (ifp != NULL && inc), inc);
831 if (neigh)
832 neigh_release(neigh);
833 }
834
835out:
836 if (ifp)
837 in6_ifa_put(ifp);
838 else
839 in6_dev_put(idev);
840
841 return;
842}
843
844static void ndisc_recv_na(struct sk_buff *skb)
845{
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -0700846 struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb);
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -0700847 struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
848 struct in6_addr *daddr = &ipv6_hdr(skb)->daddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700849 u8 *lladdr = NULL;
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -0700850 u32 ndoptlen = skb->tail - (skb->transport_header +
851 offsetof(struct nd_msg, opt));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700852 struct ndisc_options ndopts;
853 struct net_device *dev = skb->dev;
854 struct inet6_ifaddr *ifp;
855 struct neighbour *neigh;
856
857 if (skb->len < sizeof(struct nd_msg)) {
858 ND_PRINTK2(KERN_WARNING
859 "ICMPv6 NA: packet too short\n");
860 return;
861 }
862
863 if (ipv6_addr_is_multicast(&msg->target)) {
864 ND_PRINTK2(KERN_WARNING
865 "ICMPv6 NA: target address is multicast.\n");
866 return;
867 }
868
869 if (ipv6_addr_is_multicast(daddr) &&
870 msg->icmph.icmp6_solicited) {
871 ND_PRINTK2(KERN_WARNING
872 "ICMPv6 NA: solicited NA is multicasted.\n");
873 return;
874 }
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900875
Linus Torvalds1da177e2005-04-16 15:20:36 -0700876 if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts)) {
877 ND_PRINTK2(KERN_WARNING
878 "ICMPv6 NS: invalid ND option\n");
879 return;
880 }
881 if (ndopts.nd_opts_tgt_lladdr) {
882 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr, dev);
883 if (!lladdr) {
884 ND_PRINTK2(KERN_WARNING
885 "ICMPv6 NA: invalid link-layer address length\n");
886 return;
887 }
888 }
Daniel Lezcanoa18bc692008-03-07 11:14:49 -0800889 ifp = ipv6_get_ifaddr(dev->nd_net, &msg->target, dev, 1);
890 if (ifp) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700891 if (ifp->flags & IFA_F_TENTATIVE) {
892 addrconf_dad_failure(ifp);
893 return;
894 }
895 /* What should we make now? The advertisement
896 is invalid, but ndisc specs say nothing
897 about it. It could be misconfiguration, or
898 an smart proxy agent tries to help us :-)
899 */
900 ND_PRINTK1(KERN_WARNING
901 "ICMPv6 NA: someone advertises our address on %s!\n",
902 ifp->idev->dev->name);
903 in6_ifa_put(ifp);
904 return;
905 }
906 neigh = neigh_lookup(&nd_tbl, &msg->target, dev);
907
908 if (neigh) {
909 u8 old_flags = neigh->flags;
910
911 if (neigh->nud_state & NUD_FAILED)
912 goto out;
913
Ville Nuorvala5f3e6e92006-09-22 14:42:46 -0700914 /*
915 * Don't update the neighbor cache entry on a proxy NA from
916 * ourselves because either the proxied node is off link or it
917 * has already sent a NA to us.
918 */
919 if (lladdr && !memcmp(lladdr, dev->dev_addr, dev->addr_len) &&
YOSHIFUJI Hideakifbea49e2006-09-22 14:43:49 -0700920 ipv6_devconf.forwarding && ipv6_devconf.proxy_ndp &&
Daniel Lezcanoa18bc692008-03-07 11:14:49 -0800921 pneigh_lookup(&nd_tbl, dev->nd_net, &msg->target, dev, 0)) {
YOSHIFUJI Hideakifbea49e2006-09-22 14:43:49 -0700922 /* XXX: idev->cnf.prixy_ndp */
Ville Nuorvala5f3e6e92006-09-22 14:42:46 -0700923 goto out;
YOSHIFUJI Hideakifbea49e2006-09-22 14:43:49 -0700924 }
Ville Nuorvala5f3e6e92006-09-22 14:42:46 -0700925
Linus Torvalds1da177e2005-04-16 15:20:36 -0700926 neigh_update(neigh, lladdr,
927 msg->icmph.icmp6_solicited ? NUD_REACHABLE : NUD_STALE,
928 NEIGH_UPDATE_F_WEAK_OVERRIDE|
929 (msg->icmph.icmp6_override ? NEIGH_UPDATE_F_OVERRIDE : 0)|
930 NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
931 (msg->icmph.icmp6_router ? NEIGH_UPDATE_F_ISROUTER : 0));
932
933 if ((old_flags & ~neigh->flags) & NTF_ROUTER) {
934 /*
935 * Change: router to host
936 */
937 struct rt6_info *rt;
938 rt = rt6_get_dflt_router(saddr, dev);
939 if (rt)
Thomas Grafe0a1ad732006-08-22 00:00:21 -0700940 ip6_del_rt(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700941 }
942
943out:
944 neigh_release(neigh);
945 }
946}
947
948static void ndisc_recv_rs(struct sk_buff *skb)
949{
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -0700950 struct rs_msg *rs_msg = (struct rs_msg *)skb_transport_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700951 unsigned long ndoptlen = skb->len - sizeof(*rs_msg);
952 struct neighbour *neigh;
953 struct inet6_dev *idev;
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -0700954 struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700955 struct ndisc_options ndopts;
956 u8 *lladdr = NULL;
957
958 if (skb->len < sizeof(*rs_msg))
959 return;
960
961 idev = in6_dev_get(skb->dev);
962 if (!idev) {
963 if (net_ratelimit())
964 ND_PRINTK1("ICMP6 RS: can't find in6 device\n");
965 return;
966 }
967
968 /* Don't accept RS if we're not in router mode */
969 if (!idev->cnf.forwarding)
970 goto out;
971
972 /*
973 * Don't update NCE if src = ::;
974 * this implies that the source node has no ip address assigned yet.
975 */
976 if (ipv6_addr_any(saddr))
977 goto out;
978
979 /* Parse ND options */
980 if (!ndisc_parse_options(rs_msg->opt, ndoptlen, &ndopts)) {
981 if (net_ratelimit())
982 ND_PRINTK2("ICMP6 NS: invalid ND option, ignored\n");
983 goto out;
984 }
985
986 if (ndopts.nd_opts_src_lladdr) {
987 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr,
988 skb->dev);
989 if (!lladdr)
990 goto out;
991 }
992
993 neigh = __neigh_lookup(&nd_tbl, saddr, skb->dev, 1);
994 if (neigh) {
995 neigh_update(neigh, lladdr, NUD_STALE,
996 NEIGH_UPDATE_F_WEAK_OVERRIDE|
997 NEIGH_UPDATE_F_OVERRIDE|
998 NEIGH_UPDATE_F_OVERRIDE_ISROUTER);
999 neigh_release(neigh);
1000 }
1001out:
1002 in6_dev_put(idev);
1003}
1004
Pierre Ynard31910572007-10-10 21:22:05 -07001005static void ndisc_ra_useropt(struct sk_buff *ra, struct nd_opt_hdr *opt)
1006{
1007 struct icmp6hdr *icmp6h = (struct icmp6hdr *)skb_transport_header(ra);
1008 struct sk_buff *skb;
1009 struct nlmsghdr *nlh;
1010 struct nduseroptmsg *ndmsg;
Daniel Lezcanoa18bc692008-03-07 11:14:49 -08001011 struct net *net = ra->dev->nd_net;
Pierre Ynard31910572007-10-10 21:22:05 -07001012 int err;
1013 int base_size = NLMSG_ALIGN(sizeof(struct nduseroptmsg)
1014 + (opt->nd_opt_len << 3));
1015 size_t msg_size = base_size + nla_total_size(sizeof(struct in6_addr));
1016
1017 skb = nlmsg_new(msg_size, GFP_ATOMIC);
1018 if (skb == NULL) {
1019 err = -ENOBUFS;
1020 goto errout;
1021 }
1022
1023 nlh = nlmsg_put(skb, 0, 0, RTM_NEWNDUSEROPT, base_size, 0);
1024 if (nlh == NULL) {
1025 goto nla_put_failure;
1026 }
1027
1028 ndmsg = nlmsg_data(nlh);
1029 ndmsg->nduseropt_family = AF_INET6;
Pierre Ynarddbb2ed22007-11-12 17:58:35 -08001030 ndmsg->nduseropt_ifindex = ra->dev->ifindex;
Pierre Ynard31910572007-10-10 21:22:05 -07001031 ndmsg->nduseropt_icmp_type = icmp6h->icmp6_type;
1032 ndmsg->nduseropt_icmp_code = icmp6h->icmp6_code;
1033 ndmsg->nduseropt_opts_len = opt->nd_opt_len << 3;
1034
1035 memcpy(ndmsg + 1, opt, opt->nd_opt_len << 3);
1036
1037 NLA_PUT(skb, NDUSEROPT_SRCADDR, sizeof(struct in6_addr),
1038 &ipv6_hdr(ra)->saddr);
1039 nlmsg_end(skb, nlh);
1040
Daniel Lezcanoa18bc692008-03-07 11:14:49 -08001041 err = rtnl_notify(skb, net, 0, RTNLGRP_ND_USEROPT, NULL,
Denis V. Lunev97c53ca2007-11-19 22:26:51 -08001042 GFP_ATOMIC);
Pierre Ynard31910572007-10-10 21:22:05 -07001043 if (err < 0)
1044 goto errout;
1045
1046 return;
1047
1048nla_put_failure:
1049 nlmsg_free(skb);
1050 err = -EMSGSIZE;
1051errout:
Daniel Lezcanoa18bc692008-03-07 11:14:49 -08001052 rtnl_set_sk_err(net, RTNLGRP_ND_USEROPT, err);
Pierre Ynard31910572007-10-10 21:22:05 -07001053}
1054
Linus Torvalds1da177e2005-04-16 15:20:36 -07001055static void ndisc_router_discovery(struct sk_buff *skb)
1056{
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -07001057 struct ra_msg *ra_msg = (struct ra_msg *)skb_transport_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001058 struct neighbour *neigh = NULL;
1059 struct inet6_dev *in6_dev;
YOSHIFUJI Hideaki65f5c7c2006-03-20 16:55:08 -08001060 struct rt6_info *rt = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001061 int lifetime;
1062 struct ndisc_options ndopts;
1063 int optlen;
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001064 unsigned int pref = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001065
1066 __u8 * opt = (__u8 *)(ra_msg + 1);
1067
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -07001068 optlen = (skb->tail - skb->transport_header) - sizeof(struct ra_msg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001069
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001070 if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001071 ND_PRINTK2(KERN_WARNING
1072 "ICMPv6 RA: source address is not link-local.\n");
1073 return;
1074 }
1075 if (optlen < 0) {
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001076 ND_PRINTK2(KERN_WARNING
Linus Torvalds1da177e2005-04-16 15:20:36 -07001077 "ICMPv6 RA: packet too short\n");
1078 return;
1079 }
1080
1081 /*
1082 * set the RA_RECV flag in the interface
1083 */
1084
1085 in6_dev = in6_dev_get(skb->dev);
1086 if (in6_dev == NULL) {
1087 ND_PRINTK0(KERN_ERR
1088 "ICMPv6 RA: can't find inet6 device for %s.\n",
1089 skb->dev->name);
1090 return;
1091 }
1092 if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_ra) {
1093 in6_dev_put(in6_dev);
1094 return;
1095 }
1096
1097 if (!ndisc_parse_options(opt, optlen, &ndopts)) {
1098 in6_dev_put(in6_dev);
1099 ND_PRINTK2(KERN_WARNING
1100 "ICMP6 RA: invalid ND options\n");
1101 return;
1102 }
1103
1104 if (in6_dev->if_flags & IF_RS_SENT) {
1105 /*
1106 * flag that an RA was received after an RS was sent
1107 * out on this interface.
1108 */
1109 in6_dev->if_flags |= IF_RA_RCVD;
1110 }
1111
1112 /*
1113 * Remember the managed/otherconf flags from most recently
1114 * received RA message (RFC 2462) -- yoshfuji
1115 */
1116 in6_dev->if_flags = (in6_dev->if_flags & ~(IF_RA_MANAGED |
1117 IF_RA_OTHERCONF)) |
1118 (ra_msg->icmph.icmp6_addrconf_managed ?
1119 IF_RA_MANAGED : 0) |
1120 (ra_msg->icmph.icmp6_addrconf_other ?
1121 IF_RA_OTHERCONF : 0);
1122
YOSHIFUJI Hideaki65f5c7c2006-03-20 16:55:08 -08001123 if (!in6_dev->cnf.accept_ra_defrtr)
1124 goto skip_defrtr;
1125
Linus Torvalds1da177e2005-04-16 15:20:36 -07001126 lifetime = ntohs(ra_msg->icmph.icmp6_rt_lifetime);
1127
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001128#ifdef CONFIG_IPV6_ROUTER_PREF
1129 pref = ra_msg->icmph.icmp6_router_pref;
1130 /* 10b is handled as if it were 00b (medium) */
YOSHIFUJI Hideaki930d6ff2006-03-20 17:05:30 -08001131 if (pref == ICMPV6_ROUTER_PREF_INVALID ||
YOSHIFUJI Hideaki6d5b78c2007-06-22 16:07:04 -07001132 !in6_dev->cnf.accept_ra_rtr_pref)
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001133 pref = ICMPV6_ROUTER_PREF_MEDIUM;
1134#endif
1135
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001136 rt = rt6_get_dflt_router(&ipv6_hdr(skb)->saddr, skb->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001137
1138 if (rt)
1139 neigh = rt->rt6i_nexthop;
1140
1141 if (rt && lifetime == 0) {
1142 neigh_clone(neigh);
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001143 ip6_del_rt(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001144 rt = NULL;
1145 }
1146
1147 if (rt == NULL && lifetime) {
1148 ND_PRINTK3(KERN_DEBUG
1149 "ICMPv6 RA: adding default router.\n");
1150
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001151 rt = rt6_add_dflt_router(&ipv6_hdr(skb)->saddr, skb->dev, pref);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001152 if (rt == NULL) {
1153 ND_PRINTK0(KERN_ERR
1154 "ICMPv6 RA: %s() failed to add default route.\n",
Harvey Harrison0dc47872008-03-05 20:47:47 -08001155 __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001156 in6_dev_put(in6_dev);
1157 return;
1158 }
1159
1160 neigh = rt->rt6i_nexthop;
1161 if (neigh == NULL) {
1162 ND_PRINTK0(KERN_ERR
1163 "ICMPv6 RA: %s() got default router without neighbour.\n",
Harvey Harrison0dc47872008-03-05 20:47:47 -08001164 __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001165 dst_release(&rt->u.dst);
1166 in6_dev_put(in6_dev);
1167 return;
1168 }
1169 neigh->flags |= NTF_ROUTER;
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001170 } else if (rt) {
1171 rt->rt6i_flags |= (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001172 }
1173
1174 if (rt)
1175 rt->rt6i_expires = jiffies + (HZ * lifetime);
1176
1177 if (ra_msg->icmph.icmp6_hop_limit) {
1178 in6_dev->cnf.hop_limit = ra_msg->icmph.icmp6_hop_limit;
1179 if (rt)
1180 rt->u.dst.metrics[RTAX_HOPLIMIT-1] = ra_msg->icmph.icmp6_hop_limit;
1181 }
1182
YOSHIFUJI Hideaki65f5c7c2006-03-20 16:55:08 -08001183skip_defrtr:
1184
Linus Torvalds1da177e2005-04-16 15:20:36 -07001185 /*
1186 * Update Reachable Time and Retrans Timer
1187 */
1188
1189 if (in6_dev->nd_parms) {
1190 unsigned long rtime = ntohl(ra_msg->retrans_timer);
1191
1192 if (rtime && rtime/1000 < MAX_SCHEDULE_TIMEOUT/HZ) {
1193 rtime = (rtime*HZ)/1000;
1194 if (rtime < HZ/10)
1195 rtime = HZ/10;
1196 in6_dev->nd_parms->retrans_time = rtime;
1197 in6_dev->tstamp = jiffies;
1198 inet6_ifinfo_notify(RTM_NEWLINK, in6_dev);
1199 }
1200
1201 rtime = ntohl(ra_msg->reachable_time);
1202 if (rtime && rtime/1000 < MAX_SCHEDULE_TIMEOUT/(3*HZ)) {
1203 rtime = (rtime*HZ)/1000;
1204
1205 if (rtime < HZ/10)
1206 rtime = HZ/10;
1207
1208 if (rtime != in6_dev->nd_parms->base_reachable_time) {
1209 in6_dev->nd_parms->base_reachable_time = rtime;
1210 in6_dev->nd_parms->gc_staletime = 3 * rtime;
1211 in6_dev->nd_parms->reachable_time = neigh_rand_reach_time(rtime);
1212 in6_dev->tstamp = jiffies;
1213 inet6_ifinfo_notify(RTM_NEWLINK, in6_dev);
1214 }
1215 }
1216 }
1217
1218 /*
1219 * Process options.
1220 */
1221
1222 if (!neigh)
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001223 neigh = __neigh_lookup(&nd_tbl, &ipv6_hdr(skb)->saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001224 skb->dev, 1);
1225 if (neigh) {
1226 u8 *lladdr = NULL;
1227 if (ndopts.nd_opts_src_lladdr) {
1228 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr,
1229 skb->dev);
1230 if (!lladdr) {
1231 ND_PRINTK2(KERN_WARNING
1232 "ICMPv6 RA: invalid link-layer address length\n");
1233 goto out;
1234 }
1235 }
1236 neigh_update(neigh, lladdr, NUD_STALE,
1237 NEIGH_UPDATE_F_WEAK_OVERRIDE|
1238 NEIGH_UPDATE_F_OVERRIDE|
1239 NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
1240 NEIGH_UPDATE_F_ISROUTER);
1241 }
1242
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001243#ifdef CONFIG_IPV6_ROUTE_INFO
YOSHIFUJI Hideaki09c884d2006-03-20 17:07:03 -08001244 if (in6_dev->cnf.accept_ra_rtr_pref && ndopts.nd_opts_ri) {
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001245 struct nd_opt_hdr *p;
1246 for (p = ndopts.nd_opts_ri;
1247 p;
1248 p = ndisc_next_option(p, ndopts.nd_opts_ri_end)) {
YOSHIFUJI Hideaki09c884d2006-03-20 17:07:03 -08001249 if (((struct route_info *)p)->prefix_len > in6_dev->cnf.accept_ra_rt_info_max_plen)
1250 continue;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001251 rt6_route_rcv(skb->dev, (u8*)p, (p->nd_opt_len) << 3,
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001252 &ipv6_hdr(skb)->saddr);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001253 }
1254 }
1255#endif
1256
YOSHIFUJI Hideakic4fd30e2006-03-20 16:55:26 -08001257 if (in6_dev->cnf.accept_ra_pinfo && ndopts.nd_opts_pi) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001258 struct nd_opt_hdr *p;
1259 for (p = ndopts.nd_opts_pi;
1260 p;
1261 p = ndisc_next_option(p, ndopts.nd_opts_pi_end)) {
1262 addrconf_prefix_rcv(skb->dev, (u8*)p, (p->nd_opt_len) << 3);
1263 }
1264 }
1265
1266 if (ndopts.nd_opts_mtu) {
Al Viroe69a4ad2006-11-14 20:56:00 -08001267 __be32 n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001268 u32 mtu;
1269
Al Viroe69a4ad2006-11-14 20:56:00 -08001270 memcpy(&n, ((u8*)(ndopts.nd_opts_mtu+1))+2, sizeof(mtu));
1271 mtu = ntohl(n);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001272
1273 if (mtu < IPV6_MIN_MTU || mtu > skb->dev->mtu) {
1274 ND_PRINTK2(KERN_WARNING
1275 "ICMPv6 RA: invalid mtu: %d\n",
1276 mtu);
1277 } else if (in6_dev->cnf.mtu6 != mtu) {
1278 in6_dev->cnf.mtu6 = mtu;
1279
1280 if (rt)
1281 rt->u.dst.metrics[RTAX_MTU-1] = mtu;
1282
1283 rt6_mtu_change(skb->dev, mtu);
1284 }
1285 }
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001286
Pierre Ynard31910572007-10-10 21:22:05 -07001287 if (ndopts.nd_useropts) {
YOSHIFUJI Hideaki61cf46a2008-01-22 17:32:53 +09001288 struct nd_opt_hdr *p;
1289 for (p = ndopts.nd_useropts;
1290 p;
1291 p = ndisc_next_useropt(p, ndopts.nd_useropts_end)) {
1292 ndisc_ra_useropt(skb, p);
Pierre Ynard31910572007-10-10 21:22:05 -07001293 }
1294 }
1295
Linus Torvalds1da177e2005-04-16 15:20:36 -07001296 if (ndopts.nd_opts_tgt_lladdr || ndopts.nd_opts_rh) {
1297 ND_PRINTK2(KERN_WARNING
1298 "ICMPv6 RA: invalid RA options");
1299 }
1300out:
1301 if (rt)
1302 dst_release(&rt->u.dst);
1303 else if (neigh)
1304 neigh_release(neigh);
1305 in6_dev_put(in6_dev);
1306}
1307
1308static void ndisc_redirect_rcv(struct sk_buff *skb)
1309{
1310 struct inet6_dev *in6_dev;
1311 struct icmp6hdr *icmph;
1312 struct in6_addr *dest;
1313 struct in6_addr *target; /* new first hop to destination */
1314 struct neighbour *neigh;
1315 int on_link = 0;
1316 struct ndisc_options ndopts;
1317 int optlen;
1318 u8 *lladdr = NULL;
1319
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001320 if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001321 ND_PRINTK2(KERN_WARNING
1322 "ICMPv6 Redirect: source address is not link-local.\n");
1323 return;
1324 }
1325
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -07001326 optlen = skb->tail - skb->transport_header;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001327 optlen -= sizeof(struct icmp6hdr) + 2 * sizeof(struct in6_addr);
1328
1329 if (optlen < 0) {
1330 ND_PRINTK2(KERN_WARNING
1331 "ICMPv6 Redirect: packet too short\n");
1332 return;
1333 }
1334
Arnaldo Carvalho de Melocc70ab22007-03-13 14:03:22 -03001335 icmph = icmp6_hdr(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001336 target = (struct in6_addr *) (icmph + 1);
1337 dest = target + 1;
1338
1339 if (ipv6_addr_is_multicast(dest)) {
1340 ND_PRINTK2(KERN_WARNING
1341 "ICMPv6 Redirect: destination address is multicast.\n");
1342 return;
1343 }
1344
1345 if (ipv6_addr_equal(dest, target)) {
1346 on_link = 1;
Brian Haleybf0b48d2007-10-08 00:12:05 -07001347 } else if (ipv6_addr_type(target) !=
1348 (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) {
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001349 ND_PRINTK2(KERN_WARNING
Brian Haleybf0b48d2007-10-08 00:12:05 -07001350 "ICMPv6 Redirect: target address is not link-local unicast.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001351 return;
1352 }
1353
1354 in6_dev = in6_dev_get(skb->dev);
1355 if (!in6_dev)
1356 return;
1357 if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects) {
1358 in6_dev_put(in6_dev);
1359 return;
1360 }
1361
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001362 /* RFC2461 8.1:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001363 * The IP source address of the Redirect MUST be the same as the current
1364 * first-hop router for the specified ICMP Destination Address.
1365 */
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001366
Linus Torvalds1da177e2005-04-16 15:20:36 -07001367 if (!ndisc_parse_options((u8*)(dest + 1), optlen, &ndopts)) {
1368 ND_PRINTK2(KERN_WARNING
1369 "ICMPv6 Redirect: invalid ND options\n");
1370 in6_dev_put(in6_dev);
1371 return;
1372 }
1373 if (ndopts.nd_opts_tgt_lladdr) {
1374 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr,
1375 skb->dev);
1376 if (!lladdr) {
1377 ND_PRINTK2(KERN_WARNING
1378 "ICMPv6 Redirect: invalid link-layer address length\n");
1379 in6_dev_put(in6_dev);
1380 return;
1381 }
1382 }
1383
1384 neigh = __neigh_lookup(&nd_tbl, target, skb->dev, 1);
1385 if (neigh) {
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001386 rt6_redirect(dest, &ipv6_hdr(skb)->daddr,
1387 &ipv6_hdr(skb)->saddr, neigh, lladdr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001388 on_link);
1389 neigh_release(neigh);
1390 }
1391 in6_dev_put(in6_dev);
1392}
1393
1394void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
1395 struct in6_addr *target)
1396{
1397 struct sock *sk = ndisc_socket->sk;
1398 int len = sizeof(struct icmp6hdr) + 2 * sizeof(struct in6_addr);
1399 struct sk_buff *buff;
1400 struct icmp6hdr *icmph;
1401 struct in6_addr saddr_buf;
1402 struct in6_addr *addrp;
1403 struct net_device *dev;
1404 struct rt6_info *rt;
1405 struct dst_entry *dst;
1406 struct inet6_dev *idev;
1407 struct flowi fl;
1408 u8 *opt;
1409 int rd_len;
1410 int err;
1411 int hlen;
1412 u8 ha_buf[MAX_ADDR_LEN], *ha = NULL;
1413
1414 dev = skb->dev;
1415
Neil Horman95c385b2007-04-25 17:08:10 -07001416 if (ipv6_get_lladdr(dev, &saddr_buf, IFA_F_TENTATIVE)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001417 ND_PRINTK2(KERN_WARNING
1418 "ICMPv6 Redirect: no link-local address on %s\n",
1419 dev->name);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001420 return;
1421 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001422
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001423 if (!ipv6_addr_equal(&ipv6_hdr(skb)->daddr, target) &&
Brian Haleybf0b48d2007-10-08 00:12:05 -07001424 ipv6_addr_type(target) != (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) {
Li Yewang29556522007-01-30 14:33:20 -08001425 ND_PRINTK2(KERN_WARNING
Brian Haleybf0b48d2007-10-08 00:12:05 -07001426 "ICMPv6 Redirect: target address is not link-local unicast.\n");
Li Yewang29556522007-01-30 14:33:20 -08001427 return;
1428 }
1429
YOSHIFUJI Hideaki95e41e92007-12-06 15:43:30 -08001430 icmpv6_flow_init(ndisc_socket->sk, &fl, NDISC_REDIRECT,
1431 &saddr_buf, &ipv6_hdr(skb)->saddr, dev->ifindex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001432
Daniel Lezcano4591db42008-03-05 10:48:10 -08001433 dst = ip6_route_output(&init_net, NULL, &fl);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001434 if (dst == NULL)
1435 return;
1436
1437 err = xfrm_lookup(&dst, &fl, NULL, 0);
Patrick McHardye1044112005-09-08 15:11:55 -07001438 if (err)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001439 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001440
1441 rt = (struct rt6_info *) dst;
1442
1443 if (rt->rt6i_flags & RTF_GATEWAY) {
1444 ND_PRINTK2(KERN_WARNING
1445 "ICMPv6 Redirect: destination is not a neighbour.\n");
1446 dst_release(dst);
1447 return;
1448 }
1449 if (!xrlim_allow(dst, 1*HZ)) {
1450 dst_release(dst);
1451 return;
1452 }
1453
1454 if (dev->addr_len) {
1455 read_lock_bh(&neigh->lock);
1456 if (neigh->nud_state & NUD_VALID) {
1457 memcpy(ha_buf, neigh->ha, dev->addr_len);
1458 read_unlock_bh(&neigh->lock);
1459 ha = ha_buf;
1460 len += ndisc_opt_addr_space(dev);
1461 } else
1462 read_unlock_bh(&neigh->lock);
1463 }
1464
1465 rd_len = min_t(unsigned int,
1466 IPV6_MIN_MTU-sizeof(struct ipv6hdr)-len, skb->len + 8);
1467 rd_len &= ~0x7;
1468 len += rd_len;
1469
David S. Millerd54a81d2006-12-02 21:00:06 -08001470 buff = sock_alloc_send_skb(sk,
1471 (MAX_HEADER + sizeof(struct ipv6hdr) +
1472 len + LL_RESERVED_SPACE(dev)),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001473 1, &err);
1474 if (buff == NULL) {
1475 ND_PRINTK0(KERN_ERR
1476 "ICMPv6 Redirect: %s() failed to allocate an skb.\n",
Harvey Harrison0dc47872008-03-05 20:47:47 -08001477 __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001478 dst_release(dst);
1479 return;
1480 }
1481
1482 hlen = 0;
1483
1484 skb_reserve(buff, LL_RESERVED_SPACE(dev));
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001485 ip6_nd_hdr(sk, buff, dev, &saddr_buf, &ipv6_hdr(skb)->saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001486 IPPROTO_ICMPV6, len);
1487
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -07001488 skb_set_transport_header(buff, skb_tail_pointer(buff) - buff->data);
Arnaldo Carvalho de Melod10ba342007-03-14 21:05:37 -03001489 skb_put(buff, len);
1490 icmph = icmp6_hdr(buff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001491
1492 memset(icmph, 0, sizeof(struct icmp6hdr));
1493 icmph->icmp6_type = NDISC_REDIRECT;
1494
1495 /*
1496 * copy target and destination addresses
1497 */
1498
1499 addrp = (struct in6_addr *)(icmph + 1);
1500 ipv6_addr_copy(addrp, target);
1501 addrp++;
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001502 ipv6_addr_copy(addrp, &ipv6_hdr(skb)->daddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001503
1504 opt = (u8*) (addrp + 1);
1505
1506 /*
1507 * include target_address option
1508 */
1509
1510 if (ha)
1511 opt = ndisc_fill_addr_option(opt, ND_OPT_TARGET_LL_ADDR, ha,
1512 dev->addr_len, dev->type);
1513
1514 /*
1515 * build redirect option and copy skb over to the new packet.
1516 */
1517
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001518 memset(opt, 0, 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001519 *(opt++) = ND_OPT_REDIRECT_HDR;
1520 *(opt++) = (rd_len >> 3);
1521 opt += 6;
1522
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001523 memcpy(opt, ipv6_hdr(skb), rd_len - 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001524
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001525 icmph->icmp6_cksum = csum_ipv6_magic(&saddr_buf, &ipv6_hdr(skb)->saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001526 len, IPPROTO_ICMPV6,
1527 csum_partial((u8 *) icmph, len, 0));
1528
1529 buff->dst = dst;
1530 idev = in6_dev_get(dst->dev);
YOSHIFUJI Hideakia11d2062006-11-04 20:11:37 +09001531 IP6_INC_STATS(idev, IPSTATS_MIB_OUTREQUESTS);
Patrick McHardy6e23ae22007-11-19 18:53:30 -08001532 err = NF_HOOK(PF_INET6, NF_INET_LOCAL_OUT, buff, NULL, dst->dev,
1533 dst_output);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001534 if (!err) {
David L Stevens14878f72007-09-16 16:52:35 -07001535 ICMP6MSGOUT_INC_STATS(idev, NDISC_REDIRECT);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001536 ICMP6_INC_STATS(idev, ICMP6_MIB_OUTMSGS);
1537 }
1538
1539 if (likely(idev != NULL))
1540 in6_dev_put(idev);
1541}
1542
1543static void pndisc_redo(struct sk_buff *skb)
1544{
YOSHIFUJI Hideaki140e26fc2005-10-05 12:11:41 -07001545 ndisc_recv_ns(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001546 kfree_skb(skb);
1547}
1548
1549int ndisc_rcv(struct sk_buff *skb)
1550{
1551 struct nd_msg *msg;
1552
1553 if (!pskb_may_pull(skb, skb->len))
1554 return 0;
1555
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -07001556 msg = (struct nd_msg *)skb_transport_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001557
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -07001558 __skb_push(skb, skb->data - skb_transport_header(skb));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001559
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001560 if (ipv6_hdr(skb)->hop_limit != 255) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001561 ND_PRINTK2(KERN_WARNING
1562 "ICMPv6 NDISC: invalid hop-limit: %d\n",
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001563 ipv6_hdr(skb)->hop_limit);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001564 return 0;
1565 }
1566
1567 if (msg->icmph.icmp6_code != 0) {
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001568 ND_PRINTK2(KERN_WARNING
Linus Torvalds1da177e2005-04-16 15:20:36 -07001569 "ICMPv6 NDISC: invalid ICMPv6 code: %d\n",
1570 msg->icmph.icmp6_code);
1571 return 0;
1572 }
1573
Patrick McHardya61bbcf2005-08-14 17:24:31 -07001574 memset(NEIGH_CB(skb), 0, sizeof(struct neighbour_cb));
1575
Linus Torvalds1da177e2005-04-16 15:20:36 -07001576 switch (msg->icmph.icmp6_type) {
1577 case NDISC_NEIGHBOUR_SOLICITATION:
1578 ndisc_recv_ns(skb);
1579 break;
1580
1581 case NDISC_NEIGHBOUR_ADVERTISEMENT:
1582 ndisc_recv_na(skb);
1583 break;
1584
1585 case NDISC_ROUTER_SOLICITATION:
1586 ndisc_recv_rs(skb);
1587 break;
1588
1589 case NDISC_ROUTER_ADVERTISEMENT:
1590 ndisc_router_discovery(skb);
1591 break;
1592
1593 case NDISC_REDIRECT:
1594 ndisc_redirect_rcv(skb);
1595 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -07001596 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001597
1598 return 0;
1599}
1600
1601static int ndisc_netdev_event(struct notifier_block *this, unsigned long event, void *ptr)
1602{
1603 struct net_device *dev = ptr;
Daniel Lezcano5b7c9312008-03-03 23:28:58 -08001604 struct net *net = dev->nd_net;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001605
1606 switch (event) {
1607 case NETDEV_CHANGEADDR:
1608 neigh_changeaddr(&nd_tbl, dev);
Daniel Lezcano5b7c9312008-03-03 23:28:58 -08001609 fib6_run_gc(~0UL, net);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001610 break;
1611 case NETDEV_DOWN:
1612 neigh_ifdown(&nd_tbl, dev);
Daniel Lezcano5b7c9312008-03-03 23:28:58 -08001613 fib6_run_gc(~0UL, net);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001614 break;
1615 default:
1616 break;
1617 }
1618
1619 return NOTIFY_DONE;
1620}
1621
1622static struct notifier_block ndisc_netdev_notifier = {
1623 .notifier_call = ndisc_netdev_event,
1624};
1625
1626#ifdef CONFIG_SYSCTL
1627static void ndisc_warn_deprecated_sysctl(struct ctl_table *ctl,
1628 const char *func, const char *dev_name)
1629{
1630 static char warncomm[TASK_COMM_LEN];
1631 static int warned;
1632 if (strcmp(warncomm, current->comm) && warned < 5) {
1633 strcpy(warncomm, current->comm);
1634 printk(KERN_WARNING
1635 "process `%s' is using deprecated sysctl (%s) "
1636 "net.ipv6.neigh.%s.%s; "
1637 "Use net.ipv6.neigh.%s.%s_ms "
1638 "instead.\n",
1639 warncomm, func,
1640 dev_name, ctl->procname,
1641 dev_name, ctl->procname);
1642 warned++;
1643 }
1644}
1645
1646int ndisc_ifinfo_sysctl_change(struct ctl_table *ctl, int write, struct file * filp, void __user *buffer, size_t *lenp, loff_t *ppos)
1647{
1648 struct net_device *dev = ctl->extra1;
1649 struct inet6_dev *idev;
1650 int ret;
1651
Eric W. Biedermand12af672007-10-18 03:05:25 -07001652 if ((strcmp(ctl->procname, "retrans_time") == 0) ||
1653 (strcmp(ctl->procname, "base_reachable_time") == 0))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001654 ndisc_warn_deprecated_sysctl(ctl, "syscall", dev ? dev->name : "default");
1655
Eric W. Biedermand12af672007-10-18 03:05:25 -07001656 if (strcmp(ctl->procname, "retrans_time") == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001657 ret = proc_dointvec(ctl, write, filp, buffer, lenp, ppos);
Eric W. Biedermand12af672007-10-18 03:05:25 -07001658
1659 else if (strcmp(ctl->procname, "base_reachable_time") == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001660 ret = proc_dointvec_jiffies(ctl, write,
1661 filp, buffer, lenp, ppos);
Eric W. Biedermand12af672007-10-18 03:05:25 -07001662
1663 else if ((strcmp(ctl->procname, "retrans_time_ms") == 0) ||
YOSHIFUJI Hideakiad02ac12007-10-29 01:32:23 -07001664 (strcmp(ctl->procname, "base_reachable_time_ms") == 0))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001665 ret = proc_dointvec_ms_jiffies(ctl, write,
1666 filp, buffer, lenp, ppos);
Eric W. Biedermand12af672007-10-18 03:05:25 -07001667 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001668 ret = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001669
1670 if (write && ret == 0 && dev && (idev = in6_dev_get(dev)) != NULL) {
Eric W. Biedermand12af672007-10-18 03:05:25 -07001671 if (ctl->data == &idev->nd_parms->base_reachable_time)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001672 idev->nd_parms->reachable_time = neigh_rand_reach_time(idev->nd_parms->base_reachable_time);
1673 idev->tstamp = jiffies;
1674 inet6_ifinfo_notify(RTM_NEWLINK, idev);
1675 in6_dev_put(idev);
1676 }
1677 return ret;
1678}
1679
1680static int ndisc_ifinfo_sysctl_strategy(ctl_table *ctl, int __user *name,
1681 int nlen, void __user *oldval,
1682 size_t __user *oldlenp,
Alexey Dobriyan1f29bcd2006-12-10 02:19:10 -08001683 void __user *newval, size_t newlen)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001684{
1685 struct net_device *dev = ctl->extra1;
1686 struct inet6_dev *idev;
1687 int ret;
1688
1689 if (ctl->ctl_name == NET_NEIGH_RETRANS_TIME ||
1690 ctl->ctl_name == NET_NEIGH_REACHABLE_TIME)
1691 ndisc_warn_deprecated_sysctl(ctl, "procfs", dev ? dev->name : "default");
1692
1693 switch (ctl->ctl_name) {
1694 case NET_NEIGH_REACHABLE_TIME:
1695 ret = sysctl_jiffies(ctl, name, nlen,
Alexey Dobriyan1f29bcd2006-12-10 02:19:10 -08001696 oldval, oldlenp, newval, newlen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001697 break;
1698 case NET_NEIGH_RETRANS_TIME_MS:
1699 case NET_NEIGH_REACHABLE_TIME_MS:
1700 ret = sysctl_ms_jiffies(ctl, name, nlen,
Alexey Dobriyan1f29bcd2006-12-10 02:19:10 -08001701 oldval, oldlenp, newval, newlen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001702 break;
1703 default:
1704 ret = 0;
1705 }
1706
1707 if (newval && newlen && ret > 0 &&
1708 dev && (idev = in6_dev_get(dev)) != NULL) {
1709 if (ctl->ctl_name == NET_NEIGH_REACHABLE_TIME ||
1710 ctl->ctl_name == NET_NEIGH_REACHABLE_TIME_MS)
1711 idev->nd_parms->reachable_time = neigh_rand_reach_time(idev->nd_parms->base_reachable_time);
1712 idev->tstamp = jiffies;
1713 inet6_ifinfo_notify(RTM_NEWLINK, idev);
1714 in6_dev_put(idev);
1715 }
1716
1717 return ret;
1718}
1719
1720#endif
1721
Denis V. Lunev9b0f9762008-02-29 11:13:15 -08001722int __init ndisc_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001723{
1724 struct ipv6_pinfo *np;
1725 struct sock *sk;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001726 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001727
1728 err = sock_create_kern(PF_INET6, SOCK_RAW, IPPROTO_ICMPV6, &ndisc_socket);
1729 if (err < 0) {
1730 ND_PRINTK0(KERN_ERR
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001731 "ICMPv6 NDISC: Failed to initialize the control socket (err %d).\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001732 err);
1733 ndisc_socket = NULL; /* For safety. */
1734 return err;
1735 }
1736
1737 sk = ndisc_socket->sk;
1738 np = inet6_sk(sk);
1739 sk->sk_allocation = GFP_ATOMIC;
1740 np->hop_limit = 255;
1741 /* Do not loopback ndisc messages */
1742 np->mc_loop = 0;
1743 sk->sk_prot->unhash(sk);
1744
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001745 /*
1746 * Initialize the neighbour table
1747 */
1748
Linus Torvalds1da177e2005-04-16 15:20:36 -07001749 neigh_table_init(&nd_tbl);
1750
1751#ifdef CONFIG_SYSCTL
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001752 neigh_sysctl_register(NULL, &nd_tbl.parms, NET_IPV6, NET_IPV6_NEIGH,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001753 "ipv6",
1754 &ndisc_ifinfo_sysctl_change,
1755 &ndisc_ifinfo_sysctl_strategy);
1756#endif
1757
1758 register_netdevice_notifier(&ndisc_netdev_notifier);
1759 return 0;
1760}
1761
1762void ndisc_cleanup(void)
1763{
Dmitry Mishin36f73d02006-11-03 16:08:19 -08001764 unregister_netdevice_notifier(&ndisc_netdev_notifier);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001765#ifdef CONFIG_SYSCTL
1766 neigh_sysctl_unregister(&nd_tbl.parms);
1767#endif
1768 neigh_table_clear(&nd_tbl);
1769 sock_release(ndisc_socket);
1770 ndisc_socket = NULL; /* For safety. */
1771}