blob: 429622d747053346ea6b58878010ddd6576e135e [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{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700154 int pad = ndisc_addr_option_pad(addr_type);
YOSHIFUJI Hideaki / 吉藤英明7bdc1b42012-12-13 04:29:36 +0000155 int space = NDISC_OPT_SPACE(data_len + pad);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700156
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
YOSHIFUJI Hideakifd0ea7d2012-12-13 02:40:26 +0900373static struct 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,
YOSHIFUJI Hideaki / 吉藤英明b7dc8c32013-01-04 03:58:04 +0000398 (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
YOSHIFUJI Hideakifd0ea7d2012-12-13 02:40:26 +0900434static void ndisc_send_skb(struct sk_buff *skb, struct net_device *dev,
YOSHIFUJI Hideakifd0ea7d2012-12-13 02:40:26 +0900435 const struct in6_addr *daddr,
436 const struct in6_addr *saddr,
437 struct icmp6hdr *icmp6h)
Brian Haley305d5522008-11-04 17:51:14 -0800438{
David S. Miller4c9483b2011-03-12 16:22:43 -0500439 struct flowi6 fl6;
Brian Haley305d5522008-11-04 17:51:14 -0800440 struct dst_entry *dst;
441 struct net *net = dev_net(dev);
442 struct sock *sk = net->ipv6.ndisc_sk;
443 struct inet6_dev *idev;
444 int err;
445 u8 type;
446
447 type = icmp6h->icmp6_type;
448
David S. Miller4c9483b2011-03-12 16:22:43 -0500449 icmpv6_flow_init(sk, &fl6, type, saddr, daddr, dev->ifindex);
YOSHIFUJI Hideaki / 吉藤英明12fd84f2013-01-18 02:00:24 +0000450 dst = icmp6_dst_alloc(dev, &fl6);
David S. Miller452edd52011-03-02 13:27:41 -0800451 if (IS_ERR(dst)) {
Brian Haley305d5522008-11-04 17:51:14 -0800452 kfree_skb(skb);
453 return;
454 }
455
Eric Dumazetadf30902009-06-02 05:19:30 +0000456 skb_dst_set(skb, dst);
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900457
Eric Dumazetcfdf7642011-07-27 21:13:03 +0000458 rcu_read_lock();
459 idev = __in6_dev_get(dst->dev);
Neil Hormanedf391f2009-04-27 02:45:02 -0700460 IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUT, skb->len);
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900461
Jan Engelhardtb2e0b382010-03-23 04:09:07 +0100462 err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, skb, NULL, dst->dev,
Patrick McHardy6e23ae22007-11-19 18:53:30 -0800463 dst_output);
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900464 if (!err) {
Denis V. Lunev5c5d2442008-10-08 10:33:50 -0700465 ICMP6MSGOUT_INC_STATS(net, idev, type);
Denis V. Luneva862f6a2008-10-08 10:33:06 -0700466 ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS);
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900467 }
468
Eric Dumazetcfdf7642011-07-27 21:13:03 +0000469 rcu_read_unlock();
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900470}
471
Brian Haley305d5522008-11-04 17:51:14 -0800472/*
473 * Send a Neighbour Discover packet
474 */
475static void __ndisc_send(struct net_device *dev,
Brian Haley305d5522008-11-04 17:51:14 -0800476 const struct in6_addr *daddr,
477 const struct in6_addr *saddr,
478 struct icmp6hdr *icmp6h, const struct in6_addr *target,
479 int llinfo)
480{
481 struct sk_buff *skb;
482
483 skb = ndisc_build_skb(dev, daddr, saddr, icmp6h, target, llinfo);
484 if (!skb)
485 return;
486
YOSHIFUJI Hideaki / 吉藤英明12fd84f2013-01-18 02:00:24 +0000487 ndisc_send_skb(skb, dev, daddr, saddr, icmp6h);
Brian Haley305d5522008-11-04 17:51:14 -0800488}
489
Linus Torvalds1da177e2005-04-16 15:20:36 -0700490static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +0900491 const struct in6_addr *daddr,
492 const struct in6_addr *solicited_addr,
493 int router, int solicited, int override, int inc_opt)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700494{
495 struct in6_addr tmpaddr;
496 struct inet6_ifaddr *ifp;
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +0900497 const struct in6_addr *src_addr;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900498 struct icmp6hdr icmp6h = {
499 .icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT,
500 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501
502 /* for anycast or proxy, solicited_addr != src_addr */
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900503 ifp = ipv6_get_ifaddr(dev_net(dev), solicited_addr, dev, 1);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900504 if (ifp) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700505 src_addr = solicited_addr;
Neil Horman95c385b2007-04-25 17:08:10 -0700506 if (ifp->flags & IFA_F_OPTIMISTIC)
507 override = 0;
stephen hemminger9f888162010-06-21 11:00:13 +0000508 inc_opt |= ifp->idev->cnf.force_tllao;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700509 in6_ifa_put(ifp);
510 } else {
Brian Haley191cd582008-08-14 15:33:21 -0700511 if (ipv6_dev_get_saddr(dev_net(dev), dev, daddr,
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900512 inet6_sk(dev_net(dev)->ipv6.ndisc_sk)->srcprefs,
YOSHIFUJI Hideaki7cbca672008-03-25 09:37:42 +0900513 &tmpaddr))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514 return;
515 src_addr = &tmpaddr;
516 }
517
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900518 icmp6h.icmp6_router = router;
519 icmp6h.icmp6_solicited = solicited;
520 icmp6h.icmp6_override = override;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700521
YOSHIFUJI Hideaki / 吉藤英明12fd84f2013-01-18 02:00:24 +0000522 __ndisc_send(dev, daddr, src_addr, &icmp6h, solicited_addr,
David L Stevens14878f72007-09-16 16:52:35 -0700523 inc_opt ? ND_OPT_TARGET_LL_ADDR : 0);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900524}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700525
Ben Hutchingsf47b9462011-04-15 13:46:02 +0000526static void ndisc_send_unsol_na(struct net_device *dev)
527{
528 struct inet6_dev *idev;
529 struct inet6_ifaddr *ifa;
Ben Hutchingsf47b9462011-04-15 13:46:02 +0000530
531 idev = in6_dev_get(dev);
532 if (!idev)
533 return;
534
535 read_lock_bh(&idev->lock);
536 list_for_each_entry(ifa, &idev->addr_list, if_list) {
YOSHIFUJI Hideaki / 吉藤英明9fafd652012-11-12 07:50:17 +0000537 ndisc_send_na(dev, NULL, &in6addr_linklocal_allnodes, &ifa->addr,
Ben Hutchingsf47b9462011-04-15 13:46:02 +0000538 /*router=*/ !!idev->cnf.forwarding,
539 /*solicited=*/ false, /*override=*/ true,
540 /*inc_opt=*/ true);
541 }
542 read_unlock_bh(&idev->lock);
543
544 in6_dev_put(idev);
545}
546
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +0900548 const struct in6_addr *solicit,
549 const struct in6_addr *daddr, const struct in6_addr *saddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700550{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700551 struct in6_addr addr_buf;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900552 struct icmp6hdr icmp6h = {
553 .icmp6_type = NDISC_NEIGHBOUR_SOLICITATION,
554 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700555
556 if (saddr == NULL) {
Neil Horman95c385b2007-04-25 17:08:10 -0700557 if (ipv6_get_lladdr(dev, &addr_buf,
558 (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700559 return;
560 saddr = &addr_buf;
561 }
562
YOSHIFUJI Hideaki / 吉藤英明12fd84f2013-01-18 02:00:24 +0000563 __ndisc_send(dev, daddr, saddr, &icmp6h, solicit,
David L Stevens14878f72007-09-16 16:52:35 -0700564 !ipv6_addr_any(saddr) ? ND_OPT_SOURCE_LL_ADDR : 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565}
566
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +0900567void ndisc_send_rs(struct net_device *dev, const struct in6_addr *saddr,
568 const struct in6_addr *daddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569{
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900570 struct icmp6hdr icmp6h = {
571 .icmp6_type = NDISC_ROUTER_SOLICITATION,
572 };
Neil Horman95c385b2007-04-25 17:08:10 -0700573 int send_sllao = dev->addr_len;
Neil Horman95c385b2007-04-25 17:08:10 -0700574
575#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
576 /*
577 * According to section 2.2 of RFC 4429, we must not
578 * send router solicitations with a sllao from
579 * optimistic addresses, but we may send the solicitation
580 * if we don't include the sllao. So here we check
581 * if our address is optimistic, and if so, we
Joe Perchesbea85192007-12-20 14:01:35 -0800582 * suppress the inclusion of the sllao.
Neil Horman95c385b2007-04-25 17:08:10 -0700583 */
584 if (send_sllao) {
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900585 struct inet6_ifaddr *ifp = ipv6_get_ifaddr(dev_net(dev), saddr,
Daniel Lezcano1cab3da2008-01-10 22:44:09 -0800586 dev, 1);
Neil Horman95c385b2007-04-25 17:08:10 -0700587 if (ifp) {
588 if (ifp->flags & IFA_F_OPTIMISTIC) {
YOSHIFUJI Hideakica043562007-02-28 23:13:20 +0900589 send_sllao = 0;
Neil Horman95c385b2007-04-25 17:08:10 -0700590 }
YOSHIFUJI Hideakica043562007-02-28 23:13:20 +0900591 in6_ifa_put(ifp);
Neil Horman95c385b2007-04-25 17:08:10 -0700592 } else {
593 send_sllao = 0;
594 }
595 }
596#endif
YOSHIFUJI Hideaki / 吉藤英明12fd84f2013-01-18 02:00:24 +0000597 __ndisc_send(dev, daddr, saddr, &icmp6h, NULL,
David L Stevens14878f72007-09-16 16:52:35 -0700598 send_sllao ? ND_OPT_SOURCE_LL_ADDR : 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700599}
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900600
Linus Torvalds1da177e2005-04-16 15:20:36 -0700601
602static void ndisc_error_report(struct neighbour *neigh, struct sk_buff *skb)
603{
604 /*
605 * "The sender MUST return an ICMP
606 * destination unreachable"
607 */
608 dst_link_failure(skb);
609 kfree_skb(skb);
610}
611
612/* Called with locked neigh: either read or both */
613
614static void ndisc_solicit(struct neighbour *neigh, struct sk_buff *skb)
615{
616 struct in6_addr *saddr = NULL;
617 struct in6_addr mcaddr;
618 struct net_device *dev = neigh->dev;
619 struct in6_addr *target = (struct in6_addr *)&neigh->primary_key;
620 int probes = atomic_read(&neigh->probes);
621
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900622 if (skb && ipv6_chk_addr(dev_net(dev), &ipv6_hdr(skb)->saddr, dev, 1))
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -0700623 saddr = &ipv6_hdr(skb)->saddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700624
625 if ((probes -= neigh->parms->ucast_probes) < 0) {
626 if (!(neigh->nud_state & NUD_VALID)) {
Joe Perches675418d2012-05-16 19:28:38 +0000627 ND_PRINTK(1, dbg,
628 "%s: trying to ucast probe in NUD_INVALID: %pI6\n",
629 __func__, target);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700630 }
631 ndisc_send_ns(dev, neigh, target, target, saddr);
632 } else if ((probes -= neigh->parms->app_probes) < 0) {
633#ifdef CONFIG_ARPD
634 neigh_app_ns(neigh);
635#endif
636 } else {
637 addrconf_addr_solict_mult(target, &mcaddr);
638 ndisc_send_ns(dev, NULL, target, &mcaddr, saddr);
639 }
640}
641
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900642static int pndisc_is_router(const void *pkey,
643 struct net_device *dev)
Pavel Emelyanovfa86d322008-03-24 14:48:59 -0700644{
645 struct pneigh_entry *n;
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900646 int ret = -1;
Pavel Emelyanovfa86d322008-03-24 14:48:59 -0700647
648 read_lock_bh(&nd_tbl.lock);
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900649 n = __pneigh_lookup(&nd_tbl, dev_net(dev), pkey, dev);
650 if (n)
651 ret = !!(n->flags & NTF_ROUTER);
Pavel Emelyanovfa86d322008-03-24 14:48:59 -0700652 read_unlock_bh(&nd_tbl.lock);
653
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900654 return ret;
Pavel Emelyanovfa86d322008-03-24 14:48:59 -0700655}
656
Linus Torvalds1da177e2005-04-16 15:20:36 -0700657static void ndisc_recv_ns(struct sk_buff *skb)
658{
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -0700659 struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb);
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000660 const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
661 const struct in6_addr *daddr = &ipv6_hdr(skb)->daddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700662 u8 *lladdr = NULL;
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -0700663 u32 ndoptlen = skb->tail - (skb->transport_header +
664 offsetof(struct nd_msg, opt));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700665 struct ndisc_options ndopts;
666 struct net_device *dev = skb->dev;
667 struct inet6_ifaddr *ifp;
668 struct inet6_dev *idev = NULL;
669 struct neighbour *neigh;
670 int dad = ipv6_addr_any(saddr);
Eric Dumazeta50feda2012-05-18 18:57:34 +0000671 bool inc;
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900672 int is_router = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700673
674 if (ipv6_addr_is_multicast(&msg->target)) {
Joe Perches675418d2012-05-16 19:28:38 +0000675 ND_PRINTK(2, warn, "NS: multicast target address\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700676 return;
677 }
678
679 /*
680 * RFC2461 7.1.1:
681 * DAD has to be destined for solicited node multicast address.
682 */
683 if (dad &&
684 !(daddr->s6_addr32[0] == htonl(0xff020000) &&
685 daddr->s6_addr32[1] == htonl(0x00000000) &&
686 daddr->s6_addr32[2] == htonl(0x00000001) &&
687 daddr->s6_addr [12] == 0xff )) {
Joe Perches675418d2012-05-16 19:28:38 +0000688 ND_PRINTK(2, warn, "NS: bad DAD packet (wrong destination)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700689 return;
690 }
691
692 if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts)) {
Joe Perches675418d2012-05-16 19:28:38 +0000693 ND_PRINTK(2, warn, "NS: invalid ND options\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700694 return;
695 }
696
697 if (ndopts.nd_opts_src_lladdr) {
698 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr, dev);
699 if (!lladdr) {
Joe Perches675418d2012-05-16 19:28:38 +0000700 ND_PRINTK(2, warn,
701 "NS: invalid link-layer address length\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700702 return;
703 }
704
705 /* RFC2461 7.1.1:
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900706 * If the IP source address is the unspecified address,
707 * there MUST NOT be source link-layer address option
Linus Torvalds1da177e2005-04-16 15:20:36 -0700708 * in the message.
709 */
710 if (dad) {
Joe Perches675418d2012-05-16 19:28:38 +0000711 ND_PRINTK(2, warn,
712 "NS: bad DAD packet (link-layer address option)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700713 return;
714 }
715 }
716
717 inc = ipv6_addr_is_multicast(daddr);
718
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900719 ifp = ipv6_get_ifaddr(dev_net(dev), &msg->target, dev, 1);
Daniel Lezcanoa18bc692008-03-07 11:14:49 -0800720 if (ifp) {
Neil Horman95c385b2007-04-25 17:08:10 -0700721
722 if (ifp->flags & (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)) {
723 if (dad) {
Neil Horman95c385b2007-04-25 17:08:10 -0700724 /*
725 * We are colliding with another node
726 * who is doing DAD
727 * so fail our DAD process
728 */
729 addrconf_dad_failure(ifp);
Denis V. Lunev9e3be4b2007-09-11 11:04:49 +0200730 return;
Neil Horman95c385b2007-04-25 17:08:10 -0700731 } else {
732 /*
733 * This is not a dad solicitation.
734 * If we are an optimistic node,
735 * we should respond.
736 * Otherwise, we should ignore it.
737 */
738 if (!(ifp->flags & IFA_F_OPTIMISTIC))
739 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700740 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700741 }
742
743 idev = ifp->idev;
744 } else {
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700745 struct net *net = dev_net(dev);
746
Linus Torvalds1da177e2005-04-16 15:20:36 -0700747 idev = in6_dev_get(dev);
748 if (!idev) {
749 /* XXX: count this drop? */
750 return;
751 }
752
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700753 if (ipv6_chk_acast_addr(net, dev, &msg->target) ||
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900754 (idev->cnf.forwarding &&
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700755 (net->ipv6.devconf_all->proxy_ndp || idev->cnf.proxy_ndp) &&
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900756 (is_router = pndisc_is_router(&msg->target, dev)) >= 0)) {
Patrick McHardya61bbcf2005-08-14 17:24:31 -0700757 if (!(NEIGH_CB(skb)->flags & LOCALLY_ENQUEUED) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700758 skb->pkt_type != PACKET_HOST &&
759 inc != 0 &&
760 idev->nd_parms->proxy_delay != 0) {
761 /*
762 * for anycast or proxy,
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900763 * sender should delay its response
764 * by a random time between 0 and
Linus Torvalds1da177e2005-04-16 15:20:36 -0700765 * MAX_ANYCAST_DELAY_TIME seconds.
766 * (RFC2461) -- yoshfuji
767 */
768 struct sk_buff *n = skb_clone(skb, GFP_ATOMIC);
769 if (n)
770 pneigh_enqueue(&nd_tbl, idev->nd_parms, n);
771 goto out;
772 }
773 } else
774 goto out;
775 }
776
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900777 if (is_router < 0)
778 is_router = !!idev->cnf.forwarding;
Ville Nuorvala62dd9312006-09-22 14:43:19 -0700779
Linus Torvalds1da177e2005-04-16 15:20:36 -0700780 if (dad) {
YOSHIFUJI Hideakif3ee4012008-04-10 15:42:11 +0900781 ndisc_send_na(dev, NULL, &in6addr_linklocal_allnodes, &msg->target,
Ville Nuorvala62dd9312006-09-22 14:43:19 -0700782 is_router, 0, (ifp != NULL), 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700783 goto out;
784 }
785
786 if (inc)
787 NEIGH_CACHE_STAT_INC(&nd_tbl, rcv_probes_mcast);
788 else
789 NEIGH_CACHE_STAT_INC(&nd_tbl, rcv_probes_ucast);
790
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900791 /*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700792 * update / create cache entry
793 * for the source address
794 */
795 neigh = __neigh_lookup(&nd_tbl, saddr, dev,
796 !inc || lladdr || !dev->addr_len);
797 if (neigh)
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900798 neigh_update(neigh, lladdr, NUD_STALE,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799 NEIGH_UPDATE_F_WEAK_OVERRIDE|
800 NEIGH_UPDATE_F_OVERRIDE);
Stephen Hemminger3b04ddd2007-10-09 01:40:57 -0700801 if (neigh || !dev->header_ops) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700802 ndisc_send_na(dev, neigh, saddr, &msg->target,
Ville Nuorvala62dd9312006-09-22 14:43:19 -0700803 is_router,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700804 1, (ifp != NULL && inc), inc);
805 if (neigh)
806 neigh_release(neigh);
807 }
808
809out:
810 if (ifp)
811 in6_ifa_put(ifp);
812 else
813 in6_dev_put(idev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700814}
815
816static void ndisc_recv_na(struct sk_buff *skb)
817{
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -0700818 struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb);
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000819 const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
820 const struct in6_addr *daddr = &ipv6_hdr(skb)->daddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700821 u8 *lladdr = NULL;
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -0700822 u32 ndoptlen = skb->tail - (skb->transport_header +
823 offsetof(struct nd_msg, opt));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700824 struct ndisc_options ndopts;
825 struct net_device *dev = skb->dev;
826 struct inet6_ifaddr *ifp;
827 struct neighbour *neigh;
828
829 if (skb->len < sizeof(struct nd_msg)) {
Joe Perches675418d2012-05-16 19:28:38 +0000830 ND_PRINTK(2, warn, "NA: packet too short\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700831 return;
832 }
833
834 if (ipv6_addr_is_multicast(&msg->target)) {
Joe Perches675418d2012-05-16 19:28:38 +0000835 ND_PRINTK(2, warn, "NA: target address is multicast\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700836 return;
837 }
838
839 if (ipv6_addr_is_multicast(daddr) &&
840 msg->icmph.icmp6_solicited) {
Joe Perches675418d2012-05-16 19:28:38 +0000841 ND_PRINTK(2, warn, "NA: solicited NA is multicasted\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700842 return;
843 }
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900844
Linus Torvalds1da177e2005-04-16 15:20:36 -0700845 if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts)) {
Joe Perches675418d2012-05-16 19:28:38 +0000846 ND_PRINTK(2, warn, "NS: invalid ND option\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700847 return;
848 }
849 if (ndopts.nd_opts_tgt_lladdr) {
850 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr, dev);
851 if (!lladdr) {
Joe Perches675418d2012-05-16 19:28:38 +0000852 ND_PRINTK(2, warn,
853 "NA: invalid link-layer address length\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700854 return;
855 }
856 }
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900857 ifp = ipv6_get_ifaddr(dev_net(dev), &msg->target, dev, 1);
Daniel Lezcanoa18bc692008-03-07 11:14:49 -0800858 if (ifp) {
Daniel Walterbd015922011-04-13 21:09:25 +0000859 if (skb->pkt_type != PACKET_LOOPBACK
860 && (ifp->flags & IFA_F_TENTATIVE)) {
861 addrconf_dad_failure(ifp);
862 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700863 }
864 /* What should we make now? The advertisement
865 is invalid, but ndisc specs say nothing
866 about it. It could be misconfiguration, or
867 an smart proxy agent tries to help us :-)
Jan Sembera24fc7b82008-12-09 15:48:32 -0800868
869 We should not print the error if NA has been
870 received from loopback - it is just our own
871 unsolicited advertisement.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700872 */
Jan Sembera24fc7b82008-12-09 15:48:32 -0800873 if (skb->pkt_type != PACKET_LOOPBACK)
Joe Perches675418d2012-05-16 19:28:38 +0000874 ND_PRINTK(1, warn,
875 "NA: someone advertises our address %pI6 on %s!\n",
876 &ifp->addr, ifp->idev->dev->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700877 in6_ifa_put(ifp);
878 return;
879 }
880 neigh = neigh_lookup(&nd_tbl, &msg->target, dev);
881
882 if (neigh) {
883 u8 old_flags = neigh->flags;
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700884 struct net *net = dev_net(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700885
886 if (neigh->nud_state & NUD_FAILED)
887 goto out;
888
Ville Nuorvala5f3e6e92006-09-22 14:42:46 -0700889 /*
890 * Don't update the neighbor cache entry on a proxy NA from
891 * ourselves because either the proxied node is off link or it
892 * has already sent a NA to us.
893 */
894 if (lladdr && !memcmp(lladdr, dev->dev_addr, dev->addr_len) &&
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700895 net->ipv6.devconf_all->forwarding && net->ipv6.devconf_all->proxy_ndp &&
896 pneigh_lookup(&nd_tbl, net, &msg->target, dev, 0)) {
Nicolas Dichtelb20b6d92012-11-07 05:05:38 +0000897 /* XXX: idev->cnf.proxy_ndp */
Ville Nuorvala5f3e6e92006-09-22 14:42:46 -0700898 goto out;
YOSHIFUJI Hideakifbea49e2006-09-22 14:43:49 -0700899 }
Ville Nuorvala5f3e6e92006-09-22 14:42:46 -0700900
Linus Torvalds1da177e2005-04-16 15:20:36 -0700901 neigh_update(neigh, lladdr,
902 msg->icmph.icmp6_solicited ? NUD_REACHABLE : NUD_STALE,
903 NEIGH_UPDATE_F_WEAK_OVERRIDE|
904 (msg->icmph.icmp6_override ? NEIGH_UPDATE_F_OVERRIDE : 0)|
905 NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
906 (msg->icmph.icmp6_router ? NEIGH_UPDATE_F_ISROUTER : 0));
907
908 if ((old_flags & ~neigh->flags) & NTF_ROUTER) {
909 /*
910 * Change: router to host
911 */
912 struct rt6_info *rt;
913 rt = rt6_get_dflt_router(saddr, dev);
914 if (rt)
Thomas Grafe0a1ad732006-08-22 00:00:21 -0700915 ip6_del_rt(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700916 }
917
918out:
919 neigh_release(neigh);
920 }
921}
922
923static void ndisc_recv_rs(struct sk_buff *skb)
924{
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -0700925 struct rs_msg *rs_msg = (struct rs_msg *)skb_transport_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700926 unsigned long ndoptlen = skb->len - sizeof(*rs_msg);
927 struct neighbour *neigh;
928 struct inet6_dev *idev;
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000929 const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700930 struct ndisc_options ndopts;
931 u8 *lladdr = NULL;
932
933 if (skb->len < sizeof(*rs_msg))
934 return;
935
Eric Dumazetcfdf7642011-07-27 21:13:03 +0000936 idev = __in6_dev_get(skb->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700937 if (!idev) {
Joe Perches675418d2012-05-16 19:28:38 +0000938 ND_PRINTK(1, err, "RS: can't find in6 device\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700939 return;
940 }
941
942 /* Don't accept RS if we're not in router mode */
943 if (!idev->cnf.forwarding)
944 goto out;
945
946 /*
947 * Don't update NCE if src = ::;
948 * this implies that the source node has no ip address assigned yet.
949 */
950 if (ipv6_addr_any(saddr))
951 goto out;
952
953 /* Parse ND options */
954 if (!ndisc_parse_options(rs_msg->opt, ndoptlen, &ndopts)) {
Joe Perches675418d2012-05-16 19:28:38 +0000955 ND_PRINTK(2, notice, "NS: invalid ND option, ignored\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700956 goto out;
957 }
958
959 if (ndopts.nd_opts_src_lladdr) {
960 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr,
961 skb->dev);
962 if (!lladdr)
963 goto out;
964 }
965
966 neigh = __neigh_lookup(&nd_tbl, saddr, skb->dev, 1);
967 if (neigh) {
968 neigh_update(neigh, lladdr, NUD_STALE,
969 NEIGH_UPDATE_F_WEAK_OVERRIDE|
970 NEIGH_UPDATE_F_OVERRIDE|
971 NEIGH_UPDATE_F_OVERRIDE_ISROUTER);
972 neigh_release(neigh);
973 }
974out:
Eric Dumazetcfdf7642011-07-27 21:13:03 +0000975 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700976}
977
Pierre Ynard31910572007-10-10 21:22:05 -0700978static void ndisc_ra_useropt(struct sk_buff *ra, struct nd_opt_hdr *opt)
979{
980 struct icmp6hdr *icmp6h = (struct icmp6hdr *)skb_transport_header(ra);
981 struct sk_buff *skb;
982 struct nlmsghdr *nlh;
983 struct nduseroptmsg *ndmsg;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900984 struct net *net = dev_net(ra->dev);
Pierre Ynard31910572007-10-10 21:22:05 -0700985 int err;
986 int base_size = NLMSG_ALIGN(sizeof(struct nduseroptmsg)
987 + (opt->nd_opt_len << 3));
988 size_t msg_size = base_size + nla_total_size(sizeof(struct in6_addr));
989
990 skb = nlmsg_new(msg_size, GFP_ATOMIC);
991 if (skb == NULL) {
992 err = -ENOBUFS;
993 goto errout;
994 }
995
996 nlh = nlmsg_put(skb, 0, 0, RTM_NEWNDUSEROPT, base_size, 0);
997 if (nlh == NULL) {
998 goto nla_put_failure;
999 }
1000
1001 ndmsg = nlmsg_data(nlh);
1002 ndmsg->nduseropt_family = AF_INET6;
Pierre Ynarddbb2ed22007-11-12 17:58:35 -08001003 ndmsg->nduseropt_ifindex = ra->dev->ifindex;
Pierre Ynard31910572007-10-10 21:22:05 -07001004 ndmsg->nduseropt_icmp_type = icmp6h->icmp6_type;
1005 ndmsg->nduseropt_icmp_code = icmp6h->icmp6_code;
1006 ndmsg->nduseropt_opts_len = opt->nd_opt_len << 3;
1007
1008 memcpy(ndmsg + 1, opt, opt->nd_opt_len << 3);
1009
David S. Millerc78679e2012-04-01 20:27:33 -04001010 if (nla_put(skb, NDUSEROPT_SRCADDR, sizeof(struct in6_addr),
1011 &ipv6_hdr(ra)->saddr))
1012 goto nla_put_failure;
Pierre Ynard31910572007-10-10 21:22:05 -07001013 nlmsg_end(skb, nlh);
1014
Pablo Neira Ayuso1ce85fe2009-02-24 23:18:28 -08001015 rtnl_notify(skb, net, 0, RTNLGRP_ND_USEROPT, NULL, GFP_ATOMIC);
Pierre Ynard31910572007-10-10 21:22:05 -07001016 return;
1017
1018nla_put_failure:
1019 nlmsg_free(skb);
1020 err = -EMSGSIZE;
1021errout:
Daniel Lezcanoa18bc692008-03-07 11:14:49 -08001022 rtnl_set_sk_err(net, RTNLGRP_ND_USEROPT, err);
Pierre Ynard31910572007-10-10 21:22:05 -07001023}
1024
Linus Torvalds1da177e2005-04-16 15:20:36 -07001025static void ndisc_router_discovery(struct sk_buff *skb)
1026{
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -07001027 struct ra_msg *ra_msg = (struct ra_msg *)skb_transport_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001028 struct neighbour *neigh = NULL;
1029 struct inet6_dev *in6_dev;
YOSHIFUJI Hideaki65f5c7c2006-03-20 16:55:08 -08001030 struct rt6_info *rt = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001031 int lifetime;
1032 struct ndisc_options ndopts;
1033 int optlen;
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001034 unsigned int pref = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001035
1036 __u8 * opt = (__u8 *)(ra_msg + 1);
1037
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -07001038 optlen = (skb->tail - skb->transport_header) - sizeof(struct ra_msg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001039
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001040 if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) {
Joe Perches675418d2012-05-16 19:28:38 +00001041 ND_PRINTK(2, warn, "RA: source address is not link-local\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001042 return;
1043 }
1044 if (optlen < 0) {
Joe Perches675418d2012-05-16 19:28:38 +00001045 ND_PRINTK(2, warn, "RA: packet too short\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001046 return;
1047 }
1048
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001049#ifdef CONFIG_IPV6_NDISC_NODETYPE
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001050 if (skb->ndisc_nodetype == NDISC_NODETYPE_HOST) {
Joe Perches675418d2012-05-16 19:28:38 +00001051 ND_PRINTK(2, warn, "RA: from host or unauthorized router\n");
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001052 return;
1053 }
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001054#endif
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001055
Linus Torvalds1da177e2005-04-16 15:20:36 -07001056 /*
1057 * set the RA_RECV flag in the interface
1058 */
1059
Eric Dumazetcfdf7642011-07-27 21:13:03 +00001060 in6_dev = __in6_dev_get(skb->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001061 if (in6_dev == NULL) {
Joe Perches675418d2012-05-16 19:28:38 +00001062 ND_PRINTK(0, err, "RA: can't find inet6 device for %s\n",
1063 skb->dev->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001064 return;
1065 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001066
1067 if (!ndisc_parse_options(opt, optlen, &ndopts)) {
Joe Perches675418d2012-05-16 19:28:38 +00001068 ND_PRINTK(2, warn, "RA: invalid ND options\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001069 return;
1070 }
1071
Shmulik Ladkaniaeaf6e92012-11-30 10:25:59 +00001072 if (!ipv6_accept_ra(in6_dev))
David Ward31ce8c72009-08-29 00:04:09 -07001073 goto skip_linkparms;
1074
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001075#ifdef CONFIG_IPV6_NDISC_NODETYPE
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001076 /* skip link-specific parameters from interior routers */
1077 if (skb->ndisc_nodetype == NDISC_NODETYPE_NODEFAULT)
1078 goto skip_linkparms;
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001079#endif
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001080
Linus Torvalds1da177e2005-04-16 15:20:36 -07001081 if (in6_dev->if_flags & IF_RS_SENT) {
1082 /*
1083 * flag that an RA was received after an RS was sent
1084 * out on this interface.
1085 */
1086 in6_dev->if_flags |= IF_RA_RCVD;
1087 }
1088
1089 /*
1090 * Remember the managed/otherconf flags from most recently
1091 * received RA message (RFC 2462) -- yoshfuji
1092 */
1093 in6_dev->if_flags = (in6_dev->if_flags & ~(IF_RA_MANAGED |
1094 IF_RA_OTHERCONF)) |
1095 (ra_msg->icmph.icmp6_addrconf_managed ?
1096 IF_RA_MANAGED : 0) |
1097 (ra_msg->icmph.icmp6_addrconf_other ?
1098 IF_RA_OTHERCONF : 0);
1099
YOSHIFUJI Hideaki65f5c7c2006-03-20 16:55:08 -08001100 if (!in6_dev->cnf.accept_ra_defrtr)
1101 goto skip_defrtr;
1102
Andreas Hofmeister9f562202011-10-24 19:13:15 -04001103 if (ipv6_chk_addr(dev_net(in6_dev->dev), &ipv6_hdr(skb)->saddr, NULL, 0))
1104 goto skip_defrtr;
1105
Linus Torvalds1da177e2005-04-16 15:20:36 -07001106 lifetime = ntohs(ra_msg->icmph.icmp6_rt_lifetime);
1107
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001108#ifdef CONFIG_IPV6_ROUTER_PREF
1109 pref = ra_msg->icmph.icmp6_router_pref;
1110 /* 10b is handled as if it were 00b (medium) */
YOSHIFUJI Hideaki930d6ff2006-03-20 17:05:30 -08001111 if (pref == ICMPV6_ROUTER_PREF_INVALID ||
YOSHIFUJI Hideaki6d5b78c2007-06-22 16:07:04 -07001112 !in6_dev->cnf.accept_ra_rtr_pref)
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001113 pref = ICMPV6_ROUTER_PREF_MEDIUM;
1114#endif
1115
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001116 rt = rt6_get_dflt_router(&ipv6_hdr(skb)->saddr, skb->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001117
David S. Millereb857182012-01-27 15:07:56 -08001118 if (rt) {
1119 neigh = dst_neigh_lookup(&rt->dst, &ipv6_hdr(skb)->saddr);
1120 if (!neigh) {
Joe Perches675418d2012-05-16 19:28:38 +00001121 ND_PRINTK(0, err,
1122 "RA: %s got default router without neighbour\n",
1123 __func__);
Amerigo Wang94e187c2012-10-29 00:13:19 +00001124 ip6_rt_put(rt);
David S. Millereb857182012-01-27 15:07:56 -08001125 return;
1126 }
1127 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001128 if (rt && lifetime == 0) {
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001129 ip6_del_rt(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001130 rt = NULL;
1131 }
1132
1133 if (rt == NULL && lifetime) {
Joe Perches675418d2012-05-16 19:28:38 +00001134 ND_PRINTK(3, dbg, "RA: adding default router\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001135
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001136 rt = rt6_add_dflt_router(&ipv6_hdr(skb)->saddr, skb->dev, pref);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001137 if (rt == NULL) {
Joe Perches675418d2012-05-16 19:28:38 +00001138 ND_PRINTK(0, err,
1139 "RA: %s failed to add default route\n",
1140 __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001141 return;
1142 }
1143
David S. Millereb857182012-01-27 15:07:56 -08001144 neigh = dst_neigh_lookup(&rt->dst, &ipv6_hdr(skb)->saddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001145 if (neigh == NULL) {
Joe Perches675418d2012-05-16 19:28:38 +00001146 ND_PRINTK(0, err,
1147 "RA: %s got default router without neighbour\n",
1148 __func__);
Amerigo Wang94e187c2012-10-29 00:13:19 +00001149 ip6_rt_put(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001150 return;
1151 }
1152 neigh->flags |= NTF_ROUTER;
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001153 } else if (rt) {
Pedro Ribeiro22441cf2008-10-15 15:47:49 -07001154 rt->rt6i_flags = (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001155 }
1156
1157 if (rt)
Gao feng1716a962012-04-06 00:13:10 +00001158 rt6_set_expires(rt, jiffies + (HZ * lifetime));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001159 if (ra_msg->icmph.icmp6_hop_limit) {
1160 in6_dev->cnf.hop_limit = ra_msg->icmph.icmp6_hop_limit;
1161 if (rt)
David S. Millerdefb3512010-12-08 21:16:57 -08001162 dst_metric_set(&rt->dst, RTAX_HOPLIMIT,
1163 ra_msg->icmph.icmp6_hop_limit);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001164 }
1165
YOSHIFUJI Hideaki65f5c7c2006-03-20 16:55:08 -08001166skip_defrtr:
1167
Linus Torvalds1da177e2005-04-16 15:20:36 -07001168 /*
1169 * Update Reachable Time and Retrans Timer
1170 */
1171
1172 if (in6_dev->nd_parms) {
1173 unsigned long rtime = ntohl(ra_msg->retrans_timer);
1174
1175 if (rtime && rtime/1000 < MAX_SCHEDULE_TIMEOUT/HZ) {
1176 rtime = (rtime*HZ)/1000;
1177 if (rtime < HZ/10)
1178 rtime = HZ/10;
1179 in6_dev->nd_parms->retrans_time = rtime;
1180 in6_dev->tstamp = jiffies;
1181 inet6_ifinfo_notify(RTM_NEWLINK, in6_dev);
1182 }
1183
1184 rtime = ntohl(ra_msg->reachable_time);
1185 if (rtime && rtime/1000 < MAX_SCHEDULE_TIMEOUT/(3*HZ)) {
1186 rtime = (rtime*HZ)/1000;
1187
1188 if (rtime < HZ/10)
1189 rtime = HZ/10;
1190
1191 if (rtime != in6_dev->nd_parms->base_reachable_time) {
1192 in6_dev->nd_parms->base_reachable_time = rtime;
1193 in6_dev->nd_parms->gc_staletime = 3 * rtime;
1194 in6_dev->nd_parms->reachable_time = neigh_rand_reach_time(rtime);
1195 in6_dev->tstamp = jiffies;
1196 inet6_ifinfo_notify(RTM_NEWLINK, in6_dev);
1197 }
1198 }
1199 }
1200
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001201skip_linkparms:
1202
Linus Torvalds1da177e2005-04-16 15:20:36 -07001203 /*
1204 * Process options.
1205 */
1206
1207 if (!neigh)
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001208 neigh = __neigh_lookup(&nd_tbl, &ipv6_hdr(skb)->saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001209 skb->dev, 1);
1210 if (neigh) {
1211 u8 *lladdr = NULL;
1212 if (ndopts.nd_opts_src_lladdr) {
1213 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr,
1214 skb->dev);
1215 if (!lladdr) {
Joe Perches675418d2012-05-16 19:28:38 +00001216 ND_PRINTK(2, warn,
1217 "RA: invalid link-layer address length\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001218 goto out;
1219 }
1220 }
1221 neigh_update(neigh, lladdr, NUD_STALE,
1222 NEIGH_UPDATE_F_WEAK_OVERRIDE|
1223 NEIGH_UPDATE_F_OVERRIDE|
1224 NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
1225 NEIGH_UPDATE_F_ISROUTER);
1226 }
1227
Shmulik Ladkaniaeaf6e92012-11-30 10:25:59 +00001228 if (!ipv6_accept_ra(in6_dev))
David Ward31ce8c72009-08-29 00:04:09 -07001229 goto out;
1230
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001231#ifdef CONFIG_IPV6_ROUTE_INFO
Andreas Hofmeister9f562202011-10-24 19:13:15 -04001232 if (ipv6_chk_addr(dev_net(in6_dev->dev), &ipv6_hdr(skb)->saddr, NULL, 0))
1233 goto skip_routeinfo;
1234
YOSHIFUJI Hideaki09c884d2006-03-20 17:07:03 -08001235 if (in6_dev->cnf.accept_ra_rtr_pref && ndopts.nd_opts_ri) {
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001236 struct nd_opt_hdr *p;
1237 for (p = ndopts.nd_opts_ri;
1238 p;
1239 p = ndisc_next_option(p, ndopts.nd_opts_ri_end)) {
YOSHIFUJI Hideaki6294e002008-03-15 23:56:52 -04001240 struct route_info *ri = (struct route_info *)p;
1241#ifdef CONFIG_IPV6_NDISC_NODETYPE
1242 if (skb->ndisc_nodetype == NDISC_NODETYPE_NODEFAULT &&
1243 ri->prefix_len == 0)
1244 continue;
1245#endif
1246 if (ri->prefix_len > in6_dev->cnf.accept_ra_rt_info_max_plen)
YOSHIFUJI Hideaki09c884d2006-03-20 17:07:03 -08001247 continue;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001248 rt6_route_rcv(skb->dev, (u8*)p, (p->nd_opt_len) << 3,
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001249 &ipv6_hdr(skb)->saddr);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001250 }
1251 }
Andreas Hofmeister9f562202011-10-24 19:13:15 -04001252
1253skip_routeinfo:
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001254#endif
1255
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001256#ifdef CONFIG_IPV6_NDISC_NODETYPE
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001257 /* skip link-specific ndopts from interior routers */
1258 if (skb->ndisc_nodetype == NDISC_NODETYPE_NODEFAULT)
1259 goto out;
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001260#endif
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001261
YOSHIFUJI Hideakic4fd30e2006-03-20 16:55:26 -08001262 if (in6_dev->cnf.accept_ra_pinfo && ndopts.nd_opts_pi) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001263 struct nd_opt_hdr *p;
1264 for (p = ndopts.nd_opts_pi;
1265 p;
1266 p = ndisc_next_option(p, ndopts.nd_opts_pi_end)) {
Neil Hormane6bff992012-01-04 10:49:15 +00001267 addrconf_prefix_rcv(skb->dev, (u8 *)p,
1268 (p->nd_opt_len) << 3,
1269 ndopts.nd_opts_src_lladdr != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001270 }
1271 }
1272
1273 if (ndopts.nd_opts_mtu) {
Al Viroe69a4adc2006-11-14 20:56:00 -08001274 __be32 n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001275 u32 mtu;
1276
Al Viroe69a4adc2006-11-14 20:56:00 -08001277 memcpy(&n, ((u8*)(ndopts.nd_opts_mtu+1))+2, sizeof(mtu));
1278 mtu = ntohl(n);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001279
1280 if (mtu < IPV6_MIN_MTU || mtu > skb->dev->mtu) {
Joe Perches675418d2012-05-16 19:28:38 +00001281 ND_PRINTK(2, warn, "RA: invalid mtu: %d\n", mtu);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001282 } else if (in6_dev->cnf.mtu6 != mtu) {
1283 in6_dev->cnf.mtu6 = mtu;
1284
1285 if (rt)
David S. Millerdefb3512010-12-08 21:16:57 -08001286 dst_metric_set(&rt->dst, RTAX_MTU, mtu);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001287
1288 rt6_mtu_change(skb->dev, mtu);
1289 }
1290 }
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001291
Pierre Ynard31910572007-10-10 21:22:05 -07001292 if (ndopts.nd_useropts) {
YOSHIFUJI Hideaki61cf46ad2008-01-22 17:32:53 +09001293 struct nd_opt_hdr *p;
1294 for (p = ndopts.nd_useropts;
1295 p;
1296 p = ndisc_next_useropt(p, ndopts.nd_useropts_end)) {
1297 ndisc_ra_useropt(skb, p);
Pierre Ynard31910572007-10-10 21:22:05 -07001298 }
1299 }
1300
Linus Torvalds1da177e2005-04-16 15:20:36 -07001301 if (ndopts.nd_opts_tgt_lladdr || ndopts.nd_opts_rh) {
Joe Perches675418d2012-05-16 19:28:38 +00001302 ND_PRINTK(2, warn, "RA: invalid RA options\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001303 }
1304out:
Amerigo Wang94e187c2012-10-29 00:13:19 +00001305 ip6_rt_put(rt);
David S. Millereb857182012-01-27 15:07:56 -08001306 if (neigh)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001307 neigh_release(neigh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001308}
1309
1310static void ndisc_redirect_rcv(struct sk_buff *skb)
1311{
Duan Jiong093d04d2012-12-14 02:59:59 +00001312 u8 *hdr;
1313 struct ndisc_options ndopts;
1314 struct rd_msg *msg = (struct rd_msg *)skb_transport_header(skb);
1315 u32 ndoptlen = skb->tail - (skb->transport_header +
1316 offsetof(struct rd_msg, opt));
1317
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001318#ifdef CONFIG_IPV6_NDISC_NODETYPE
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001319 switch (skb->ndisc_nodetype) {
1320 case NDISC_NODETYPE_HOST:
1321 case NDISC_NODETYPE_NODEFAULT:
Joe Perches675418d2012-05-16 19:28:38 +00001322 ND_PRINTK(2, warn,
1323 "Redirect: from host or unauthorized router\n");
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001324 return;
1325 }
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001326#endif
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001327
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001328 if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) {
Joe Perches675418d2012-05-16 19:28:38 +00001329 ND_PRINTK(2, warn,
1330 "Redirect: source address is not link-local\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001331 return;
1332 }
1333
Duan Jiong093d04d2012-12-14 02:59:59 +00001334 if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts))
1335 return;
1336
1337 if (!ndopts.nd_opts_rh)
1338 return;
1339
1340 hdr = (u8 *)ndopts.nd_opts_rh;
1341 hdr += 8;
1342 if (!pskb_pull(skb, hdr - skb_transport_header(skb)))
1343 return;
1344
David S. Millerb94f1c02012-07-12 00:33:37 -07001345 icmpv6_notify(skb, NDISC_REDIRECT, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001346}
1347
David S. Miller49919692012-01-27 15:30:48 -08001348void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001349{
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001350 struct net_device *dev = skb->dev;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001351 struct net *net = dev_net(dev);
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001352 struct sock *sk = net->ipv6.ndisc_sk;
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00001353 int len = sizeof(struct rd_msg);
David S. Millerfbfe95a2012-06-08 23:24:18 -07001354 struct inet_peer *peer;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001355 struct sk_buff *buff;
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00001356 struct rd_msg *msg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001357 struct in6_addr saddr_buf;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001358 struct rt6_info *rt;
1359 struct dst_entry *dst;
1360 struct inet6_dev *idev;
David S. Miller4c9483b2011-03-12 16:22:43 -05001361 struct flowi6 fl6;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001362 u8 *opt;
Herbert Xua7ae1992011-11-18 02:20:04 +00001363 int hlen, tlen;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001364 int rd_len;
1365 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001366 u8 ha_buf[MAX_ADDR_LEN], *ha = NULL;
David S. Miller1d861aa2012-07-10 03:58:16 -07001367 bool ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001368
Neil Horman95c385b2007-04-25 17:08:10 -07001369 if (ipv6_get_lladdr(dev, &saddr_buf, IFA_F_TENTATIVE)) {
Joe Perches675418d2012-05-16 19:28:38 +00001370 ND_PRINTK(2, warn, "Redirect: no link-local address on %s\n",
1371 dev->name);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001372 return;
1373 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001374
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001375 if (!ipv6_addr_equal(&ipv6_hdr(skb)->daddr, target) &&
Brian Haleybf0b48d2007-10-08 00:12:05 -07001376 ipv6_addr_type(target) != (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) {
Joe Perches675418d2012-05-16 19:28:38 +00001377 ND_PRINTK(2, warn,
1378 "Redirect: target address is not link-local unicast\n");
Li Yewang29556522007-01-30 14:33:20 -08001379 return;
1380 }
1381
David S. Miller4c9483b2011-03-12 16:22:43 -05001382 icmpv6_flow_init(sk, &fl6, NDISC_REDIRECT,
YOSHIFUJI Hideaki95e41e92007-12-06 15:43:30 -08001383 &saddr_buf, &ipv6_hdr(skb)->saddr, dev->ifindex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001384
David S. Miller4c9483b2011-03-12 16:22:43 -05001385 dst = ip6_route_output(net, NULL, &fl6);
RongQing.Li5095d642012-02-21 22:10:49 +00001386 if (dst->error) {
1387 dst_release(dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001388 return;
RongQing.Li5095d642012-02-21 22:10:49 +00001389 }
David S. Miller4c9483b2011-03-12 16:22:43 -05001390 dst = xfrm_lookup(net, dst, flowi6_to_flowi(&fl6), NULL, 0);
David S. Miller452edd52011-03-02 13:27:41 -08001391 if (IS_ERR(dst))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001392 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001393
1394 rt = (struct rt6_info *) dst;
1395
1396 if (rt->rt6i_flags & RTF_GATEWAY) {
Joe Perches675418d2012-05-16 19:28:38 +00001397 ND_PRINTK(2, warn,
1398 "Redirect: destination is not a neighbour\n");
Ilpo Järvinend73f0802009-02-06 23:47:37 -08001399 goto release;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001400 }
David S. Miller1d861aa2012-07-10 03:58:16 -07001401 peer = inet_getpeer_v6(net->ipv6.peers, &rt->rt6i_dst.addr, 1);
1402 ret = inet_peer_xrlim_allow(peer, 1*HZ);
1403 if (peer)
1404 inet_putpeer(peer);
1405 if (!ret)
Ilpo Järvinend73f0802009-02-06 23:47:37 -08001406 goto release;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001407
1408 if (dev->addr_len) {
David S. Miller49919692012-01-27 15:30:48 -08001409 struct neighbour *neigh = dst_neigh_lookup(skb_dst(skb), target);
1410 if (!neigh) {
Joe Perches675418d2012-05-16 19:28:38 +00001411 ND_PRINTK(2, warn,
1412 "Redirect: no neigh for target address\n");
David S. Miller49919692012-01-27 15:30:48 -08001413 goto release;
1414 }
1415
Linus Torvalds1da177e2005-04-16 15:20:36 -07001416 read_lock_bh(&neigh->lock);
1417 if (neigh->nud_state & NUD_VALID) {
1418 memcpy(ha_buf, neigh->ha, dev->addr_len);
1419 read_unlock_bh(&neigh->lock);
1420 ha = ha_buf;
1421 len += ndisc_opt_addr_space(dev);
1422 } else
1423 read_unlock_bh(&neigh->lock);
David S. Miller49919692012-01-27 15:30:48 -08001424
1425 neigh_release(neigh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001426 }
1427
1428 rd_len = min_t(unsigned int,
1429 IPV6_MIN_MTU-sizeof(struct ipv6hdr)-len, skb->len + 8);
1430 rd_len &= ~0x7;
1431 len += rd_len;
1432
Herbert Xua7ae1992011-11-18 02:20:04 +00001433 hlen = LL_RESERVED_SPACE(dev);
1434 tlen = dev->needed_tailroom;
David S. Millerd54a81d2006-12-02 21:00:06 -08001435 buff = sock_alloc_send_skb(sk,
YOSHIFUJI Hideaki / 吉藤英明b7dc8c32013-01-04 03:58:04 +00001436 (sizeof(struct ipv6hdr) +
Herbert Xua7ae1992011-11-18 02:20:04 +00001437 len + hlen + tlen),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001438 1, &err);
1439 if (buff == NULL) {
Joe Perches675418d2012-05-16 19:28:38 +00001440 ND_PRINTK(0, err,
1441 "Redirect: %s failed to allocate an skb, err=%d\n",
1442 __func__, err);
Ilpo Järvinend73f0802009-02-06 23:47:37 -08001443 goto release;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001444 }
1445
Herbert Xua7ae1992011-11-18 02:20:04 +00001446 skb_reserve(buff, hlen);
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001447 ip6_nd_hdr(sk, buff, dev, &saddr_buf, &ipv6_hdr(skb)->saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001448 IPPROTO_ICMPV6, len);
1449
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -07001450 skb_set_transport_header(buff, skb_tail_pointer(buff) - buff->data);
Arnaldo Carvalho de Melod10ba342007-03-14 21:05:37 -03001451 skb_put(buff, len);
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00001452 msg = (struct rd_msg *)icmp6_hdr(buff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001453
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00001454 memset(&msg->icmph, 0, sizeof(struct icmp6hdr));
1455 msg->icmph.icmp6_type = NDISC_REDIRECT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001456
1457 /*
1458 * copy target and destination addresses
1459 */
1460
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00001461 msg->target = *target;
1462 msg->dest = ipv6_hdr(skb)->daddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001463
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00001464 opt = msg->opt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001465
1466 /*
1467 * include target_address option
1468 */
1469
1470 if (ha)
1471 opt = ndisc_fill_addr_option(opt, ND_OPT_TARGET_LL_ADDR, ha,
1472 dev->addr_len, dev->type);
1473
1474 /*
1475 * build redirect option and copy skb over to the new packet.
1476 */
1477
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001478 memset(opt, 0, 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001479 *(opt++) = ND_OPT_REDIRECT_HDR;
1480 *(opt++) = (rd_len >> 3);
1481 opt += 6;
1482
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001483 memcpy(opt, ipv6_hdr(skb), rd_len - 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001484
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00001485 msg->icmph.icmp6_cksum = csum_ipv6_magic(&saddr_buf, &ipv6_hdr(skb)->saddr,
1486 len, IPPROTO_ICMPV6,
1487 csum_partial(msg, len, 0));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001488
Eric Dumazetadf30902009-06-02 05:19:30 +00001489 skb_dst_set(buff, dst);
Eric Dumazetcfdf7642011-07-27 21:13:03 +00001490 rcu_read_lock();
1491 idev = __in6_dev_get(dst->dev);
Neil Hormanedf391f2009-04-27 02:45:02 -07001492 IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUT, skb->len);
Jan Engelhardtb2e0b382010-03-23 04:09:07 +01001493 err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, buff, NULL, dst->dev,
Patrick McHardy6e23ae22007-11-19 18:53:30 -08001494 dst_output);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001495 if (!err) {
Denis V. Lunev5c5d2442008-10-08 10:33:50 -07001496 ICMP6MSGOUT_INC_STATS(net, idev, NDISC_REDIRECT);
Denis V. Luneva862f6a2008-10-08 10:33:06 -07001497 ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001498 }
1499
Eric Dumazetcfdf7642011-07-27 21:13:03 +00001500 rcu_read_unlock();
Ilpo Järvinend73f0802009-02-06 23:47:37 -08001501 return;
1502
1503release:
1504 dst_release(dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001505}
1506
1507static void pndisc_redo(struct sk_buff *skb)
1508{
YOSHIFUJI Hideaki140e26fc2005-10-05 12:11:41 -07001509 ndisc_recv_ns(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001510 kfree_skb(skb);
1511}
1512
1513int ndisc_rcv(struct sk_buff *skb)
1514{
1515 struct nd_msg *msg;
1516
1517 if (!pskb_may_pull(skb, skb->len))
1518 return 0;
1519
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -07001520 msg = (struct nd_msg *)skb_transport_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001521
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -07001522 __skb_push(skb, skb->data - skb_transport_header(skb));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001523
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001524 if (ipv6_hdr(skb)->hop_limit != 255) {
Joe Perches675418d2012-05-16 19:28:38 +00001525 ND_PRINTK(2, warn, "NDISC: invalid hop-limit: %d\n",
1526 ipv6_hdr(skb)->hop_limit);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001527 return 0;
1528 }
1529
1530 if (msg->icmph.icmp6_code != 0) {
Joe Perches675418d2012-05-16 19:28:38 +00001531 ND_PRINTK(2, warn, "NDISC: invalid ICMPv6 code: %d\n",
1532 msg->icmph.icmp6_code);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001533 return 0;
1534 }
1535
Patrick McHardya61bbcf2005-08-14 17:24:31 -07001536 memset(NEIGH_CB(skb), 0, sizeof(struct neighbour_cb));
1537
Linus Torvalds1da177e2005-04-16 15:20:36 -07001538 switch (msg->icmph.icmp6_type) {
1539 case NDISC_NEIGHBOUR_SOLICITATION:
1540 ndisc_recv_ns(skb);
1541 break;
1542
1543 case NDISC_NEIGHBOUR_ADVERTISEMENT:
1544 ndisc_recv_na(skb);
1545 break;
1546
1547 case NDISC_ROUTER_SOLICITATION:
1548 ndisc_recv_rs(skb);
1549 break;
1550
1551 case NDISC_ROUTER_ADVERTISEMENT:
1552 ndisc_router_discovery(skb);
1553 break;
1554
1555 case NDISC_REDIRECT:
1556 ndisc_redirect_rcv(skb);
1557 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -07001558 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001559
1560 return 0;
1561}
1562
1563static int ndisc_netdev_event(struct notifier_block *this, unsigned long event, void *ptr)
1564{
1565 struct net_device *dev = ptr;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001566 struct net *net = dev_net(dev);
Hannes Frederic Sowa5cb04432012-11-06 16:46:20 +00001567 struct inet6_dev *idev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001568
1569 switch (event) {
1570 case NETDEV_CHANGEADDR:
1571 neigh_changeaddr(&nd_tbl, dev);
Daniel Lezcano5b7c9312008-03-03 23:28:58 -08001572 fib6_run_gc(~0UL, net);
Hannes Frederic Sowa5cb04432012-11-06 16:46:20 +00001573 idev = in6_dev_get(dev);
1574 if (!idev)
1575 break;
1576 if (idev->cnf.ndisc_notify)
1577 ndisc_send_unsol_na(dev);
1578 in6_dev_put(idev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001579 break;
1580 case NETDEV_DOWN:
1581 neigh_ifdown(&nd_tbl, dev);
Daniel Lezcano5b7c9312008-03-03 23:28:58 -08001582 fib6_run_gc(~0UL, net);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001583 break;
Ben Hutchingsf47b9462011-04-15 13:46:02 +00001584 case NETDEV_NOTIFY_PEERS:
1585 ndisc_send_unsol_na(dev);
1586 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001587 default:
1588 break;
1589 }
1590
1591 return NOTIFY_DONE;
1592}
1593
1594static struct notifier_block ndisc_netdev_notifier = {
1595 .notifier_call = ndisc_netdev_event,
1596};
1597
1598#ifdef CONFIG_SYSCTL
1599static void ndisc_warn_deprecated_sysctl(struct ctl_table *ctl,
1600 const char *func, const char *dev_name)
1601{
1602 static char warncomm[TASK_COMM_LEN];
1603 static int warned;
1604 if (strcmp(warncomm, current->comm) && warned < 5) {
1605 strcpy(warncomm, current->comm);
Joe Perchesf3213832012-05-15 14:11:53 +00001606 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 -07001607 warncomm, func,
1608 dev_name, ctl->procname,
1609 dev_name, ctl->procname);
1610 warned++;
1611 }
1612}
1613
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001614int 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 -07001615{
1616 struct net_device *dev = ctl->extra1;
1617 struct inet6_dev *idev;
1618 int ret;
1619
Eric W. Biedermand12af672007-10-18 03:05:25 -07001620 if ((strcmp(ctl->procname, "retrans_time") == 0) ||
1621 (strcmp(ctl->procname, "base_reachable_time") == 0))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001622 ndisc_warn_deprecated_sysctl(ctl, "syscall", dev ? dev->name : "default");
1623
Eric W. Biedermand12af672007-10-18 03:05:25 -07001624 if (strcmp(ctl->procname, "retrans_time") == 0)
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001625 ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
Eric W. Biedermand12af672007-10-18 03:05:25 -07001626
1627 else if (strcmp(ctl->procname, "base_reachable_time") == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001628 ret = proc_dointvec_jiffies(ctl, write,
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001629 buffer, lenp, ppos);
Eric W. Biedermand12af672007-10-18 03:05:25 -07001630
1631 else if ((strcmp(ctl->procname, "retrans_time_ms") == 0) ||
YOSHIFUJI Hideakiad02ac12007-10-29 01:32:23 -07001632 (strcmp(ctl->procname, "base_reachable_time_ms") == 0))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001633 ret = proc_dointvec_ms_jiffies(ctl, write,
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001634 buffer, lenp, ppos);
Eric W. Biedermand12af672007-10-18 03:05:25 -07001635 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001636 ret = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001637
1638 if (write && ret == 0 && dev && (idev = in6_dev_get(dev)) != NULL) {
Eric W. Biedermand12af672007-10-18 03:05:25 -07001639 if (ctl->data == &idev->nd_parms->base_reachable_time)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001640 idev->nd_parms->reachable_time = neigh_rand_reach_time(idev->nd_parms->base_reachable_time);
1641 idev->tstamp = jiffies;
1642 inet6_ifinfo_notify(RTM_NEWLINK, idev);
1643 in6_dev_put(idev);
1644 }
1645 return ret;
1646}
1647
Linus Torvalds1da177e2005-04-16 15:20:36 -07001648
1649#endif
1650
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00001651static int __net_init ndisc_net_init(struct net *net)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001652{
1653 struct ipv6_pinfo *np;
1654 struct sock *sk;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001655 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001656
Denis V. Lunev1ed85162008-04-03 14:31:03 -07001657 err = inet_ctl_sock_create(&sk, PF_INET6,
1658 SOCK_RAW, IPPROTO_ICMPV6, net);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001659 if (err < 0) {
Joe Perches675418d2012-05-16 19:28:38 +00001660 ND_PRINTK(0, err,
1661 "NDISC: Failed to initialize the control socket (err %d)\n",
1662 err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001663 return err;
1664 }
1665
Denis V. Lunev1ed85162008-04-03 14:31:03 -07001666 net->ipv6.ndisc_sk = sk;
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001667
Linus Torvalds1da177e2005-04-16 15:20:36 -07001668 np = inet6_sk(sk);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001669 np->hop_limit = 255;
1670 /* Do not loopback ndisc messages */
1671 np->mc_loop = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001672
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001673 return 0;
1674}
1675
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00001676static void __net_exit ndisc_net_exit(struct net *net)
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001677{
Denis V. Lunev1ed85162008-04-03 14:31:03 -07001678 inet_ctl_sock_destroy(net->ipv6.ndisc_sk);
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001679}
1680
1681static struct pernet_operations ndisc_net_ops = {
1682 .init = ndisc_net_init,
1683 .exit = ndisc_net_exit,
1684};
1685
1686int __init ndisc_init(void)
1687{
1688 int err;
1689
1690 err = register_pernet_subsys(&ndisc_net_ops);
1691 if (err)
1692 return err;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001693 /*
1694 * Initialize the neighbour table
1695 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001696 neigh_table_init(&nd_tbl);
1697
1698#ifdef CONFIG_SYSCTL
Eric W. Biederman54716e32010-02-14 03:27:03 +00001699 err = neigh_sysctl_register(NULL, &nd_tbl.parms, "ipv6",
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001700 &ndisc_ifinfo_sysctl_change);
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001701 if (err)
1702 goto out_unregister_pernet;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001703#endif
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001704 err = register_netdevice_notifier(&ndisc_netdev_notifier);
1705 if (err)
1706 goto out_unregister_sysctl;
1707out:
1708 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001709
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001710out_unregister_sysctl:
1711#ifdef CONFIG_SYSCTL
1712 neigh_sysctl_unregister(&nd_tbl.parms);
1713out_unregister_pernet:
1714#endif
1715 unregister_pernet_subsys(&ndisc_net_ops);
1716 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001717}
1718
1719void ndisc_cleanup(void)
1720{
Dmitry Mishin36f73d02006-11-03 16:08:19 -08001721 unregister_netdevice_notifier(&ndisc_netdev_notifier);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001722#ifdef CONFIG_SYSCTL
1723 neigh_sysctl_unregister(&nd_tbl.parms);
1724#endif
1725 neigh_table_clear(&nd_tbl);
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001726 unregister_pernet_subsys(&ndisc_net_ops);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001727}