blob: f41853bca4289faebe21133084bd829953891611 [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 *
Alexey I. Froloffe35f30c2012-04-06 05:50:58 +000018 * Alexey I. Froloff : RFC6106 (DNSSL) support
Pierre Ynard31910572007-10-10 21:22:05 -070019 * Pierre Ynard : export userland ND options
20 * through netlink (RDNSS support)
Linus Torvalds1da177e2005-04-16 15:20:36 -070021 * Lars Fenneberg : fixed MTU setting on receipt
22 * of an RA.
Linus Torvalds1da177e2005-04-16 15:20:36 -070023 * Janos Farkas : kmalloc failure checks
24 * Alexey Kuznetsov : state machine reworked
25 * and moved to net/core.
26 * Pekka Savola : RFC2461 validation
27 * YOSHIFUJI Hideaki @USAGI : Verify ND options properly
28 */
29
Joe Perches675418d2012-05-16 19:28:38 +000030#define pr_fmt(fmt) "ICMPv6: " fmt
Linus Torvalds1da177e2005-04-16 15:20:36 -070031
32#include <linux/module.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070033#include <linux/errno.h>
34#include <linux/types.h>
35#include <linux/socket.h>
36#include <linux/sockios.h>
37#include <linux/sched.h>
38#include <linux/net.h>
39#include <linux/in6.h>
40#include <linux/route.h>
41#include <linux/init.h>
42#include <linux/rcupdate.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090043#include <linux/slab.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070044#ifdef CONFIG_SYSCTL
45#include <linux/sysctl.h>
46#endif
47
Thomas Graf18237302006-08-04 23:04:54 -070048#include <linux/if_addr.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070049#include <linux/if_arp.h>
50#include <linux/ipv6.h>
51#include <linux/icmpv6.h>
52#include <linux/jhash.h>
53
54#include <net/sock.h>
55#include <net/snmp.h>
56
57#include <net/ipv6.h>
58#include <net/protocol.h>
59#include <net/ndisc.h>
60#include <net/ip6_route.h>
61#include <net/addrconf.h>
62#include <net/icmp.h>
63
Pierre Ynard31910572007-10-10 21:22:05 -070064#include <net/netlink.h>
65#include <linux/rtnetlink.h>
66
Linus Torvalds1da177e2005-04-16 15:20:36 -070067#include <net/flow.h>
68#include <net/ip6_checksum.h>
Denis V. Lunev1ed85162008-04-03 14:31:03 -070069#include <net/inet_common.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070070#include <linux/proc_fs.h>
71
72#include <linux/netfilter.h>
73#include <linux/netfilter_ipv6.h>
74
Joe Perches675418d2012-05-16 19:28:38 +000075/* Set to 3 to get tracing... */
76#define ND_DEBUG 1
77
78#define ND_PRINTK(val, level, fmt, ...) \
79do { \
80 if (val <= ND_DEBUG) \
81 net_##level##_ratelimited(fmt, ##__VA_ARGS__); \
82} while (0)
83
Eric Dumazetd6bf7812010-10-04 06:15:44 +000084static u32 ndisc_hash(const void *pkey,
85 const struct net_device *dev,
David S. Miller2c2aba62011-12-28 15:06:58 -050086 __u32 *hash_rnd);
Linus Torvalds1da177e2005-04-16 15:20:36 -070087static int ndisc_constructor(struct neighbour *neigh);
88static void ndisc_solicit(struct neighbour *neigh, struct sk_buff *skb);
89static void ndisc_error_report(struct neighbour *neigh, struct sk_buff *skb);
90static int pndisc_constructor(struct pneigh_entry *n);
91static void pndisc_destructor(struct pneigh_entry *n);
92static void pndisc_redo(struct sk_buff *skb);
93
Stephen Hemminger89d69d22009-09-01 11:13:19 +000094static const struct neigh_ops ndisc_generic_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -070095 .family = AF_INET6,
96 .solicit = ndisc_solicit,
97 .error_report = ndisc_error_report,
98 .output = neigh_resolve_output,
99 .connected_output = neigh_connected_output,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100};
101
Stephen Hemminger89d69d22009-09-01 11:13:19 +0000102static const struct neigh_ops ndisc_hh_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700103 .family = AF_INET6,
104 .solicit = ndisc_solicit,
105 .error_report = ndisc_error_report,
106 .output = neigh_resolve_output,
107 .connected_output = neigh_resolve_output,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108};
109
110
Stephen Hemminger89d69d22009-09-01 11:13:19 +0000111static const struct neigh_ops ndisc_direct_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700112 .family = AF_INET6,
David S. Miller8f40b162011-07-17 13:34:11 -0700113 .output = neigh_direct_output,
114 .connected_output = neigh_direct_output,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700115};
116
117struct neigh_table nd_tbl = {
118 .family = AF_INET6,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700119 .key_len = sizeof(struct in6_addr),
120 .hash = ndisc_hash,
121 .constructor = ndisc_constructor,
122 .pconstructor = pndisc_constructor,
123 .pdestructor = pndisc_destructor,
124 .proxy_redo = pndisc_redo,
125 .id = "ndisc_cache",
126 .parms = {
Shan Weib6720832010-12-01 18:05:12 +0000127 .tbl = &nd_tbl,
128 .base_reachable_time = ND_REACHABLE_TIME,
129 .retrans_time = ND_RETRANS_TIMER,
130 .gc_staletime = 60 * HZ,
131 .reachable_time = ND_REACHABLE_TIME,
132 .delay_probe_time = 5 * HZ,
Eric Dumazet8b5c1712011-11-09 12:07:14 +0000133 .queue_len_bytes = 64*1024,
Shan Weib6720832010-12-01 18:05:12 +0000134 .ucast_probes = 3,
135 .mcast_probes = 3,
136 .anycast_delay = 1 * HZ,
137 .proxy_delay = (8 * HZ) / 10,
138 .proxy_qlen = 64,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700139 },
140 .gc_interval = 30 * HZ,
141 .gc_thresh1 = 128,
142 .gc_thresh2 = 512,
143 .gc_thresh3 = 1024,
144};
145
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146static inline int ndisc_opt_addr_space(struct net_device *dev)
147{
148 return NDISC_OPT_SPACE(dev->addr_len + ndisc_addr_option_pad(dev->type));
149}
150
151static u8 *ndisc_fill_addr_option(u8 *opt, int type, void *data, int data_len,
152 unsigned short addr_type)
153{
154 int space = NDISC_OPT_SPACE(data_len);
155 int pad = ndisc_addr_option_pad(addr_type);
156
157 opt[0] = type;
158 opt[1] = space>>3;
159
160 memset(opt + 2, 0, pad);
161 opt += pad;
162 space -= pad;
163
164 memcpy(opt+2, data, data_len);
165 data_len += 2;
166 opt += data_len;
167 if ((space -= data_len) > 0)
168 memset(opt, 0, space);
169 return opt + space;
170}
171
172static struct nd_opt_hdr *ndisc_next_option(struct nd_opt_hdr *cur,
173 struct nd_opt_hdr *end)
174{
175 int type;
176 if (!cur || !end || cur >= end)
177 return NULL;
178 type = cur->nd_opt_type;
179 do {
180 cur = ((void *)cur) + (cur->nd_opt_len << 3);
181 } while(cur < end && cur->nd_opt_type != type);
Eric Dumazeta02cec22010-09-22 20:43:57 +0000182 return cur <= end && cur->nd_opt_type == type ? cur : NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183}
184
Pierre Ynard31910572007-10-10 21:22:05 -0700185static inline int ndisc_is_useropt(struct nd_opt_hdr *opt)
186{
Alexey I. Froloffe35f30c2012-04-06 05:50:58 +0000187 return opt->nd_opt_type == ND_OPT_RDNSS ||
188 opt->nd_opt_type == ND_OPT_DNSSL;
Pierre Ynard31910572007-10-10 21:22:05 -0700189}
190
191static struct nd_opt_hdr *ndisc_next_useropt(struct nd_opt_hdr *cur,
192 struct nd_opt_hdr *end)
193{
194 if (!cur || !end || cur >= end)
195 return NULL;
196 do {
197 cur = ((void *)cur) + (cur->nd_opt_len << 3);
198 } while(cur < end && !ndisc_is_useropt(cur));
Eric Dumazeta02cec22010-09-22 20:43:57 +0000199 return cur <= end && ndisc_is_useropt(cur) ? cur : NULL;
Pierre Ynard31910572007-10-10 21:22:05 -0700200}
201
David S. Miller30f2a5f2012-07-11 23:26:46 -0700202struct ndisc_options *ndisc_parse_options(u8 *opt, int opt_len,
203 struct ndisc_options *ndopts)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700204{
205 struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)opt;
206
207 if (!nd_opt || opt_len < 0 || !ndopts)
208 return NULL;
209 memset(ndopts, 0, sizeof(*ndopts));
210 while (opt_len) {
211 int l;
212 if (opt_len < sizeof(struct nd_opt_hdr))
213 return NULL;
214 l = nd_opt->nd_opt_len << 3;
215 if (opt_len < l || l == 0)
216 return NULL;
217 switch (nd_opt->nd_opt_type) {
218 case ND_OPT_SOURCE_LL_ADDR:
219 case ND_OPT_TARGET_LL_ADDR:
220 case ND_OPT_MTU:
221 case ND_OPT_REDIRECT_HDR:
222 if (ndopts->nd_opt_array[nd_opt->nd_opt_type]) {
Joe Perches675418d2012-05-16 19:28:38 +0000223 ND_PRINTK(2, warn,
224 "%s: duplicated ND6 option found: type=%d\n",
225 __func__, nd_opt->nd_opt_type);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700226 } else {
227 ndopts->nd_opt_array[nd_opt->nd_opt_type] = nd_opt;
228 }
229 break;
230 case ND_OPT_PREFIX_INFO:
231 ndopts->nd_opts_pi_end = nd_opt;
Stephen Hemmingercfcabdc2007-10-09 01:59:42 -0700232 if (!ndopts->nd_opt_array[nd_opt->nd_opt_type])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700233 ndopts->nd_opt_array[nd_opt->nd_opt_type] = nd_opt;
234 break;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800235#ifdef CONFIG_IPV6_ROUTE_INFO
236 case ND_OPT_ROUTE_INFO:
237 ndopts->nd_opts_ri_end = nd_opt;
238 if (!ndopts->nd_opts_ri)
239 ndopts->nd_opts_ri = nd_opt;
240 break;
241#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700242 default:
Pierre Ynard31910572007-10-10 21:22:05 -0700243 if (ndisc_is_useropt(nd_opt)) {
244 ndopts->nd_useropts_end = nd_opt;
245 if (!ndopts->nd_useropts)
246 ndopts->nd_useropts = nd_opt;
247 } else {
248 /*
249 * Unknown options must be silently ignored,
250 * to accommodate future extension to the
251 * protocol.
252 */
Joe Perches675418d2012-05-16 19:28:38 +0000253 ND_PRINTK(2, notice,
254 "%s: ignored unsupported option; type=%d, len=%d\n",
255 __func__,
256 nd_opt->nd_opt_type,
257 nd_opt->nd_opt_len);
Pierre Ynard31910572007-10-10 21:22:05 -0700258 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700259 }
260 opt_len -= l;
261 nd_opt = ((void *)nd_opt) + l;
262 }
263 return ndopts;
264}
265
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000266int ndisc_mc_map(const struct in6_addr *addr, char *buf, struct net_device *dev, int dir)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700267{
268 switch (dev->type) {
269 case ARPHRD_ETHER:
270 case ARPHRD_IEEE802: /* Not sure. Check it later. --ANK */
271 case ARPHRD_FDDI:
272 ipv6_eth_mc_map(addr, buf);
273 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700274 case ARPHRD_ARCNET:
275 ipv6_arcnet_mc_map(addr, buf);
276 return 0;
277 case ARPHRD_INFINIBAND:
Rolf Manderscheida9e527e2007-12-10 13:38:41 -0700278 ipv6_ib_mc_map(addr, dev->broadcast, buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279 return 0;
Timo Teräs93ca3bb2011-03-28 22:40:53 +0000280 case ARPHRD_IPGRE:
281 return ipv6_ipgre_mc_map(addr, dev->broadcast, buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282 default:
283 if (dir) {
284 memcpy(buf, dev->broadcast, dev->addr_len);
285 return 0;
286 }
287 }
288 return -EINVAL;
289}
290
YOSHIFUJI Hideaki71590392007-02-22 22:05:40 +0900291EXPORT_SYMBOL(ndisc_mc_map);
292
Eric Dumazetd6bf7812010-10-04 06:15:44 +0000293static u32 ndisc_hash(const void *pkey,
294 const struct net_device *dev,
David S. Miller2c2aba62011-12-28 15:06:58 -0500295 __u32 *hash_rnd)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296{
David S. Miller2c2aba62011-12-28 15:06:58 -0500297 return ndisc_hashfn(pkey, dev, hash_rnd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298}
299
300static int ndisc_constructor(struct neighbour *neigh)
301{
302 struct in6_addr *addr = (struct in6_addr*)&neigh->primary_key;
303 struct net_device *dev = neigh->dev;
304 struct inet6_dev *in6_dev;
305 struct neigh_parms *parms;
Eric Dumazeta50feda2012-05-18 18:57:34 +0000306 bool is_multicast = ipv6_addr_is_multicast(addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700307
Linus Torvalds1da177e2005-04-16 15:20:36 -0700308 in6_dev = in6_dev_get(dev);
309 if (in6_dev == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700310 return -EINVAL;
311 }
312
313 parms = in6_dev->nd_parms;
314 __neigh_parms_put(neigh->parms);
315 neigh->parms = neigh_parms_clone(parms);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700316
317 neigh->type = is_multicast ? RTN_MULTICAST : RTN_UNICAST;
Stephen Hemminger3b04ddd2007-10-09 01:40:57 -0700318 if (!dev->header_ops) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700319 neigh->nud_state = NUD_NOARP;
320 neigh->ops = &ndisc_direct_ops;
David S. Miller8f40b162011-07-17 13:34:11 -0700321 neigh->output = neigh_direct_output;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322 } else {
323 if (is_multicast) {
324 neigh->nud_state = NUD_NOARP;
325 ndisc_mc_map(addr, neigh->ha, dev, 1);
326 } else if (dev->flags&(IFF_NOARP|IFF_LOOPBACK)) {
327 neigh->nud_state = NUD_NOARP;
328 memcpy(neigh->ha, dev->dev_addr, dev->addr_len);
329 if (dev->flags&IFF_LOOPBACK)
330 neigh->type = RTN_LOCAL;
331 } else if (dev->flags&IFF_POINTOPOINT) {
332 neigh->nud_state = NUD_NOARP;
333 memcpy(neigh->ha, dev->broadcast, dev->addr_len);
334 }
Stephen Hemminger3b04ddd2007-10-09 01:40:57 -0700335 if (dev->header_ops->cache)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336 neigh->ops = &ndisc_hh_ops;
337 else
338 neigh->ops = &ndisc_generic_ops;
339 if (neigh->nud_state&NUD_VALID)
340 neigh->output = neigh->ops->connected_output;
341 else
342 neigh->output = neigh->ops->output;
343 }
344 in6_dev_put(in6_dev);
345 return 0;
346}
347
348static int pndisc_constructor(struct pneigh_entry *n)
349{
350 struct in6_addr *addr = (struct in6_addr*)&n->key;
351 struct in6_addr maddr;
352 struct net_device *dev = n->dev;
353
354 if (dev == NULL || __in6_dev_get(dev) == NULL)
355 return -EINVAL;
356 addrconf_addr_solict_mult(addr, &maddr);
357 ipv6_dev_mc_inc(dev, &maddr);
358 return 0;
359}
360
361static void pndisc_destructor(struct pneigh_entry *n)
362{
363 struct in6_addr *addr = (struct in6_addr*)&n->key;
364 struct in6_addr maddr;
365 struct net_device *dev = n->dev;
366
367 if (dev == NULL || __in6_dev_get(dev) == NULL)
368 return;
369 addrconf_addr_solict_mult(addr, &maddr);
370 ipv6_dev_mc_dec(dev, &maddr);
371}
372
Brian Haley305d5522008-11-04 17:51:14 -0800373struct sk_buff *ndisc_build_skb(struct net_device *dev,
374 const struct in6_addr *daddr,
375 const struct in6_addr *saddr,
376 struct icmp6hdr *icmp6h,
377 const struct in6_addr *target,
378 int llinfo)
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900379{
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900380 struct net *net = dev_net(dev);
Daniel Lezcano1762f7e2008-03-07 11:15:34 -0800381 struct sock *sk = net->ipv6.ndisc_sk;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900382 struct sk_buff *skb;
383 struct icmp6hdr *hdr;
Herbert Xua7ae1992011-11-18 02:20:04 +0000384 int hlen = LL_RESERVED_SPACE(dev);
385 int tlen = dev->needed_tailroom;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900386 int len;
387 int err;
Brian Haley305d5522008-11-04 17:51:14 -0800388 u8 *opt;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900389
390 if (!dev->addr_len)
391 llinfo = 0;
392
393 len = sizeof(struct icmp6hdr) + (target ? sizeof(*target) : 0);
394 if (llinfo)
395 len += ndisc_opt_addr_space(dev);
396
397 skb = sock_alloc_send_skb(sk,
398 (MAX_HEADER + sizeof(struct ipv6hdr) +
Herbert Xua7ae1992011-11-18 02:20:04 +0000399 len + hlen + tlen),
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900400 1, &err);
401 if (!skb) {
Joe Perches675418d2012-05-16 19:28:38 +0000402 ND_PRINTK(0, err, "ND: %s failed to allocate an skb, err=%d\n",
403 __func__, err);
Brian Haley305d5522008-11-04 17:51:14 -0800404 return NULL;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900405 }
406
Herbert Xua7ae1992011-11-18 02:20:04 +0000407 skb_reserve(skb, hlen);
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900408 ip6_nd_hdr(sk, skb, dev, saddr, daddr, IPPROTO_ICMPV6, len);
409
410 skb->transport_header = skb->tail;
411 skb_put(skb, len);
412
413 hdr = (struct icmp6hdr *)skb_transport_header(skb);
414 memcpy(hdr, icmp6h, sizeof(*hdr));
415
416 opt = skb_transport_header(skb) + sizeof(struct icmp6hdr);
417 if (target) {
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +0000418 *(struct in6_addr *)opt = *target;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900419 opt += sizeof(*target);
420 }
421
422 if (llinfo)
423 ndisc_fill_addr_option(opt, llinfo, dev->dev_addr,
424 dev->addr_len, dev->type);
425
426 hdr->icmp6_cksum = csum_ipv6_magic(saddr, daddr, len,
427 IPPROTO_ICMPV6,
Joe Perches07f07572008-11-19 15:44:53 -0800428 csum_partial(hdr,
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900429 len, 0));
430
Brian Haley305d5522008-11-04 17:51:14 -0800431 return skb;
432}
433
434EXPORT_SYMBOL(ndisc_build_skb);
435
436void ndisc_send_skb(struct sk_buff *skb,
437 struct net_device *dev,
438 struct neighbour *neigh,
439 const struct in6_addr *daddr,
440 const struct in6_addr *saddr,
441 struct icmp6hdr *icmp6h)
442{
David S. Miller4c9483b2011-03-12 16:22:43 -0500443 struct flowi6 fl6;
Brian Haley305d5522008-11-04 17:51:14 -0800444 struct dst_entry *dst;
445 struct net *net = dev_net(dev);
446 struct sock *sk = net->ipv6.ndisc_sk;
447 struct inet6_dev *idev;
448 int err;
449 u8 type;
450
451 type = icmp6h->icmp6_type;
452
David S. Miller4c9483b2011-03-12 16:22:43 -0500453 icmpv6_flow_init(sk, &fl6, type, saddr, daddr, dev->ifindex);
David S. Miller87a11572011-12-06 17:04:13 -0500454 dst = icmp6_dst_alloc(dev, neigh, &fl6);
David S. Miller452edd52011-03-02 13:27:41 -0800455 if (IS_ERR(dst)) {
Brian Haley305d5522008-11-04 17:51:14 -0800456 kfree_skb(skb);
457 return;
458 }
459
Eric Dumazetadf30902009-06-02 05:19:30 +0000460 skb_dst_set(skb, dst);
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900461
Eric Dumazetcfdf7642011-07-27 21:13:03 +0000462 rcu_read_lock();
463 idev = __in6_dev_get(dst->dev);
Neil Hormanedf391f2009-04-27 02:45:02 -0700464 IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUT, skb->len);
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900465
Jan Engelhardtb2e0b382010-03-23 04:09:07 +0100466 err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, skb, NULL, dst->dev,
Patrick McHardy6e23ae22007-11-19 18:53:30 -0800467 dst_output);
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900468 if (!err) {
Denis V. Lunev5c5d2442008-10-08 10:33:50 -0700469 ICMP6MSGOUT_INC_STATS(net, idev, type);
Denis V. Luneva862f6a2008-10-08 10:33:06 -0700470 ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS);
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900471 }
472
Eric Dumazetcfdf7642011-07-27 21:13:03 +0000473 rcu_read_unlock();
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900474}
475
Brian Haley305d5522008-11-04 17:51:14 -0800476EXPORT_SYMBOL(ndisc_send_skb);
477
478/*
479 * Send a Neighbour Discover packet
480 */
481static void __ndisc_send(struct net_device *dev,
482 struct neighbour *neigh,
483 const struct in6_addr *daddr,
484 const struct in6_addr *saddr,
485 struct icmp6hdr *icmp6h, const struct in6_addr *target,
486 int llinfo)
487{
488 struct sk_buff *skb;
489
490 skb = ndisc_build_skb(dev, daddr, saddr, icmp6h, target, llinfo);
491 if (!skb)
492 return;
493
494 ndisc_send_skb(skb, dev, neigh, daddr, saddr, icmp6h);
495}
496
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +0900498 const struct in6_addr *daddr,
499 const struct in6_addr *solicited_addr,
500 int router, int solicited, int override, int inc_opt)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501{
502 struct in6_addr tmpaddr;
503 struct inet6_ifaddr *ifp;
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +0900504 const struct in6_addr *src_addr;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900505 struct icmp6hdr icmp6h = {
506 .icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT,
507 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700508
509 /* for anycast or proxy, solicited_addr != src_addr */
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900510 ifp = ipv6_get_ifaddr(dev_net(dev), solicited_addr, dev, 1);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900511 if (ifp) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700512 src_addr = solicited_addr;
Neil Horman95c385b2007-04-25 17:08:10 -0700513 if (ifp->flags & IFA_F_OPTIMISTIC)
514 override = 0;
stephen hemminger9f888162010-06-21 11:00:13 +0000515 inc_opt |= ifp->idev->cnf.force_tllao;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700516 in6_ifa_put(ifp);
517 } else {
Brian Haley191cd582008-08-14 15:33:21 -0700518 if (ipv6_dev_get_saddr(dev_net(dev), dev, daddr,
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900519 inet6_sk(dev_net(dev)->ipv6.ndisc_sk)->srcprefs,
YOSHIFUJI Hideaki7cbca672008-03-25 09:37:42 +0900520 &tmpaddr))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700521 return;
522 src_addr = &tmpaddr;
523 }
524
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900525 icmp6h.icmp6_router = router;
526 icmp6h.icmp6_solicited = solicited;
527 icmp6h.icmp6_override = override;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700528
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900529 __ndisc_send(dev, neigh, daddr, src_addr,
530 &icmp6h, solicited_addr,
David L Stevens14878f72007-09-16 16:52:35 -0700531 inc_opt ? ND_OPT_TARGET_LL_ADDR : 0);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900532}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700533
Ben Hutchingsf47b9462011-04-15 13:46:02 +0000534static void ndisc_send_unsol_na(struct net_device *dev)
535{
536 struct inet6_dev *idev;
537 struct inet6_ifaddr *ifa;
Ben Hutchingsf47b9462011-04-15 13:46:02 +0000538
539 idev = in6_dev_get(dev);
540 if (!idev)
541 return;
542
543 read_lock_bh(&idev->lock);
544 list_for_each_entry(ifa, &idev->addr_list, if_list) {
YOSHIFUJI Hideaki / 吉藤英明9fafd652012-11-12 07:50:17 +0000545 ndisc_send_na(dev, NULL, &in6addr_linklocal_allnodes, &ifa->addr,
Ben Hutchingsf47b9462011-04-15 13:46:02 +0000546 /*router=*/ !!idev->cnf.forwarding,
547 /*solicited=*/ false, /*override=*/ true,
548 /*inc_opt=*/ true);
549 }
550 read_unlock_bh(&idev->lock);
551
552 in6_dev_put(idev);
553}
554
Linus Torvalds1da177e2005-04-16 15:20:36 -0700555void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +0900556 const struct in6_addr *solicit,
557 const struct in6_addr *daddr, const struct in6_addr *saddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700558{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700559 struct in6_addr addr_buf;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900560 struct icmp6hdr icmp6h = {
561 .icmp6_type = NDISC_NEIGHBOUR_SOLICITATION,
562 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700563
564 if (saddr == NULL) {
Neil Horman95c385b2007-04-25 17:08:10 -0700565 if (ipv6_get_lladdr(dev, &addr_buf,
566 (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700567 return;
568 saddr = &addr_buf;
569 }
570
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900571 __ndisc_send(dev, neigh, daddr, saddr,
572 &icmp6h, solicit,
David L Stevens14878f72007-09-16 16:52:35 -0700573 !ipv6_addr_any(saddr) ? ND_OPT_SOURCE_LL_ADDR : 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700574}
575
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +0900576void ndisc_send_rs(struct net_device *dev, const struct in6_addr *saddr,
577 const struct in6_addr *daddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578{
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900579 struct icmp6hdr icmp6h = {
580 .icmp6_type = NDISC_ROUTER_SOLICITATION,
581 };
Neil Horman95c385b2007-04-25 17:08:10 -0700582 int send_sllao = dev->addr_len;
Neil Horman95c385b2007-04-25 17:08:10 -0700583
584#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
585 /*
586 * According to section 2.2 of RFC 4429, we must not
587 * send router solicitations with a sllao from
588 * optimistic addresses, but we may send the solicitation
589 * if we don't include the sllao. So here we check
590 * if our address is optimistic, and if so, we
Joe Perchesbea85192007-12-20 14:01:35 -0800591 * suppress the inclusion of the sllao.
Neil Horman95c385b2007-04-25 17:08:10 -0700592 */
593 if (send_sllao) {
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900594 struct inet6_ifaddr *ifp = ipv6_get_ifaddr(dev_net(dev), saddr,
Daniel Lezcano1cab3da2008-01-10 22:44:09 -0800595 dev, 1);
Neil Horman95c385b2007-04-25 17:08:10 -0700596 if (ifp) {
597 if (ifp->flags & IFA_F_OPTIMISTIC) {
YOSHIFUJI Hideakica043562007-02-28 23:13:20 +0900598 send_sllao = 0;
Neil Horman95c385b2007-04-25 17:08:10 -0700599 }
YOSHIFUJI Hideakica043562007-02-28 23:13:20 +0900600 in6_ifa_put(ifp);
Neil Horman95c385b2007-04-25 17:08:10 -0700601 } else {
602 send_sllao = 0;
603 }
604 }
605#endif
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900606 __ndisc_send(dev, NULL, daddr, saddr,
607 &icmp6h, NULL,
David L Stevens14878f72007-09-16 16:52:35 -0700608 send_sllao ? ND_OPT_SOURCE_LL_ADDR : 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700609}
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900610
Linus Torvalds1da177e2005-04-16 15:20:36 -0700611
612static void ndisc_error_report(struct neighbour *neigh, struct sk_buff *skb)
613{
614 /*
615 * "The sender MUST return an ICMP
616 * destination unreachable"
617 */
618 dst_link_failure(skb);
619 kfree_skb(skb);
620}
621
622/* Called with locked neigh: either read or both */
623
624static void ndisc_solicit(struct neighbour *neigh, struct sk_buff *skb)
625{
626 struct in6_addr *saddr = NULL;
627 struct in6_addr mcaddr;
628 struct net_device *dev = neigh->dev;
629 struct in6_addr *target = (struct in6_addr *)&neigh->primary_key;
630 int probes = atomic_read(&neigh->probes);
631
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900632 if (skb && ipv6_chk_addr(dev_net(dev), &ipv6_hdr(skb)->saddr, dev, 1))
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -0700633 saddr = &ipv6_hdr(skb)->saddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700634
635 if ((probes -= neigh->parms->ucast_probes) < 0) {
636 if (!(neigh->nud_state & NUD_VALID)) {
Joe Perches675418d2012-05-16 19:28:38 +0000637 ND_PRINTK(1, dbg,
638 "%s: trying to ucast probe in NUD_INVALID: %pI6\n",
639 __func__, target);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700640 }
641 ndisc_send_ns(dev, neigh, target, target, saddr);
642 } else if ((probes -= neigh->parms->app_probes) < 0) {
643#ifdef CONFIG_ARPD
644 neigh_app_ns(neigh);
645#endif
646 } else {
647 addrconf_addr_solict_mult(target, &mcaddr);
648 ndisc_send_ns(dev, NULL, target, &mcaddr, saddr);
649 }
650}
651
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900652static int pndisc_is_router(const void *pkey,
653 struct net_device *dev)
Pavel Emelyanovfa86d322008-03-24 14:48:59 -0700654{
655 struct pneigh_entry *n;
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900656 int ret = -1;
Pavel Emelyanovfa86d322008-03-24 14:48:59 -0700657
658 read_lock_bh(&nd_tbl.lock);
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900659 n = __pneigh_lookup(&nd_tbl, dev_net(dev), pkey, dev);
660 if (n)
661 ret = !!(n->flags & NTF_ROUTER);
Pavel Emelyanovfa86d322008-03-24 14:48:59 -0700662 read_unlock_bh(&nd_tbl.lock);
663
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900664 return ret;
Pavel Emelyanovfa86d322008-03-24 14:48:59 -0700665}
666
Linus Torvalds1da177e2005-04-16 15:20:36 -0700667static void ndisc_recv_ns(struct sk_buff *skb)
668{
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -0700669 struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb);
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000670 const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
671 const struct in6_addr *daddr = &ipv6_hdr(skb)->daddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700672 u8 *lladdr = NULL;
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -0700673 u32 ndoptlen = skb->tail - (skb->transport_header +
674 offsetof(struct nd_msg, opt));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700675 struct ndisc_options ndopts;
676 struct net_device *dev = skb->dev;
677 struct inet6_ifaddr *ifp;
678 struct inet6_dev *idev = NULL;
679 struct neighbour *neigh;
680 int dad = ipv6_addr_any(saddr);
Eric Dumazeta50feda2012-05-18 18:57:34 +0000681 bool inc;
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900682 int is_router = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700683
684 if (ipv6_addr_is_multicast(&msg->target)) {
Joe Perches675418d2012-05-16 19:28:38 +0000685 ND_PRINTK(2, warn, "NS: multicast target address\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700686 return;
687 }
688
689 /*
690 * RFC2461 7.1.1:
691 * DAD has to be destined for solicited node multicast address.
692 */
693 if (dad &&
694 !(daddr->s6_addr32[0] == htonl(0xff020000) &&
695 daddr->s6_addr32[1] == htonl(0x00000000) &&
696 daddr->s6_addr32[2] == htonl(0x00000001) &&
697 daddr->s6_addr [12] == 0xff )) {
Joe Perches675418d2012-05-16 19:28:38 +0000698 ND_PRINTK(2, warn, "NS: bad DAD packet (wrong destination)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700699 return;
700 }
701
702 if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts)) {
Joe Perches675418d2012-05-16 19:28:38 +0000703 ND_PRINTK(2, warn, "NS: invalid ND options\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700704 return;
705 }
706
707 if (ndopts.nd_opts_src_lladdr) {
708 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr, dev);
709 if (!lladdr) {
Joe Perches675418d2012-05-16 19:28:38 +0000710 ND_PRINTK(2, warn,
711 "NS: invalid link-layer address length\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700712 return;
713 }
714
715 /* RFC2461 7.1.1:
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900716 * If the IP source address is the unspecified address,
717 * there MUST NOT be source link-layer address option
Linus Torvalds1da177e2005-04-16 15:20:36 -0700718 * in the message.
719 */
720 if (dad) {
Joe Perches675418d2012-05-16 19:28:38 +0000721 ND_PRINTK(2, warn,
722 "NS: bad DAD packet (link-layer address option)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700723 return;
724 }
725 }
726
727 inc = ipv6_addr_is_multicast(daddr);
728
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900729 ifp = ipv6_get_ifaddr(dev_net(dev), &msg->target, dev, 1);
Daniel Lezcanoa18bc692008-03-07 11:14:49 -0800730 if (ifp) {
Neil Horman95c385b2007-04-25 17:08:10 -0700731
732 if (ifp->flags & (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)) {
733 if (dad) {
Neil Horman95c385b2007-04-25 17:08:10 -0700734 /*
735 * We are colliding with another node
736 * who is doing DAD
737 * so fail our DAD process
738 */
739 addrconf_dad_failure(ifp);
Denis V. Lunev9e3be4b2007-09-11 11:04:49 +0200740 return;
Neil Horman95c385b2007-04-25 17:08:10 -0700741 } else {
742 /*
743 * This is not a dad solicitation.
744 * If we are an optimistic node,
745 * we should respond.
746 * Otherwise, we should ignore it.
747 */
748 if (!(ifp->flags & IFA_F_OPTIMISTIC))
749 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700750 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700751 }
752
753 idev = ifp->idev;
754 } else {
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700755 struct net *net = dev_net(dev);
756
Linus Torvalds1da177e2005-04-16 15:20:36 -0700757 idev = in6_dev_get(dev);
758 if (!idev) {
759 /* XXX: count this drop? */
760 return;
761 }
762
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700763 if (ipv6_chk_acast_addr(net, dev, &msg->target) ||
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900764 (idev->cnf.forwarding &&
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700765 (net->ipv6.devconf_all->proxy_ndp || idev->cnf.proxy_ndp) &&
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900766 (is_router = pndisc_is_router(&msg->target, dev)) >= 0)) {
Patrick McHardya61bbcf2005-08-14 17:24:31 -0700767 if (!(NEIGH_CB(skb)->flags & LOCALLY_ENQUEUED) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700768 skb->pkt_type != PACKET_HOST &&
769 inc != 0 &&
770 idev->nd_parms->proxy_delay != 0) {
771 /*
772 * for anycast or proxy,
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900773 * sender should delay its response
774 * by a random time between 0 and
Linus Torvalds1da177e2005-04-16 15:20:36 -0700775 * MAX_ANYCAST_DELAY_TIME seconds.
776 * (RFC2461) -- yoshfuji
777 */
778 struct sk_buff *n = skb_clone(skb, GFP_ATOMIC);
779 if (n)
780 pneigh_enqueue(&nd_tbl, idev->nd_parms, n);
781 goto out;
782 }
783 } else
784 goto out;
785 }
786
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900787 if (is_router < 0)
788 is_router = !!idev->cnf.forwarding;
Ville Nuorvala62dd9312006-09-22 14:43:19 -0700789
Linus Torvalds1da177e2005-04-16 15:20:36 -0700790 if (dad) {
YOSHIFUJI Hideakif3ee4012008-04-10 15:42:11 +0900791 ndisc_send_na(dev, NULL, &in6addr_linklocal_allnodes, &msg->target,
Ville Nuorvala62dd9312006-09-22 14:43:19 -0700792 is_router, 0, (ifp != NULL), 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700793 goto out;
794 }
795
796 if (inc)
797 NEIGH_CACHE_STAT_INC(&nd_tbl, rcv_probes_mcast);
798 else
799 NEIGH_CACHE_STAT_INC(&nd_tbl, rcv_probes_ucast);
800
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900801 /*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700802 * update / create cache entry
803 * for the source address
804 */
805 neigh = __neigh_lookup(&nd_tbl, saddr, dev,
806 !inc || lladdr || !dev->addr_len);
807 if (neigh)
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900808 neigh_update(neigh, lladdr, NUD_STALE,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700809 NEIGH_UPDATE_F_WEAK_OVERRIDE|
810 NEIGH_UPDATE_F_OVERRIDE);
Stephen Hemminger3b04ddd2007-10-09 01:40:57 -0700811 if (neigh || !dev->header_ops) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700812 ndisc_send_na(dev, neigh, saddr, &msg->target,
Ville Nuorvala62dd9312006-09-22 14:43:19 -0700813 is_router,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700814 1, (ifp != NULL && inc), inc);
815 if (neigh)
816 neigh_release(neigh);
817 }
818
819out:
820 if (ifp)
821 in6_ifa_put(ifp);
822 else
823 in6_dev_put(idev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700824}
825
826static void ndisc_recv_na(struct sk_buff *skb)
827{
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -0700828 struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb);
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000829 const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
830 const struct in6_addr *daddr = &ipv6_hdr(skb)->daddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700831 u8 *lladdr = NULL;
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -0700832 u32 ndoptlen = skb->tail - (skb->transport_header +
833 offsetof(struct nd_msg, opt));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700834 struct ndisc_options ndopts;
835 struct net_device *dev = skb->dev;
836 struct inet6_ifaddr *ifp;
837 struct neighbour *neigh;
838
839 if (skb->len < sizeof(struct nd_msg)) {
Joe Perches675418d2012-05-16 19:28:38 +0000840 ND_PRINTK(2, warn, "NA: packet too short\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700841 return;
842 }
843
844 if (ipv6_addr_is_multicast(&msg->target)) {
Joe Perches675418d2012-05-16 19:28:38 +0000845 ND_PRINTK(2, warn, "NA: target address is multicast\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700846 return;
847 }
848
849 if (ipv6_addr_is_multicast(daddr) &&
850 msg->icmph.icmp6_solicited) {
Joe Perches675418d2012-05-16 19:28:38 +0000851 ND_PRINTK(2, warn, "NA: solicited NA is multicasted\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700852 return;
853 }
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900854
Linus Torvalds1da177e2005-04-16 15:20:36 -0700855 if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts)) {
Joe Perches675418d2012-05-16 19:28:38 +0000856 ND_PRINTK(2, warn, "NS: invalid ND option\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700857 return;
858 }
859 if (ndopts.nd_opts_tgt_lladdr) {
860 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr, dev);
861 if (!lladdr) {
Joe Perches675418d2012-05-16 19:28:38 +0000862 ND_PRINTK(2, warn,
863 "NA: invalid link-layer address length\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700864 return;
865 }
866 }
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900867 ifp = ipv6_get_ifaddr(dev_net(dev), &msg->target, dev, 1);
Daniel Lezcanoa18bc692008-03-07 11:14:49 -0800868 if (ifp) {
Daniel Walterbd015922011-04-13 21:09:25 +0000869 if (skb->pkt_type != PACKET_LOOPBACK
870 && (ifp->flags & IFA_F_TENTATIVE)) {
871 addrconf_dad_failure(ifp);
872 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700873 }
874 /* What should we make now? The advertisement
875 is invalid, but ndisc specs say nothing
876 about it. It could be misconfiguration, or
877 an smart proxy agent tries to help us :-)
Jan Sembera24fc7b82008-12-09 15:48:32 -0800878
879 We should not print the error if NA has been
880 received from loopback - it is just our own
881 unsolicited advertisement.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700882 */
Jan Sembera24fc7b82008-12-09 15:48:32 -0800883 if (skb->pkt_type != PACKET_LOOPBACK)
Joe Perches675418d2012-05-16 19:28:38 +0000884 ND_PRINTK(1, warn,
885 "NA: someone advertises our address %pI6 on %s!\n",
886 &ifp->addr, ifp->idev->dev->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700887 in6_ifa_put(ifp);
888 return;
889 }
890 neigh = neigh_lookup(&nd_tbl, &msg->target, dev);
891
892 if (neigh) {
893 u8 old_flags = neigh->flags;
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700894 struct net *net = dev_net(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700895
896 if (neigh->nud_state & NUD_FAILED)
897 goto out;
898
Ville Nuorvala5f3e6e92006-09-22 14:42:46 -0700899 /*
900 * Don't update the neighbor cache entry on a proxy NA from
901 * ourselves because either the proxied node is off link or it
902 * has already sent a NA to us.
903 */
904 if (lladdr && !memcmp(lladdr, dev->dev_addr, dev->addr_len) &&
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700905 net->ipv6.devconf_all->forwarding && net->ipv6.devconf_all->proxy_ndp &&
906 pneigh_lookup(&nd_tbl, net, &msg->target, dev, 0)) {
Nicolas Dichtelb20b6d92012-11-07 05:05:38 +0000907 /* XXX: idev->cnf.proxy_ndp */
Ville Nuorvala5f3e6e92006-09-22 14:42:46 -0700908 goto out;
YOSHIFUJI Hideakifbea49e2006-09-22 14:43:49 -0700909 }
Ville Nuorvala5f3e6e92006-09-22 14:42:46 -0700910
Linus Torvalds1da177e2005-04-16 15:20:36 -0700911 neigh_update(neigh, lladdr,
912 msg->icmph.icmp6_solicited ? NUD_REACHABLE : NUD_STALE,
913 NEIGH_UPDATE_F_WEAK_OVERRIDE|
914 (msg->icmph.icmp6_override ? NEIGH_UPDATE_F_OVERRIDE : 0)|
915 NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
916 (msg->icmph.icmp6_router ? NEIGH_UPDATE_F_ISROUTER : 0));
917
918 if ((old_flags & ~neigh->flags) & NTF_ROUTER) {
919 /*
920 * Change: router to host
921 */
922 struct rt6_info *rt;
923 rt = rt6_get_dflt_router(saddr, dev);
924 if (rt)
Thomas Grafe0a1ad732006-08-22 00:00:21 -0700925 ip6_del_rt(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700926 }
927
928out:
929 neigh_release(neigh);
930 }
931}
932
933static void ndisc_recv_rs(struct sk_buff *skb)
934{
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -0700935 struct rs_msg *rs_msg = (struct rs_msg *)skb_transport_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700936 unsigned long ndoptlen = skb->len - sizeof(*rs_msg);
937 struct neighbour *neigh;
938 struct inet6_dev *idev;
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000939 const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700940 struct ndisc_options ndopts;
941 u8 *lladdr = NULL;
942
943 if (skb->len < sizeof(*rs_msg))
944 return;
945
Eric Dumazetcfdf7642011-07-27 21:13:03 +0000946 idev = __in6_dev_get(skb->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700947 if (!idev) {
Joe Perches675418d2012-05-16 19:28:38 +0000948 ND_PRINTK(1, err, "RS: can't find in6 device\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700949 return;
950 }
951
952 /* Don't accept RS if we're not in router mode */
953 if (!idev->cnf.forwarding)
954 goto out;
955
956 /*
957 * Don't update NCE if src = ::;
958 * this implies that the source node has no ip address assigned yet.
959 */
960 if (ipv6_addr_any(saddr))
961 goto out;
962
963 /* Parse ND options */
964 if (!ndisc_parse_options(rs_msg->opt, ndoptlen, &ndopts)) {
Joe Perches675418d2012-05-16 19:28:38 +0000965 ND_PRINTK(2, notice, "NS: invalid ND option, ignored\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700966 goto out;
967 }
968
969 if (ndopts.nd_opts_src_lladdr) {
970 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr,
971 skb->dev);
972 if (!lladdr)
973 goto out;
974 }
975
976 neigh = __neigh_lookup(&nd_tbl, saddr, skb->dev, 1);
977 if (neigh) {
978 neigh_update(neigh, lladdr, NUD_STALE,
979 NEIGH_UPDATE_F_WEAK_OVERRIDE|
980 NEIGH_UPDATE_F_OVERRIDE|
981 NEIGH_UPDATE_F_OVERRIDE_ISROUTER);
982 neigh_release(neigh);
983 }
984out:
Eric Dumazetcfdf7642011-07-27 21:13:03 +0000985 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700986}
987
Pierre Ynard31910572007-10-10 21:22:05 -0700988static void ndisc_ra_useropt(struct sk_buff *ra, struct nd_opt_hdr *opt)
989{
990 struct icmp6hdr *icmp6h = (struct icmp6hdr *)skb_transport_header(ra);
991 struct sk_buff *skb;
992 struct nlmsghdr *nlh;
993 struct nduseroptmsg *ndmsg;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900994 struct net *net = dev_net(ra->dev);
Pierre Ynard31910572007-10-10 21:22:05 -0700995 int err;
996 int base_size = NLMSG_ALIGN(sizeof(struct nduseroptmsg)
997 + (opt->nd_opt_len << 3));
998 size_t msg_size = base_size + nla_total_size(sizeof(struct in6_addr));
999
1000 skb = nlmsg_new(msg_size, GFP_ATOMIC);
1001 if (skb == NULL) {
1002 err = -ENOBUFS;
1003 goto errout;
1004 }
1005
1006 nlh = nlmsg_put(skb, 0, 0, RTM_NEWNDUSEROPT, base_size, 0);
1007 if (nlh == NULL) {
1008 goto nla_put_failure;
1009 }
1010
1011 ndmsg = nlmsg_data(nlh);
1012 ndmsg->nduseropt_family = AF_INET6;
Pierre Ynarddbb2ed22007-11-12 17:58:35 -08001013 ndmsg->nduseropt_ifindex = ra->dev->ifindex;
Pierre Ynard31910572007-10-10 21:22:05 -07001014 ndmsg->nduseropt_icmp_type = icmp6h->icmp6_type;
1015 ndmsg->nduseropt_icmp_code = icmp6h->icmp6_code;
1016 ndmsg->nduseropt_opts_len = opt->nd_opt_len << 3;
1017
1018 memcpy(ndmsg + 1, opt, opt->nd_opt_len << 3);
1019
David S. Millerc78679e2012-04-01 20:27:33 -04001020 if (nla_put(skb, NDUSEROPT_SRCADDR, sizeof(struct in6_addr),
1021 &ipv6_hdr(ra)->saddr))
1022 goto nla_put_failure;
Pierre Ynard31910572007-10-10 21:22:05 -07001023 nlmsg_end(skb, nlh);
1024
Pablo Neira Ayuso1ce85fe2009-02-24 23:18:28 -08001025 rtnl_notify(skb, net, 0, RTNLGRP_ND_USEROPT, NULL, GFP_ATOMIC);
Pierre Ynard31910572007-10-10 21:22:05 -07001026 return;
1027
1028nla_put_failure:
1029 nlmsg_free(skb);
1030 err = -EMSGSIZE;
1031errout:
Daniel Lezcanoa18bc692008-03-07 11:14:49 -08001032 rtnl_set_sk_err(net, RTNLGRP_ND_USEROPT, err);
Pierre Ynard31910572007-10-10 21:22:05 -07001033}
1034
Thomas Graf65e9b622010-09-03 02:59:14 +00001035static inline int accept_ra(struct inet6_dev *in6_dev)
1036{
1037 /*
1038 * If forwarding is enabled, RA are not accepted unless the special
1039 * hybrid mode (accept_ra=2) is enabled.
1040 */
1041 if (in6_dev->cnf.forwarding && in6_dev->cnf.accept_ra < 2)
1042 return 0;
1043
1044 return in6_dev->cnf.accept_ra;
1045}
1046
Linus Torvalds1da177e2005-04-16 15:20:36 -07001047static void ndisc_router_discovery(struct sk_buff *skb)
1048{
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -07001049 struct ra_msg *ra_msg = (struct ra_msg *)skb_transport_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001050 struct neighbour *neigh = NULL;
1051 struct inet6_dev *in6_dev;
YOSHIFUJI Hideaki65f5c7c2006-03-20 16:55:08 -08001052 struct rt6_info *rt = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001053 int lifetime;
1054 struct ndisc_options ndopts;
1055 int optlen;
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001056 unsigned int pref = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001057
1058 __u8 * opt = (__u8 *)(ra_msg + 1);
1059
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -07001060 optlen = (skb->tail - skb->transport_header) - sizeof(struct ra_msg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001061
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001062 if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) {
Joe Perches675418d2012-05-16 19:28:38 +00001063 ND_PRINTK(2, warn, "RA: source address is not link-local\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001064 return;
1065 }
1066 if (optlen < 0) {
Joe Perches675418d2012-05-16 19:28:38 +00001067 ND_PRINTK(2, warn, "RA: packet too short\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001068 return;
1069 }
1070
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001071#ifdef CONFIG_IPV6_NDISC_NODETYPE
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001072 if (skb->ndisc_nodetype == NDISC_NODETYPE_HOST) {
Joe Perches675418d2012-05-16 19:28:38 +00001073 ND_PRINTK(2, warn, "RA: from host or unauthorized router\n");
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001074 return;
1075 }
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001076#endif
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001077
Linus Torvalds1da177e2005-04-16 15:20:36 -07001078 /*
1079 * set the RA_RECV flag in the interface
1080 */
1081
Eric Dumazetcfdf7642011-07-27 21:13:03 +00001082 in6_dev = __in6_dev_get(skb->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001083 if (in6_dev == NULL) {
Joe Perches675418d2012-05-16 19:28:38 +00001084 ND_PRINTK(0, err, "RA: can't find inet6 device for %s\n",
1085 skb->dev->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001086 return;
1087 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001088
1089 if (!ndisc_parse_options(opt, optlen, &ndopts)) {
Joe Perches675418d2012-05-16 19:28:38 +00001090 ND_PRINTK(2, warn, "RA: invalid ND options\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001091 return;
1092 }
1093
Thomas Graf65e9b622010-09-03 02:59:14 +00001094 if (!accept_ra(in6_dev))
David Ward31ce8c72009-08-29 00:04:09 -07001095 goto skip_linkparms;
1096
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001097#ifdef CONFIG_IPV6_NDISC_NODETYPE
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001098 /* skip link-specific parameters from interior routers */
1099 if (skb->ndisc_nodetype == NDISC_NODETYPE_NODEFAULT)
1100 goto skip_linkparms;
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001101#endif
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001102
Linus Torvalds1da177e2005-04-16 15:20:36 -07001103 if (in6_dev->if_flags & IF_RS_SENT) {
1104 /*
1105 * flag that an RA was received after an RS was sent
1106 * out on this interface.
1107 */
1108 in6_dev->if_flags |= IF_RA_RCVD;
1109 }
1110
1111 /*
1112 * Remember the managed/otherconf flags from most recently
1113 * received RA message (RFC 2462) -- yoshfuji
1114 */
1115 in6_dev->if_flags = (in6_dev->if_flags & ~(IF_RA_MANAGED |
1116 IF_RA_OTHERCONF)) |
1117 (ra_msg->icmph.icmp6_addrconf_managed ?
1118 IF_RA_MANAGED : 0) |
1119 (ra_msg->icmph.icmp6_addrconf_other ?
1120 IF_RA_OTHERCONF : 0);
1121
YOSHIFUJI Hideaki65f5c7c2006-03-20 16:55:08 -08001122 if (!in6_dev->cnf.accept_ra_defrtr)
1123 goto skip_defrtr;
1124
Andreas Hofmeister9f562202011-10-24 19:13:15 -04001125 if (ipv6_chk_addr(dev_net(in6_dev->dev), &ipv6_hdr(skb)->saddr, NULL, 0))
1126 goto skip_defrtr;
1127
Linus Torvalds1da177e2005-04-16 15:20:36 -07001128 lifetime = ntohs(ra_msg->icmph.icmp6_rt_lifetime);
1129
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001130#ifdef CONFIG_IPV6_ROUTER_PREF
1131 pref = ra_msg->icmph.icmp6_router_pref;
1132 /* 10b is handled as if it were 00b (medium) */
YOSHIFUJI Hideaki930d6ff2006-03-20 17:05:30 -08001133 if (pref == ICMPV6_ROUTER_PREF_INVALID ||
YOSHIFUJI Hideaki6d5b78c2007-06-22 16:07:04 -07001134 !in6_dev->cnf.accept_ra_rtr_pref)
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001135 pref = ICMPV6_ROUTER_PREF_MEDIUM;
1136#endif
1137
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001138 rt = rt6_get_dflt_router(&ipv6_hdr(skb)->saddr, skb->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001139
David S. Millereb857182012-01-27 15:07:56 -08001140 if (rt) {
1141 neigh = dst_neigh_lookup(&rt->dst, &ipv6_hdr(skb)->saddr);
1142 if (!neigh) {
Joe Perches675418d2012-05-16 19:28:38 +00001143 ND_PRINTK(0, err,
1144 "RA: %s got default router without neighbour\n",
1145 __func__);
Amerigo Wang94e187c2012-10-29 00:13:19 +00001146 ip6_rt_put(rt);
David S. Millereb857182012-01-27 15:07:56 -08001147 return;
1148 }
1149 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001150 if (rt && lifetime == 0) {
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001151 ip6_del_rt(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001152 rt = NULL;
1153 }
1154
1155 if (rt == NULL && lifetime) {
Joe Perches675418d2012-05-16 19:28:38 +00001156 ND_PRINTK(3, dbg, "RA: adding default router\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001157
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001158 rt = rt6_add_dflt_router(&ipv6_hdr(skb)->saddr, skb->dev, pref);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001159 if (rt == NULL) {
Joe Perches675418d2012-05-16 19:28:38 +00001160 ND_PRINTK(0, err,
1161 "RA: %s failed to add default route\n",
1162 __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001163 return;
1164 }
1165
David S. Millereb857182012-01-27 15:07:56 -08001166 neigh = dst_neigh_lookup(&rt->dst, &ipv6_hdr(skb)->saddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001167 if (neigh == NULL) {
Joe Perches675418d2012-05-16 19:28:38 +00001168 ND_PRINTK(0, err,
1169 "RA: %s got default router without neighbour\n",
1170 __func__);
Amerigo Wang94e187c2012-10-29 00:13:19 +00001171 ip6_rt_put(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001172 return;
1173 }
1174 neigh->flags |= NTF_ROUTER;
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001175 } else if (rt) {
Pedro Ribeiro22441cf2008-10-15 15:47:49 -07001176 rt->rt6i_flags = (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001177 }
1178
1179 if (rt)
Gao feng1716a962012-04-06 00:13:10 +00001180 rt6_set_expires(rt, jiffies + (HZ * lifetime));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001181 if (ra_msg->icmph.icmp6_hop_limit) {
1182 in6_dev->cnf.hop_limit = ra_msg->icmph.icmp6_hop_limit;
1183 if (rt)
David S. Millerdefb3512010-12-08 21:16:57 -08001184 dst_metric_set(&rt->dst, RTAX_HOPLIMIT,
1185 ra_msg->icmph.icmp6_hop_limit);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001186 }
1187
YOSHIFUJI Hideaki65f5c7c2006-03-20 16:55:08 -08001188skip_defrtr:
1189
Linus Torvalds1da177e2005-04-16 15:20:36 -07001190 /*
1191 * Update Reachable Time and Retrans Timer
1192 */
1193
1194 if (in6_dev->nd_parms) {
1195 unsigned long rtime = ntohl(ra_msg->retrans_timer);
1196
1197 if (rtime && rtime/1000 < MAX_SCHEDULE_TIMEOUT/HZ) {
1198 rtime = (rtime*HZ)/1000;
1199 if (rtime < HZ/10)
1200 rtime = HZ/10;
1201 in6_dev->nd_parms->retrans_time = rtime;
1202 in6_dev->tstamp = jiffies;
1203 inet6_ifinfo_notify(RTM_NEWLINK, in6_dev);
1204 }
1205
1206 rtime = ntohl(ra_msg->reachable_time);
1207 if (rtime && rtime/1000 < MAX_SCHEDULE_TIMEOUT/(3*HZ)) {
1208 rtime = (rtime*HZ)/1000;
1209
1210 if (rtime < HZ/10)
1211 rtime = HZ/10;
1212
1213 if (rtime != in6_dev->nd_parms->base_reachable_time) {
1214 in6_dev->nd_parms->base_reachable_time = rtime;
1215 in6_dev->nd_parms->gc_staletime = 3 * rtime;
1216 in6_dev->nd_parms->reachable_time = neigh_rand_reach_time(rtime);
1217 in6_dev->tstamp = jiffies;
1218 inet6_ifinfo_notify(RTM_NEWLINK, in6_dev);
1219 }
1220 }
1221 }
1222
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001223skip_linkparms:
1224
Linus Torvalds1da177e2005-04-16 15:20:36 -07001225 /*
1226 * Process options.
1227 */
1228
1229 if (!neigh)
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001230 neigh = __neigh_lookup(&nd_tbl, &ipv6_hdr(skb)->saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001231 skb->dev, 1);
1232 if (neigh) {
1233 u8 *lladdr = NULL;
1234 if (ndopts.nd_opts_src_lladdr) {
1235 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr,
1236 skb->dev);
1237 if (!lladdr) {
Joe Perches675418d2012-05-16 19:28:38 +00001238 ND_PRINTK(2, warn,
1239 "RA: invalid link-layer address length\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001240 goto out;
1241 }
1242 }
1243 neigh_update(neigh, lladdr, NUD_STALE,
1244 NEIGH_UPDATE_F_WEAK_OVERRIDE|
1245 NEIGH_UPDATE_F_OVERRIDE|
1246 NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
1247 NEIGH_UPDATE_F_ISROUTER);
1248 }
1249
Thomas Graf65e9b622010-09-03 02:59:14 +00001250 if (!accept_ra(in6_dev))
David Ward31ce8c72009-08-29 00:04:09 -07001251 goto out;
1252
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001253#ifdef CONFIG_IPV6_ROUTE_INFO
Andreas Hofmeister9f562202011-10-24 19:13:15 -04001254 if (ipv6_chk_addr(dev_net(in6_dev->dev), &ipv6_hdr(skb)->saddr, NULL, 0))
1255 goto skip_routeinfo;
1256
YOSHIFUJI Hideaki09c884d2006-03-20 17:07:03 -08001257 if (in6_dev->cnf.accept_ra_rtr_pref && ndopts.nd_opts_ri) {
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001258 struct nd_opt_hdr *p;
1259 for (p = ndopts.nd_opts_ri;
1260 p;
1261 p = ndisc_next_option(p, ndopts.nd_opts_ri_end)) {
YOSHIFUJI Hideaki6294e002008-03-15 23:56:52 -04001262 struct route_info *ri = (struct route_info *)p;
1263#ifdef CONFIG_IPV6_NDISC_NODETYPE
1264 if (skb->ndisc_nodetype == NDISC_NODETYPE_NODEFAULT &&
1265 ri->prefix_len == 0)
1266 continue;
1267#endif
1268 if (ri->prefix_len > in6_dev->cnf.accept_ra_rt_info_max_plen)
YOSHIFUJI Hideaki09c884d2006-03-20 17:07:03 -08001269 continue;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001270 rt6_route_rcv(skb->dev, (u8*)p, (p->nd_opt_len) << 3,
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001271 &ipv6_hdr(skb)->saddr);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001272 }
1273 }
Andreas Hofmeister9f562202011-10-24 19:13:15 -04001274
1275skip_routeinfo:
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001276#endif
1277
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001278#ifdef CONFIG_IPV6_NDISC_NODETYPE
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001279 /* skip link-specific ndopts from interior routers */
1280 if (skb->ndisc_nodetype == NDISC_NODETYPE_NODEFAULT)
1281 goto out;
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001282#endif
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001283
YOSHIFUJI Hideakic4fd30e2006-03-20 16:55:26 -08001284 if (in6_dev->cnf.accept_ra_pinfo && ndopts.nd_opts_pi) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001285 struct nd_opt_hdr *p;
1286 for (p = ndopts.nd_opts_pi;
1287 p;
1288 p = ndisc_next_option(p, ndopts.nd_opts_pi_end)) {
Neil Hormane6bff992012-01-04 10:49:15 +00001289 addrconf_prefix_rcv(skb->dev, (u8 *)p,
1290 (p->nd_opt_len) << 3,
1291 ndopts.nd_opts_src_lladdr != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001292 }
1293 }
1294
1295 if (ndopts.nd_opts_mtu) {
Al Viroe69a4ad2006-11-14 20:56:00 -08001296 __be32 n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001297 u32 mtu;
1298
Al Viroe69a4ad2006-11-14 20:56:00 -08001299 memcpy(&n, ((u8*)(ndopts.nd_opts_mtu+1))+2, sizeof(mtu));
1300 mtu = ntohl(n);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001301
1302 if (mtu < IPV6_MIN_MTU || mtu > skb->dev->mtu) {
Joe Perches675418d2012-05-16 19:28:38 +00001303 ND_PRINTK(2, warn, "RA: invalid mtu: %d\n", mtu);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001304 } else if (in6_dev->cnf.mtu6 != mtu) {
1305 in6_dev->cnf.mtu6 = mtu;
1306
1307 if (rt)
David S. Millerdefb3512010-12-08 21:16:57 -08001308 dst_metric_set(&rt->dst, RTAX_MTU, mtu);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001309
1310 rt6_mtu_change(skb->dev, mtu);
1311 }
1312 }
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001313
Pierre Ynard31910572007-10-10 21:22:05 -07001314 if (ndopts.nd_useropts) {
YOSHIFUJI Hideaki61cf46ad2008-01-22 17:32:53 +09001315 struct nd_opt_hdr *p;
1316 for (p = ndopts.nd_useropts;
1317 p;
1318 p = ndisc_next_useropt(p, ndopts.nd_useropts_end)) {
1319 ndisc_ra_useropt(skb, p);
Pierre Ynard31910572007-10-10 21:22:05 -07001320 }
1321 }
1322
Linus Torvalds1da177e2005-04-16 15:20:36 -07001323 if (ndopts.nd_opts_tgt_lladdr || ndopts.nd_opts_rh) {
Joe Perches675418d2012-05-16 19:28:38 +00001324 ND_PRINTK(2, warn, "RA: invalid RA options\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001325 }
1326out:
Amerigo Wang94e187c2012-10-29 00:13:19 +00001327 ip6_rt_put(rt);
David S. Millereb857182012-01-27 15:07:56 -08001328 if (neigh)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001329 neigh_release(neigh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001330}
1331
1332static void ndisc_redirect_rcv(struct sk_buff *skb)
1333{
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001334#ifdef CONFIG_IPV6_NDISC_NODETYPE
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001335 switch (skb->ndisc_nodetype) {
1336 case NDISC_NODETYPE_HOST:
1337 case NDISC_NODETYPE_NODEFAULT:
Joe Perches675418d2012-05-16 19:28:38 +00001338 ND_PRINTK(2, warn,
1339 "Redirect: from host or unauthorized router\n");
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001340 return;
1341 }
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001342#endif
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001343
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001344 if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) {
Joe Perches675418d2012-05-16 19:28:38 +00001345 ND_PRINTK(2, warn,
1346 "Redirect: source address is not link-local\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001347 return;
1348 }
1349
David S. Millerb94f1c02012-07-12 00:33:37 -07001350 icmpv6_notify(skb, NDISC_REDIRECT, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001351}
1352
David S. Miller49919692012-01-27 15:30:48 -08001353void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001354{
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001355 struct net_device *dev = skb->dev;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001356 struct net *net = dev_net(dev);
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001357 struct sock *sk = net->ipv6.ndisc_sk;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001358 int len = sizeof(struct icmp6hdr) + 2 * sizeof(struct in6_addr);
David S. Millerfbfe95a2012-06-08 23:24:18 -07001359 struct inet_peer *peer;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001360 struct sk_buff *buff;
1361 struct icmp6hdr *icmph;
1362 struct in6_addr saddr_buf;
1363 struct in6_addr *addrp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001364 struct rt6_info *rt;
1365 struct dst_entry *dst;
1366 struct inet6_dev *idev;
David S. Miller4c9483b2011-03-12 16:22:43 -05001367 struct flowi6 fl6;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001368 u8 *opt;
Herbert Xua7ae1992011-11-18 02:20:04 +00001369 int hlen, tlen;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001370 int rd_len;
1371 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001372 u8 ha_buf[MAX_ADDR_LEN], *ha = NULL;
David S. Miller1d861aa2012-07-10 03:58:16 -07001373 bool ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001374
Neil Horman95c385b2007-04-25 17:08:10 -07001375 if (ipv6_get_lladdr(dev, &saddr_buf, IFA_F_TENTATIVE)) {
Joe Perches675418d2012-05-16 19:28:38 +00001376 ND_PRINTK(2, warn, "Redirect: no link-local address on %s\n",
1377 dev->name);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001378 return;
1379 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001380
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001381 if (!ipv6_addr_equal(&ipv6_hdr(skb)->daddr, target) &&
Brian Haleybf0b48d2007-10-08 00:12:05 -07001382 ipv6_addr_type(target) != (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) {
Joe Perches675418d2012-05-16 19:28:38 +00001383 ND_PRINTK(2, warn,
1384 "Redirect: target address is not link-local unicast\n");
Li Yewang29556522007-01-30 14:33:20 -08001385 return;
1386 }
1387
David S. Miller4c9483b2011-03-12 16:22:43 -05001388 icmpv6_flow_init(sk, &fl6, NDISC_REDIRECT,
YOSHIFUJI Hideaki95e41e92007-12-06 15:43:30 -08001389 &saddr_buf, &ipv6_hdr(skb)->saddr, dev->ifindex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001390
David S. Miller4c9483b2011-03-12 16:22:43 -05001391 dst = ip6_route_output(net, NULL, &fl6);
RongQing.Li5095d642012-02-21 22:10:49 +00001392 if (dst->error) {
1393 dst_release(dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001394 return;
RongQing.Li5095d642012-02-21 22:10:49 +00001395 }
David S. Miller4c9483b2011-03-12 16:22:43 -05001396 dst = xfrm_lookup(net, dst, flowi6_to_flowi(&fl6), NULL, 0);
David S. Miller452edd52011-03-02 13:27:41 -08001397 if (IS_ERR(dst))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001398 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001399
1400 rt = (struct rt6_info *) dst;
1401
1402 if (rt->rt6i_flags & RTF_GATEWAY) {
Joe Perches675418d2012-05-16 19:28:38 +00001403 ND_PRINTK(2, warn,
1404 "Redirect: destination is not a neighbour\n");
Ilpo Järvinend73f0802009-02-06 23:47:37 -08001405 goto release;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001406 }
David S. Miller1d861aa2012-07-10 03:58:16 -07001407 peer = inet_getpeer_v6(net->ipv6.peers, &rt->rt6i_dst.addr, 1);
1408 ret = inet_peer_xrlim_allow(peer, 1*HZ);
1409 if (peer)
1410 inet_putpeer(peer);
1411 if (!ret)
Ilpo Järvinend73f0802009-02-06 23:47:37 -08001412 goto release;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001413
1414 if (dev->addr_len) {
David S. Miller49919692012-01-27 15:30:48 -08001415 struct neighbour *neigh = dst_neigh_lookup(skb_dst(skb), target);
1416 if (!neigh) {
Joe Perches675418d2012-05-16 19:28:38 +00001417 ND_PRINTK(2, warn,
1418 "Redirect: no neigh for target address\n");
David S. Miller49919692012-01-27 15:30:48 -08001419 goto release;
1420 }
1421
Linus Torvalds1da177e2005-04-16 15:20:36 -07001422 read_lock_bh(&neigh->lock);
1423 if (neigh->nud_state & NUD_VALID) {
1424 memcpy(ha_buf, neigh->ha, dev->addr_len);
1425 read_unlock_bh(&neigh->lock);
1426 ha = ha_buf;
1427 len += ndisc_opt_addr_space(dev);
1428 } else
1429 read_unlock_bh(&neigh->lock);
David S. Miller49919692012-01-27 15:30:48 -08001430
1431 neigh_release(neigh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001432 }
1433
1434 rd_len = min_t(unsigned int,
1435 IPV6_MIN_MTU-sizeof(struct ipv6hdr)-len, skb->len + 8);
1436 rd_len &= ~0x7;
1437 len += rd_len;
1438
Herbert Xua7ae1992011-11-18 02:20:04 +00001439 hlen = LL_RESERVED_SPACE(dev);
1440 tlen = dev->needed_tailroom;
David S. Millerd54a81d2006-12-02 21:00:06 -08001441 buff = sock_alloc_send_skb(sk,
1442 (MAX_HEADER + sizeof(struct ipv6hdr) +
Herbert Xua7ae1992011-11-18 02:20:04 +00001443 len + hlen + tlen),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001444 1, &err);
1445 if (buff == NULL) {
Joe Perches675418d2012-05-16 19:28:38 +00001446 ND_PRINTK(0, err,
1447 "Redirect: %s failed to allocate an skb, err=%d\n",
1448 __func__, err);
Ilpo Järvinend73f0802009-02-06 23:47:37 -08001449 goto release;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001450 }
1451
Herbert Xua7ae1992011-11-18 02:20:04 +00001452 skb_reserve(buff, hlen);
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001453 ip6_nd_hdr(sk, buff, dev, &saddr_buf, &ipv6_hdr(skb)->saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001454 IPPROTO_ICMPV6, len);
1455
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -07001456 skb_set_transport_header(buff, skb_tail_pointer(buff) - buff->data);
Arnaldo Carvalho de Melod10ba342007-03-14 21:05:37 -03001457 skb_put(buff, len);
1458 icmph = icmp6_hdr(buff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001459
1460 memset(icmph, 0, sizeof(struct icmp6hdr));
1461 icmph->icmp6_type = NDISC_REDIRECT;
1462
1463 /*
1464 * copy target and destination addresses
1465 */
1466
1467 addrp = (struct in6_addr *)(icmph + 1);
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001468 *addrp = *target;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001469 addrp++;
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001470 *addrp = ipv6_hdr(skb)->daddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001471
1472 opt = (u8*) (addrp + 1);
1473
1474 /*
1475 * include target_address option
1476 */
1477
1478 if (ha)
1479 opt = ndisc_fill_addr_option(opt, ND_OPT_TARGET_LL_ADDR, ha,
1480 dev->addr_len, dev->type);
1481
1482 /*
1483 * build redirect option and copy skb over to the new packet.
1484 */
1485
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001486 memset(opt, 0, 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001487 *(opt++) = ND_OPT_REDIRECT_HDR;
1488 *(opt++) = (rd_len >> 3);
1489 opt += 6;
1490
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001491 memcpy(opt, ipv6_hdr(skb), rd_len - 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001492
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001493 icmph->icmp6_cksum = csum_ipv6_magic(&saddr_buf, &ipv6_hdr(skb)->saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001494 len, IPPROTO_ICMPV6,
Joe Perches07f07572008-11-19 15:44:53 -08001495 csum_partial(icmph, len, 0));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001496
Eric Dumazetadf30902009-06-02 05:19:30 +00001497 skb_dst_set(buff, dst);
Eric Dumazetcfdf7642011-07-27 21:13:03 +00001498 rcu_read_lock();
1499 idev = __in6_dev_get(dst->dev);
Neil Hormanedf391f2009-04-27 02:45:02 -07001500 IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUT, skb->len);
Jan Engelhardtb2e0b382010-03-23 04:09:07 +01001501 err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, buff, NULL, dst->dev,
Patrick McHardy6e23ae22007-11-19 18:53:30 -08001502 dst_output);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001503 if (!err) {
Denis V. Lunev5c5d2442008-10-08 10:33:50 -07001504 ICMP6MSGOUT_INC_STATS(net, idev, NDISC_REDIRECT);
Denis V. Luneva862f6a2008-10-08 10:33:06 -07001505 ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001506 }
1507
Eric Dumazetcfdf7642011-07-27 21:13:03 +00001508 rcu_read_unlock();
Ilpo Järvinend73f0802009-02-06 23:47:37 -08001509 return;
1510
1511release:
1512 dst_release(dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001513}
1514
1515static void pndisc_redo(struct sk_buff *skb)
1516{
YOSHIFUJI Hideaki140e26fc2005-10-05 12:11:41 -07001517 ndisc_recv_ns(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001518 kfree_skb(skb);
1519}
1520
1521int ndisc_rcv(struct sk_buff *skb)
1522{
1523 struct nd_msg *msg;
1524
1525 if (!pskb_may_pull(skb, skb->len))
1526 return 0;
1527
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -07001528 msg = (struct nd_msg *)skb_transport_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001529
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -07001530 __skb_push(skb, skb->data - skb_transport_header(skb));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001531
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001532 if (ipv6_hdr(skb)->hop_limit != 255) {
Joe Perches675418d2012-05-16 19:28:38 +00001533 ND_PRINTK(2, warn, "NDISC: invalid hop-limit: %d\n",
1534 ipv6_hdr(skb)->hop_limit);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001535 return 0;
1536 }
1537
1538 if (msg->icmph.icmp6_code != 0) {
Joe Perches675418d2012-05-16 19:28:38 +00001539 ND_PRINTK(2, warn, "NDISC: invalid ICMPv6 code: %d\n",
1540 msg->icmph.icmp6_code);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001541 return 0;
1542 }
1543
Patrick McHardya61bbcf2005-08-14 17:24:31 -07001544 memset(NEIGH_CB(skb), 0, sizeof(struct neighbour_cb));
1545
Linus Torvalds1da177e2005-04-16 15:20:36 -07001546 switch (msg->icmph.icmp6_type) {
1547 case NDISC_NEIGHBOUR_SOLICITATION:
1548 ndisc_recv_ns(skb);
1549 break;
1550
1551 case NDISC_NEIGHBOUR_ADVERTISEMENT:
1552 ndisc_recv_na(skb);
1553 break;
1554
1555 case NDISC_ROUTER_SOLICITATION:
1556 ndisc_recv_rs(skb);
1557 break;
1558
1559 case NDISC_ROUTER_ADVERTISEMENT:
1560 ndisc_router_discovery(skb);
1561 break;
1562
1563 case NDISC_REDIRECT:
1564 ndisc_redirect_rcv(skb);
1565 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -07001566 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001567
1568 return 0;
1569}
1570
1571static int ndisc_netdev_event(struct notifier_block *this, unsigned long event, void *ptr)
1572{
1573 struct net_device *dev = ptr;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001574 struct net *net = dev_net(dev);
Hannes Frederic Sowa5cb04432012-11-06 16:46:20 +00001575 struct inet6_dev *idev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001576
1577 switch (event) {
1578 case NETDEV_CHANGEADDR:
1579 neigh_changeaddr(&nd_tbl, dev);
Daniel Lezcano5b7c9312008-03-03 23:28:58 -08001580 fib6_run_gc(~0UL, net);
Hannes Frederic Sowa5cb04432012-11-06 16:46:20 +00001581 idev = in6_dev_get(dev);
1582 if (!idev)
1583 break;
1584 if (idev->cnf.ndisc_notify)
1585 ndisc_send_unsol_na(dev);
1586 in6_dev_put(idev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001587 break;
1588 case NETDEV_DOWN:
1589 neigh_ifdown(&nd_tbl, dev);
Daniel Lezcano5b7c9312008-03-03 23:28:58 -08001590 fib6_run_gc(~0UL, net);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001591 break;
Ben Hutchingsf47b9462011-04-15 13:46:02 +00001592 case NETDEV_NOTIFY_PEERS:
1593 ndisc_send_unsol_na(dev);
1594 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001595 default:
1596 break;
1597 }
1598
1599 return NOTIFY_DONE;
1600}
1601
1602static struct notifier_block ndisc_netdev_notifier = {
1603 .notifier_call = ndisc_netdev_event,
1604};
1605
1606#ifdef CONFIG_SYSCTL
1607static void ndisc_warn_deprecated_sysctl(struct ctl_table *ctl,
1608 const char *func, const char *dev_name)
1609{
1610 static char warncomm[TASK_COMM_LEN];
1611 static int warned;
1612 if (strcmp(warncomm, current->comm) && warned < 5) {
1613 strcpy(warncomm, current->comm);
Joe Perchesf3213832012-05-15 14:11:53 +00001614 pr_warn("process `%s' is using deprecated sysctl (%s) net.ipv6.neigh.%s.%s - use net.ipv6.neigh.%s.%s_ms instead\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001615 warncomm, func,
1616 dev_name, ctl->procname,
1617 dev_name, ctl->procname);
1618 warned++;
1619 }
1620}
1621
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001622int 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 -07001623{
1624 struct net_device *dev = ctl->extra1;
1625 struct inet6_dev *idev;
1626 int ret;
1627
Eric W. Biedermand12af672007-10-18 03:05:25 -07001628 if ((strcmp(ctl->procname, "retrans_time") == 0) ||
1629 (strcmp(ctl->procname, "base_reachable_time") == 0))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001630 ndisc_warn_deprecated_sysctl(ctl, "syscall", dev ? dev->name : "default");
1631
Eric W. Biedermand12af672007-10-18 03:05:25 -07001632 if (strcmp(ctl->procname, "retrans_time") == 0)
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001633 ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
Eric W. Biedermand12af672007-10-18 03:05:25 -07001634
1635 else if (strcmp(ctl->procname, "base_reachable_time") == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001636 ret = proc_dointvec_jiffies(ctl, write,
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001637 buffer, lenp, ppos);
Eric W. Biedermand12af672007-10-18 03:05:25 -07001638
1639 else if ((strcmp(ctl->procname, "retrans_time_ms") == 0) ||
YOSHIFUJI Hideakiad02ac12007-10-29 01:32:23 -07001640 (strcmp(ctl->procname, "base_reachable_time_ms") == 0))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001641 ret = proc_dointvec_ms_jiffies(ctl, write,
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001642 buffer, lenp, ppos);
Eric W. Biedermand12af672007-10-18 03:05:25 -07001643 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001644 ret = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001645
1646 if (write && ret == 0 && dev && (idev = in6_dev_get(dev)) != NULL) {
Eric W. Biedermand12af672007-10-18 03:05:25 -07001647 if (ctl->data == &idev->nd_parms->base_reachable_time)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001648 idev->nd_parms->reachable_time = neigh_rand_reach_time(idev->nd_parms->base_reachable_time);
1649 idev->tstamp = jiffies;
1650 inet6_ifinfo_notify(RTM_NEWLINK, idev);
1651 in6_dev_put(idev);
1652 }
1653 return ret;
1654}
1655
Linus Torvalds1da177e2005-04-16 15:20:36 -07001656
1657#endif
1658
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00001659static int __net_init ndisc_net_init(struct net *net)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001660{
1661 struct ipv6_pinfo *np;
1662 struct sock *sk;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001663 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001664
Denis V. Lunev1ed85162008-04-03 14:31:03 -07001665 err = inet_ctl_sock_create(&sk, PF_INET6,
1666 SOCK_RAW, IPPROTO_ICMPV6, net);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001667 if (err < 0) {
Joe Perches675418d2012-05-16 19:28:38 +00001668 ND_PRINTK(0, err,
1669 "NDISC: Failed to initialize the control socket (err %d)\n",
1670 err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001671 return err;
1672 }
1673
Denis V. Lunev1ed85162008-04-03 14:31:03 -07001674 net->ipv6.ndisc_sk = sk;
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001675
Linus Torvalds1da177e2005-04-16 15:20:36 -07001676 np = inet6_sk(sk);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001677 np->hop_limit = 255;
1678 /* Do not loopback ndisc messages */
1679 np->mc_loop = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001680
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001681 return 0;
1682}
1683
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00001684static void __net_exit ndisc_net_exit(struct net *net)
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001685{
Denis V. Lunev1ed85162008-04-03 14:31:03 -07001686 inet_ctl_sock_destroy(net->ipv6.ndisc_sk);
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001687}
1688
1689static struct pernet_operations ndisc_net_ops = {
1690 .init = ndisc_net_init,
1691 .exit = ndisc_net_exit,
1692};
1693
1694int __init ndisc_init(void)
1695{
1696 int err;
1697
1698 err = register_pernet_subsys(&ndisc_net_ops);
1699 if (err)
1700 return err;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001701 /*
1702 * Initialize the neighbour table
1703 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001704 neigh_table_init(&nd_tbl);
1705
1706#ifdef CONFIG_SYSCTL
Eric W. Biederman54716e32010-02-14 03:27:03 +00001707 err = neigh_sysctl_register(NULL, &nd_tbl.parms, "ipv6",
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001708 &ndisc_ifinfo_sysctl_change);
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001709 if (err)
1710 goto out_unregister_pernet;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001711#endif
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001712 err = register_netdevice_notifier(&ndisc_netdev_notifier);
1713 if (err)
1714 goto out_unregister_sysctl;
1715out:
1716 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001717
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001718out_unregister_sysctl:
1719#ifdef CONFIG_SYSCTL
1720 neigh_sysctl_unregister(&nd_tbl.parms);
1721out_unregister_pernet:
1722#endif
1723 unregister_pernet_subsys(&ndisc_net_ops);
1724 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001725}
1726
1727void ndisc_cleanup(void)
1728{
Dmitry Mishin36f73d02006-11-03 16:08:19 -08001729 unregister_netdevice_notifier(&ndisc_netdev_notifier);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001730#ifdef CONFIG_SYSCTL
1731 neigh_sysctl_unregister(&nd_tbl.parms);
1732#endif
1733 neigh_table_clear(&nd_tbl);
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001734 unregister_pernet_subsys(&ndisc_net_ops);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001735}