blob: a4769881c5b5eea69d4a1c00fd9012a85330ad01 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * Neighbour Discovery for IPv6
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09003 * Linux INET6 implementation
Linus Torvalds1da177e2005-04-16 15:20:36 -07004 *
5 * Authors:
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09006 * Pedro Roque <roque@di.fc.ul.pt>
Linus Torvalds1da177e2005-04-16 15:20:36 -07007 * Mike Shaver <shaver@ingenia.com>
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version
12 * 2 of the License, or (at your option) any later version.
13 */
14
15/*
16 * Changes:
17 *
Pierre Ynard31910572007-10-10 21:22:05 -070018 * Pierre Ynard : export userland ND options
19 * through netlink (RDNSS support)
Linus Torvalds1da177e2005-04-16 15:20:36 -070020 * Lars Fenneberg : fixed MTU setting on receipt
21 * of an RA.
Linus Torvalds1da177e2005-04-16 15:20:36 -070022 * Janos Farkas : kmalloc failure checks
23 * Alexey Kuznetsov : state machine reworked
24 * and moved to net/core.
25 * Pekka Savola : RFC2461 validation
26 * YOSHIFUJI Hideaki @USAGI : Verify ND options properly
27 */
28
29/* Set to 3 to get tracing... */
30#define ND_DEBUG 1
31
32#define ND_PRINTK(fmt, args...) do { if (net_ratelimit()) { printk(fmt, ## args); } } while(0)
33#define ND_NOPRINTK(x...) do { ; } while(0)
34#define ND_PRINTK0 ND_PRINTK
35#define ND_PRINTK1 ND_NOPRINTK
36#define ND_PRINTK2 ND_NOPRINTK
37#define ND_PRINTK3 ND_NOPRINTK
38#if ND_DEBUG >= 1
39#undef ND_PRINTK1
40#define ND_PRINTK1 ND_PRINTK
41#endif
42#if ND_DEBUG >= 2
43#undef ND_PRINTK2
44#define ND_PRINTK2 ND_PRINTK
45#endif
46#if ND_DEBUG >= 3
47#undef ND_PRINTK3
48#define ND_PRINTK3 ND_PRINTK
49#endif
50
51#include <linux/module.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070052#include <linux/errno.h>
53#include <linux/types.h>
54#include <linux/socket.h>
55#include <linux/sockios.h>
56#include <linux/sched.h>
57#include <linux/net.h>
58#include <linux/in6.h>
59#include <linux/route.h>
60#include <linux/init.h>
61#include <linux/rcupdate.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090062#include <linux/slab.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070063#ifdef CONFIG_SYSCTL
64#include <linux/sysctl.h>
65#endif
66
Thomas Graf18237302006-08-04 23:04:54 -070067#include <linux/if_addr.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070068#include <linux/if_arp.h>
69#include <linux/ipv6.h>
70#include <linux/icmpv6.h>
71#include <linux/jhash.h>
72
73#include <net/sock.h>
74#include <net/snmp.h>
75
76#include <net/ipv6.h>
77#include <net/protocol.h>
78#include <net/ndisc.h>
79#include <net/ip6_route.h>
80#include <net/addrconf.h>
81#include <net/icmp.h>
82
Pierre Ynard31910572007-10-10 21:22:05 -070083#include <net/netlink.h>
84#include <linux/rtnetlink.h>
85
Linus Torvalds1da177e2005-04-16 15:20:36 -070086#include <net/flow.h>
87#include <net/ip6_checksum.h>
Denis V. Lunev1ed85162008-04-03 14:31:03 -070088#include <net/inet_common.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070089#include <linux/proc_fs.h>
90
91#include <linux/netfilter.h>
92#include <linux/netfilter_ipv6.h>
93
Eric Dumazetd6bf7812010-10-04 06:15:44 +000094static u32 ndisc_hash(const void *pkey,
95 const struct net_device *dev,
96 __u32 rnd);
Linus Torvalds1da177e2005-04-16 15:20:36 -070097static int ndisc_constructor(struct neighbour *neigh);
98static void ndisc_solicit(struct neighbour *neigh, struct sk_buff *skb);
99static void ndisc_error_report(struct neighbour *neigh, struct sk_buff *skb);
100static int pndisc_constructor(struct pneigh_entry *n);
101static void pndisc_destructor(struct pneigh_entry *n);
102static void pndisc_redo(struct sk_buff *skb);
103
Stephen Hemminger89d69d22009-09-01 11:13:19 +0000104static const struct neigh_ops ndisc_generic_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700105 .family = AF_INET6,
106 .solicit = ndisc_solicit,
107 .error_report = ndisc_error_report,
108 .output = neigh_resolve_output,
109 .connected_output = neigh_connected_output,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700110};
111
Stephen Hemminger89d69d22009-09-01 11:13:19 +0000112static const struct neigh_ops ndisc_hh_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700113 .family = AF_INET6,
114 .solicit = ndisc_solicit,
115 .error_report = ndisc_error_report,
116 .output = neigh_resolve_output,
117 .connected_output = neigh_resolve_output,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700118};
119
120
Stephen Hemminger89d69d22009-09-01 11:13:19 +0000121static const struct neigh_ops ndisc_direct_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700122 .family = AF_INET6,
David S. Miller8f40b162011-07-17 13:34:11 -0700123 .output = neigh_direct_output,
124 .connected_output = neigh_direct_output,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125};
126
127struct neigh_table nd_tbl = {
128 .family = AF_INET6,
129 .entry_size = sizeof(struct neighbour) + sizeof(struct in6_addr),
130 .key_len = sizeof(struct in6_addr),
131 .hash = ndisc_hash,
132 .constructor = ndisc_constructor,
133 .pconstructor = pndisc_constructor,
134 .pdestructor = pndisc_destructor,
135 .proxy_redo = pndisc_redo,
136 .id = "ndisc_cache",
137 .parms = {
Shan Weib6720832010-12-01 18:05:12 +0000138 .tbl = &nd_tbl,
139 .base_reachable_time = ND_REACHABLE_TIME,
140 .retrans_time = ND_RETRANS_TIMER,
141 .gc_staletime = 60 * HZ,
142 .reachable_time = ND_REACHABLE_TIME,
143 .delay_probe_time = 5 * HZ,
Eric Dumazet8b5c1712011-11-09 12:07:14 +0000144 .queue_len_bytes = 64*1024,
Shan Weib6720832010-12-01 18:05:12 +0000145 .ucast_probes = 3,
146 .mcast_probes = 3,
147 .anycast_delay = 1 * HZ,
148 .proxy_delay = (8 * HZ) / 10,
149 .proxy_qlen = 64,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150 },
151 .gc_interval = 30 * HZ,
152 .gc_thresh1 = 128,
153 .gc_thresh2 = 512,
154 .gc_thresh3 = 1024,
155};
156
157/* ND options */
158struct ndisc_options {
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800159 struct nd_opt_hdr *nd_opt_array[__ND_OPT_ARRAY_MAX];
160#ifdef CONFIG_IPV6_ROUTE_INFO
161 struct nd_opt_hdr *nd_opts_ri;
162 struct nd_opt_hdr *nd_opts_ri_end;
163#endif
Pierre Ynard31910572007-10-10 21:22:05 -0700164 struct nd_opt_hdr *nd_useropts;
165 struct nd_opt_hdr *nd_useropts_end;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700166};
167
168#define nd_opts_src_lladdr nd_opt_array[ND_OPT_SOURCE_LL_ADDR]
169#define nd_opts_tgt_lladdr nd_opt_array[ND_OPT_TARGET_LL_ADDR]
170#define nd_opts_pi nd_opt_array[ND_OPT_PREFIX_INFO]
171#define nd_opts_pi_end nd_opt_array[__ND_OPT_PREFIX_INFO_END]
172#define nd_opts_rh nd_opt_array[ND_OPT_REDIRECT_HDR]
173#define nd_opts_mtu nd_opt_array[ND_OPT_MTU]
174
175#define NDISC_OPT_SPACE(len) (((len)+2+7)&~7)
176
177/*
178 * Return the padding between the option length and the start of the
179 * link addr. Currently only IP-over-InfiniBand needs this, although
180 * if RFC 3831 IPv6-over-Fibre Channel is ever implemented it may
181 * also need a pad of 2.
182 */
183static int ndisc_addr_option_pad(unsigned short type)
184{
185 switch (type) {
186 case ARPHRD_INFINIBAND: return 2;
187 default: return 0;
188 }
189}
190
191static inline int ndisc_opt_addr_space(struct net_device *dev)
192{
193 return NDISC_OPT_SPACE(dev->addr_len + ndisc_addr_option_pad(dev->type));
194}
195
196static u8 *ndisc_fill_addr_option(u8 *opt, int type, void *data, int data_len,
197 unsigned short addr_type)
198{
199 int space = NDISC_OPT_SPACE(data_len);
200 int pad = ndisc_addr_option_pad(addr_type);
201
202 opt[0] = type;
203 opt[1] = space>>3;
204
205 memset(opt + 2, 0, pad);
206 opt += pad;
207 space -= pad;
208
209 memcpy(opt+2, data, data_len);
210 data_len += 2;
211 opt += data_len;
212 if ((space -= data_len) > 0)
213 memset(opt, 0, space);
214 return opt + space;
215}
216
217static struct nd_opt_hdr *ndisc_next_option(struct nd_opt_hdr *cur,
218 struct nd_opt_hdr *end)
219{
220 int type;
221 if (!cur || !end || cur >= end)
222 return NULL;
223 type = cur->nd_opt_type;
224 do {
225 cur = ((void *)cur) + (cur->nd_opt_len << 3);
226 } while(cur < end && cur->nd_opt_type != type);
Eric Dumazeta02cec22010-09-22 20:43:57 +0000227 return cur <= end && cur->nd_opt_type == type ? cur : NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700228}
229
Pierre Ynard31910572007-10-10 21:22:05 -0700230static inline int ndisc_is_useropt(struct nd_opt_hdr *opt)
231{
Eric Dumazeta02cec22010-09-22 20:43:57 +0000232 return opt->nd_opt_type == ND_OPT_RDNSS;
Pierre Ynard31910572007-10-10 21:22:05 -0700233}
234
235static struct nd_opt_hdr *ndisc_next_useropt(struct nd_opt_hdr *cur,
236 struct nd_opt_hdr *end)
237{
238 if (!cur || !end || cur >= end)
239 return NULL;
240 do {
241 cur = ((void *)cur) + (cur->nd_opt_len << 3);
242 } while(cur < end && !ndisc_is_useropt(cur));
Eric Dumazeta02cec22010-09-22 20:43:57 +0000243 return cur <= end && ndisc_is_useropt(cur) ? cur : NULL;
Pierre Ynard31910572007-10-10 21:22:05 -0700244}
245
Linus Torvalds1da177e2005-04-16 15:20:36 -0700246static struct ndisc_options *ndisc_parse_options(u8 *opt, int opt_len,
247 struct ndisc_options *ndopts)
248{
249 struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)opt;
250
251 if (!nd_opt || opt_len < 0 || !ndopts)
252 return NULL;
253 memset(ndopts, 0, sizeof(*ndopts));
254 while (opt_len) {
255 int l;
256 if (opt_len < sizeof(struct nd_opt_hdr))
257 return NULL;
258 l = nd_opt->nd_opt_len << 3;
259 if (opt_len < l || l == 0)
260 return NULL;
261 switch (nd_opt->nd_opt_type) {
262 case ND_OPT_SOURCE_LL_ADDR:
263 case ND_OPT_TARGET_LL_ADDR:
264 case ND_OPT_MTU:
265 case ND_OPT_REDIRECT_HDR:
266 if (ndopts->nd_opt_array[nd_opt->nd_opt_type]) {
267 ND_PRINTK2(KERN_WARNING
268 "%s(): duplicated ND6 option found: type=%d\n",
Harvey Harrison0dc47872008-03-05 20:47:47 -0800269 __func__,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700270 nd_opt->nd_opt_type);
271 } else {
272 ndopts->nd_opt_array[nd_opt->nd_opt_type] = nd_opt;
273 }
274 break;
275 case ND_OPT_PREFIX_INFO:
276 ndopts->nd_opts_pi_end = nd_opt;
Stephen Hemmingercfcabdc2007-10-09 01:59:42 -0700277 if (!ndopts->nd_opt_array[nd_opt->nd_opt_type])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700278 ndopts->nd_opt_array[nd_opt->nd_opt_type] = nd_opt;
279 break;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800280#ifdef CONFIG_IPV6_ROUTE_INFO
281 case ND_OPT_ROUTE_INFO:
282 ndopts->nd_opts_ri_end = nd_opt;
283 if (!ndopts->nd_opts_ri)
284 ndopts->nd_opts_ri = nd_opt;
285 break;
286#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700287 default:
Pierre Ynard31910572007-10-10 21:22:05 -0700288 if (ndisc_is_useropt(nd_opt)) {
289 ndopts->nd_useropts_end = nd_opt;
290 if (!ndopts->nd_useropts)
291 ndopts->nd_useropts = nd_opt;
292 } else {
293 /*
294 * Unknown options must be silently ignored,
295 * to accommodate future extension to the
296 * protocol.
297 */
298 ND_PRINTK2(KERN_NOTICE
299 "%s(): ignored unsupported option; type=%d, len=%d\n",
Harvey Harrison0dc47872008-03-05 20:47:47 -0800300 __func__,
Pierre Ynard31910572007-10-10 21:22:05 -0700301 nd_opt->nd_opt_type, nd_opt->nd_opt_len);
302 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303 }
304 opt_len -= l;
305 nd_opt = ((void *)nd_opt) + l;
306 }
307 return ndopts;
308}
309
310static inline u8 *ndisc_opt_addr_data(struct nd_opt_hdr *p,
311 struct net_device *dev)
312{
313 u8 *lladdr = (u8 *)(p + 1);
314 int lladdrlen = p->nd_opt_len << 3;
315 int prepad = ndisc_addr_option_pad(dev->type);
316 if (lladdrlen != NDISC_OPT_SPACE(dev->addr_len + prepad))
317 return NULL;
Eric Dumazeta02cec22010-09-22 20:43:57 +0000318 return lladdr + prepad;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700319}
320
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000321int ndisc_mc_map(const struct in6_addr *addr, char *buf, struct net_device *dev, int dir)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322{
323 switch (dev->type) {
324 case ARPHRD_ETHER:
325 case ARPHRD_IEEE802: /* Not sure. Check it later. --ANK */
326 case ARPHRD_FDDI:
327 ipv6_eth_mc_map(addr, buf);
328 return 0;
329 case ARPHRD_IEEE802_TR:
330 ipv6_tr_mc_map(addr,buf);
331 return 0;
332 case ARPHRD_ARCNET:
333 ipv6_arcnet_mc_map(addr, buf);
334 return 0;
335 case ARPHRD_INFINIBAND:
Rolf Manderscheida9e527e2007-12-10 13:38:41 -0700336 ipv6_ib_mc_map(addr, dev->broadcast, buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337 return 0;
Timo Teräs93ca3bb2011-03-28 22:40:53 +0000338 case ARPHRD_IPGRE:
339 return ipv6_ipgre_mc_map(addr, dev->broadcast, buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340 default:
341 if (dir) {
342 memcpy(buf, dev->broadcast, dev->addr_len);
343 return 0;
344 }
345 }
346 return -EINVAL;
347}
348
YOSHIFUJI Hideaki71590392007-02-22 22:05:40 +0900349EXPORT_SYMBOL(ndisc_mc_map);
350
Eric Dumazetd6bf7812010-10-04 06:15:44 +0000351static u32 ndisc_hash(const void *pkey,
352 const struct net_device *dev,
353 __u32 hash_rnd)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700354{
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
Eric Dumazetd6bf7812010-10-04 06:15:44 +0000362 return jhash_2words(addr_hash, dev->ifindex, hash_rnd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363}
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
Linus Torvalds1da177e2005-04-16 15:20:36 -0700373 in6_dev = in6_dev_get(dev);
374 if (in6_dev == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700375 return -EINVAL;
376 }
377
378 parms = in6_dev->nd_parms;
379 __neigh_parms_put(neigh->parms);
380 neigh->parms = neigh_parms_clone(parms);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700381
382 neigh->type = is_multicast ? RTN_MULTICAST : RTN_UNICAST;
Stephen Hemminger3b04ddd2007-10-09 01:40:57 -0700383 if (!dev->header_ops) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700384 neigh->nud_state = NUD_NOARP;
385 neigh->ops = &ndisc_direct_ops;
David S. Miller8f40b162011-07-17 13:34:11 -0700386 neigh->output = neigh_direct_output;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700387 } else {
388 if (is_multicast) {
389 neigh->nud_state = NUD_NOARP;
390 ndisc_mc_map(addr, neigh->ha, dev, 1);
391 } else if (dev->flags&(IFF_NOARP|IFF_LOOPBACK)) {
392 neigh->nud_state = NUD_NOARP;
393 memcpy(neigh->ha, dev->dev_addr, dev->addr_len);
394 if (dev->flags&IFF_LOOPBACK)
395 neigh->type = RTN_LOCAL;
396 } else if (dev->flags&IFF_POINTOPOINT) {
397 neigh->nud_state = NUD_NOARP;
398 memcpy(neigh->ha, dev->broadcast, dev->addr_len);
399 }
Stephen Hemminger3b04ddd2007-10-09 01:40:57 -0700400 if (dev->header_ops->cache)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700401 neigh->ops = &ndisc_hh_ops;
402 else
403 neigh->ops = &ndisc_generic_ops;
404 if (neigh->nud_state&NUD_VALID)
405 neigh->output = neigh->ops->connected_output;
406 else
407 neigh->output = neigh->ops->output;
408 }
409 in6_dev_put(in6_dev);
410 return 0;
411}
412
413static int pndisc_constructor(struct pneigh_entry *n)
414{
415 struct in6_addr *addr = (struct in6_addr*)&n->key;
416 struct in6_addr maddr;
417 struct net_device *dev = n->dev;
418
419 if (dev == NULL || __in6_dev_get(dev) == NULL)
420 return -EINVAL;
421 addrconf_addr_solict_mult(addr, &maddr);
422 ipv6_dev_mc_inc(dev, &maddr);
423 return 0;
424}
425
426static void pndisc_destructor(struct pneigh_entry *n)
427{
428 struct in6_addr *addr = (struct in6_addr*)&n->key;
429 struct in6_addr maddr;
430 struct net_device *dev = n->dev;
431
432 if (dev == NULL || __in6_dev_get(dev) == NULL)
433 return;
434 addrconf_addr_solict_mult(addr, &maddr);
435 ipv6_dev_mc_dec(dev, &maddr);
436}
437
Brian Haley305d5522008-11-04 17:51:14 -0800438struct sk_buff *ndisc_build_skb(struct net_device *dev,
439 const struct in6_addr *daddr,
440 const struct in6_addr *saddr,
441 struct icmp6hdr *icmp6h,
442 const struct in6_addr *target,
443 int llinfo)
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900444{
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900445 struct net *net = dev_net(dev);
Daniel Lezcano1762f7e2008-03-07 11:15:34 -0800446 struct sock *sk = net->ipv6.ndisc_sk;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900447 struct sk_buff *skb;
448 struct icmp6hdr *hdr;
Herbert Xua7ae1992011-11-18 02:20:04 +0000449 int hlen = LL_RESERVED_SPACE(dev);
450 int tlen = dev->needed_tailroom;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900451 int len;
452 int err;
Brian Haley305d5522008-11-04 17:51:14 -0800453 u8 *opt;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900454
455 if (!dev->addr_len)
456 llinfo = 0;
457
458 len = sizeof(struct icmp6hdr) + (target ? sizeof(*target) : 0);
459 if (llinfo)
460 len += ndisc_opt_addr_space(dev);
461
462 skb = sock_alloc_send_skb(sk,
463 (MAX_HEADER + sizeof(struct ipv6hdr) +
Herbert Xua7ae1992011-11-18 02:20:04 +0000464 len + hlen + tlen),
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900465 1, &err);
466 if (!skb) {
467 ND_PRINTK0(KERN_ERR
Brian Haleydae9de82009-06-02 00:20:26 -0700468 "ICMPv6 ND: %s() failed to allocate an skb, err=%d.\n",
469 __func__, err);
Brian Haley305d5522008-11-04 17:51:14 -0800470 return NULL;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900471 }
472
Herbert Xua7ae1992011-11-18 02:20:04 +0000473 skb_reserve(skb, hlen);
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900474 ip6_nd_hdr(sk, skb, dev, saddr, daddr, IPPROTO_ICMPV6, len);
475
476 skb->transport_header = skb->tail;
477 skb_put(skb, len);
478
479 hdr = (struct icmp6hdr *)skb_transport_header(skb);
480 memcpy(hdr, icmp6h, sizeof(*hdr));
481
482 opt = skb_transport_header(skb) + sizeof(struct icmp6hdr);
483 if (target) {
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +0000484 *(struct in6_addr *)opt = *target;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900485 opt += sizeof(*target);
486 }
487
488 if (llinfo)
489 ndisc_fill_addr_option(opt, llinfo, dev->dev_addr,
490 dev->addr_len, dev->type);
491
492 hdr->icmp6_cksum = csum_ipv6_magic(saddr, daddr, len,
493 IPPROTO_ICMPV6,
Joe Perches07f07572008-11-19 15:44:53 -0800494 csum_partial(hdr,
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900495 len, 0));
496
Brian Haley305d5522008-11-04 17:51:14 -0800497 return skb;
498}
499
500EXPORT_SYMBOL(ndisc_build_skb);
501
502void ndisc_send_skb(struct sk_buff *skb,
503 struct net_device *dev,
504 struct neighbour *neigh,
505 const struct in6_addr *daddr,
506 const struct in6_addr *saddr,
507 struct icmp6hdr *icmp6h)
508{
David S. Miller4c9483b2011-03-12 16:22:43 -0500509 struct flowi6 fl6;
Brian Haley305d5522008-11-04 17:51:14 -0800510 struct dst_entry *dst;
511 struct net *net = dev_net(dev);
512 struct sock *sk = net->ipv6.ndisc_sk;
513 struct inet6_dev *idev;
514 int err;
515 u8 type;
516
517 type = icmp6h->icmp6_type;
518
David S. Miller4c9483b2011-03-12 16:22:43 -0500519 icmpv6_flow_init(sk, &fl6, type, saddr, daddr, dev->ifindex);
Brian Haley305d5522008-11-04 17:51:14 -0800520
521 dst = icmp6_dst_alloc(dev, neigh, daddr);
522 if (!dst) {
523 kfree_skb(skb);
524 return;
525 }
526
David S. Miller4c9483b2011-03-12 16:22:43 -0500527 dst = xfrm_lookup(net, dst, flowi6_to_flowi(&fl6), NULL, 0);
David S. Miller452edd52011-03-02 13:27:41 -0800528 if (IS_ERR(dst)) {
Brian Haley305d5522008-11-04 17:51:14 -0800529 kfree_skb(skb);
530 return;
531 }
532
Eric Dumazetadf30902009-06-02 05:19:30 +0000533 skb_dst_set(skb, dst);
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900534
Eric Dumazetcfdf7642011-07-27 21:13:03 +0000535 rcu_read_lock();
536 idev = __in6_dev_get(dst->dev);
Neil Hormanedf391f2009-04-27 02:45:02 -0700537 IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUT, skb->len);
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900538
Jan Engelhardtb2e0b382010-03-23 04:09:07 +0100539 err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, skb, NULL, dst->dev,
Patrick McHardy6e23ae22007-11-19 18:53:30 -0800540 dst_output);
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900541 if (!err) {
Denis V. Lunev5c5d2442008-10-08 10:33:50 -0700542 ICMP6MSGOUT_INC_STATS(net, idev, type);
Denis V. Luneva862f6a2008-10-08 10:33:06 -0700543 ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS);
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900544 }
545
Eric Dumazetcfdf7642011-07-27 21:13:03 +0000546 rcu_read_unlock();
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900547}
548
Brian Haley305d5522008-11-04 17:51:14 -0800549EXPORT_SYMBOL(ndisc_send_skb);
550
551/*
552 * Send a Neighbour Discover packet
553 */
554static void __ndisc_send(struct net_device *dev,
555 struct neighbour *neigh,
556 const struct in6_addr *daddr,
557 const struct in6_addr *saddr,
558 struct icmp6hdr *icmp6h, const struct in6_addr *target,
559 int llinfo)
560{
561 struct sk_buff *skb;
562
563 skb = ndisc_build_skb(dev, daddr, saddr, icmp6h, target, llinfo);
564 if (!skb)
565 return;
566
567 ndisc_send_skb(skb, dev, neigh, daddr, saddr, icmp6h);
568}
569
Linus Torvalds1da177e2005-04-16 15:20:36 -0700570static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +0900571 const struct in6_addr *daddr,
572 const struct in6_addr *solicited_addr,
573 int router, int solicited, int override, int inc_opt)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700574{
575 struct in6_addr tmpaddr;
576 struct inet6_ifaddr *ifp;
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +0900577 const struct in6_addr *src_addr;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900578 struct icmp6hdr icmp6h = {
579 .icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT,
580 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581
582 /* for anycast or proxy, solicited_addr != src_addr */
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900583 ifp = ipv6_get_ifaddr(dev_net(dev), solicited_addr, dev, 1);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900584 if (ifp) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700585 src_addr = solicited_addr;
Neil Horman95c385b2007-04-25 17:08:10 -0700586 if (ifp->flags & IFA_F_OPTIMISTIC)
587 override = 0;
stephen hemminger9f888162010-06-21 11:00:13 +0000588 inc_opt |= ifp->idev->cnf.force_tllao;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700589 in6_ifa_put(ifp);
590 } else {
Brian Haley191cd582008-08-14 15:33:21 -0700591 if (ipv6_dev_get_saddr(dev_net(dev), dev, daddr,
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900592 inet6_sk(dev_net(dev)->ipv6.ndisc_sk)->srcprefs,
YOSHIFUJI Hideaki7cbca672008-03-25 09:37:42 +0900593 &tmpaddr))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700594 return;
595 src_addr = &tmpaddr;
596 }
597
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900598 icmp6h.icmp6_router = router;
599 icmp6h.icmp6_solicited = solicited;
600 icmp6h.icmp6_override = override;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700601
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900602 __ndisc_send(dev, neigh, daddr, src_addr,
603 &icmp6h, solicited_addr,
David L Stevens14878f72007-09-16 16:52:35 -0700604 inc_opt ? ND_OPT_TARGET_LL_ADDR : 0);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900605}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700606
Ben Hutchingsf47b9462011-04-15 13:46:02 +0000607static void ndisc_send_unsol_na(struct net_device *dev)
608{
609 struct inet6_dev *idev;
610 struct inet6_ifaddr *ifa;
611 struct in6_addr mcaddr;
612
613 idev = in6_dev_get(dev);
614 if (!idev)
615 return;
616
617 read_lock_bh(&idev->lock);
618 list_for_each_entry(ifa, &idev->addr_list, if_list) {
619 addrconf_addr_solict_mult(&ifa->addr, &mcaddr);
620 ndisc_send_na(dev, NULL, &mcaddr, &ifa->addr,
621 /*router=*/ !!idev->cnf.forwarding,
622 /*solicited=*/ false, /*override=*/ true,
623 /*inc_opt=*/ true);
624 }
625 read_unlock_bh(&idev->lock);
626
627 in6_dev_put(idev);
628}
629
Linus Torvalds1da177e2005-04-16 15:20:36 -0700630void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +0900631 const struct in6_addr *solicit,
632 const struct in6_addr *daddr, const struct in6_addr *saddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700633{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700634 struct in6_addr addr_buf;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900635 struct icmp6hdr icmp6h = {
636 .icmp6_type = NDISC_NEIGHBOUR_SOLICITATION,
637 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700638
639 if (saddr == NULL) {
Neil Horman95c385b2007-04-25 17:08:10 -0700640 if (ipv6_get_lladdr(dev, &addr_buf,
641 (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700642 return;
643 saddr = &addr_buf;
644 }
645
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900646 __ndisc_send(dev, neigh, daddr, saddr,
647 &icmp6h, solicit,
David L Stevens14878f72007-09-16 16:52:35 -0700648 !ipv6_addr_any(saddr) ? ND_OPT_SOURCE_LL_ADDR : 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700649}
650
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +0900651void ndisc_send_rs(struct net_device *dev, const struct in6_addr *saddr,
652 const struct in6_addr *daddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700653{
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900654 struct icmp6hdr icmp6h = {
655 .icmp6_type = NDISC_ROUTER_SOLICITATION,
656 };
Neil Horman95c385b2007-04-25 17:08:10 -0700657 int send_sllao = dev->addr_len;
Neil Horman95c385b2007-04-25 17:08:10 -0700658
659#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
660 /*
661 * According to section 2.2 of RFC 4429, we must not
662 * send router solicitations with a sllao from
663 * optimistic addresses, but we may send the solicitation
664 * if we don't include the sllao. So here we check
665 * if our address is optimistic, and if so, we
Joe Perchesbea85192007-12-20 14:01:35 -0800666 * suppress the inclusion of the sllao.
Neil Horman95c385b2007-04-25 17:08:10 -0700667 */
668 if (send_sllao) {
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900669 struct inet6_ifaddr *ifp = ipv6_get_ifaddr(dev_net(dev), saddr,
Daniel Lezcano1cab3da2008-01-10 22:44:09 -0800670 dev, 1);
Neil Horman95c385b2007-04-25 17:08:10 -0700671 if (ifp) {
672 if (ifp->flags & IFA_F_OPTIMISTIC) {
YOSHIFUJI Hideakica043562007-02-28 23:13:20 +0900673 send_sllao = 0;
Neil Horman95c385b2007-04-25 17:08:10 -0700674 }
YOSHIFUJI Hideakica043562007-02-28 23:13:20 +0900675 in6_ifa_put(ifp);
Neil Horman95c385b2007-04-25 17:08:10 -0700676 } else {
677 send_sllao = 0;
678 }
679 }
680#endif
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900681 __ndisc_send(dev, NULL, daddr, saddr,
682 &icmp6h, NULL,
David L Stevens14878f72007-09-16 16:52:35 -0700683 send_sllao ? ND_OPT_SOURCE_LL_ADDR : 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700684}
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900685
Linus Torvalds1da177e2005-04-16 15:20:36 -0700686
687static void ndisc_error_report(struct neighbour *neigh, struct sk_buff *skb)
688{
689 /*
690 * "The sender MUST return an ICMP
691 * destination unreachable"
692 */
693 dst_link_failure(skb);
694 kfree_skb(skb);
695}
696
697/* Called with locked neigh: either read or both */
698
699static void ndisc_solicit(struct neighbour *neigh, struct sk_buff *skb)
700{
701 struct in6_addr *saddr = NULL;
702 struct in6_addr mcaddr;
703 struct net_device *dev = neigh->dev;
704 struct in6_addr *target = (struct in6_addr *)&neigh->primary_key;
705 int probes = atomic_read(&neigh->probes);
706
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900707 if (skb && ipv6_chk_addr(dev_net(dev), &ipv6_hdr(skb)->saddr, dev, 1))
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -0700708 saddr = &ipv6_hdr(skb)->saddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700709
710 if ((probes -= neigh->parms->ucast_probes) < 0) {
711 if (!(neigh->nud_state & NUD_VALID)) {
Harvey Harrison5b095d9892008-10-29 12:52:50 -0700712 ND_PRINTK1(KERN_DEBUG "%s(): trying to ucast probe in NUD_INVALID: %pI6\n",
Harvey Harrison0c6ce782008-10-28 16:09:23 -0700713 __func__, target);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700714 }
715 ndisc_send_ns(dev, neigh, target, target, saddr);
716 } else if ((probes -= neigh->parms->app_probes) < 0) {
717#ifdef CONFIG_ARPD
718 neigh_app_ns(neigh);
719#endif
720 } else {
721 addrconf_addr_solict_mult(target, &mcaddr);
722 ndisc_send_ns(dev, NULL, target, &mcaddr, saddr);
723 }
724}
725
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900726static int pndisc_is_router(const void *pkey,
727 struct net_device *dev)
Pavel Emelyanovfa86d322008-03-24 14:48:59 -0700728{
729 struct pneigh_entry *n;
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900730 int ret = -1;
Pavel Emelyanovfa86d322008-03-24 14:48:59 -0700731
732 read_lock_bh(&nd_tbl.lock);
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900733 n = __pneigh_lookup(&nd_tbl, dev_net(dev), pkey, dev);
734 if (n)
735 ret = !!(n->flags & NTF_ROUTER);
Pavel Emelyanovfa86d322008-03-24 14:48:59 -0700736 read_unlock_bh(&nd_tbl.lock);
737
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900738 return ret;
Pavel Emelyanovfa86d322008-03-24 14:48:59 -0700739}
740
Linus Torvalds1da177e2005-04-16 15:20:36 -0700741static void ndisc_recv_ns(struct sk_buff *skb)
742{
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -0700743 struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb);
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000744 const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
745 const struct in6_addr *daddr = &ipv6_hdr(skb)->daddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700746 u8 *lladdr = NULL;
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -0700747 u32 ndoptlen = skb->tail - (skb->transport_header +
748 offsetof(struct nd_msg, opt));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700749 struct ndisc_options ndopts;
750 struct net_device *dev = skb->dev;
751 struct inet6_ifaddr *ifp;
752 struct inet6_dev *idev = NULL;
753 struct neighbour *neigh;
754 int dad = ipv6_addr_any(saddr);
755 int inc;
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900756 int is_router = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700757
758 if (ipv6_addr_is_multicast(&msg->target)) {
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900759 ND_PRINTK2(KERN_WARNING
Linus Torvalds1da177e2005-04-16 15:20:36 -0700760 "ICMPv6 NS: multicast target address");
761 return;
762 }
763
764 /*
765 * RFC2461 7.1.1:
766 * DAD has to be destined for solicited node multicast address.
767 */
768 if (dad &&
769 !(daddr->s6_addr32[0] == htonl(0xff020000) &&
770 daddr->s6_addr32[1] == htonl(0x00000000) &&
771 daddr->s6_addr32[2] == htonl(0x00000001) &&
772 daddr->s6_addr [12] == 0xff )) {
773 ND_PRINTK2(KERN_WARNING
774 "ICMPv6 NS: bad DAD packet (wrong destination)\n");
775 return;
776 }
777
778 if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts)) {
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900779 ND_PRINTK2(KERN_WARNING
Linus Torvalds1da177e2005-04-16 15:20:36 -0700780 "ICMPv6 NS: invalid ND options\n");
781 return;
782 }
783
784 if (ndopts.nd_opts_src_lladdr) {
785 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr, dev);
786 if (!lladdr) {
787 ND_PRINTK2(KERN_WARNING
788 "ICMPv6 NS: invalid link-layer address length\n");
789 return;
790 }
791
792 /* RFC2461 7.1.1:
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900793 * If the IP source address is the unspecified address,
794 * there MUST NOT be source link-layer address option
Linus Torvalds1da177e2005-04-16 15:20:36 -0700795 * in the message.
796 */
797 if (dad) {
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900798 ND_PRINTK2(KERN_WARNING
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799 "ICMPv6 NS: bad DAD packet (link-layer address option)\n");
800 return;
801 }
802 }
803
804 inc = ipv6_addr_is_multicast(daddr);
805
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900806 ifp = ipv6_get_ifaddr(dev_net(dev), &msg->target, dev, 1);
Daniel Lezcanoa18bc692008-03-07 11:14:49 -0800807 if (ifp) {
Neil Horman95c385b2007-04-25 17:08:10 -0700808
809 if (ifp->flags & (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)) {
810 if (dad) {
811 if (dev->type == ARPHRD_IEEE802_TR) {
Arnaldo Carvalho de Melo98e399f2007-03-19 15:33:04 -0700812 const unsigned char *sadr;
813 sadr = skb_mac_header(skb);
Neil Horman95c385b2007-04-25 17:08:10 -0700814 if (((sadr[8] ^ dev->dev_addr[0]) & 0x7f) == 0 &&
815 sadr[9] == dev->dev_addr[1] &&
816 sadr[10] == dev->dev_addr[2] &&
817 sadr[11] == dev->dev_addr[3] &&
818 sadr[12] == dev->dev_addr[4] &&
819 sadr[13] == dev->dev_addr[5]) {
820 /* looped-back to us */
821 goto out;
822 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700823 }
Neil Horman95c385b2007-04-25 17:08:10 -0700824
825 /*
826 * We are colliding with another node
827 * who is doing DAD
828 * so fail our DAD process
829 */
830 addrconf_dad_failure(ifp);
Denis V. Lunev9e3be4b2007-09-11 11:04:49 +0200831 return;
Neil Horman95c385b2007-04-25 17:08:10 -0700832 } else {
833 /*
834 * This is not a dad solicitation.
835 * If we are an optimistic node,
836 * we should respond.
837 * Otherwise, we should ignore it.
838 */
839 if (!(ifp->flags & IFA_F_OPTIMISTIC))
840 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700841 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700842 }
843
844 idev = ifp->idev;
845 } else {
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700846 struct net *net = dev_net(dev);
847
Linus Torvalds1da177e2005-04-16 15:20:36 -0700848 idev = in6_dev_get(dev);
849 if (!idev) {
850 /* XXX: count this drop? */
851 return;
852 }
853
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700854 if (ipv6_chk_acast_addr(net, dev, &msg->target) ||
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900855 (idev->cnf.forwarding &&
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700856 (net->ipv6.devconf_all->proxy_ndp || idev->cnf.proxy_ndp) &&
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900857 (is_router = pndisc_is_router(&msg->target, dev)) >= 0)) {
Patrick McHardya61bbcf2005-08-14 17:24:31 -0700858 if (!(NEIGH_CB(skb)->flags & LOCALLY_ENQUEUED) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700859 skb->pkt_type != PACKET_HOST &&
860 inc != 0 &&
861 idev->nd_parms->proxy_delay != 0) {
862 /*
863 * for anycast or proxy,
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900864 * sender should delay its response
865 * by a random time between 0 and
Linus Torvalds1da177e2005-04-16 15:20:36 -0700866 * MAX_ANYCAST_DELAY_TIME seconds.
867 * (RFC2461) -- yoshfuji
868 */
869 struct sk_buff *n = skb_clone(skb, GFP_ATOMIC);
870 if (n)
871 pneigh_enqueue(&nd_tbl, idev->nd_parms, n);
872 goto out;
873 }
874 } else
875 goto out;
876 }
877
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900878 if (is_router < 0)
879 is_router = !!idev->cnf.forwarding;
Ville Nuorvala62dd9312006-09-22 14:43:19 -0700880
Linus Torvalds1da177e2005-04-16 15:20:36 -0700881 if (dad) {
YOSHIFUJI Hideakif3ee4012008-04-10 15:42:11 +0900882 ndisc_send_na(dev, NULL, &in6addr_linklocal_allnodes, &msg->target,
Ville Nuorvala62dd9312006-09-22 14:43:19 -0700883 is_router, 0, (ifp != NULL), 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700884 goto out;
885 }
886
887 if (inc)
888 NEIGH_CACHE_STAT_INC(&nd_tbl, rcv_probes_mcast);
889 else
890 NEIGH_CACHE_STAT_INC(&nd_tbl, rcv_probes_ucast);
891
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900892 /*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700893 * update / create cache entry
894 * for the source address
895 */
896 neigh = __neigh_lookup(&nd_tbl, saddr, dev,
897 !inc || lladdr || !dev->addr_len);
898 if (neigh)
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900899 neigh_update(neigh, lladdr, NUD_STALE,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700900 NEIGH_UPDATE_F_WEAK_OVERRIDE|
901 NEIGH_UPDATE_F_OVERRIDE);
Stephen Hemminger3b04ddd2007-10-09 01:40:57 -0700902 if (neigh || !dev->header_ops) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700903 ndisc_send_na(dev, neigh, saddr, &msg->target,
Ville Nuorvala62dd9312006-09-22 14:43:19 -0700904 is_router,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700905 1, (ifp != NULL && inc), inc);
906 if (neigh)
907 neigh_release(neigh);
908 }
909
910out:
911 if (ifp)
912 in6_ifa_put(ifp);
913 else
914 in6_dev_put(idev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700915}
916
917static void ndisc_recv_na(struct sk_buff *skb)
918{
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -0700919 struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb);
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000920 const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
921 const struct in6_addr *daddr = &ipv6_hdr(skb)->daddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700922 u8 *lladdr = NULL;
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -0700923 u32 ndoptlen = skb->tail - (skb->transport_header +
924 offsetof(struct nd_msg, opt));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700925 struct ndisc_options ndopts;
926 struct net_device *dev = skb->dev;
927 struct inet6_ifaddr *ifp;
928 struct neighbour *neigh;
929
930 if (skb->len < sizeof(struct nd_msg)) {
931 ND_PRINTK2(KERN_WARNING
932 "ICMPv6 NA: packet too short\n");
933 return;
934 }
935
936 if (ipv6_addr_is_multicast(&msg->target)) {
937 ND_PRINTK2(KERN_WARNING
938 "ICMPv6 NA: target address is multicast.\n");
939 return;
940 }
941
942 if (ipv6_addr_is_multicast(daddr) &&
943 msg->icmph.icmp6_solicited) {
944 ND_PRINTK2(KERN_WARNING
945 "ICMPv6 NA: solicited NA is multicasted.\n");
946 return;
947 }
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900948
Linus Torvalds1da177e2005-04-16 15:20:36 -0700949 if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts)) {
950 ND_PRINTK2(KERN_WARNING
951 "ICMPv6 NS: invalid ND option\n");
952 return;
953 }
954 if (ndopts.nd_opts_tgt_lladdr) {
955 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr, dev);
956 if (!lladdr) {
957 ND_PRINTK2(KERN_WARNING
958 "ICMPv6 NA: invalid link-layer address length\n");
959 return;
960 }
961 }
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900962 ifp = ipv6_get_ifaddr(dev_net(dev), &msg->target, dev, 1);
Daniel Lezcanoa18bc692008-03-07 11:14:49 -0800963 if (ifp) {
Daniel Walterbd015922011-04-13 21:09:25 +0000964 if (skb->pkt_type != PACKET_LOOPBACK
965 && (ifp->flags & IFA_F_TENTATIVE)) {
966 addrconf_dad_failure(ifp);
967 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700968 }
969 /* What should we make now? The advertisement
970 is invalid, but ndisc specs say nothing
971 about it. It could be misconfiguration, or
972 an smart proxy agent tries to help us :-)
Jan Sembera24fc7b82008-12-09 15:48:32 -0800973
974 We should not print the error if NA has been
975 received from loopback - it is just our own
976 unsolicited advertisement.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700977 */
Jan Sembera24fc7b82008-12-09 15:48:32 -0800978 if (skb->pkt_type != PACKET_LOOPBACK)
979 ND_PRINTK1(KERN_WARNING
Jens Rosenbooma6fa3282009-08-12 22:16:04 +0000980 "ICMPv6 NA: someone advertises our address %pI6 on %s!\n",
981 &ifp->addr, ifp->idev->dev->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700982 in6_ifa_put(ifp);
983 return;
984 }
985 neigh = neigh_lookup(&nd_tbl, &msg->target, dev);
986
987 if (neigh) {
988 u8 old_flags = neigh->flags;
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700989 struct net *net = dev_net(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700990
991 if (neigh->nud_state & NUD_FAILED)
992 goto out;
993
Ville Nuorvala5f3e6e92006-09-22 14:42:46 -0700994 /*
995 * Don't update the neighbor cache entry on a proxy NA from
996 * ourselves because either the proxied node is off link or it
997 * has already sent a NA to us.
998 */
999 if (lladdr && !memcmp(lladdr, dev->dev_addr, dev->addr_len) &&
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -07001000 net->ipv6.devconf_all->forwarding && net->ipv6.devconf_all->proxy_ndp &&
1001 pneigh_lookup(&nd_tbl, net, &msg->target, dev, 0)) {
YOSHIFUJI Hideakifbea49e2006-09-22 14:43:49 -07001002 /* XXX: idev->cnf.prixy_ndp */
Ville Nuorvala5f3e6e92006-09-22 14:42:46 -07001003 goto out;
YOSHIFUJI Hideakifbea49e2006-09-22 14:43:49 -07001004 }
Ville Nuorvala5f3e6e92006-09-22 14:42:46 -07001005
Linus Torvalds1da177e2005-04-16 15:20:36 -07001006 neigh_update(neigh, lladdr,
1007 msg->icmph.icmp6_solicited ? NUD_REACHABLE : NUD_STALE,
1008 NEIGH_UPDATE_F_WEAK_OVERRIDE|
1009 (msg->icmph.icmp6_override ? NEIGH_UPDATE_F_OVERRIDE : 0)|
1010 NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
1011 (msg->icmph.icmp6_router ? NEIGH_UPDATE_F_ISROUTER : 0));
1012
1013 if ((old_flags & ~neigh->flags) & NTF_ROUTER) {
1014 /*
1015 * Change: router to host
1016 */
1017 struct rt6_info *rt;
1018 rt = rt6_get_dflt_router(saddr, dev);
1019 if (rt)
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001020 ip6_del_rt(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001021 }
1022
1023out:
1024 neigh_release(neigh);
1025 }
1026}
1027
1028static void ndisc_recv_rs(struct sk_buff *skb)
1029{
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -07001030 struct rs_msg *rs_msg = (struct rs_msg *)skb_transport_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001031 unsigned long ndoptlen = skb->len - sizeof(*rs_msg);
1032 struct neighbour *neigh;
1033 struct inet6_dev *idev;
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001034 const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001035 struct ndisc_options ndopts;
1036 u8 *lladdr = NULL;
1037
1038 if (skb->len < sizeof(*rs_msg))
1039 return;
1040
Eric Dumazetcfdf7642011-07-27 21:13:03 +00001041 idev = __in6_dev_get(skb->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001042 if (!idev) {
1043 if (net_ratelimit())
1044 ND_PRINTK1("ICMP6 RS: can't find in6 device\n");
1045 return;
1046 }
1047
1048 /* Don't accept RS if we're not in router mode */
1049 if (!idev->cnf.forwarding)
1050 goto out;
1051
1052 /*
1053 * Don't update NCE if src = ::;
1054 * this implies that the source node has no ip address assigned yet.
1055 */
1056 if (ipv6_addr_any(saddr))
1057 goto out;
1058
1059 /* Parse ND options */
1060 if (!ndisc_parse_options(rs_msg->opt, ndoptlen, &ndopts)) {
1061 if (net_ratelimit())
1062 ND_PRINTK2("ICMP6 NS: invalid ND option, ignored\n");
1063 goto out;
1064 }
1065
1066 if (ndopts.nd_opts_src_lladdr) {
1067 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr,
1068 skb->dev);
1069 if (!lladdr)
1070 goto out;
1071 }
1072
1073 neigh = __neigh_lookup(&nd_tbl, saddr, skb->dev, 1);
1074 if (neigh) {
1075 neigh_update(neigh, lladdr, NUD_STALE,
1076 NEIGH_UPDATE_F_WEAK_OVERRIDE|
1077 NEIGH_UPDATE_F_OVERRIDE|
1078 NEIGH_UPDATE_F_OVERRIDE_ISROUTER);
1079 neigh_release(neigh);
1080 }
1081out:
Eric Dumazetcfdf7642011-07-27 21:13:03 +00001082 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001083}
1084
Pierre Ynard31910572007-10-10 21:22:05 -07001085static void ndisc_ra_useropt(struct sk_buff *ra, struct nd_opt_hdr *opt)
1086{
1087 struct icmp6hdr *icmp6h = (struct icmp6hdr *)skb_transport_header(ra);
1088 struct sk_buff *skb;
1089 struct nlmsghdr *nlh;
1090 struct nduseroptmsg *ndmsg;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001091 struct net *net = dev_net(ra->dev);
Pierre Ynard31910572007-10-10 21:22:05 -07001092 int err;
1093 int base_size = NLMSG_ALIGN(sizeof(struct nduseroptmsg)
1094 + (opt->nd_opt_len << 3));
1095 size_t msg_size = base_size + nla_total_size(sizeof(struct in6_addr));
1096
1097 skb = nlmsg_new(msg_size, GFP_ATOMIC);
1098 if (skb == NULL) {
1099 err = -ENOBUFS;
1100 goto errout;
1101 }
1102
1103 nlh = nlmsg_put(skb, 0, 0, RTM_NEWNDUSEROPT, base_size, 0);
1104 if (nlh == NULL) {
1105 goto nla_put_failure;
1106 }
1107
1108 ndmsg = nlmsg_data(nlh);
1109 ndmsg->nduseropt_family = AF_INET6;
Pierre Ynarddbb2ed22007-11-12 17:58:35 -08001110 ndmsg->nduseropt_ifindex = ra->dev->ifindex;
Pierre Ynard31910572007-10-10 21:22:05 -07001111 ndmsg->nduseropt_icmp_type = icmp6h->icmp6_type;
1112 ndmsg->nduseropt_icmp_code = icmp6h->icmp6_code;
1113 ndmsg->nduseropt_opts_len = opt->nd_opt_len << 3;
1114
1115 memcpy(ndmsg + 1, opt, opt->nd_opt_len << 3);
1116
1117 NLA_PUT(skb, NDUSEROPT_SRCADDR, sizeof(struct in6_addr),
1118 &ipv6_hdr(ra)->saddr);
1119 nlmsg_end(skb, nlh);
1120
Pablo Neira Ayuso1ce85fe2009-02-24 23:18:28 -08001121 rtnl_notify(skb, net, 0, RTNLGRP_ND_USEROPT, NULL, GFP_ATOMIC);
Pierre Ynard31910572007-10-10 21:22:05 -07001122 return;
1123
1124nla_put_failure:
1125 nlmsg_free(skb);
1126 err = -EMSGSIZE;
1127errout:
Daniel Lezcanoa18bc692008-03-07 11:14:49 -08001128 rtnl_set_sk_err(net, RTNLGRP_ND_USEROPT, err);
Pierre Ynard31910572007-10-10 21:22:05 -07001129}
1130
Thomas Graf65e9b622010-09-03 02:59:14 +00001131static inline int accept_ra(struct inet6_dev *in6_dev)
1132{
1133 /*
1134 * If forwarding is enabled, RA are not accepted unless the special
1135 * hybrid mode (accept_ra=2) is enabled.
1136 */
1137 if (in6_dev->cnf.forwarding && in6_dev->cnf.accept_ra < 2)
1138 return 0;
1139
1140 return in6_dev->cnf.accept_ra;
1141}
1142
Linus Torvalds1da177e2005-04-16 15:20:36 -07001143static void ndisc_router_discovery(struct sk_buff *skb)
1144{
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -07001145 struct ra_msg *ra_msg = (struct ra_msg *)skb_transport_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001146 struct neighbour *neigh = NULL;
1147 struct inet6_dev *in6_dev;
YOSHIFUJI Hideaki65f5c7c2006-03-20 16:55:08 -08001148 struct rt6_info *rt = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001149 int lifetime;
1150 struct ndisc_options ndopts;
1151 int optlen;
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001152 unsigned int pref = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001153
1154 __u8 * opt = (__u8 *)(ra_msg + 1);
1155
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -07001156 optlen = (skb->tail - skb->transport_header) - sizeof(struct ra_msg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001157
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001158 if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001159 ND_PRINTK2(KERN_WARNING
1160 "ICMPv6 RA: source address is not link-local.\n");
1161 return;
1162 }
1163 if (optlen < 0) {
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001164 ND_PRINTK2(KERN_WARNING
Linus Torvalds1da177e2005-04-16 15:20:36 -07001165 "ICMPv6 RA: packet too short\n");
1166 return;
1167 }
1168
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001169#ifdef CONFIG_IPV6_NDISC_NODETYPE
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001170 if (skb->ndisc_nodetype == NDISC_NODETYPE_HOST) {
1171 ND_PRINTK2(KERN_WARNING
1172 "ICMPv6 RA: from host or unauthorized router\n");
1173 return;
1174 }
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001175#endif
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001176
Linus Torvalds1da177e2005-04-16 15:20:36 -07001177 /*
1178 * set the RA_RECV flag in the interface
1179 */
1180
Eric Dumazetcfdf7642011-07-27 21:13:03 +00001181 in6_dev = __in6_dev_get(skb->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001182 if (in6_dev == NULL) {
1183 ND_PRINTK0(KERN_ERR
1184 "ICMPv6 RA: can't find inet6 device for %s.\n",
1185 skb->dev->name);
1186 return;
1187 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001188
1189 if (!ndisc_parse_options(opt, optlen, &ndopts)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001190 ND_PRINTK2(KERN_WARNING
1191 "ICMP6 RA: invalid ND options\n");
1192 return;
1193 }
1194
Thomas Graf65e9b622010-09-03 02:59:14 +00001195 if (!accept_ra(in6_dev))
David Ward31ce8c72009-08-29 00:04:09 -07001196 goto skip_linkparms;
1197
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001198#ifdef CONFIG_IPV6_NDISC_NODETYPE
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001199 /* skip link-specific parameters from interior routers */
1200 if (skb->ndisc_nodetype == NDISC_NODETYPE_NODEFAULT)
1201 goto skip_linkparms;
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001202#endif
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001203
Linus Torvalds1da177e2005-04-16 15:20:36 -07001204 if (in6_dev->if_flags & IF_RS_SENT) {
1205 /*
1206 * flag that an RA was received after an RS was sent
1207 * out on this interface.
1208 */
1209 in6_dev->if_flags |= IF_RA_RCVD;
1210 }
1211
1212 /*
1213 * Remember the managed/otherconf flags from most recently
1214 * received RA message (RFC 2462) -- yoshfuji
1215 */
1216 in6_dev->if_flags = (in6_dev->if_flags & ~(IF_RA_MANAGED |
1217 IF_RA_OTHERCONF)) |
1218 (ra_msg->icmph.icmp6_addrconf_managed ?
1219 IF_RA_MANAGED : 0) |
1220 (ra_msg->icmph.icmp6_addrconf_other ?
1221 IF_RA_OTHERCONF : 0);
1222
YOSHIFUJI Hideaki65f5c7c2006-03-20 16:55:08 -08001223 if (!in6_dev->cnf.accept_ra_defrtr)
1224 goto skip_defrtr;
1225
Andreas Hofmeister9f562202011-10-24 19:13:15 -04001226 if (ipv6_chk_addr(dev_net(in6_dev->dev), &ipv6_hdr(skb)->saddr, NULL, 0))
1227 goto skip_defrtr;
1228
Linus Torvalds1da177e2005-04-16 15:20:36 -07001229 lifetime = ntohs(ra_msg->icmph.icmp6_rt_lifetime);
1230
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001231#ifdef CONFIG_IPV6_ROUTER_PREF
1232 pref = ra_msg->icmph.icmp6_router_pref;
1233 /* 10b is handled as if it were 00b (medium) */
YOSHIFUJI Hideaki930d6ff2006-03-20 17:05:30 -08001234 if (pref == ICMPV6_ROUTER_PREF_INVALID ||
YOSHIFUJI Hideaki6d5b78c2007-06-22 16:07:04 -07001235 !in6_dev->cnf.accept_ra_rtr_pref)
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001236 pref = ICMPV6_ROUTER_PREF_MEDIUM;
1237#endif
1238
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001239 rt = rt6_get_dflt_router(&ipv6_hdr(skb)->saddr, skb->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001240
1241 if (rt)
David S. Miller69cce1d2011-07-17 23:09:49 -07001242 neigh = dst_get_neighbour(&rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001243
1244 if (rt && lifetime == 0) {
1245 neigh_clone(neigh);
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001246 ip6_del_rt(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001247 rt = NULL;
1248 }
1249
1250 if (rt == NULL && lifetime) {
1251 ND_PRINTK3(KERN_DEBUG
1252 "ICMPv6 RA: adding default router.\n");
1253
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001254 rt = rt6_add_dflt_router(&ipv6_hdr(skb)->saddr, skb->dev, pref);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001255 if (rt == NULL) {
1256 ND_PRINTK0(KERN_ERR
1257 "ICMPv6 RA: %s() failed to add default route.\n",
Harvey Harrison0dc47872008-03-05 20:47:47 -08001258 __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001259 return;
1260 }
1261
David S. Miller69cce1d2011-07-17 23:09:49 -07001262 neigh = dst_get_neighbour(&rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001263 if (neigh == NULL) {
1264 ND_PRINTK0(KERN_ERR
1265 "ICMPv6 RA: %s() got default router without neighbour.\n",
Harvey Harrison0dc47872008-03-05 20:47:47 -08001266 __func__);
Changli Gaod8d1f302010-06-10 23:31:35 -07001267 dst_release(&rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001268 return;
1269 }
1270 neigh->flags |= NTF_ROUTER;
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001271 } else if (rt) {
Pedro Ribeiro22441cf2008-10-15 15:47:49 -07001272 rt->rt6i_flags = (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001273 }
1274
1275 if (rt)
1276 rt->rt6i_expires = jiffies + (HZ * lifetime);
1277
1278 if (ra_msg->icmph.icmp6_hop_limit) {
1279 in6_dev->cnf.hop_limit = ra_msg->icmph.icmp6_hop_limit;
1280 if (rt)
David S. Millerdefb3512010-12-08 21:16:57 -08001281 dst_metric_set(&rt->dst, RTAX_HOPLIMIT,
1282 ra_msg->icmph.icmp6_hop_limit);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001283 }
1284
YOSHIFUJI Hideaki65f5c7c2006-03-20 16:55:08 -08001285skip_defrtr:
1286
Linus Torvalds1da177e2005-04-16 15:20:36 -07001287 /*
1288 * Update Reachable Time and Retrans Timer
1289 */
1290
1291 if (in6_dev->nd_parms) {
1292 unsigned long rtime = ntohl(ra_msg->retrans_timer);
1293
1294 if (rtime && rtime/1000 < MAX_SCHEDULE_TIMEOUT/HZ) {
1295 rtime = (rtime*HZ)/1000;
1296 if (rtime < HZ/10)
1297 rtime = HZ/10;
1298 in6_dev->nd_parms->retrans_time = rtime;
1299 in6_dev->tstamp = jiffies;
1300 inet6_ifinfo_notify(RTM_NEWLINK, in6_dev);
1301 }
1302
1303 rtime = ntohl(ra_msg->reachable_time);
1304 if (rtime && rtime/1000 < MAX_SCHEDULE_TIMEOUT/(3*HZ)) {
1305 rtime = (rtime*HZ)/1000;
1306
1307 if (rtime < HZ/10)
1308 rtime = HZ/10;
1309
1310 if (rtime != in6_dev->nd_parms->base_reachable_time) {
1311 in6_dev->nd_parms->base_reachable_time = rtime;
1312 in6_dev->nd_parms->gc_staletime = 3 * rtime;
1313 in6_dev->nd_parms->reachable_time = neigh_rand_reach_time(rtime);
1314 in6_dev->tstamp = jiffies;
1315 inet6_ifinfo_notify(RTM_NEWLINK, in6_dev);
1316 }
1317 }
1318 }
1319
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001320skip_linkparms:
1321
Linus Torvalds1da177e2005-04-16 15:20:36 -07001322 /*
1323 * Process options.
1324 */
1325
1326 if (!neigh)
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001327 neigh = __neigh_lookup(&nd_tbl, &ipv6_hdr(skb)->saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001328 skb->dev, 1);
1329 if (neigh) {
1330 u8 *lladdr = NULL;
1331 if (ndopts.nd_opts_src_lladdr) {
1332 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr,
1333 skb->dev);
1334 if (!lladdr) {
1335 ND_PRINTK2(KERN_WARNING
1336 "ICMPv6 RA: invalid link-layer address length\n");
1337 goto out;
1338 }
1339 }
1340 neigh_update(neigh, lladdr, NUD_STALE,
1341 NEIGH_UPDATE_F_WEAK_OVERRIDE|
1342 NEIGH_UPDATE_F_OVERRIDE|
1343 NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
1344 NEIGH_UPDATE_F_ISROUTER);
1345 }
1346
Thomas Graf65e9b622010-09-03 02:59:14 +00001347 if (!accept_ra(in6_dev))
David Ward31ce8c72009-08-29 00:04:09 -07001348 goto out;
1349
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001350#ifdef CONFIG_IPV6_ROUTE_INFO
Andreas Hofmeister9f562202011-10-24 19:13:15 -04001351 if (ipv6_chk_addr(dev_net(in6_dev->dev), &ipv6_hdr(skb)->saddr, NULL, 0))
1352 goto skip_routeinfo;
1353
YOSHIFUJI Hideaki09c884d2006-03-20 17:07:03 -08001354 if (in6_dev->cnf.accept_ra_rtr_pref && ndopts.nd_opts_ri) {
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001355 struct nd_opt_hdr *p;
1356 for (p = ndopts.nd_opts_ri;
1357 p;
1358 p = ndisc_next_option(p, ndopts.nd_opts_ri_end)) {
YOSHIFUJI Hideaki6294e002008-03-15 23:56:52 -04001359 struct route_info *ri = (struct route_info *)p;
1360#ifdef CONFIG_IPV6_NDISC_NODETYPE
1361 if (skb->ndisc_nodetype == NDISC_NODETYPE_NODEFAULT &&
1362 ri->prefix_len == 0)
1363 continue;
1364#endif
1365 if (ri->prefix_len > in6_dev->cnf.accept_ra_rt_info_max_plen)
YOSHIFUJI Hideaki09c884d2006-03-20 17:07:03 -08001366 continue;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001367 rt6_route_rcv(skb->dev, (u8*)p, (p->nd_opt_len) << 3,
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001368 &ipv6_hdr(skb)->saddr);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001369 }
1370 }
Andreas Hofmeister9f562202011-10-24 19:13:15 -04001371
1372skip_routeinfo:
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001373#endif
1374
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001375#ifdef CONFIG_IPV6_NDISC_NODETYPE
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001376 /* skip link-specific ndopts from interior routers */
1377 if (skb->ndisc_nodetype == NDISC_NODETYPE_NODEFAULT)
1378 goto out;
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001379#endif
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001380
YOSHIFUJI Hideakic4fd30e2006-03-20 16:55:26 -08001381 if (in6_dev->cnf.accept_ra_pinfo && ndopts.nd_opts_pi) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001382 struct nd_opt_hdr *p;
1383 for (p = ndopts.nd_opts_pi;
1384 p;
1385 p = ndisc_next_option(p, ndopts.nd_opts_pi_end)) {
1386 addrconf_prefix_rcv(skb->dev, (u8*)p, (p->nd_opt_len) << 3);
1387 }
1388 }
1389
1390 if (ndopts.nd_opts_mtu) {
Al Viroe69a4ad2006-11-14 20:56:00 -08001391 __be32 n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001392 u32 mtu;
1393
Al Viroe69a4ad2006-11-14 20:56:00 -08001394 memcpy(&n, ((u8*)(ndopts.nd_opts_mtu+1))+2, sizeof(mtu));
1395 mtu = ntohl(n);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001396
1397 if (mtu < IPV6_MIN_MTU || mtu > skb->dev->mtu) {
1398 ND_PRINTK2(KERN_WARNING
1399 "ICMPv6 RA: invalid mtu: %d\n",
1400 mtu);
1401 } else if (in6_dev->cnf.mtu6 != mtu) {
1402 in6_dev->cnf.mtu6 = mtu;
1403
1404 if (rt)
David S. Millerdefb3512010-12-08 21:16:57 -08001405 dst_metric_set(&rt->dst, RTAX_MTU, mtu);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001406
1407 rt6_mtu_change(skb->dev, mtu);
1408 }
1409 }
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001410
Pierre Ynard31910572007-10-10 21:22:05 -07001411 if (ndopts.nd_useropts) {
YOSHIFUJI Hideaki61cf46a2008-01-22 17:32:53 +09001412 struct nd_opt_hdr *p;
1413 for (p = ndopts.nd_useropts;
1414 p;
1415 p = ndisc_next_useropt(p, ndopts.nd_useropts_end)) {
1416 ndisc_ra_useropt(skb, p);
Pierre Ynard31910572007-10-10 21:22:05 -07001417 }
1418 }
1419
Linus Torvalds1da177e2005-04-16 15:20:36 -07001420 if (ndopts.nd_opts_tgt_lladdr || ndopts.nd_opts_rh) {
1421 ND_PRINTK2(KERN_WARNING
1422 "ICMPv6 RA: invalid RA options");
1423 }
1424out:
1425 if (rt)
Changli Gaod8d1f302010-06-10 23:31:35 -07001426 dst_release(&rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001427 else if (neigh)
1428 neigh_release(neigh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001429}
1430
1431static void ndisc_redirect_rcv(struct sk_buff *skb)
1432{
1433 struct inet6_dev *in6_dev;
1434 struct icmp6hdr *icmph;
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001435 const struct in6_addr *dest;
1436 const struct in6_addr *target; /* new first hop to destination */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001437 struct neighbour *neigh;
1438 int on_link = 0;
1439 struct ndisc_options ndopts;
1440 int optlen;
1441 u8 *lladdr = NULL;
1442
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001443#ifdef CONFIG_IPV6_NDISC_NODETYPE
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001444 switch (skb->ndisc_nodetype) {
1445 case NDISC_NODETYPE_HOST:
1446 case NDISC_NODETYPE_NODEFAULT:
1447 ND_PRINTK2(KERN_WARNING
1448 "ICMPv6 Redirect: from host or unauthorized router\n");
1449 return;
1450 }
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001451#endif
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001452
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001453 if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001454 ND_PRINTK2(KERN_WARNING
1455 "ICMPv6 Redirect: source address is not link-local.\n");
1456 return;
1457 }
1458
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -07001459 optlen = skb->tail - skb->transport_header;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001460 optlen -= sizeof(struct icmp6hdr) + 2 * sizeof(struct in6_addr);
1461
1462 if (optlen < 0) {
1463 ND_PRINTK2(KERN_WARNING
1464 "ICMPv6 Redirect: packet too short\n");
1465 return;
1466 }
1467
Arnaldo Carvalho de Melocc70ab22007-03-13 14:03:22 -03001468 icmph = icmp6_hdr(skb);
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001469 target = (const struct in6_addr *) (icmph + 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001470 dest = target + 1;
1471
1472 if (ipv6_addr_is_multicast(dest)) {
1473 ND_PRINTK2(KERN_WARNING
1474 "ICMPv6 Redirect: destination address is multicast.\n");
1475 return;
1476 }
1477
1478 if (ipv6_addr_equal(dest, target)) {
1479 on_link = 1;
Brian Haleybf0b48d2007-10-08 00:12:05 -07001480 } else if (ipv6_addr_type(target) !=
1481 (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) {
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001482 ND_PRINTK2(KERN_WARNING
Brian Haleybf0b48d2007-10-08 00:12:05 -07001483 "ICMPv6 Redirect: target address is not link-local unicast.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001484 return;
1485 }
1486
Eric Dumazetcfdf7642011-07-27 21:13:03 +00001487 in6_dev = __in6_dev_get(skb->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001488 if (!in6_dev)
1489 return;
Eric Dumazetcfdf7642011-07-27 21:13:03 +00001490 if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001491 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001492
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001493 /* RFC2461 8.1:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001494 * The IP source address of the Redirect MUST be the same as the current
1495 * first-hop router for the specified ICMP Destination Address.
1496 */
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001497
Linus Torvalds1da177e2005-04-16 15:20:36 -07001498 if (!ndisc_parse_options((u8*)(dest + 1), optlen, &ndopts)) {
1499 ND_PRINTK2(KERN_WARNING
1500 "ICMPv6 Redirect: invalid ND options\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001501 return;
1502 }
1503 if (ndopts.nd_opts_tgt_lladdr) {
1504 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr,
1505 skb->dev);
1506 if (!lladdr) {
1507 ND_PRINTK2(KERN_WARNING
1508 "ICMPv6 Redirect: invalid link-layer address length\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001509 return;
1510 }
1511 }
1512
1513 neigh = __neigh_lookup(&nd_tbl, target, skb->dev, 1);
1514 if (neigh) {
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001515 rt6_redirect(dest, &ipv6_hdr(skb)->daddr,
1516 &ipv6_hdr(skb)->saddr, neigh, lladdr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001517 on_link);
1518 neigh_release(neigh);
1519 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001520}
1521
1522void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +09001523 const struct in6_addr *target)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001524{
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001525 struct net_device *dev = skb->dev;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001526 struct net *net = dev_net(dev);
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001527 struct sock *sk = net->ipv6.ndisc_sk;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001528 int len = sizeof(struct icmp6hdr) + 2 * sizeof(struct in6_addr);
1529 struct sk_buff *buff;
1530 struct icmp6hdr *icmph;
1531 struct in6_addr saddr_buf;
1532 struct in6_addr *addrp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001533 struct rt6_info *rt;
1534 struct dst_entry *dst;
1535 struct inet6_dev *idev;
David S. Miller4c9483b2011-03-12 16:22:43 -05001536 struct flowi6 fl6;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001537 u8 *opt;
Herbert Xua7ae1992011-11-18 02:20:04 +00001538 int hlen, tlen;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001539 int rd_len;
1540 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001541 u8 ha_buf[MAX_ADDR_LEN], *ha = NULL;
1542
Neil Horman95c385b2007-04-25 17:08:10 -07001543 if (ipv6_get_lladdr(dev, &saddr_buf, IFA_F_TENTATIVE)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001544 ND_PRINTK2(KERN_WARNING
1545 "ICMPv6 Redirect: no link-local address on %s\n",
1546 dev->name);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001547 return;
1548 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001549
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001550 if (!ipv6_addr_equal(&ipv6_hdr(skb)->daddr, target) &&
Brian Haleybf0b48d2007-10-08 00:12:05 -07001551 ipv6_addr_type(target) != (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) {
Li Yewang29556522007-01-30 14:33:20 -08001552 ND_PRINTK2(KERN_WARNING
Brian Haleybf0b48d2007-10-08 00:12:05 -07001553 "ICMPv6 Redirect: target address is not link-local unicast.\n");
Li Yewang29556522007-01-30 14:33:20 -08001554 return;
1555 }
1556
David S. Miller4c9483b2011-03-12 16:22:43 -05001557 icmpv6_flow_init(sk, &fl6, NDISC_REDIRECT,
YOSHIFUJI Hideaki95e41e92007-12-06 15:43:30 -08001558 &saddr_buf, &ipv6_hdr(skb)->saddr, dev->ifindex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001559
David S. Miller4c9483b2011-03-12 16:22:43 -05001560 dst = ip6_route_output(net, NULL, &fl6);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001561 if (dst == NULL)
1562 return;
1563
David S. Miller4c9483b2011-03-12 16:22:43 -05001564 dst = xfrm_lookup(net, dst, flowi6_to_flowi(&fl6), NULL, 0);
David S. Miller452edd52011-03-02 13:27:41 -08001565 if (IS_ERR(dst))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001566 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001567
1568 rt = (struct rt6_info *) dst;
1569
1570 if (rt->rt6i_flags & RTF_GATEWAY) {
1571 ND_PRINTK2(KERN_WARNING
1572 "ICMPv6 Redirect: destination is not a neighbour.\n");
Ilpo Järvinend73f0802009-02-06 23:47:37 -08001573 goto release;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001574 }
David S. Miller92d86822011-02-04 15:55:25 -08001575 if (!rt->rt6i_peer)
1576 rt6_bind_peer(rt, 1);
1577 if (inet_peer_xrlim_allow(rt->rt6i_peer, 1*HZ))
Ilpo Järvinend73f0802009-02-06 23:47:37 -08001578 goto release;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001579
1580 if (dev->addr_len) {
1581 read_lock_bh(&neigh->lock);
1582 if (neigh->nud_state & NUD_VALID) {
1583 memcpy(ha_buf, neigh->ha, dev->addr_len);
1584 read_unlock_bh(&neigh->lock);
1585 ha = ha_buf;
1586 len += ndisc_opt_addr_space(dev);
1587 } else
1588 read_unlock_bh(&neigh->lock);
1589 }
1590
1591 rd_len = min_t(unsigned int,
1592 IPV6_MIN_MTU-sizeof(struct ipv6hdr)-len, skb->len + 8);
1593 rd_len &= ~0x7;
1594 len += rd_len;
1595
Herbert Xua7ae1992011-11-18 02:20:04 +00001596 hlen = LL_RESERVED_SPACE(dev);
1597 tlen = dev->needed_tailroom;
David S. Millerd54a81d2006-12-02 21:00:06 -08001598 buff = sock_alloc_send_skb(sk,
1599 (MAX_HEADER + sizeof(struct ipv6hdr) +
Herbert Xua7ae1992011-11-18 02:20:04 +00001600 len + hlen + tlen),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001601 1, &err);
1602 if (buff == NULL) {
1603 ND_PRINTK0(KERN_ERR
Brian Haleydae9de82009-06-02 00:20:26 -07001604 "ICMPv6 Redirect: %s() failed to allocate an skb, err=%d.\n",
1605 __func__, err);
Ilpo Järvinend73f0802009-02-06 23:47:37 -08001606 goto release;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001607 }
1608
Herbert Xua7ae1992011-11-18 02:20:04 +00001609 skb_reserve(buff, hlen);
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001610 ip6_nd_hdr(sk, buff, dev, &saddr_buf, &ipv6_hdr(skb)->saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001611 IPPROTO_ICMPV6, len);
1612
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -07001613 skb_set_transport_header(buff, skb_tail_pointer(buff) - buff->data);
Arnaldo Carvalho de Melod10ba342007-03-14 21:05:37 -03001614 skb_put(buff, len);
1615 icmph = icmp6_hdr(buff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001616
1617 memset(icmph, 0, sizeof(struct icmp6hdr));
1618 icmph->icmp6_type = NDISC_REDIRECT;
1619
1620 /*
1621 * copy target and destination addresses
1622 */
1623
1624 addrp = (struct in6_addr *)(icmph + 1);
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001625 *addrp = *target;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001626 addrp++;
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001627 *addrp = ipv6_hdr(skb)->daddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001628
1629 opt = (u8*) (addrp + 1);
1630
1631 /*
1632 * include target_address option
1633 */
1634
1635 if (ha)
1636 opt = ndisc_fill_addr_option(opt, ND_OPT_TARGET_LL_ADDR, ha,
1637 dev->addr_len, dev->type);
1638
1639 /*
1640 * build redirect option and copy skb over to the new packet.
1641 */
1642
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001643 memset(opt, 0, 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001644 *(opt++) = ND_OPT_REDIRECT_HDR;
1645 *(opt++) = (rd_len >> 3);
1646 opt += 6;
1647
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001648 memcpy(opt, ipv6_hdr(skb), rd_len - 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001649
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001650 icmph->icmp6_cksum = csum_ipv6_magic(&saddr_buf, &ipv6_hdr(skb)->saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001651 len, IPPROTO_ICMPV6,
Joe Perches07f07572008-11-19 15:44:53 -08001652 csum_partial(icmph, len, 0));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001653
Eric Dumazetadf30902009-06-02 05:19:30 +00001654 skb_dst_set(buff, dst);
Eric Dumazetcfdf7642011-07-27 21:13:03 +00001655 rcu_read_lock();
1656 idev = __in6_dev_get(dst->dev);
Neil Hormanedf391f2009-04-27 02:45:02 -07001657 IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUT, skb->len);
Jan Engelhardtb2e0b382010-03-23 04:09:07 +01001658 err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, buff, NULL, dst->dev,
Patrick McHardy6e23ae22007-11-19 18:53:30 -08001659 dst_output);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001660 if (!err) {
Denis V. Lunev5c5d2442008-10-08 10:33:50 -07001661 ICMP6MSGOUT_INC_STATS(net, idev, NDISC_REDIRECT);
Denis V. Luneva862f6a2008-10-08 10:33:06 -07001662 ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001663 }
1664
Eric Dumazetcfdf7642011-07-27 21:13:03 +00001665 rcu_read_unlock();
Ilpo Järvinend73f0802009-02-06 23:47:37 -08001666 return;
1667
1668release:
1669 dst_release(dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001670}
1671
1672static void pndisc_redo(struct sk_buff *skb)
1673{
YOSHIFUJI Hideaki140e26fc2005-10-05 12:11:41 -07001674 ndisc_recv_ns(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001675 kfree_skb(skb);
1676}
1677
1678int ndisc_rcv(struct sk_buff *skb)
1679{
1680 struct nd_msg *msg;
1681
1682 if (!pskb_may_pull(skb, skb->len))
1683 return 0;
1684
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -07001685 msg = (struct nd_msg *)skb_transport_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001686
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -07001687 __skb_push(skb, skb->data - skb_transport_header(skb));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001688
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001689 if (ipv6_hdr(skb)->hop_limit != 255) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001690 ND_PRINTK2(KERN_WARNING
1691 "ICMPv6 NDISC: invalid hop-limit: %d\n",
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001692 ipv6_hdr(skb)->hop_limit);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001693 return 0;
1694 }
1695
1696 if (msg->icmph.icmp6_code != 0) {
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001697 ND_PRINTK2(KERN_WARNING
Linus Torvalds1da177e2005-04-16 15:20:36 -07001698 "ICMPv6 NDISC: invalid ICMPv6 code: %d\n",
1699 msg->icmph.icmp6_code);
1700 return 0;
1701 }
1702
Patrick McHardya61bbcf2005-08-14 17:24:31 -07001703 memset(NEIGH_CB(skb), 0, sizeof(struct neighbour_cb));
1704
Linus Torvalds1da177e2005-04-16 15:20:36 -07001705 switch (msg->icmph.icmp6_type) {
1706 case NDISC_NEIGHBOUR_SOLICITATION:
1707 ndisc_recv_ns(skb);
1708 break;
1709
1710 case NDISC_NEIGHBOUR_ADVERTISEMENT:
1711 ndisc_recv_na(skb);
1712 break;
1713
1714 case NDISC_ROUTER_SOLICITATION:
1715 ndisc_recv_rs(skb);
1716 break;
1717
1718 case NDISC_ROUTER_ADVERTISEMENT:
1719 ndisc_router_discovery(skb);
1720 break;
1721
1722 case NDISC_REDIRECT:
1723 ndisc_redirect_rcv(skb);
1724 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -07001725 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001726
1727 return 0;
1728}
1729
1730static int ndisc_netdev_event(struct notifier_block *this, unsigned long event, void *ptr)
1731{
1732 struct net_device *dev = ptr;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001733 struct net *net = dev_net(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001734
1735 switch (event) {
1736 case NETDEV_CHANGEADDR:
1737 neigh_changeaddr(&nd_tbl, dev);
Daniel Lezcano5b7c9312008-03-03 23:28:58 -08001738 fib6_run_gc(~0UL, net);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001739 break;
1740 case NETDEV_DOWN:
1741 neigh_ifdown(&nd_tbl, dev);
Daniel Lezcano5b7c9312008-03-03 23:28:58 -08001742 fib6_run_gc(~0UL, net);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001743 break;
Ben Hutchingsf47b9462011-04-15 13:46:02 +00001744 case NETDEV_NOTIFY_PEERS:
1745 ndisc_send_unsol_na(dev);
1746 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001747 default:
1748 break;
1749 }
1750
1751 return NOTIFY_DONE;
1752}
1753
1754static struct notifier_block ndisc_netdev_notifier = {
1755 .notifier_call = ndisc_netdev_event,
1756};
1757
1758#ifdef CONFIG_SYSCTL
1759static void ndisc_warn_deprecated_sysctl(struct ctl_table *ctl,
1760 const char *func, const char *dev_name)
1761{
1762 static char warncomm[TASK_COMM_LEN];
1763 static int warned;
1764 if (strcmp(warncomm, current->comm) && warned < 5) {
1765 strcpy(warncomm, current->comm);
1766 printk(KERN_WARNING
1767 "process `%s' is using deprecated sysctl (%s) "
1768 "net.ipv6.neigh.%s.%s; "
1769 "Use net.ipv6.neigh.%s.%s_ms "
1770 "instead.\n",
1771 warncomm, func,
1772 dev_name, ctl->procname,
1773 dev_name, ctl->procname);
1774 warned++;
1775 }
1776}
1777
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001778int ndisc_ifinfo_sysctl_change(struct ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *ppos)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001779{
1780 struct net_device *dev = ctl->extra1;
1781 struct inet6_dev *idev;
1782 int ret;
1783
Eric W. Biedermand12af672007-10-18 03:05:25 -07001784 if ((strcmp(ctl->procname, "retrans_time") == 0) ||
1785 (strcmp(ctl->procname, "base_reachable_time") == 0))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001786 ndisc_warn_deprecated_sysctl(ctl, "syscall", dev ? dev->name : "default");
1787
Eric W. Biedermand12af672007-10-18 03:05:25 -07001788 if (strcmp(ctl->procname, "retrans_time") == 0)
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001789 ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
Eric W. Biedermand12af672007-10-18 03:05:25 -07001790
1791 else if (strcmp(ctl->procname, "base_reachable_time") == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001792 ret = proc_dointvec_jiffies(ctl, write,
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001793 buffer, lenp, ppos);
Eric W. Biedermand12af672007-10-18 03:05:25 -07001794
1795 else if ((strcmp(ctl->procname, "retrans_time_ms") == 0) ||
YOSHIFUJI Hideakiad02ac12007-10-29 01:32:23 -07001796 (strcmp(ctl->procname, "base_reachable_time_ms") == 0))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001797 ret = proc_dointvec_ms_jiffies(ctl, write,
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001798 buffer, lenp, ppos);
Eric W. Biedermand12af672007-10-18 03:05:25 -07001799 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001800 ret = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001801
1802 if (write && ret == 0 && dev && (idev = in6_dev_get(dev)) != NULL) {
Eric W. Biedermand12af672007-10-18 03:05:25 -07001803 if (ctl->data == &idev->nd_parms->base_reachable_time)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001804 idev->nd_parms->reachable_time = neigh_rand_reach_time(idev->nd_parms->base_reachable_time);
1805 idev->tstamp = jiffies;
1806 inet6_ifinfo_notify(RTM_NEWLINK, idev);
1807 in6_dev_put(idev);
1808 }
1809 return ret;
1810}
1811
Linus Torvalds1da177e2005-04-16 15:20:36 -07001812
1813#endif
1814
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00001815static int __net_init ndisc_net_init(struct net *net)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001816{
1817 struct ipv6_pinfo *np;
1818 struct sock *sk;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001819 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001820
Denis V. Lunev1ed85162008-04-03 14:31:03 -07001821 err = inet_ctl_sock_create(&sk, PF_INET6,
1822 SOCK_RAW, IPPROTO_ICMPV6, net);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001823 if (err < 0) {
1824 ND_PRINTK0(KERN_ERR
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001825 "ICMPv6 NDISC: Failed to initialize the control socket (err %d).\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001826 err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001827 return err;
1828 }
1829
Denis V. Lunev1ed85162008-04-03 14:31:03 -07001830 net->ipv6.ndisc_sk = sk;
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001831
Linus Torvalds1da177e2005-04-16 15:20:36 -07001832 np = inet6_sk(sk);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001833 np->hop_limit = 255;
1834 /* Do not loopback ndisc messages */
1835 np->mc_loop = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001836
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001837 return 0;
1838}
1839
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00001840static void __net_exit ndisc_net_exit(struct net *net)
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001841{
Denis V. Lunev1ed85162008-04-03 14:31:03 -07001842 inet_ctl_sock_destroy(net->ipv6.ndisc_sk);
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001843}
1844
1845static struct pernet_operations ndisc_net_ops = {
1846 .init = ndisc_net_init,
1847 .exit = ndisc_net_exit,
1848};
1849
1850int __init ndisc_init(void)
1851{
1852 int err;
1853
1854 err = register_pernet_subsys(&ndisc_net_ops);
1855 if (err)
1856 return err;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001857 /*
1858 * Initialize the neighbour table
1859 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001860 neigh_table_init(&nd_tbl);
1861
1862#ifdef CONFIG_SYSCTL
Eric W. Biederman54716e32010-02-14 03:27:03 +00001863 err = neigh_sysctl_register(NULL, &nd_tbl.parms, "ipv6",
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001864 &ndisc_ifinfo_sysctl_change);
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001865 if (err)
1866 goto out_unregister_pernet;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001867#endif
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001868 err = register_netdevice_notifier(&ndisc_netdev_notifier);
1869 if (err)
1870 goto out_unregister_sysctl;
1871out:
1872 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001873
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001874out_unregister_sysctl:
1875#ifdef CONFIG_SYSCTL
1876 neigh_sysctl_unregister(&nd_tbl.parms);
1877out_unregister_pernet:
1878#endif
1879 unregister_pernet_subsys(&ndisc_net_ops);
1880 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001881}
1882
1883void ndisc_cleanup(void)
1884{
Dmitry Mishin36f73d02006-11-03 16:08:19 -08001885 unregister_netdevice_notifier(&ndisc_netdev_notifier);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001886#ifdef CONFIG_SYSCTL
1887 neigh_sysctl_unregister(&nd_tbl.parms);
1888#endif
1889 neigh_table_clear(&nd_tbl);
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001890 unregister_pernet_subsys(&ndisc_net_ops);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001891}