blob: b2531f80317e405bae41c93ff6d80cef240ab0c7 [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 */
559 ifp = ipv6_get_ifaddr(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) {
YOSHIFUJI Hideakica043562007-02-28 23:13:20 +0900619 struct inet6_ifaddr *ifp = ipv6_get_ifaddr(saddr, dev, 1);
Neil Horman95c385b2007-04-25 17:08:10 -0700620 if (ifp) {
621 if (ifp->flags & IFA_F_OPTIMISTIC) {
YOSHIFUJI Hideakica043562007-02-28 23:13:20 +0900622 send_sllao = 0;
Neil Horman95c385b2007-04-25 17:08:10 -0700623 }
YOSHIFUJI Hideakica043562007-02-28 23:13:20 +0900624 in6_ifa_put(ifp);
Neil Horman95c385b2007-04-25 17:08:10 -0700625 } else {
626 send_sllao = 0;
627 }
628 }
629#endif
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900630 __ndisc_send(dev, NULL, daddr, saddr,
631 &icmp6h, NULL,
David L Stevens14878f72007-09-16 16:52:35 -0700632 send_sllao ? ND_OPT_SOURCE_LL_ADDR : 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700633}
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900634
Linus Torvalds1da177e2005-04-16 15:20:36 -0700635
636static void ndisc_error_report(struct neighbour *neigh, struct sk_buff *skb)
637{
638 /*
639 * "The sender MUST return an ICMP
640 * destination unreachable"
641 */
642 dst_link_failure(skb);
643 kfree_skb(skb);
644}
645
646/* Called with locked neigh: either read or both */
647
648static void ndisc_solicit(struct neighbour *neigh, struct sk_buff *skb)
649{
650 struct in6_addr *saddr = NULL;
651 struct in6_addr mcaddr;
652 struct net_device *dev = neigh->dev;
653 struct in6_addr *target = (struct in6_addr *)&neigh->primary_key;
654 int probes = atomic_read(&neigh->probes);
655
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -0700656 if (skb && ipv6_chk_addr(&ipv6_hdr(skb)->saddr, dev, 1))
657 saddr = &ipv6_hdr(skb)->saddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700658
659 if ((probes -= neigh->parms->ucast_probes) < 0) {
660 if (!(neigh->nud_state & NUD_VALID)) {
661 ND_PRINTK1(KERN_DEBUG
662 "%s(): trying to ucast probe in NUD_INVALID: "
Joe Perches46b86a22006-01-13 14:29:07 -0800663 NIP6_FMT "\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664 __FUNCTION__,
665 NIP6(*target));
666 }
667 ndisc_send_ns(dev, neigh, target, target, saddr);
668 } else if ((probes -= neigh->parms->app_probes) < 0) {
669#ifdef CONFIG_ARPD
670 neigh_app_ns(neigh);
671#endif
672 } else {
673 addrconf_addr_solict_mult(target, &mcaddr);
674 ndisc_send_ns(dev, NULL, target, &mcaddr, saddr);
675 }
676}
677
678static void ndisc_recv_ns(struct sk_buff *skb)
679{
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -0700680 struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb);
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -0700681 struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
682 struct in6_addr *daddr = &ipv6_hdr(skb)->daddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700683 u8 *lladdr = NULL;
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -0700684 u32 ndoptlen = skb->tail - (skb->transport_header +
685 offsetof(struct nd_msg, opt));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700686 struct ndisc_options ndopts;
687 struct net_device *dev = skb->dev;
688 struct inet6_ifaddr *ifp;
689 struct inet6_dev *idev = NULL;
690 struct neighbour *neigh;
Ville Nuorvala62dd9312006-09-22 14:43:19 -0700691 struct pneigh_entry *pneigh = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700692 int dad = ipv6_addr_any(saddr);
693 int inc;
Ville Nuorvala62dd9312006-09-22 14:43:19 -0700694 int is_router;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700695
696 if (ipv6_addr_is_multicast(&msg->target)) {
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900697 ND_PRINTK2(KERN_WARNING
Linus Torvalds1da177e2005-04-16 15:20:36 -0700698 "ICMPv6 NS: multicast target address");
699 return;
700 }
701
702 /*
703 * RFC2461 7.1.1:
704 * DAD has to be destined for solicited node multicast address.
705 */
706 if (dad &&
707 !(daddr->s6_addr32[0] == htonl(0xff020000) &&
708 daddr->s6_addr32[1] == htonl(0x00000000) &&
709 daddr->s6_addr32[2] == htonl(0x00000001) &&
710 daddr->s6_addr [12] == 0xff )) {
711 ND_PRINTK2(KERN_WARNING
712 "ICMPv6 NS: bad DAD packet (wrong destination)\n");
713 return;
714 }
715
716 if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts)) {
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900717 ND_PRINTK2(KERN_WARNING
Linus Torvalds1da177e2005-04-16 15:20:36 -0700718 "ICMPv6 NS: invalid ND options\n");
719 return;
720 }
721
722 if (ndopts.nd_opts_src_lladdr) {
723 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr, dev);
724 if (!lladdr) {
725 ND_PRINTK2(KERN_WARNING
726 "ICMPv6 NS: invalid link-layer address length\n");
727 return;
728 }
729
730 /* RFC2461 7.1.1:
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900731 * If the IP source address is the unspecified address,
732 * there MUST NOT be source link-layer address option
Linus Torvalds1da177e2005-04-16 15:20:36 -0700733 * in the message.
734 */
735 if (dad) {
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900736 ND_PRINTK2(KERN_WARNING
Linus Torvalds1da177e2005-04-16 15:20:36 -0700737 "ICMPv6 NS: bad DAD packet (link-layer address option)\n");
738 return;
739 }
740 }
741
742 inc = ipv6_addr_is_multicast(daddr);
743
744 if ((ifp = ipv6_get_ifaddr(&msg->target, dev, 1)) != NULL) {
Neil Horman95c385b2007-04-25 17:08:10 -0700745
746 if (ifp->flags & (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)) {
747 if (dad) {
748 if (dev->type == ARPHRD_IEEE802_TR) {
Arnaldo Carvalho de Melo98e399f2007-03-19 15:33:04 -0700749 const unsigned char *sadr;
750 sadr = skb_mac_header(skb);
Neil Horman95c385b2007-04-25 17:08:10 -0700751 if (((sadr[8] ^ dev->dev_addr[0]) & 0x7f) == 0 &&
752 sadr[9] == dev->dev_addr[1] &&
753 sadr[10] == dev->dev_addr[2] &&
754 sadr[11] == dev->dev_addr[3] &&
755 sadr[12] == dev->dev_addr[4] &&
756 sadr[13] == dev->dev_addr[5]) {
757 /* looped-back to us */
758 goto out;
759 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700760 }
Neil Horman95c385b2007-04-25 17:08:10 -0700761
762 /*
763 * We are colliding with another node
764 * who is doing DAD
765 * so fail our DAD process
766 */
767 addrconf_dad_failure(ifp);
Denis V. Lunev9e3be4b2007-09-11 11:04:49 +0200768 return;
Neil Horman95c385b2007-04-25 17:08:10 -0700769 } else {
770 /*
771 * This is not a dad solicitation.
772 * If we are an optimistic node,
773 * we should respond.
774 * Otherwise, we should ignore it.
775 */
776 if (!(ifp->flags & IFA_F_OPTIMISTIC))
777 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700778 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700779 }
780
781 idev = ifp->idev;
782 } else {
783 idev = in6_dev_get(dev);
784 if (!idev) {
785 /* XXX: count this drop? */
786 return;
787 }
788
789 if (ipv6_chk_acast_addr(dev, &msg->target) ||
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900790 (idev->cnf.forwarding &&
YOSHIFUJI Hideakifbea49e2006-09-22 14:43:49 -0700791 (ipv6_devconf.proxy_ndp || idev->cnf.proxy_ndp) &&
Ville Nuorvala62dd9312006-09-22 14:43:19 -0700792 (pneigh = pneigh_lookup(&nd_tbl,
793 &msg->target, dev, 0)) != NULL)) {
Patrick McHardya61bbcf2005-08-14 17:24:31 -0700794 if (!(NEIGH_CB(skb)->flags & LOCALLY_ENQUEUED) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700795 skb->pkt_type != PACKET_HOST &&
796 inc != 0 &&
797 idev->nd_parms->proxy_delay != 0) {
798 /*
799 * for anycast or proxy,
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900800 * sender should delay its response
801 * by a random time between 0 and
Linus Torvalds1da177e2005-04-16 15:20:36 -0700802 * MAX_ANYCAST_DELAY_TIME seconds.
803 * (RFC2461) -- yoshfuji
804 */
805 struct sk_buff *n = skb_clone(skb, GFP_ATOMIC);
806 if (n)
807 pneigh_enqueue(&nd_tbl, idev->nd_parms, n);
808 goto out;
809 }
810 } else
811 goto out;
812 }
813
YOSHIFUJI Hideakifc26d0a2006-09-22 14:44:53 -0700814 is_router = !!(pneigh ? pneigh->flags & NTF_ROUTER : idev->cnf.forwarding);
Ville Nuorvala62dd9312006-09-22 14:43:19 -0700815
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816 if (dad) {
817 struct in6_addr maddr;
818
819 ipv6_addr_all_nodes(&maddr);
820 ndisc_send_na(dev, NULL, &maddr, &msg->target,
Ville Nuorvala62dd9312006-09-22 14:43:19 -0700821 is_router, 0, (ifp != NULL), 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700822 goto out;
823 }
824
825 if (inc)
826 NEIGH_CACHE_STAT_INC(&nd_tbl, rcv_probes_mcast);
827 else
828 NEIGH_CACHE_STAT_INC(&nd_tbl, rcv_probes_ucast);
829
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900830 /*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700831 * update / create cache entry
832 * for the source address
833 */
834 neigh = __neigh_lookup(&nd_tbl, saddr, dev,
835 !inc || lladdr || !dev->addr_len);
836 if (neigh)
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900837 neigh_update(neigh, lladdr, NUD_STALE,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700838 NEIGH_UPDATE_F_WEAK_OVERRIDE|
839 NEIGH_UPDATE_F_OVERRIDE);
Stephen Hemminger3b04ddd2007-10-09 01:40:57 -0700840 if (neigh || !dev->header_ops) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700841 ndisc_send_na(dev, neigh, saddr, &msg->target,
Ville Nuorvala62dd9312006-09-22 14:43:19 -0700842 is_router,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700843 1, (ifp != NULL && inc), inc);
844 if (neigh)
845 neigh_release(neigh);
846 }
847
848out:
849 if (ifp)
850 in6_ifa_put(ifp);
851 else
852 in6_dev_put(idev);
853
854 return;
855}
856
857static void ndisc_recv_na(struct sk_buff *skb)
858{
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -0700859 struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb);
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -0700860 struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
861 struct in6_addr *daddr = &ipv6_hdr(skb)->daddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700862 u8 *lladdr = NULL;
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -0700863 u32 ndoptlen = skb->tail - (skb->transport_header +
864 offsetof(struct nd_msg, opt));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700865 struct ndisc_options ndopts;
866 struct net_device *dev = skb->dev;
867 struct inet6_ifaddr *ifp;
868 struct neighbour *neigh;
869
870 if (skb->len < sizeof(struct nd_msg)) {
871 ND_PRINTK2(KERN_WARNING
872 "ICMPv6 NA: packet too short\n");
873 return;
874 }
875
876 if (ipv6_addr_is_multicast(&msg->target)) {
877 ND_PRINTK2(KERN_WARNING
878 "ICMPv6 NA: target address is multicast.\n");
879 return;
880 }
881
882 if (ipv6_addr_is_multicast(daddr) &&
883 msg->icmph.icmp6_solicited) {
884 ND_PRINTK2(KERN_WARNING
885 "ICMPv6 NA: solicited NA is multicasted.\n");
886 return;
887 }
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900888
Linus Torvalds1da177e2005-04-16 15:20:36 -0700889 if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts)) {
890 ND_PRINTK2(KERN_WARNING
891 "ICMPv6 NS: invalid ND option\n");
892 return;
893 }
894 if (ndopts.nd_opts_tgt_lladdr) {
895 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr, dev);
896 if (!lladdr) {
897 ND_PRINTK2(KERN_WARNING
898 "ICMPv6 NA: invalid link-layer address length\n");
899 return;
900 }
901 }
902 if ((ifp = ipv6_get_ifaddr(&msg->target, dev, 1))) {
903 if (ifp->flags & IFA_F_TENTATIVE) {
904 addrconf_dad_failure(ifp);
905 return;
906 }
907 /* What should we make now? The advertisement
908 is invalid, but ndisc specs say nothing
909 about it. It could be misconfiguration, or
910 an smart proxy agent tries to help us :-)
911 */
912 ND_PRINTK1(KERN_WARNING
913 "ICMPv6 NA: someone advertises our address on %s!\n",
914 ifp->idev->dev->name);
915 in6_ifa_put(ifp);
916 return;
917 }
918 neigh = neigh_lookup(&nd_tbl, &msg->target, dev);
919
920 if (neigh) {
921 u8 old_flags = neigh->flags;
922
923 if (neigh->nud_state & NUD_FAILED)
924 goto out;
925
Ville Nuorvala5f3e6e92006-09-22 14:42:46 -0700926 /*
927 * Don't update the neighbor cache entry on a proxy NA from
928 * ourselves because either the proxied node is off link or it
929 * has already sent a NA to us.
930 */
931 if (lladdr && !memcmp(lladdr, dev->dev_addr, dev->addr_len) &&
YOSHIFUJI Hideakifbea49e2006-09-22 14:43:49 -0700932 ipv6_devconf.forwarding && ipv6_devconf.proxy_ndp &&
933 pneigh_lookup(&nd_tbl, &msg->target, dev, 0)) {
934 /* XXX: idev->cnf.prixy_ndp */
Ville Nuorvala5f3e6e92006-09-22 14:42:46 -0700935 goto out;
YOSHIFUJI Hideakifbea49e2006-09-22 14:43:49 -0700936 }
Ville Nuorvala5f3e6e92006-09-22 14:42:46 -0700937
Linus Torvalds1da177e2005-04-16 15:20:36 -0700938 neigh_update(neigh, lladdr,
939 msg->icmph.icmp6_solicited ? NUD_REACHABLE : NUD_STALE,
940 NEIGH_UPDATE_F_WEAK_OVERRIDE|
941 (msg->icmph.icmp6_override ? NEIGH_UPDATE_F_OVERRIDE : 0)|
942 NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
943 (msg->icmph.icmp6_router ? NEIGH_UPDATE_F_ISROUTER : 0));
944
945 if ((old_flags & ~neigh->flags) & NTF_ROUTER) {
946 /*
947 * Change: router to host
948 */
949 struct rt6_info *rt;
950 rt = rt6_get_dflt_router(saddr, dev);
951 if (rt)
Thomas Grafe0a1ad732006-08-22 00:00:21 -0700952 ip6_del_rt(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700953 }
954
955out:
956 neigh_release(neigh);
957 }
958}
959
960static void ndisc_recv_rs(struct sk_buff *skb)
961{
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -0700962 struct rs_msg *rs_msg = (struct rs_msg *)skb_transport_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700963 unsigned long ndoptlen = skb->len - sizeof(*rs_msg);
964 struct neighbour *neigh;
965 struct inet6_dev *idev;
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -0700966 struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700967 struct ndisc_options ndopts;
968 u8 *lladdr = NULL;
969
970 if (skb->len < sizeof(*rs_msg))
971 return;
972
973 idev = in6_dev_get(skb->dev);
974 if (!idev) {
975 if (net_ratelimit())
976 ND_PRINTK1("ICMP6 RS: can't find in6 device\n");
977 return;
978 }
979
980 /* Don't accept RS if we're not in router mode */
981 if (!idev->cnf.forwarding)
982 goto out;
983
984 /*
985 * Don't update NCE if src = ::;
986 * this implies that the source node has no ip address assigned yet.
987 */
988 if (ipv6_addr_any(saddr))
989 goto out;
990
991 /* Parse ND options */
992 if (!ndisc_parse_options(rs_msg->opt, ndoptlen, &ndopts)) {
993 if (net_ratelimit())
994 ND_PRINTK2("ICMP6 NS: invalid ND option, ignored\n");
995 goto out;
996 }
997
998 if (ndopts.nd_opts_src_lladdr) {
999 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr,
1000 skb->dev);
1001 if (!lladdr)
1002 goto out;
1003 }
1004
1005 neigh = __neigh_lookup(&nd_tbl, saddr, skb->dev, 1);
1006 if (neigh) {
1007 neigh_update(neigh, lladdr, NUD_STALE,
1008 NEIGH_UPDATE_F_WEAK_OVERRIDE|
1009 NEIGH_UPDATE_F_OVERRIDE|
1010 NEIGH_UPDATE_F_OVERRIDE_ISROUTER);
1011 neigh_release(neigh);
1012 }
1013out:
1014 in6_dev_put(idev);
1015}
1016
Pierre Ynard31910572007-10-10 21:22:05 -07001017static void ndisc_ra_useropt(struct sk_buff *ra, struct nd_opt_hdr *opt)
1018{
1019 struct icmp6hdr *icmp6h = (struct icmp6hdr *)skb_transport_header(ra);
1020 struct sk_buff *skb;
1021 struct nlmsghdr *nlh;
1022 struct nduseroptmsg *ndmsg;
1023 int err;
1024 int base_size = NLMSG_ALIGN(sizeof(struct nduseroptmsg)
1025 + (opt->nd_opt_len << 3));
1026 size_t msg_size = base_size + nla_total_size(sizeof(struct in6_addr));
1027
1028 skb = nlmsg_new(msg_size, GFP_ATOMIC);
1029 if (skb == NULL) {
1030 err = -ENOBUFS;
1031 goto errout;
1032 }
1033
1034 nlh = nlmsg_put(skb, 0, 0, RTM_NEWNDUSEROPT, base_size, 0);
1035 if (nlh == NULL) {
1036 goto nla_put_failure;
1037 }
1038
1039 ndmsg = nlmsg_data(nlh);
1040 ndmsg->nduseropt_family = AF_INET6;
Pierre Ynarddbb2ed22007-11-12 17:58:35 -08001041 ndmsg->nduseropt_ifindex = ra->dev->ifindex;
Pierre Ynard31910572007-10-10 21:22:05 -07001042 ndmsg->nduseropt_icmp_type = icmp6h->icmp6_type;
1043 ndmsg->nduseropt_icmp_code = icmp6h->icmp6_code;
1044 ndmsg->nduseropt_opts_len = opt->nd_opt_len << 3;
1045
1046 memcpy(ndmsg + 1, opt, opt->nd_opt_len << 3);
1047
1048 NLA_PUT(skb, NDUSEROPT_SRCADDR, sizeof(struct in6_addr),
1049 &ipv6_hdr(ra)->saddr);
1050 nlmsg_end(skb, nlh);
1051
1052 err = rtnl_notify(skb, 0, RTNLGRP_ND_USEROPT, NULL, GFP_ATOMIC);
1053 if (err < 0)
1054 goto errout;
1055
1056 return;
1057
1058nla_put_failure:
1059 nlmsg_free(skb);
1060 err = -EMSGSIZE;
1061errout:
1062 rtnl_set_sk_err(RTNLGRP_ND_USEROPT, err);
1063}
1064
Linus Torvalds1da177e2005-04-16 15:20:36 -07001065static void ndisc_router_discovery(struct sk_buff *skb)
1066{
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -07001067 struct ra_msg *ra_msg = (struct ra_msg *)skb_transport_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001068 struct neighbour *neigh = NULL;
1069 struct inet6_dev *in6_dev;
YOSHIFUJI Hideaki65f5c7c2006-03-20 16:55:08 -08001070 struct rt6_info *rt = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001071 int lifetime;
1072 struct ndisc_options ndopts;
1073 int optlen;
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001074 unsigned int pref = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001075
1076 __u8 * opt = (__u8 *)(ra_msg + 1);
1077
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -07001078 optlen = (skb->tail - skb->transport_header) - sizeof(struct ra_msg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001079
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001080 if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001081 ND_PRINTK2(KERN_WARNING
1082 "ICMPv6 RA: source address is not link-local.\n");
1083 return;
1084 }
1085 if (optlen < 0) {
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001086 ND_PRINTK2(KERN_WARNING
Linus Torvalds1da177e2005-04-16 15:20:36 -07001087 "ICMPv6 RA: packet too short\n");
1088 return;
1089 }
1090
1091 /*
1092 * set the RA_RECV flag in the interface
1093 */
1094
1095 in6_dev = in6_dev_get(skb->dev);
1096 if (in6_dev == NULL) {
1097 ND_PRINTK0(KERN_ERR
1098 "ICMPv6 RA: can't find inet6 device for %s.\n",
1099 skb->dev->name);
1100 return;
1101 }
1102 if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_ra) {
1103 in6_dev_put(in6_dev);
1104 return;
1105 }
1106
1107 if (!ndisc_parse_options(opt, optlen, &ndopts)) {
1108 in6_dev_put(in6_dev);
1109 ND_PRINTK2(KERN_WARNING
1110 "ICMP6 RA: invalid ND options\n");
1111 return;
1112 }
1113
1114 if (in6_dev->if_flags & IF_RS_SENT) {
1115 /*
1116 * flag that an RA was received after an RS was sent
1117 * out on this interface.
1118 */
1119 in6_dev->if_flags |= IF_RA_RCVD;
1120 }
1121
1122 /*
1123 * Remember the managed/otherconf flags from most recently
1124 * received RA message (RFC 2462) -- yoshfuji
1125 */
1126 in6_dev->if_flags = (in6_dev->if_flags & ~(IF_RA_MANAGED |
1127 IF_RA_OTHERCONF)) |
1128 (ra_msg->icmph.icmp6_addrconf_managed ?
1129 IF_RA_MANAGED : 0) |
1130 (ra_msg->icmph.icmp6_addrconf_other ?
1131 IF_RA_OTHERCONF : 0);
1132
YOSHIFUJI Hideaki65f5c7c2006-03-20 16:55:08 -08001133 if (!in6_dev->cnf.accept_ra_defrtr)
1134 goto skip_defrtr;
1135
Linus Torvalds1da177e2005-04-16 15:20:36 -07001136 lifetime = ntohs(ra_msg->icmph.icmp6_rt_lifetime);
1137
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001138#ifdef CONFIG_IPV6_ROUTER_PREF
1139 pref = ra_msg->icmph.icmp6_router_pref;
1140 /* 10b is handled as if it were 00b (medium) */
YOSHIFUJI Hideaki930d6ff2006-03-20 17:05:30 -08001141 if (pref == ICMPV6_ROUTER_PREF_INVALID ||
YOSHIFUJI Hideaki6d5b78c2007-06-22 16:07:04 -07001142 !in6_dev->cnf.accept_ra_rtr_pref)
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001143 pref = ICMPV6_ROUTER_PREF_MEDIUM;
1144#endif
1145
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001146 rt = rt6_get_dflt_router(&ipv6_hdr(skb)->saddr, skb->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001147
1148 if (rt)
1149 neigh = rt->rt6i_nexthop;
1150
1151 if (rt && lifetime == 0) {
1152 neigh_clone(neigh);
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001153 ip6_del_rt(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001154 rt = NULL;
1155 }
1156
1157 if (rt == NULL && lifetime) {
1158 ND_PRINTK3(KERN_DEBUG
1159 "ICMPv6 RA: adding default router.\n");
1160
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001161 rt = rt6_add_dflt_router(&ipv6_hdr(skb)->saddr, skb->dev, pref);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001162 if (rt == NULL) {
1163 ND_PRINTK0(KERN_ERR
1164 "ICMPv6 RA: %s() failed to add default route.\n",
1165 __FUNCTION__);
1166 in6_dev_put(in6_dev);
1167 return;
1168 }
1169
1170 neigh = rt->rt6i_nexthop;
1171 if (neigh == NULL) {
1172 ND_PRINTK0(KERN_ERR
1173 "ICMPv6 RA: %s() got default router without neighbour.\n",
1174 __FUNCTION__);
1175 dst_release(&rt->u.dst);
1176 in6_dev_put(in6_dev);
1177 return;
1178 }
1179 neigh->flags |= NTF_ROUTER;
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001180 } else if (rt) {
1181 rt->rt6i_flags |= (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001182 }
1183
1184 if (rt)
1185 rt->rt6i_expires = jiffies + (HZ * lifetime);
1186
1187 if (ra_msg->icmph.icmp6_hop_limit) {
1188 in6_dev->cnf.hop_limit = ra_msg->icmph.icmp6_hop_limit;
1189 if (rt)
1190 rt->u.dst.metrics[RTAX_HOPLIMIT-1] = ra_msg->icmph.icmp6_hop_limit;
1191 }
1192
YOSHIFUJI Hideaki65f5c7c2006-03-20 16:55:08 -08001193skip_defrtr:
1194
Linus Torvalds1da177e2005-04-16 15:20:36 -07001195 /*
1196 * Update Reachable Time and Retrans Timer
1197 */
1198
1199 if (in6_dev->nd_parms) {
1200 unsigned long rtime = ntohl(ra_msg->retrans_timer);
1201
1202 if (rtime && rtime/1000 < MAX_SCHEDULE_TIMEOUT/HZ) {
1203 rtime = (rtime*HZ)/1000;
1204 if (rtime < HZ/10)
1205 rtime = HZ/10;
1206 in6_dev->nd_parms->retrans_time = rtime;
1207 in6_dev->tstamp = jiffies;
1208 inet6_ifinfo_notify(RTM_NEWLINK, in6_dev);
1209 }
1210
1211 rtime = ntohl(ra_msg->reachable_time);
1212 if (rtime && rtime/1000 < MAX_SCHEDULE_TIMEOUT/(3*HZ)) {
1213 rtime = (rtime*HZ)/1000;
1214
1215 if (rtime < HZ/10)
1216 rtime = HZ/10;
1217
1218 if (rtime != in6_dev->nd_parms->base_reachable_time) {
1219 in6_dev->nd_parms->base_reachable_time = rtime;
1220 in6_dev->nd_parms->gc_staletime = 3 * rtime;
1221 in6_dev->nd_parms->reachable_time = neigh_rand_reach_time(rtime);
1222 in6_dev->tstamp = jiffies;
1223 inet6_ifinfo_notify(RTM_NEWLINK, in6_dev);
1224 }
1225 }
1226 }
1227
1228 /*
1229 * Process options.
1230 */
1231
1232 if (!neigh)
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001233 neigh = __neigh_lookup(&nd_tbl, &ipv6_hdr(skb)->saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001234 skb->dev, 1);
1235 if (neigh) {
1236 u8 *lladdr = NULL;
1237 if (ndopts.nd_opts_src_lladdr) {
1238 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr,
1239 skb->dev);
1240 if (!lladdr) {
1241 ND_PRINTK2(KERN_WARNING
1242 "ICMPv6 RA: invalid link-layer address length\n");
1243 goto out;
1244 }
1245 }
1246 neigh_update(neigh, lladdr, NUD_STALE,
1247 NEIGH_UPDATE_F_WEAK_OVERRIDE|
1248 NEIGH_UPDATE_F_OVERRIDE|
1249 NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
1250 NEIGH_UPDATE_F_ISROUTER);
1251 }
1252
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001253#ifdef CONFIG_IPV6_ROUTE_INFO
YOSHIFUJI Hideaki09c884d2006-03-20 17:07:03 -08001254 if (in6_dev->cnf.accept_ra_rtr_pref && ndopts.nd_opts_ri) {
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001255 struct nd_opt_hdr *p;
1256 for (p = ndopts.nd_opts_ri;
1257 p;
1258 p = ndisc_next_option(p, ndopts.nd_opts_ri_end)) {
YOSHIFUJI Hideaki09c884d2006-03-20 17:07:03 -08001259 if (((struct route_info *)p)->prefix_len > in6_dev->cnf.accept_ra_rt_info_max_plen)
1260 continue;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001261 rt6_route_rcv(skb->dev, (u8*)p, (p->nd_opt_len) << 3,
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001262 &ipv6_hdr(skb)->saddr);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001263 }
1264 }
1265#endif
1266
YOSHIFUJI Hideakic4fd30e2006-03-20 16:55:26 -08001267 if (in6_dev->cnf.accept_ra_pinfo && ndopts.nd_opts_pi) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001268 struct nd_opt_hdr *p;
1269 for (p = ndopts.nd_opts_pi;
1270 p;
1271 p = ndisc_next_option(p, ndopts.nd_opts_pi_end)) {
1272 addrconf_prefix_rcv(skb->dev, (u8*)p, (p->nd_opt_len) << 3);
1273 }
1274 }
1275
1276 if (ndopts.nd_opts_mtu) {
Al Viroe69a4ad2006-11-14 20:56:00 -08001277 __be32 n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001278 u32 mtu;
1279
Al Viroe69a4ad2006-11-14 20:56:00 -08001280 memcpy(&n, ((u8*)(ndopts.nd_opts_mtu+1))+2, sizeof(mtu));
1281 mtu = ntohl(n);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001282
1283 if (mtu < IPV6_MIN_MTU || mtu > skb->dev->mtu) {
1284 ND_PRINTK2(KERN_WARNING
1285 "ICMPv6 RA: invalid mtu: %d\n",
1286 mtu);
1287 } else if (in6_dev->cnf.mtu6 != mtu) {
1288 in6_dev->cnf.mtu6 = mtu;
1289
1290 if (rt)
1291 rt->u.dst.metrics[RTAX_MTU-1] = mtu;
1292
1293 rt6_mtu_change(skb->dev, mtu);
1294 }
1295 }
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001296
Pierre Ynard31910572007-10-10 21:22:05 -07001297 if (ndopts.nd_useropts) {
1298 struct nd_opt_hdr *opt;
1299 for (opt = ndopts.nd_useropts;
1300 opt;
1301 opt = ndisc_next_useropt(opt, ndopts.nd_useropts_end)) {
1302 ndisc_ra_useropt(skb, opt);
1303 }
1304 }
1305
Linus Torvalds1da177e2005-04-16 15:20:36 -07001306 if (ndopts.nd_opts_tgt_lladdr || ndopts.nd_opts_rh) {
1307 ND_PRINTK2(KERN_WARNING
1308 "ICMPv6 RA: invalid RA options");
1309 }
1310out:
1311 if (rt)
1312 dst_release(&rt->u.dst);
1313 else if (neigh)
1314 neigh_release(neigh);
1315 in6_dev_put(in6_dev);
1316}
1317
1318static void ndisc_redirect_rcv(struct sk_buff *skb)
1319{
1320 struct inet6_dev *in6_dev;
1321 struct icmp6hdr *icmph;
1322 struct in6_addr *dest;
1323 struct in6_addr *target; /* new first hop to destination */
1324 struct neighbour *neigh;
1325 int on_link = 0;
1326 struct ndisc_options ndopts;
1327 int optlen;
1328 u8 *lladdr = NULL;
1329
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001330 if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001331 ND_PRINTK2(KERN_WARNING
1332 "ICMPv6 Redirect: source address is not link-local.\n");
1333 return;
1334 }
1335
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -07001336 optlen = skb->tail - skb->transport_header;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001337 optlen -= sizeof(struct icmp6hdr) + 2 * sizeof(struct in6_addr);
1338
1339 if (optlen < 0) {
1340 ND_PRINTK2(KERN_WARNING
1341 "ICMPv6 Redirect: packet too short\n");
1342 return;
1343 }
1344
Arnaldo Carvalho de Melocc70ab22007-03-13 14:03:22 -03001345 icmph = icmp6_hdr(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001346 target = (struct in6_addr *) (icmph + 1);
1347 dest = target + 1;
1348
1349 if (ipv6_addr_is_multicast(dest)) {
1350 ND_PRINTK2(KERN_WARNING
1351 "ICMPv6 Redirect: destination address is multicast.\n");
1352 return;
1353 }
1354
1355 if (ipv6_addr_equal(dest, target)) {
1356 on_link = 1;
Brian Haleybf0b48d2007-10-08 00:12:05 -07001357 } else if (ipv6_addr_type(target) !=
1358 (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) {
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001359 ND_PRINTK2(KERN_WARNING
Brian Haleybf0b48d2007-10-08 00:12:05 -07001360 "ICMPv6 Redirect: target address is not link-local unicast.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001361 return;
1362 }
1363
1364 in6_dev = in6_dev_get(skb->dev);
1365 if (!in6_dev)
1366 return;
1367 if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects) {
1368 in6_dev_put(in6_dev);
1369 return;
1370 }
1371
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001372 /* RFC2461 8.1:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001373 * The IP source address of the Redirect MUST be the same as the current
1374 * first-hop router for the specified ICMP Destination Address.
1375 */
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001376
Linus Torvalds1da177e2005-04-16 15:20:36 -07001377 if (!ndisc_parse_options((u8*)(dest + 1), optlen, &ndopts)) {
1378 ND_PRINTK2(KERN_WARNING
1379 "ICMPv6 Redirect: invalid ND options\n");
1380 in6_dev_put(in6_dev);
1381 return;
1382 }
1383 if (ndopts.nd_opts_tgt_lladdr) {
1384 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr,
1385 skb->dev);
1386 if (!lladdr) {
1387 ND_PRINTK2(KERN_WARNING
1388 "ICMPv6 Redirect: invalid link-layer address length\n");
1389 in6_dev_put(in6_dev);
1390 return;
1391 }
1392 }
1393
1394 neigh = __neigh_lookup(&nd_tbl, target, skb->dev, 1);
1395 if (neigh) {
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001396 rt6_redirect(dest, &ipv6_hdr(skb)->daddr,
1397 &ipv6_hdr(skb)->saddr, neigh, lladdr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001398 on_link);
1399 neigh_release(neigh);
1400 }
1401 in6_dev_put(in6_dev);
1402}
1403
1404void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
1405 struct in6_addr *target)
1406{
1407 struct sock *sk = ndisc_socket->sk;
1408 int len = sizeof(struct icmp6hdr) + 2 * sizeof(struct in6_addr);
1409 struct sk_buff *buff;
1410 struct icmp6hdr *icmph;
1411 struct in6_addr saddr_buf;
1412 struct in6_addr *addrp;
1413 struct net_device *dev;
1414 struct rt6_info *rt;
1415 struct dst_entry *dst;
1416 struct inet6_dev *idev;
1417 struct flowi fl;
1418 u8 *opt;
1419 int rd_len;
1420 int err;
1421 int hlen;
1422 u8 ha_buf[MAX_ADDR_LEN], *ha = NULL;
1423
1424 dev = skb->dev;
1425
Neil Horman95c385b2007-04-25 17:08:10 -07001426 if (ipv6_get_lladdr(dev, &saddr_buf, IFA_F_TENTATIVE)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001427 ND_PRINTK2(KERN_WARNING
1428 "ICMPv6 Redirect: no link-local address on %s\n",
1429 dev->name);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001430 return;
1431 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001432
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001433 if (!ipv6_addr_equal(&ipv6_hdr(skb)->daddr, target) &&
Brian Haleybf0b48d2007-10-08 00:12:05 -07001434 ipv6_addr_type(target) != (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) {
Li Yewang29556522007-01-30 14:33:20 -08001435 ND_PRINTK2(KERN_WARNING
Brian Haleybf0b48d2007-10-08 00:12:05 -07001436 "ICMPv6 Redirect: target address is not link-local unicast.\n");
Li Yewang29556522007-01-30 14:33:20 -08001437 return;
1438 }
1439
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001440 ndisc_flow_init(&fl, NDISC_REDIRECT, &saddr_buf, &ipv6_hdr(skb)->saddr,
YOSHIFUJI Hideakiaf184762006-08-23 17:18:57 -07001441 dev->ifindex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001442
1443 dst = ip6_route_output(NULL, &fl);
1444 if (dst == NULL)
1445 return;
1446
1447 err = xfrm_lookup(&dst, &fl, NULL, 0);
Patrick McHardye1044112005-09-08 15:11:55 -07001448 if (err)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001449 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001450
1451 rt = (struct rt6_info *) dst;
1452
1453 if (rt->rt6i_flags & RTF_GATEWAY) {
1454 ND_PRINTK2(KERN_WARNING
1455 "ICMPv6 Redirect: destination is not a neighbour.\n");
1456 dst_release(dst);
1457 return;
1458 }
1459 if (!xrlim_allow(dst, 1*HZ)) {
1460 dst_release(dst);
1461 return;
1462 }
1463
1464 if (dev->addr_len) {
1465 read_lock_bh(&neigh->lock);
1466 if (neigh->nud_state & NUD_VALID) {
1467 memcpy(ha_buf, neigh->ha, dev->addr_len);
1468 read_unlock_bh(&neigh->lock);
1469 ha = ha_buf;
1470 len += ndisc_opt_addr_space(dev);
1471 } else
1472 read_unlock_bh(&neigh->lock);
1473 }
1474
1475 rd_len = min_t(unsigned int,
1476 IPV6_MIN_MTU-sizeof(struct ipv6hdr)-len, skb->len + 8);
1477 rd_len &= ~0x7;
1478 len += rd_len;
1479
David S. Millerd54a81d2006-12-02 21:00:06 -08001480 buff = sock_alloc_send_skb(sk,
1481 (MAX_HEADER + sizeof(struct ipv6hdr) +
1482 len + LL_RESERVED_SPACE(dev)),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001483 1, &err);
1484 if (buff == NULL) {
1485 ND_PRINTK0(KERN_ERR
1486 "ICMPv6 Redirect: %s() failed to allocate an skb.\n",
1487 __FUNCTION__);
1488 dst_release(dst);
1489 return;
1490 }
1491
1492 hlen = 0;
1493
1494 skb_reserve(buff, LL_RESERVED_SPACE(dev));
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001495 ip6_nd_hdr(sk, buff, dev, &saddr_buf, &ipv6_hdr(skb)->saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001496 IPPROTO_ICMPV6, len);
1497
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -07001498 skb_set_transport_header(buff, skb_tail_pointer(buff) - buff->data);
Arnaldo Carvalho de Melod10ba342007-03-14 21:05:37 -03001499 skb_put(buff, len);
1500 icmph = icmp6_hdr(buff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001501
1502 memset(icmph, 0, sizeof(struct icmp6hdr));
1503 icmph->icmp6_type = NDISC_REDIRECT;
1504
1505 /*
1506 * copy target and destination addresses
1507 */
1508
1509 addrp = (struct in6_addr *)(icmph + 1);
1510 ipv6_addr_copy(addrp, target);
1511 addrp++;
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001512 ipv6_addr_copy(addrp, &ipv6_hdr(skb)->daddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001513
1514 opt = (u8*) (addrp + 1);
1515
1516 /*
1517 * include target_address option
1518 */
1519
1520 if (ha)
1521 opt = ndisc_fill_addr_option(opt, ND_OPT_TARGET_LL_ADDR, ha,
1522 dev->addr_len, dev->type);
1523
1524 /*
1525 * build redirect option and copy skb over to the new packet.
1526 */
1527
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001528 memset(opt, 0, 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001529 *(opt++) = ND_OPT_REDIRECT_HDR;
1530 *(opt++) = (rd_len >> 3);
1531 opt += 6;
1532
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001533 memcpy(opt, ipv6_hdr(skb), rd_len - 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001534
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001535 icmph->icmp6_cksum = csum_ipv6_magic(&saddr_buf, &ipv6_hdr(skb)->saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001536 len, IPPROTO_ICMPV6,
1537 csum_partial((u8 *) icmph, len, 0));
1538
1539 buff->dst = dst;
1540 idev = in6_dev_get(dst->dev);
YOSHIFUJI Hideakia11d2062006-11-04 20:11:37 +09001541 IP6_INC_STATS(idev, IPSTATS_MIB_OUTREQUESTS);
Patrick McHardy6e23ae22007-11-19 18:53:30 -08001542 err = NF_HOOK(PF_INET6, NF_INET_LOCAL_OUT, buff, NULL, dst->dev,
1543 dst_output);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001544 if (!err) {
David L Stevens14878f72007-09-16 16:52:35 -07001545 ICMP6MSGOUT_INC_STATS(idev, NDISC_REDIRECT);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001546 ICMP6_INC_STATS(idev, ICMP6_MIB_OUTMSGS);
1547 }
1548
1549 if (likely(idev != NULL))
1550 in6_dev_put(idev);
1551}
1552
1553static void pndisc_redo(struct sk_buff *skb)
1554{
YOSHIFUJI Hideaki140e26fc2005-10-05 12:11:41 -07001555 ndisc_recv_ns(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001556 kfree_skb(skb);
1557}
1558
1559int ndisc_rcv(struct sk_buff *skb)
1560{
1561 struct nd_msg *msg;
1562
1563 if (!pskb_may_pull(skb, skb->len))
1564 return 0;
1565
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -07001566 msg = (struct nd_msg *)skb_transport_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001567
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -07001568 __skb_push(skb, skb->data - skb_transport_header(skb));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001569
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001570 if (ipv6_hdr(skb)->hop_limit != 255) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001571 ND_PRINTK2(KERN_WARNING
1572 "ICMPv6 NDISC: invalid hop-limit: %d\n",
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001573 ipv6_hdr(skb)->hop_limit);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001574 return 0;
1575 }
1576
1577 if (msg->icmph.icmp6_code != 0) {
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001578 ND_PRINTK2(KERN_WARNING
Linus Torvalds1da177e2005-04-16 15:20:36 -07001579 "ICMPv6 NDISC: invalid ICMPv6 code: %d\n",
1580 msg->icmph.icmp6_code);
1581 return 0;
1582 }
1583
Patrick McHardya61bbcf2005-08-14 17:24:31 -07001584 memset(NEIGH_CB(skb), 0, sizeof(struct neighbour_cb));
1585
Linus Torvalds1da177e2005-04-16 15:20:36 -07001586 switch (msg->icmph.icmp6_type) {
1587 case NDISC_NEIGHBOUR_SOLICITATION:
1588 ndisc_recv_ns(skb);
1589 break;
1590
1591 case NDISC_NEIGHBOUR_ADVERTISEMENT:
1592 ndisc_recv_na(skb);
1593 break;
1594
1595 case NDISC_ROUTER_SOLICITATION:
1596 ndisc_recv_rs(skb);
1597 break;
1598
1599 case NDISC_ROUTER_ADVERTISEMENT:
1600 ndisc_router_discovery(skb);
1601 break;
1602
1603 case NDISC_REDIRECT:
1604 ndisc_redirect_rcv(skb);
1605 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -07001606 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001607
1608 return 0;
1609}
1610
1611static int ndisc_netdev_event(struct notifier_block *this, unsigned long event, void *ptr)
1612{
1613 struct net_device *dev = ptr;
1614
Eric W. Biedermane9dc8652007-09-12 13:02:17 +02001615 if (dev->nd_net != &init_net)
1616 return NOTIFY_DONE;
1617
Linus Torvalds1da177e2005-04-16 15:20:36 -07001618 switch (event) {
1619 case NETDEV_CHANGEADDR:
1620 neigh_changeaddr(&nd_tbl, dev);
1621 fib6_run_gc(~0UL);
1622 break;
1623 case NETDEV_DOWN:
1624 neigh_ifdown(&nd_tbl, dev);
1625 fib6_run_gc(~0UL);
1626 break;
1627 default:
1628 break;
1629 }
1630
1631 return NOTIFY_DONE;
1632}
1633
1634static struct notifier_block ndisc_netdev_notifier = {
1635 .notifier_call = ndisc_netdev_event,
1636};
1637
1638#ifdef CONFIG_SYSCTL
1639static void ndisc_warn_deprecated_sysctl(struct ctl_table *ctl,
1640 const char *func, const char *dev_name)
1641{
1642 static char warncomm[TASK_COMM_LEN];
1643 static int warned;
1644 if (strcmp(warncomm, current->comm) && warned < 5) {
1645 strcpy(warncomm, current->comm);
1646 printk(KERN_WARNING
1647 "process `%s' is using deprecated sysctl (%s) "
1648 "net.ipv6.neigh.%s.%s; "
1649 "Use net.ipv6.neigh.%s.%s_ms "
1650 "instead.\n",
1651 warncomm, func,
1652 dev_name, ctl->procname,
1653 dev_name, ctl->procname);
1654 warned++;
1655 }
1656}
1657
1658int ndisc_ifinfo_sysctl_change(struct ctl_table *ctl, int write, struct file * filp, void __user *buffer, size_t *lenp, loff_t *ppos)
1659{
1660 struct net_device *dev = ctl->extra1;
1661 struct inet6_dev *idev;
1662 int ret;
1663
Eric W. Biedermand12af672007-10-18 03:05:25 -07001664 if ((strcmp(ctl->procname, "retrans_time") == 0) ||
1665 (strcmp(ctl->procname, "base_reachable_time") == 0))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001666 ndisc_warn_deprecated_sysctl(ctl, "syscall", dev ? dev->name : "default");
1667
Eric W. Biedermand12af672007-10-18 03:05:25 -07001668 if (strcmp(ctl->procname, "retrans_time") == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001669 ret = proc_dointvec(ctl, write, filp, buffer, lenp, ppos);
Eric W. Biedermand12af672007-10-18 03:05:25 -07001670
1671 else if (strcmp(ctl->procname, "base_reachable_time") == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001672 ret = proc_dointvec_jiffies(ctl, write,
1673 filp, buffer, lenp, ppos);
Eric W. Biedermand12af672007-10-18 03:05:25 -07001674
1675 else if ((strcmp(ctl->procname, "retrans_time_ms") == 0) ||
YOSHIFUJI Hideakiad02ac12007-10-29 01:32:23 -07001676 (strcmp(ctl->procname, "base_reachable_time_ms") == 0))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001677 ret = proc_dointvec_ms_jiffies(ctl, write,
1678 filp, buffer, lenp, ppos);
Eric W. Biedermand12af672007-10-18 03:05:25 -07001679 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001680 ret = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001681
1682 if (write && ret == 0 && dev && (idev = in6_dev_get(dev)) != NULL) {
Eric W. Biedermand12af672007-10-18 03:05:25 -07001683 if (ctl->data == &idev->nd_parms->base_reachable_time)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001684 idev->nd_parms->reachable_time = neigh_rand_reach_time(idev->nd_parms->base_reachable_time);
1685 idev->tstamp = jiffies;
1686 inet6_ifinfo_notify(RTM_NEWLINK, idev);
1687 in6_dev_put(idev);
1688 }
1689 return ret;
1690}
1691
1692static int ndisc_ifinfo_sysctl_strategy(ctl_table *ctl, int __user *name,
1693 int nlen, void __user *oldval,
1694 size_t __user *oldlenp,
Alexey Dobriyan1f29bcd2006-12-10 02:19:10 -08001695 void __user *newval, size_t newlen)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001696{
1697 struct net_device *dev = ctl->extra1;
1698 struct inet6_dev *idev;
1699 int ret;
1700
1701 if (ctl->ctl_name == NET_NEIGH_RETRANS_TIME ||
1702 ctl->ctl_name == NET_NEIGH_REACHABLE_TIME)
1703 ndisc_warn_deprecated_sysctl(ctl, "procfs", dev ? dev->name : "default");
1704
1705 switch (ctl->ctl_name) {
1706 case NET_NEIGH_REACHABLE_TIME:
1707 ret = sysctl_jiffies(ctl, name, nlen,
Alexey Dobriyan1f29bcd2006-12-10 02:19:10 -08001708 oldval, oldlenp, newval, newlen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001709 break;
1710 case NET_NEIGH_RETRANS_TIME_MS:
1711 case NET_NEIGH_REACHABLE_TIME_MS:
1712 ret = sysctl_ms_jiffies(ctl, name, nlen,
Alexey Dobriyan1f29bcd2006-12-10 02:19:10 -08001713 oldval, oldlenp, newval, newlen);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001714 break;
1715 default:
1716 ret = 0;
1717 }
1718
1719 if (newval && newlen && ret > 0 &&
1720 dev && (idev = in6_dev_get(dev)) != NULL) {
1721 if (ctl->ctl_name == NET_NEIGH_REACHABLE_TIME ||
1722 ctl->ctl_name == NET_NEIGH_REACHABLE_TIME_MS)
1723 idev->nd_parms->reachable_time = neigh_rand_reach_time(idev->nd_parms->base_reachable_time);
1724 idev->tstamp = jiffies;
1725 inet6_ifinfo_notify(RTM_NEWLINK, idev);
1726 in6_dev_put(idev);
1727 }
1728
1729 return ret;
1730}
1731
1732#endif
1733
1734int __init ndisc_init(struct net_proto_family *ops)
1735{
1736 struct ipv6_pinfo *np;
1737 struct sock *sk;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001738 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001739
1740 err = sock_create_kern(PF_INET6, SOCK_RAW, IPPROTO_ICMPV6, &ndisc_socket);
1741 if (err < 0) {
1742 ND_PRINTK0(KERN_ERR
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001743 "ICMPv6 NDISC: Failed to initialize the control socket (err %d).\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001744 err);
1745 ndisc_socket = NULL; /* For safety. */
1746 return err;
1747 }
1748
1749 sk = ndisc_socket->sk;
1750 np = inet6_sk(sk);
1751 sk->sk_allocation = GFP_ATOMIC;
1752 np->hop_limit = 255;
1753 /* Do not loopback ndisc messages */
1754 np->mc_loop = 0;
1755 sk->sk_prot->unhash(sk);
1756
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001757 /*
1758 * Initialize the neighbour table
1759 */
1760
Linus Torvalds1da177e2005-04-16 15:20:36 -07001761 neigh_table_init(&nd_tbl);
1762
1763#ifdef CONFIG_SYSCTL
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001764 neigh_sysctl_register(NULL, &nd_tbl.parms, NET_IPV6, NET_IPV6_NEIGH,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001765 "ipv6",
1766 &ndisc_ifinfo_sysctl_change,
1767 &ndisc_ifinfo_sysctl_strategy);
1768#endif
1769
1770 register_netdevice_notifier(&ndisc_netdev_notifier);
1771 return 0;
1772}
1773
1774void ndisc_cleanup(void)
1775{
Dmitry Mishin36f73d02006-11-03 16:08:19 -08001776 unregister_netdevice_notifier(&ndisc_netdev_notifier);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001777#ifdef CONFIG_SYSCTL
1778 neigh_sysctl_unregister(&nd_tbl.parms);
1779#endif
1780 neigh_table_clear(&nd_tbl);
1781 sock_release(ndisc_socket);
1782 ndisc_socket = NULL; /* For safety. */
1783}