blob: 067a0d2eb5570a7a108194bbab417247cba6f86e [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,
YOSHIFUJI Hideaki / 吉藤英明fb568632013-01-20 07:39:18 +0000493 bool router, bool solicited, bool override, bool 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
YOSHIFUJI Hideaki / 吉藤英明115b0aa2013-01-18 02:05:03 +0000674 if (skb->len < sizeof(struct nd_msg)) {
675 ND_PRINTK(2, warn, "NS: packet too short\n");
676 return;
677 }
678
Linus Torvalds1da177e2005-04-16 15:20:36 -0700679 if (ipv6_addr_is_multicast(&msg->target)) {
Joe Perches675418d2012-05-16 19:28:38 +0000680 ND_PRINTK(2, warn, "NS: multicast target address\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700681 return;
682 }
683
684 /*
685 * RFC2461 7.1.1:
686 * DAD has to be destined for solicited node multicast address.
687 */
YOSHIFUJI Hideaki / 吉藤英明ca97a642013-01-20 07:39:00 +0000688 if (dad && !ipv6_addr_is_solict_mult(daddr)) {
Joe Perches675418d2012-05-16 19:28:38 +0000689 ND_PRINTK(2, warn, "NS: bad DAD packet (wrong destination)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700690 return;
691 }
692
693 if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts)) {
Joe Perches675418d2012-05-16 19:28:38 +0000694 ND_PRINTK(2, warn, "NS: invalid ND options\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700695 return;
696 }
697
698 if (ndopts.nd_opts_src_lladdr) {
699 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr, dev);
700 if (!lladdr) {
Joe Perches675418d2012-05-16 19:28:38 +0000701 ND_PRINTK(2, warn,
702 "NS: invalid link-layer address length\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700703 return;
704 }
705
706 /* RFC2461 7.1.1:
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900707 * If the IP source address is the unspecified address,
708 * there MUST NOT be source link-layer address option
Linus Torvalds1da177e2005-04-16 15:20:36 -0700709 * in the message.
710 */
711 if (dad) {
Joe Perches675418d2012-05-16 19:28:38 +0000712 ND_PRINTK(2, warn,
713 "NS: bad DAD packet (link-layer address option)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700714 return;
715 }
716 }
717
718 inc = ipv6_addr_is_multicast(daddr);
719
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900720 ifp = ipv6_get_ifaddr(dev_net(dev), &msg->target, dev, 1);
Daniel Lezcanoa18bc692008-03-07 11:14:49 -0800721 if (ifp) {
Neil Horman95c385b2007-04-25 17:08:10 -0700722
723 if (ifp->flags & (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)) {
724 if (dad) {
Neil Horman95c385b2007-04-25 17:08:10 -0700725 /*
726 * We are colliding with another node
727 * who is doing DAD
728 * so fail our DAD process
729 */
730 addrconf_dad_failure(ifp);
Denis V. Lunev9e3be4b2007-09-11 11:04:49 +0200731 return;
Neil Horman95c385b2007-04-25 17:08:10 -0700732 } else {
733 /*
734 * This is not a dad solicitation.
735 * If we are an optimistic node,
736 * we should respond.
737 * Otherwise, we should ignore it.
738 */
739 if (!(ifp->flags & IFA_F_OPTIMISTIC))
740 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700741 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700742 }
743
744 idev = ifp->idev;
745 } else {
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700746 struct net *net = dev_net(dev);
747
Linus Torvalds1da177e2005-04-16 15:20:36 -0700748 idev = in6_dev_get(dev);
749 if (!idev) {
750 /* XXX: count this drop? */
751 return;
752 }
753
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700754 if (ipv6_chk_acast_addr(net, dev, &msg->target) ||
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900755 (idev->cnf.forwarding &&
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700756 (net->ipv6.devconf_all->proxy_ndp || idev->cnf.proxy_ndp) &&
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900757 (is_router = pndisc_is_router(&msg->target, dev)) >= 0)) {
Patrick McHardya61bbcf2005-08-14 17:24:31 -0700758 if (!(NEIGH_CB(skb)->flags & LOCALLY_ENQUEUED) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700759 skb->pkt_type != PACKET_HOST &&
760 inc != 0 &&
761 idev->nd_parms->proxy_delay != 0) {
762 /*
763 * for anycast or proxy,
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900764 * sender should delay its response
765 * by a random time between 0 and
Linus Torvalds1da177e2005-04-16 15:20:36 -0700766 * MAX_ANYCAST_DELAY_TIME seconds.
767 * (RFC2461) -- yoshfuji
768 */
769 struct sk_buff *n = skb_clone(skb, GFP_ATOMIC);
770 if (n)
771 pneigh_enqueue(&nd_tbl, idev->nd_parms, n);
772 goto out;
773 }
774 } else
775 goto out;
776 }
777
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900778 if (is_router < 0)
YOSHIFUJI Hideaki / 吉藤英明fb568632013-01-20 07:39:18 +0000779 is_router = idev->cnf.forwarding;
Ville Nuorvala62dd9312006-09-22 14:43:19 -0700780
Linus Torvalds1da177e2005-04-16 15:20:36 -0700781 if (dad) {
YOSHIFUJI Hideakif3ee4012008-04-10 15:42:11 +0900782 ndisc_send_na(dev, NULL, &in6addr_linklocal_allnodes, &msg->target,
YOSHIFUJI Hideaki / 吉藤英明fb568632013-01-20 07:39:18 +0000783 !!is_router, false, (ifp != NULL), true);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700784 goto out;
785 }
786
787 if (inc)
788 NEIGH_CACHE_STAT_INC(&nd_tbl, rcv_probes_mcast);
789 else
790 NEIGH_CACHE_STAT_INC(&nd_tbl, rcv_probes_ucast);
791
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900792 /*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700793 * update / create cache entry
794 * for the source address
795 */
796 neigh = __neigh_lookup(&nd_tbl, saddr, dev,
797 !inc || lladdr || !dev->addr_len);
798 if (neigh)
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900799 neigh_update(neigh, lladdr, NUD_STALE,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700800 NEIGH_UPDATE_F_WEAK_OVERRIDE|
801 NEIGH_UPDATE_F_OVERRIDE);
Stephen Hemminger3b04ddd2007-10-09 01:40:57 -0700802 if (neigh || !dev->header_ops) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700803 ndisc_send_na(dev, neigh, saddr, &msg->target,
YOSHIFUJI Hideaki / 吉藤英明fb568632013-01-20 07:39:18 +0000804 !!is_router,
805 true, (ifp != NULL && inc), inc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700806 if (neigh)
807 neigh_release(neigh);
808 }
809
810out:
811 if (ifp)
812 in6_ifa_put(ifp);
813 else
814 in6_dev_put(idev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700815}
816
817static void ndisc_recv_na(struct sk_buff *skb)
818{
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -0700819 struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb);
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000820 const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
821 const struct in6_addr *daddr = &ipv6_hdr(skb)->daddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700822 u8 *lladdr = NULL;
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -0700823 u32 ndoptlen = skb->tail - (skb->transport_header +
824 offsetof(struct nd_msg, opt));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700825 struct ndisc_options ndopts;
826 struct net_device *dev = skb->dev;
827 struct inet6_ifaddr *ifp;
828 struct neighbour *neigh;
829
830 if (skb->len < sizeof(struct nd_msg)) {
Joe Perches675418d2012-05-16 19:28:38 +0000831 ND_PRINTK(2, warn, "NA: packet too short\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700832 return;
833 }
834
835 if (ipv6_addr_is_multicast(&msg->target)) {
Joe Perches675418d2012-05-16 19:28:38 +0000836 ND_PRINTK(2, warn, "NA: target address is multicast\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700837 return;
838 }
839
840 if (ipv6_addr_is_multicast(daddr) &&
841 msg->icmph.icmp6_solicited) {
Joe Perches675418d2012-05-16 19:28:38 +0000842 ND_PRINTK(2, warn, "NA: solicited NA is multicasted\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700843 return;
844 }
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900845
Linus Torvalds1da177e2005-04-16 15:20:36 -0700846 if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts)) {
Joe Perches675418d2012-05-16 19:28:38 +0000847 ND_PRINTK(2, warn, "NS: invalid ND option\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700848 return;
849 }
850 if (ndopts.nd_opts_tgt_lladdr) {
851 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr, dev);
852 if (!lladdr) {
Joe Perches675418d2012-05-16 19:28:38 +0000853 ND_PRINTK(2, warn,
854 "NA: invalid link-layer address length\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700855 return;
856 }
857 }
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900858 ifp = ipv6_get_ifaddr(dev_net(dev), &msg->target, dev, 1);
Daniel Lezcanoa18bc692008-03-07 11:14:49 -0800859 if (ifp) {
Daniel Walterbd015922011-04-13 21:09:25 +0000860 if (skb->pkt_type != PACKET_LOOPBACK
861 && (ifp->flags & IFA_F_TENTATIVE)) {
862 addrconf_dad_failure(ifp);
863 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700864 }
865 /* What should we make now? The advertisement
866 is invalid, but ndisc specs say nothing
867 about it. It could be misconfiguration, or
868 an smart proxy agent tries to help us :-)
Jan Sembera24fc7b82008-12-09 15:48:32 -0800869
870 We should not print the error if NA has been
871 received from loopback - it is just our own
872 unsolicited advertisement.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700873 */
Jan Sembera24fc7b82008-12-09 15:48:32 -0800874 if (skb->pkt_type != PACKET_LOOPBACK)
Joe Perches675418d2012-05-16 19:28:38 +0000875 ND_PRINTK(1, warn,
876 "NA: someone advertises our address %pI6 on %s!\n",
877 &ifp->addr, ifp->idev->dev->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700878 in6_ifa_put(ifp);
879 return;
880 }
881 neigh = neigh_lookup(&nd_tbl, &msg->target, dev);
882
883 if (neigh) {
884 u8 old_flags = neigh->flags;
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700885 struct net *net = dev_net(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700886
887 if (neigh->nud_state & NUD_FAILED)
888 goto out;
889
Ville Nuorvala5f3e6e92006-09-22 14:42:46 -0700890 /*
891 * Don't update the neighbor cache entry on a proxy NA from
892 * ourselves because either the proxied node is off link or it
893 * has already sent a NA to us.
894 */
895 if (lladdr && !memcmp(lladdr, dev->dev_addr, dev->addr_len) &&
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700896 net->ipv6.devconf_all->forwarding && net->ipv6.devconf_all->proxy_ndp &&
897 pneigh_lookup(&nd_tbl, net, &msg->target, dev, 0)) {
Nicolas Dichtelb20b6d92012-11-07 05:05:38 +0000898 /* XXX: idev->cnf.proxy_ndp */
Ville Nuorvala5f3e6e92006-09-22 14:42:46 -0700899 goto out;
YOSHIFUJI Hideakifbea49e2006-09-22 14:43:49 -0700900 }
Ville Nuorvala5f3e6e92006-09-22 14:42:46 -0700901
Linus Torvalds1da177e2005-04-16 15:20:36 -0700902 neigh_update(neigh, lladdr,
903 msg->icmph.icmp6_solicited ? NUD_REACHABLE : NUD_STALE,
904 NEIGH_UPDATE_F_WEAK_OVERRIDE|
905 (msg->icmph.icmp6_override ? NEIGH_UPDATE_F_OVERRIDE : 0)|
906 NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
907 (msg->icmph.icmp6_router ? NEIGH_UPDATE_F_ISROUTER : 0));
908
909 if ((old_flags & ~neigh->flags) & NTF_ROUTER) {
910 /*
911 * Change: router to host
912 */
913 struct rt6_info *rt;
914 rt = rt6_get_dflt_router(saddr, dev);
915 if (rt)
Thomas Grafe0a1ad732006-08-22 00:00:21 -0700916 ip6_del_rt(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700917 }
918
919out:
920 neigh_release(neigh);
921 }
922}
923
924static void ndisc_recv_rs(struct sk_buff *skb)
925{
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -0700926 struct rs_msg *rs_msg = (struct rs_msg *)skb_transport_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700927 unsigned long ndoptlen = skb->len - sizeof(*rs_msg);
928 struct neighbour *neigh;
929 struct inet6_dev *idev;
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000930 const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700931 struct ndisc_options ndopts;
932 u8 *lladdr = NULL;
933
934 if (skb->len < sizeof(*rs_msg))
935 return;
936
Eric Dumazetcfdf7642011-07-27 21:13:03 +0000937 idev = __in6_dev_get(skb->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700938 if (!idev) {
Joe Perches675418d2012-05-16 19:28:38 +0000939 ND_PRINTK(1, err, "RS: can't find in6 device\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700940 return;
941 }
942
943 /* Don't accept RS if we're not in router mode */
944 if (!idev->cnf.forwarding)
945 goto out;
946
947 /*
948 * Don't update NCE if src = ::;
949 * this implies that the source node has no ip address assigned yet.
950 */
951 if (ipv6_addr_any(saddr))
952 goto out;
953
954 /* Parse ND options */
955 if (!ndisc_parse_options(rs_msg->opt, ndoptlen, &ndopts)) {
Joe Perches675418d2012-05-16 19:28:38 +0000956 ND_PRINTK(2, notice, "NS: invalid ND option, ignored\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700957 goto out;
958 }
959
960 if (ndopts.nd_opts_src_lladdr) {
961 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr,
962 skb->dev);
963 if (!lladdr)
964 goto out;
965 }
966
967 neigh = __neigh_lookup(&nd_tbl, saddr, skb->dev, 1);
968 if (neigh) {
969 neigh_update(neigh, lladdr, NUD_STALE,
970 NEIGH_UPDATE_F_WEAK_OVERRIDE|
971 NEIGH_UPDATE_F_OVERRIDE|
972 NEIGH_UPDATE_F_OVERRIDE_ISROUTER);
973 neigh_release(neigh);
974 }
975out:
Eric Dumazetcfdf7642011-07-27 21:13:03 +0000976 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700977}
978
Pierre Ynard31910572007-10-10 21:22:05 -0700979static void ndisc_ra_useropt(struct sk_buff *ra, struct nd_opt_hdr *opt)
980{
981 struct icmp6hdr *icmp6h = (struct icmp6hdr *)skb_transport_header(ra);
982 struct sk_buff *skb;
983 struct nlmsghdr *nlh;
984 struct nduseroptmsg *ndmsg;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900985 struct net *net = dev_net(ra->dev);
Pierre Ynard31910572007-10-10 21:22:05 -0700986 int err;
987 int base_size = NLMSG_ALIGN(sizeof(struct nduseroptmsg)
988 + (opt->nd_opt_len << 3));
989 size_t msg_size = base_size + nla_total_size(sizeof(struct in6_addr));
990
991 skb = nlmsg_new(msg_size, GFP_ATOMIC);
992 if (skb == NULL) {
993 err = -ENOBUFS;
994 goto errout;
995 }
996
997 nlh = nlmsg_put(skb, 0, 0, RTM_NEWNDUSEROPT, base_size, 0);
998 if (nlh == NULL) {
999 goto nla_put_failure;
1000 }
1001
1002 ndmsg = nlmsg_data(nlh);
1003 ndmsg->nduseropt_family = AF_INET6;
Pierre Ynarddbb2ed22007-11-12 17:58:35 -08001004 ndmsg->nduseropt_ifindex = ra->dev->ifindex;
Pierre Ynard31910572007-10-10 21:22:05 -07001005 ndmsg->nduseropt_icmp_type = icmp6h->icmp6_type;
1006 ndmsg->nduseropt_icmp_code = icmp6h->icmp6_code;
1007 ndmsg->nduseropt_opts_len = opt->nd_opt_len << 3;
1008
1009 memcpy(ndmsg + 1, opt, opt->nd_opt_len << 3);
1010
David S. Millerc78679e2012-04-01 20:27:33 -04001011 if (nla_put(skb, NDUSEROPT_SRCADDR, sizeof(struct in6_addr),
1012 &ipv6_hdr(ra)->saddr))
1013 goto nla_put_failure;
Pierre Ynard31910572007-10-10 21:22:05 -07001014 nlmsg_end(skb, nlh);
1015
Pablo Neira Ayuso1ce85fe2009-02-24 23:18:28 -08001016 rtnl_notify(skb, net, 0, RTNLGRP_ND_USEROPT, NULL, GFP_ATOMIC);
Pierre Ynard31910572007-10-10 21:22:05 -07001017 return;
1018
1019nla_put_failure:
1020 nlmsg_free(skb);
1021 err = -EMSGSIZE;
1022errout:
Daniel Lezcanoa18bc692008-03-07 11:14:49 -08001023 rtnl_set_sk_err(net, RTNLGRP_ND_USEROPT, err);
Pierre Ynard31910572007-10-10 21:22:05 -07001024}
1025
Linus Torvalds1da177e2005-04-16 15:20:36 -07001026static void ndisc_router_discovery(struct sk_buff *skb)
1027{
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -07001028 struct ra_msg *ra_msg = (struct ra_msg *)skb_transport_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001029 struct neighbour *neigh = NULL;
1030 struct inet6_dev *in6_dev;
YOSHIFUJI Hideaki65f5c7c2006-03-20 16:55:08 -08001031 struct rt6_info *rt = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001032 int lifetime;
1033 struct ndisc_options ndopts;
1034 int optlen;
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001035 unsigned int pref = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001036
1037 __u8 * opt = (__u8 *)(ra_msg + 1);
1038
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -07001039 optlen = (skb->tail - skb->transport_header) - sizeof(struct ra_msg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001040
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001041 if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) {
Joe Perches675418d2012-05-16 19:28:38 +00001042 ND_PRINTK(2, warn, "RA: source address is not link-local\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001043 return;
1044 }
1045 if (optlen < 0) {
Joe Perches675418d2012-05-16 19:28:38 +00001046 ND_PRINTK(2, warn, "RA: packet too short\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001047 return;
1048 }
1049
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001050#ifdef CONFIG_IPV6_NDISC_NODETYPE
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001051 if (skb->ndisc_nodetype == NDISC_NODETYPE_HOST) {
Joe Perches675418d2012-05-16 19:28:38 +00001052 ND_PRINTK(2, warn, "RA: from host or unauthorized router\n");
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001053 return;
1054 }
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001055#endif
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001056
Linus Torvalds1da177e2005-04-16 15:20:36 -07001057 /*
1058 * set the RA_RECV flag in the interface
1059 */
1060
Eric Dumazetcfdf7642011-07-27 21:13:03 +00001061 in6_dev = __in6_dev_get(skb->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001062 if (in6_dev == NULL) {
Joe Perches675418d2012-05-16 19:28:38 +00001063 ND_PRINTK(0, err, "RA: can't find inet6 device for %s\n",
1064 skb->dev->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001065 return;
1066 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001067
1068 if (!ndisc_parse_options(opt, optlen, &ndopts)) {
Joe Perches675418d2012-05-16 19:28:38 +00001069 ND_PRINTK(2, warn, "RA: invalid ND options\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001070 return;
1071 }
1072
Shmulik Ladkaniaeaf6e92012-11-30 10:25:59 +00001073 if (!ipv6_accept_ra(in6_dev))
David Ward31ce8c72009-08-29 00:04:09 -07001074 goto skip_linkparms;
1075
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001076#ifdef CONFIG_IPV6_NDISC_NODETYPE
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001077 /* skip link-specific parameters from interior routers */
1078 if (skb->ndisc_nodetype == NDISC_NODETYPE_NODEFAULT)
1079 goto skip_linkparms;
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001080#endif
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001081
Linus Torvalds1da177e2005-04-16 15:20:36 -07001082 if (in6_dev->if_flags & IF_RS_SENT) {
1083 /*
1084 * flag that an RA was received after an RS was sent
1085 * out on this interface.
1086 */
1087 in6_dev->if_flags |= IF_RA_RCVD;
1088 }
1089
1090 /*
1091 * Remember the managed/otherconf flags from most recently
1092 * received RA message (RFC 2462) -- yoshfuji
1093 */
1094 in6_dev->if_flags = (in6_dev->if_flags & ~(IF_RA_MANAGED |
1095 IF_RA_OTHERCONF)) |
1096 (ra_msg->icmph.icmp6_addrconf_managed ?
1097 IF_RA_MANAGED : 0) |
1098 (ra_msg->icmph.icmp6_addrconf_other ?
1099 IF_RA_OTHERCONF : 0);
1100
YOSHIFUJI Hideaki65f5c7c2006-03-20 16:55:08 -08001101 if (!in6_dev->cnf.accept_ra_defrtr)
1102 goto skip_defrtr;
1103
Andreas Hofmeister9f562202011-10-24 19:13:15 -04001104 if (ipv6_chk_addr(dev_net(in6_dev->dev), &ipv6_hdr(skb)->saddr, NULL, 0))
1105 goto skip_defrtr;
1106
Linus Torvalds1da177e2005-04-16 15:20:36 -07001107 lifetime = ntohs(ra_msg->icmph.icmp6_rt_lifetime);
1108
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001109#ifdef CONFIG_IPV6_ROUTER_PREF
1110 pref = ra_msg->icmph.icmp6_router_pref;
1111 /* 10b is handled as if it were 00b (medium) */
YOSHIFUJI Hideaki930d6ff2006-03-20 17:05:30 -08001112 if (pref == ICMPV6_ROUTER_PREF_INVALID ||
YOSHIFUJI Hideaki6d5b78c2007-06-22 16:07:04 -07001113 !in6_dev->cnf.accept_ra_rtr_pref)
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001114 pref = ICMPV6_ROUTER_PREF_MEDIUM;
1115#endif
1116
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001117 rt = rt6_get_dflt_router(&ipv6_hdr(skb)->saddr, skb->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001118
David S. Millereb857182012-01-27 15:07:56 -08001119 if (rt) {
1120 neigh = dst_neigh_lookup(&rt->dst, &ipv6_hdr(skb)->saddr);
1121 if (!neigh) {
Joe Perches675418d2012-05-16 19:28:38 +00001122 ND_PRINTK(0, err,
1123 "RA: %s got default router without neighbour\n",
1124 __func__);
Amerigo Wang94e187c2012-10-29 00:13:19 +00001125 ip6_rt_put(rt);
David S. Millereb857182012-01-27 15:07:56 -08001126 return;
1127 }
1128 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001129 if (rt && lifetime == 0) {
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001130 ip6_del_rt(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001131 rt = NULL;
1132 }
1133
1134 if (rt == NULL && lifetime) {
Joe Perches675418d2012-05-16 19:28:38 +00001135 ND_PRINTK(3, dbg, "RA: adding default router\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001136
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001137 rt = rt6_add_dflt_router(&ipv6_hdr(skb)->saddr, skb->dev, pref);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001138 if (rt == NULL) {
Joe Perches675418d2012-05-16 19:28:38 +00001139 ND_PRINTK(0, err,
1140 "RA: %s failed to add default route\n",
1141 __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001142 return;
1143 }
1144
David S. Millereb857182012-01-27 15:07:56 -08001145 neigh = dst_neigh_lookup(&rt->dst, &ipv6_hdr(skb)->saddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001146 if (neigh == NULL) {
Joe Perches675418d2012-05-16 19:28:38 +00001147 ND_PRINTK(0, err,
1148 "RA: %s got default router without neighbour\n",
1149 __func__);
Amerigo Wang94e187c2012-10-29 00:13:19 +00001150 ip6_rt_put(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001151 return;
1152 }
1153 neigh->flags |= NTF_ROUTER;
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001154 } else if (rt) {
Pedro Ribeiro22441cf2008-10-15 15:47:49 -07001155 rt->rt6i_flags = (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001156 }
1157
1158 if (rt)
Gao feng1716a962012-04-06 00:13:10 +00001159 rt6_set_expires(rt, jiffies + (HZ * lifetime));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001160 if (ra_msg->icmph.icmp6_hop_limit) {
1161 in6_dev->cnf.hop_limit = ra_msg->icmph.icmp6_hop_limit;
1162 if (rt)
David S. Millerdefb3512010-12-08 21:16:57 -08001163 dst_metric_set(&rt->dst, RTAX_HOPLIMIT,
1164 ra_msg->icmph.icmp6_hop_limit);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001165 }
1166
YOSHIFUJI Hideaki65f5c7c2006-03-20 16:55:08 -08001167skip_defrtr:
1168
Linus Torvalds1da177e2005-04-16 15:20:36 -07001169 /*
1170 * Update Reachable Time and Retrans Timer
1171 */
1172
1173 if (in6_dev->nd_parms) {
1174 unsigned long rtime = ntohl(ra_msg->retrans_timer);
1175
1176 if (rtime && rtime/1000 < MAX_SCHEDULE_TIMEOUT/HZ) {
1177 rtime = (rtime*HZ)/1000;
1178 if (rtime < HZ/10)
1179 rtime = HZ/10;
1180 in6_dev->nd_parms->retrans_time = rtime;
1181 in6_dev->tstamp = jiffies;
1182 inet6_ifinfo_notify(RTM_NEWLINK, in6_dev);
1183 }
1184
1185 rtime = ntohl(ra_msg->reachable_time);
1186 if (rtime && rtime/1000 < MAX_SCHEDULE_TIMEOUT/(3*HZ)) {
1187 rtime = (rtime*HZ)/1000;
1188
1189 if (rtime < HZ/10)
1190 rtime = HZ/10;
1191
1192 if (rtime != in6_dev->nd_parms->base_reachable_time) {
1193 in6_dev->nd_parms->base_reachable_time = rtime;
1194 in6_dev->nd_parms->gc_staletime = 3 * rtime;
1195 in6_dev->nd_parms->reachable_time = neigh_rand_reach_time(rtime);
1196 in6_dev->tstamp = jiffies;
1197 inet6_ifinfo_notify(RTM_NEWLINK, in6_dev);
1198 }
1199 }
1200 }
1201
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001202skip_linkparms:
1203
Linus Torvalds1da177e2005-04-16 15:20:36 -07001204 /*
1205 * Process options.
1206 */
1207
1208 if (!neigh)
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001209 neigh = __neigh_lookup(&nd_tbl, &ipv6_hdr(skb)->saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001210 skb->dev, 1);
1211 if (neigh) {
1212 u8 *lladdr = NULL;
1213 if (ndopts.nd_opts_src_lladdr) {
1214 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr,
1215 skb->dev);
1216 if (!lladdr) {
Joe Perches675418d2012-05-16 19:28:38 +00001217 ND_PRINTK(2, warn,
1218 "RA: invalid link-layer address length\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001219 goto out;
1220 }
1221 }
1222 neigh_update(neigh, lladdr, NUD_STALE,
1223 NEIGH_UPDATE_F_WEAK_OVERRIDE|
1224 NEIGH_UPDATE_F_OVERRIDE|
1225 NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
1226 NEIGH_UPDATE_F_ISROUTER);
1227 }
1228
Shmulik Ladkaniaeaf6e92012-11-30 10:25:59 +00001229 if (!ipv6_accept_ra(in6_dev))
David Ward31ce8c72009-08-29 00:04:09 -07001230 goto out;
1231
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001232#ifdef CONFIG_IPV6_ROUTE_INFO
Andreas Hofmeister9f562202011-10-24 19:13:15 -04001233 if (ipv6_chk_addr(dev_net(in6_dev->dev), &ipv6_hdr(skb)->saddr, NULL, 0))
1234 goto skip_routeinfo;
1235
YOSHIFUJI Hideaki09c884d2006-03-20 17:07:03 -08001236 if (in6_dev->cnf.accept_ra_rtr_pref && ndopts.nd_opts_ri) {
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001237 struct nd_opt_hdr *p;
1238 for (p = ndopts.nd_opts_ri;
1239 p;
1240 p = ndisc_next_option(p, ndopts.nd_opts_ri_end)) {
YOSHIFUJI Hideaki6294e002008-03-15 23:56:52 -04001241 struct route_info *ri = (struct route_info *)p;
1242#ifdef CONFIG_IPV6_NDISC_NODETYPE
1243 if (skb->ndisc_nodetype == NDISC_NODETYPE_NODEFAULT &&
1244 ri->prefix_len == 0)
1245 continue;
1246#endif
1247 if (ri->prefix_len > in6_dev->cnf.accept_ra_rt_info_max_plen)
YOSHIFUJI Hideaki09c884d2006-03-20 17:07:03 -08001248 continue;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001249 rt6_route_rcv(skb->dev, (u8*)p, (p->nd_opt_len) << 3,
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001250 &ipv6_hdr(skb)->saddr);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001251 }
1252 }
Andreas Hofmeister9f562202011-10-24 19:13:15 -04001253
1254skip_routeinfo:
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001255#endif
1256
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001257#ifdef CONFIG_IPV6_NDISC_NODETYPE
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001258 /* skip link-specific ndopts from interior routers */
1259 if (skb->ndisc_nodetype == NDISC_NODETYPE_NODEFAULT)
1260 goto out;
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001261#endif
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001262
YOSHIFUJI Hideakic4fd30e2006-03-20 16:55:26 -08001263 if (in6_dev->cnf.accept_ra_pinfo && ndopts.nd_opts_pi) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001264 struct nd_opt_hdr *p;
1265 for (p = ndopts.nd_opts_pi;
1266 p;
1267 p = ndisc_next_option(p, ndopts.nd_opts_pi_end)) {
Neil Hormane6bff992012-01-04 10:49:15 +00001268 addrconf_prefix_rcv(skb->dev, (u8 *)p,
1269 (p->nd_opt_len) << 3,
1270 ndopts.nd_opts_src_lladdr != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001271 }
1272 }
1273
1274 if (ndopts.nd_opts_mtu) {
Al Viroe69a4adc2006-11-14 20:56:00 -08001275 __be32 n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001276 u32 mtu;
1277
Al Viroe69a4adc2006-11-14 20:56:00 -08001278 memcpy(&n, ((u8*)(ndopts.nd_opts_mtu+1))+2, sizeof(mtu));
1279 mtu = ntohl(n);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001280
1281 if (mtu < IPV6_MIN_MTU || mtu > skb->dev->mtu) {
Joe Perches675418d2012-05-16 19:28:38 +00001282 ND_PRINTK(2, warn, "RA: invalid mtu: %d\n", mtu);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001283 } else if (in6_dev->cnf.mtu6 != mtu) {
1284 in6_dev->cnf.mtu6 = mtu;
1285
1286 if (rt)
David S. Millerdefb3512010-12-08 21:16:57 -08001287 dst_metric_set(&rt->dst, RTAX_MTU, mtu);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001288
1289 rt6_mtu_change(skb->dev, mtu);
1290 }
1291 }
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001292
Pierre Ynard31910572007-10-10 21:22:05 -07001293 if (ndopts.nd_useropts) {
YOSHIFUJI Hideaki61cf46ad2008-01-22 17:32:53 +09001294 struct nd_opt_hdr *p;
1295 for (p = ndopts.nd_useropts;
1296 p;
1297 p = ndisc_next_useropt(p, ndopts.nd_useropts_end)) {
1298 ndisc_ra_useropt(skb, p);
Pierre Ynard31910572007-10-10 21:22:05 -07001299 }
1300 }
1301
Linus Torvalds1da177e2005-04-16 15:20:36 -07001302 if (ndopts.nd_opts_tgt_lladdr || ndopts.nd_opts_rh) {
Joe Perches675418d2012-05-16 19:28:38 +00001303 ND_PRINTK(2, warn, "RA: invalid RA options\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001304 }
1305out:
Amerigo Wang94e187c2012-10-29 00:13:19 +00001306 ip6_rt_put(rt);
David S. Millereb857182012-01-27 15:07:56 -08001307 if (neigh)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001308 neigh_release(neigh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001309}
1310
1311static void ndisc_redirect_rcv(struct sk_buff *skb)
1312{
Duan Jiong093d04d2012-12-14 02:59:59 +00001313 u8 *hdr;
1314 struct ndisc_options ndopts;
1315 struct rd_msg *msg = (struct rd_msg *)skb_transport_header(skb);
1316 u32 ndoptlen = skb->tail - (skb->transport_header +
1317 offsetof(struct rd_msg, opt));
1318
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001319#ifdef CONFIG_IPV6_NDISC_NODETYPE
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001320 switch (skb->ndisc_nodetype) {
1321 case NDISC_NODETYPE_HOST:
1322 case NDISC_NODETYPE_NODEFAULT:
Joe Perches675418d2012-05-16 19:28:38 +00001323 ND_PRINTK(2, warn,
1324 "Redirect: from host or unauthorized router\n");
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001325 return;
1326 }
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001327#endif
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001328
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001329 if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) {
Joe Perches675418d2012-05-16 19:28:38 +00001330 ND_PRINTK(2, warn,
1331 "Redirect: source address is not link-local\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001332 return;
1333 }
1334
Duan Jiong093d04d2012-12-14 02:59:59 +00001335 if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts))
1336 return;
1337
1338 if (!ndopts.nd_opts_rh)
1339 return;
1340
1341 hdr = (u8 *)ndopts.nd_opts_rh;
1342 hdr += 8;
1343 if (!pskb_pull(skb, hdr - skb_transport_header(skb)))
1344 return;
1345
David S. Millerb94f1c02012-07-12 00:33:37 -07001346 icmpv6_notify(skb, NDISC_REDIRECT, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001347}
1348
David S. Miller49919692012-01-27 15:30:48 -08001349void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001350{
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001351 struct net_device *dev = skb->dev;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001352 struct net *net = dev_net(dev);
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001353 struct sock *sk = net->ipv6.ndisc_sk;
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00001354 int len = sizeof(struct rd_msg);
David S. Millerfbfe95a2012-06-08 23:24:18 -07001355 struct inet_peer *peer;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001356 struct sk_buff *buff;
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00001357 struct rd_msg *msg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001358 struct in6_addr saddr_buf;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001359 struct rt6_info *rt;
1360 struct dst_entry *dst;
1361 struct inet6_dev *idev;
David S. Miller4c9483b2011-03-12 16:22:43 -05001362 struct flowi6 fl6;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001363 u8 *opt;
Herbert Xua7ae1992011-11-18 02:20:04 +00001364 int hlen, tlen;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001365 int rd_len;
1366 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001367 u8 ha_buf[MAX_ADDR_LEN], *ha = NULL;
David S. Miller1d861aa2012-07-10 03:58:16 -07001368 bool ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001369
Neil Horman95c385b2007-04-25 17:08:10 -07001370 if (ipv6_get_lladdr(dev, &saddr_buf, IFA_F_TENTATIVE)) {
Joe Perches675418d2012-05-16 19:28:38 +00001371 ND_PRINTK(2, warn, "Redirect: no link-local address on %s\n",
1372 dev->name);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001373 return;
1374 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001375
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001376 if (!ipv6_addr_equal(&ipv6_hdr(skb)->daddr, target) &&
Brian Haleybf0b48d2007-10-08 00:12:05 -07001377 ipv6_addr_type(target) != (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) {
Joe Perches675418d2012-05-16 19:28:38 +00001378 ND_PRINTK(2, warn,
1379 "Redirect: target address is not link-local unicast\n");
Li Yewang29556522007-01-30 14:33:20 -08001380 return;
1381 }
1382
David S. Miller4c9483b2011-03-12 16:22:43 -05001383 icmpv6_flow_init(sk, &fl6, NDISC_REDIRECT,
YOSHIFUJI Hideaki95e41e92007-12-06 15:43:30 -08001384 &saddr_buf, &ipv6_hdr(skb)->saddr, dev->ifindex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001385
David S. Miller4c9483b2011-03-12 16:22:43 -05001386 dst = ip6_route_output(net, NULL, &fl6);
RongQing.Li5095d642012-02-21 22:10:49 +00001387 if (dst->error) {
1388 dst_release(dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001389 return;
RongQing.Li5095d642012-02-21 22:10:49 +00001390 }
David S. Miller4c9483b2011-03-12 16:22:43 -05001391 dst = xfrm_lookup(net, dst, flowi6_to_flowi(&fl6), NULL, 0);
David S. Miller452edd52011-03-02 13:27:41 -08001392 if (IS_ERR(dst))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001393 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001394
1395 rt = (struct rt6_info *) dst;
1396
1397 if (rt->rt6i_flags & RTF_GATEWAY) {
Joe Perches675418d2012-05-16 19:28:38 +00001398 ND_PRINTK(2, warn,
1399 "Redirect: destination is not a neighbour\n");
Ilpo Järvinend73f0802009-02-06 23:47:37 -08001400 goto release;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001401 }
David S. Miller1d861aa2012-07-10 03:58:16 -07001402 peer = inet_getpeer_v6(net->ipv6.peers, &rt->rt6i_dst.addr, 1);
1403 ret = inet_peer_xrlim_allow(peer, 1*HZ);
1404 if (peer)
1405 inet_putpeer(peer);
1406 if (!ret)
Ilpo Järvinend73f0802009-02-06 23:47:37 -08001407 goto release;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001408
1409 if (dev->addr_len) {
David S. Miller49919692012-01-27 15:30:48 -08001410 struct neighbour *neigh = dst_neigh_lookup(skb_dst(skb), target);
1411 if (!neigh) {
Joe Perches675418d2012-05-16 19:28:38 +00001412 ND_PRINTK(2, warn,
1413 "Redirect: no neigh for target address\n");
David S. Miller49919692012-01-27 15:30:48 -08001414 goto release;
1415 }
1416
Linus Torvalds1da177e2005-04-16 15:20:36 -07001417 read_lock_bh(&neigh->lock);
1418 if (neigh->nud_state & NUD_VALID) {
1419 memcpy(ha_buf, neigh->ha, dev->addr_len);
1420 read_unlock_bh(&neigh->lock);
1421 ha = ha_buf;
1422 len += ndisc_opt_addr_space(dev);
1423 } else
1424 read_unlock_bh(&neigh->lock);
David S. Miller49919692012-01-27 15:30:48 -08001425
1426 neigh_release(neigh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001427 }
1428
1429 rd_len = min_t(unsigned int,
1430 IPV6_MIN_MTU-sizeof(struct ipv6hdr)-len, skb->len + 8);
1431 rd_len &= ~0x7;
1432 len += rd_len;
1433
Herbert Xua7ae1992011-11-18 02:20:04 +00001434 hlen = LL_RESERVED_SPACE(dev);
1435 tlen = dev->needed_tailroom;
David S. Millerd54a81d2006-12-02 21:00:06 -08001436 buff = sock_alloc_send_skb(sk,
YOSHIFUJI Hideaki / 吉藤英明b7dc8c32013-01-04 03:58:04 +00001437 (sizeof(struct ipv6hdr) +
Herbert Xua7ae1992011-11-18 02:20:04 +00001438 len + hlen + tlen),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001439 1, &err);
1440 if (buff == NULL) {
Joe Perches675418d2012-05-16 19:28:38 +00001441 ND_PRINTK(0, err,
1442 "Redirect: %s failed to allocate an skb, err=%d\n",
1443 __func__, err);
Ilpo Järvinend73f0802009-02-06 23:47:37 -08001444 goto release;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001445 }
1446
Herbert Xua7ae1992011-11-18 02:20:04 +00001447 skb_reserve(buff, hlen);
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001448 ip6_nd_hdr(sk, buff, dev, &saddr_buf, &ipv6_hdr(skb)->saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001449 IPPROTO_ICMPV6, len);
1450
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -07001451 skb_set_transport_header(buff, skb_tail_pointer(buff) - buff->data);
Arnaldo Carvalho de Melod10ba342007-03-14 21:05:37 -03001452 skb_put(buff, len);
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00001453 msg = (struct rd_msg *)icmp6_hdr(buff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001454
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00001455 memset(&msg->icmph, 0, sizeof(struct icmp6hdr));
1456 msg->icmph.icmp6_type = NDISC_REDIRECT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001457
1458 /*
1459 * copy target and destination addresses
1460 */
1461
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00001462 msg->target = *target;
1463 msg->dest = ipv6_hdr(skb)->daddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001464
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00001465 opt = msg->opt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001466
1467 /*
1468 * include target_address option
1469 */
1470
1471 if (ha)
1472 opt = ndisc_fill_addr_option(opt, ND_OPT_TARGET_LL_ADDR, ha,
1473 dev->addr_len, dev->type);
1474
1475 /*
1476 * build redirect option and copy skb over to the new packet.
1477 */
1478
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001479 memset(opt, 0, 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001480 *(opt++) = ND_OPT_REDIRECT_HDR;
1481 *(opt++) = (rd_len >> 3);
1482 opt += 6;
1483
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001484 memcpy(opt, ipv6_hdr(skb), rd_len - 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001485
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00001486 msg->icmph.icmp6_cksum = csum_ipv6_magic(&saddr_buf, &ipv6_hdr(skb)->saddr,
1487 len, IPPROTO_ICMPV6,
1488 csum_partial(msg, len, 0));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001489
Eric Dumazetadf30902009-06-02 05:19:30 +00001490 skb_dst_set(buff, dst);
Eric Dumazetcfdf7642011-07-27 21:13:03 +00001491 rcu_read_lock();
1492 idev = __in6_dev_get(dst->dev);
Neil Hormanedf391f2009-04-27 02:45:02 -07001493 IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUT, skb->len);
Jan Engelhardtb2e0b382010-03-23 04:09:07 +01001494 err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, buff, NULL, dst->dev,
Patrick McHardy6e23ae22007-11-19 18:53:30 -08001495 dst_output);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001496 if (!err) {
Denis V. Lunev5c5d2442008-10-08 10:33:50 -07001497 ICMP6MSGOUT_INC_STATS(net, idev, NDISC_REDIRECT);
Denis V. Luneva862f6a2008-10-08 10:33:06 -07001498 ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001499 }
1500
Eric Dumazetcfdf7642011-07-27 21:13:03 +00001501 rcu_read_unlock();
Ilpo Järvinend73f0802009-02-06 23:47:37 -08001502 return;
1503
1504release:
1505 dst_release(dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001506}
1507
1508static void pndisc_redo(struct sk_buff *skb)
1509{
YOSHIFUJI Hideaki140e26fc2005-10-05 12:11:41 -07001510 ndisc_recv_ns(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001511 kfree_skb(skb);
1512}
1513
1514int ndisc_rcv(struct sk_buff *skb)
1515{
1516 struct nd_msg *msg;
1517
1518 if (!pskb_may_pull(skb, skb->len))
1519 return 0;
1520
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -07001521 msg = (struct nd_msg *)skb_transport_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001522
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -07001523 __skb_push(skb, skb->data - skb_transport_header(skb));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001524
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001525 if (ipv6_hdr(skb)->hop_limit != 255) {
Joe Perches675418d2012-05-16 19:28:38 +00001526 ND_PRINTK(2, warn, "NDISC: invalid hop-limit: %d\n",
1527 ipv6_hdr(skb)->hop_limit);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001528 return 0;
1529 }
1530
1531 if (msg->icmph.icmp6_code != 0) {
Joe Perches675418d2012-05-16 19:28:38 +00001532 ND_PRINTK(2, warn, "NDISC: invalid ICMPv6 code: %d\n",
1533 msg->icmph.icmp6_code);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001534 return 0;
1535 }
1536
Patrick McHardya61bbcf2005-08-14 17:24:31 -07001537 memset(NEIGH_CB(skb), 0, sizeof(struct neighbour_cb));
1538
Linus Torvalds1da177e2005-04-16 15:20:36 -07001539 switch (msg->icmph.icmp6_type) {
1540 case NDISC_NEIGHBOUR_SOLICITATION:
1541 ndisc_recv_ns(skb);
1542 break;
1543
1544 case NDISC_NEIGHBOUR_ADVERTISEMENT:
1545 ndisc_recv_na(skb);
1546 break;
1547
1548 case NDISC_ROUTER_SOLICITATION:
1549 ndisc_recv_rs(skb);
1550 break;
1551
1552 case NDISC_ROUTER_ADVERTISEMENT:
1553 ndisc_router_discovery(skb);
1554 break;
1555
1556 case NDISC_REDIRECT:
1557 ndisc_redirect_rcv(skb);
1558 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -07001559 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001560
1561 return 0;
1562}
1563
1564static int ndisc_netdev_event(struct notifier_block *this, unsigned long event, void *ptr)
1565{
1566 struct net_device *dev = ptr;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001567 struct net *net = dev_net(dev);
Hannes Frederic Sowa5cb04432012-11-06 16:46:20 +00001568 struct inet6_dev *idev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001569
1570 switch (event) {
1571 case NETDEV_CHANGEADDR:
1572 neigh_changeaddr(&nd_tbl, dev);
Daniel Lezcano5b7c9312008-03-03 23:28:58 -08001573 fib6_run_gc(~0UL, net);
Hannes Frederic Sowa5cb04432012-11-06 16:46:20 +00001574 idev = in6_dev_get(dev);
1575 if (!idev)
1576 break;
1577 if (idev->cnf.ndisc_notify)
1578 ndisc_send_unsol_na(dev);
1579 in6_dev_put(idev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001580 break;
1581 case NETDEV_DOWN:
1582 neigh_ifdown(&nd_tbl, dev);
Daniel Lezcano5b7c9312008-03-03 23:28:58 -08001583 fib6_run_gc(~0UL, net);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001584 break;
Ben Hutchingsf47b9462011-04-15 13:46:02 +00001585 case NETDEV_NOTIFY_PEERS:
1586 ndisc_send_unsol_na(dev);
1587 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001588 default:
1589 break;
1590 }
1591
1592 return NOTIFY_DONE;
1593}
1594
1595static struct notifier_block ndisc_netdev_notifier = {
1596 .notifier_call = ndisc_netdev_event,
1597};
1598
1599#ifdef CONFIG_SYSCTL
1600static void ndisc_warn_deprecated_sysctl(struct ctl_table *ctl,
1601 const char *func, const char *dev_name)
1602{
1603 static char warncomm[TASK_COMM_LEN];
1604 static int warned;
1605 if (strcmp(warncomm, current->comm) && warned < 5) {
1606 strcpy(warncomm, current->comm);
Joe Perchesf3213832012-05-15 14:11:53 +00001607 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 -07001608 warncomm, func,
1609 dev_name, ctl->procname,
1610 dev_name, ctl->procname);
1611 warned++;
1612 }
1613}
1614
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001615int 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 -07001616{
1617 struct net_device *dev = ctl->extra1;
1618 struct inet6_dev *idev;
1619 int ret;
1620
Eric W. Biedermand12af672007-10-18 03:05:25 -07001621 if ((strcmp(ctl->procname, "retrans_time") == 0) ||
1622 (strcmp(ctl->procname, "base_reachable_time") == 0))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001623 ndisc_warn_deprecated_sysctl(ctl, "syscall", dev ? dev->name : "default");
1624
Eric W. Biedermand12af672007-10-18 03:05:25 -07001625 if (strcmp(ctl->procname, "retrans_time") == 0)
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001626 ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
Eric W. Biedermand12af672007-10-18 03:05:25 -07001627
1628 else if (strcmp(ctl->procname, "base_reachable_time") == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001629 ret = proc_dointvec_jiffies(ctl, write,
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001630 buffer, lenp, ppos);
Eric W. Biedermand12af672007-10-18 03:05:25 -07001631
1632 else if ((strcmp(ctl->procname, "retrans_time_ms") == 0) ||
YOSHIFUJI Hideakiad02ac12007-10-29 01:32:23 -07001633 (strcmp(ctl->procname, "base_reachable_time_ms") == 0))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001634 ret = proc_dointvec_ms_jiffies(ctl, write,
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001635 buffer, lenp, ppos);
Eric W. Biedermand12af672007-10-18 03:05:25 -07001636 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001637 ret = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001638
1639 if (write && ret == 0 && dev && (idev = in6_dev_get(dev)) != NULL) {
Eric W. Biedermand12af672007-10-18 03:05:25 -07001640 if (ctl->data == &idev->nd_parms->base_reachable_time)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001641 idev->nd_parms->reachable_time = neigh_rand_reach_time(idev->nd_parms->base_reachable_time);
1642 idev->tstamp = jiffies;
1643 inet6_ifinfo_notify(RTM_NEWLINK, idev);
1644 in6_dev_put(idev);
1645 }
1646 return ret;
1647}
1648
Linus Torvalds1da177e2005-04-16 15:20:36 -07001649
1650#endif
1651
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00001652static int __net_init ndisc_net_init(struct net *net)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001653{
1654 struct ipv6_pinfo *np;
1655 struct sock *sk;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001656 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001657
Denis V. Lunev1ed85162008-04-03 14:31:03 -07001658 err = inet_ctl_sock_create(&sk, PF_INET6,
1659 SOCK_RAW, IPPROTO_ICMPV6, net);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001660 if (err < 0) {
Joe Perches675418d2012-05-16 19:28:38 +00001661 ND_PRINTK(0, err,
1662 "NDISC: Failed to initialize the control socket (err %d)\n",
1663 err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001664 return err;
1665 }
1666
Denis V. Lunev1ed85162008-04-03 14:31:03 -07001667 net->ipv6.ndisc_sk = sk;
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001668
Linus Torvalds1da177e2005-04-16 15:20:36 -07001669 np = inet6_sk(sk);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001670 np->hop_limit = 255;
1671 /* Do not loopback ndisc messages */
1672 np->mc_loop = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001673
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001674 return 0;
1675}
1676
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00001677static void __net_exit ndisc_net_exit(struct net *net)
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001678{
Denis V. Lunev1ed85162008-04-03 14:31:03 -07001679 inet_ctl_sock_destroy(net->ipv6.ndisc_sk);
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001680}
1681
1682static struct pernet_operations ndisc_net_ops = {
1683 .init = ndisc_net_init,
1684 .exit = ndisc_net_exit,
1685};
1686
1687int __init ndisc_init(void)
1688{
1689 int err;
1690
1691 err = register_pernet_subsys(&ndisc_net_ops);
1692 if (err)
1693 return err;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001694 /*
1695 * Initialize the neighbour table
1696 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001697 neigh_table_init(&nd_tbl);
1698
1699#ifdef CONFIG_SYSCTL
Eric W. Biederman54716e32010-02-14 03:27:03 +00001700 err = neigh_sysctl_register(NULL, &nd_tbl.parms, "ipv6",
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001701 &ndisc_ifinfo_sysctl_change);
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001702 if (err)
1703 goto out_unregister_pernet;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001704#endif
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001705 err = register_netdevice_notifier(&ndisc_netdev_notifier);
1706 if (err)
1707 goto out_unregister_sysctl;
1708out:
1709 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001710
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001711out_unregister_sysctl:
1712#ifdef CONFIG_SYSCTL
1713 neigh_sysctl_unregister(&nd_tbl.parms);
1714out_unregister_pernet:
1715#endif
1716 unregister_pernet_subsys(&ndisc_net_ops);
1717 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001718}
1719
1720void ndisc_cleanup(void)
1721{
Dmitry Mishin36f73d02006-11-03 16:08:19 -08001722 unregister_netdevice_notifier(&ndisc_netdev_notifier);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001723#ifdef CONFIG_SYSCTL
1724 neigh_sysctl_unregister(&nd_tbl.parms);
1725#endif
1726 neigh_table_clear(&nd_tbl);
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001727 unregister_pernet_subsys(&ndisc_net_ops);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001728}