blob: 2edce30ef7338cdf1916eef98b353c7dbbc7bcda [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;
Hannes Frederic Sowa60713a02012-11-06 16:18:41 +0000538 struct in6_addr mcaddr = IN6ADDR_LINKLOCAL_ALLNODES_INIT;
Ben Hutchingsf47b9462011-04-15 13:46:02 +0000539
540 idev = in6_dev_get(dev);
541 if (!idev)
542 return;
543
544 read_lock_bh(&idev->lock);
545 list_for_each_entry(ifa, &idev->addr_list, if_list) {
Ben Hutchingsf47b9462011-04-15 13:46:02 +0000546 ndisc_send_na(dev, NULL, &mcaddr, &ifa->addr,
547 /*router=*/ !!idev->cnf.forwarding,
548 /*solicited=*/ false, /*override=*/ true,
549 /*inc_opt=*/ true);
550 }
551 read_unlock_bh(&idev->lock);
552
553 in6_dev_put(idev);
554}
555
Linus Torvalds1da177e2005-04-16 15:20:36 -0700556void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +0900557 const struct in6_addr *solicit,
558 const struct in6_addr *daddr, const struct in6_addr *saddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700559{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700560 struct in6_addr addr_buf;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900561 struct icmp6hdr icmp6h = {
562 .icmp6_type = NDISC_NEIGHBOUR_SOLICITATION,
563 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564
565 if (saddr == NULL) {
Neil Horman95c385b2007-04-25 17:08:10 -0700566 if (ipv6_get_lladdr(dev, &addr_buf,
567 (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700568 return;
569 saddr = &addr_buf;
570 }
571
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900572 __ndisc_send(dev, neigh, daddr, saddr,
573 &icmp6h, solicit,
David L Stevens14878f72007-09-16 16:52:35 -0700574 !ipv6_addr_any(saddr) ? ND_OPT_SOURCE_LL_ADDR : 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575}
576
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +0900577void ndisc_send_rs(struct net_device *dev, const struct in6_addr *saddr,
578 const struct in6_addr *daddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579{
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900580 struct icmp6hdr icmp6h = {
581 .icmp6_type = NDISC_ROUTER_SOLICITATION,
582 };
Neil Horman95c385b2007-04-25 17:08:10 -0700583 int send_sllao = dev->addr_len;
Neil Horman95c385b2007-04-25 17:08:10 -0700584
585#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
586 /*
587 * According to section 2.2 of RFC 4429, we must not
588 * send router solicitations with a sllao from
589 * optimistic addresses, but we may send the solicitation
590 * if we don't include the sllao. So here we check
591 * if our address is optimistic, and if so, we
Joe Perchesbea85192007-12-20 14:01:35 -0800592 * suppress the inclusion of the sllao.
Neil Horman95c385b2007-04-25 17:08:10 -0700593 */
594 if (send_sllao) {
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900595 struct inet6_ifaddr *ifp = ipv6_get_ifaddr(dev_net(dev), saddr,
Daniel Lezcano1cab3da2008-01-10 22:44:09 -0800596 dev, 1);
Neil Horman95c385b2007-04-25 17:08:10 -0700597 if (ifp) {
598 if (ifp->flags & IFA_F_OPTIMISTIC) {
YOSHIFUJI Hideakica043562007-02-28 23:13:20 +0900599 send_sllao = 0;
Neil Horman95c385b2007-04-25 17:08:10 -0700600 }
YOSHIFUJI Hideakica043562007-02-28 23:13:20 +0900601 in6_ifa_put(ifp);
Neil Horman95c385b2007-04-25 17:08:10 -0700602 } else {
603 send_sllao = 0;
604 }
605 }
606#endif
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900607 __ndisc_send(dev, NULL, daddr, saddr,
608 &icmp6h, NULL,
David L Stevens14878f72007-09-16 16:52:35 -0700609 send_sllao ? ND_OPT_SOURCE_LL_ADDR : 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700610}
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900611
Linus Torvalds1da177e2005-04-16 15:20:36 -0700612
613static void ndisc_error_report(struct neighbour *neigh, struct sk_buff *skb)
614{
615 /*
616 * "The sender MUST return an ICMP
617 * destination unreachable"
618 */
619 dst_link_failure(skb);
620 kfree_skb(skb);
621}
622
623/* Called with locked neigh: either read or both */
624
625static void ndisc_solicit(struct neighbour *neigh, struct sk_buff *skb)
626{
627 struct in6_addr *saddr = NULL;
628 struct in6_addr mcaddr;
629 struct net_device *dev = neigh->dev;
630 struct in6_addr *target = (struct in6_addr *)&neigh->primary_key;
631 int probes = atomic_read(&neigh->probes);
632
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900633 if (skb && ipv6_chk_addr(dev_net(dev), &ipv6_hdr(skb)->saddr, dev, 1))
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -0700634 saddr = &ipv6_hdr(skb)->saddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700635
636 if ((probes -= neigh->parms->ucast_probes) < 0) {
637 if (!(neigh->nud_state & NUD_VALID)) {
Joe Perches675418d2012-05-16 19:28:38 +0000638 ND_PRINTK(1, dbg,
639 "%s: trying to ucast probe in NUD_INVALID: %pI6\n",
640 __func__, target);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700641 }
642 ndisc_send_ns(dev, neigh, target, target, saddr);
643 } else if ((probes -= neigh->parms->app_probes) < 0) {
644#ifdef CONFIG_ARPD
645 neigh_app_ns(neigh);
646#endif
647 } else {
648 addrconf_addr_solict_mult(target, &mcaddr);
649 ndisc_send_ns(dev, NULL, target, &mcaddr, saddr);
650 }
651}
652
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900653static int pndisc_is_router(const void *pkey,
654 struct net_device *dev)
Pavel Emelyanovfa86d322008-03-24 14:48:59 -0700655{
656 struct pneigh_entry *n;
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900657 int ret = -1;
Pavel Emelyanovfa86d322008-03-24 14:48:59 -0700658
659 read_lock_bh(&nd_tbl.lock);
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900660 n = __pneigh_lookup(&nd_tbl, dev_net(dev), pkey, dev);
661 if (n)
662 ret = !!(n->flags & NTF_ROUTER);
Pavel Emelyanovfa86d322008-03-24 14:48:59 -0700663 read_unlock_bh(&nd_tbl.lock);
664
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900665 return ret;
Pavel Emelyanovfa86d322008-03-24 14:48:59 -0700666}
667
Linus Torvalds1da177e2005-04-16 15:20:36 -0700668static void ndisc_recv_ns(struct sk_buff *skb)
669{
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -0700670 struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb);
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000671 const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
672 const struct in6_addr *daddr = &ipv6_hdr(skb)->daddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700673 u8 *lladdr = NULL;
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -0700674 u32 ndoptlen = skb->tail - (skb->transport_header +
675 offsetof(struct nd_msg, opt));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700676 struct ndisc_options ndopts;
677 struct net_device *dev = skb->dev;
678 struct inet6_ifaddr *ifp;
679 struct inet6_dev *idev = NULL;
680 struct neighbour *neigh;
681 int dad = ipv6_addr_any(saddr);
Eric Dumazeta50feda2012-05-18 18:57:34 +0000682 bool inc;
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900683 int is_router = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700684
685 if (ipv6_addr_is_multicast(&msg->target)) {
Joe Perches675418d2012-05-16 19:28:38 +0000686 ND_PRINTK(2, warn, "NS: multicast target address\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700687 return;
688 }
689
690 /*
691 * RFC2461 7.1.1:
692 * DAD has to be destined for solicited node multicast address.
693 */
694 if (dad &&
695 !(daddr->s6_addr32[0] == htonl(0xff020000) &&
696 daddr->s6_addr32[1] == htonl(0x00000000) &&
697 daddr->s6_addr32[2] == htonl(0x00000001) &&
698 daddr->s6_addr [12] == 0xff )) {
Joe Perches675418d2012-05-16 19:28:38 +0000699 ND_PRINTK(2, warn, "NS: bad DAD packet (wrong destination)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700700 return;
701 }
702
703 if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts)) {
Joe Perches675418d2012-05-16 19:28:38 +0000704 ND_PRINTK(2, warn, "NS: invalid ND options\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700705 return;
706 }
707
708 if (ndopts.nd_opts_src_lladdr) {
709 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr, dev);
710 if (!lladdr) {
Joe Perches675418d2012-05-16 19:28:38 +0000711 ND_PRINTK(2, warn,
712 "NS: invalid link-layer address length\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700713 return;
714 }
715
716 /* RFC2461 7.1.1:
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900717 * If the IP source address is the unspecified address,
718 * there MUST NOT be source link-layer address option
Linus Torvalds1da177e2005-04-16 15:20:36 -0700719 * in the message.
720 */
721 if (dad) {
Joe Perches675418d2012-05-16 19:28:38 +0000722 ND_PRINTK(2, warn,
723 "NS: bad DAD packet (link-layer address option)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700724 return;
725 }
726 }
727
728 inc = ipv6_addr_is_multicast(daddr);
729
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900730 ifp = ipv6_get_ifaddr(dev_net(dev), &msg->target, dev, 1);
Daniel Lezcanoa18bc692008-03-07 11:14:49 -0800731 if (ifp) {
Neil Horman95c385b2007-04-25 17:08:10 -0700732
733 if (ifp->flags & (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)) {
734 if (dad) {
Neil Horman95c385b2007-04-25 17:08:10 -0700735 /*
736 * We are colliding with another node
737 * who is doing DAD
738 * so fail our DAD process
739 */
740 addrconf_dad_failure(ifp);
Denis V. Lunev9e3be4b2007-09-11 11:04:49 +0200741 return;
Neil Horman95c385b2007-04-25 17:08:10 -0700742 } else {
743 /*
744 * This is not a dad solicitation.
745 * If we are an optimistic node,
746 * we should respond.
747 * Otherwise, we should ignore it.
748 */
749 if (!(ifp->flags & IFA_F_OPTIMISTIC))
750 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700751 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700752 }
753
754 idev = ifp->idev;
755 } else {
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700756 struct net *net = dev_net(dev);
757
Linus Torvalds1da177e2005-04-16 15:20:36 -0700758 idev = in6_dev_get(dev);
759 if (!idev) {
760 /* XXX: count this drop? */
761 return;
762 }
763
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700764 if (ipv6_chk_acast_addr(net, dev, &msg->target) ||
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900765 (idev->cnf.forwarding &&
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700766 (net->ipv6.devconf_all->proxy_ndp || idev->cnf.proxy_ndp) &&
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900767 (is_router = pndisc_is_router(&msg->target, dev)) >= 0)) {
Patrick McHardya61bbcf2005-08-14 17:24:31 -0700768 if (!(NEIGH_CB(skb)->flags & LOCALLY_ENQUEUED) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700769 skb->pkt_type != PACKET_HOST &&
770 inc != 0 &&
771 idev->nd_parms->proxy_delay != 0) {
772 /*
773 * for anycast or proxy,
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900774 * sender should delay its response
775 * by a random time between 0 and
Linus Torvalds1da177e2005-04-16 15:20:36 -0700776 * MAX_ANYCAST_DELAY_TIME seconds.
777 * (RFC2461) -- yoshfuji
778 */
779 struct sk_buff *n = skb_clone(skb, GFP_ATOMIC);
780 if (n)
781 pneigh_enqueue(&nd_tbl, idev->nd_parms, n);
782 goto out;
783 }
784 } else
785 goto out;
786 }
787
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900788 if (is_router < 0)
789 is_router = !!idev->cnf.forwarding;
Ville Nuorvala62dd9312006-09-22 14:43:19 -0700790
Linus Torvalds1da177e2005-04-16 15:20:36 -0700791 if (dad) {
YOSHIFUJI Hideakif3ee4012008-04-10 15:42:11 +0900792 ndisc_send_na(dev, NULL, &in6addr_linklocal_allnodes, &msg->target,
Ville Nuorvala62dd9312006-09-22 14:43:19 -0700793 is_router, 0, (ifp != NULL), 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794 goto out;
795 }
796
797 if (inc)
798 NEIGH_CACHE_STAT_INC(&nd_tbl, rcv_probes_mcast);
799 else
800 NEIGH_CACHE_STAT_INC(&nd_tbl, rcv_probes_ucast);
801
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900802 /*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700803 * update / create cache entry
804 * for the source address
805 */
806 neigh = __neigh_lookup(&nd_tbl, saddr, dev,
807 !inc || lladdr || !dev->addr_len);
808 if (neigh)
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900809 neigh_update(neigh, lladdr, NUD_STALE,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700810 NEIGH_UPDATE_F_WEAK_OVERRIDE|
811 NEIGH_UPDATE_F_OVERRIDE);
Stephen Hemminger3b04ddd2007-10-09 01:40:57 -0700812 if (neigh || !dev->header_ops) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700813 ndisc_send_na(dev, neigh, saddr, &msg->target,
Ville Nuorvala62dd9312006-09-22 14:43:19 -0700814 is_router,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700815 1, (ifp != NULL && inc), inc);
816 if (neigh)
817 neigh_release(neigh);
818 }
819
820out:
821 if (ifp)
822 in6_ifa_put(ifp);
823 else
824 in6_dev_put(idev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700825}
826
827static void ndisc_recv_na(struct sk_buff *skb)
828{
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -0700829 struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb);
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000830 const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
831 const struct in6_addr *daddr = &ipv6_hdr(skb)->daddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700832 u8 *lladdr = NULL;
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -0700833 u32 ndoptlen = skb->tail - (skb->transport_header +
834 offsetof(struct nd_msg, opt));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700835 struct ndisc_options ndopts;
836 struct net_device *dev = skb->dev;
837 struct inet6_ifaddr *ifp;
838 struct neighbour *neigh;
839
840 if (skb->len < sizeof(struct nd_msg)) {
Joe Perches675418d2012-05-16 19:28:38 +0000841 ND_PRINTK(2, warn, "NA: packet too short\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700842 return;
843 }
844
845 if (ipv6_addr_is_multicast(&msg->target)) {
Joe Perches675418d2012-05-16 19:28:38 +0000846 ND_PRINTK(2, warn, "NA: target address is multicast\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700847 return;
848 }
849
850 if (ipv6_addr_is_multicast(daddr) &&
851 msg->icmph.icmp6_solicited) {
Joe Perches675418d2012-05-16 19:28:38 +0000852 ND_PRINTK(2, warn, "NA: solicited NA is multicasted\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700853 return;
854 }
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900855
Linus Torvalds1da177e2005-04-16 15:20:36 -0700856 if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts)) {
Joe Perches675418d2012-05-16 19:28:38 +0000857 ND_PRINTK(2, warn, "NS: invalid ND option\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700858 return;
859 }
860 if (ndopts.nd_opts_tgt_lladdr) {
861 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr, dev);
862 if (!lladdr) {
Joe Perches675418d2012-05-16 19:28:38 +0000863 ND_PRINTK(2, warn,
864 "NA: invalid link-layer address length\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700865 return;
866 }
867 }
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900868 ifp = ipv6_get_ifaddr(dev_net(dev), &msg->target, dev, 1);
Daniel Lezcanoa18bc692008-03-07 11:14:49 -0800869 if (ifp) {
Daniel Walterbd015922011-04-13 21:09:25 +0000870 if (skb->pkt_type != PACKET_LOOPBACK
871 && (ifp->flags & IFA_F_TENTATIVE)) {
872 addrconf_dad_failure(ifp);
873 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700874 }
875 /* What should we make now? The advertisement
876 is invalid, but ndisc specs say nothing
877 about it. It could be misconfiguration, or
878 an smart proxy agent tries to help us :-)
Jan Sembera24fc7b82008-12-09 15:48:32 -0800879
880 We should not print the error if NA has been
881 received from loopback - it is just our own
882 unsolicited advertisement.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700883 */
Jan Sembera24fc7b82008-12-09 15:48:32 -0800884 if (skb->pkt_type != PACKET_LOOPBACK)
Joe Perches675418d2012-05-16 19:28:38 +0000885 ND_PRINTK(1, warn,
886 "NA: someone advertises our address %pI6 on %s!\n",
887 &ifp->addr, ifp->idev->dev->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700888 in6_ifa_put(ifp);
889 return;
890 }
891 neigh = neigh_lookup(&nd_tbl, &msg->target, dev);
892
893 if (neigh) {
894 u8 old_flags = neigh->flags;
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700895 struct net *net = dev_net(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700896
897 if (neigh->nud_state & NUD_FAILED)
898 goto out;
899
Ville Nuorvala5f3e6e92006-09-22 14:42:46 -0700900 /*
901 * Don't update the neighbor cache entry on a proxy NA from
902 * ourselves because either the proxied node is off link or it
903 * has already sent a NA to us.
904 */
905 if (lladdr && !memcmp(lladdr, dev->dev_addr, dev->addr_len) &&
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700906 net->ipv6.devconf_all->forwarding && net->ipv6.devconf_all->proxy_ndp &&
907 pneigh_lookup(&nd_tbl, net, &msg->target, dev, 0)) {
YOSHIFUJI Hideakifbea49e2006-09-22 14:43:49 -0700908 /* XXX: idev->cnf.prixy_ndp */
Ville Nuorvala5f3e6e92006-09-22 14:42:46 -0700909 goto out;
YOSHIFUJI Hideakifbea49e2006-09-22 14:43:49 -0700910 }
Ville Nuorvala5f3e6e92006-09-22 14:42:46 -0700911
Linus Torvalds1da177e2005-04-16 15:20:36 -0700912 neigh_update(neigh, lladdr,
913 msg->icmph.icmp6_solicited ? NUD_REACHABLE : NUD_STALE,
914 NEIGH_UPDATE_F_WEAK_OVERRIDE|
915 (msg->icmph.icmp6_override ? NEIGH_UPDATE_F_OVERRIDE : 0)|
916 NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
917 (msg->icmph.icmp6_router ? NEIGH_UPDATE_F_ISROUTER : 0));
918
919 if ((old_flags & ~neigh->flags) & NTF_ROUTER) {
920 /*
921 * Change: router to host
922 */
923 struct rt6_info *rt;
924 rt = rt6_get_dflt_router(saddr, dev);
925 if (rt)
Thomas Grafe0a1ad732006-08-22 00:00:21 -0700926 ip6_del_rt(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700927 }
928
929out:
930 neigh_release(neigh);
931 }
932}
933
934static void ndisc_recv_rs(struct sk_buff *skb)
935{
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -0700936 struct rs_msg *rs_msg = (struct rs_msg *)skb_transport_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700937 unsigned long ndoptlen = skb->len - sizeof(*rs_msg);
938 struct neighbour *neigh;
939 struct inet6_dev *idev;
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000940 const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700941 struct ndisc_options ndopts;
942 u8 *lladdr = NULL;
943
944 if (skb->len < sizeof(*rs_msg))
945 return;
946
Eric Dumazetcfdf7642011-07-27 21:13:03 +0000947 idev = __in6_dev_get(skb->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700948 if (!idev) {
Joe Perches675418d2012-05-16 19:28:38 +0000949 ND_PRINTK(1, err, "RS: can't find in6 device\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700950 return;
951 }
952
953 /* Don't accept RS if we're not in router mode */
954 if (!idev->cnf.forwarding)
955 goto out;
956
957 /*
958 * Don't update NCE if src = ::;
959 * this implies that the source node has no ip address assigned yet.
960 */
961 if (ipv6_addr_any(saddr))
962 goto out;
963
964 /* Parse ND options */
965 if (!ndisc_parse_options(rs_msg->opt, ndoptlen, &ndopts)) {
Joe Perches675418d2012-05-16 19:28:38 +0000966 ND_PRINTK(2, notice, "NS: invalid ND option, ignored\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700967 goto out;
968 }
969
970 if (ndopts.nd_opts_src_lladdr) {
971 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr,
972 skb->dev);
973 if (!lladdr)
974 goto out;
975 }
976
977 neigh = __neigh_lookup(&nd_tbl, saddr, skb->dev, 1);
978 if (neigh) {
979 neigh_update(neigh, lladdr, NUD_STALE,
980 NEIGH_UPDATE_F_WEAK_OVERRIDE|
981 NEIGH_UPDATE_F_OVERRIDE|
982 NEIGH_UPDATE_F_OVERRIDE_ISROUTER);
983 neigh_release(neigh);
984 }
985out:
Eric Dumazetcfdf7642011-07-27 21:13:03 +0000986 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700987}
988
Pierre Ynard31910572007-10-10 21:22:05 -0700989static void ndisc_ra_useropt(struct sk_buff *ra, struct nd_opt_hdr *opt)
990{
991 struct icmp6hdr *icmp6h = (struct icmp6hdr *)skb_transport_header(ra);
992 struct sk_buff *skb;
993 struct nlmsghdr *nlh;
994 struct nduseroptmsg *ndmsg;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900995 struct net *net = dev_net(ra->dev);
Pierre Ynard31910572007-10-10 21:22:05 -0700996 int err;
997 int base_size = NLMSG_ALIGN(sizeof(struct nduseroptmsg)
998 + (opt->nd_opt_len << 3));
999 size_t msg_size = base_size + nla_total_size(sizeof(struct in6_addr));
1000
1001 skb = nlmsg_new(msg_size, GFP_ATOMIC);
1002 if (skb == NULL) {
1003 err = -ENOBUFS;
1004 goto errout;
1005 }
1006
1007 nlh = nlmsg_put(skb, 0, 0, RTM_NEWNDUSEROPT, base_size, 0);
1008 if (nlh == NULL) {
1009 goto nla_put_failure;
1010 }
1011
1012 ndmsg = nlmsg_data(nlh);
1013 ndmsg->nduseropt_family = AF_INET6;
Pierre Ynarddbb2ed22007-11-12 17:58:35 -08001014 ndmsg->nduseropt_ifindex = ra->dev->ifindex;
Pierre Ynard31910572007-10-10 21:22:05 -07001015 ndmsg->nduseropt_icmp_type = icmp6h->icmp6_type;
1016 ndmsg->nduseropt_icmp_code = icmp6h->icmp6_code;
1017 ndmsg->nduseropt_opts_len = opt->nd_opt_len << 3;
1018
1019 memcpy(ndmsg + 1, opt, opt->nd_opt_len << 3);
1020
David S. Millerc78679e2012-04-01 20:27:33 -04001021 if (nla_put(skb, NDUSEROPT_SRCADDR, sizeof(struct in6_addr),
1022 &ipv6_hdr(ra)->saddr))
1023 goto nla_put_failure;
Pierre Ynard31910572007-10-10 21:22:05 -07001024 nlmsg_end(skb, nlh);
1025
Pablo Neira Ayuso1ce85fe2009-02-24 23:18:28 -08001026 rtnl_notify(skb, net, 0, RTNLGRP_ND_USEROPT, NULL, GFP_ATOMIC);
Pierre Ynard31910572007-10-10 21:22:05 -07001027 return;
1028
1029nla_put_failure:
1030 nlmsg_free(skb);
1031 err = -EMSGSIZE;
1032errout:
Daniel Lezcanoa18bc692008-03-07 11:14:49 -08001033 rtnl_set_sk_err(net, RTNLGRP_ND_USEROPT, err);
Pierre Ynard31910572007-10-10 21:22:05 -07001034}
1035
Thomas Graf65e9b622010-09-03 02:59:14 +00001036static inline int accept_ra(struct inet6_dev *in6_dev)
1037{
1038 /*
1039 * If forwarding is enabled, RA are not accepted unless the special
1040 * hybrid mode (accept_ra=2) is enabled.
1041 */
1042 if (in6_dev->cnf.forwarding && in6_dev->cnf.accept_ra < 2)
1043 return 0;
1044
1045 return in6_dev->cnf.accept_ra;
1046}
1047
Linus Torvalds1da177e2005-04-16 15:20:36 -07001048static void ndisc_router_discovery(struct sk_buff *skb)
1049{
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -07001050 struct ra_msg *ra_msg = (struct ra_msg *)skb_transport_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001051 struct neighbour *neigh = NULL;
1052 struct inet6_dev *in6_dev;
YOSHIFUJI Hideaki65f5c7c2006-03-20 16:55:08 -08001053 struct rt6_info *rt = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001054 int lifetime;
1055 struct ndisc_options ndopts;
1056 int optlen;
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001057 unsigned int pref = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001058
1059 __u8 * opt = (__u8 *)(ra_msg + 1);
1060
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -07001061 optlen = (skb->tail - skb->transport_header) - sizeof(struct ra_msg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001062
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001063 if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) {
Joe Perches675418d2012-05-16 19:28:38 +00001064 ND_PRINTK(2, warn, "RA: source address is not link-local\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001065 return;
1066 }
1067 if (optlen < 0) {
Joe Perches675418d2012-05-16 19:28:38 +00001068 ND_PRINTK(2, warn, "RA: packet too short\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001069 return;
1070 }
1071
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001072#ifdef CONFIG_IPV6_NDISC_NODETYPE
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001073 if (skb->ndisc_nodetype == NDISC_NODETYPE_HOST) {
Joe Perches675418d2012-05-16 19:28:38 +00001074 ND_PRINTK(2, warn, "RA: from host or unauthorized router\n");
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001075 return;
1076 }
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001077#endif
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001078
Linus Torvalds1da177e2005-04-16 15:20:36 -07001079 /*
1080 * set the RA_RECV flag in the interface
1081 */
1082
Eric Dumazetcfdf7642011-07-27 21:13:03 +00001083 in6_dev = __in6_dev_get(skb->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001084 if (in6_dev == NULL) {
Joe Perches675418d2012-05-16 19:28:38 +00001085 ND_PRINTK(0, err, "RA: can't find inet6 device for %s\n",
1086 skb->dev->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001087 return;
1088 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001089
1090 if (!ndisc_parse_options(opt, optlen, &ndopts)) {
Joe Perches675418d2012-05-16 19:28:38 +00001091 ND_PRINTK(2, warn, "RA: invalid ND options\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001092 return;
1093 }
1094
Thomas Graf65e9b622010-09-03 02:59:14 +00001095 if (!accept_ra(in6_dev))
David Ward31ce8c72009-08-29 00:04:09 -07001096 goto skip_linkparms;
1097
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001098#ifdef CONFIG_IPV6_NDISC_NODETYPE
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001099 /* skip link-specific parameters from interior routers */
1100 if (skb->ndisc_nodetype == NDISC_NODETYPE_NODEFAULT)
1101 goto skip_linkparms;
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001102#endif
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001103
Linus Torvalds1da177e2005-04-16 15:20:36 -07001104 if (in6_dev->if_flags & IF_RS_SENT) {
1105 /*
1106 * flag that an RA was received after an RS was sent
1107 * out on this interface.
1108 */
1109 in6_dev->if_flags |= IF_RA_RCVD;
1110 }
1111
1112 /*
1113 * Remember the managed/otherconf flags from most recently
1114 * received RA message (RFC 2462) -- yoshfuji
1115 */
1116 in6_dev->if_flags = (in6_dev->if_flags & ~(IF_RA_MANAGED |
1117 IF_RA_OTHERCONF)) |
1118 (ra_msg->icmph.icmp6_addrconf_managed ?
1119 IF_RA_MANAGED : 0) |
1120 (ra_msg->icmph.icmp6_addrconf_other ?
1121 IF_RA_OTHERCONF : 0);
1122
YOSHIFUJI Hideaki65f5c7c2006-03-20 16:55:08 -08001123 if (!in6_dev->cnf.accept_ra_defrtr)
1124 goto skip_defrtr;
1125
Andreas Hofmeister9f562202011-10-24 19:13:15 -04001126 if (ipv6_chk_addr(dev_net(in6_dev->dev), &ipv6_hdr(skb)->saddr, NULL, 0))
1127 goto skip_defrtr;
1128
Linus Torvalds1da177e2005-04-16 15:20:36 -07001129 lifetime = ntohs(ra_msg->icmph.icmp6_rt_lifetime);
1130
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001131#ifdef CONFIG_IPV6_ROUTER_PREF
1132 pref = ra_msg->icmph.icmp6_router_pref;
1133 /* 10b is handled as if it were 00b (medium) */
YOSHIFUJI Hideaki930d6ff2006-03-20 17:05:30 -08001134 if (pref == ICMPV6_ROUTER_PREF_INVALID ||
YOSHIFUJI Hideaki6d5b78c2007-06-22 16:07:04 -07001135 !in6_dev->cnf.accept_ra_rtr_pref)
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001136 pref = ICMPV6_ROUTER_PREF_MEDIUM;
1137#endif
1138
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001139 rt = rt6_get_dflt_router(&ipv6_hdr(skb)->saddr, skb->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001140
David S. Millereb857182012-01-27 15:07:56 -08001141 if (rt) {
1142 neigh = dst_neigh_lookup(&rt->dst, &ipv6_hdr(skb)->saddr);
1143 if (!neigh) {
Joe Perches675418d2012-05-16 19:28:38 +00001144 ND_PRINTK(0, err,
1145 "RA: %s got default router without neighbour\n",
1146 __func__);
David S. Millereb857182012-01-27 15:07:56 -08001147 dst_release(&rt->dst);
1148 return;
1149 }
1150 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001151 if (rt && lifetime == 0) {
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001152 ip6_del_rt(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001153 rt = NULL;
1154 }
1155
1156 if (rt == NULL && lifetime) {
Joe Perches675418d2012-05-16 19:28:38 +00001157 ND_PRINTK(3, dbg, "RA: adding default router\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001158
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001159 rt = rt6_add_dflt_router(&ipv6_hdr(skb)->saddr, skb->dev, pref);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001160 if (rt == NULL) {
Joe Perches675418d2012-05-16 19:28:38 +00001161 ND_PRINTK(0, err,
1162 "RA: %s failed to add default route\n",
1163 __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001164 return;
1165 }
1166
David S. Millereb857182012-01-27 15:07:56 -08001167 neigh = dst_neigh_lookup(&rt->dst, &ipv6_hdr(skb)->saddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001168 if (neigh == NULL) {
Joe Perches675418d2012-05-16 19:28:38 +00001169 ND_PRINTK(0, err,
1170 "RA: %s got default router without neighbour\n",
1171 __func__);
Changli Gaod8d1f302010-06-10 23:31:35 -07001172 dst_release(&rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001173 return;
1174 }
1175 neigh->flags |= NTF_ROUTER;
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001176 } else if (rt) {
Pedro Ribeiro22441cf2008-10-15 15:47:49 -07001177 rt->rt6i_flags = (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001178 }
1179
1180 if (rt)
Gao feng1716a962012-04-06 00:13:10 +00001181 rt6_set_expires(rt, jiffies + (HZ * lifetime));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001182 if (ra_msg->icmph.icmp6_hop_limit) {
1183 in6_dev->cnf.hop_limit = ra_msg->icmph.icmp6_hop_limit;
1184 if (rt)
David S. Millerdefb3512010-12-08 21:16:57 -08001185 dst_metric_set(&rt->dst, RTAX_HOPLIMIT,
1186 ra_msg->icmph.icmp6_hop_limit);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001187 }
1188
YOSHIFUJI Hideaki65f5c7c2006-03-20 16:55:08 -08001189skip_defrtr:
1190
Linus Torvalds1da177e2005-04-16 15:20:36 -07001191 /*
1192 * Update Reachable Time and Retrans Timer
1193 */
1194
1195 if (in6_dev->nd_parms) {
1196 unsigned long rtime = ntohl(ra_msg->retrans_timer);
1197
1198 if (rtime && rtime/1000 < MAX_SCHEDULE_TIMEOUT/HZ) {
1199 rtime = (rtime*HZ)/1000;
1200 if (rtime < HZ/10)
1201 rtime = HZ/10;
1202 in6_dev->nd_parms->retrans_time = rtime;
1203 in6_dev->tstamp = jiffies;
1204 inet6_ifinfo_notify(RTM_NEWLINK, in6_dev);
1205 }
1206
1207 rtime = ntohl(ra_msg->reachable_time);
1208 if (rtime && rtime/1000 < MAX_SCHEDULE_TIMEOUT/(3*HZ)) {
1209 rtime = (rtime*HZ)/1000;
1210
1211 if (rtime < HZ/10)
1212 rtime = HZ/10;
1213
1214 if (rtime != in6_dev->nd_parms->base_reachable_time) {
1215 in6_dev->nd_parms->base_reachable_time = rtime;
1216 in6_dev->nd_parms->gc_staletime = 3 * rtime;
1217 in6_dev->nd_parms->reachable_time = neigh_rand_reach_time(rtime);
1218 in6_dev->tstamp = jiffies;
1219 inet6_ifinfo_notify(RTM_NEWLINK, in6_dev);
1220 }
1221 }
1222 }
1223
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001224skip_linkparms:
1225
Linus Torvalds1da177e2005-04-16 15:20:36 -07001226 /*
1227 * Process options.
1228 */
1229
1230 if (!neigh)
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001231 neigh = __neigh_lookup(&nd_tbl, &ipv6_hdr(skb)->saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001232 skb->dev, 1);
1233 if (neigh) {
1234 u8 *lladdr = NULL;
1235 if (ndopts.nd_opts_src_lladdr) {
1236 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr,
1237 skb->dev);
1238 if (!lladdr) {
Joe Perches675418d2012-05-16 19:28:38 +00001239 ND_PRINTK(2, warn,
1240 "RA: invalid link-layer address length\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001241 goto out;
1242 }
1243 }
1244 neigh_update(neigh, lladdr, NUD_STALE,
1245 NEIGH_UPDATE_F_WEAK_OVERRIDE|
1246 NEIGH_UPDATE_F_OVERRIDE|
1247 NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
1248 NEIGH_UPDATE_F_ISROUTER);
1249 }
1250
Thomas Graf65e9b622010-09-03 02:59:14 +00001251 if (!accept_ra(in6_dev))
David Ward31ce8c72009-08-29 00:04:09 -07001252 goto out;
1253
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001254#ifdef CONFIG_IPV6_ROUTE_INFO
Andreas Hofmeister9f562202011-10-24 19:13:15 -04001255 if (ipv6_chk_addr(dev_net(in6_dev->dev), &ipv6_hdr(skb)->saddr, NULL, 0))
1256 goto skip_routeinfo;
1257
YOSHIFUJI Hideaki09c884d2006-03-20 17:07:03 -08001258 if (in6_dev->cnf.accept_ra_rtr_pref && ndopts.nd_opts_ri) {
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001259 struct nd_opt_hdr *p;
1260 for (p = ndopts.nd_opts_ri;
1261 p;
1262 p = ndisc_next_option(p, ndopts.nd_opts_ri_end)) {
YOSHIFUJI Hideaki6294e002008-03-15 23:56:52 -04001263 struct route_info *ri = (struct route_info *)p;
1264#ifdef CONFIG_IPV6_NDISC_NODETYPE
1265 if (skb->ndisc_nodetype == NDISC_NODETYPE_NODEFAULT &&
1266 ri->prefix_len == 0)
1267 continue;
1268#endif
1269 if (ri->prefix_len > in6_dev->cnf.accept_ra_rt_info_max_plen)
YOSHIFUJI Hideaki09c884d2006-03-20 17:07:03 -08001270 continue;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001271 rt6_route_rcv(skb->dev, (u8*)p, (p->nd_opt_len) << 3,
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001272 &ipv6_hdr(skb)->saddr);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001273 }
1274 }
Andreas Hofmeister9f562202011-10-24 19:13:15 -04001275
1276skip_routeinfo:
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001277#endif
1278
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001279#ifdef CONFIG_IPV6_NDISC_NODETYPE
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001280 /* skip link-specific ndopts from interior routers */
1281 if (skb->ndisc_nodetype == NDISC_NODETYPE_NODEFAULT)
1282 goto out;
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001283#endif
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001284
YOSHIFUJI Hideakic4fd30e2006-03-20 16:55:26 -08001285 if (in6_dev->cnf.accept_ra_pinfo && ndopts.nd_opts_pi) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001286 struct nd_opt_hdr *p;
1287 for (p = ndopts.nd_opts_pi;
1288 p;
1289 p = ndisc_next_option(p, ndopts.nd_opts_pi_end)) {
Neil Hormane6bff992012-01-04 10:49:15 +00001290 addrconf_prefix_rcv(skb->dev, (u8 *)p,
1291 (p->nd_opt_len) << 3,
1292 ndopts.nd_opts_src_lladdr != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001293 }
1294 }
1295
1296 if (ndopts.nd_opts_mtu) {
Al Viroe69a4adc2006-11-14 20:56:00 -08001297 __be32 n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001298 u32 mtu;
1299
Al Viroe69a4adc2006-11-14 20:56:00 -08001300 memcpy(&n, ((u8*)(ndopts.nd_opts_mtu+1))+2, sizeof(mtu));
1301 mtu = ntohl(n);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001302
1303 if (mtu < IPV6_MIN_MTU || mtu > skb->dev->mtu) {
Joe Perches675418d2012-05-16 19:28:38 +00001304 ND_PRINTK(2, warn, "RA: invalid mtu: %d\n", mtu);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001305 } else if (in6_dev->cnf.mtu6 != mtu) {
1306 in6_dev->cnf.mtu6 = mtu;
1307
1308 if (rt)
David S. Millerdefb3512010-12-08 21:16:57 -08001309 dst_metric_set(&rt->dst, RTAX_MTU, mtu);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001310
1311 rt6_mtu_change(skb->dev, mtu);
1312 }
1313 }
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001314
Pierre Ynard31910572007-10-10 21:22:05 -07001315 if (ndopts.nd_useropts) {
YOSHIFUJI Hideaki61cf46ad2008-01-22 17:32:53 +09001316 struct nd_opt_hdr *p;
1317 for (p = ndopts.nd_useropts;
1318 p;
1319 p = ndisc_next_useropt(p, ndopts.nd_useropts_end)) {
1320 ndisc_ra_useropt(skb, p);
Pierre Ynard31910572007-10-10 21:22:05 -07001321 }
1322 }
1323
Linus Torvalds1da177e2005-04-16 15:20:36 -07001324 if (ndopts.nd_opts_tgt_lladdr || ndopts.nd_opts_rh) {
Joe Perches675418d2012-05-16 19:28:38 +00001325 ND_PRINTK(2, warn, "RA: invalid RA options\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001326 }
1327out:
1328 if (rt)
Changli Gaod8d1f302010-06-10 23:31:35 -07001329 dst_release(&rt->dst);
David S. Millereb857182012-01-27 15:07:56 -08001330 if (neigh)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001331 neigh_release(neigh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001332}
1333
1334static void ndisc_redirect_rcv(struct sk_buff *skb)
1335{
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001336#ifdef CONFIG_IPV6_NDISC_NODETYPE
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001337 switch (skb->ndisc_nodetype) {
1338 case NDISC_NODETYPE_HOST:
1339 case NDISC_NODETYPE_NODEFAULT:
Joe Perches675418d2012-05-16 19:28:38 +00001340 ND_PRINTK(2, warn,
1341 "Redirect: from host or unauthorized router\n");
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001342 return;
1343 }
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001344#endif
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001345
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001346 if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) {
Joe Perches675418d2012-05-16 19:28:38 +00001347 ND_PRINTK(2, warn,
1348 "Redirect: source address is not link-local\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001349 return;
1350 }
1351
David S. Millerb94f1c02012-07-12 00:33:37 -07001352 icmpv6_notify(skb, NDISC_REDIRECT, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001353}
1354
David S. Miller49919692012-01-27 15:30:48 -08001355void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001356{
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001357 struct net_device *dev = skb->dev;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001358 struct net *net = dev_net(dev);
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001359 struct sock *sk = net->ipv6.ndisc_sk;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001360 int len = sizeof(struct icmp6hdr) + 2 * sizeof(struct in6_addr);
David S. Millerfbfe95a2012-06-08 23:24:18 -07001361 struct inet_peer *peer;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001362 struct sk_buff *buff;
1363 struct icmp6hdr *icmph;
1364 struct in6_addr saddr_buf;
1365 struct in6_addr *addrp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001366 struct rt6_info *rt;
1367 struct dst_entry *dst;
1368 struct inet6_dev *idev;
David S. Miller4c9483b2011-03-12 16:22:43 -05001369 struct flowi6 fl6;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001370 u8 *opt;
Herbert Xua7ae1992011-11-18 02:20:04 +00001371 int hlen, tlen;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001372 int rd_len;
1373 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001374 u8 ha_buf[MAX_ADDR_LEN], *ha = NULL;
David S. Miller1d861aa2012-07-10 03:58:16 -07001375 bool ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001376
Neil Horman95c385b2007-04-25 17:08:10 -07001377 if (ipv6_get_lladdr(dev, &saddr_buf, IFA_F_TENTATIVE)) {
Joe Perches675418d2012-05-16 19:28:38 +00001378 ND_PRINTK(2, warn, "Redirect: no link-local address on %s\n",
1379 dev->name);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001380 return;
1381 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001382
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001383 if (!ipv6_addr_equal(&ipv6_hdr(skb)->daddr, target) &&
Brian Haleybf0b48d2007-10-08 00:12:05 -07001384 ipv6_addr_type(target) != (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) {
Joe Perches675418d2012-05-16 19:28:38 +00001385 ND_PRINTK(2, warn,
1386 "Redirect: target address is not link-local unicast\n");
Li Yewang29556522007-01-30 14:33:20 -08001387 return;
1388 }
1389
David S. Miller4c9483b2011-03-12 16:22:43 -05001390 icmpv6_flow_init(sk, &fl6, NDISC_REDIRECT,
YOSHIFUJI Hideaki95e41e92007-12-06 15:43:30 -08001391 &saddr_buf, &ipv6_hdr(skb)->saddr, dev->ifindex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001392
David S. Miller4c9483b2011-03-12 16:22:43 -05001393 dst = ip6_route_output(net, NULL, &fl6);
RongQing.Li5095d642012-02-21 22:10:49 +00001394 if (dst->error) {
1395 dst_release(dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001396 return;
RongQing.Li5095d642012-02-21 22:10:49 +00001397 }
David S. Miller4c9483b2011-03-12 16:22:43 -05001398 dst = xfrm_lookup(net, dst, flowi6_to_flowi(&fl6), NULL, 0);
David S. Miller452edd52011-03-02 13:27:41 -08001399 if (IS_ERR(dst))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001400 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001401
1402 rt = (struct rt6_info *) dst;
1403
1404 if (rt->rt6i_flags & RTF_GATEWAY) {
Joe Perches675418d2012-05-16 19:28:38 +00001405 ND_PRINTK(2, warn,
1406 "Redirect: destination is not a neighbour\n");
Ilpo Järvinend73f0802009-02-06 23:47:37 -08001407 goto release;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001408 }
David S. Miller1d861aa2012-07-10 03:58:16 -07001409 peer = inet_getpeer_v6(net->ipv6.peers, &rt->rt6i_dst.addr, 1);
1410 ret = inet_peer_xrlim_allow(peer, 1*HZ);
1411 if (peer)
1412 inet_putpeer(peer);
1413 if (!ret)
Ilpo Järvinend73f0802009-02-06 23:47:37 -08001414 goto release;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001415
1416 if (dev->addr_len) {
David S. Miller49919692012-01-27 15:30:48 -08001417 struct neighbour *neigh = dst_neigh_lookup(skb_dst(skb), target);
1418 if (!neigh) {
Joe Perches675418d2012-05-16 19:28:38 +00001419 ND_PRINTK(2, warn,
1420 "Redirect: no neigh for target address\n");
David S. Miller49919692012-01-27 15:30:48 -08001421 goto release;
1422 }
1423
Linus Torvalds1da177e2005-04-16 15:20:36 -07001424 read_lock_bh(&neigh->lock);
1425 if (neigh->nud_state & NUD_VALID) {
1426 memcpy(ha_buf, neigh->ha, dev->addr_len);
1427 read_unlock_bh(&neigh->lock);
1428 ha = ha_buf;
1429 len += ndisc_opt_addr_space(dev);
1430 } else
1431 read_unlock_bh(&neigh->lock);
David S. Miller49919692012-01-27 15:30:48 -08001432
1433 neigh_release(neigh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001434 }
1435
1436 rd_len = min_t(unsigned int,
1437 IPV6_MIN_MTU-sizeof(struct ipv6hdr)-len, skb->len + 8);
1438 rd_len &= ~0x7;
1439 len += rd_len;
1440
Herbert Xua7ae1992011-11-18 02:20:04 +00001441 hlen = LL_RESERVED_SPACE(dev);
1442 tlen = dev->needed_tailroom;
David S. Millerd54a81d2006-12-02 21:00:06 -08001443 buff = sock_alloc_send_skb(sk,
1444 (MAX_HEADER + sizeof(struct ipv6hdr) +
Herbert Xua7ae1992011-11-18 02:20:04 +00001445 len + hlen + tlen),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001446 1, &err);
1447 if (buff == NULL) {
Joe Perches675418d2012-05-16 19:28:38 +00001448 ND_PRINTK(0, err,
1449 "Redirect: %s failed to allocate an skb, err=%d\n",
1450 __func__, err);
Ilpo Järvinend73f0802009-02-06 23:47:37 -08001451 goto release;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001452 }
1453
Herbert Xua7ae1992011-11-18 02:20:04 +00001454 skb_reserve(buff, hlen);
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001455 ip6_nd_hdr(sk, buff, dev, &saddr_buf, &ipv6_hdr(skb)->saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001456 IPPROTO_ICMPV6, len);
1457
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -07001458 skb_set_transport_header(buff, skb_tail_pointer(buff) - buff->data);
Arnaldo Carvalho de Melod10ba342007-03-14 21:05:37 -03001459 skb_put(buff, len);
1460 icmph = icmp6_hdr(buff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001461
1462 memset(icmph, 0, sizeof(struct icmp6hdr));
1463 icmph->icmp6_type = NDISC_REDIRECT;
1464
1465 /*
1466 * copy target and destination addresses
1467 */
1468
1469 addrp = (struct in6_addr *)(icmph + 1);
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001470 *addrp = *target;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001471 addrp++;
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001472 *addrp = ipv6_hdr(skb)->daddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001473
1474 opt = (u8*) (addrp + 1);
1475
1476 /*
1477 * include target_address option
1478 */
1479
1480 if (ha)
1481 opt = ndisc_fill_addr_option(opt, ND_OPT_TARGET_LL_ADDR, ha,
1482 dev->addr_len, dev->type);
1483
1484 /*
1485 * build redirect option and copy skb over to the new packet.
1486 */
1487
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001488 memset(opt, 0, 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001489 *(opt++) = ND_OPT_REDIRECT_HDR;
1490 *(opt++) = (rd_len >> 3);
1491 opt += 6;
1492
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001493 memcpy(opt, ipv6_hdr(skb), rd_len - 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001494
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001495 icmph->icmp6_cksum = csum_ipv6_magic(&saddr_buf, &ipv6_hdr(skb)->saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001496 len, IPPROTO_ICMPV6,
Joe Perches07f07572008-11-19 15:44:53 -08001497 csum_partial(icmph, len, 0));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001498
Eric Dumazetadf30902009-06-02 05:19:30 +00001499 skb_dst_set(buff, dst);
Eric Dumazetcfdf7642011-07-27 21:13:03 +00001500 rcu_read_lock();
1501 idev = __in6_dev_get(dst->dev);
Neil Hormanedf391f2009-04-27 02:45:02 -07001502 IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUT, skb->len);
Jan Engelhardtb2e0b382010-03-23 04:09:07 +01001503 err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, buff, NULL, dst->dev,
Patrick McHardy6e23ae22007-11-19 18:53:30 -08001504 dst_output);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001505 if (!err) {
Denis V. Lunev5c5d2442008-10-08 10:33:50 -07001506 ICMP6MSGOUT_INC_STATS(net, idev, NDISC_REDIRECT);
Denis V. Luneva862f6a2008-10-08 10:33:06 -07001507 ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001508 }
1509
Eric Dumazetcfdf7642011-07-27 21:13:03 +00001510 rcu_read_unlock();
Ilpo Järvinend73f0802009-02-06 23:47:37 -08001511 return;
1512
1513release:
1514 dst_release(dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001515}
1516
1517static void pndisc_redo(struct sk_buff *skb)
1518{
YOSHIFUJI Hideaki140e26fc2005-10-05 12:11:41 -07001519 ndisc_recv_ns(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001520 kfree_skb(skb);
1521}
1522
1523int ndisc_rcv(struct sk_buff *skb)
1524{
1525 struct nd_msg *msg;
1526
1527 if (!pskb_may_pull(skb, skb->len))
1528 return 0;
1529
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -07001530 msg = (struct nd_msg *)skb_transport_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001531
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -07001532 __skb_push(skb, skb->data - skb_transport_header(skb));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001533
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001534 if (ipv6_hdr(skb)->hop_limit != 255) {
Joe Perches675418d2012-05-16 19:28:38 +00001535 ND_PRINTK(2, warn, "NDISC: invalid hop-limit: %d\n",
1536 ipv6_hdr(skb)->hop_limit);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001537 return 0;
1538 }
1539
1540 if (msg->icmph.icmp6_code != 0) {
Joe Perches675418d2012-05-16 19:28:38 +00001541 ND_PRINTK(2, warn, "NDISC: invalid ICMPv6 code: %d\n",
1542 msg->icmph.icmp6_code);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001543 return 0;
1544 }
1545
Patrick McHardya61bbcf2005-08-14 17:24:31 -07001546 memset(NEIGH_CB(skb), 0, sizeof(struct neighbour_cb));
1547
Linus Torvalds1da177e2005-04-16 15:20:36 -07001548 switch (msg->icmph.icmp6_type) {
1549 case NDISC_NEIGHBOUR_SOLICITATION:
1550 ndisc_recv_ns(skb);
1551 break;
1552
1553 case NDISC_NEIGHBOUR_ADVERTISEMENT:
1554 ndisc_recv_na(skb);
1555 break;
1556
1557 case NDISC_ROUTER_SOLICITATION:
1558 ndisc_recv_rs(skb);
1559 break;
1560
1561 case NDISC_ROUTER_ADVERTISEMENT:
1562 ndisc_router_discovery(skb);
1563 break;
1564
1565 case NDISC_REDIRECT:
1566 ndisc_redirect_rcv(skb);
1567 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -07001568 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001569
1570 return 0;
1571}
1572
1573static int ndisc_netdev_event(struct notifier_block *this, unsigned long event, void *ptr)
1574{
1575 struct net_device *dev = ptr;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001576 struct net *net = dev_net(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001577
1578 switch (event) {
1579 case NETDEV_CHANGEADDR:
1580 neigh_changeaddr(&nd_tbl, dev);
Daniel Lezcano5b7c9312008-03-03 23:28:58 -08001581 fib6_run_gc(~0UL, net);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001582 break;
1583 case NETDEV_DOWN:
1584 neigh_ifdown(&nd_tbl, dev);
Daniel Lezcano5b7c9312008-03-03 23:28:58 -08001585 fib6_run_gc(~0UL, net);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001586 break;
Ben Hutchingsf47b9462011-04-15 13:46:02 +00001587 case NETDEV_NOTIFY_PEERS:
1588 ndisc_send_unsol_na(dev);
1589 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001590 default:
1591 break;
1592 }
1593
1594 return NOTIFY_DONE;
1595}
1596
1597static struct notifier_block ndisc_netdev_notifier = {
1598 .notifier_call = ndisc_netdev_event,
1599};
1600
1601#ifdef CONFIG_SYSCTL
1602static void ndisc_warn_deprecated_sysctl(struct ctl_table *ctl,
1603 const char *func, const char *dev_name)
1604{
1605 static char warncomm[TASK_COMM_LEN];
1606 static int warned;
1607 if (strcmp(warncomm, current->comm) && warned < 5) {
1608 strcpy(warncomm, current->comm);
Joe Perchesf3213832012-05-15 14:11:53 +00001609 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 -07001610 warncomm, func,
1611 dev_name, ctl->procname,
1612 dev_name, ctl->procname);
1613 warned++;
1614 }
1615}
1616
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001617int 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 -07001618{
1619 struct net_device *dev = ctl->extra1;
1620 struct inet6_dev *idev;
1621 int ret;
1622
Eric W. Biedermand12af672007-10-18 03:05:25 -07001623 if ((strcmp(ctl->procname, "retrans_time") == 0) ||
1624 (strcmp(ctl->procname, "base_reachable_time") == 0))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001625 ndisc_warn_deprecated_sysctl(ctl, "syscall", dev ? dev->name : "default");
1626
Eric W. Biedermand12af672007-10-18 03:05:25 -07001627 if (strcmp(ctl->procname, "retrans_time") == 0)
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001628 ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
Eric W. Biedermand12af672007-10-18 03:05:25 -07001629
1630 else if (strcmp(ctl->procname, "base_reachable_time") == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001631 ret = proc_dointvec_jiffies(ctl, write,
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001632 buffer, lenp, ppos);
Eric W. Biedermand12af672007-10-18 03:05:25 -07001633
1634 else if ((strcmp(ctl->procname, "retrans_time_ms") == 0) ||
YOSHIFUJI Hideakiad02ac12007-10-29 01:32:23 -07001635 (strcmp(ctl->procname, "base_reachable_time_ms") == 0))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001636 ret = proc_dointvec_ms_jiffies(ctl, write,
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001637 buffer, lenp, ppos);
Eric W. Biedermand12af672007-10-18 03:05:25 -07001638 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001639 ret = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001640
1641 if (write && ret == 0 && dev && (idev = in6_dev_get(dev)) != NULL) {
Eric W. Biedermand12af672007-10-18 03:05:25 -07001642 if (ctl->data == &idev->nd_parms->base_reachable_time)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001643 idev->nd_parms->reachable_time = neigh_rand_reach_time(idev->nd_parms->base_reachable_time);
1644 idev->tstamp = jiffies;
1645 inet6_ifinfo_notify(RTM_NEWLINK, idev);
1646 in6_dev_put(idev);
1647 }
1648 return ret;
1649}
1650
Linus Torvalds1da177e2005-04-16 15:20:36 -07001651
1652#endif
1653
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00001654static int __net_init ndisc_net_init(struct net *net)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001655{
1656 struct ipv6_pinfo *np;
1657 struct sock *sk;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001658 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001659
Denis V. Lunev1ed85162008-04-03 14:31:03 -07001660 err = inet_ctl_sock_create(&sk, PF_INET6,
1661 SOCK_RAW, IPPROTO_ICMPV6, net);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001662 if (err < 0) {
Joe Perches675418d2012-05-16 19:28:38 +00001663 ND_PRINTK(0, err,
1664 "NDISC: Failed to initialize the control socket (err %d)\n",
1665 err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001666 return err;
1667 }
1668
Denis V. Lunev1ed85162008-04-03 14:31:03 -07001669 net->ipv6.ndisc_sk = sk;
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001670
Linus Torvalds1da177e2005-04-16 15:20:36 -07001671 np = inet6_sk(sk);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001672 np->hop_limit = 255;
1673 /* Do not loopback ndisc messages */
1674 np->mc_loop = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001675
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001676 return 0;
1677}
1678
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00001679static void __net_exit ndisc_net_exit(struct net *net)
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001680{
Denis V. Lunev1ed85162008-04-03 14:31:03 -07001681 inet_ctl_sock_destroy(net->ipv6.ndisc_sk);
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001682}
1683
1684static struct pernet_operations ndisc_net_ops = {
1685 .init = ndisc_net_init,
1686 .exit = ndisc_net_exit,
1687};
1688
1689int __init ndisc_init(void)
1690{
1691 int err;
1692
1693 err = register_pernet_subsys(&ndisc_net_ops);
1694 if (err)
1695 return err;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001696 /*
1697 * Initialize the neighbour table
1698 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001699 neigh_table_init(&nd_tbl);
1700
1701#ifdef CONFIG_SYSCTL
Eric W. Biederman54716e32010-02-14 03:27:03 +00001702 err = neigh_sysctl_register(NULL, &nd_tbl.parms, "ipv6",
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001703 &ndisc_ifinfo_sysctl_change);
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001704 if (err)
1705 goto out_unregister_pernet;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001706#endif
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001707 err = register_netdevice_notifier(&ndisc_netdev_notifier);
1708 if (err)
1709 goto out_unregister_sysctl;
1710out:
1711 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001712
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001713out_unregister_sysctl:
1714#ifdef CONFIG_SYSCTL
1715 neigh_sysctl_unregister(&nd_tbl.parms);
1716out_unregister_pernet:
1717#endif
1718 unregister_pernet_subsys(&ndisc_net_ops);
1719 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001720}
1721
1722void ndisc_cleanup(void)
1723{
Dmitry Mishin36f73d02006-11-03 16:08:19 -08001724 unregister_netdevice_notifier(&ndisc_netdev_notifier);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001725#ifdef CONFIG_SYSCTL
1726 neigh_sysctl_unregister(&nd_tbl.parms);
1727#endif
1728 neigh_table_clear(&nd_tbl);
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001729 unregister_pernet_subsys(&ndisc_net_ops);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001730}