blob: f3e50c29add42a1ce66d137c8cd77bfa9174ddc3 [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,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700129 .key_len = sizeof(struct in6_addr),
130 .hash = ndisc_hash,
131 .constructor = ndisc_constructor,
132 .pconstructor = pndisc_constructor,
133 .pdestructor = pndisc_destructor,
134 .proxy_redo = pndisc_redo,
135 .id = "ndisc_cache",
136 .parms = {
Shan Weib6720832010-12-01 18:05:12 +0000137 .tbl = &nd_tbl,
138 .base_reachable_time = ND_REACHABLE_TIME,
139 .retrans_time = ND_RETRANS_TIMER,
140 .gc_staletime = 60 * HZ,
141 .reachable_time = ND_REACHABLE_TIME,
142 .delay_probe_time = 5 * HZ,
Eric Dumazet8b5c1712011-11-09 12:07:14 +0000143 .queue_len_bytes = 64*1024,
Shan Weib6720832010-12-01 18:05:12 +0000144 .ucast_probes = 3,
145 .mcast_probes = 3,
146 .anycast_delay = 1 * HZ,
147 .proxy_delay = (8 * HZ) / 10,
148 .proxy_qlen = 64,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700149 },
150 .gc_interval = 30 * HZ,
151 .gc_thresh1 = 128,
152 .gc_thresh2 = 512,
153 .gc_thresh3 = 1024,
154};
155
156/* ND options */
157struct ndisc_options {
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800158 struct nd_opt_hdr *nd_opt_array[__ND_OPT_ARRAY_MAX];
159#ifdef CONFIG_IPV6_ROUTE_INFO
160 struct nd_opt_hdr *nd_opts_ri;
161 struct nd_opt_hdr *nd_opts_ri_end;
162#endif
Pierre Ynard31910572007-10-10 21:22:05 -0700163 struct nd_opt_hdr *nd_useropts;
164 struct nd_opt_hdr *nd_useropts_end;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700165};
166
167#define nd_opts_src_lladdr nd_opt_array[ND_OPT_SOURCE_LL_ADDR]
168#define nd_opts_tgt_lladdr nd_opt_array[ND_OPT_TARGET_LL_ADDR]
169#define nd_opts_pi nd_opt_array[ND_OPT_PREFIX_INFO]
170#define nd_opts_pi_end nd_opt_array[__ND_OPT_PREFIX_INFO_END]
171#define nd_opts_rh nd_opt_array[ND_OPT_REDIRECT_HDR]
172#define nd_opts_mtu nd_opt_array[ND_OPT_MTU]
173
174#define NDISC_OPT_SPACE(len) (((len)+2+7)&~7)
175
176/*
177 * Return the padding between the option length and the start of the
178 * link addr. Currently only IP-over-InfiniBand needs this, although
179 * if RFC 3831 IPv6-over-Fibre Channel is ever implemented it may
180 * also need a pad of 2.
181 */
182static int ndisc_addr_option_pad(unsigned short type)
183{
184 switch (type) {
185 case ARPHRD_INFINIBAND: return 2;
186 default: return 0;
187 }
188}
189
190static inline int ndisc_opt_addr_space(struct net_device *dev)
191{
192 return NDISC_OPT_SPACE(dev->addr_len + ndisc_addr_option_pad(dev->type));
193}
194
195static u8 *ndisc_fill_addr_option(u8 *opt, int type, void *data, int data_len,
196 unsigned short addr_type)
197{
198 int space = NDISC_OPT_SPACE(data_len);
199 int pad = ndisc_addr_option_pad(addr_type);
200
201 opt[0] = type;
202 opt[1] = space>>3;
203
204 memset(opt + 2, 0, pad);
205 opt += pad;
206 space -= pad;
207
208 memcpy(opt+2, data, data_len);
209 data_len += 2;
210 opt += data_len;
211 if ((space -= data_len) > 0)
212 memset(opt, 0, space);
213 return opt + space;
214}
215
216static struct nd_opt_hdr *ndisc_next_option(struct nd_opt_hdr *cur,
217 struct nd_opt_hdr *end)
218{
219 int type;
220 if (!cur || !end || cur >= end)
221 return NULL;
222 type = cur->nd_opt_type;
223 do {
224 cur = ((void *)cur) + (cur->nd_opt_len << 3);
225 } while(cur < end && cur->nd_opt_type != type);
Eric Dumazeta02cec22010-09-22 20:43:57 +0000226 return cur <= end && cur->nd_opt_type == type ? cur : NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700227}
228
Pierre Ynard31910572007-10-10 21:22:05 -0700229static inline int ndisc_is_useropt(struct nd_opt_hdr *opt)
230{
Eric Dumazeta02cec22010-09-22 20:43:57 +0000231 return opt->nd_opt_type == ND_OPT_RDNSS;
Pierre Ynard31910572007-10-10 21:22:05 -0700232}
233
234static struct nd_opt_hdr *ndisc_next_useropt(struct nd_opt_hdr *cur,
235 struct nd_opt_hdr *end)
236{
237 if (!cur || !end || cur >= end)
238 return NULL;
239 do {
240 cur = ((void *)cur) + (cur->nd_opt_len << 3);
241 } while(cur < end && !ndisc_is_useropt(cur));
Eric Dumazeta02cec22010-09-22 20:43:57 +0000242 return cur <= end && ndisc_is_useropt(cur) ? cur : NULL;
Pierre Ynard31910572007-10-10 21:22:05 -0700243}
244
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245static struct ndisc_options *ndisc_parse_options(u8 *opt, int opt_len,
246 struct ndisc_options *ndopts)
247{
248 struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)opt;
249
250 if (!nd_opt || opt_len < 0 || !ndopts)
251 return NULL;
252 memset(ndopts, 0, sizeof(*ndopts));
253 while (opt_len) {
254 int l;
255 if (opt_len < sizeof(struct nd_opt_hdr))
256 return NULL;
257 l = nd_opt->nd_opt_len << 3;
258 if (opt_len < l || l == 0)
259 return NULL;
260 switch (nd_opt->nd_opt_type) {
261 case ND_OPT_SOURCE_LL_ADDR:
262 case ND_OPT_TARGET_LL_ADDR:
263 case ND_OPT_MTU:
264 case ND_OPT_REDIRECT_HDR:
265 if (ndopts->nd_opt_array[nd_opt->nd_opt_type]) {
266 ND_PRINTK2(KERN_WARNING
267 "%s(): duplicated ND6 option found: type=%d\n",
Harvey Harrison0dc47872008-03-05 20:47:47 -0800268 __func__,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700269 nd_opt->nd_opt_type);
270 } else {
271 ndopts->nd_opt_array[nd_opt->nd_opt_type] = nd_opt;
272 }
273 break;
274 case ND_OPT_PREFIX_INFO:
275 ndopts->nd_opts_pi_end = nd_opt;
Stephen Hemmingercfcabdc2007-10-09 01:59:42 -0700276 if (!ndopts->nd_opt_array[nd_opt->nd_opt_type])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277 ndopts->nd_opt_array[nd_opt->nd_opt_type] = nd_opt;
278 break;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800279#ifdef CONFIG_IPV6_ROUTE_INFO
280 case ND_OPT_ROUTE_INFO:
281 ndopts->nd_opts_ri_end = nd_opt;
282 if (!ndopts->nd_opts_ri)
283 ndopts->nd_opts_ri = nd_opt;
284 break;
285#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700286 default:
Pierre Ynard31910572007-10-10 21:22:05 -0700287 if (ndisc_is_useropt(nd_opt)) {
288 ndopts->nd_useropts_end = nd_opt;
289 if (!ndopts->nd_useropts)
290 ndopts->nd_useropts = nd_opt;
291 } else {
292 /*
293 * Unknown options must be silently ignored,
294 * to accommodate future extension to the
295 * protocol.
296 */
297 ND_PRINTK2(KERN_NOTICE
298 "%s(): ignored unsupported option; type=%d, len=%d\n",
Harvey Harrison0dc47872008-03-05 20:47:47 -0800299 __func__,
Pierre Ynard31910572007-10-10 21:22:05 -0700300 nd_opt->nd_opt_type, nd_opt->nd_opt_len);
301 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700302 }
303 opt_len -= l;
304 nd_opt = ((void *)nd_opt) + l;
305 }
306 return ndopts;
307}
308
309static inline u8 *ndisc_opt_addr_data(struct nd_opt_hdr *p,
310 struct net_device *dev)
311{
312 u8 *lladdr = (u8 *)(p + 1);
313 int lladdrlen = p->nd_opt_len << 3;
314 int prepad = ndisc_addr_option_pad(dev->type);
315 if (lladdrlen != NDISC_OPT_SPACE(dev->addr_len + prepad))
316 return NULL;
Eric Dumazeta02cec22010-09-22 20:43:57 +0000317 return lladdr + prepad;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700318}
319
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000320int ndisc_mc_map(const struct in6_addr *addr, char *buf, struct net_device *dev, int dir)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700321{
322 switch (dev->type) {
323 case ARPHRD_ETHER:
324 case ARPHRD_IEEE802: /* Not sure. Check it later. --ANK */
325 case ARPHRD_FDDI:
326 ipv6_eth_mc_map(addr, buf);
327 return 0;
328 case ARPHRD_IEEE802_TR:
329 ipv6_tr_mc_map(addr,buf);
330 return 0;
331 case ARPHRD_ARCNET:
332 ipv6_arcnet_mc_map(addr, buf);
333 return 0;
334 case ARPHRD_INFINIBAND:
Rolf Manderscheida9e527e2007-12-10 13:38:41 -0700335 ipv6_ib_mc_map(addr, dev->broadcast, buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336 return 0;
Timo Teräs93ca3bb2011-03-28 22:40:53 +0000337 case ARPHRD_IPGRE:
338 return ipv6_ipgre_mc_map(addr, dev->broadcast, buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339 default:
340 if (dir) {
341 memcpy(buf, dev->broadcast, dev->addr_len);
342 return 0;
343 }
344 }
345 return -EINVAL;
346}
347
YOSHIFUJI Hideaki71590392007-02-22 22:05:40 +0900348EXPORT_SYMBOL(ndisc_mc_map);
349
Eric Dumazetd6bf7812010-10-04 06:15:44 +0000350static u32 ndisc_hash(const void *pkey,
351 const struct net_device *dev,
352 __u32 hash_rnd)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700353{
354 const u32 *p32 = pkey;
355 u32 addr_hash, i;
356
357 addr_hash = 0;
358 for (i = 0; i < (sizeof(struct in6_addr) / sizeof(u32)); i++)
359 addr_hash ^= *p32++;
360
Eric Dumazetd6bf7812010-10-04 06:15:44 +0000361 return jhash_2words(addr_hash, dev->ifindex, hash_rnd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362}
363
364static int ndisc_constructor(struct neighbour *neigh)
365{
366 struct in6_addr *addr = (struct in6_addr*)&neigh->primary_key;
367 struct net_device *dev = neigh->dev;
368 struct inet6_dev *in6_dev;
369 struct neigh_parms *parms;
370 int is_multicast = ipv6_addr_is_multicast(addr);
371
Linus Torvalds1da177e2005-04-16 15:20:36 -0700372 in6_dev = in6_dev_get(dev);
373 if (in6_dev == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700374 return -EINVAL;
375 }
376
377 parms = in6_dev->nd_parms;
378 __neigh_parms_put(neigh->parms);
379 neigh->parms = neigh_parms_clone(parms);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700380
381 neigh->type = is_multicast ? RTN_MULTICAST : RTN_UNICAST;
Stephen Hemminger3b04ddd2007-10-09 01:40:57 -0700382 if (!dev->header_ops) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700383 neigh->nud_state = NUD_NOARP;
384 neigh->ops = &ndisc_direct_ops;
David S. Miller8f40b162011-07-17 13:34:11 -0700385 neigh->output = neigh_direct_output;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700386 } else {
387 if (is_multicast) {
388 neigh->nud_state = NUD_NOARP;
389 ndisc_mc_map(addr, neigh->ha, dev, 1);
390 } else if (dev->flags&(IFF_NOARP|IFF_LOOPBACK)) {
391 neigh->nud_state = NUD_NOARP;
392 memcpy(neigh->ha, dev->dev_addr, dev->addr_len);
393 if (dev->flags&IFF_LOOPBACK)
394 neigh->type = RTN_LOCAL;
395 } else if (dev->flags&IFF_POINTOPOINT) {
396 neigh->nud_state = NUD_NOARP;
397 memcpy(neigh->ha, dev->broadcast, dev->addr_len);
398 }
Stephen Hemminger3b04ddd2007-10-09 01:40:57 -0700399 if (dev->header_ops->cache)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700400 neigh->ops = &ndisc_hh_ops;
401 else
402 neigh->ops = &ndisc_generic_ops;
403 if (neigh->nud_state&NUD_VALID)
404 neigh->output = neigh->ops->connected_output;
405 else
406 neigh->output = neigh->ops->output;
407 }
408 in6_dev_put(in6_dev);
409 return 0;
410}
411
412static int pndisc_constructor(struct pneigh_entry *n)
413{
414 struct in6_addr *addr = (struct in6_addr*)&n->key;
415 struct in6_addr maddr;
416 struct net_device *dev = n->dev;
417
418 if (dev == NULL || __in6_dev_get(dev) == NULL)
419 return -EINVAL;
420 addrconf_addr_solict_mult(addr, &maddr);
421 ipv6_dev_mc_inc(dev, &maddr);
422 return 0;
423}
424
425static void pndisc_destructor(struct pneigh_entry *n)
426{
427 struct in6_addr *addr = (struct in6_addr*)&n->key;
428 struct in6_addr maddr;
429 struct net_device *dev = n->dev;
430
431 if (dev == NULL || __in6_dev_get(dev) == NULL)
432 return;
433 addrconf_addr_solict_mult(addr, &maddr);
434 ipv6_dev_mc_dec(dev, &maddr);
435}
436
Brian Haley305d5522008-11-04 17:51:14 -0800437struct sk_buff *ndisc_build_skb(struct net_device *dev,
438 const struct in6_addr *daddr,
439 const struct in6_addr *saddr,
440 struct icmp6hdr *icmp6h,
441 const struct in6_addr *target,
442 int llinfo)
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900443{
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900444 struct net *net = dev_net(dev);
Daniel Lezcano1762f7e2008-03-07 11:15:34 -0800445 struct sock *sk = net->ipv6.ndisc_sk;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900446 struct sk_buff *skb;
447 struct icmp6hdr *hdr;
Herbert Xua7ae1992011-11-18 02:20:04 +0000448 int hlen = LL_RESERVED_SPACE(dev);
449 int tlen = dev->needed_tailroom;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900450 int len;
451 int err;
Brian Haley305d5522008-11-04 17:51:14 -0800452 u8 *opt;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900453
454 if (!dev->addr_len)
455 llinfo = 0;
456
457 len = sizeof(struct icmp6hdr) + (target ? sizeof(*target) : 0);
458 if (llinfo)
459 len += ndisc_opt_addr_space(dev);
460
461 skb = sock_alloc_send_skb(sk,
462 (MAX_HEADER + sizeof(struct ipv6hdr) +
Herbert Xua7ae1992011-11-18 02:20:04 +0000463 len + hlen + tlen),
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900464 1, &err);
465 if (!skb) {
466 ND_PRINTK0(KERN_ERR
Brian Haleydae9de82009-06-02 00:20:26 -0700467 "ICMPv6 ND: %s() failed to allocate an skb, err=%d.\n",
468 __func__, err);
Brian Haley305d5522008-11-04 17:51:14 -0800469 return NULL;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900470 }
471
Herbert Xua7ae1992011-11-18 02:20:04 +0000472 skb_reserve(skb, hlen);
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900473 ip6_nd_hdr(sk, skb, dev, saddr, daddr, IPPROTO_ICMPV6, len);
474
475 skb->transport_header = skb->tail;
476 skb_put(skb, len);
477
478 hdr = (struct icmp6hdr *)skb_transport_header(skb);
479 memcpy(hdr, icmp6h, sizeof(*hdr));
480
481 opt = skb_transport_header(skb) + sizeof(struct icmp6hdr);
482 if (target) {
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +0000483 *(struct in6_addr *)opt = *target;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900484 opt += sizeof(*target);
485 }
486
487 if (llinfo)
488 ndisc_fill_addr_option(opt, llinfo, dev->dev_addr,
489 dev->addr_len, dev->type);
490
491 hdr->icmp6_cksum = csum_ipv6_magic(saddr, daddr, len,
492 IPPROTO_ICMPV6,
Joe Perches07f07572008-11-19 15:44:53 -0800493 csum_partial(hdr,
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900494 len, 0));
495
Brian Haley305d5522008-11-04 17:51:14 -0800496 return skb;
497}
498
499EXPORT_SYMBOL(ndisc_build_skb);
500
501void ndisc_send_skb(struct sk_buff *skb,
502 struct net_device *dev,
503 struct neighbour *neigh,
504 const struct in6_addr *daddr,
505 const struct in6_addr *saddr,
506 struct icmp6hdr *icmp6h)
507{
David S. Miller4c9483b2011-03-12 16:22:43 -0500508 struct flowi6 fl6;
Brian Haley305d5522008-11-04 17:51:14 -0800509 struct dst_entry *dst;
510 struct net *net = dev_net(dev);
511 struct sock *sk = net->ipv6.ndisc_sk;
512 struct inet6_dev *idev;
513 int err;
514 u8 type;
515
516 type = icmp6h->icmp6_type;
517
David S. Miller4c9483b2011-03-12 16:22:43 -0500518 icmpv6_flow_init(sk, &fl6, type, saddr, daddr, dev->ifindex);
David S. Miller87a11572011-12-06 17:04:13 -0500519 dst = icmp6_dst_alloc(dev, neigh, &fl6);
David S. Miller452edd52011-03-02 13:27:41 -0800520 if (IS_ERR(dst)) {
Brian Haley305d5522008-11-04 17:51:14 -0800521 kfree_skb(skb);
522 return;
523 }
524
Eric Dumazetadf30902009-06-02 05:19:30 +0000525 skb_dst_set(skb, dst);
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900526
Eric Dumazetcfdf7642011-07-27 21:13:03 +0000527 rcu_read_lock();
528 idev = __in6_dev_get(dst->dev);
Neil Hormanedf391f2009-04-27 02:45:02 -0700529 IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUT, skb->len);
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900530
Jan Engelhardtb2e0b382010-03-23 04:09:07 +0100531 err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, skb, NULL, dst->dev,
Patrick McHardy6e23ae22007-11-19 18:53:30 -0800532 dst_output);
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900533 if (!err) {
Denis V. Lunev5c5d2442008-10-08 10:33:50 -0700534 ICMP6MSGOUT_INC_STATS(net, idev, type);
Denis V. Luneva862f6a2008-10-08 10:33:06 -0700535 ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS);
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900536 }
537
Eric Dumazetcfdf7642011-07-27 21:13:03 +0000538 rcu_read_unlock();
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900539}
540
Brian Haley305d5522008-11-04 17:51:14 -0800541EXPORT_SYMBOL(ndisc_send_skb);
542
543/*
544 * Send a Neighbour Discover packet
545 */
546static void __ndisc_send(struct net_device *dev,
547 struct neighbour *neigh,
548 const struct in6_addr *daddr,
549 const struct in6_addr *saddr,
550 struct icmp6hdr *icmp6h, const struct in6_addr *target,
551 int llinfo)
552{
553 struct sk_buff *skb;
554
555 skb = ndisc_build_skb(dev, daddr, saddr, icmp6h, target, llinfo);
556 if (!skb)
557 return;
558
559 ndisc_send_skb(skb, dev, neigh, daddr, saddr, icmp6h);
560}
561
Linus Torvalds1da177e2005-04-16 15:20:36 -0700562static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +0900563 const struct in6_addr *daddr,
564 const struct in6_addr *solicited_addr,
565 int router, int solicited, int override, int inc_opt)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566{
567 struct in6_addr tmpaddr;
568 struct inet6_ifaddr *ifp;
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +0900569 const struct in6_addr *src_addr;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900570 struct icmp6hdr icmp6h = {
571 .icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT,
572 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700573
574 /* for anycast or proxy, solicited_addr != src_addr */
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900575 ifp = ipv6_get_ifaddr(dev_net(dev), solicited_addr, dev, 1);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900576 if (ifp) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700577 src_addr = solicited_addr;
Neil Horman95c385b2007-04-25 17:08:10 -0700578 if (ifp->flags & IFA_F_OPTIMISTIC)
579 override = 0;
stephen hemminger9f888162010-06-21 11:00:13 +0000580 inc_opt |= ifp->idev->cnf.force_tllao;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581 in6_ifa_put(ifp);
582 } else {
Brian Haley191cd582008-08-14 15:33:21 -0700583 if (ipv6_dev_get_saddr(dev_net(dev), dev, daddr,
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900584 inet6_sk(dev_net(dev)->ipv6.ndisc_sk)->srcprefs,
YOSHIFUJI Hideaki7cbca672008-03-25 09:37:42 +0900585 &tmpaddr))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700586 return;
587 src_addr = &tmpaddr;
588 }
589
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900590 icmp6h.icmp6_router = router;
591 icmp6h.icmp6_solicited = solicited;
592 icmp6h.icmp6_override = override;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700593
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900594 __ndisc_send(dev, neigh, daddr, src_addr,
595 &icmp6h, solicited_addr,
David L Stevens14878f72007-09-16 16:52:35 -0700596 inc_opt ? ND_OPT_TARGET_LL_ADDR : 0);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900597}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700598
Ben Hutchingsf47b9462011-04-15 13:46:02 +0000599static void ndisc_send_unsol_na(struct net_device *dev)
600{
601 struct inet6_dev *idev;
602 struct inet6_ifaddr *ifa;
603 struct in6_addr mcaddr;
604
605 idev = in6_dev_get(dev);
606 if (!idev)
607 return;
608
609 read_lock_bh(&idev->lock);
610 list_for_each_entry(ifa, &idev->addr_list, if_list) {
611 addrconf_addr_solict_mult(&ifa->addr, &mcaddr);
612 ndisc_send_na(dev, NULL, &mcaddr, &ifa->addr,
613 /*router=*/ !!idev->cnf.forwarding,
614 /*solicited=*/ false, /*override=*/ true,
615 /*inc_opt=*/ true);
616 }
617 read_unlock_bh(&idev->lock);
618
619 in6_dev_put(idev);
620}
621
Linus Torvalds1da177e2005-04-16 15:20:36 -0700622void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +0900623 const struct in6_addr *solicit,
624 const struct in6_addr *daddr, const struct in6_addr *saddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700625{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700626 struct in6_addr addr_buf;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900627 struct icmp6hdr icmp6h = {
628 .icmp6_type = NDISC_NEIGHBOUR_SOLICITATION,
629 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700630
631 if (saddr == NULL) {
Neil Horman95c385b2007-04-25 17:08:10 -0700632 if (ipv6_get_lladdr(dev, &addr_buf,
633 (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700634 return;
635 saddr = &addr_buf;
636 }
637
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900638 __ndisc_send(dev, neigh, daddr, saddr,
639 &icmp6h, solicit,
David L Stevens14878f72007-09-16 16:52:35 -0700640 !ipv6_addr_any(saddr) ? ND_OPT_SOURCE_LL_ADDR : 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700641}
642
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +0900643void ndisc_send_rs(struct net_device *dev, const struct in6_addr *saddr,
644 const struct in6_addr *daddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700645{
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900646 struct icmp6hdr icmp6h = {
647 .icmp6_type = NDISC_ROUTER_SOLICITATION,
648 };
Neil Horman95c385b2007-04-25 17:08:10 -0700649 int send_sllao = dev->addr_len;
Neil Horman95c385b2007-04-25 17:08:10 -0700650
651#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
652 /*
653 * According to section 2.2 of RFC 4429, we must not
654 * send router solicitations with a sllao from
655 * optimistic addresses, but we may send the solicitation
656 * if we don't include the sllao. So here we check
657 * if our address is optimistic, and if so, we
Joe Perchesbea85192007-12-20 14:01:35 -0800658 * suppress the inclusion of the sllao.
Neil Horman95c385b2007-04-25 17:08:10 -0700659 */
660 if (send_sllao) {
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900661 struct inet6_ifaddr *ifp = ipv6_get_ifaddr(dev_net(dev), saddr,
Daniel Lezcano1cab3da2008-01-10 22:44:09 -0800662 dev, 1);
Neil Horman95c385b2007-04-25 17:08:10 -0700663 if (ifp) {
664 if (ifp->flags & IFA_F_OPTIMISTIC) {
YOSHIFUJI Hideakica043562007-02-28 23:13:20 +0900665 send_sllao = 0;
Neil Horman95c385b2007-04-25 17:08:10 -0700666 }
YOSHIFUJI Hideakica043562007-02-28 23:13:20 +0900667 in6_ifa_put(ifp);
Neil Horman95c385b2007-04-25 17:08:10 -0700668 } else {
669 send_sllao = 0;
670 }
671 }
672#endif
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900673 __ndisc_send(dev, NULL, daddr, saddr,
674 &icmp6h, NULL,
David L Stevens14878f72007-09-16 16:52:35 -0700675 send_sllao ? ND_OPT_SOURCE_LL_ADDR : 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700676}
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900677
Linus Torvalds1da177e2005-04-16 15:20:36 -0700678
679static void ndisc_error_report(struct neighbour *neigh, struct sk_buff *skb)
680{
681 /*
682 * "The sender MUST return an ICMP
683 * destination unreachable"
684 */
685 dst_link_failure(skb);
686 kfree_skb(skb);
687}
688
689/* Called with locked neigh: either read or both */
690
691static void ndisc_solicit(struct neighbour *neigh, struct sk_buff *skb)
692{
693 struct in6_addr *saddr = NULL;
694 struct in6_addr mcaddr;
695 struct net_device *dev = neigh->dev;
696 struct in6_addr *target = (struct in6_addr *)&neigh->primary_key;
697 int probes = atomic_read(&neigh->probes);
698
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900699 if (skb && ipv6_chk_addr(dev_net(dev), &ipv6_hdr(skb)->saddr, dev, 1))
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -0700700 saddr = &ipv6_hdr(skb)->saddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700701
702 if ((probes -= neigh->parms->ucast_probes) < 0) {
703 if (!(neigh->nud_state & NUD_VALID)) {
Harvey Harrison5b095d9892008-10-29 12:52:50 -0700704 ND_PRINTK1(KERN_DEBUG "%s(): trying to ucast probe in NUD_INVALID: %pI6\n",
Harvey Harrison0c6ce782008-10-28 16:09:23 -0700705 __func__, target);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700706 }
707 ndisc_send_ns(dev, neigh, target, target, saddr);
708 } else if ((probes -= neigh->parms->app_probes) < 0) {
709#ifdef CONFIG_ARPD
710 neigh_app_ns(neigh);
711#endif
712 } else {
713 addrconf_addr_solict_mult(target, &mcaddr);
714 ndisc_send_ns(dev, NULL, target, &mcaddr, saddr);
715 }
716}
717
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900718static int pndisc_is_router(const void *pkey,
719 struct net_device *dev)
Pavel Emelyanovfa86d322008-03-24 14:48:59 -0700720{
721 struct pneigh_entry *n;
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900722 int ret = -1;
Pavel Emelyanovfa86d322008-03-24 14:48:59 -0700723
724 read_lock_bh(&nd_tbl.lock);
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900725 n = __pneigh_lookup(&nd_tbl, dev_net(dev), pkey, dev);
726 if (n)
727 ret = !!(n->flags & NTF_ROUTER);
Pavel Emelyanovfa86d322008-03-24 14:48:59 -0700728 read_unlock_bh(&nd_tbl.lock);
729
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900730 return ret;
Pavel Emelyanovfa86d322008-03-24 14:48:59 -0700731}
732
Linus Torvalds1da177e2005-04-16 15:20:36 -0700733static void ndisc_recv_ns(struct sk_buff *skb)
734{
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -0700735 struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb);
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000736 const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
737 const struct in6_addr *daddr = &ipv6_hdr(skb)->daddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700738 u8 *lladdr = NULL;
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -0700739 u32 ndoptlen = skb->tail - (skb->transport_header +
740 offsetof(struct nd_msg, opt));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700741 struct ndisc_options ndopts;
742 struct net_device *dev = skb->dev;
743 struct inet6_ifaddr *ifp;
744 struct inet6_dev *idev = NULL;
745 struct neighbour *neigh;
746 int dad = ipv6_addr_any(saddr);
747 int inc;
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900748 int is_router = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700749
750 if (ipv6_addr_is_multicast(&msg->target)) {
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900751 ND_PRINTK2(KERN_WARNING
Linus Torvalds1da177e2005-04-16 15:20:36 -0700752 "ICMPv6 NS: multicast target address");
753 return;
754 }
755
756 /*
757 * RFC2461 7.1.1:
758 * DAD has to be destined for solicited node multicast address.
759 */
760 if (dad &&
761 !(daddr->s6_addr32[0] == htonl(0xff020000) &&
762 daddr->s6_addr32[1] == htonl(0x00000000) &&
763 daddr->s6_addr32[2] == htonl(0x00000001) &&
764 daddr->s6_addr [12] == 0xff )) {
765 ND_PRINTK2(KERN_WARNING
766 "ICMPv6 NS: bad DAD packet (wrong destination)\n");
767 return;
768 }
769
770 if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts)) {
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900771 ND_PRINTK2(KERN_WARNING
Linus Torvalds1da177e2005-04-16 15:20:36 -0700772 "ICMPv6 NS: invalid ND options\n");
773 return;
774 }
775
776 if (ndopts.nd_opts_src_lladdr) {
777 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr, dev);
778 if (!lladdr) {
779 ND_PRINTK2(KERN_WARNING
780 "ICMPv6 NS: invalid link-layer address length\n");
781 return;
782 }
783
784 /* RFC2461 7.1.1:
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900785 * If the IP source address is the unspecified address,
786 * there MUST NOT be source link-layer address option
Linus Torvalds1da177e2005-04-16 15:20:36 -0700787 * in the message.
788 */
789 if (dad) {
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900790 ND_PRINTK2(KERN_WARNING
Linus Torvalds1da177e2005-04-16 15:20:36 -0700791 "ICMPv6 NS: bad DAD packet (link-layer address option)\n");
792 return;
793 }
794 }
795
796 inc = ipv6_addr_is_multicast(daddr);
797
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900798 ifp = ipv6_get_ifaddr(dev_net(dev), &msg->target, dev, 1);
Daniel Lezcanoa18bc692008-03-07 11:14:49 -0800799 if (ifp) {
Neil Horman95c385b2007-04-25 17:08:10 -0700800
801 if (ifp->flags & (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)) {
802 if (dad) {
803 if (dev->type == ARPHRD_IEEE802_TR) {
Arnaldo Carvalho de Melo98e399f2007-03-19 15:33:04 -0700804 const unsigned char *sadr;
805 sadr = skb_mac_header(skb);
Neil Horman95c385b2007-04-25 17:08:10 -0700806 if (((sadr[8] ^ dev->dev_addr[0]) & 0x7f) == 0 &&
807 sadr[9] == dev->dev_addr[1] &&
808 sadr[10] == dev->dev_addr[2] &&
809 sadr[11] == dev->dev_addr[3] &&
810 sadr[12] == dev->dev_addr[4] &&
811 sadr[13] == dev->dev_addr[5]) {
812 /* looped-back to us */
813 goto out;
814 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700815 }
Neil Horman95c385b2007-04-25 17:08:10 -0700816
817 /*
818 * We are colliding with another node
819 * who is doing DAD
820 * so fail our DAD process
821 */
822 addrconf_dad_failure(ifp);
Denis V. Lunev9e3be4b2007-09-11 11:04:49 +0200823 return;
Neil Horman95c385b2007-04-25 17:08:10 -0700824 } else {
825 /*
826 * This is not a dad solicitation.
827 * If we are an optimistic node,
828 * we should respond.
829 * Otherwise, we should ignore it.
830 */
831 if (!(ifp->flags & IFA_F_OPTIMISTIC))
832 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700833 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700834 }
835
836 idev = ifp->idev;
837 } else {
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700838 struct net *net = dev_net(dev);
839
Linus Torvalds1da177e2005-04-16 15:20:36 -0700840 idev = in6_dev_get(dev);
841 if (!idev) {
842 /* XXX: count this drop? */
843 return;
844 }
845
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700846 if (ipv6_chk_acast_addr(net, dev, &msg->target) ||
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900847 (idev->cnf.forwarding &&
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700848 (net->ipv6.devconf_all->proxy_ndp || idev->cnf.proxy_ndp) &&
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900849 (is_router = pndisc_is_router(&msg->target, dev)) >= 0)) {
Patrick McHardya61bbcf2005-08-14 17:24:31 -0700850 if (!(NEIGH_CB(skb)->flags & LOCALLY_ENQUEUED) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700851 skb->pkt_type != PACKET_HOST &&
852 inc != 0 &&
853 idev->nd_parms->proxy_delay != 0) {
854 /*
855 * for anycast or proxy,
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900856 * sender should delay its response
857 * by a random time between 0 and
Linus Torvalds1da177e2005-04-16 15:20:36 -0700858 * MAX_ANYCAST_DELAY_TIME seconds.
859 * (RFC2461) -- yoshfuji
860 */
861 struct sk_buff *n = skb_clone(skb, GFP_ATOMIC);
862 if (n)
863 pneigh_enqueue(&nd_tbl, idev->nd_parms, n);
864 goto out;
865 }
866 } else
867 goto out;
868 }
869
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900870 if (is_router < 0)
871 is_router = !!idev->cnf.forwarding;
Ville Nuorvala62dd9312006-09-22 14:43:19 -0700872
Linus Torvalds1da177e2005-04-16 15:20:36 -0700873 if (dad) {
YOSHIFUJI Hideakif3ee4012008-04-10 15:42:11 +0900874 ndisc_send_na(dev, NULL, &in6addr_linklocal_allnodes, &msg->target,
Ville Nuorvala62dd9312006-09-22 14:43:19 -0700875 is_router, 0, (ifp != NULL), 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700876 goto out;
877 }
878
879 if (inc)
880 NEIGH_CACHE_STAT_INC(&nd_tbl, rcv_probes_mcast);
881 else
882 NEIGH_CACHE_STAT_INC(&nd_tbl, rcv_probes_ucast);
883
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900884 /*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700885 * update / create cache entry
886 * for the source address
887 */
888 neigh = __neigh_lookup(&nd_tbl, saddr, dev,
889 !inc || lladdr || !dev->addr_len);
890 if (neigh)
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900891 neigh_update(neigh, lladdr, NUD_STALE,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700892 NEIGH_UPDATE_F_WEAK_OVERRIDE|
893 NEIGH_UPDATE_F_OVERRIDE);
Stephen Hemminger3b04ddd2007-10-09 01:40:57 -0700894 if (neigh || !dev->header_ops) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700895 ndisc_send_na(dev, neigh, saddr, &msg->target,
Ville Nuorvala62dd9312006-09-22 14:43:19 -0700896 is_router,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700897 1, (ifp != NULL && inc), inc);
898 if (neigh)
899 neigh_release(neigh);
900 }
901
902out:
903 if (ifp)
904 in6_ifa_put(ifp);
905 else
906 in6_dev_put(idev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700907}
908
909static void ndisc_recv_na(struct sk_buff *skb)
910{
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -0700911 struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb);
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000912 const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
913 const struct in6_addr *daddr = &ipv6_hdr(skb)->daddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700914 u8 *lladdr = NULL;
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -0700915 u32 ndoptlen = skb->tail - (skb->transport_header +
916 offsetof(struct nd_msg, opt));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700917 struct ndisc_options ndopts;
918 struct net_device *dev = skb->dev;
919 struct inet6_ifaddr *ifp;
920 struct neighbour *neigh;
921
922 if (skb->len < sizeof(struct nd_msg)) {
923 ND_PRINTK2(KERN_WARNING
924 "ICMPv6 NA: packet too short\n");
925 return;
926 }
927
928 if (ipv6_addr_is_multicast(&msg->target)) {
929 ND_PRINTK2(KERN_WARNING
930 "ICMPv6 NA: target address is multicast.\n");
931 return;
932 }
933
934 if (ipv6_addr_is_multicast(daddr) &&
935 msg->icmph.icmp6_solicited) {
936 ND_PRINTK2(KERN_WARNING
937 "ICMPv6 NA: solicited NA is multicasted.\n");
938 return;
939 }
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900940
Linus Torvalds1da177e2005-04-16 15:20:36 -0700941 if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts)) {
942 ND_PRINTK2(KERN_WARNING
943 "ICMPv6 NS: invalid ND option\n");
944 return;
945 }
946 if (ndopts.nd_opts_tgt_lladdr) {
947 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr, dev);
948 if (!lladdr) {
949 ND_PRINTK2(KERN_WARNING
950 "ICMPv6 NA: invalid link-layer address length\n");
951 return;
952 }
953 }
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900954 ifp = ipv6_get_ifaddr(dev_net(dev), &msg->target, dev, 1);
Daniel Lezcanoa18bc692008-03-07 11:14:49 -0800955 if (ifp) {
Daniel Walterbd015922011-04-13 21:09:25 +0000956 if (skb->pkt_type != PACKET_LOOPBACK
957 && (ifp->flags & IFA_F_TENTATIVE)) {
958 addrconf_dad_failure(ifp);
959 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700960 }
961 /* What should we make now? The advertisement
962 is invalid, but ndisc specs say nothing
963 about it. It could be misconfiguration, or
964 an smart proxy agent tries to help us :-)
Jan Sembera24fc7b82008-12-09 15:48:32 -0800965
966 We should not print the error if NA has been
967 received from loopback - it is just our own
968 unsolicited advertisement.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700969 */
Jan Sembera24fc7b82008-12-09 15:48:32 -0800970 if (skb->pkt_type != PACKET_LOOPBACK)
971 ND_PRINTK1(KERN_WARNING
Jens Rosenbooma6fa3282009-08-12 22:16:04 +0000972 "ICMPv6 NA: someone advertises our address %pI6 on %s!\n",
973 &ifp->addr, ifp->idev->dev->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700974 in6_ifa_put(ifp);
975 return;
976 }
977 neigh = neigh_lookup(&nd_tbl, &msg->target, dev);
978
979 if (neigh) {
980 u8 old_flags = neigh->flags;
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700981 struct net *net = dev_net(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700982
983 if (neigh->nud_state & NUD_FAILED)
984 goto out;
985
Ville Nuorvala5f3e6e92006-09-22 14:42:46 -0700986 /*
987 * Don't update the neighbor cache entry on a proxy NA from
988 * ourselves because either the proxied node is off link or it
989 * has already sent a NA to us.
990 */
991 if (lladdr && !memcmp(lladdr, dev->dev_addr, dev->addr_len) &&
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700992 net->ipv6.devconf_all->forwarding && net->ipv6.devconf_all->proxy_ndp &&
993 pneigh_lookup(&nd_tbl, net, &msg->target, dev, 0)) {
YOSHIFUJI Hideakifbea49e2006-09-22 14:43:49 -0700994 /* XXX: idev->cnf.prixy_ndp */
Ville Nuorvala5f3e6e92006-09-22 14:42:46 -0700995 goto out;
YOSHIFUJI Hideakifbea49e2006-09-22 14:43:49 -0700996 }
Ville Nuorvala5f3e6e92006-09-22 14:42:46 -0700997
Linus Torvalds1da177e2005-04-16 15:20:36 -0700998 neigh_update(neigh, lladdr,
999 msg->icmph.icmp6_solicited ? NUD_REACHABLE : NUD_STALE,
1000 NEIGH_UPDATE_F_WEAK_OVERRIDE|
1001 (msg->icmph.icmp6_override ? NEIGH_UPDATE_F_OVERRIDE : 0)|
1002 NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
1003 (msg->icmph.icmp6_router ? NEIGH_UPDATE_F_ISROUTER : 0));
1004
1005 if ((old_flags & ~neigh->flags) & NTF_ROUTER) {
1006 /*
1007 * Change: router to host
1008 */
1009 struct rt6_info *rt;
1010 rt = rt6_get_dflt_router(saddr, dev);
1011 if (rt)
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001012 ip6_del_rt(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001013 }
1014
1015out:
1016 neigh_release(neigh);
1017 }
1018}
1019
1020static void ndisc_recv_rs(struct sk_buff *skb)
1021{
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -07001022 struct rs_msg *rs_msg = (struct rs_msg *)skb_transport_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001023 unsigned long ndoptlen = skb->len - sizeof(*rs_msg);
1024 struct neighbour *neigh;
1025 struct inet6_dev *idev;
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001026 const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001027 struct ndisc_options ndopts;
1028 u8 *lladdr = NULL;
1029
1030 if (skb->len < sizeof(*rs_msg))
1031 return;
1032
Eric Dumazetcfdf7642011-07-27 21:13:03 +00001033 idev = __in6_dev_get(skb->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001034 if (!idev) {
1035 if (net_ratelimit())
1036 ND_PRINTK1("ICMP6 RS: can't find in6 device\n");
1037 return;
1038 }
1039
1040 /* Don't accept RS if we're not in router mode */
1041 if (!idev->cnf.forwarding)
1042 goto out;
1043
1044 /*
1045 * Don't update NCE if src = ::;
1046 * this implies that the source node has no ip address assigned yet.
1047 */
1048 if (ipv6_addr_any(saddr))
1049 goto out;
1050
1051 /* Parse ND options */
1052 if (!ndisc_parse_options(rs_msg->opt, ndoptlen, &ndopts)) {
1053 if (net_ratelimit())
1054 ND_PRINTK2("ICMP6 NS: invalid ND option, ignored\n");
1055 goto out;
1056 }
1057
1058 if (ndopts.nd_opts_src_lladdr) {
1059 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr,
1060 skb->dev);
1061 if (!lladdr)
1062 goto out;
1063 }
1064
1065 neigh = __neigh_lookup(&nd_tbl, saddr, skb->dev, 1);
1066 if (neigh) {
1067 neigh_update(neigh, lladdr, NUD_STALE,
1068 NEIGH_UPDATE_F_WEAK_OVERRIDE|
1069 NEIGH_UPDATE_F_OVERRIDE|
1070 NEIGH_UPDATE_F_OVERRIDE_ISROUTER);
1071 neigh_release(neigh);
1072 }
1073out:
Eric Dumazetcfdf7642011-07-27 21:13:03 +00001074 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001075}
1076
Pierre Ynard31910572007-10-10 21:22:05 -07001077static void ndisc_ra_useropt(struct sk_buff *ra, struct nd_opt_hdr *opt)
1078{
1079 struct icmp6hdr *icmp6h = (struct icmp6hdr *)skb_transport_header(ra);
1080 struct sk_buff *skb;
1081 struct nlmsghdr *nlh;
1082 struct nduseroptmsg *ndmsg;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001083 struct net *net = dev_net(ra->dev);
Pierre Ynard31910572007-10-10 21:22:05 -07001084 int err;
1085 int base_size = NLMSG_ALIGN(sizeof(struct nduseroptmsg)
1086 + (opt->nd_opt_len << 3));
1087 size_t msg_size = base_size + nla_total_size(sizeof(struct in6_addr));
1088
1089 skb = nlmsg_new(msg_size, GFP_ATOMIC);
1090 if (skb == NULL) {
1091 err = -ENOBUFS;
1092 goto errout;
1093 }
1094
1095 nlh = nlmsg_put(skb, 0, 0, RTM_NEWNDUSEROPT, base_size, 0);
1096 if (nlh == NULL) {
1097 goto nla_put_failure;
1098 }
1099
1100 ndmsg = nlmsg_data(nlh);
1101 ndmsg->nduseropt_family = AF_INET6;
Pierre Ynarddbb2ed22007-11-12 17:58:35 -08001102 ndmsg->nduseropt_ifindex = ra->dev->ifindex;
Pierre Ynard31910572007-10-10 21:22:05 -07001103 ndmsg->nduseropt_icmp_type = icmp6h->icmp6_type;
1104 ndmsg->nduseropt_icmp_code = icmp6h->icmp6_code;
1105 ndmsg->nduseropt_opts_len = opt->nd_opt_len << 3;
1106
1107 memcpy(ndmsg + 1, opt, opt->nd_opt_len << 3);
1108
1109 NLA_PUT(skb, NDUSEROPT_SRCADDR, sizeof(struct in6_addr),
1110 &ipv6_hdr(ra)->saddr);
1111 nlmsg_end(skb, nlh);
1112
Pablo Neira Ayuso1ce85fe2009-02-24 23:18:28 -08001113 rtnl_notify(skb, net, 0, RTNLGRP_ND_USEROPT, NULL, GFP_ATOMIC);
Pierre Ynard31910572007-10-10 21:22:05 -07001114 return;
1115
1116nla_put_failure:
1117 nlmsg_free(skb);
1118 err = -EMSGSIZE;
1119errout:
Daniel Lezcanoa18bc692008-03-07 11:14:49 -08001120 rtnl_set_sk_err(net, RTNLGRP_ND_USEROPT, err);
Pierre Ynard31910572007-10-10 21:22:05 -07001121}
1122
Thomas Graf65e9b622010-09-03 02:59:14 +00001123static inline int accept_ra(struct inet6_dev *in6_dev)
1124{
1125 /*
1126 * If forwarding is enabled, RA are not accepted unless the special
1127 * hybrid mode (accept_ra=2) is enabled.
1128 */
1129 if (in6_dev->cnf.forwarding && in6_dev->cnf.accept_ra < 2)
1130 return 0;
1131
1132 return in6_dev->cnf.accept_ra;
1133}
1134
Linus Torvalds1da177e2005-04-16 15:20:36 -07001135static void ndisc_router_discovery(struct sk_buff *skb)
1136{
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -07001137 struct ra_msg *ra_msg = (struct ra_msg *)skb_transport_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001138 struct neighbour *neigh = NULL;
1139 struct inet6_dev *in6_dev;
YOSHIFUJI Hideaki65f5c7c2006-03-20 16:55:08 -08001140 struct rt6_info *rt = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001141 int lifetime;
1142 struct ndisc_options ndopts;
1143 int optlen;
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001144 unsigned int pref = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001145
1146 __u8 * opt = (__u8 *)(ra_msg + 1);
1147
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -07001148 optlen = (skb->tail - skb->transport_header) - sizeof(struct ra_msg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001149
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001150 if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001151 ND_PRINTK2(KERN_WARNING
1152 "ICMPv6 RA: source address is not link-local.\n");
1153 return;
1154 }
1155 if (optlen < 0) {
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001156 ND_PRINTK2(KERN_WARNING
Linus Torvalds1da177e2005-04-16 15:20:36 -07001157 "ICMPv6 RA: packet too short\n");
1158 return;
1159 }
1160
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001161#ifdef CONFIG_IPV6_NDISC_NODETYPE
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001162 if (skb->ndisc_nodetype == NDISC_NODETYPE_HOST) {
1163 ND_PRINTK2(KERN_WARNING
1164 "ICMPv6 RA: from host or unauthorized router\n");
1165 return;
1166 }
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001167#endif
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001168
Linus Torvalds1da177e2005-04-16 15:20:36 -07001169 /*
1170 * set the RA_RECV flag in the interface
1171 */
1172
Eric Dumazetcfdf7642011-07-27 21:13:03 +00001173 in6_dev = __in6_dev_get(skb->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001174 if (in6_dev == NULL) {
1175 ND_PRINTK0(KERN_ERR
1176 "ICMPv6 RA: can't find inet6 device for %s.\n",
1177 skb->dev->name);
1178 return;
1179 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001180
1181 if (!ndisc_parse_options(opt, optlen, &ndopts)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001182 ND_PRINTK2(KERN_WARNING
1183 "ICMP6 RA: invalid ND options\n");
1184 return;
1185 }
1186
Thomas Graf65e9b622010-09-03 02:59:14 +00001187 if (!accept_ra(in6_dev))
David Ward31ce8c72009-08-29 00:04:09 -07001188 goto skip_linkparms;
1189
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001190#ifdef CONFIG_IPV6_NDISC_NODETYPE
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001191 /* skip link-specific parameters from interior routers */
1192 if (skb->ndisc_nodetype == NDISC_NODETYPE_NODEFAULT)
1193 goto skip_linkparms;
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001194#endif
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001195
Linus Torvalds1da177e2005-04-16 15:20:36 -07001196 if (in6_dev->if_flags & IF_RS_SENT) {
1197 /*
1198 * flag that an RA was received after an RS was sent
1199 * out on this interface.
1200 */
1201 in6_dev->if_flags |= IF_RA_RCVD;
1202 }
1203
1204 /*
1205 * Remember the managed/otherconf flags from most recently
1206 * received RA message (RFC 2462) -- yoshfuji
1207 */
1208 in6_dev->if_flags = (in6_dev->if_flags & ~(IF_RA_MANAGED |
1209 IF_RA_OTHERCONF)) |
1210 (ra_msg->icmph.icmp6_addrconf_managed ?
1211 IF_RA_MANAGED : 0) |
1212 (ra_msg->icmph.icmp6_addrconf_other ?
1213 IF_RA_OTHERCONF : 0);
1214
YOSHIFUJI Hideaki65f5c7c2006-03-20 16:55:08 -08001215 if (!in6_dev->cnf.accept_ra_defrtr)
1216 goto skip_defrtr;
1217
Andreas Hofmeister9f562202011-10-24 19:13:15 -04001218 if (ipv6_chk_addr(dev_net(in6_dev->dev), &ipv6_hdr(skb)->saddr, NULL, 0))
1219 goto skip_defrtr;
1220
Linus Torvalds1da177e2005-04-16 15:20:36 -07001221 lifetime = ntohs(ra_msg->icmph.icmp6_rt_lifetime);
1222
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001223#ifdef CONFIG_IPV6_ROUTER_PREF
1224 pref = ra_msg->icmph.icmp6_router_pref;
1225 /* 10b is handled as if it were 00b (medium) */
YOSHIFUJI Hideaki930d6ff2006-03-20 17:05:30 -08001226 if (pref == ICMPV6_ROUTER_PREF_INVALID ||
YOSHIFUJI Hideaki6d5b78c2007-06-22 16:07:04 -07001227 !in6_dev->cnf.accept_ra_rtr_pref)
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001228 pref = ICMPV6_ROUTER_PREF_MEDIUM;
1229#endif
1230
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001231 rt = rt6_get_dflt_router(&ipv6_hdr(skb)->saddr, skb->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001232
1233 if (rt)
David Miller27217452011-12-02 16:52:08 +00001234 neigh = dst_get_neighbour_noref(&rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001235
1236 if (rt && lifetime == 0) {
1237 neigh_clone(neigh);
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001238 ip6_del_rt(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001239 rt = NULL;
1240 }
1241
1242 if (rt == NULL && lifetime) {
1243 ND_PRINTK3(KERN_DEBUG
1244 "ICMPv6 RA: adding default router.\n");
1245
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001246 rt = rt6_add_dflt_router(&ipv6_hdr(skb)->saddr, skb->dev, pref);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001247 if (rt == NULL) {
1248 ND_PRINTK0(KERN_ERR
1249 "ICMPv6 RA: %s() failed to add default route.\n",
Harvey Harrison0dc47872008-03-05 20:47:47 -08001250 __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001251 return;
1252 }
1253
David Miller27217452011-12-02 16:52:08 +00001254 neigh = dst_get_neighbour_noref(&rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001255 if (neigh == NULL) {
1256 ND_PRINTK0(KERN_ERR
1257 "ICMPv6 RA: %s() got default router without neighbour.\n",
Harvey Harrison0dc47872008-03-05 20:47:47 -08001258 __func__);
Changli Gaod8d1f302010-06-10 23:31:35 -07001259 dst_release(&rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001260 return;
1261 }
1262 neigh->flags |= NTF_ROUTER;
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001263 } else if (rt) {
Pedro Ribeiro22441cf2008-10-15 15:47:49 -07001264 rt->rt6i_flags = (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001265 }
1266
1267 if (rt)
1268 rt->rt6i_expires = jiffies + (HZ * lifetime);
1269
1270 if (ra_msg->icmph.icmp6_hop_limit) {
1271 in6_dev->cnf.hop_limit = ra_msg->icmph.icmp6_hop_limit;
1272 if (rt)
David S. Millerdefb3512010-12-08 21:16:57 -08001273 dst_metric_set(&rt->dst, RTAX_HOPLIMIT,
1274 ra_msg->icmph.icmp6_hop_limit);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001275 }
1276
YOSHIFUJI Hideaki65f5c7c2006-03-20 16:55:08 -08001277skip_defrtr:
1278
Linus Torvalds1da177e2005-04-16 15:20:36 -07001279 /*
1280 * Update Reachable Time and Retrans Timer
1281 */
1282
1283 if (in6_dev->nd_parms) {
1284 unsigned long rtime = ntohl(ra_msg->retrans_timer);
1285
1286 if (rtime && rtime/1000 < MAX_SCHEDULE_TIMEOUT/HZ) {
1287 rtime = (rtime*HZ)/1000;
1288 if (rtime < HZ/10)
1289 rtime = HZ/10;
1290 in6_dev->nd_parms->retrans_time = rtime;
1291 in6_dev->tstamp = jiffies;
1292 inet6_ifinfo_notify(RTM_NEWLINK, in6_dev);
1293 }
1294
1295 rtime = ntohl(ra_msg->reachable_time);
1296 if (rtime && rtime/1000 < MAX_SCHEDULE_TIMEOUT/(3*HZ)) {
1297 rtime = (rtime*HZ)/1000;
1298
1299 if (rtime < HZ/10)
1300 rtime = HZ/10;
1301
1302 if (rtime != in6_dev->nd_parms->base_reachable_time) {
1303 in6_dev->nd_parms->base_reachable_time = rtime;
1304 in6_dev->nd_parms->gc_staletime = 3 * rtime;
1305 in6_dev->nd_parms->reachable_time = neigh_rand_reach_time(rtime);
1306 in6_dev->tstamp = jiffies;
1307 inet6_ifinfo_notify(RTM_NEWLINK, in6_dev);
1308 }
1309 }
1310 }
1311
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001312skip_linkparms:
1313
Linus Torvalds1da177e2005-04-16 15:20:36 -07001314 /*
1315 * Process options.
1316 */
1317
1318 if (!neigh)
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001319 neigh = __neigh_lookup(&nd_tbl, &ipv6_hdr(skb)->saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001320 skb->dev, 1);
1321 if (neigh) {
1322 u8 *lladdr = NULL;
1323 if (ndopts.nd_opts_src_lladdr) {
1324 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr,
1325 skb->dev);
1326 if (!lladdr) {
1327 ND_PRINTK2(KERN_WARNING
1328 "ICMPv6 RA: invalid link-layer address length\n");
1329 goto out;
1330 }
1331 }
1332 neigh_update(neigh, lladdr, NUD_STALE,
1333 NEIGH_UPDATE_F_WEAK_OVERRIDE|
1334 NEIGH_UPDATE_F_OVERRIDE|
1335 NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
1336 NEIGH_UPDATE_F_ISROUTER);
1337 }
1338
Thomas Graf65e9b622010-09-03 02:59:14 +00001339 if (!accept_ra(in6_dev))
David Ward31ce8c72009-08-29 00:04:09 -07001340 goto out;
1341
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001342#ifdef CONFIG_IPV6_ROUTE_INFO
Andreas Hofmeister9f562202011-10-24 19:13:15 -04001343 if (ipv6_chk_addr(dev_net(in6_dev->dev), &ipv6_hdr(skb)->saddr, NULL, 0))
1344 goto skip_routeinfo;
1345
YOSHIFUJI Hideaki09c884d2006-03-20 17:07:03 -08001346 if (in6_dev->cnf.accept_ra_rtr_pref && ndopts.nd_opts_ri) {
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001347 struct nd_opt_hdr *p;
1348 for (p = ndopts.nd_opts_ri;
1349 p;
1350 p = ndisc_next_option(p, ndopts.nd_opts_ri_end)) {
YOSHIFUJI Hideaki6294e002008-03-15 23:56:52 -04001351 struct route_info *ri = (struct route_info *)p;
1352#ifdef CONFIG_IPV6_NDISC_NODETYPE
1353 if (skb->ndisc_nodetype == NDISC_NODETYPE_NODEFAULT &&
1354 ri->prefix_len == 0)
1355 continue;
1356#endif
1357 if (ri->prefix_len > in6_dev->cnf.accept_ra_rt_info_max_plen)
YOSHIFUJI Hideaki09c884d2006-03-20 17:07:03 -08001358 continue;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001359 rt6_route_rcv(skb->dev, (u8*)p, (p->nd_opt_len) << 3,
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001360 &ipv6_hdr(skb)->saddr);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001361 }
1362 }
Andreas Hofmeister9f562202011-10-24 19:13:15 -04001363
1364skip_routeinfo:
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001365#endif
1366
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001367#ifdef CONFIG_IPV6_NDISC_NODETYPE
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001368 /* skip link-specific ndopts from interior routers */
1369 if (skb->ndisc_nodetype == NDISC_NODETYPE_NODEFAULT)
1370 goto out;
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001371#endif
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001372
YOSHIFUJI Hideakic4fd30e2006-03-20 16:55:26 -08001373 if (in6_dev->cnf.accept_ra_pinfo && ndopts.nd_opts_pi) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001374 struct nd_opt_hdr *p;
1375 for (p = ndopts.nd_opts_pi;
1376 p;
1377 p = ndisc_next_option(p, ndopts.nd_opts_pi_end)) {
1378 addrconf_prefix_rcv(skb->dev, (u8*)p, (p->nd_opt_len) << 3);
1379 }
1380 }
1381
1382 if (ndopts.nd_opts_mtu) {
Al Viroe69a4adc2006-11-14 20:56:00 -08001383 __be32 n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001384 u32 mtu;
1385
Al Viroe69a4adc2006-11-14 20:56:00 -08001386 memcpy(&n, ((u8*)(ndopts.nd_opts_mtu+1))+2, sizeof(mtu));
1387 mtu = ntohl(n);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001388
1389 if (mtu < IPV6_MIN_MTU || mtu > skb->dev->mtu) {
1390 ND_PRINTK2(KERN_WARNING
1391 "ICMPv6 RA: invalid mtu: %d\n",
1392 mtu);
1393 } else if (in6_dev->cnf.mtu6 != mtu) {
1394 in6_dev->cnf.mtu6 = mtu;
1395
1396 if (rt)
David S. Millerdefb3512010-12-08 21:16:57 -08001397 dst_metric_set(&rt->dst, RTAX_MTU, mtu);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001398
1399 rt6_mtu_change(skb->dev, mtu);
1400 }
1401 }
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001402
Pierre Ynard31910572007-10-10 21:22:05 -07001403 if (ndopts.nd_useropts) {
YOSHIFUJI Hideaki61cf46ad2008-01-22 17:32:53 +09001404 struct nd_opt_hdr *p;
1405 for (p = ndopts.nd_useropts;
1406 p;
1407 p = ndisc_next_useropt(p, ndopts.nd_useropts_end)) {
1408 ndisc_ra_useropt(skb, p);
Pierre Ynard31910572007-10-10 21:22:05 -07001409 }
1410 }
1411
Linus Torvalds1da177e2005-04-16 15:20:36 -07001412 if (ndopts.nd_opts_tgt_lladdr || ndopts.nd_opts_rh) {
1413 ND_PRINTK2(KERN_WARNING
1414 "ICMPv6 RA: invalid RA options");
1415 }
1416out:
1417 if (rt)
Changli Gaod8d1f302010-06-10 23:31:35 -07001418 dst_release(&rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001419 else if (neigh)
1420 neigh_release(neigh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001421}
1422
1423static void ndisc_redirect_rcv(struct sk_buff *skb)
1424{
1425 struct inet6_dev *in6_dev;
1426 struct icmp6hdr *icmph;
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001427 const struct in6_addr *dest;
1428 const struct in6_addr *target; /* new first hop to destination */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001429 struct neighbour *neigh;
1430 int on_link = 0;
1431 struct ndisc_options ndopts;
1432 int optlen;
1433 u8 *lladdr = NULL;
1434
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001435#ifdef CONFIG_IPV6_NDISC_NODETYPE
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001436 switch (skb->ndisc_nodetype) {
1437 case NDISC_NODETYPE_HOST:
1438 case NDISC_NODETYPE_NODEFAULT:
1439 ND_PRINTK2(KERN_WARNING
1440 "ICMPv6 Redirect: from host or unauthorized router\n");
1441 return;
1442 }
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001443#endif
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001444
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001445 if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001446 ND_PRINTK2(KERN_WARNING
1447 "ICMPv6 Redirect: source address is not link-local.\n");
1448 return;
1449 }
1450
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -07001451 optlen = skb->tail - skb->transport_header;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001452 optlen -= sizeof(struct icmp6hdr) + 2 * sizeof(struct in6_addr);
1453
1454 if (optlen < 0) {
1455 ND_PRINTK2(KERN_WARNING
1456 "ICMPv6 Redirect: packet too short\n");
1457 return;
1458 }
1459
Arnaldo Carvalho de Melocc70ab22007-03-13 14:03:22 -03001460 icmph = icmp6_hdr(skb);
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001461 target = (const struct in6_addr *) (icmph + 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001462 dest = target + 1;
1463
1464 if (ipv6_addr_is_multicast(dest)) {
1465 ND_PRINTK2(KERN_WARNING
1466 "ICMPv6 Redirect: destination address is multicast.\n");
1467 return;
1468 }
1469
1470 if (ipv6_addr_equal(dest, target)) {
1471 on_link = 1;
Brian Haleybf0b48d2007-10-08 00:12:05 -07001472 } else if (ipv6_addr_type(target) !=
1473 (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) {
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001474 ND_PRINTK2(KERN_WARNING
Brian Haleybf0b48d2007-10-08 00:12:05 -07001475 "ICMPv6 Redirect: target address is not link-local unicast.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001476 return;
1477 }
1478
Eric Dumazetcfdf7642011-07-27 21:13:03 +00001479 in6_dev = __in6_dev_get(skb->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001480 if (!in6_dev)
1481 return;
Eric Dumazetcfdf7642011-07-27 21:13:03 +00001482 if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001483 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001484
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001485 /* RFC2461 8.1:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001486 * The IP source address of the Redirect MUST be the same as the current
1487 * first-hop router for the specified ICMP Destination Address.
1488 */
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001489
Linus Torvalds1da177e2005-04-16 15:20:36 -07001490 if (!ndisc_parse_options((u8*)(dest + 1), optlen, &ndopts)) {
1491 ND_PRINTK2(KERN_WARNING
1492 "ICMPv6 Redirect: invalid ND options\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001493 return;
1494 }
1495 if (ndopts.nd_opts_tgt_lladdr) {
1496 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr,
1497 skb->dev);
1498 if (!lladdr) {
1499 ND_PRINTK2(KERN_WARNING
1500 "ICMPv6 Redirect: invalid link-layer address length\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001501 return;
1502 }
1503 }
1504
1505 neigh = __neigh_lookup(&nd_tbl, target, skb->dev, 1);
1506 if (neigh) {
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001507 rt6_redirect(dest, &ipv6_hdr(skb)->daddr,
1508 &ipv6_hdr(skb)->saddr, neigh, lladdr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001509 on_link);
1510 neigh_release(neigh);
1511 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001512}
1513
1514void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +09001515 const struct in6_addr *target)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001516{
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001517 struct net_device *dev = skb->dev;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001518 struct net *net = dev_net(dev);
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001519 struct sock *sk = net->ipv6.ndisc_sk;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001520 int len = sizeof(struct icmp6hdr) + 2 * sizeof(struct in6_addr);
1521 struct sk_buff *buff;
1522 struct icmp6hdr *icmph;
1523 struct in6_addr saddr_buf;
1524 struct in6_addr *addrp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001525 struct rt6_info *rt;
1526 struct dst_entry *dst;
1527 struct inet6_dev *idev;
David S. Miller4c9483b2011-03-12 16:22:43 -05001528 struct flowi6 fl6;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001529 u8 *opt;
Herbert Xua7ae1992011-11-18 02:20:04 +00001530 int hlen, tlen;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001531 int rd_len;
1532 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001533 u8 ha_buf[MAX_ADDR_LEN], *ha = NULL;
1534
Neil Horman95c385b2007-04-25 17:08:10 -07001535 if (ipv6_get_lladdr(dev, &saddr_buf, IFA_F_TENTATIVE)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001536 ND_PRINTK2(KERN_WARNING
1537 "ICMPv6 Redirect: no link-local address on %s\n",
1538 dev->name);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001539 return;
1540 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001541
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001542 if (!ipv6_addr_equal(&ipv6_hdr(skb)->daddr, target) &&
Brian Haleybf0b48d2007-10-08 00:12:05 -07001543 ipv6_addr_type(target) != (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) {
Li Yewang29556522007-01-30 14:33:20 -08001544 ND_PRINTK2(KERN_WARNING
Brian Haleybf0b48d2007-10-08 00:12:05 -07001545 "ICMPv6 Redirect: target address is not link-local unicast.\n");
Li Yewang29556522007-01-30 14:33:20 -08001546 return;
1547 }
1548
David S. Miller4c9483b2011-03-12 16:22:43 -05001549 icmpv6_flow_init(sk, &fl6, NDISC_REDIRECT,
YOSHIFUJI Hideaki95e41e92007-12-06 15:43:30 -08001550 &saddr_buf, &ipv6_hdr(skb)->saddr, dev->ifindex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001551
David S. Miller4c9483b2011-03-12 16:22:43 -05001552 dst = ip6_route_output(net, NULL, &fl6);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001553 if (dst == NULL)
1554 return;
1555
David S. Miller4c9483b2011-03-12 16:22:43 -05001556 dst = xfrm_lookup(net, dst, flowi6_to_flowi(&fl6), NULL, 0);
David S. Miller452edd52011-03-02 13:27:41 -08001557 if (IS_ERR(dst))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001558 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001559
1560 rt = (struct rt6_info *) dst;
1561
1562 if (rt->rt6i_flags & RTF_GATEWAY) {
1563 ND_PRINTK2(KERN_WARNING
1564 "ICMPv6 Redirect: destination is not a neighbour.\n");
Ilpo Järvinend73f0802009-02-06 23:47:37 -08001565 goto release;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001566 }
David S. Miller92d86822011-02-04 15:55:25 -08001567 if (!rt->rt6i_peer)
1568 rt6_bind_peer(rt, 1);
Li Wei4d65a242011-11-23 03:51:54 -05001569 if (!inet_peer_xrlim_allow(rt->rt6i_peer, 1*HZ))
Ilpo Järvinend73f0802009-02-06 23:47:37 -08001570 goto release;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001571
1572 if (dev->addr_len) {
1573 read_lock_bh(&neigh->lock);
1574 if (neigh->nud_state & NUD_VALID) {
1575 memcpy(ha_buf, neigh->ha, dev->addr_len);
1576 read_unlock_bh(&neigh->lock);
1577 ha = ha_buf;
1578 len += ndisc_opt_addr_space(dev);
1579 } else
1580 read_unlock_bh(&neigh->lock);
1581 }
1582
1583 rd_len = min_t(unsigned int,
1584 IPV6_MIN_MTU-sizeof(struct ipv6hdr)-len, skb->len + 8);
1585 rd_len &= ~0x7;
1586 len += rd_len;
1587
Herbert Xua7ae1992011-11-18 02:20:04 +00001588 hlen = LL_RESERVED_SPACE(dev);
1589 tlen = dev->needed_tailroom;
David S. Millerd54a81d2006-12-02 21:00:06 -08001590 buff = sock_alloc_send_skb(sk,
1591 (MAX_HEADER + sizeof(struct ipv6hdr) +
Herbert Xua7ae1992011-11-18 02:20:04 +00001592 len + hlen + tlen),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001593 1, &err);
1594 if (buff == NULL) {
1595 ND_PRINTK0(KERN_ERR
Brian Haleydae9de82009-06-02 00:20:26 -07001596 "ICMPv6 Redirect: %s() failed to allocate an skb, err=%d.\n",
1597 __func__, err);
Ilpo Järvinend73f0802009-02-06 23:47:37 -08001598 goto release;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001599 }
1600
Herbert Xua7ae1992011-11-18 02:20:04 +00001601 skb_reserve(buff, hlen);
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001602 ip6_nd_hdr(sk, buff, dev, &saddr_buf, &ipv6_hdr(skb)->saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001603 IPPROTO_ICMPV6, len);
1604
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -07001605 skb_set_transport_header(buff, skb_tail_pointer(buff) - buff->data);
Arnaldo Carvalho de Melod10ba342007-03-14 21:05:37 -03001606 skb_put(buff, len);
1607 icmph = icmp6_hdr(buff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001608
1609 memset(icmph, 0, sizeof(struct icmp6hdr));
1610 icmph->icmp6_type = NDISC_REDIRECT;
1611
1612 /*
1613 * copy target and destination addresses
1614 */
1615
1616 addrp = (struct in6_addr *)(icmph + 1);
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001617 *addrp = *target;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001618 addrp++;
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001619 *addrp = ipv6_hdr(skb)->daddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001620
1621 opt = (u8*) (addrp + 1);
1622
1623 /*
1624 * include target_address option
1625 */
1626
1627 if (ha)
1628 opt = ndisc_fill_addr_option(opt, ND_OPT_TARGET_LL_ADDR, ha,
1629 dev->addr_len, dev->type);
1630
1631 /*
1632 * build redirect option and copy skb over to the new packet.
1633 */
1634
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001635 memset(opt, 0, 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001636 *(opt++) = ND_OPT_REDIRECT_HDR;
1637 *(opt++) = (rd_len >> 3);
1638 opt += 6;
1639
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001640 memcpy(opt, ipv6_hdr(skb), rd_len - 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001641
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001642 icmph->icmp6_cksum = csum_ipv6_magic(&saddr_buf, &ipv6_hdr(skb)->saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001643 len, IPPROTO_ICMPV6,
Joe Perches07f07572008-11-19 15:44:53 -08001644 csum_partial(icmph, len, 0));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001645
Eric Dumazetadf30902009-06-02 05:19:30 +00001646 skb_dst_set(buff, dst);
Eric Dumazetcfdf7642011-07-27 21:13:03 +00001647 rcu_read_lock();
1648 idev = __in6_dev_get(dst->dev);
Neil Hormanedf391f2009-04-27 02:45:02 -07001649 IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUT, skb->len);
Jan Engelhardtb2e0b382010-03-23 04:09:07 +01001650 err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, buff, NULL, dst->dev,
Patrick McHardy6e23ae22007-11-19 18:53:30 -08001651 dst_output);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001652 if (!err) {
Denis V. Lunev5c5d2442008-10-08 10:33:50 -07001653 ICMP6MSGOUT_INC_STATS(net, idev, NDISC_REDIRECT);
Denis V. Luneva862f6a2008-10-08 10:33:06 -07001654 ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001655 }
1656
Eric Dumazetcfdf7642011-07-27 21:13:03 +00001657 rcu_read_unlock();
Ilpo Järvinend73f0802009-02-06 23:47:37 -08001658 return;
1659
1660release:
1661 dst_release(dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001662}
1663
1664static void pndisc_redo(struct sk_buff *skb)
1665{
YOSHIFUJI Hideaki140e26fc2005-10-05 12:11:41 -07001666 ndisc_recv_ns(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001667 kfree_skb(skb);
1668}
1669
1670int ndisc_rcv(struct sk_buff *skb)
1671{
1672 struct nd_msg *msg;
1673
1674 if (!pskb_may_pull(skb, skb->len))
1675 return 0;
1676
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -07001677 msg = (struct nd_msg *)skb_transport_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001678
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -07001679 __skb_push(skb, skb->data - skb_transport_header(skb));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001680
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001681 if (ipv6_hdr(skb)->hop_limit != 255) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001682 ND_PRINTK2(KERN_WARNING
1683 "ICMPv6 NDISC: invalid hop-limit: %d\n",
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001684 ipv6_hdr(skb)->hop_limit);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001685 return 0;
1686 }
1687
1688 if (msg->icmph.icmp6_code != 0) {
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001689 ND_PRINTK2(KERN_WARNING
Linus Torvalds1da177e2005-04-16 15:20:36 -07001690 "ICMPv6 NDISC: invalid ICMPv6 code: %d\n",
1691 msg->icmph.icmp6_code);
1692 return 0;
1693 }
1694
Patrick McHardya61bbcf2005-08-14 17:24:31 -07001695 memset(NEIGH_CB(skb), 0, sizeof(struct neighbour_cb));
1696
Linus Torvalds1da177e2005-04-16 15:20:36 -07001697 switch (msg->icmph.icmp6_type) {
1698 case NDISC_NEIGHBOUR_SOLICITATION:
1699 ndisc_recv_ns(skb);
1700 break;
1701
1702 case NDISC_NEIGHBOUR_ADVERTISEMENT:
1703 ndisc_recv_na(skb);
1704 break;
1705
1706 case NDISC_ROUTER_SOLICITATION:
1707 ndisc_recv_rs(skb);
1708 break;
1709
1710 case NDISC_ROUTER_ADVERTISEMENT:
1711 ndisc_router_discovery(skb);
1712 break;
1713
1714 case NDISC_REDIRECT:
1715 ndisc_redirect_rcv(skb);
1716 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -07001717 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001718
1719 return 0;
1720}
1721
1722static int ndisc_netdev_event(struct notifier_block *this, unsigned long event, void *ptr)
1723{
1724 struct net_device *dev = ptr;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001725 struct net *net = dev_net(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001726
1727 switch (event) {
1728 case NETDEV_CHANGEADDR:
1729 neigh_changeaddr(&nd_tbl, dev);
Daniel Lezcano5b7c9312008-03-03 23:28:58 -08001730 fib6_run_gc(~0UL, net);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001731 break;
1732 case NETDEV_DOWN:
1733 neigh_ifdown(&nd_tbl, dev);
Daniel Lezcano5b7c9312008-03-03 23:28:58 -08001734 fib6_run_gc(~0UL, net);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001735 break;
Ben Hutchingsf47b9462011-04-15 13:46:02 +00001736 case NETDEV_NOTIFY_PEERS:
1737 ndisc_send_unsol_na(dev);
1738 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001739 default:
1740 break;
1741 }
1742
1743 return NOTIFY_DONE;
1744}
1745
1746static struct notifier_block ndisc_netdev_notifier = {
1747 .notifier_call = ndisc_netdev_event,
1748};
1749
1750#ifdef CONFIG_SYSCTL
1751static void ndisc_warn_deprecated_sysctl(struct ctl_table *ctl,
1752 const char *func, const char *dev_name)
1753{
1754 static char warncomm[TASK_COMM_LEN];
1755 static int warned;
1756 if (strcmp(warncomm, current->comm) && warned < 5) {
1757 strcpy(warncomm, current->comm);
1758 printk(KERN_WARNING
1759 "process `%s' is using deprecated sysctl (%s) "
1760 "net.ipv6.neigh.%s.%s; "
1761 "Use net.ipv6.neigh.%s.%s_ms "
1762 "instead.\n",
1763 warncomm, func,
1764 dev_name, ctl->procname,
1765 dev_name, ctl->procname);
1766 warned++;
1767 }
1768}
1769
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001770int 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 -07001771{
1772 struct net_device *dev = ctl->extra1;
1773 struct inet6_dev *idev;
1774 int ret;
1775
Eric W. Biedermand12af672007-10-18 03:05:25 -07001776 if ((strcmp(ctl->procname, "retrans_time") == 0) ||
1777 (strcmp(ctl->procname, "base_reachable_time") == 0))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001778 ndisc_warn_deprecated_sysctl(ctl, "syscall", dev ? dev->name : "default");
1779
Eric W. Biedermand12af672007-10-18 03:05:25 -07001780 if (strcmp(ctl->procname, "retrans_time") == 0)
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001781 ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
Eric W. Biedermand12af672007-10-18 03:05:25 -07001782
1783 else if (strcmp(ctl->procname, "base_reachable_time") == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001784 ret = proc_dointvec_jiffies(ctl, write,
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001785 buffer, lenp, ppos);
Eric W. Biedermand12af672007-10-18 03:05:25 -07001786
1787 else if ((strcmp(ctl->procname, "retrans_time_ms") == 0) ||
YOSHIFUJI Hideakiad02ac12007-10-29 01:32:23 -07001788 (strcmp(ctl->procname, "base_reachable_time_ms") == 0))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001789 ret = proc_dointvec_ms_jiffies(ctl, write,
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001790 buffer, lenp, ppos);
Eric W. Biedermand12af672007-10-18 03:05:25 -07001791 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001792 ret = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001793
1794 if (write && ret == 0 && dev && (idev = in6_dev_get(dev)) != NULL) {
Eric W. Biedermand12af672007-10-18 03:05:25 -07001795 if (ctl->data == &idev->nd_parms->base_reachable_time)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001796 idev->nd_parms->reachable_time = neigh_rand_reach_time(idev->nd_parms->base_reachable_time);
1797 idev->tstamp = jiffies;
1798 inet6_ifinfo_notify(RTM_NEWLINK, idev);
1799 in6_dev_put(idev);
1800 }
1801 return ret;
1802}
1803
Linus Torvalds1da177e2005-04-16 15:20:36 -07001804
1805#endif
1806
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00001807static int __net_init ndisc_net_init(struct net *net)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001808{
1809 struct ipv6_pinfo *np;
1810 struct sock *sk;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001811 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001812
Denis V. Lunev1ed85162008-04-03 14:31:03 -07001813 err = inet_ctl_sock_create(&sk, PF_INET6,
1814 SOCK_RAW, IPPROTO_ICMPV6, net);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001815 if (err < 0) {
1816 ND_PRINTK0(KERN_ERR
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001817 "ICMPv6 NDISC: Failed to initialize the control socket (err %d).\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001818 err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001819 return err;
1820 }
1821
Denis V. Lunev1ed85162008-04-03 14:31:03 -07001822 net->ipv6.ndisc_sk = sk;
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001823
Linus Torvalds1da177e2005-04-16 15:20:36 -07001824 np = inet6_sk(sk);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001825 np->hop_limit = 255;
1826 /* Do not loopback ndisc messages */
1827 np->mc_loop = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001828
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001829 return 0;
1830}
1831
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00001832static void __net_exit ndisc_net_exit(struct net *net)
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001833{
Denis V. Lunev1ed85162008-04-03 14:31:03 -07001834 inet_ctl_sock_destroy(net->ipv6.ndisc_sk);
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001835}
1836
1837static struct pernet_operations ndisc_net_ops = {
1838 .init = ndisc_net_init,
1839 .exit = ndisc_net_exit,
1840};
1841
1842int __init ndisc_init(void)
1843{
1844 int err;
1845
1846 err = register_pernet_subsys(&ndisc_net_ops);
1847 if (err)
1848 return err;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001849 /*
1850 * Initialize the neighbour table
1851 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001852 neigh_table_init(&nd_tbl);
1853
1854#ifdef CONFIG_SYSCTL
Eric W. Biederman54716e32010-02-14 03:27:03 +00001855 err = neigh_sysctl_register(NULL, &nd_tbl.parms, "ipv6",
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001856 &ndisc_ifinfo_sysctl_change);
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001857 if (err)
1858 goto out_unregister_pernet;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001859#endif
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001860 err = register_netdevice_notifier(&ndisc_netdev_notifier);
1861 if (err)
1862 goto out_unregister_sysctl;
1863out:
1864 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001865
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001866out_unregister_sysctl:
1867#ifdef CONFIG_SYSCTL
1868 neigh_sysctl_unregister(&nd_tbl.parms);
1869out_unregister_pernet:
1870#endif
1871 unregister_pernet_subsys(&ndisc_net_ops);
1872 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001873}
1874
1875void ndisc_cleanup(void)
1876{
Dmitry Mishin36f73d02006-11-03 16:08:19 -08001877 unregister_netdevice_notifier(&ndisc_netdev_notifier);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001878#ifdef CONFIG_SYSCTL
1879 neigh_sysctl_unregister(&nd_tbl.parms);
1880#endif
1881 neigh_table_clear(&nd_tbl);
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001882 unregister_pernet_subsys(&ndisc_net_ops);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001883}