blob: 1776a0deee7f337f9ee824930596d7ba143b8c80 [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
YOSHIFUJI Hideaki / 吉藤英明315ff092013-01-21 06:47:50 +0000146static u8 *ndisc_fill_addr_option(u8 *opt, int type, void *data,
147 struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148{
YOSHIFUJI Hideaki / 吉藤英明315ff092013-01-21 06:47:50 +0000149 int pad = ndisc_addr_option_pad(dev->type);
150 int data_len = dev->addr_len;
YOSHIFUJI Hideaki / 吉藤英明c558e9f2013-01-21 06:47:56 +0000151 int space = ndisc_opt_addr_space(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700152
153 opt[0] = type;
154 opt[1] = space>>3;
155
156 memset(opt + 2, 0, pad);
157 opt += pad;
158 space -= pad;
159
160 memcpy(opt+2, data, data_len);
161 data_len += 2;
162 opt += data_len;
163 if ((space -= data_len) > 0)
164 memset(opt, 0, space);
165 return opt + space;
166}
167
168static struct nd_opt_hdr *ndisc_next_option(struct nd_opt_hdr *cur,
169 struct nd_opt_hdr *end)
170{
171 int type;
172 if (!cur || !end || cur >= end)
173 return NULL;
174 type = cur->nd_opt_type;
175 do {
176 cur = ((void *)cur) + (cur->nd_opt_len << 3);
177 } while(cur < end && cur->nd_opt_type != type);
Eric Dumazeta02cec22010-09-22 20:43:57 +0000178 return cur <= end && cur->nd_opt_type == type ? cur : NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700179}
180
Pierre Ynard31910572007-10-10 21:22:05 -0700181static inline int ndisc_is_useropt(struct nd_opt_hdr *opt)
182{
Alexey I. Froloffe35f30c2012-04-06 05:50:58 +0000183 return opt->nd_opt_type == ND_OPT_RDNSS ||
184 opt->nd_opt_type == ND_OPT_DNSSL;
Pierre Ynard31910572007-10-10 21:22:05 -0700185}
186
187static struct nd_opt_hdr *ndisc_next_useropt(struct nd_opt_hdr *cur,
188 struct nd_opt_hdr *end)
189{
190 if (!cur || !end || cur >= end)
191 return NULL;
192 do {
193 cur = ((void *)cur) + (cur->nd_opt_len << 3);
194 } while(cur < end && !ndisc_is_useropt(cur));
Eric Dumazeta02cec22010-09-22 20:43:57 +0000195 return cur <= end && ndisc_is_useropt(cur) ? cur : NULL;
Pierre Ynard31910572007-10-10 21:22:05 -0700196}
197
David S. Miller30f2a5f2012-07-11 23:26:46 -0700198struct ndisc_options *ndisc_parse_options(u8 *opt, int opt_len,
199 struct ndisc_options *ndopts)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700200{
201 struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)opt;
202
203 if (!nd_opt || opt_len < 0 || !ndopts)
204 return NULL;
205 memset(ndopts, 0, sizeof(*ndopts));
206 while (opt_len) {
207 int l;
208 if (opt_len < sizeof(struct nd_opt_hdr))
209 return NULL;
210 l = nd_opt->nd_opt_len << 3;
211 if (opt_len < l || l == 0)
212 return NULL;
213 switch (nd_opt->nd_opt_type) {
214 case ND_OPT_SOURCE_LL_ADDR:
215 case ND_OPT_TARGET_LL_ADDR:
216 case ND_OPT_MTU:
217 case ND_OPT_REDIRECT_HDR:
218 if (ndopts->nd_opt_array[nd_opt->nd_opt_type]) {
Joe Perches675418d2012-05-16 19:28:38 +0000219 ND_PRINTK(2, warn,
220 "%s: duplicated ND6 option found: type=%d\n",
221 __func__, nd_opt->nd_opt_type);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700222 } else {
223 ndopts->nd_opt_array[nd_opt->nd_opt_type] = nd_opt;
224 }
225 break;
226 case ND_OPT_PREFIX_INFO:
227 ndopts->nd_opts_pi_end = nd_opt;
Stephen Hemmingercfcabdc2007-10-09 01:59:42 -0700228 if (!ndopts->nd_opt_array[nd_opt->nd_opt_type])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700229 ndopts->nd_opt_array[nd_opt->nd_opt_type] = nd_opt;
230 break;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800231#ifdef CONFIG_IPV6_ROUTE_INFO
232 case ND_OPT_ROUTE_INFO:
233 ndopts->nd_opts_ri_end = nd_opt;
234 if (!ndopts->nd_opts_ri)
235 ndopts->nd_opts_ri = nd_opt;
236 break;
237#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700238 default:
Pierre Ynard31910572007-10-10 21:22:05 -0700239 if (ndisc_is_useropt(nd_opt)) {
240 ndopts->nd_useropts_end = nd_opt;
241 if (!ndopts->nd_useropts)
242 ndopts->nd_useropts = nd_opt;
243 } else {
244 /*
245 * Unknown options must be silently ignored,
246 * to accommodate future extension to the
247 * protocol.
248 */
Joe Perches675418d2012-05-16 19:28:38 +0000249 ND_PRINTK(2, notice,
250 "%s: ignored unsupported option; type=%d, len=%d\n",
251 __func__,
252 nd_opt->nd_opt_type,
253 nd_opt->nd_opt_len);
Pierre Ynard31910572007-10-10 21:22:05 -0700254 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700255 }
256 opt_len -= l;
257 nd_opt = ((void *)nd_opt) + l;
258 }
259 return ndopts;
260}
261
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000262int ndisc_mc_map(const struct in6_addr *addr, char *buf, struct net_device *dev, int dir)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700263{
264 switch (dev->type) {
265 case ARPHRD_ETHER:
266 case ARPHRD_IEEE802: /* Not sure. Check it later. --ANK */
267 case ARPHRD_FDDI:
268 ipv6_eth_mc_map(addr, buf);
269 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700270 case ARPHRD_ARCNET:
271 ipv6_arcnet_mc_map(addr, buf);
272 return 0;
273 case ARPHRD_INFINIBAND:
Rolf Manderscheida9e527e2007-12-10 13:38:41 -0700274 ipv6_ib_mc_map(addr, dev->broadcast, buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700275 return 0;
Timo Teräs93ca3bb2011-03-28 22:40:53 +0000276 case ARPHRD_IPGRE:
277 return ipv6_ipgre_mc_map(addr, dev->broadcast, buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700278 default:
279 if (dir) {
280 memcpy(buf, dev->broadcast, dev->addr_len);
281 return 0;
282 }
283 }
284 return -EINVAL;
285}
286
YOSHIFUJI Hideaki71590392007-02-22 22:05:40 +0900287EXPORT_SYMBOL(ndisc_mc_map);
288
Eric Dumazetd6bf7812010-10-04 06:15:44 +0000289static u32 ndisc_hash(const void *pkey,
290 const struct net_device *dev,
David S. Miller2c2aba62011-12-28 15:06:58 -0500291 __u32 *hash_rnd)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292{
David S. Miller2c2aba62011-12-28 15:06:58 -0500293 return ndisc_hashfn(pkey, dev, hash_rnd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700294}
295
296static int ndisc_constructor(struct neighbour *neigh)
297{
298 struct in6_addr *addr = (struct in6_addr*)&neigh->primary_key;
299 struct net_device *dev = neigh->dev;
300 struct inet6_dev *in6_dev;
301 struct neigh_parms *parms;
Eric Dumazeta50feda2012-05-18 18:57:34 +0000302 bool is_multicast = ipv6_addr_is_multicast(addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303
Linus Torvalds1da177e2005-04-16 15:20:36 -0700304 in6_dev = in6_dev_get(dev);
305 if (in6_dev == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700306 return -EINVAL;
307 }
308
309 parms = in6_dev->nd_parms;
310 __neigh_parms_put(neigh->parms);
311 neigh->parms = neigh_parms_clone(parms);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700312
313 neigh->type = is_multicast ? RTN_MULTICAST : RTN_UNICAST;
Stephen Hemminger3b04ddd2007-10-09 01:40:57 -0700314 if (!dev->header_ops) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700315 neigh->nud_state = NUD_NOARP;
316 neigh->ops = &ndisc_direct_ops;
David S. Miller8f40b162011-07-17 13:34:11 -0700317 neigh->output = neigh_direct_output;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700318 } else {
319 if (is_multicast) {
320 neigh->nud_state = NUD_NOARP;
321 ndisc_mc_map(addr, neigh->ha, dev, 1);
322 } else if (dev->flags&(IFF_NOARP|IFF_LOOPBACK)) {
323 neigh->nud_state = NUD_NOARP;
324 memcpy(neigh->ha, dev->dev_addr, dev->addr_len);
325 if (dev->flags&IFF_LOOPBACK)
326 neigh->type = RTN_LOCAL;
327 } else if (dev->flags&IFF_POINTOPOINT) {
328 neigh->nud_state = NUD_NOARP;
329 memcpy(neigh->ha, dev->broadcast, dev->addr_len);
330 }
Stephen Hemminger3b04ddd2007-10-09 01:40:57 -0700331 if (dev->header_ops->cache)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700332 neigh->ops = &ndisc_hh_ops;
333 else
334 neigh->ops = &ndisc_generic_ops;
335 if (neigh->nud_state&NUD_VALID)
336 neigh->output = neigh->ops->connected_output;
337 else
338 neigh->output = neigh->ops->output;
339 }
340 in6_dev_put(in6_dev);
341 return 0;
342}
343
344static int pndisc_constructor(struct pneigh_entry *n)
345{
346 struct in6_addr *addr = (struct in6_addr*)&n->key;
347 struct in6_addr maddr;
348 struct net_device *dev = n->dev;
349
350 if (dev == NULL || __in6_dev_get(dev) == NULL)
351 return -EINVAL;
352 addrconf_addr_solict_mult(addr, &maddr);
353 ipv6_dev_mc_inc(dev, &maddr);
354 return 0;
355}
356
357static void pndisc_destructor(struct pneigh_entry *n)
358{
359 struct in6_addr *addr = (struct in6_addr*)&n->key;
360 struct in6_addr maddr;
361 struct net_device *dev = n->dev;
362
363 if (dev == NULL || __in6_dev_get(dev) == NULL)
364 return;
365 addrconf_addr_solict_mult(addr, &maddr);
366 ipv6_dev_mc_dec(dev, &maddr);
367}
368
YOSHIFUJI Hideaki / 吉藤英明de093342013-01-21 06:48:14 +0000369static struct sk_buff *ndisc_alloc_skb(struct net_device *dev,
370 int len)
371{
372 int hlen = LL_RESERVED_SPACE(dev);
373 int tlen = dev->needed_tailroom;
374 struct sock *sk = dev_net(dev)->ipv6.ndisc_sk;
375 struct sk_buff *skb;
376 int err;
377
378 skb = sock_alloc_send_skb(sk,
379 hlen + sizeof(struct ipv6hdr) + len + tlen,
380 1, &err);
381 if (!skb) {
382 ND_PRINTK(0, err, "ndisc: %s failed to allocate an skb, err=%d\n",
383 __func__, err);
384 return NULL;
385 }
386
387 skb_reserve(skb, hlen);
388
389 return skb;
390}
391
YOSHIFUJI Hideakifd0ea7d2012-12-13 02:40:26 +0900392static struct sk_buff *ndisc_build_skb(struct net_device *dev,
393 const struct in6_addr *daddr,
394 const struct in6_addr *saddr,
395 struct icmp6hdr *icmp6h,
396 const struct in6_addr *target,
397 int llinfo)
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900398{
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900399 struct net *net = dev_net(dev);
Daniel Lezcano1762f7e2008-03-07 11:15:34 -0800400 struct sock *sk = net->ipv6.ndisc_sk;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900401 struct sk_buff *skb;
402 struct icmp6hdr *hdr;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900403 int len;
Brian Haley305d5522008-11-04 17:51:14 -0800404 u8 *opt;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900405
406 if (!dev->addr_len)
407 llinfo = 0;
408
409 len = sizeof(struct icmp6hdr) + (target ? sizeof(*target) : 0);
410 if (llinfo)
411 len += ndisc_opt_addr_space(dev);
412
YOSHIFUJI Hideaki / 吉藤英明de093342013-01-21 06:48:14 +0000413 skb = ndisc_alloc_skb(dev, len);
414 if (!skb)
Brian Haley305d5522008-11-04 17:51:14 -0800415 return NULL;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900416
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900417 ip6_nd_hdr(sk, skb, dev, saddr, daddr, IPPROTO_ICMPV6, len);
418
419 skb->transport_header = skb->tail;
420 skb_put(skb, len);
421
422 hdr = (struct icmp6hdr *)skb_transport_header(skb);
423 memcpy(hdr, icmp6h, sizeof(*hdr));
424
425 opt = skb_transport_header(skb) + sizeof(struct icmp6hdr);
426 if (target) {
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +0000427 *(struct in6_addr *)opt = *target;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900428 opt += sizeof(*target);
429 }
430
431 if (llinfo)
YOSHIFUJI Hideaki / 吉藤英明315ff092013-01-21 06:47:50 +0000432 ndisc_fill_addr_option(opt, llinfo, dev->dev_addr, dev);
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900433
434 hdr->icmp6_cksum = csum_ipv6_magic(saddr, daddr, len,
435 IPPROTO_ICMPV6,
Joe Perches07f07572008-11-19 15:44:53 -0800436 csum_partial(hdr,
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900437 len, 0));
438
Brian Haley305d5522008-11-04 17:51:14 -0800439 return skb;
440}
441
YOSHIFUJI Hideakifd0ea7d2012-12-13 02:40:26 +0900442static void ndisc_send_skb(struct sk_buff *skb, struct net_device *dev,
YOSHIFUJI Hideakifd0ea7d2012-12-13 02:40:26 +0900443 const struct in6_addr *daddr,
444 const struct in6_addr *saddr,
445 struct icmp6hdr *icmp6h)
Brian Haley305d5522008-11-04 17:51:14 -0800446{
David S. Miller4c9483b2011-03-12 16:22:43 -0500447 struct flowi6 fl6;
Brian Haley305d5522008-11-04 17:51:14 -0800448 struct dst_entry *dst;
449 struct net *net = dev_net(dev);
450 struct sock *sk = net->ipv6.ndisc_sk;
451 struct inet6_dev *idev;
452 int err;
453 u8 type;
454
455 type = icmp6h->icmp6_type;
456
David S. Miller4c9483b2011-03-12 16:22:43 -0500457 icmpv6_flow_init(sk, &fl6, type, saddr, daddr, dev->ifindex);
YOSHIFUJI Hideaki / 吉藤英明12fd84f2013-01-18 02:00:24 +0000458 dst = icmp6_dst_alloc(dev, &fl6);
David S. Miller452edd52011-03-02 13:27:41 -0800459 if (IS_ERR(dst)) {
Brian Haley305d5522008-11-04 17:51:14 -0800460 kfree_skb(skb);
461 return;
462 }
463
Eric Dumazetadf30902009-06-02 05:19:30 +0000464 skb_dst_set(skb, dst);
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900465
Eric Dumazetcfdf7642011-07-27 21:13:03 +0000466 rcu_read_lock();
467 idev = __in6_dev_get(dst->dev);
Neil Hormanedf391f2009-04-27 02:45:02 -0700468 IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUT, skb->len);
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900469
Jan Engelhardtb2e0b382010-03-23 04:09:07 +0100470 err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, skb, NULL, dst->dev,
Patrick McHardy6e23ae22007-11-19 18:53:30 -0800471 dst_output);
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900472 if (!err) {
Denis V. Lunev5c5d2442008-10-08 10:33:50 -0700473 ICMP6MSGOUT_INC_STATS(net, idev, type);
Denis V. Luneva862f6a2008-10-08 10:33:06 -0700474 ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS);
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900475 }
476
Eric Dumazetcfdf7642011-07-27 21:13:03 +0000477 rcu_read_unlock();
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900478}
479
Brian Haley305d5522008-11-04 17:51:14 -0800480/*
481 * Send a Neighbour Discover packet
482 */
483static void __ndisc_send(struct net_device *dev,
Brian Haley305d5522008-11-04 17:51:14 -0800484 const struct in6_addr *daddr,
485 const struct in6_addr *saddr,
486 struct icmp6hdr *icmp6h, const struct in6_addr *target,
487 int llinfo)
488{
489 struct sk_buff *skb;
490
491 skb = ndisc_build_skb(dev, daddr, saddr, icmp6h, target, llinfo);
492 if (!skb)
493 return;
494
YOSHIFUJI Hideaki / 吉藤英明12fd84f2013-01-18 02:00:24 +0000495 ndisc_send_skb(skb, dev, daddr, saddr, icmp6h);
Brian Haley305d5522008-11-04 17:51:14 -0800496}
497
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +0900499 const struct in6_addr *daddr,
500 const struct in6_addr *solicited_addr,
YOSHIFUJI Hideaki / 吉藤英明fb568632013-01-20 07:39:18 +0000501 bool router, bool solicited, bool override, bool inc_opt)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502{
503 struct in6_addr tmpaddr;
504 struct inet6_ifaddr *ifp;
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +0900505 const struct in6_addr *src_addr;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900506 struct icmp6hdr icmp6h = {
507 .icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT,
508 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700509
510 /* for anycast or proxy, solicited_addr != src_addr */
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900511 ifp = ipv6_get_ifaddr(dev_net(dev), solicited_addr, dev, 1);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900512 if (ifp) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700513 src_addr = solicited_addr;
Neil Horman95c385b2007-04-25 17:08:10 -0700514 if (ifp->flags & IFA_F_OPTIMISTIC)
515 override = 0;
stephen hemminger9f888162010-06-21 11:00:13 +0000516 inc_opt |= ifp->idev->cnf.force_tllao;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700517 in6_ifa_put(ifp);
518 } else {
Brian Haley191cd582008-08-14 15:33:21 -0700519 if (ipv6_dev_get_saddr(dev_net(dev), dev, daddr,
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900520 inet6_sk(dev_net(dev)->ipv6.ndisc_sk)->srcprefs,
YOSHIFUJI Hideaki7cbca672008-03-25 09:37:42 +0900521 &tmpaddr))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700522 return;
523 src_addr = &tmpaddr;
524 }
525
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900526 icmp6h.icmp6_router = router;
527 icmp6h.icmp6_solicited = solicited;
528 icmp6h.icmp6_override = override;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700529
YOSHIFUJI Hideaki / 吉藤英明12fd84f2013-01-18 02:00:24 +0000530 __ndisc_send(dev, daddr, src_addr, &icmp6h, solicited_addr,
David L Stevens14878f72007-09-16 16:52:35 -0700531 inc_opt ? ND_OPT_TARGET_LL_ADDR : 0);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900532}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700533
Ben Hutchingsf47b9462011-04-15 13:46:02 +0000534static void ndisc_send_unsol_na(struct net_device *dev)
535{
536 struct inet6_dev *idev;
537 struct inet6_ifaddr *ifa;
Ben Hutchingsf47b9462011-04-15 13:46:02 +0000538
539 idev = in6_dev_get(dev);
540 if (!idev)
541 return;
542
543 read_lock_bh(&idev->lock);
544 list_for_each_entry(ifa, &idev->addr_list, if_list) {
YOSHIFUJI Hideaki / 吉藤英明9fafd652012-11-12 07:50:17 +0000545 ndisc_send_na(dev, NULL, &in6addr_linklocal_allnodes, &ifa->addr,
Ben Hutchingsf47b9462011-04-15 13:46:02 +0000546 /*router=*/ !!idev->cnf.forwarding,
547 /*solicited=*/ false, /*override=*/ true,
548 /*inc_opt=*/ true);
549 }
550 read_unlock_bh(&idev->lock);
551
552 in6_dev_put(idev);
553}
554
Linus Torvalds1da177e2005-04-16 15:20:36 -0700555void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +0900556 const struct in6_addr *solicit,
557 const struct in6_addr *daddr, const struct in6_addr *saddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700558{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700559 struct in6_addr addr_buf;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900560 struct icmp6hdr icmp6h = {
561 .icmp6_type = NDISC_NEIGHBOUR_SOLICITATION,
562 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700563
564 if (saddr == NULL) {
Neil Horman95c385b2007-04-25 17:08:10 -0700565 if (ipv6_get_lladdr(dev, &addr_buf,
566 (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700567 return;
568 saddr = &addr_buf;
569 }
570
YOSHIFUJI Hideaki / 吉藤英明12fd84f2013-01-18 02:00:24 +0000571 __ndisc_send(dev, daddr, saddr, &icmp6h, solicit,
David L Stevens14878f72007-09-16 16:52:35 -0700572 !ipv6_addr_any(saddr) ? ND_OPT_SOURCE_LL_ADDR : 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700573}
574
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +0900575void ndisc_send_rs(struct net_device *dev, const struct in6_addr *saddr,
576 const struct in6_addr *daddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700577{
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900578 struct icmp6hdr icmp6h = {
579 .icmp6_type = NDISC_ROUTER_SOLICITATION,
580 };
Neil Horman95c385b2007-04-25 17:08:10 -0700581 int send_sllao = dev->addr_len;
Neil Horman95c385b2007-04-25 17:08:10 -0700582
583#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
584 /*
585 * According to section 2.2 of RFC 4429, we must not
586 * send router solicitations with a sllao from
587 * optimistic addresses, but we may send the solicitation
588 * if we don't include the sllao. So here we check
589 * if our address is optimistic, and if so, we
Joe Perchesbea85192007-12-20 14:01:35 -0800590 * suppress the inclusion of the sllao.
Neil Horman95c385b2007-04-25 17:08:10 -0700591 */
592 if (send_sllao) {
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900593 struct inet6_ifaddr *ifp = ipv6_get_ifaddr(dev_net(dev), saddr,
Daniel Lezcano1cab3da2008-01-10 22:44:09 -0800594 dev, 1);
Neil Horman95c385b2007-04-25 17:08:10 -0700595 if (ifp) {
596 if (ifp->flags & IFA_F_OPTIMISTIC) {
YOSHIFUJI Hideakica043562007-02-28 23:13:20 +0900597 send_sllao = 0;
Neil Horman95c385b2007-04-25 17:08:10 -0700598 }
YOSHIFUJI Hideakica043562007-02-28 23:13:20 +0900599 in6_ifa_put(ifp);
Neil Horman95c385b2007-04-25 17:08:10 -0700600 } else {
601 send_sllao = 0;
602 }
603 }
604#endif
YOSHIFUJI Hideaki / 吉藤英明12fd84f2013-01-18 02:00:24 +0000605 __ndisc_send(dev, daddr, saddr, &icmp6h, NULL,
David L Stevens14878f72007-09-16 16:52:35 -0700606 send_sllao ? ND_OPT_SOURCE_LL_ADDR : 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700607}
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900608
Linus Torvalds1da177e2005-04-16 15:20:36 -0700609
610static void ndisc_error_report(struct neighbour *neigh, struct sk_buff *skb)
611{
612 /*
613 * "The sender MUST return an ICMP
614 * destination unreachable"
615 */
616 dst_link_failure(skb);
617 kfree_skb(skb);
618}
619
620/* Called with locked neigh: either read or both */
621
622static void ndisc_solicit(struct neighbour *neigh, struct sk_buff *skb)
623{
624 struct in6_addr *saddr = NULL;
625 struct in6_addr mcaddr;
626 struct net_device *dev = neigh->dev;
627 struct in6_addr *target = (struct in6_addr *)&neigh->primary_key;
628 int probes = atomic_read(&neigh->probes);
629
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900630 if (skb && ipv6_chk_addr(dev_net(dev), &ipv6_hdr(skb)->saddr, dev, 1))
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -0700631 saddr = &ipv6_hdr(skb)->saddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700632
633 if ((probes -= neigh->parms->ucast_probes) < 0) {
634 if (!(neigh->nud_state & NUD_VALID)) {
Joe Perches675418d2012-05-16 19:28:38 +0000635 ND_PRINTK(1, dbg,
636 "%s: trying to ucast probe in NUD_INVALID: %pI6\n",
637 __func__, target);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700638 }
639 ndisc_send_ns(dev, neigh, target, target, saddr);
640 } else if ((probes -= neigh->parms->app_probes) < 0) {
641#ifdef CONFIG_ARPD
642 neigh_app_ns(neigh);
643#endif
644 } else {
645 addrconf_addr_solict_mult(target, &mcaddr);
646 ndisc_send_ns(dev, NULL, target, &mcaddr, saddr);
647 }
648}
649
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900650static int pndisc_is_router(const void *pkey,
651 struct net_device *dev)
Pavel Emelyanovfa86d322008-03-24 14:48:59 -0700652{
653 struct pneigh_entry *n;
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900654 int ret = -1;
Pavel Emelyanovfa86d322008-03-24 14:48:59 -0700655
656 read_lock_bh(&nd_tbl.lock);
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900657 n = __pneigh_lookup(&nd_tbl, dev_net(dev), pkey, dev);
658 if (n)
659 ret = !!(n->flags & NTF_ROUTER);
Pavel Emelyanovfa86d322008-03-24 14:48:59 -0700660 read_unlock_bh(&nd_tbl.lock);
661
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900662 return ret;
Pavel Emelyanovfa86d322008-03-24 14:48:59 -0700663}
664
Linus Torvalds1da177e2005-04-16 15:20:36 -0700665static void ndisc_recv_ns(struct sk_buff *skb)
666{
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -0700667 struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb);
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000668 const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
669 const struct in6_addr *daddr = &ipv6_hdr(skb)->daddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700670 u8 *lladdr = NULL;
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -0700671 u32 ndoptlen = skb->tail - (skb->transport_header +
672 offsetof(struct nd_msg, opt));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700673 struct ndisc_options ndopts;
674 struct net_device *dev = skb->dev;
675 struct inet6_ifaddr *ifp;
676 struct inet6_dev *idev = NULL;
677 struct neighbour *neigh;
678 int dad = ipv6_addr_any(saddr);
Eric Dumazeta50feda2012-05-18 18:57:34 +0000679 bool inc;
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900680 int is_router = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700681
YOSHIFUJI Hideaki / 吉藤英明115b0aa2013-01-18 02:05:03 +0000682 if (skb->len < sizeof(struct nd_msg)) {
683 ND_PRINTK(2, warn, "NS: packet too short\n");
684 return;
685 }
686
Linus Torvalds1da177e2005-04-16 15:20:36 -0700687 if (ipv6_addr_is_multicast(&msg->target)) {
Joe Perches675418d2012-05-16 19:28:38 +0000688 ND_PRINTK(2, warn, "NS: multicast target address\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700689 return;
690 }
691
692 /*
693 * RFC2461 7.1.1:
694 * DAD has to be destined for solicited node multicast address.
695 */
YOSHIFUJI Hideaki / 吉藤英明ca97a642013-01-20 07:39:00 +0000696 if (dad && !ipv6_addr_is_solict_mult(daddr)) {
Joe Perches675418d2012-05-16 19:28:38 +0000697 ND_PRINTK(2, warn, "NS: bad DAD packet (wrong destination)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700698 return;
699 }
700
701 if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts)) {
Joe Perches675418d2012-05-16 19:28:38 +0000702 ND_PRINTK(2, warn, "NS: invalid ND options\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700703 return;
704 }
705
706 if (ndopts.nd_opts_src_lladdr) {
707 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr, dev);
708 if (!lladdr) {
Joe Perches675418d2012-05-16 19:28:38 +0000709 ND_PRINTK(2, warn,
710 "NS: invalid link-layer address length\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700711 return;
712 }
713
714 /* RFC2461 7.1.1:
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900715 * If the IP source address is the unspecified address,
716 * there MUST NOT be source link-layer address option
Linus Torvalds1da177e2005-04-16 15:20:36 -0700717 * in the message.
718 */
719 if (dad) {
Joe Perches675418d2012-05-16 19:28:38 +0000720 ND_PRINTK(2, warn,
721 "NS: bad DAD packet (link-layer address option)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700722 return;
723 }
724 }
725
726 inc = ipv6_addr_is_multicast(daddr);
727
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900728 ifp = ipv6_get_ifaddr(dev_net(dev), &msg->target, dev, 1);
Daniel Lezcanoa18bc692008-03-07 11:14:49 -0800729 if (ifp) {
Neil Horman95c385b2007-04-25 17:08:10 -0700730
731 if (ifp->flags & (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)) {
732 if (dad) {
Neil Horman95c385b2007-04-25 17:08:10 -0700733 /*
734 * We are colliding with another node
735 * who is doing DAD
736 * so fail our DAD process
737 */
738 addrconf_dad_failure(ifp);
Denis V. Lunev9e3be4b2007-09-11 11:04:49 +0200739 return;
Neil Horman95c385b2007-04-25 17:08:10 -0700740 } else {
741 /*
742 * This is not a dad solicitation.
743 * If we are an optimistic node,
744 * we should respond.
745 * Otherwise, we should ignore it.
746 */
747 if (!(ifp->flags & IFA_F_OPTIMISTIC))
748 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700749 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700750 }
751
752 idev = ifp->idev;
753 } else {
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700754 struct net *net = dev_net(dev);
755
Linus Torvalds1da177e2005-04-16 15:20:36 -0700756 idev = in6_dev_get(dev);
757 if (!idev) {
758 /* XXX: count this drop? */
759 return;
760 }
761
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700762 if (ipv6_chk_acast_addr(net, dev, &msg->target) ||
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900763 (idev->cnf.forwarding &&
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700764 (net->ipv6.devconf_all->proxy_ndp || idev->cnf.proxy_ndp) &&
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900765 (is_router = pndisc_is_router(&msg->target, dev)) >= 0)) {
Patrick McHardya61bbcf2005-08-14 17:24:31 -0700766 if (!(NEIGH_CB(skb)->flags & LOCALLY_ENQUEUED) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700767 skb->pkt_type != PACKET_HOST &&
768 inc != 0 &&
769 idev->nd_parms->proxy_delay != 0) {
770 /*
771 * for anycast or proxy,
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900772 * sender should delay its response
773 * by a random time between 0 and
Linus Torvalds1da177e2005-04-16 15:20:36 -0700774 * MAX_ANYCAST_DELAY_TIME seconds.
775 * (RFC2461) -- yoshfuji
776 */
777 struct sk_buff *n = skb_clone(skb, GFP_ATOMIC);
778 if (n)
779 pneigh_enqueue(&nd_tbl, idev->nd_parms, n);
780 goto out;
781 }
782 } else
783 goto out;
784 }
785
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900786 if (is_router < 0)
YOSHIFUJI Hideaki / 吉藤英明fb568632013-01-20 07:39:18 +0000787 is_router = idev->cnf.forwarding;
Ville Nuorvala62dd9312006-09-22 14:43:19 -0700788
Linus Torvalds1da177e2005-04-16 15:20:36 -0700789 if (dad) {
YOSHIFUJI Hideakif3ee4012008-04-10 15:42:11 +0900790 ndisc_send_na(dev, NULL, &in6addr_linklocal_allnodes, &msg->target,
YOSHIFUJI Hideaki / 吉藤英明fb568632013-01-20 07:39:18 +0000791 !!is_router, false, (ifp != NULL), true);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700792 goto out;
793 }
794
795 if (inc)
796 NEIGH_CACHE_STAT_INC(&nd_tbl, rcv_probes_mcast);
797 else
798 NEIGH_CACHE_STAT_INC(&nd_tbl, rcv_probes_ucast);
799
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900800 /*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700801 * update / create cache entry
802 * for the source address
803 */
804 neigh = __neigh_lookup(&nd_tbl, saddr, dev,
805 !inc || lladdr || !dev->addr_len);
806 if (neigh)
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900807 neigh_update(neigh, lladdr, NUD_STALE,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700808 NEIGH_UPDATE_F_WEAK_OVERRIDE|
809 NEIGH_UPDATE_F_OVERRIDE);
Stephen Hemminger3b04ddd2007-10-09 01:40:57 -0700810 if (neigh || !dev->header_ops) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700811 ndisc_send_na(dev, neigh, saddr, &msg->target,
YOSHIFUJI Hideaki / 吉藤英明fb568632013-01-20 07:39:18 +0000812 !!is_router,
813 true, (ifp != NULL && inc), inc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700814 if (neigh)
815 neigh_release(neigh);
816 }
817
818out:
819 if (ifp)
820 in6_ifa_put(ifp);
821 else
822 in6_dev_put(idev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700823}
824
825static void ndisc_recv_na(struct sk_buff *skb)
826{
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -0700827 struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb);
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000828 const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
829 const struct in6_addr *daddr = &ipv6_hdr(skb)->daddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700830 u8 *lladdr = NULL;
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -0700831 u32 ndoptlen = skb->tail - (skb->transport_header +
832 offsetof(struct nd_msg, opt));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700833 struct ndisc_options ndopts;
834 struct net_device *dev = skb->dev;
835 struct inet6_ifaddr *ifp;
836 struct neighbour *neigh;
837
838 if (skb->len < sizeof(struct nd_msg)) {
Joe Perches675418d2012-05-16 19:28:38 +0000839 ND_PRINTK(2, warn, "NA: packet too short\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700840 return;
841 }
842
843 if (ipv6_addr_is_multicast(&msg->target)) {
Joe Perches675418d2012-05-16 19:28:38 +0000844 ND_PRINTK(2, warn, "NA: target address is multicast\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700845 return;
846 }
847
848 if (ipv6_addr_is_multicast(daddr) &&
849 msg->icmph.icmp6_solicited) {
Joe Perches675418d2012-05-16 19:28:38 +0000850 ND_PRINTK(2, warn, "NA: solicited NA is multicasted\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700851 return;
852 }
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900853
Linus Torvalds1da177e2005-04-16 15:20:36 -0700854 if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts)) {
Joe Perches675418d2012-05-16 19:28:38 +0000855 ND_PRINTK(2, warn, "NS: invalid ND option\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700856 return;
857 }
858 if (ndopts.nd_opts_tgt_lladdr) {
859 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr, dev);
860 if (!lladdr) {
Joe Perches675418d2012-05-16 19:28:38 +0000861 ND_PRINTK(2, warn,
862 "NA: invalid link-layer address length\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700863 return;
864 }
865 }
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900866 ifp = ipv6_get_ifaddr(dev_net(dev), &msg->target, dev, 1);
Daniel Lezcanoa18bc692008-03-07 11:14:49 -0800867 if (ifp) {
Daniel Walterbd015922011-04-13 21:09:25 +0000868 if (skb->pkt_type != PACKET_LOOPBACK
869 && (ifp->flags & IFA_F_TENTATIVE)) {
870 addrconf_dad_failure(ifp);
871 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700872 }
873 /* What should we make now? The advertisement
874 is invalid, but ndisc specs say nothing
875 about it. It could be misconfiguration, or
876 an smart proxy agent tries to help us :-)
Jan Sembera24fc7b82008-12-09 15:48:32 -0800877
878 We should not print the error if NA has been
879 received from loopback - it is just our own
880 unsolicited advertisement.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700881 */
Jan Sembera24fc7b82008-12-09 15:48:32 -0800882 if (skb->pkt_type != PACKET_LOOPBACK)
Joe Perches675418d2012-05-16 19:28:38 +0000883 ND_PRINTK(1, warn,
884 "NA: someone advertises our address %pI6 on %s!\n",
885 &ifp->addr, ifp->idev->dev->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700886 in6_ifa_put(ifp);
887 return;
888 }
889 neigh = neigh_lookup(&nd_tbl, &msg->target, dev);
890
891 if (neigh) {
892 u8 old_flags = neigh->flags;
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700893 struct net *net = dev_net(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700894
895 if (neigh->nud_state & NUD_FAILED)
896 goto out;
897
Ville Nuorvala5f3e6e92006-09-22 14:42:46 -0700898 /*
899 * Don't update the neighbor cache entry on a proxy NA from
900 * ourselves because either the proxied node is off link or it
901 * has already sent a NA to us.
902 */
903 if (lladdr && !memcmp(lladdr, dev->dev_addr, dev->addr_len) &&
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700904 net->ipv6.devconf_all->forwarding && net->ipv6.devconf_all->proxy_ndp &&
905 pneigh_lookup(&nd_tbl, net, &msg->target, dev, 0)) {
Nicolas Dichtelb20b6d92012-11-07 05:05:38 +0000906 /* XXX: idev->cnf.proxy_ndp */
Ville Nuorvala5f3e6e92006-09-22 14:42:46 -0700907 goto out;
YOSHIFUJI Hideakifbea49e2006-09-22 14:43:49 -0700908 }
Ville Nuorvala5f3e6e92006-09-22 14:42:46 -0700909
Linus Torvalds1da177e2005-04-16 15:20:36 -0700910 neigh_update(neigh, lladdr,
911 msg->icmph.icmp6_solicited ? NUD_REACHABLE : NUD_STALE,
912 NEIGH_UPDATE_F_WEAK_OVERRIDE|
913 (msg->icmph.icmp6_override ? NEIGH_UPDATE_F_OVERRIDE : 0)|
914 NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
915 (msg->icmph.icmp6_router ? NEIGH_UPDATE_F_ISROUTER : 0));
916
917 if ((old_flags & ~neigh->flags) & NTF_ROUTER) {
918 /*
919 * Change: router to host
920 */
921 struct rt6_info *rt;
922 rt = rt6_get_dflt_router(saddr, dev);
923 if (rt)
Thomas Grafe0a1ad732006-08-22 00:00:21 -0700924 ip6_del_rt(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700925 }
926
927out:
928 neigh_release(neigh);
929 }
930}
931
932static void ndisc_recv_rs(struct sk_buff *skb)
933{
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -0700934 struct rs_msg *rs_msg = (struct rs_msg *)skb_transport_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700935 unsigned long ndoptlen = skb->len - sizeof(*rs_msg);
936 struct neighbour *neigh;
937 struct inet6_dev *idev;
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000938 const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700939 struct ndisc_options ndopts;
940 u8 *lladdr = NULL;
941
942 if (skb->len < sizeof(*rs_msg))
943 return;
944
Eric Dumazetcfdf7642011-07-27 21:13:03 +0000945 idev = __in6_dev_get(skb->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700946 if (!idev) {
Joe Perches675418d2012-05-16 19:28:38 +0000947 ND_PRINTK(1, err, "RS: can't find in6 device\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700948 return;
949 }
950
951 /* Don't accept RS if we're not in router mode */
952 if (!idev->cnf.forwarding)
953 goto out;
954
955 /*
956 * Don't update NCE if src = ::;
957 * this implies that the source node has no ip address assigned yet.
958 */
959 if (ipv6_addr_any(saddr))
960 goto out;
961
962 /* Parse ND options */
963 if (!ndisc_parse_options(rs_msg->opt, ndoptlen, &ndopts)) {
Joe Perches675418d2012-05-16 19:28:38 +0000964 ND_PRINTK(2, notice, "NS: invalid ND option, ignored\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700965 goto out;
966 }
967
968 if (ndopts.nd_opts_src_lladdr) {
969 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr,
970 skb->dev);
971 if (!lladdr)
972 goto out;
973 }
974
975 neigh = __neigh_lookup(&nd_tbl, saddr, skb->dev, 1);
976 if (neigh) {
977 neigh_update(neigh, lladdr, NUD_STALE,
978 NEIGH_UPDATE_F_WEAK_OVERRIDE|
979 NEIGH_UPDATE_F_OVERRIDE|
980 NEIGH_UPDATE_F_OVERRIDE_ISROUTER);
981 neigh_release(neigh);
982 }
983out:
Eric Dumazetcfdf7642011-07-27 21:13:03 +0000984 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700985}
986
Pierre Ynard31910572007-10-10 21:22:05 -0700987static void ndisc_ra_useropt(struct sk_buff *ra, struct nd_opt_hdr *opt)
988{
989 struct icmp6hdr *icmp6h = (struct icmp6hdr *)skb_transport_header(ra);
990 struct sk_buff *skb;
991 struct nlmsghdr *nlh;
992 struct nduseroptmsg *ndmsg;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900993 struct net *net = dev_net(ra->dev);
Pierre Ynard31910572007-10-10 21:22:05 -0700994 int err;
995 int base_size = NLMSG_ALIGN(sizeof(struct nduseroptmsg)
996 + (opt->nd_opt_len << 3));
997 size_t msg_size = base_size + nla_total_size(sizeof(struct in6_addr));
998
999 skb = nlmsg_new(msg_size, GFP_ATOMIC);
1000 if (skb == NULL) {
1001 err = -ENOBUFS;
1002 goto errout;
1003 }
1004
1005 nlh = nlmsg_put(skb, 0, 0, RTM_NEWNDUSEROPT, base_size, 0);
1006 if (nlh == NULL) {
1007 goto nla_put_failure;
1008 }
1009
1010 ndmsg = nlmsg_data(nlh);
1011 ndmsg->nduseropt_family = AF_INET6;
Pierre Ynarddbb2ed22007-11-12 17:58:35 -08001012 ndmsg->nduseropt_ifindex = ra->dev->ifindex;
Pierre Ynard31910572007-10-10 21:22:05 -07001013 ndmsg->nduseropt_icmp_type = icmp6h->icmp6_type;
1014 ndmsg->nduseropt_icmp_code = icmp6h->icmp6_code;
1015 ndmsg->nduseropt_opts_len = opt->nd_opt_len << 3;
1016
1017 memcpy(ndmsg + 1, opt, opt->nd_opt_len << 3);
1018
David S. Millerc78679e2012-04-01 20:27:33 -04001019 if (nla_put(skb, NDUSEROPT_SRCADDR, sizeof(struct in6_addr),
1020 &ipv6_hdr(ra)->saddr))
1021 goto nla_put_failure;
Pierre Ynard31910572007-10-10 21:22:05 -07001022 nlmsg_end(skb, nlh);
1023
Pablo Neira Ayuso1ce85fe2009-02-24 23:18:28 -08001024 rtnl_notify(skb, net, 0, RTNLGRP_ND_USEROPT, NULL, GFP_ATOMIC);
Pierre Ynard31910572007-10-10 21:22:05 -07001025 return;
1026
1027nla_put_failure:
1028 nlmsg_free(skb);
1029 err = -EMSGSIZE;
1030errout:
Daniel Lezcanoa18bc692008-03-07 11:14:49 -08001031 rtnl_set_sk_err(net, RTNLGRP_ND_USEROPT, err);
Pierre Ynard31910572007-10-10 21:22:05 -07001032}
1033
Linus Torvalds1da177e2005-04-16 15:20:36 -07001034static void ndisc_router_discovery(struct sk_buff *skb)
1035{
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -07001036 struct ra_msg *ra_msg = (struct ra_msg *)skb_transport_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001037 struct neighbour *neigh = NULL;
1038 struct inet6_dev *in6_dev;
YOSHIFUJI Hideaki65f5c7c2006-03-20 16:55:08 -08001039 struct rt6_info *rt = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001040 int lifetime;
1041 struct ndisc_options ndopts;
1042 int optlen;
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001043 unsigned int pref = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001044
1045 __u8 * opt = (__u8 *)(ra_msg + 1);
1046
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -07001047 optlen = (skb->tail - skb->transport_header) - sizeof(struct ra_msg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001048
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001049 if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) {
Joe Perches675418d2012-05-16 19:28:38 +00001050 ND_PRINTK(2, warn, "RA: source address is not link-local\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001051 return;
1052 }
1053 if (optlen < 0) {
Joe Perches675418d2012-05-16 19:28:38 +00001054 ND_PRINTK(2, warn, "RA: packet too short\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001055 return;
1056 }
1057
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001058#ifdef CONFIG_IPV6_NDISC_NODETYPE
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001059 if (skb->ndisc_nodetype == NDISC_NODETYPE_HOST) {
Joe Perches675418d2012-05-16 19:28:38 +00001060 ND_PRINTK(2, warn, "RA: from host or unauthorized router\n");
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001061 return;
1062 }
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001063#endif
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001064
Linus Torvalds1da177e2005-04-16 15:20:36 -07001065 /*
1066 * set the RA_RECV flag in the interface
1067 */
1068
Eric Dumazetcfdf7642011-07-27 21:13:03 +00001069 in6_dev = __in6_dev_get(skb->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001070 if (in6_dev == NULL) {
Joe Perches675418d2012-05-16 19:28:38 +00001071 ND_PRINTK(0, err, "RA: can't find inet6 device for %s\n",
1072 skb->dev->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001073 return;
1074 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001075
1076 if (!ndisc_parse_options(opt, optlen, &ndopts)) {
Joe Perches675418d2012-05-16 19:28:38 +00001077 ND_PRINTK(2, warn, "RA: invalid ND options\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001078 return;
1079 }
1080
Shmulik Ladkaniaeaf6e92012-11-30 10:25:59 +00001081 if (!ipv6_accept_ra(in6_dev))
David Ward31ce8c72009-08-29 00:04:09 -07001082 goto skip_linkparms;
1083
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001084#ifdef CONFIG_IPV6_NDISC_NODETYPE
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001085 /* skip link-specific parameters from interior routers */
1086 if (skb->ndisc_nodetype == NDISC_NODETYPE_NODEFAULT)
1087 goto skip_linkparms;
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001088#endif
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001089
Linus Torvalds1da177e2005-04-16 15:20:36 -07001090 if (in6_dev->if_flags & IF_RS_SENT) {
1091 /*
1092 * flag that an RA was received after an RS was sent
1093 * out on this interface.
1094 */
1095 in6_dev->if_flags |= IF_RA_RCVD;
1096 }
1097
1098 /*
1099 * Remember the managed/otherconf flags from most recently
1100 * received RA message (RFC 2462) -- yoshfuji
1101 */
1102 in6_dev->if_flags = (in6_dev->if_flags & ~(IF_RA_MANAGED |
1103 IF_RA_OTHERCONF)) |
1104 (ra_msg->icmph.icmp6_addrconf_managed ?
1105 IF_RA_MANAGED : 0) |
1106 (ra_msg->icmph.icmp6_addrconf_other ?
1107 IF_RA_OTHERCONF : 0);
1108
YOSHIFUJI Hideaki65f5c7c2006-03-20 16:55:08 -08001109 if (!in6_dev->cnf.accept_ra_defrtr)
1110 goto skip_defrtr;
1111
Andreas Hofmeister9f562202011-10-24 19:13:15 -04001112 if (ipv6_chk_addr(dev_net(in6_dev->dev), &ipv6_hdr(skb)->saddr, NULL, 0))
1113 goto skip_defrtr;
1114
Linus Torvalds1da177e2005-04-16 15:20:36 -07001115 lifetime = ntohs(ra_msg->icmph.icmp6_rt_lifetime);
1116
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001117#ifdef CONFIG_IPV6_ROUTER_PREF
1118 pref = ra_msg->icmph.icmp6_router_pref;
1119 /* 10b is handled as if it were 00b (medium) */
YOSHIFUJI Hideaki930d6ff2006-03-20 17:05:30 -08001120 if (pref == ICMPV6_ROUTER_PREF_INVALID ||
YOSHIFUJI Hideaki6d5b78c2007-06-22 16:07:04 -07001121 !in6_dev->cnf.accept_ra_rtr_pref)
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001122 pref = ICMPV6_ROUTER_PREF_MEDIUM;
1123#endif
1124
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001125 rt = rt6_get_dflt_router(&ipv6_hdr(skb)->saddr, skb->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001126
David S. Millereb857182012-01-27 15:07:56 -08001127 if (rt) {
1128 neigh = dst_neigh_lookup(&rt->dst, &ipv6_hdr(skb)->saddr);
1129 if (!neigh) {
Joe Perches675418d2012-05-16 19:28:38 +00001130 ND_PRINTK(0, err,
1131 "RA: %s got default router without neighbour\n",
1132 __func__);
Amerigo Wang94e187c2012-10-29 00:13:19 +00001133 ip6_rt_put(rt);
David S. Millereb857182012-01-27 15:07:56 -08001134 return;
1135 }
1136 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001137 if (rt && lifetime == 0) {
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001138 ip6_del_rt(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001139 rt = NULL;
1140 }
1141
1142 if (rt == NULL && lifetime) {
Joe Perches675418d2012-05-16 19:28:38 +00001143 ND_PRINTK(3, dbg, "RA: adding default router\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001144
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001145 rt = rt6_add_dflt_router(&ipv6_hdr(skb)->saddr, skb->dev, pref);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001146 if (rt == NULL) {
Joe Perches675418d2012-05-16 19:28:38 +00001147 ND_PRINTK(0, err,
1148 "RA: %s failed to add default route\n",
1149 __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001150 return;
1151 }
1152
David S. Millereb857182012-01-27 15:07:56 -08001153 neigh = dst_neigh_lookup(&rt->dst, &ipv6_hdr(skb)->saddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001154 if (neigh == NULL) {
Joe Perches675418d2012-05-16 19:28:38 +00001155 ND_PRINTK(0, err,
1156 "RA: %s got default router without neighbour\n",
1157 __func__);
Amerigo Wang94e187c2012-10-29 00:13:19 +00001158 ip6_rt_put(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001159 return;
1160 }
1161 neigh->flags |= NTF_ROUTER;
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001162 } else if (rt) {
Pedro Ribeiro22441cf2008-10-15 15:47:49 -07001163 rt->rt6i_flags = (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001164 }
1165
1166 if (rt)
Gao feng1716a962012-04-06 00:13:10 +00001167 rt6_set_expires(rt, jiffies + (HZ * lifetime));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001168 if (ra_msg->icmph.icmp6_hop_limit) {
1169 in6_dev->cnf.hop_limit = ra_msg->icmph.icmp6_hop_limit;
1170 if (rt)
David S. Millerdefb3512010-12-08 21:16:57 -08001171 dst_metric_set(&rt->dst, RTAX_HOPLIMIT,
1172 ra_msg->icmph.icmp6_hop_limit);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001173 }
1174
YOSHIFUJI Hideaki65f5c7c2006-03-20 16:55:08 -08001175skip_defrtr:
1176
Linus Torvalds1da177e2005-04-16 15:20:36 -07001177 /*
1178 * Update Reachable Time and Retrans Timer
1179 */
1180
1181 if (in6_dev->nd_parms) {
1182 unsigned long rtime = ntohl(ra_msg->retrans_timer);
1183
1184 if (rtime && rtime/1000 < MAX_SCHEDULE_TIMEOUT/HZ) {
1185 rtime = (rtime*HZ)/1000;
1186 if (rtime < HZ/10)
1187 rtime = HZ/10;
1188 in6_dev->nd_parms->retrans_time = rtime;
1189 in6_dev->tstamp = jiffies;
1190 inet6_ifinfo_notify(RTM_NEWLINK, in6_dev);
1191 }
1192
1193 rtime = ntohl(ra_msg->reachable_time);
1194 if (rtime && rtime/1000 < MAX_SCHEDULE_TIMEOUT/(3*HZ)) {
1195 rtime = (rtime*HZ)/1000;
1196
1197 if (rtime < HZ/10)
1198 rtime = HZ/10;
1199
1200 if (rtime != in6_dev->nd_parms->base_reachable_time) {
1201 in6_dev->nd_parms->base_reachable_time = rtime;
1202 in6_dev->nd_parms->gc_staletime = 3 * rtime;
1203 in6_dev->nd_parms->reachable_time = neigh_rand_reach_time(rtime);
1204 in6_dev->tstamp = jiffies;
1205 inet6_ifinfo_notify(RTM_NEWLINK, in6_dev);
1206 }
1207 }
1208 }
1209
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001210skip_linkparms:
1211
Linus Torvalds1da177e2005-04-16 15:20:36 -07001212 /*
1213 * Process options.
1214 */
1215
1216 if (!neigh)
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001217 neigh = __neigh_lookup(&nd_tbl, &ipv6_hdr(skb)->saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001218 skb->dev, 1);
1219 if (neigh) {
1220 u8 *lladdr = NULL;
1221 if (ndopts.nd_opts_src_lladdr) {
1222 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr,
1223 skb->dev);
1224 if (!lladdr) {
Joe Perches675418d2012-05-16 19:28:38 +00001225 ND_PRINTK(2, warn,
1226 "RA: invalid link-layer address length\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001227 goto out;
1228 }
1229 }
1230 neigh_update(neigh, lladdr, NUD_STALE,
1231 NEIGH_UPDATE_F_WEAK_OVERRIDE|
1232 NEIGH_UPDATE_F_OVERRIDE|
1233 NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
1234 NEIGH_UPDATE_F_ISROUTER);
1235 }
1236
Shmulik Ladkaniaeaf6e92012-11-30 10:25:59 +00001237 if (!ipv6_accept_ra(in6_dev))
David Ward31ce8c72009-08-29 00:04:09 -07001238 goto out;
1239
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001240#ifdef CONFIG_IPV6_ROUTE_INFO
Andreas Hofmeister9f562202011-10-24 19:13:15 -04001241 if (ipv6_chk_addr(dev_net(in6_dev->dev), &ipv6_hdr(skb)->saddr, NULL, 0))
1242 goto skip_routeinfo;
1243
YOSHIFUJI Hideaki09c884d2006-03-20 17:07:03 -08001244 if (in6_dev->cnf.accept_ra_rtr_pref && ndopts.nd_opts_ri) {
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001245 struct nd_opt_hdr *p;
1246 for (p = ndopts.nd_opts_ri;
1247 p;
1248 p = ndisc_next_option(p, ndopts.nd_opts_ri_end)) {
YOSHIFUJI Hideaki6294e002008-03-15 23:56:52 -04001249 struct route_info *ri = (struct route_info *)p;
1250#ifdef CONFIG_IPV6_NDISC_NODETYPE
1251 if (skb->ndisc_nodetype == NDISC_NODETYPE_NODEFAULT &&
1252 ri->prefix_len == 0)
1253 continue;
1254#endif
1255 if (ri->prefix_len > in6_dev->cnf.accept_ra_rt_info_max_plen)
YOSHIFUJI Hideaki09c884d2006-03-20 17:07:03 -08001256 continue;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001257 rt6_route_rcv(skb->dev, (u8*)p, (p->nd_opt_len) << 3,
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001258 &ipv6_hdr(skb)->saddr);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001259 }
1260 }
Andreas Hofmeister9f562202011-10-24 19:13:15 -04001261
1262skip_routeinfo:
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001263#endif
1264
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001265#ifdef CONFIG_IPV6_NDISC_NODETYPE
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001266 /* skip link-specific ndopts from interior routers */
1267 if (skb->ndisc_nodetype == NDISC_NODETYPE_NODEFAULT)
1268 goto out;
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001269#endif
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001270
YOSHIFUJI Hideakic4fd30e2006-03-20 16:55:26 -08001271 if (in6_dev->cnf.accept_ra_pinfo && ndopts.nd_opts_pi) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001272 struct nd_opt_hdr *p;
1273 for (p = ndopts.nd_opts_pi;
1274 p;
1275 p = ndisc_next_option(p, ndopts.nd_opts_pi_end)) {
Neil Hormane6bff992012-01-04 10:49:15 +00001276 addrconf_prefix_rcv(skb->dev, (u8 *)p,
1277 (p->nd_opt_len) << 3,
1278 ndopts.nd_opts_src_lladdr != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001279 }
1280 }
1281
1282 if (ndopts.nd_opts_mtu) {
Al Viroe69a4ad2006-11-14 20:56:00 -08001283 __be32 n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001284 u32 mtu;
1285
Al Viroe69a4ad2006-11-14 20:56:00 -08001286 memcpy(&n, ((u8*)(ndopts.nd_opts_mtu+1))+2, sizeof(mtu));
1287 mtu = ntohl(n);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001288
1289 if (mtu < IPV6_MIN_MTU || mtu > skb->dev->mtu) {
Joe Perches675418d2012-05-16 19:28:38 +00001290 ND_PRINTK(2, warn, "RA: invalid mtu: %d\n", mtu);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001291 } else if (in6_dev->cnf.mtu6 != mtu) {
1292 in6_dev->cnf.mtu6 = mtu;
1293
1294 if (rt)
David S. Millerdefb3512010-12-08 21:16:57 -08001295 dst_metric_set(&rt->dst, RTAX_MTU, mtu);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001296
1297 rt6_mtu_change(skb->dev, mtu);
1298 }
1299 }
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001300
Pierre Ynard31910572007-10-10 21:22:05 -07001301 if (ndopts.nd_useropts) {
YOSHIFUJI Hideaki61cf46a2008-01-22 17:32:53 +09001302 struct nd_opt_hdr *p;
1303 for (p = ndopts.nd_useropts;
1304 p;
1305 p = ndisc_next_useropt(p, ndopts.nd_useropts_end)) {
1306 ndisc_ra_useropt(skb, p);
Pierre Ynard31910572007-10-10 21:22:05 -07001307 }
1308 }
1309
Linus Torvalds1da177e2005-04-16 15:20:36 -07001310 if (ndopts.nd_opts_tgt_lladdr || ndopts.nd_opts_rh) {
Joe Perches675418d2012-05-16 19:28:38 +00001311 ND_PRINTK(2, warn, "RA: invalid RA options\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001312 }
1313out:
Amerigo Wang94e187c2012-10-29 00:13:19 +00001314 ip6_rt_put(rt);
David S. Millereb857182012-01-27 15:07:56 -08001315 if (neigh)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001316 neigh_release(neigh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001317}
1318
1319static void ndisc_redirect_rcv(struct sk_buff *skb)
1320{
Duan Jiong093d04d42012-12-14 02:59:59 +00001321 u8 *hdr;
1322 struct ndisc_options ndopts;
1323 struct rd_msg *msg = (struct rd_msg *)skb_transport_header(skb);
1324 u32 ndoptlen = skb->tail - (skb->transport_header +
1325 offsetof(struct rd_msg, opt));
1326
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001327#ifdef CONFIG_IPV6_NDISC_NODETYPE
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001328 switch (skb->ndisc_nodetype) {
1329 case NDISC_NODETYPE_HOST:
1330 case NDISC_NODETYPE_NODEFAULT:
Joe Perches675418d2012-05-16 19:28:38 +00001331 ND_PRINTK(2, warn,
1332 "Redirect: from host or unauthorized router\n");
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001333 return;
1334 }
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001335#endif
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001336
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001337 if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) {
Joe Perches675418d2012-05-16 19:28:38 +00001338 ND_PRINTK(2, warn,
1339 "Redirect: source address is not link-local\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001340 return;
1341 }
1342
Duan Jiong093d04d42012-12-14 02:59:59 +00001343 if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts))
1344 return;
1345
1346 if (!ndopts.nd_opts_rh)
1347 return;
1348
1349 hdr = (u8 *)ndopts.nd_opts_rh;
1350 hdr += 8;
1351 if (!pskb_pull(skb, hdr - skb_transport_header(skb)))
1352 return;
1353
David S. Millerb94f1c02012-07-12 00:33:37 -07001354 icmpv6_notify(skb, NDISC_REDIRECT, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001355}
1356
YOSHIFUJI Hideaki / 吉藤英明9c86daf2013-01-21 06:48:09 +00001357static u8 *ndisc_fill_redirect_hdr_option(u8 *opt, struct sk_buff *orig_skb,
1358 int rd_len)
1359{
1360 memset(opt, 0, 8);
1361 *(opt++) = ND_OPT_REDIRECT_HDR;
1362 *(opt++) = (rd_len >> 3);
1363 opt += 6;
1364
1365 memcpy(opt, ipv6_hdr(orig_skb), rd_len - 8);
1366
1367 return opt + rd_len - 8;
1368}
1369
David S. Miller49919692012-01-27 15:30:48 -08001370void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001371{
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001372 struct net_device *dev = skb->dev;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001373 struct net *net = dev_net(dev);
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001374 struct sock *sk = net->ipv6.ndisc_sk;
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00001375 int len = sizeof(struct rd_msg);
David S. Millerfbfe95a2012-06-08 23:24:18 -07001376 struct inet_peer *peer;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001377 struct sk_buff *buff;
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00001378 struct rd_msg *msg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001379 struct in6_addr saddr_buf;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001380 struct rt6_info *rt;
1381 struct dst_entry *dst;
1382 struct inet6_dev *idev;
David S. Miller4c9483b2011-03-12 16:22:43 -05001383 struct flowi6 fl6;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001384 u8 *opt;
1385 int rd_len;
1386 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001387 u8 ha_buf[MAX_ADDR_LEN], *ha = NULL;
David S. Miller1d861aa2012-07-10 03:58:16 -07001388 bool ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001389
Neil Horman95c385b2007-04-25 17:08:10 -07001390 if (ipv6_get_lladdr(dev, &saddr_buf, IFA_F_TENTATIVE)) {
Joe Perches675418d2012-05-16 19:28:38 +00001391 ND_PRINTK(2, warn, "Redirect: no link-local address on %s\n",
1392 dev->name);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001393 return;
1394 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001395
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001396 if (!ipv6_addr_equal(&ipv6_hdr(skb)->daddr, target) &&
Brian Haleybf0b48d2007-10-08 00:12:05 -07001397 ipv6_addr_type(target) != (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) {
Joe Perches675418d2012-05-16 19:28:38 +00001398 ND_PRINTK(2, warn,
1399 "Redirect: target address is not link-local unicast\n");
Li Yewang29556522007-01-30 14:33:20 -08001400 return;
1401 }
1402
David S. Miller4c9483b2011-03-12 16:22:43 -05001403 icmpv6_flow_init(sk, &fl6, NDISC_REDIRECT,
YOSHIFUJI Hideaki95e41e92007-12-06 15:43:30 -08001404 &saddr_buf, &ipv6_hdr(skb)->saddr, dev->ifindex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001405
David S. Miller4c9483b2011-03-12 16:22:43 -05001406 dst = ip6_route_output(net, NULL, &fl6);
RongQing.Li5095d642012-02-21 22:10:49 +00001407 if (dst->error) {
1408 dst_release(dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001409 return;
RongQing.Li5095d642012-02-21 22:10:49 +00001410 }
David S. Miller4c9483b2011-03-12 16:22:43 -05001411 dst = xfrm_lookup(net, dst, flowi6_to_flowi(&fl6), NULL, 0);
David S. Miller452edd52011-03-02 13:27:41 -08001412 if (IS_ERR(dst))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001413 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001414
1415 rt = (struct rt6_info *) dst;
1416
1417 if (rt->rt6i_flags & RTF_GATEWAY) {
Joe Perches675418d2012-05-16 19:28:38 +00001418 ND_PRINTK(2, warn,
1419 "Redirect: destination is not a neighbour\n");
Ilpo Järvinend73f0802009-02-06 23:47:37 -08001420 goto release;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001421 }
David S. Miller1d861aa2012-07-10 03:58:16 -07001422 peer = inet_getpeer_v6(net->ipv6.peers, &rt->rt6i_dst.addr, 1);
1423 ret = inet_peer_xrlim_allow(peer, 1*HZ);
1424 if (peer)
1425 inet_putpeer(peer);
1426 if (!ret)
Ilpo Järvinend73f0802009-02-06 23:47:37 -08001427 goto release;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001428
1429 if (dev->addr_len) {
David S. Miller49919692012-01-27 15:30:48 -08001430 struct neighbour *neigh = dst_neigh_lookup(skb_dst(skb), target);
1431 if (!neigh) {
Joe Perches675418d2012-05-16 19:28:38 +00001432 ND_PRINTK(2, warn,
1433 "Redirect: no neigh for target address\n");
David S. Miller49919692012-01-27 15:30:48 -08001434 goto release;
1435 }
1436
Linus Torvalds1da177e2005-04-16 15:20:36 -07001437 read_lock_bh(&neigh->lock);
1438 if (neigh->nud_state & NUD_VALID) {
1439 memcpy(ha_buf, neigh->ha, dev->addr_len);
1440 read_unlock_bh(&neigh->lock);
1441 ha = ha_buf;
1442 len += ndisc_opt_addr_space(dev);
1443 } else
1444 read_unlock_bh(&neigh->lock);
David S. Miller49919692012-01-27 15:30:48 -08001445
1446 neigh_release(neigh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001447 }
1448
1449 rd_len = min_t(unsigned int,
1450 IPV6_MIN_MTU-sizeof(struct ipv6hdr)-len, skb->len + 8);
1451 rd_len &= ~0x7;
1452 len += rd_len;
1453
YOSHIFUJI Hideaki / 吉藤英明de093342013-01-21 06:48:14 +00001454 buff = ndisc_alloc_skb(dev, len);
1455 if (!buff)
Ilpo Järvinend73f0802009-02-06 23:47:37 -08001456 goto release;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001457
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001458 ip6_nd_hdr(sk, buff, dev, &saddr_buf, &ipv6_hdr(skb)->saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001459 IPPROTO_ICMPV6, len);
1460
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -07001461 skb_set_transport_header(buff, skb_tail_pointer(buff) - buff->data);
Arnaldo Carvalho de Melod10ba342007-03-14 21:05:37 -03001462 skb_put(buff, len);
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00001463 msg = (struct rd_msg *)icmp6_hdr(buff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001464
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00001465 memset(&msg->icmph, 0, sizeof(struct icmp6hdr));
1466 msg->icmph.icmp6_type = NDISC_REDIRECT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001467
1468 /*
1469 * copy target and destination addresses
1470 */
1471
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00001472 msg->target = *target;
1473 msg->dest = ipv6_hdr(skb)->daddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001474
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00001475 opt = msg->opt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001476
1477 /*
1478 * include target_address option
1479 */
1480
1481 if (ha)
YOSHIFUJI Hideaki / 吉藤英明315ff092013-01-21 06:47:50 +00001482 opt = ndisc_fill_addr_option(opt, ND_OPT_TARGET_LL_ADDR, ha, dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001483
1484 /*
1485 * build redirect option and copy skb over to the new packet.
1486 */
1487
YOSHIFUJI Hideaki / 吉藤英明9c86daf2013-01-21 06:48:09 +00001488 if (rd_len)
1489 opt = ndisc_fill_redirect_hdr_option(opt, skb, rd_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001490
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00001491 msg->icmph.icmp6_cksum = csum_ipv6_magic(&saddr_buf, &ipv6_hdr(skb)->saddr,
1492 len, IPPROTO_ICMPV6,
1493 csum_partial(msg, len, 0));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001494
Eric Dumazetadf30902009-06-02 05:19:30 +00001495 skb_dst_set(buff, dst);
Eric Dumazetcfdf7642011-07-27 21:13:03 +00001496 rcu_read_lock();
1497 idev = __in6_dev_get(dst->dev);
Neil Hormanedf391f2009-04-27 02:45:02 -07001498 IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUT, skb->len);
Jan Engelhardtb2e0b382010-03-23 04:09:07 +01001499 err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, buff, NULL, dst->dev,
Patrick McHardy6e23ae22007-11-19 18:53:30 -08001500 dst_output);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001501 if (!err) {
Denis V. Lunev5c5d2442008-10-08 10:33:50 -07001502 ICMP6MSGOUT_INC_STATS(net, idev, NDISC_REDIRECT);
Denis V. Luneva862f6a2008-10-08 10:33:06 -07001503 ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001504 }
1505
Eric Dumazetcfdf7642011-07-27 21:13:03 +00001506 rcu_read_unlock();
Ilpo Järvinend73f0802009-02-06 23:47:37 -08001507 return;
1508
1509release:
1510 dst_release(dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001511}
1512
1513static void pndisc_redo(struct sk_buff *skb)
1514{
YOSHIFUJI Hideaki140e26fc2005-10-05 12:11:41 -07001515 ndisc_recv_ns(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001516 kfree_skb(skb);
1517}
1518
1519int ndisc_rcv(struct sk_buff *skb)
1520{
1521 struct nd_msg *msg;
1522
YOSHIFUJI Hideaki / 吉藤英明6bce6b42013-01-21 06:48:03 +00001523 if (skb_linearize(skb))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001524 return 0;
1525
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -07001526 msg = (struct nd_msg *)skb_transport_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001527
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -07001528 __skb_push(skb, skb->data - skb_transport_header(skb));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001529
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001530 if (ipv6_hdr(skb)->hop_limit != 255) {
Joe Perches675418d2012-05-16 19:28:38 +00001531 ND_PRINTK(2, warn, "NDISC: invalid hop-limit: %d\n",
1532 ipv6_hdr(skb)->hop_limit);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001533 return 0;
1534 }
1535
1536 if (msg->icmph.icmp6_code != 0) {
Joe Perches675418d2012-05-16 19:28:38 +00001537 ND_PRINTK(2, warn, "NDISC: invalid ICMPv6 code: %d\n",
1538 msg->icmph.icmp6_code);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001539 return 0;
1540 }
1541
Patrick McHardya61bbcf2005-08-14 17:24:31 -07001542 memset(NEIGH_CB(skb), 0, sizeof(struct neighbour_cb));
1543
Linus Torvalds1da177e2005-04-16 15:20:36 -07001544 switch (msg->icmph.icmp6_type) {
1545 case NDISC_NEIGHBOUR_SOLICITATION:
1546 ndisc_recv_ns(skb);
1547 break;
1548
1549 case NDISC_NEIGHBOUR_ADVERTISEMENT:
1550 ndisc_recv_na(skb);
1551 break;
1552
1553 case NDISC_ROUTER_SOLICITATION:
1554 ndisc_recv_rs(skb);
1555 break;
1556
1557 case NDISC_ROUTER_ADVERTISEMENT:
1558 ndisc_router_discovery(skb);
1559 break;
1560
1561 case NDISC_REDIRECT:
1562 ndisc_redirect_rcv(skb);
1563 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -07001564 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001565
1566 return 0;
1567}
1568
1569static int ndisc_netdev_event(struct notifier_block *this, unsigned long event, void *ptr)
1570{
1571 struct net_device *dev = ptr;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001572 struct net *net = dev_net(dev);
Hannes Frederic Sowa5cb04432012-11-06 16:46:20 +00001573 struct inet6_dev *idev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001574
1575 switch (event) {
1576 case NETDEV_CHANGEADDR:
1577 neigh_changeaddr(&nd_tbl, dev);
Daniel Lezcano5b7c9312008-03-03 23:28:58 -08001578 fib6_run_gc(~0UL, net);
Hannes Frederic Sowa5cb04432012-11-06 16:46:20 +00001579 idev = in6_dev_get(dev);
1580 if (!idev)
1581 break;
1582 if (idev->cnf.ndisc_notify)
1583 ndisc_send_unsol_na(dev);
1584 in6_dev_put(idev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001585 break;
1586 case NETDEV_DOWN:
1587 neigh_ifdown(&nd_tbl, dev);
Daniel Lezcano5b7c9312008-03-03 23:28:58 -08001588 fib6_run_gc(~0UL, net);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001589 break;
Ben Hutchingsf47b9462011-04-15 13:46:02 +00001590 case NETDEV_NOTIFY_PEERS:
1591 ndisc_send_unsol_na(dev);
1592 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001593 default:
1594 break;
1595 }
1596
1597 return NOTIFY_DONE;
1598}
1599
1600static struct notifier_block ndisc_netdev_notifier = {
1601 .notifier_call = ndisc_netdev_event,
1602};
1603
1604#ifdef CONFIG_SYSCTL
1605static void ndisc_warn_deprecated_sysctl(struct ctl_table *ctl,
1606 const char *func, const char *dev_name)
1607{
1608 static char warncomm[TASK_COMM_LEN];
1609 static int warned;
1610 if (strcmp(warncomm, current->comm) && warned < 5) {
1611 strcpy(warncomm, current->comm);
Joe Perchesf3213832012-05-15 14:11:53 +00001612 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 -07001613 warncomm, func,
1614 dev_name, ctl->procname,
1615 dev_name, ctl->procname);
1616 warned++;
1617 }
1618}
1619
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001620int 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 -07001621{
1622 struct net_device *dev = ctl->extra1;
1623 struct inet6_dev *idev;
1624 int ret;
1625
Eric W. Biedermand12af672007-10-18 03:05:25 -07001626 if ((strcmp(ctl->procname, "retrans_time") == 0) ||
1627 (strcmp(ctl->procname, "base_reachable_time") == 0))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001628 ndisc_warn_deprecated_sysctl(ctl, "syscall", dev ? dev->name : "default");
1629
Eric W. Biedermand12af672007-10-18 03:05:25 -07001630 if (strcmp(ctl->procname, "retrans_time") == 0)
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001631 ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
Eric W. Biedermand12af672007-10-18 03:05:25 -07001632
1633 else if (strcmp(ctl->procname, "base_reachable_time") == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001634 ret = proc_dointvec_jiffies(ctl, write,
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001635 buffer, lenp, ppos);
Eric W. Biedermand12af672007-10-18 03:05:25 -07001636
1637 else if ((strcmp(ctl->procname, "retrans_time_ms") == 0) ||
YOSHIFUJI Hideakiad02ac12007-10-29 01:32:23 -07001638 (strcmp(ctl->procname, "base_reachable_time_ms") == 0))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001639 ret = proc_dointvec_ms_jiffies(ctl, write,
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001640 buffer, lenp, ppos);
Eric W. Biedermand12af672007-10-18 03:05:25 -07001641 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001642 ret = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001643
1644 if (write && ret == 0 && dev && (idev = in6_dev_get(dev)) != NULL) {
Eric W. Biedermand12af672007-10-18 03:05:25 -07001645 if (ctl->data == &idev->nd_parms->base_reachable_time)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001646 idev->nd_parms->reachable_time = neigh_rand_reach_time(idev->nd_parms->base_reachable_time);
1647 idev->tstamp = jiffies;
1648 inet6_ifinfo_notify(RTM_NEWLINK, idev);
1649 in6_dev_put(idev);
1650 }
1651 return ret;
1652}
1653
Linus Torvalds1da177e2005-04-16 15:20:36 -07001654
1655#endif
1656
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00001657static int __net_init ndisc_net_init(struct net *net)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001658{
1659 struct ipv6_pinfo *np;
1660 struct sock *sk;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001661 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001662
Denis V. Lunev1ed85162008-04-03 14:31:03 -07001663 err = inet_ctl_sock_create(&sk, PF_INET6,
1664 SOCK_RAW, IPPROTO_ICMPV6, net);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001665 if (err < 0) {
Joe Perches675418d2012-05-16 19:28:38 +00001666 ND_PRINTK(0, err,
1667 "NDISC: Failed to initialize the control socket (err %d)\n",
1668 err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001669 return err;
1670 }
1671
Denis V. Lunev1ed85162008-04-03 14:31:03 -07001672 net->ipv6.ndisc_sk = sk;
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001673
Linus Torvalds1da177e2005-04-16 15:20:36 -07001674 np = inet6_sk(sk);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001675 np->hop_limit = 255;
1676 /* Do not loopback ndisc messages */
1677 np->mc_loop = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001678
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001679 return 0;
1680}
1681
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00001682static void __net_exit ndisc_net_exit(struct net *net)
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001683{
Denis V. Lunev1ed85162008-04-03 14:31:03 -07001684 inet_ctl_sock_destroy(net->ipv6.ndisc_sk);
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001685}
1686
1687static struct pernet_operations ndisc_net_ops = {
1688 .init = ndisc_net_init,
1689 .exit = ndisc_net_exit,
1690};
1691
1692int __init ndisc_init(void)
1693{
1694 int err;
1695
1696 err = register_pernet_subsys(&ndisc_net_ops);
1697 if (err)
1698 return err;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001699 /*
1700 * Initialize the neighbour table
1701 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001702 neigh_table_init(&nd_tbl);
1703
1704#ifdef CONFIG_SYSCTL
Eric W. Biederman54716e32010-02-14 03:27:03 +00001705 err = neigh_sysctl_register(NULL, &nd_tbl.parms, "ipv6",
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001706 &ndisc_ifinfo_sysctl_change);
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001707 if (err)
1708 goto out_unregister_pernet;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001709#endif
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001710 err = register_netdevice_notifier(&ndisc_netdev_notifier);
1711 if (err)
1712 goto out_unregister_sysctl;
1713out:
1714 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001715
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001716out_unregister_sysctl:
1717#ifdef CONFIG_SYSCTL
1718 neigh_sysctl_unregister(&nd_tbl.parms);
1719out_unregister_pernet:
1720#endif
1721 unregister_pernet_subsys(&ndisc_net_ops);
1722 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001723}
1724
1725void ndisc_cleanup(void)
1726{
Dmitry Mishin36f73d02006-11-03 16:08:19 -08001727 unregister_netdevice_notifier(&ndisc_netdev_notifier);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001728#ifdef CONFIG_SYSCTL
1729 neigh_sysctl_unregister(&nd_tbl.parms);
1730#endif
1731 neigh_table_clear(&nd_tbl);
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001732 unregister_pernet_subsys(&ndisc_net_ops);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001733}