blob: 0d33a7d32125b80f102d28e851202c16d2f14fed [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",
273 __FUNCTION__,
274 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",
304 __FUNCTION__,
305 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 */
444
445static inline void ndisc_flow_init(struct flowi *fl, u8 type,
YOSHIFUJI Hideakiaf184762006-08-23 17:18:57 -0700446 struct in6_addr *saddr, struct in6_addr *daddr,
447 int oif)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700448{
449 memset(fl, 0, sizeof(*fl));
450 ipv6_addr_copy(&fl->fl6_src, saddr);
451 ipv6_addr_copy(&fl->fl6_dst, daddr);
452 fl->proto = IPPROTO_ICMPV6;
453 fl->fl_icmp_type = type;
454 fl->fl_icmp_code = 0;
YOSHIFUJI Hideakiaf184762006-08-23 17:18:57 -0700455 fl->oif = oif;
Venkat Yekkiralabeb8d132006-08-04 23:12:42 -0700456 security_sk_classify_flow(ndisc_socket->sk, fl);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700457}
458
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900459static void __ndisc_send(struct net_device *dev,
460 struct neighbour *neigh,
461 struct in6_addr *daddr, struct in6_addr *saddr,
462 struct icmp6hdr *icmp6h, struct in6_addr *target,
David L Stevens14878f72007-09-16 16:52:35 -0700463 int llinfo)
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900464{
465 struct flowi fl;
466 struct dst_entry *dst;
467 struct sock *sk = ndisc_socket->sk;
468 struct sk_buff *skb;
469 struct icmp6hdr *hdr;
470 struct inet6_dev *idev;
471 int len;
472 int err;
David L Stevens14878f72007-09-16 16:52:35 -0700473 u8 *opt, type;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900474
David L Stevens14878f72007-09-16 16:52:35 -0700475 type = icmp6h->icmp6_type;
476
477 ndisc_flow_init(&fl, type, saddr, daddr,
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900478 dev->ifindex);
479
480 dst = ndisc_dst_alloc(dev, neigh, daddr, ip6_output);
481 if (!dst)
482 return;
483
484 err = xfrm_lookup(&dst, &fl, NULL, 0);
485 if (err < 0)
486 return;
487
488 if (!dev->addr_len)
489 llinfo = 0;
490
491 len = sizeof(struct icmp6hdr) + (target ? sizeof(*target) : 0);
492 if (llinfo)
493 len += ndisc_opt_addr_space(dev);
494
495 skb = sock_alloc_send_skb(sk,
496 (MAX_HEADER + sizeof(struct ipv6hdr) +
497 len + LL_RESERVED_SPACE(dev)),
498 1, &err);
499 if (!skb) {
500 ND_PRINTK0(KERN_ERR
501 "ICMPv6 ND: %s() failed to allocate an skb.\n",
502 __FUNCTION__);
503 dst_release(dst);
504 return;
505 }
506
507 skb_reserve(skb, LL_RESERVED_SPACE(dev));
508 ip6_nd_hdr(sk, skb, dev, saddr, daddr, IPPROTO_ICMPV6, len);
509
510 skb->transport_header = skb->tail;
511 skb_put(skb, len);
512
513 hdr = (struct icmp6hdr *)skb_transport_header(skb);
514 memcpy(hdr, icmp6h, sizeof(*hdr));
515
516 opt = skb_transport_header(skb) + sizeof(struct icmp6hdr);
517 if (target) {
518 ipv6_addr_copy((struct in6_addr *)opt, target);
519 opt += sizeof(*target);
520 }
521
522 if (llinfo)
523 ndisc_fill_addr_option(opt, llinfo, dev->dev_addr,
524 dev->addr_len, dev->type);
525
526 hdr->icmp6_cksum = csum_ipv6_magic(saddr, daddr, len,
527 IPPROTO_ICMPV6,
528 csum_partial((__u8 *) hdr,
529 len, 0));
530
531 skb->dst = dst;
532
533 idev = in6_dev_get(dst->dev);
534 IP6_INC_STATS(idev, IPSTATS_MIB_OUTREQUESTS);
535
Patrick McHardy6e23ae22007-11-19 18:53:30 -0800536 err = NF_HOOK(PF_INET6, NF_INET_LOCAL_OUT, skb, NULL, dst->dev,
537 dst_output);
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900538 if (!err) {
David L Stevens14878f72007-09-16 16:52:35 -0700539 ICMP6MSGOUT_INC_STATS(idev, type);
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900540 ICMP6_INC_STATS(idev, ICMP6_MIB_OUTMSGS);
541 }
542
543 if (likely(idev != NULL))
544 in6_dev_put(idev);
545}
546
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
548 struct in6_addr *daddr, struct in6_addr *solicited_addr,
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900549 int router, int solicited, int override, int inc_opt)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700550{
551 struct in6_addr tmpaddr;
552 struct inet6_ifaddr *ifp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700553 struct in6_addr *src_addr;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900554 struct icmp6hdr icmp6h = {
555 .icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT,
556 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700557
558 /* for anycast or proxy, solicited_addr != src_addr */
Daniel Lezcano1cab3da2008-01-10 22:44:09 -0800559 ifp = ipv6_get_ifaddr(&init_net, solicited_addr, dev, 1);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900560 if (ifp) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700561 src_addr = solicited_addr;
Neil Horman95c385b2007-04-25 17:08:10 -0700562 if (ifp->flags & IFA_F_OPTIMISTIC)
563 override = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564 in6_ifa_put(ifp);
565 } else {
566 if (ipv6_dev_get_saddr(dev, daddr, &tmpaddr))
567 return;
568 src_addr = &tmpaddr;
569 }
570
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900571 icmp6h.icmp6_router = router;
572 icmp6h.icmp6_solicited = solicited;
573 icmp6h.icmp6_override = override;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700574
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900575 __ndisc_send(dev, neigh, daddr, src_addr,
576 &icmp6h, solicited_addr,
David L Stevens14878f72007-09-16 16:52:35 -0700577 inc_opt ? ND_OPT_TARGET_LL_ADDR : 0);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900578}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579
580void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,
581 struct in6_addr *solicit,
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900582 struct in6_addr *daddr, struct in6_addr *saddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700583{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584 struct in6_addr addr_buf;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900585 struct icmp6hdr icmp6h = {
586 .icmp6_type = NDISC_NEIGHBOUR_SOLICITATION,
587 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700588
589 if (saddr == NULL) {
Neil Horman95c385b2007-04-25 17:08:10 -0700590 if (ipv6_get_lladdr(dev, &addr_buf,
591 (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700592 return;
593 saddr = &addr_buf;
594 }
595
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900596 __ndisc_send(dev, neigh, daddr, saddr,
597 &icmp6h, solicit,
David L Stevens14878f72007-09-16 16:52:35 -0700598 !ipv6_addr_any(saddr) ? ND_OPT_SOURCE_LL_ADDR : 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700599}
600
601void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr,
602 struct in6_addr *daddr)
603{
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900604 struct icmp6hdr icmp6h = {
605 .icmp6_type = NDISC_ROUTER_SOLICITATION,
606 };
Neil Horman95c385b2007-04-25 17:08:10 -0700607 int send_sllao = dev->addr_len;
Neil Horman95c385b2007-04-25 17:08:10 -0700608
609#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
610 /*
611 * According to section 2.2 of RFC 4429, we must not
612 * send router solicitations with a sllao from
613 * optimistic addresses, but we may send the solicitation
614 * if we don't include the sllao. So here we check
615 * if our address is optimistic, and if so, we
Joe Perchesbea85192007-12-20 14:01:35 -0800616 * suppress the inclusion of the sllao.
Neil Horman95c385b2007-04-25 17:08:10 -0700617 */
618 if (send_sllao) {
Daniel Lezcano1cab3da2008-01-10 22:44:09 -0800619 struct inet6_ifaddr *ifp = ipv6_get_ifaddr(&init_net, saddr,
620 dev, 1);
Neil Horman95c385b2007-04-25 17:08:10 -0700621 if (ifp) {
622 if (ifp->flags & IFA_F_OPTIMISTIC) {
YOSHIFUJI Hideakica043562007-02-28 23:13:20 +0900623 send_sllao = 0;
Neil Horman95c385b2007-04-25 17:08:10 -0700624 }
YOSHIFUJI Hideakica043562007-02-28 23:13:20 +0900625 in6_ifa_put(ifp);
Neil Horman95c385b2007-04-25 17:08:10 -0700626 } else {
627 send_sllao = 0;
628 }
629 }
630#endif
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900631 __ndisc_send(dev, NULL, daddr, saddr,
632 &icmp6h, NULL,
David L Stevens14878f72007-09-16 16:52:35 -0700633 send_sllao ? ND_OPT_SOURCE_LL_ADDR : 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700634}
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900635
Linus Torvalds1da177e2005-04-16 15:20:36 -0700636
637static void ndisc_error_report(struct neighbour *neigh, struct sk_buff *skb)
638{
639 /*
640 * "The sender MUST return an ICMP
641 * destination unreachable"
642 */
643 dst_link_failure(skb);
644 kfree_skb(skb);
645}
646
647/* Called with locked neigh: either read or both */
648
649static void ndisc_solicit(struct neighbour *neigh, struct sk_buff *skb)
650{
651 struct in6_addr *saddr = NULL;
652 struct in6_addr mcaddr;
653 struct net_device *dev = neigh->dev;
654 struct in6_addr *target = (struct in6_addr *)&neigh->primary_key;
655 int probes = atomic_read(&neigh->probes);
656
Daniel Lezcanobfeade02008-01-10 22:43:18 -0800657 if (skb && ipv6_chk_addr(&init_net, &ipv6_hdr(skb)->saddr, dev, 1))
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -0700658 saddr = &ipv6_hdr(skb)->saddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700659
660 if ((probes -= neigh->parms->ucast_probes) < 0) {
661 if (!(neigh->nud_state & NUD_VALID)) {
662 ND_PRINTK1(KERN_DEBUG
663 "%s(): trying to ucast probe in NUD_INVALID: "
Joe Perches46b86a22006-01-13 14:29:07 -0800664 NIP6_FMT "\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700665 __FUNCTION__,
666 NIP6(*target));
667 }
668 ndisc_send_ns(dev, neigh, target, target, saddr);
669 } else if ((probes -= neigh->parms->app_probes) < 0) {
670#ifdef CONFIG_ARPD
671 neigh_app_ns(neigh);
672#endif
673 } else {
674 addrconf_addr_solict_mult(target, &mcaddr);
675 ndisc_send_ns(dev, NULL, target, &mcaddr, saddr);
676 }
677}
678
679static void ndisc_recv_ns(struct sk_buff *skb)
680{
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -0700681 struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb);
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -0700682 struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
683 struct in6_addr *daddr = &ipv6_hdr(skb)->daddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700684 u8 *lladdr = NULL;
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -0700685 u32 ndoptlen = skb->tail - (skb->transport_header +
686 offsetof(struct nd_msg, opt));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700687 struct ndisc_options ndopts;
688 struct net_device *dev = skb->dev;
689 struct inet6_ifaddr *ifp;
690 struct inet6_dev *idev = NULL;
691 struct neighbour *neigh;
Ville Nuorvala62dd9312006-09-22 14:43:19 -0700692 struct pneigh_entry *pneigh = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700693 int dad = ipv6_addr_any(saddr);
694 int inc;
Ville Nuorvala62dd9312006-09-22 14:43:19 -0700695 int is_router;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700696
697 if (ipv6_addr_is_multicast(&msg->target)) {
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900698 ND_PRINTK2(KERN_WARNING
Linus Torvalds1da177e2005-04-16 15:20:36 -0700699 "ICMPv6 NS: multicast target address");
700 return;
701 }
702
703 /*
704 * RFC2461 7.1.1:
705 * DAD has to be destined for solicited node multicast address.
706 */
707 if (dad &&
708 !(daddr->s6_addr32[0] == htonl(0xff020000) &&
709 daddr->s6_addr32[1] == htonl(0x00000000) &&
710 daddr->s6_addr32[2] == htonl(0x00000001) &&
711 daddr->s6_addr [12] == 0xff )) {
712 ND_PRINTK2(KERN_WARNING
713 "ICMPv6 NS: bad DAD packet (wrong destination)\n");
714 return;
715 }
716
717 if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts)) {
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900718 ND_PRINTK2(KERN_WARNING
Linus Torvalds1da177e2005-04-16 15:20:36 -0700719 "ICMPv6 NS: invalid ND options\n");
720 return;
721 }
722
723 if (ndopts.nd_opts_src_lladdr) {
724 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr, dev);
725 if (!lladdr) {
726 ND_PRINTK2(KERN_WARNING
727 "ICMPv6 NS: invalid link-layer address length\n");
728 return;
729 }
730
731 /* RFC2461 7.1.1:
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900732 * If the IP source address is the unspecified address,
733 * there MUST NOT be source link-layer address option
Linus Torvalds1da177e2005-04-16 15:20:36 -0700734 * in the message.
735 */
736 if (dad) {
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900737 ND_PRINTK2(KERN_WARNING
Linus Torvalds1da177e2005-04-16 15:20:36 -0700738 "ICMPv6 NS: bad DAD packet (link-layer address option)\n");
739 return;
740 }
741 }
742
743 inc = ipv6_addr_is_multicast(daddr);
744
Daniel Lezcano1cab3da2008-01-10 22:44:09 -0800745 if ((ifp = ipv6_get_ifaddr(&init_net, &msg->target, dev, 1)) != NULL) {
Neil Horman95c385b2007-04-25 17:08:10 -0700746
747 if (ifp->flags & (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)) {
748 if (dad) {
749 if (dev->type == ARPHRD_IEEE802_TR) {
Arnaldo Carvalho de Melo98e399f2007-03-19 15:33:04 -0700750 const unsigned char *sadr;
751 sadr = skb_mac_header(skb);
Neil Horman95c385b2007-04-25 17:08:10 -0700752 if (((sadr[8] ^ dev->dev_addr[0]) & 0x7f) == 0 &&
753 sadr[9] == dev->dev_addr[1] &&
754 sadr[10] == dev->dev_addr[2] &&
755 sadr[11] == dev->dev_addr[3] &&
756 sadr[12] == dev->dev_addr[4] &&
757 sadr[13] == dev->dev_addr[5]) {
758 /* looped-back to us */
759 goto out;
760 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700761 }
Neil Horman95c385b2007-04-25 17:08:10 -0700762
763 /*
764 * We are colliding with another node
765 * who is doing DAD
766 * so fail our DAD process
767 */
768 addrconf_dad_failure(ifp);
Denis V. Lunev9e3be4b2007-09-11 11:04:49 +0200769 return;
Neil Horman95c385b2007-04-25 17:08:10 -0700770 } else {
771 /*
772 * This is not a dad solicitation.
773 * If we are an optimistic node,
774 * we should respond.
775 * Otherwise, we should ignore it.
776 */
777 if (!(ifp->flags & IFA_F_OPTIMISTIC))
778 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700779 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700780 }
781
782 idev = ifp->idev;
783 } else {
784 idev = in6_dev_get(dev);
785 if (!idev) {
786 /* XXX: count this drop? */
787 return;
788 }
789
790 if (ipv6_chk_acast_addr(dev, &msg->target) ||
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900791 (idev->cnf.forwarding &&
YOSHIFUJI Hideakifbea49e2006-09-22 14:43:49 -0700792 (ipv6_devconf.proxy_ndp || idev->cnf.proxy_ndp) &&
Eric W. Biederman426b5302008-01-24 00:13:18 -0800793 (pneigh = pneigh_lookup(&nd_tbl, &init_net,
Ville Nuorvala62dd9312006-09-22 14:43:19 -0700794 &msg->target, dev, 0)) != NULL)) {
Patrick McHardya61bbcf2005-08-14 17:24:31 -0700795 if (!(NEIGH_CB(skb)->flags & LOCALLY_ENQUEUED) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700796 skb->pkt_type != PACKET_HOST &&
797 inc != 0 &&
798 idev->nd_parms->proxy_delay != 0) {
799 /*
800 * for anycast or proxy,
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900801 * sender should delay its response
802 * by a random time between 0 and
Linus Torvalds1da177e2005-04-16 15:20:36 -0700803 * MAX_ANYCAST_DELAY_TIME seconds.
804 * (RFC2461) -- yoshfuji
805 */
806 struct sk_buff *n = skb_clone(skb, GFP_ATOMIC);
807 if (n)
808 pneigh_enqueue(&nd_tbl, idev->nd_parms, n);
809 goto out;
810 }
811 } else
812 goto out;
813 }
814
YOSHIFUJI Hideakifc26d0a2006-09-22 14:44:53 -0700815 is_router = !!(pneigh ? pneigh->flags & NTF_ROUTER : idev->cnf.forwarding);
Ville Nuorvala62dd9312006-09-22 14:43:19 -0700816
Linus Torvalds1da177e2005-04-16 15:20:36 -0700817 if (dad) {
818 struct in6_addr maddr;
819
820 ipv6_addr_all_nodes(&maddr);
821 ndisc_send_na(dev, NULL, &maddr, &msg->target,
Ville Nuorvala62dd9312006-09-22 14:43:19 -0700822 is_router, 0, (ifp != NULL), 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700823 goto out;
824 }
825
826 if (inc)
827 NEIGH_CACHE_STAT_INC(&nd_tbl, rcv_probes_mcast);
828 else
829 NEIGH_CACHE_STAT_INC(&nd_tbl, rcv_probes_ucast);
830
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900831 /*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700832 * update / create cache entry
833 * for the source address
834 */
835 neigh = __neigh_lookup(&nd_tbl, saddr, dev,
836 !inc || lladdr || !dev->addr_len);
837 if (neigh)
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900838 neigh_update(neigh, lladdr, NUD_STALE,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700839 NEIGH_UPDATE_F_WEAK_OVERRIDE|
840 NEIGH_UPDATE_F_OVERRIDE);
Stephen Hemminger3b04ddd2007-10-09 01:40:57 -0700841 if (neigh || !dev->header_ops) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700842 ndisc_send_na(dev, neigh, saddr, &msg->target,
Ville Nuorvala62dd9312006-09-22 14:43:19 -0700843 is_router,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700844 1, (ifp != NULL && inc), inc);
845 if (neigh)
846 neigh_release(neigh);
847 }
848
849out:
850 if (ifp)
851 in6_ifa_put(ifp);
852 else
853 in6_dev_put(idev);
854
855 return;
856}
857
858static void ndisc_recv_na(struct sk_buff *skb)
859{
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -0700860 struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb);
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -0700861 struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
862 struct in6_addr *daddr = &ipv6_hdr(skb)->daddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700863 u8 *lladdr = NULL;
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -0700864 u32 ndoptlen = skb->tail - (skb->transport_header +
865 offsetof(struct nd_msg, opt));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700866 struct ndisc_options ndopts;
867 struct net_device *dev = skb->dev;
868 struct inet6_ifaddr *ifp;
869 struct neighbour *neigh;
870
871 if (skb->len < sizeof(struct nd_msg)) {
872 ND_PRINTK2(KERN_WARNING
873 "ICMPv6 NA: packet too short\n");
874 return;
875 }
876
877 if (ipv6_addr_is_multicast(&msg->target)) {
878 ND_PRINTK2(KERN_WARNING
879 "ICMPv6 NA: target address is multicast.\n");
880 return;
881 }
882
883 if (ipv6_addr_is_multicast(daddr) &&
884 msg->icmph.icmp6_solicited) {
885 ND_PRINTK2(KERN_WARNING
886 "ICMPv6 NA: solicited NA is multicasted.\n");
887 return;
888 }
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900889
Linus Torvalds1da177e2005-04-16 15:20:36 -0700890 if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts)) {
891 ND_PRINTK2(KERN_WARNING
892 "ICMPv6 NS: invalid ND option\n");
893 return;
894 }
895 if (ndopts.nd_opts_tgt_lladdr) {
896 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr, dev);
897 if (!lladdr) {
898 ND_PRINTK2(KERN_WARNING
899 "ICMPv6 NA: invalid link-layer address length\n");
900 return;
901 }
902 }
Daniel Lezcano1cab3da2008-01-10 22:44:09 -0800903 if ((ifp = ipv6_get_ifaddr(&init_net, &msg->target, dev, 1))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700904 if (ifp->flags & IFA_F_TENTATIVE) {
905 addrconf_dad_failure(ifp);
906 return;
907 }
908 /* What should we make now? The advertisement
909 is invalid, but ndisc specs say nothing
910 about it. It could be misconfiguration, or
911 an smart proxy agent tries to help us :-)
912 */
913 ND_PRINTK1(KERN_WARNING
914 "ICMPv6 NA: someone advertises our address on %s!\n",
915 ifp->idev->dev->name);
916 in6_ifa_put(ifp);
917 return;
918 }
919 neigh = neigh_lookup(&nd_tbl, &msg->target, dev);
920
921 if (neigh) {
922 u8 old_flags = neigh->flags;
923
924 if (neigh->nud_state & NUD_FAILED)
925 goto out;
926
Ville Nuorvala5f3e6e92006-09-22 14:42:46 -0700927 /*
928 * Don't update the neighbor cache entry on a proxy NA from
929 * ourselves because either the proxied node is off link or it
930 * has already sent a NA to us.
931 */
932 if (lladdr && !memcmp(lladdr, dev->dev_addr, dev->addr_len) &&
YOSHIFUJI Hideakifbea49e2006-09-22 14:43:49 -0700933 ipv6_devconf.forwarding && ipv6_devconf.proxy_ndp &&
Eric W. Biederman426b5302008-01-24 00:13:18 -0800934 pneigh_lookup(&nd_tbl, &init_net, &msg->target, dev, 0)) {
YOSHIFUJI Hideakifbea49e2006-09-22 14:43:49 -0700935 /* XXX: idev->cnf.prixy_ndp */
Ville Nuorvala5f3e6e92006-09-22 14:42:46 -0700936 goto out;
YOSHIFUJI Hideakifbea49e2006-09-22 14:43:49 -0700937 }
Ville Nuorvala5f3e6e92006-09-22 14:42:46 -0700938
Linus Torvalds1da177e2005-04-16 15:20:36 -0700939 neigh_update(neigh, lladdr,
940 msg->icmph.icmp6_solicited ? NUD_REACHABLE : NUD_STALE,
941 NEIGH_UPDATE_F_WEAK_OVERRIDE|
942 (msg->icmph.icmp6_override ? NEIGH_UPDATE_F_OVERRIDE : 0)|
943 NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
944 (msg->icmph.icmp6_router ? NEIGH_UPDATE_F_ISROUTER : 0));
945
946 if ((old_flags & ~neigh->flags) & NTF_ROUTER) {
947 /*
948 * Change: router to host
949 */
950 struct rt6_info *rt;
951 rt = rt6_get_dflt_router(saddr, dev);
952 if (rt)
Thomas Grafe0a1ad732006-08-22 00:00:21 -0700953 ip6_del_rt(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700954 }
955
956out:
957 neigh_release(neigh);
958 }
959}
960
961static void ndisc_recv_rs(struct sk_buff *skb)
962{
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -0700963 struct rs_msg *rs_msg = (struct rs_msg *)skb_transport_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700964 unsigned long ndoptlen = skb->len - sizeof(*rs_msg);
965 struct neighbour *neigh;
966 struct inet6_dev *idev;
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -0700967 struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700968 struct ndisc_options ndopts;
969 u8 *lladdr = NULL;
970
971 if (skb->len < sizeof(*rs_msg))
972 return;
973
974 idev = in6_dev_get(skb->dev);
975 if (!idev) {
976 if (net_ratelimit())
977 ND_PRINTK1("ICMP6 RS: can't find in6 device\n");
978 return;
979 }
980
981 /* Don't accept RS if we're not in router mode */
982 if (!idev->cnf.forwarding)
983 goto out;
984
985 /*
986 * Don't update NCE if src = ::;
987 * this implies that the source node has no ip address assigned yet.
988 */
989 if (ipv6_addr_any(saddr))
990 goto out;
991
992 /* Parse ND options */
993 if (!ndisc_parse_options(rs_msg->opt, ndoptlen, &ndopts)) {
994 if (net_ratelimit())
995 ND_PRINTK2("ICMP6 NS: invalid ND option, ignored\n");
996 goto out;
997 }
998
999 if (ndopts.nd_opts_src_lladdr) {
1000 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr,
1001 skb->dev);
1002 if (!lladdr)
1003 goto out;
1004 }
1005
1006 neigh = __neigh_lookup(&nd_tbl, saddr, skb->dev, 1);
1007 if (neigh) {
1008 neigh_update(neigh, lladdr, NUD_STALE,
1009 NEIGH_UPDATE_F_WEAK_OVERRIDE|
1010 NEIGH_UPDATE_F_OVERRIDE|
1011 NEIGH_UPDATE_F_OVERRIDE_ISROUTER);
1012 neigh_release(neigh);
1013 }
1014out:
1015 in6_dev_put(idev);
1016}
1017
Pierre Ynard31910572007-10-10 21:22:05 -07001018static void ndisc_ra_useropt(struct sk_buff *ra, struct nd_opt_hdr *opt)
1019{
1020 struct icmp6hdr *icmp6h = (struct icmp6hdr *)skb_transport_header(ra);
1021 struct sk_buff *skb;
1022 struct nlmsghdr *nlh;
1023 struct nduseroptmsg *ndmsg;
1024 int err;
1025 int base_size = NLMSG_ALIGN(sizeof(struct nduseroptmsg)
1026 + (opt->nd_opt_len << 3));
1027 size_t msg_size = base_size + nla_total_size(sizeof(struct in6_addr));
1028
1029 skb = nlmsg_new(msg_size, GFP_ATOMIC);
1030 if (skb == NULL) {
1031 err = -ENOBUFS;
1032 goto errout;
1033 }
1034
1035 nlh = nlmsg_put(skb, 0, 0, RTM_NEWNDUSEROPT, base_size, 0);
1036 if (nlh == NULL) {
1037 goto nla_put_failure;
1038 }
1039
1040 ndmsg = nlmsg_data(nlh);
1041 ndmsg->nduseropt_family = AF_INET6;
Pierre Ynarddbb2ed22007-11-12 17:58:35 -08001042 ndmsg->nduseropt_ifindex = ra->dev->ifindex;
Pierre Ynard31910572007-10-10 21:22:05 -07001043 ndmsg->nduseropt_icmp_type = icmp6h->icmp6_type;
1044 ndmsg->nduseropt_icmp_code = icmp6h->icmp6_code;
1045 ndmsg->nduseropt_opts_len = opt->nd_opt_len << 3;
1046
1047 memcpy(ndmsg + 1, opt, opt->nd_opt_len << 3);
1048
1049 NLA_PUT(skb, NDUSEROPT_SRCADDR, sizeof(struct in6_addr),
1050 &ipv6_hdr(ra)->saddr);
1051 nlmsg_end(skb, nlh);
1052
Denis V. Lunev97c53ca2007-11-19 22:26:51 -08001053 err = rtnl_notify(skb, &init_net, 0, RTNLGRP_ND_USEROPT, NULL,
1054 GFP_ATOMIC);
Pierre Ynard31910572007-10-10 21:22:05 -07001055 if (err < 0)
1056 goto errout;
1057
1058 return;
1059
1060nla_put_failure:
1061 nlmsg_free(skb);
1062 err = -EMSGSIZE;
1063errout:
Denis V. Lunev97c53ca2007-11-19 22:26:51 -08001064 rtnl_set_sk_err(&init_net, RTNLGRP_ND_USEROPT, err);
Pierre Ynard31910572007-10-10 21:22:05 -07001065}
1066
Linus Torvalds1da177e2005-04-16 15:20:36 -07001067static void ndisc_router_discovery(struct sk_buff *skb)
1068{
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -07001069 struct ra_msg *ra_msg = (struct ra_msg *)skb_transport_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001070 struct neighbour *neigh = NULL;
1071 struct inet6_dev *in6_dev;
YOSHIFUJI Hideaki65f5c7c2006-03-20 16:55:08 -08001072 struct rt6_info *rt = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001073 int lifetime;
1074 struct ndisc_options ndopts;
1075 int optlen;
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001076 unsigned int pref = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001077
1078 __u8 * opt = (__u8 *)(ra_msg + 1);
1079
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -07001080 optlen = (skb->tail - skb->transport_header) - sizeof(struct ra_msg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001081
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001082 if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001083 ND_PRINTK2(KERN_WARNING
1084 "ICMPv6 RA: source address is not link-local.\n");
1085 return;
1086 }
1087 if (optlen < 0) {
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001088 ND_PRINTK2(KERN_WARNING
Linus Torvalds1da177e2005-04-16 15:20:36 -07001089 "ICMPv6 RA: packet too short\n");
1090 return;
1091 }
1092
1093 /*
1094 * set the RA_RECV flag in the interface
1095 */
1096
1097 in6_dev = in6_dev_get(skb->dev);
1098 if (in6_dev == NULL) {
1099 ND_PRINTK0(KERN_ERR
1100 "ICMPv6 RA: can't find inet6 device for %s.\n",
1101 skb->dev->name);
1102 return;
1103 }
1104 if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_ra) {
1105 in6_dev_put(in6_dev);
1106 return;
1107 }
1108
1109 if (!ndisc_parse_options(opt, optlen, &ndopts)) {
1110 in6_dev_put(in6_dev);
1111 ND_PRINTK2(KERN_WARNING
1112 "ICMP6 RA: invalid ND options\n");
1113 return;
1114 }
1115
1116 if (in6_dev->if_flags & IF_RS_SENT) {
1117 /*
1118 * flag that an RA was received after an RS was sent
1119 * out on this interface.
1120 */
1121 in6_dev->if_flags |= IF_RA_RCVD;
1122 }
1123
1124 /*
1125 * Remember the managed/otherconf flags from most recently
1126 * received RA message (RFC 2462) -- yoshfuji
1127 */
1128 in6_dev->if_flags = (in6_dev->if_flags & ~(IF_RA_MANAGED |
1129 IF_RA_OTHERCONF)) |
1130 (ra_msg->icmph.icmp6_addrconf_managed ?
1131 IF_RA_MANAGED : 0) |
1132 (ra_msg->icmph.icmp6_addrconf_other ?
1133 IF_RA_OTHERCONF : 0);
1134
YOSHIFUJI Hideaki65f5c7c2006-03-20 16:55:08 -08001135 if (!in6_dev->cnf.accept_ra_defrtr)
1136 goto skip_defrtr;
1137
Linus Torvalds1da177e2005-04-16 15:20:36 -07001138 lifetime = ntohs(ra_msg->icmph.icmp6_rt_lifetime);
1139
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001140#ifdef CONFIG_IPV6_ROUTER_PREF
1141 pref = ra_msg->icmph.icmp6_router_pref;
1142 /* 10b is handled as if it were 00b (medium) */
YOSHIFUJI Hideaki930d6ff2006-03-20 17:05:30 -08001143 if (pref == ICMPV6_ROUTER_PREF_INVALID ||
YOSHIFUJI Hideaki6d5b78c2007-06-22 16:07:04 -07001144 !in6_dev->cnf.accept_ra_rtr_pref)
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001145 pref = ICMPV6_ROUTER_PREF_MEDIUM;
1146#endif
1147
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001148 rt = rt6_get_dflt_router(&ipv6_hdr(skb)->saddr, skb->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001149
1150 if (rt)
1151 neigh = rt->rt6i_nexthop;
1152
1153 if (rt && lifetime == 0) {
1154 neigh_clone(neigh);
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001155 ip6_del_rt(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001156 rt = NULL;
1157 }
1158
1159 if (rt == NULL && lifetime) {
1160 ND_PRINTK3(KERN_DEBUG
1161 "ICMPv6 RA: adding default router.\n");
1162
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001163 rt = rt6_add_dflt_router(&ipv6_hdr(skb)->saddr, skb->dev, pref);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001164 if (rt == NULL) {
1165 ND_PRINTK0(KERN_ERR
1166 "ICMPv6 RA: %s() failed to add default route.\n",
1167 __FUNCTION__);
1168 in6_dev_put(in6_dev);
1169 return;
1170 }
1171
1172 neigh = rt->rt6i_nexthop;
1173 if (neigh == NULL) {
1174 ND_PRINTK0(KERN_ERR
1175 "ICMPv6 RA: %s() got default router without neighbour.\n",
1176 __FUNCTION__);
1177 dst_release(&rt->u.dst);
1178 in6_dev_put(in6_dev);
1179 return;
1180 }
1181 neigh->flags |= NTF_ROUTER;
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001182 } else if (rt) {
1183 rt->rt6i_flags |= (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001184 }
1185
1186 if (rt)
1187 rt->rt6i_expires = jiffies + (HZ * lifetime);
1188
1189 if (ra_msg->icmph.icmp6_hop_limit) {
1190 in6_dev->cnf.hop_limit = ra_msg->icmph.icmp6_hop_limit;
1191 if (rt)
1192 rt->u.dst.metrics[RTAX_HOPLIMIT-1] = ra_msg->icmph.icmp6_hop_limit;
1193 }
1194
YOSHIFUJI Hideaki65f5c7c2006-03-20 16:55:08 -08001195skip_defrtr:
1196
Linus Torvalds1da177e2005-04-16 15:20:36 -07001197 /*
1198 * Update Reachable Time and Retrans Timer
1199 */
1200
1201 if (in6_dev->nd_parms) {
1202 unsigned long rtime = ntohl(ra_msg->retrans_timer);
1203
1204 if (rtime && rtime/1000 < MAX_SCHEDULE_TIMEOUT/HZ) {
1205 rtime = (rtime*HZ)/1000;
1206 if (rtime < HZ/10)
1207 rtime = HZ/10;
1208 in6_dev->nd_parms->retrans_time = rtime;
1209 in6_dev->tstamp = jiffies;
1210 inet6_ifinfo_notify(RTM_NEWLINK, in6_dev);
1211 }
1212
1213 rtime = ntohl(ra_msg->reachable_time);
1214 if (rtime && rtime/1000 < MAX_SCHEDULE_TIMEOUT/(3*HZ)) {
1215 rtime = (rtime*HZ)/1000;
1216
1217 if (rtime < HZ/10)
1218 rtime = HZ/10;
1219
1220 if (rtime != in6_dev->nd_parms->base_reachable_time) {
1221 in6_dev->nd_parms->base_reachable_time = rtime;
1222 in6_dev->nd_parms->gc_staletime = 3 * rtime;
1223 in6_dev->nd_parms->reachable_time = neigh_rand_reach_time(rtime);
1224 in6_dev->tstamp = jiffies;
1225 inet6_ifinfo_notify(RTM_NEWLINK, in6_dev);
1226 }
1227 }
1228 }
1229
1230 /*
1231 * Process options.
1232 */
1233
1234 if (!neigh)
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001235 neigh = __neigh_lookup(&nd_tbl, &ipv6_hdr(skb)->saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001236 skb->dev, 1);
1237 if (neigh) {
1238 u8 *lladdr = NULL;
1239 if (ndopts.nd_opts_src_lladdr) {
1240 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr,
1241 skb->dev);
1242 if (!lladdr) {
1243 ND_PRINTK2(KERN_WARNING
1244 "ICMPv6 RA: invalid link-layer address length\n");
1245 goto out;
1246 }
1247 }
1248 neigh_update(neigh, lladdr, NUD_STALE,
1249 NEIGH_UPDATE_F_WEAK_OVERRIDE|
1250 NEIGH_UPDATE_F_OVERRIDE|
1251 NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
1252 NEIGH_UPDATE_F_ISROUTER);
1253 }
1254
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001255#ifdef CONFIG_IPV6_ROUTE_INFO
YOSHIFUJI Hideaki09c884d2006-03-20 17:07:03 -08001256 if (in6_dev->cnf.accept_ra_rtr_pref && ndopts.nd_opts_ri) {
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001257 struct nd_opt_hdr *p;
1258 for (p = ndopts.nd_opts_ri;
1259 p;
1260 p = ndisc_next_option(p, ndopts.nd_opts_ri_end)) {
YOSHIFUJI Hideaki09c884d2006-03-20 17:07:03 -08001261 if (((struct route_info *)p)->prefix_len > in6_dev->cnf.accept_ra_rt_info_max_plen)
1262 continue;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001263 rt6_route_rcv(skb->dev, (u8*)p, (p->nd_opt_len) << 3,
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001264 &ipv6_hdr(skb)->saddr);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001265 }
1266 }
1267#endif
1268
YOSHIFUJI Hideakic4fd30e2006-03-20 16:55:26 -08001269 if (in6_dev->cnf.accept_ra_pinfo && ndopts.nd_opts_pi) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001270 struct nd_opt_hdr *p;
1271 for (p = ndopts.nd_opts_pi;
1272 p;
1273 p = ndisc_next_option(p, ndopts.nd_opts_pi_end)) {
1274 addrconf_prefix_rcv(skb->dev, (u8*)p, (p->nd_opt_len) << 3);
1275 }
1276 }
1277
1278 if (ndopts.nd_opts_mtu) {
Al Viroe69a4adc2006-11-14 20:56:00 -08001279 __be32 n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001280 u32 mtu;
1281
Al Viroe69a4adc2006-11-14 20:56:00 -08001282 memcpy(&n, ((u8*)(ndopts.nd_opts_mtu+1))+2, sizeof(mtu));
1283 mtu = ntohl(n);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001284
1285 if (mtu < IPV6_MIN_MTU || mtu > skb->dev->mtu) {
1286 ND_PRINTK2(KERN_WARNING
1287 "ICMPv6 RA: invalid mtu: %d\n",
1288 mtu);
1289 } else if (in6_dev->cnf.mtu6 != mtu) {
1290 in6_dev->cnf.mtu6 = mtu;
1291
1292 if (rt)
1293 rt->u.dst.metrics[RTAX_MTU-1] = mtu;
1294
1295 rt6_mtu_change(skb->dev, mtu);
1296 }
1297 }
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001298
Pierre Ynard31910572007-10-10 21:22:05 -07001299 if (ndopts.nd_useropts) {
YOSHIFUJI Hideaki61cf46ad2008-01-22 17:32:53 +09001300 struct nd_opt_hdr *p;
1301 for (p = ndopts.nd_useropts;
1302 p;
1303 p = ndisc_next_useropt(p, ndopts.nd_useropts_end)) {
1304 ndisc_ra_useropt(skb, p);
Pierre Ynard31910572007-10-10 21:22:05 -07001305 }
1306 }
1307
Linus Torvalds1da177e2005-04-16 15:20:36 -07001308 if (ndopts.nd_opts_tgt_lladdr || ndopts.nd_opts_rh) {
1309 ND_PRINTK2(KERN_WARNING
1310 "ICMPv6 RA: invalid RA options");
1311 }
1312out:
1313 if (rt)
1314 dst_release(&rt->u.dst);
1315 else if (neigh)
1316 neigh_release(neigh);
1317 in6_dev_put(in6_dev);
1318}
1319
1320static void ndisc_redirect_rcv(struct sk_buff *skb)
1321{
1322 struct inet6_dev *in6_dev;
1323 struct icmp6hdr *icmph;
1324 struct in6_addr *dest;
1325 struct in6_addr *target; /* new first hop to destination */
1326 struct neighbour *neigh;
1327 int on_link = 0;
1328 struct ndisc_options ndopts;
1329 int optlen;
1330 u8 *lladdr = NULL;
1331
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001332 if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001333 ND_PRINTK2(KERN_WARNING
1334 "ICMPv6 Redirect: source address is not link-local.\n");
1335 return;
1336 }
1337
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -07001338 optlen = skb->tail - skb->transport_header;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001339 optlen -= sizeof(struct icmp6hdr) + 2 * sizeof(struct in6_addr);
1340
1341 if (optlen < 0) {
1342 ND_PRINTK2(KERN_WARNING
1343 "ICMPv6 Redirect: packet too short\n");
1344 return;
1345 }
1346
Arnaldo Carvalho de Melocc70ab22007-03-13 14:03:22 -03001347 icmph = icmp6_hdr(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001348 target = (struct in6_addr *) (icmph + 1);
1349 dest = target + 1;
1350
1351 if (ipv6_addr_is_multicast(dest)) {
1352 ND_PRINTK2(KERN_WARNING
1353 "ICMPv6 Redirect: destination address is multicast.\n");
1354 return;
1355 }
1356
1357 if (ipv6_addr_equal(dest, target)) {
1358 on_link = 1;
Brian Haleybf0b48d2007-10-08 00:12:05 -07001359 } else if (ipv6_addr_type(target) !=
1360 (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) {
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001361 ND_PRINTK2(KERN_WARNING
Brian Haleybf0b48d2007-10-08 00:12:05 -07001362 "ICMPv6 Redirect: target address is not link-local unicast.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001363 return;
1364 }
1365
1366 in6_dev = in6_dev_get(skb->dev);
1367 if (!in6_dev)
1368 return;
1369 if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects) {
1370 in6_dev_put(in6_dev);
1371 return;
1372 }
1373
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001374 /* RFC2461 8.1:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001375 * The IP source address of the Redirect MUST be the same as the current
1376 * first-hop router for the specified ICMP Destination Address.
1377 */
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001378
Linus Torvalds1da177e2005-04-16 15:20:36 -07001379 if (!ndisc_parse_options((u8*)(dest + 1), optlen, &ndopts)) {
1380 ND_PRINTK2(KERN_WARNING
1381 "ICMPv6 Redirect: invalid ND options\n");
1382 in6_dev_put(in6_dev);
1383 return;
1384 }
1385 if (ndopts.nd_opts_tgt_lladdr) {
1386 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr,
1387 skb->dev);
1388 if (!lladdr) {
1389 ND_PRINTK2(KERN_WARNING
1390 "ICMPv6 Redirect: invalid link-layer address length\n");
1391 in6_dev_put(in6_dev);
1392 return;
1393 }
1394 }
1395
1396 neigh = __neigh_lookup(&nd_tbl, target, skb->dev, 1);
1397 if (neigh) {
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001398 rt6_redirect(dest, &ipv6_hdr(skb)->daddr,
1399 &ipv6_hdr(skb)->saddr, neigh, lladdr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001400 on_link);
1401 neigh_release(neigh);
1402 }
1403 in6_dev_put(in6_dev);
1404}
1405
1406void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
1407 struct in6_addr *target)
1408{
1409 struct sock *sk = ndisc_socket->sk;
1410 int len = sizeof(struct icmp6hdr) + 2 * sizeof(struct in6_addr);
1411 struct sk_buff *buff;
1412 struct icmp6hdr *icmph;
1413 struct in6_addr saddr_buf;
1414 struct in6_addr *addrp;
1415 struct net_device *dev;
1416 struct rt6_info *rt;
1417 struct dst_entry *dst;
1418 struct inet6_dev *idev;
1419 struct flowi fl;
1420 u8 *opt;
1421 int rd_len;
1422 int err;
1423 int hlen;
1424 u8 ha_buf[MAX_ADDR_LEN], *ha = NULL;
1425
1426 dev = skb->dev;
1427
Neil Horman95c385b2007-04-25 17:08:10 -07001428 if (ipv6_get_lladdr(dev, &saddr_buf, IFA_F_TENTATIVE)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001429 ND_PRINTK2(KERN_WARNING
1430 "ICMPv6 Redirect: no link-local address on %s\n",
1431 dev->name);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001432 return;
1433 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001434
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001435 if (!ipv6_addr_equal(&ipv6_hdr(skb)->daddr, target) &&
Brian Haleybf0b48d2007-10-08 00:12:05 -07001436 ipv6_addr_type(target) != (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) {
Li Yewang29556522007-01-30 14:33:20 -08001437 ND_PRINTK2(KERN_WARNING
Brian Haleybf0b48d2007-10-08 00:12:05 -07001438 "ICMPv6 Redirect: target address is not link-local unicast.\n");
Li Yewang29556522007-01-30 14:33:20 -08001439 return;
1440 }
1441
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001442 ndisc_flow_init(&fl, NDISC_REDIRECT, &saddr_buf, &ipv6_hdr(skb)->saddr,
YOSHIFUJI Hideakiaf184762006-08-23 17:18:57 -07001443 dev->ifindex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001444
1445 dst = ip6_route_output(NULL, &fl);
1446 if (dst == NULL)
1447 return;
1448
1449 err = xfrm_lookup(&dst, &fl, NULL, 0);
Patrick McHardye104411b2005-09-08 15:11:55 -07001450 if (err)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001451 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001452
1453 rt = (struct rt6_info *) dst;
1454
1455 if (rt->rt6i_flags & RTF_GATEWAY) {
1456 ND_PRINTK2(KERN_WARNING
1457 "ICMPv6 Redirect: destination is not a neighbour.\n");
1458 dst_release(dst);
1459 return;
1460 }
1461 if (!xrlim_allow(dst, 1*HZ)) {
1462 dst_release(dst);
1463 return;
1464 }
1465
1466 if (dev->addr_len) {
1467 read_lock_bh(&neigh->lock);
1468 if (neigh->nud_state & NUD_VALID) {
1469 memcpy(ha_buf, neigh->ha, dev->addr_len);
1470 read_unlock_bh(&neigh->lock);
1471 ha = ha_buf;
1472 len += ndisc_opt_addr_space(dev);
1473 } else
1474 read_unlock_bh(&neigh->lock);
1475 }
1476
1477 rd_len = min_t(unsigned int,
1478 IPV6_MIN_MTU-sizeof(struct ipv6hdr)-len, skb->len + 8);
1479 rd_len &= ~0x7;
1480 len += rd_len;
1481
David S. Millerd54a81d2006-12-02 21:00:06 -08001482 buff = sock_alloc_send_skb(sk,
1483 (MAX_HEADER + sizeof(struct ipv6hdr) +
1484 len + LL_RESERVED_SPACE(dev)),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001485 1, &err);
1486 if (buff == NULL) {
1487 ND_PRINTK0(KERN_ERR
1488 "ICMPv6 Redirect: %s() failed to allocate an skb.\n",
1489 __FUNCTION__);
1490 dst_release(dst);
1491 return;
1492 }
1493
1494 hlen = 0;
1495
1496 skb_reserve(buff, LL_RESERVED_SPACE(dev));
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001497 ip6_nd_hdr(sk, buff, dev, &saddr_buf, &ipv6_hdr(skb)->saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001498 IPPROTO_ICMPV6, len);
1499
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -07001500 skb_set_transport_header(buff, skb_tail_pointer(buff) - buff->data);
Arnaldo Carvalho de Melod10ba342007-03-14 21:05:37 -03001501 skb_put(buff, len);
1502 icmph = icmp6_hdr(buff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001503
1504 memset(icmph, 0, sizeof(struct icmp6hdr));
1505 icmph->icmp6_type = NDISC_REDIRECT;
1506
1507 /*
1508 * copy target and destination addresses
1509 */
1510
1511 addrp = (struct in6_addr *)(icmph + 1);
1512 ipv6_addr_copy(addrp, target);
1513 addrp++;
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001514 ipv6_addr_copy(addrp, &ipv6_hdr(skb)->daddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001515
1516 opt = (u8*) (addrp + 1);
1517
1518 /*
1519 * include target_address option
1520 */
1521
1522 if (ha)
1523 opt = ndisc_fill_addr_option(opt, ND_OPT_TARGET_LL_ADDR, ha,
1524 dev->addr_len, dev->type);
1525
1526 /*
1527 * build redirect option and copy skb over to the new packet.
1528 */
1529
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001530 memset(opt, 0, 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001531 *(opt++) = ND_OPT_REDIRECT_HDR;
1532 *(opt++) = (rd_len >> 3);
1533 opt += 6;
1534
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001535 memcpy(opt, ipv6_hdr(skb), rd_len - 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001536
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001537 icmph->icmp6_cksum = csum_ipv6_magic(&saddr_buf, &ipv6_hdr(skb)->saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001538 len, IPPROTO_ICMPV6,
1539 csum_partial((u8 *) icmph, len, 0));
1540
1541 buff->dst = dst;
1542 idev = in6_dev_get(dst->dev);
YOSHIFUJI Hideakia11d2062006-11-04 20:11:37 +09001543 IP6_INC_STATS(idev, IPSTATS_MIB_OUTREQUESTS);
Patrick McHardy6e23ae22007-11-19 18:53:30 -08001544 err = NF_HOOK(PF_INET6, NF_INET_LOCAL_OUT, buff, NULL, dst->dev,
1545 dst_output);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001546 if (!err) {
David L Stevens14878f72007-09-16 16:52:35 -07001547 ICMP6MSGOUT_INC_STATS(idev, NDISC_REDIRECT);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001548 ICMP6_INC_STATS(idev, ICMP6_MIB_OUTMSGS);
1549 }
1550
1551 if (likely(idev != NULL))
1552 in6_dev_put(idev);
1553}
1554
1555static void pndisc_redo(struct sk_buff *skb)
1556{
YOSHIFUJI Hideaki140e26fc2005-10-05 12:11:41 -07001557 ndisc_recv_ns(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001558 kfree_skb(skb);
1559}
1560
1561int ndisc_rcv(struct sk_buff *skb)
1562{
1563 struct nd_msg *msg;
1564
1565 if (!pskb_may_pull(skb, skb->len))
1566 return 0;
1567
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -07001568 msg = (struct nd_msg *)skb_transport_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001569
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -07001570 __skb_push(skb, skb->data - skb_transport_header(skb));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001571
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001572 if (ipv6_hdr(skb)->hop_limit != 255) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001573 ND_PRINTK2(KERN_WARNING
1574 "ICMPv6 NDISC: invalid hop-limit: %d\n",
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001575 ipv6_hdr(skb)->hop_limit);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001576 return 0;
1577 }
1578
1579 if (msg->icmph.icmp6_code != 0) {
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001580 ND_PRINTK2(KERN_WARNING
Linus Torvalds1da177e2005-04-16 15:20:36 -07001581 "ICMPv6 NDISC: invalid ICMPv6 code: %d\n",
1582 msg->icmph.icmp6_code);
1583 return 0;
1584 }
1585
Patrick McHardya61bbcf2005-08-14 17:24:31 -07001586 memset(NEIGH_CB(skb), 0, sizeof(struct neighbour_cb));
1587
Linus Torvalds1da177e2005-04-16 15:20:36 -07001588 switch (msg->icmph.icmp6_type) {
1589 case NDISC_NEIGHBOUR_SOLICITATION:
1590 ndisc_recv_ns(skb);
1591 break;
1592
1593 case NDISC_NEIGHBOUR_ADVERTISEMENT:
1594 ndisc_recv_na(skb);
1595 break;
1596
1597 case NDISC_ROUTER_SOLICITATION:
1598 ndisc_recv_rs(skb);
1599 break;
1600
1601 case NDISC_ROUTER_ADVERTISEMENT:
1602 ndisc_router_discovery(skb);
1603 break;
1604
1605 case NDISC_REDIRECT:
1606 ndisc_redirect_rcv(skb);
1607 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -07001608 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001609
1610 return 0;
1611}
1612
1613static int ndisc_netdev_event(struct notifier_block *this, unsigned long event, void *ptr)
1614{
1615 struct net_device *dev = ptr;
1616
Eric W. Biedermane9dc8652007-09-12 13:02:17 +02001617 if (dev->nd_net != &init_net)
1618 return NOTIFY_DONE;
1619
Linus Torvalds1da177e2005-04-16 15:20:36 -07001620 switch (event) {
1621 case NETDEV_CHANGEADDR:
1622 neigh_changeaddr(&nd_tbl, dev);
1623 fib6_run_gc(~0UL);
1624 break;
1625 case NETDEV_DOWN:
1626 neigh_ifdown(&nd_tbl, dev);
1627 fib6_run_gc(~0UL);
1628 break;
1629 default:
1630 break;
1631 }
1632
1633 return NOTIFY_DONE;
1634}
1635
1636static struct notifier_block ndisc_netdev_notifier = {
1637 .notifier_call = ndisc_netdev_event,
1638};
1639
1640#ifdef CONFIG_SYSCTL
1641static void ndisc_warn_deprecated_sysctl(struct ctl_table *ctl,
1642 const char *func, const char *dev_name)
1643{
1644 static char warncomm[TASK_COMM_LEN];
1645 static int warned;
1646 if (strcmp(warncomm, current->comm) && warned < 5) {
1647 strcpy(warncomm, current->comm);
1648 printk(KERN_WARNING
1649 "process `%s' is using deprecated sysctl (%s) "
1650 "net.ipv6.neigh.%s.%s; "
1651 "Use net.ipv6.neigh.%s.%s_ms "
1652 "instead.\n",
1653 warncomm, func,
1654 dev_name, ctl->procname,
1655 dev_name, ctl->procname);
1656 warned++;
1657 }
1658}
1659
1660int ndisc_ifinfo_sysctl_change(struct ctl_table *ctl, int write, struct file * filp, void __user *buffer, size_t *lenp, loff_t *ppos)
1661{
1662 struct net_device *dev = ctl->extra1;
1663 struct inet6_dev *idev;
1664 int ret;
1665
Eric W. Biedermand12af672007-10-18 03:05:25 -07001666 if ((strcmp(ctl->procname, "retrans_time") == 0) ||
1667 (strcmp(ctl->procname, "base_reachable_time") == 0))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001668 ndisc_warn_deprecated_sysctl(ctl, "syscall", dev ? dev->name : "default");
1669
Eric W. Biedermand12af672007-10-18 03:05:25 -07001670 if (strcmp(ctl->procname, "retrans_time") == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001671 ret = proc_dointvec(ctl, write, filp, buffer, lenp, ppos);
Eric W. Biedermand12af672007-10-18 03:05:25 -07001672
1673 else if (strcmp(ctl->procname, "base_reachable_time") == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001674 ret = proc_dointvec_jiffies(ctl, write,
1675 filp, buffer, lenp, ppos);
Eric W. Biedermand12af672007-10-18 03:05:25 -07001676
1677 else if ((strcmp(ctl->procname, "retrans_time_ms") == 0) ||
YOSHIFUJI Hideakiad02ac12007-10-29 01:32:23 -07001678 (strcmp(ctl->procname, "base_reachable_time_ms") == 0))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001679 ret = proc_dointvec_ms_jiffies(ctl, write,
1680 filp, buffer, lenp, ppos);
Eric W. Biedermand12af672007-10-18 03:05:25 -07001681 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001682 ret = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001683
1684 if (write && ret == 0 && dev && (idev = in6_dev_get(dev)) != NULL) {
Eric W. Biedermand12af672007-10-18 03:05:25 -07001685 if (ctl->data == &idev->nd_parms->base_reachable_time)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001686 idev->nd_parms->reachable_time = neigh_rand_reach_time(idev->nd_parms->base_reachable_time);
1687 idev->tstamp = jiffies;
1688 inet6_ifinfo_notify(RTM_NEWLINK, idev);
1689 in6_dev_put(idev);
1690 }
1691 return ret;
1692}
1693
1694static int ndisc_ifinfo_sysctl_strategy(ctl_table *ctl, int __user *name,
1695 int nlen, void __user *oldval,
1696 size_t __user *oldlenp,
Alexey Dobriyan1f29bcd2006-12-10 02:19:10 -08001697 void __user *newval, size_t newlen)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001698{
1699 struct net_device *dev = ctl->extra1;
1700 struct inet6_dev *idev;
1701 int ret;
1702
1703 if (ctl->ctl_name == NET_NEIGH_RETRANS_TIME ||
1704 ctl->ctl_name == NET_NEIGH_REACHABLE_TIME)
1705 ndisc_warn_deprecated_sysctl(ctl, "procfs", dev ? dev->name : "default");
1706
1707 switch (ctl->ctl_name) {
1708 case NET_NEIGH_REACHABLE_TIME:
1709 ret = sysctl_jiffies(ctl, name, nlen,
Alexey Dobriyan1f29bcd2006-12-10 02:19:10 -08001710 oldval, oldlenp, newval, newlen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001711 break;
1712 case NET_NEIGH_RETRANS_TIME_MS:
1713 case NET_NEIGH_REACHABLE_TIME_MS:
1714 ret = sysctl_ms_jiffies(ctl, name, nlen,
Alexey Dobriyan1f29bcd2006-12-10 02:19:10 -08001715 oldval, oldlenp, newval, newlen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001716 break;
1717 default:
1718 ret = 0;
1719 }
1720
1721 if (newval && newlen && ret > 0 &&
1722 dev && (idev = in6_dev_get(dev)) != NULL) {
1723 if (ctl->ctl_name == NET_NEIGH_REACHABLE_TIME ||
1724 ctl->ctl_name == NET_NEIGH_REACHABLE_TIME_MS)
1725 idev->nd_parms->reachable_time = neigh_rand_reach_time(idev->nd_parms->base_reachable_time);
1726 idev->tstamp = jiffies;
1727 inet6_ifinfo_notify(RTM_NEWLINK, idev);
1728 in6_dev_put(idev);
1729 }
1730
1731 return ret;
1732}
1733
1734#endif
1735
1736int __init ndisc_init(struct net_proto_family *ops)
1737{
1738 struct ipv6_pinfo *np;
1739 struct sock *sk;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001740 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001741
1742 err = sock_create_kern(PF_INET6, SOCK_RAW, IPPROTO_ICMPV6, &ndisc_socket);
1743 if (err < 0) {
1744 ND_PRINTK0(KERN_ERR
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001745 "ICMPv6 NDISC: Failed to initialize the control socket (err %d).\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001746 err);
1747 ndisc_socket = NULL; /* For safety. */
1748 return err;
1749 }
1750
1751 sk = ndisc_socket->sk;
1752 np = inet6_sk(sk);
1753 sk->sk_allocation = GFP_ATOMIC;
1754 np->hop_limit = 255;
1755 /* Do not loopback ndisc messages */
1756 np->mc_loop = 0;
1757 sk->sk_prot->unhash(sk);
1758
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001759 /*
1760 * Initialize the neighbour table
1761 */
1762
Linus Torvalds1da177e2005-04-16 15:20:36 -07001763 neigh_table_init(&nd_tbl);
1764
1765#ifdef CONFIG_SYSCTL
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001766 neigh_sysctl_register(NULL, &nd_tbl.parms, NET_IPV6, NET_IPV6_NEIGH,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001767 "ipv6",
1768 &ndisc_ifinfo_sysctl_change,
1769 &ndisc_ifinfo_sysctl_strategy);
1770#endif
1771
1772 register_netdevice_notifier(&ndisc_netdev_notifier);
1773 return 0;
1774}
1775
1776void ndisc_cleanup(void)
1777{
Dmitry Mishin36f73d02006-11-03 16:08:19 -08001778 unregister_netdevice_notifier(&ndisc_netdev_notifier);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001779#ifdef CONFIG_SYSCTL
1780 neigh_sysctl_unregister(&nd_tbl.parms);
1781#endif
1782 neigh_table_clear(&nd_tbl);
1783 sock_release(ndisc_socket);
1784 ndisc_socket = NULL; /* For safety. */
1785}