blob: 6574175795df8d6938e6c4bf62d37171dbd0be9c [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,
398 (MAX_HEADER + sizeof(struct ipv6hdr) +
Herbert Xua7ae1992011-11-18 02:20:04 +0000399 len + hlen + tlen),
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900400 1, &err);
401 if (!skb) {
Joe Perches675418d2012-05-16 19:28:38 +0000402 ND_PRINTK(0, err, "ND: %s failed to allocate an skb, err=%d\n",
403 __func__, err);
Brian Haley305d5522008-11-04 17:51:14 -0800404 return NULL;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900405 }
406
Herbert Xua7ae1992011-11-18 02:20:04 +0000407 skb_reserve(skb, hlen);
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900408 ip6_nd_hdr(sk, skb, dev, saddr, daddr, IPPROTO_ICMPV6, len);
409
410 skb->transport_header = skb->tail;
411 skb_put(skb, len);
412
413 hdr = (struct icmp6hdr *)skb_transport_header(skb);
414 memcpy(hdr, icmp6h, sizeof(*hdr));
415
416 opt = skb_transport_header(skb) + sizeof(struct icmp6hdr);
417 if (target) {
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +0000418 *(struct in6_addr *)opt = *target;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900419 opt += sizeof(*target);
420 }
421
422 if (llinfo)
423 ndisc_fill_addr_option(opt, llinfo, dev->dev_addr,
424 dev->addr_len, dev->type);
425
426 hdr->icmp6_cksum = csum_ipv6_magic(saddr, daddr, len,
427 IPPROTO_ICMPV6,
Joe Perches07f07572008-11-19 15:44:53 -0800428 csum_partial(hdr,
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900429 len, 0));
430
Brian Haley305d5522008-11-04 17:51:14 -0800431 return skb;
432}
433
YOSHIFUJI Hideakifd0ea7d2012-12-13 02:40:26 +0900434static void ndisc_send_skb(struct sk_buff *skb, struct net_device *dev,
435 struct neighbour *neigh,
436 const struct in6_addr *daddr,
437 const struct in6_addr *saddr,
438 struct icmp6hdr *icmp6h)
Brian Haley305d5522008-11-04 17:51:14 -0800439{
David S. Miller4c9483b2011-03-12 16:22:43 -0500440 struct flowi6 fl6;
Brian Haley305d5522008-11-04 17:51:14 -0800441 struct dst_entry *dst;
442 struct net *net = dev_net(dev);
443 struct sock *sk = net->ipv6.ndisc_sk;
444 struct inet6_dev *idev;
445 int err;
446 u8 type;
447
448 type = icmp6h->icmp6_type;
449
David S. Miller4c9483b2011-03-12 16:22:43 -0500450 icmpv6_flow_init(sk, &fl6, type, saddr, daddr, dev->ifindex);
David S. Miller87a11572011-12-06 17:04:13 -0500451 dst = icmp6_dst_alloc(dev, neigh, &fl6);
David S. Miller452edd52011-03-02 13:27:41 -0800452 if (IS_ERR(dst)) {
Brian Haley305d5522008-11-04 17:51:14 -0800453 kfree_skb(skb);
454 return;
455 }
456
Eric Dumazetadf30902009-06-02 05:19:30 +0000457 skb_dst_set(skb, dst);
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900458
Eric Dumazetcfdf7642011-07-27 21:13:03 +0000459 rcu_read_lock();
460 idev = __in6_dev_get(dst->dev);
Neil Hormanedf391f2009-04-27 02:45:02 -0700461 IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUT, skb->len);
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900462
Jan Engelhardtb2e0b382010-03-23 04:09:07 +0100463 err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, skb, NULL, dst->dev,
Patrick McHardy6e23ae22007-11-19 18:53:30 -0800464 dst_output);
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900465 if (!err) {
Denis V. Lunev5c5d2442008-10-08 10:33:50 -0700466 ICMP6MSGOUT_INC_STATS(net, idev, type);
Denis V. Luneva862f6a2008-10-08 10:33:06 -0700467 ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS);
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900468 }
469
Eric Dumazetcfdf7642011-07-27 21:13:03 +0000470 rcu_read_unlock();
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900471}
472
Brian Haley305d5522008-11-04 17:51:14 -0800473/*
474 * Send a Neighbour Discover packet
475 */
476static void __ndisc_send(struct net_device *dev,
477 struct neighbour *neigh,
478 const struct in6_addr *daddr,
479 const struct in6_addr *saddr,
480 struct icmp6hdr *icmp6h, const struct in6_addr *target,
481 int llinfo)
482{
483 struct sk_buff *skb;
484
485 skb = ndisc_build_skb(dev, daddr, saddr, icmp6h, target, llinfo);
486 if (!skb)
487 return;
488
489 ndisc_send_skb(skb, dev, neigh, daddr, saddr, icmp6h);
490}
491
Linus Torvalds1da177e2005-04-16 15:20:36 -0700492static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +0900493 const struct in6_addr *daddr,
494 const struct in6_addr *solicited_addr,
495 int router, int solicited, int override, int inc_opt)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700496{
497 struct in6_addr tmpaddr;
498 struct inet6_ifaddr *ifp;
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +0900499 const struct in6_addr *src_addr;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900500 struct icmp6hdr icmp6h = {
501 .icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT,
502 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700503
504 /* for anycast or proxy, solicited_addr != src_addr */
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900505 ifp = ipv6_get_ifaddr(dev_net(dev), solicited_addr, dev, 1);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900506 if (ifp) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700507 src_addr = solicited_addr;
Neil Horman95c385b2007-04-25 17:08:10 -0700508 if (ifp->flags & IFA_F_OPTIMISTIC)
509 override = 0;
stephen hemminger9f888162010-06-21 11:00:13 +0000510 inc_opt |= ifp->idev->cnf.force_tllao;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700511 in6_ifa_put(ifp);
512 } else {
Brian Haley191cd582008-08-14 15:33:21 -0700513 if (ipv6_dev_get_saddr(dev_net(dev), dev, daddr,
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900514 inet6_sk(dev_net(dev)->ipv6.ndisc_sk)->srcprefs,
YOSHIFUJI Hideaki7cbca672008-03-25 09:37:42 +0900515 &tmpaddr))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700516 return;
517 src_addr = &tmpaddr;
518 }
519
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900520 icmp6h.icmp6_router = router;
521 icmp6h.icmp6_solicited = solicited;
522 icmp6h.icmp6_override = override;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700523
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900524 __ndisc_send(dev, neigh, daddr, src_addr,
525 &icmp6h, solicited_addr,
David L Stevens14878f72007-09-16 16:52:35 -0700526 inc_opt ? ND_OPT_TARGET_LL_ADDR : 0);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900527}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700528
Ben Hutchingsf47b9462011-04-15 13:46:02 +0000529static void ndisc_send_unsol_na(struct net_device *dev)
530{
531 struct inet6_dev *idev;
532 struct inet6_ifaddr *ifa;
Ben Hutchingsf47b9462011-04-15 13:46:02 +0000533
534 idev = in6_dev_get(dev);
535 if (!idev)
536 return;
537
538 read_lock_bh(&idev->lock);
539 list_for_each_entry(ifa, &idev->addr_list, if_list) {
YOSHIFUJI Hideaki / 吉藤英明9fafd652012-11-12 07:50:17 +0000540 ndisc_send_na(dev, NULL, &in6addr_linklocal_allnodes, &ifa->addr,
Ben Hutchingsf47b9462011-04-15 13:46:02 +0000541 /*router=*/ !!idev->cnf.forwarding,
542 /*solicited=*/ false, /*override=*/ true,
543 /*inc_opt=*/ true);
544 }
545 read_unlock_bh(&idev->lock);
546
547 in6_dev_put(idev);
548}
549
Linus Torvalds1da177e2005-04-16 15:20:36 -0700550void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +0900551 const struct in6_addr *solicit,
552 const struct in6_addr *daddr, const struct in6_addr *saddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700553{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700554 struct in6_addr addr_buf;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900555 struct icmp6hdr icmp6h = {
556 .icmp6_type = NDISC_NEIGHBOUR_SOLICITATION,
557 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700558
559 if (saddr == NULL) {
Neil Horman95c385b2007-04-25 17:08:10 -0700560 if (ipv6_get_lladdr(dev, &addr_buf,
561 (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700562 return;
563 saddr = &addr_buf;
564 }
565
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900566 __ndisc_send(dev, neigh, daddr, saddr,
567 &icmp6h, solicit,
David L Stevens14878f72007-09-16 16:52:35 -0700568 !ipv6_addr_any(saddr) ? ND_OPT_SOURCE_LL_ADDR : 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569}
570
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +0900571void ndisc_send_rs(struct net_device *dev, const struct in6_addr *saddr,
572 const struct in6_addr *daddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700573{
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900574 struct icmp6hdr icmp6h = {
575 .icmp6_type = NDISC_ROUTER_SOLICITATION,
576 };
Neil Horman95c385b2007-04-25 17:08:10 -0700577 int send_sllao = dev->addr_len;
Neil Horman95c385b2007-04-25 17:08:10 -0700578
579#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
580 /*
581 * According to section 2.2 of RFC 4429, we must not
582 * send router solicitations with a sllao from
583 * optimistic addresses, but we may send the solicitation
584 * if we don't include the sllao. So here we check
585 * if our address is optimistic, and if so, we
Joe Perchesbea85192007-12-20 14:01:35 -0800586 * suppress the inclusion of the sllao.
Neil Horman95c385b2007-04-25 17:08:10 -0700587 */
588 if (send_sllao) {
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900589 struct inet6_ifaddr *ifp = ipv6_get_ifaddr(dev_net(dev), saddr,
Daniel Lezcano1cab3da2008-01-10 22:44:09 -0800590 dev, 1);
Neil Horman95c385b2007-04-25 17:08:10 -0700591 if (ifp) {
592 if (ifp->flags & IFA_F_OPTIMISTIC) {
YOSHIFUJI Hideakica043562007-02-28 23:13:20 +0900593 send_sllao = 0;
Neil Horman95c385b2007-04-25 17:08:10 -0700594 }
YOSHIFUJI Hideakica043562007-02-28 23:13:20 +0900595 in6_ifa_put(ifp);
Neil Horman95c385b2007-04-25 17:08:10 -0700596 } else {
597 send_sllao = 0;
598 }
599 }
600#endif
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900601 __ndisc_send(dev, NULL, daddr, saddr,
602 &icmp6h, NULL,
David L Stevens14878f72007-09-16 16:52:35 -0700603 send_sllao ? ND_OPT_SOURCE_LL_ADDR : 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700604}
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900605
Linus Torvalds1da177e2005-04-16 15:20:36 -0700606
607static void ndisc_error_report(struct neighbour *neigh, struct sk_buff *skb)
608{
609 /*
610 * "The sender MUST return an ICMP
611 * destination unreachable"
612 */
613 dst_link_failure(skb);
614 kfree_skb(skb);
615}
616
617/* Called with locked neigh: either read or both */
618
619static void ndisc_solicit(struct neighbour *neigh, struct sk_buff *skb)
620{
621 struct in6_addr *saddr = NULL;
622 struct in6_addr mcaddr;
623 struct net_device *dev = neigh->dev;
624 struct in6_addr *target = (struct in6_addr *)&neigh->primary_key;
625 int probes = atomic_read(&neigh->probes);
626
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900627 if (skb && ipv6_chk_addr(dev_net(dev), &ipv6_hdr(skb)->saddr, dev, 1))
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -0700628 saddr = &ipv6_hdr(skb)->saddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700629
630 if ((probes -= neigh->parms->ucast_probes) < 0) {
631 if (!(neigh->nud_state & NUD_VALID)) {
Joe Perches675418d2012-05-16 19:28:38 +0000632 ND_PRINTK(1, dbg,
633 "%s: trying to ucast probe in NUD_INVALID: %pI6\n",
634 __func__, target);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700635 }
636 ndisc_send_ns(dev, neigh, target, target, saddr);
637 } else if ((probes -= neigh->parms->app_probes) < 0) {
638#ifdef CONFIG_ARPD
639 neigh_app_ns(neigh);
640#endif
641 } else {
642 addrconf_addr_solict_mult(target, &mcaddr);
643 ndisc_send_ns(dev, NULL, target, &mcaddr, saddr);
644 }
645}
646
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900647static int pndisc_is_router(const void *pkey,
648 struct net_device *dev)
Pavel Emelyanovfa86d322008-03-24 14:48:59 -0700649{
650 struct pneigh_entry *n;
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900651 int ret = -1;
Pavel Emelyanovfa86d322008-03-24 14:48:59 -0700652
653 read_lock_bh(&nd_tbl.lock);
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900654 n = __pneigh_lookup(&nd_tbl, dev_net(dev), pkey, dev);
655 if (n)
656 ret = !!(n->flags & NTF_ROUTER);
Pavel Emelyanovfa86d322008-03-24 14:48:59 -0700657 read_unlock_bh(&nd_tbl.lock);
658
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900659 return ret;
Pavel Emelyanovfa86d322008-03-24 14:48:59 -0700660}
661
Linus Torvalds1da177e2005-04-16 15:20:36 -0700662static void ndisc_recv_ns(struct sk_buff *skb)
663{
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -0700664 struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb);
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000665 const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
666 const struct in6_addr *daddr = &ipv6_hdr(skb)->daddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700667 u8 *lladdr = NULL;
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -0700668 u32 ndoptlen = skb->tail - (skb->transport_header +
669 offsetof(struct nd_msg, opt));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700670 struct ndisc_options ndopts;
671 struct net_device *dev = skb->dev;
672 struct inet6_ifaddr *ifp;
673 struct inet6_dev *idev = NULL;
674 struct neighbour *neigh;
675 int dad = ipv6_addr_any(saddr);
Eric Dumazeta50feda2012-05-18 18:57:34 +0000676 bool inc;
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900677 int is_router = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700678
679 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 */
688 if (dad &&
689 !(daddr->s6_addr32[0] == htonl(0xff020000) &&
690 daddr->s6_addr32[1] == htonl(0x00000000) &&
691 daddr->s6_addr32[2] == htonl(0x00000001) &&
692 daddr->s6_addr [12] == 0xff )) {
Joe Perches675418d2012-05-16 19:28:38 +0000693 ND_PRINTK(2, warn, "NS: bad DAD packet (wrong destination)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700694 return;
695 }
696
697 if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts)) {
Joe Perches675418d2012-05-16 19:28:38 +0000698 ND_PRINTK(2, warn, "NS: invalid ND options\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700699 return;
700 }
701
702 if (ndopts.nd_opts_src_lladdr) {
703 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr, dev);
704 if (!lladdr) {
Joe Perches675418d2012-05-16 19:28:38 +0000705 ND_PRINTK(2, warn,
706 "NS: invalid link-layer address length\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700707 return;
708 }
709
710 /* RFC2461 7.1.1:
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900711 * If the IP source address is the unspecified address,
712 * there MUST NOT be source link-layer address option
Linus Torvalds1da177e2005-04-16 15:20:36 -0700713 * in the message.
714 */
715 if (dad) {
Joe Perches675418d2012-05-16 19:28:38 +0000716 ND_PRINTK(2, warn,
717 "NS: bad DAD packet (link-layer address option)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700718 return;
719 }
720 }
721
722 inc = ipv6_addr_is_multicast(daddr);
723
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900724 ifp = ipv6_get_ifaddr(dev_net(dev), &msg->target, dev, 1);
Daniel Lezcanoa18bc692008-03-07 11:14:49 -0800725 if (ifp) {
Neil Horman95c385b2007-04-25 17:08:10 -0700726
727 if (ifp->flags & (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)) {
728 if (dad) {
Neil Horman95c385b2007-04-25 17:08:10 -0700729 /*
730 * We are colliding with another node
731 * who is doing DAD
732 * so fail our DAD process
733 */
734 addrconf_dad_failure(ifp);
Denis V. Lunev9e3be4b2007-09-11 11:04:49 +0200735 return;
Neil Horman95c385b2007-04-25 17:08:10 -0700736 } else {
737 /*
738 * This is not a dad solicitation.
739 * If we are an optimistic node,
740 * we should respond.
741 * Otherwise, we should ignore it.
742 */
743 if (!(ifp->flags & IFA_F_OPTIMISTIC))
744 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700745 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700746 }
747
748 idev = ifp->idev;
749 } else {
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700750 struct net *net = dev_net(dev);
751
Linus Torvalds1da177e2005-04-16 15:20:36 -0700752 idev = in6_dev_get(dev);
753 if (!idev) {
754 /* XXX: count this drop? */
755 return;
756 }
757
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700758 if (ipv6_chk_acast_addr(net, dev, &msg->target) ||
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900759 (idev->cnf.forwarding &&
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700760 (net->ipv6.devconf_all->proxy_ndp || idev->cnf.proxy_ndp) &&
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900761 (is_router = pndisc_is_router(&msg->target, dev)) >= 0)) {
Patrick McHardya61bbcf2005-08-14 17:24:31 -0700762 if (!(NEIGH_CB(skb)->flags & LOCALLY_ENQUEUED) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700763 skb->pkt_type != PACKET_HOST &&
764 inc != 0 &&
765 idev->nd_parms->proxy_delay != 0) {
766 /*
767 * for anycast or proxy,
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900768 * sender should delay its response
769 * by a random time between 0 and
Linus Torvalds1da177e2005-04-16 15:20:36 -0700770 * MAX_ANYCAST_DELAY_TIME seconds.
771 * (RFC2461) -- yoshfuji
772 */
773 struct sk_buff *n = skb_clone(skb, GFP_ATOMIC);
774 if (n)
775 pneigh_enqueue(&nd_tbl, idev->nd_parms, n);
776 goto out;
777 }
778 } else
779 goto out;
780 }
781
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900782 if (is_router < 0)
783 is_router = !!idev->cnf.forwarding;
Ville Nuorvala62dd9312006-09-22 14:43:19 -0700784
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785 if (dad) {
YOSHIFUJI Hideakif3ee4012008-04-10 15:42:11 +0900786 ndisc_send_na(dev, NULL, &in6addr_linklocal_allnodes, &msg->target,
Ville Nuorvala62dd9312006-09-22 14:43:19 -0700787 is_router, 0, (ifp != NULL), 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700788 goto out;
789 }
790
791 if (inc)
792 NEIGH_CACHE_STAT_INC(&nd_tbl, rcv_probes_mcast);
793 else
794 NEIGH_CACHE_STAT_INC(&nd_tbl, rcv_probes_ucast);
795
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900796 /*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700797 * update / create cache entry
798 * for the source address
799 */
800 neigh = __neigh_lookup(&nd_tbl, saddr, dev,
801 !inc || lladdr || !dev->addr_len);
802 if (neigh)
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900803 neigh_update(neigh, lladdr, NUD_STALE,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700804 NEIGH_UPDATE_F_WEAK_OVERRIDE|
805 NEIGH_UPDATE_F_OVERRIDE);
Stephen Hemminger3b04ddd2007-10-09 01:40:57 -0700806 if (neigh || !dev->header_ops) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700807 ndisc_send_na(dev, neigh, saddr, &msg->target,
Ville Nuorvala62dd9312006-09-22 14:43:19 -0700808 is_router,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700809 1, (ifp != NULL && inc), inc);
810 if (neigh)
811 neigh_release(neigh);
812 }
813
814out:
815 if (ifp)
816 in6_ifa_put(ifp);
817 else
818 in6_dev_put(idev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700819}
820
821static void ndisc_recv_na(struct sk_buff *skb)
822{
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -0700823 struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb);
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000824 const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
825 const struct in6_addr *daddr = &ipv6_hdr(skb)->daddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700826 u8 *lladdr = NULL;
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -0700827 u32 ndoptlen = skb->tail - (skb->transport_header +
828 offsetof(struct nd_msg, opt));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700829 struct ndisc_options ndopts;
830 struct net_device *dev = skb->dev;
831 struct inet6_ifaddr *ifp;
832 struct neighbour *neigh;
833
834 if (skb->len < sizeof(struct nd_msg)) {
Joe Perches675418d2012-05-16 19:28:38 +0000835 ND_PRINTK(2, warn, "NA: packet too short\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700836 return;
837 }
838
839 if (ipv6_addr_is_multicast(&msg->target)) {
Joe Perches675418d2012-05-16 19:28:38 +0000840 ND_PRINTK(2, warn, "NA: target address is multicast\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700841 return;
842 }
843
844 if (ipv6_addr_is_multicast(daddr) &&
845 msg->icmph.icmp6_solicited) {
Joe Perches675418d2012-05-16 19:28:38 +0000846 ND_PRINTK(2, warn, "NA: solicited NA is multicasted\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700847 return;
848 }
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900849
Linus Torvalds1da177e2005-04-16 15:20:36 -0700850 if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts)) {
Joe Perches675418d2012-05-16 19:28:38 +0000851 ND_PRINTK(2, warn, "NS: invalid ND option\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700852 return;
853 }
854 if (ndopts.nd_opts_tgt_lladdr) {
855 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr, dev);
856 if (!lladdr) {
Joe Perches675418d2012-05-16 19:28:38 +0000857 ND_PRINTK(2, warn,
858 "NA: invalid link-layer address length\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700859 return;
860 }
861 }
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900862 ifp = ipv6_get_ifaddr(dev_net(dev), &msg->target, dev, 1);
Daniel Lezcanoa18bc692008-03-07 11:14:49 -0800863 if (ifp) {
Daniel Walterbd015922011-04-13 21:09:25 +0000864 if (skb->pkt_type != PACKET_LOOPBACK
865 && (ifp->flags & IFA_F_TENTATIVE)) {
866 addrconf_dad_failure(ifp);
867 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700868 }
869 /* What should we make now? The advertisement
870 is invalid, but ndisc specs say nothing
871 about it. It could be misconfiguration, or
872 an smart proxy agent tries to help us :-)
Jan Sembera24fc7b82008-12-09 15:48:32 -0800873
874 We should not print the error if NA has been
875 received from loopback - it is just our own
876 unsolicited advertisement.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700877 */
Jan Sembera24fc7b82008-12-09 15:48:32 -0800878 if (skb->pkt_type != PACKET_LOOPBACK)
Joe Perches675418d2012-05-16 19:28:38 +0000879 ND_PRINTK(1, warn,
880 "NA: someone advertises our address %pI6 on %s!\n",
881 &ifp->addr, ifp->idev->dev->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700882 in6_ifa_put(ifp);
883 return;
884 }
885 neigh = neigh_lookup(&nd_tbl, &msg->target, dev);
886
887 if (neigh) {
888 u8 old_flags = neigh->flags;
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700889 struct net *net = dev_net(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700890
891 if (neigh->nud_state & NUD_FAILED)
892 goto out;
893
Ville Nuorvala5f3e6e92006-09-22 14:42:46 -0700894 /*
895 * Don't update the neighbor cache entry on a proxy NA from
896 * ourselves because either the proxied node is off link or it
897 * has already sent a NA to us.
898 */
899 if (lladdr && !memcmp(lladdr, dev->dev_addr, dev->addr_len) &&
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700900 net->ipv6.devconf_all->forwarding && net->ipv6.devconf_all->proxy_ndp &&
901 pneigh_lookup(&nd_tbl, net, &msg->target, dev, 0)) {
Nicolas Dichtelb20b6d92012-11-07 05:05:38 +0000902 /* XXX: idev->cnf.proxy_ndp */
Ville Nuorvala5f3e6e92006-09-22 14:42:46 -0700903 goto out;
YOSHIFUJI Hideakifbea49e2006-09-22 14:43:49 -0700904 }
Ville Nuorvala5f3e6e92006-09-22 14:42:46 -0700905
Linus Torvalds1da177e2005-04-16 15:20:36 -0700906 neigh_update(neigh, lladdr,
907 msg->icmph.icmp6_solicited ? NUD_REACHABLE : NUD_STALE,
908 NEIGH_UPDATE_F_WEAK_OVERRIDE|
909 (msg->icmph.icmp6_override ? NEIGH_UPDATE_F_OVERRIDE : 0)|
910 NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
911 (msg->icmph.icmp6_router ? NEIGH_UPDATE_F_ISROUTER : 0));
912
913 if ((old_flags & ~neigh->flags) & NTF_ROUTER) {
914 /*
915 * Change: router to host
916 */
917 struct rt6_info *rt;
918 rt = rt6_get_dflt_router(saddr, dev);
919 if (rt)
Thomas Grafe0a1ad732006-08-22 00:00:21 -0700920 ip6_del_rt(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700921 }
922
923out:
924 neigh_release(neigh);
925 }
926}
927
928static void ndisc_recv_rs(struct sk_buff *skb)
929{
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -0700930 struct rs_msg *rs_msg = (struct rs_msg *)skb_transport_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700931 unsigned long ndoptlen = skb->len - sizeof(*rs_msg);
932 struct neighbour *neigh;
933 struct inet6_dev *idev;
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000934 const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700935 struct ndisc_options ndopts;
936 u8 *lladdr = NULL;
937
938 if (skb->len < sizeof(*rs_msg))
939 return;
940
Eric Dumazetcfdf7642011-07-27 21:13:03 +0000941 idev = __in6_dev_get(skb->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700942 if (!idev) {
Joe Perches675418d2012-05-16 19:28:38 +0000943 ND_PRINTK(1, err, "RS: can't find in6 device\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700944 return;
945 }
946
947 /* Don't accept RS if we're not in router mode */
948 if (!idev->cnf.forwarding)
949 goto out;
950
951 /*
952 * Don't update NCE if src = ::;
953 * this implies that the source node has no ip address assigned yet.
954 */
955 if (ipv6_addr_any(saddr))
956 goto out;
957
958 /* Parse ND options */
959 if (!ndisc_parse_options(rs_msg->opt, ndoptlen, &ndopts)) {
Joe Perches675418d2012-05-16 19:28:38 +0000960 ND_PRINTK(2, notice, "NS: invalid ND option, ignored\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700961 goto out;
962 }
963
964 if (ndopts.nd_opts_src_lladdr) {
965 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr,
966 skb->dev);
967 if (!lladdr)
968 goto out;
969 }
970
971 neigh = __neigh_lookup(&nd_tbl, saddr, skb->dev, 1);
972 if (neigh) {
973 neigh_update(neigh, lladdr, NUD_STALE,
974 NEIGH_UPDATE_F_WEAK_OVERRIDE|
975 NEIGH_UPDATE_F_OVERRIDE|
976 NEIGH_UPDATE_F_OVERRIDE_ISROUTER);
977 neigh_release(neigh);
978 }
979out:
Eric Dumazetcfdf7642011-07-27 21:13:03 +0000980 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700981}
982
Pierre Ynard31910572007-10-10 21:22:05 -0700983static void ndisc_ra_useropt(struct sk_buff *ra, struct nd_opt_hdr *opt)
984{
985 struct icmp6hdr *icmp6h = (struct icmp6hdr *)skb_transport_header(ra);
986 struct sk_buff *skb;
987 struct nlmsghdr *nlh;
988 struct nduseroptmsg *ndmsg;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900989 struct net *net = dev_net(ra->dev);
Pierre Ynard31910572007-10-10 21:22:05 -0700990 int err;
991 int base_size = NLMSG_ALIGN(sizeof(struct nduseroptmsg)
992 + (opt->nd_opt_len << 3));
993 size_t msg_size = base_size + nla_total_size(sizeof(struct in6_addr));
994
995 skb = nlmsg_new(msg_size, GFP_ATOMIC);
996 if (skb == NULL) {
997 err = -ENOBUFS;
998 goto errout;
999 }
1000
1001 nlh = nlmsg_put(skb, 0, 0, RTM_NEWNDUSEROPT, base_size, 0);
1002 if (nlh == NULL) {
1003 goto nla_put_failure;
1004 }
1005
1006 ndmsg = nlmsg_data(nlh);
1007 ndmsg->nduseropt_family = AF_INET6;
Pierre Ynarddbb2ed22007-11-12 17:58:35 -08001008 ndmsg->nduseropt_ifindex = ra->dev->ifindex;
Pierre Ynard31910572007-10-10 21:22:05 -07001009 ndmsg->nduseropt_icmp_type = icmp6h->icmp6_type;
1010 ndmsg->nduseropt_icmp_code = icmp6h->icmp6_code;
1011 ndmsg->nduseropt_opts_len = opt->nd_opt_len << 3;
1012
1013 memcpy(ndmsg + 1, opt, opt->nd_opt_len << 3);
1014
David S. Millerc78679e2012-04-01 20:27:33 -04001015 if (nla_put(skb, NDUSEROPT_SRCADDR, sizeof(struct in6_addr),
1016 &ipv6_hdr(ra)->saddr))
1017 goto nla_put_failure;
Pierre Ynard31910572007-10-10 21:22:05 -07001018 nlmsg_end(skb, nlh);
1019
Pablo Neira Ayuso1ce85fe2009-02-24 23:18:28 -08001020 rtnl_notify(skb, net, 0, RTNLGRP_ND_USEROPT, NULL, GFP_ATOMIC);
Pierre Ynard31910572007-10-10 21:22:05 -07001021 return;
1022
1023nla_put_failure:
1024 nlmsg_free(skb);
1025 err = -EMSGSIZE;
1026errout:
Daniel Lezcanoa18bc692008-03-07 11:14:49 -08001027 rtnl_set_sk_err(net, RTNLGRP_ND_USEROPT, err);
Pierre Ynard31910572007-10-10 21:22:05 -07001028}
1029
Linus Torvalds1da177e2005-04-16 15:20:36 -07001030static void ndisc_router_discovery(struct sk_buff *skb)
1031{
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -07001032 struct ra_msg *ra_msg = (struct ra_msg *)skb_transport_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001033 struct neighbour *neigh = NULL;
1034 struct inet6_dev *in6_dev;
YOSHIFUJI Hideaki65f5c7c2006-03-20 16:55:08 -08001035 struct rt6_info *rt = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001036 int lifetime;
1037 struct ndisc_options ndopts;
1038 int optlen;
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001039 unsigned int pref = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001040
1041 __u8 * opt = (__u8 *)(ra_msg + 1);
1042
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -07001043 optlen = (skb->tail - skb->transport_header) - sizeof(struct ra_msg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001044
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001045 if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) {
Joe Perches675418d2012-05-16 19:28:38 +00001046 ND_PRINTK(2, warn, "RA: source address is not link-local\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001047 return;
1048 }
1049 if (optlen < 0) {
Joe Perches675418d2012-05-16 19:28:38 +00001050 ND_PRINTK(2, warn, "RA: packet too short\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001051 return;
1052 }
1053
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001054#ifdef CONFIG_IPV6_NDISC_NODETYPE
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001055 if (skb->ndisc_nodetype == NDISC_NODETYPE_HOST) {
Joe Perches675418d2012-05-16 19:28:38 +00001056 ND_PRINTK(2, warn, "RA: from host or unauthorized router\n");
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001057 return;
1058 }
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001059#endif
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001060
Linus Torvalds1da177e2005-04-16 15:20:36 -07001061 /*
1062 * set the RA_RECV flag in the interface
1063 */
1064
Eric Dumazetcfdf7642011-07-27 21:13:03 +00001065 in6_dev = __in6_dev_get(skb->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001066 if (in6_dev == NULL) {
Joe Perches675418d2012-05-16 19:28:38 +00001067 ND_PRINTK(0, err, "RA: can't find inet6 device for %s\n",
1068 skb->dev->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001069 return;
1070 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001071
1072 if (!ndisc_parse_options(opt, optlen, &ndopts)) {
Joe Perches675418d2012-05-16 19:28:38 +00001073 ND_PRINTK(2, warn, "RA: invalid ND options\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001074 return;
1075 }
1076
Shmulik Ladkaniaeaf6e92012-11-30 10:25:59 +00001077 if (!ipv6_accept_ra(in6_dev))
David Ward31ce8c72009-08-29 00:04:09 -07001078 goto skip_linkparms;
1079
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001080#ifdef CONFIG_IPV6_NDISC_NODETYPE
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001081 /* skip link-specific parameters from interior routers */
1082 if (skb->ndisc_nodetype == NDISC_NODETYPE_NODEFAULT)
1083 goto skip_linkparms;
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001084#endif
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001085
Linus Torvalds1da177e2005-04-16 15:20:36 -07001086 if (in6_dev->if_flags & IF_RS_SENT) {
1087 /*
1088 * flag that an RA was received after an RS was sent
1089 * out on this interface.
1090 */
1091 in6_dev->if_flags |= IF_RA_RCVD;
1092 }
1093
1094 /*
1095 * Remember the managed/otherconf flags from most recently
1096 * received RA message (RFC 2462) -- yoshfuji
1097 */
1098 in6_dev->if_flags = (in6_dev->if_flags & ~(IF_RA_MANAGED |
1099 IF_RA_OTHERCONF)) |
1100 (ra_msg->icmph.icmp6_addrconf_managed ?
1101 IF_RA_MANAGED : 0) |
1102 (ra_msg->icmph.icmp6_addrconf_other ?
1103 IF_RA_OTHERCONF : 0);
1104
YOSHIFUJI Hideaki65f5c7c2006-03-20 16:55:08 -08001105 if (!in6_dev->cnf.accept_ra_defrtr)
1106 goto skip_defrtr;
1107
Andreas Hofmeister9f562202011-10-24 19:13:15 -04001108 if (ipv6_chk_addr(dev_net(in6_dev->dev), &ipv6_hdr(skb)->saddr, NULL, 0))
1109 goto skip_defrtr;
1110
Linus Torvalds1da177e2005-04-16 15:20:36 -07001111 lifetime = ntohs(ra_msg->icmph.icmp6_rt_lifetime);
1112
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001113#ifdef CONFIG_IPV6_ROUTER_PREF
1114 pref = ra_msg->icmph.icmp6_router_pref;
1115 /* 10b is handled as if it were 00b (medium) */
YOSHIFUJI Hideaki930d6ff2006-03-20 17:05:30 -08001116 if (pref == ICMPV6_ROUTER_PREF_INVALID ||
YOSHIFUJI Hideaki6d5b78c2007-06-22 16:07:04 -07001117 !in6_dev->cnf.accept_ra_rtr_pref)
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001118 pref = ICMPV6_ROUTER_PREF_MEDIUM;
1119#endif
1120
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001121 rt = rt6_get_dflt_router(&ipv6_hdr(skb)->saddr, skb->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001122
David S. Millereb857182012-01-27 15:07:56 -08001123 if (rt) {
1124 neigh = dst_neigh_lookup(&rt->dst, &ipv6_hdr(skb)->saddr);
1125 if (!neigh) {
Joe Perches675418d2012-05-16 19:28:38 +00001126 ND_PRINTK(0, err,
1127 "RA: %s got default router without neighbour\n",
1128 __func__);
Amerigo Wang94e187c2012-10-29 00:13:19 +00001129 ip6_rt_put(rt);
David S. Millereb857182012-01-27 15:07:56 -08001130 return;
1131 }
1132 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001133 if (rt && lifetime == 0) {
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001134 ip6_del_rt(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001135 rt = NULL;
1136 }
1137
1138 if (rt == NULL && lifetime) {
Joe Perches675418d2012-05-16 19:28:38 +00001139 ND_PRINTK(3, dbg, "RA: adding default router\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001140
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001141 rt = rt6_add_dflt_router(&ipv6_hdr(skb)->saddr, skb->dev, pref);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001142 if (rt == NULL) {
Joe Perches675418d2012-05-16 19:28:38 +00001143 ND_PRINTK(0, err,
1144 "RA: %s failed to add default route\n",
1145 __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001146 return;
1147 }
1148
David S. Millereb857182012-01-27 15:07:56 -08001149 neigh = dst_neigh_lookup(&rt->dst, &ipv6_hdr(skb)->saddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001150 if (neigh == NULL) {
Joe Perches675418d2012-05-16 19:28:38 +00001151 ND_PRINTK(0, err,
1152 "RA: %s got default router without neighbour\n",
1153 __func__);
Amerigo Wang94e187c2012-10-29 00:13:19 +00001154 ip6_rt_put(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001155 return;
1156 }
1157 neigh->flags |= NTF_ROUTER;
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001158 } else if (rt) {
Pedro Ribeiro22441cf2008-10-15 15:47:49 -07001159 rt->rt6i_flags = (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001160 }
1161
1162 if (rt)
Gao feng1716a962012-04-06 00:13:10 +00001163 rt6_set_expires(rt, jiffies + (HZ * lifetime));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001164 if (ra_msg->icmph.icmp6_hop_limit) {
1165 in6_dev->cnf.hop_limit = ra_msg->icmph.icmp6_hop_limit;
1166 if (rt)
David S. Millerdefb3512010-12-08 21:16:57 -08001167 dst_metric_set(&rt->dst, RTAX_HOPLIMIT,
1168 ra_msg->icmph.icmp6_hop_limit);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001169 }
1170
YOSHIFUJI Hideaki65f5c7c2006-03-20 16:55:08 -08001171skip_defrtr:
1172
Linus Torvalds1da177e2005-04-16 15:20:36 -07001173 /*
1174 * Update Reachable Time and Retrans Timer
1175 */
1176
1177 if (in6_dev->nd_parms) {
1178 unsigned long rtime = ntohl(ra_msg->retrans_timer);
1179
1180 if (rtime && rtime/1000 < MAX_SCHEDULE_TIMEOUT/HZ) {
1181 rtime = (rtime*HZ)/1000;
1182 if (rtime < HZ/10)
1183 rtime = HZ/10;
1184 in6_dev->nd_parms->retrans_time = rtime;
1185 in6_dev->tstamp = jiffies;
1186 inet6_ifinfo_notify(RTM_NEWLINK, in6_dev);
1187 }
1188
1189 rtime = ntohl(ra_msg->reachable_time);
1190 if (rtime && rtime/1000 < MAX_SCHEDULE_TIMEOUT/(3*HZ)) {
1191 rtime = (rtime*HZ)/1000;
1192
1193 if (rtime < HZ/10)
1194 rtime = HZ/10;
1195
1196 if (rtime != in6_dev->nd_parms->base_reachable_time) {
1197 in6_dev->nd_parms->base_reachable_time = rtime;
1198 in6_dev->nd_parms->gc_staletime = 3 * rtime;
1199 in6_dev->nd_parms->reachable_time = neigh_rand_reach_time(rtime);
1200 in6_dev->tstamp = jiffies;
1201 inet6_ifinfo_notify(RTM_NEWLINK, in6_dev);
1202 }
1203 }
1204 }
1205
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001206skip_linkparms:
1207
Linus Torvalds1da177e2005-04-16 15:20:36 -07001208 /*
1209 * Process options.
1210 */
1211
1212 if (!neigh)
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001213 neigh = __neigh_lookup(&nd_tbl, &ipv6_hdr(skb)->saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001214 skb->dev, 1);
1215 if (neigh) {
1216 u8 *lladdr = NULL;
1217 if (ndopts.nd_opts_src_lladdr) {
1218 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr,
1219 skb->dev);
1220 if (!lladdr) {
Joe Perches675418d2012-05-16 19:28:38 +00001221 ND_PRINTK(2, warn,
1222 "RA: invalid link-layer address length\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001223 goto out;
1224 }
1225 }
1226 neigh_update(neigh, lladdr, NUD_STALE,
1227 NEIGH_UPDATE_F_WEAK_OVERRIDE|
1228 NEIGH_UPDATE_F_OVERRIDE|
1229 NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
1230 NEIGH_UPDATE_F_ISROUTER);
1231 }
1232
Shmulik Ladkaniaeaf6e92012-11-30 10:25:59 +00001233 if (!ipv6_accept_ra(in6_dev))
David Ward31ce8c72009-08-29 00:04:09 -07001234 goto out;
1235
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001236#ifdef CONFIG_IPV6_ROUTE_INFO
Andreas Hofmeister9f562202011-10-24 19:13:15 -04001237 if (ipv6_chk_addr(dev_net(in6_dev->dev), &ipv6_hdr(skb)->saddr, NULL, 0))
1238 goto skip_routeinfo;
1239
YOSHIFUJI Hideaki09c884d2006-03-20 17:07:03 -08001240 if (in6_dev->cnf.accept_ra_rtr_pref && ndopts.nd_opts_ri) {
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001241 struct nd_opt_hdr *p;
1242 for (p = ndopts.nd_opts_ri;
1243 p;
1244 p = ndisc_next_option(p, ndopts.nd_opts_ri_end)) {
YOSHIFUJI Hideaki6294e002008-03-15 23:56:52 -04001245 struct route_info *ri = (struct route_info *)p;
1246#ifdef CONFIG_IPV6_NDISC_NODETYPE
1247 if (skb->ndisc_nodetype == NDISC_NODETYPE_NODEFAULT &&
1248 ri->prefix_len == 0)
1249 continue;
1250#endif
1251 if (ri->prefix_len > in6_dev->cnf.accept_ra_rt_info_max_plen)
YOSHIFUJI Hideaki09c884d2006-03-20 17:07:03 -08001252 continue;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001253 rt6_route_rcv(skb->dev, (u8*)p, (p->nd_opt_len) << 3,
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001254 &ipv6_hdr(skb)->saddr);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001255 }
1256 }
Andreas Hofmeister9f562202011-10-24 19:13:15 -04001257
1258skip_routeinfo:
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001259#endif
1260
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001261#ifdef CONFIG_IPV6_NDISC_NODETYPE
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001262 /* skip link-specific ndopts from interior routers */
1263 if (skb->ndisc_nodetype == NDISC_NODETYPE_NODEFAULT)
1264 goto out;
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001265#endif
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001266
YOSHIFUJI Hideakic4fd30e2006-03-20 16:55:26 -08001267 if (in6_dev->cnf.accept_ra_pinfo && ndopts.nd_opts_pi) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001268 struct nd_opt_hdr *p;
1269 for (p = ndopts.nd_opts_pi;
1270 p;
1271 p = ndisc_next_option(p, ndopts.nd_opts_pi_end)) {
Neil Hormane6bff992012-01-04 10:49:15 +00001272 addrconf_prefix_rcv(skb->dev, (u8 *)p,
1273 (p->nd_opt_len) << 3,
1274 ndopts.nd_opts_src_lladdr != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001275 }
1276 }
1277
1278 if (ndopts.nd_opts_mtu) {
Al Viroe69a4ad2006-11-14 20:56:00 -08001279 __be32 n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001280 u32 mtu;
1281
Al Viroe69a4ad2006-11-14 20:56:00 -08001282 memcpy(&n, ((u8*)(ndopts.nd_opts_mtu+1))+2, sizeof(mtu));
1283 mtu = ntohl(n);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001284
1285 if (mtu < IPV6_MIN_MTU || mtu > skb->dev->mtu) {
Joe Perches675418d2012-05-16 19:28:38 +00001286 ND_PRINTK(2, warn, "RA: invalid mtu: %d\n", mtu);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001287 } else if (in6_dev->cnf.mtu6 != mtu) {
1288 in6_dev->cnf.mtu6 = mtu;
1289
1290 if (rt)
David S. Millerdefb3512010-12-08 21:16:57 -08001291 dst_metric_set(&rt->dst, RTAX_MTU, mtu);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001292
1293 rt6_mtu_change(skb->dev, mtu);
1294 }
1295 }
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001296
Pierre Ynard31910572007-10-10 21:22:05 -07001297 if (ndopts.nd_useropts) {
YOSHIFUJI Hideaki61cf46a2008-01-22 17:32:53 +09001298 struct nd_opt_hdr *p;
1299 for (p = ndopts.nd_useropts;
1300 p;
1301 p = ndisc_next_useropt(p, ndopts.nd_useropts_end)) {
1302 ndisc_ra_useropt(skb, p);
Pierre Ynard31910572007-10-10 21:22:05 -07001303 }
1304 }
1305
Linus Torvalds1da177e2005-04-16 15:20:36 -07001306 if (ndopts.nd_opts_tgt_lladdr || ndopts.nd_opts_rh) {
Joe Perches675418d2012-05-16 19:28:38 +00001307 ND_PRINTK(2, warn, "RA: invalid RA options\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001308 }
1309out:
Amerigo Wang94e187c2012-10-29 00:13:19 +00001310 ip6_rt_put(rt);
David S. Millereb857182012-01-27 15:07:56 -08001311 if (neigh)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001312 neigh_release(neigh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001313}
1314
1315static void ndisc_redirect_rcv(struct sk_buff *skb)
1316{
Duan Jiong093d04d2012-12-14 02:59:59 +00001317 u8 *hdr;
1318 struct ndisc_options ndopts;
1319 struct rd_msg *msg = (struct rd_msg *)skb_transport_header(skb);
1320 u32 ndoptlen = skb->tail - (skb->transport_header +
1321 offsetof(struct rd_msg, opt));
1322
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001323#ifdef CONFIG_IPV6_NDISC_NODETYPE
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001324 switch (skb->ndisc_nodetype) {
1325 case NDISC_NODETYPE_HOST:
1326 case NDISC_NODETYPE_NODEFAULT:
Joe Perches675418d2012-05-16 19:28:38 +00001327 ND_PRINTK(2, warn,
1328 "Redirect: from host or unauthorized router\n");
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001329 return;
1330 }
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001331#endif
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001332
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001333 if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) {
Joe Perches675418d2012-05-16 19:28:38 +00001334 ND_PRINTK(2, warn,
1335 "Redirect: source address is not link-local\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001336 return;
1337 }
1338
Duan Jiong093d04d2012-12-14 02:59:59 +00001339 if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts))
1340 return;
1341
1342 if (!ndopts.nd_opts_rh)
1343 return;
1344
1345 hdr = (u8 *)ndopts.nd_opts_rh;
1346 hdr += 8;
1347 if (!pskb_pull(skb, hdr - skb_transport_header(skb)))
1348 return;
1349
David S. Millerb94f1c02012-07-12 00:33:37 -07001350 icmpv6_notify(skb, NDISC_REDIRECT, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001351}
1352
David S. Miller49919692012-01-27 15:30:48 -08001353void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001354{
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001355 struct net_device *dev = skb->dev;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001356 struct net *net = dev_net(dev);
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001357 struct sock *sk = net->ipv6.ndisc_sk;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001358 int len = sizeof(struct icmp6hdr) + 2 * sizeof(struct in6_addr);
David S. Millerfbfe95a2012-06-08 23:24:18 -07001359 struct inet_peer *peer;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001360 struct sk_buff *buff;
1361 struct icmp6hdr *icmph;
1362 struct in6_addr saddr_buf;
1363 struct in6_addr *addrp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001364 struct rt6_info *rt;
1365 struct dst_entry *dst;
1366 struct inet6_dev *idev;
David S. Miller4c9483b2011-03-12 16:22:43 -05001367 struct flowi6 fl6;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001368 u8 *opt;
Herbert Xua7ae1992011-11-18 02:20:04 +00001369 int hlen, tlen;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001370 int rd_len;
1371 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001372 u8 ha_buf[MAX_ADDR_LEN], *ha = NULL;
David S. Miller1d861aa2012-07-10 03:58:16 -07001373 bool ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001374
Neil Horman95c385b2007-04-25 17:08:10 -07001375 if (ipv6_get_lladdr(dev, &saddr_buf, IFA_F_TENTATIVE)) {
Joe Perches675418d2012-05-16 19:28:38 +00001376 ND_PRINTK(2, warn, "Redirect: no link-local address on %s\n",
1377 dev->name);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001378 return;
1379 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001380
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001381 if (!ipv6_addr_equal(&ipv6_hdr(skb)->daddr, target) &&
Brian Haleybf0b48d2007-10-08 00:12:05 -07001382 ipv6_addr_type(target) != (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) {
Joe Perches675418d2012-05-16 19:28:38 +00001383 ND_PRINTK(2, warn,
1384 "Redirect: target address is not link-local unicast\n");
Li Yewang29556522007-01-30 14:33:20 -08001385 return;
1386 }
1387
David S. Miller4c9483b2011-03-12 16:22:43 -05001388 icmpv6_flow_init(sk, &fl6, NDISC_REDIRECT,
YOSHIFUJI Hideaki95e41e92007-12-06 15:43:30 -08001389 &saddr_buf, &ipv6_hdr(skb)->saddr, dev->ifindex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001390
David S. Miller4c9483b2011-03-12 16:22:43 -05001391 dst = ip6_route_output(net, NULL, &fl6);
RongQing.Li5095d642012-02-21 22:10:49 +00001392 if (dst->error) {
1393 dst_release(dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001394 return;
RongQing.Li5095d642012-02-21 22:10:49 +00001395 }
David S. Miller4c9483b2011-03-12 16:22:43 -05001396 dst = xfrm_lookup(net, dst, flowi6_to_flowi(&fl6), NULL, 0);
David S. Miller452edd52011-03-02 13:27:41 -08001397 if (IS_ERR(dst))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001398 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001399
1400 rt = (struct rt6_info *) dst;
1401
1402 if (rt->rt6i_flags & RTF_GATEWAY) {
Joe Perches675418d2012-05-16 19:28:38 +00001403 ND_PRINTK(2, warn,
1404 "Redirect: destination is not a neighbour\n");
Ilpo Järvinend73f0802009-02-06 23:47:37 -08001405 goto release;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001406 }
David S. Miller1d861aa2012-07-10 03:58:16 -07001407 peer = inet_getpeer_v6(net->ipv6.peers, &rt->rt6i_dst.addr, 1);
1408 ret = inet_peer_xrlim_allow(peer, 1*HZ);
1409 if (peer)
1410 inet_putpeer(peer);
1411 if (!ret)
Ilpo Järvinend73f0802009-02-06 23:47:37 -08001412 goto release;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001413
1414 if (dev->addr_len) {
David S. Miller49919692012-01-27 15:30:48 -08001415 struct neighbour *neigh = dst_neigh_lookup(skb_dst(skb), target);
1416 if (!neigh) {
Joe Perches675418d2012-05-16 19:28:38 +00001417 ND_PRINTK(2, warn,
1418 "Redirect: no neigh for target address\n");
David S. Miller49919692012-01-27 15:30:48 -08001419 goto release;
1420 }
1421
Linus Torvalds1da177e2005-04-16 15:20:36 -07001422 read_lock_bh(&neigh->lock);
1423 if (neigh->nud_state & NUD_VALID) {
1424 memcpy(ha_buf, neigh->ha, dev->addr_len);
1425 read_unlock_bh(&neigh->lock);
1426 ha = ha_buf;
1427 len += ndisc_opt_addr_space(dev);
1428 } else
1429 read_unlock_bh(&neigh->lock);
David S. Miller49919692012-01-27 15:30:48 -08001430
1431 neigh_release(neigh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001432 }
1433
1434 rd_len = min_t(unsigned int,
1435 IPV6_MIN_MTU-sizeof(struct ipv6hdr)-len, skb->len + 8);
1436 rd_len &= ~0x7;
1437 len += rd_len;
1438
Herbert Xua7ae1992011-11-18 02:20:04 +00001439 hlen = LL_RESERVED_SPACE(dev);
1440 tlen = dev->needed_tailroom;
David S. Millerd54a81d2006-12-02 21:00:06 -08001441 buff = sock_alloc_send_skb(sk,
1442 (MAX_HEADER + sizeof(struct ipv6hdr) +
Herbert Xua7ae1992011-11-18 02:20:04 +00001443 len + hlen + tlen),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001444 1, &err);
1445 if (buff == NULL) {
Joe Perches675418d2012-05-16 19:28:38 +00001446 ND_PRINTK(0, err,
1447 "Redirect: %s failed to allocate an skb, err=%d\n",
1448 __func__, err);
Ilpo Järvinend73f0802009-02-06 23:47:37 -08001449 goto release;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001450 }
1451
Herbert Xua7ae1992011-11-18 02:20:04 +00001452 skb_reserve(buff, hlen);
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001453 ip6_nd_hdr(sk, buff, dev, &saddr_buf, &ipv6_hdr(skb)->saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001454 IPPROTO_ICMPV6, len);
1455
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -07001456 skb_set_transport_header(buff, skb_tail_pointer(buff) - buff->data);
Arnaldo Carvalho de Melod10ba342007-03-14 21:05:37 -03001457 skb_put(buff, len);
1458 icmph = icmp6_hdr(buff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001459
1460 memset(icmph, 0, sizeof(struct icmp6hdr));
1461 icmph->icmp6_type = NDISC_REDIRECT;
1462
1463 /*
1464 * copy target and destination addresses
1465 */
1466
1467 addrp = (struct in6_addr *)(icmph + 1);
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001468 *addrp = *target;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001469 addrp++;
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001470 *addrp = ipv6_hdr(skb)->daddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001471
1472 opt = (u8*) (addrp + 1);
1473
1474 /*
1475 * include target_address option
1476 */
1477
1478 if (ha)
1479 opt = ndisc_fill_addr_option(opt, ND_OPT_TARGET_LL_ADDR, ha,
1480 dev->addr_len, dev->type);
1481
1482 /*
1483 * build redirect option and copy skb over to the new packet.
1484 */
1485
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001486 memset(opt, 0, 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001487 *(opt++) = ND_OPT_REDIRECT_HDR;
1488 *(opt++) = (rd_len >> 3);
1489 opt += 6;
1490
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001491 memcpy(opt, ipv6_hdr(skb), rd_len - 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001492
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001493 icmph->icmp6_cksum = csum_ipv6_magic(&saddr_buf, &ipv6_hdr(skb)->saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001494 len, IPPROTO_ICMPV6,
Joe Perches07f07572008-11-19 15:44:53 -08001495 csum_partial(icmph, len, 0));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001496
Eric Dumazetadf30902009-06-02 05:19:30 +00001497 skb_dst_set(buff, dst);
Eric Dumazetcfdf7642011-07-27 21:13:03 +00001498 rcu_read_lock();
1499 idev = __in6_dev_get(dst->dev);
Neil Hormanedf391f2009-04-27 02:45:02 -07001500 IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUT, skb->len);
Jan Engelhardtb2e0b382010-03-23 04:09:07 +01001501 err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, buff, NULL, dst->dev,
Patrick McHardy6e23ae22007-11-19 18:53:30 -08001502 dst_output);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001503 if (!err) {
Denis V. Lunev5c5d2442008-10-08 10:33:50 -07001504 ICMP6MSGOUT_INC_STATS(net, idev, NDISC_REDIRECT);
Denis V. Luneva862f6a2008-10-08 10:33:06 -07001505 ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001506 }
1507
Eric Dumazetcfdf7642011-07-27 21:13:03 +00001508 rcu_read_unlock();
Ilpo Järvinend73f0802009-02-06 23:47:37 -08001509 return;
1510
1511release:
1512 dst_release(dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001513}
1514
1515static void pndisc_redo(struct sk_buff *skb)
1516{
YOSHIFUJI Hideaki140e26fc2005-10-05 12:11:41 -07001517 ndisc_recv_ns(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001518 kfree_skb(skb);
1519}
1520
1521int ndisc_rcv(struct sk_buff *skb)
1522{
1523 struct nd_msg *msg;
1524
1525 if (!pskb_may_pull(skb, skb->len))
1526 return 0;
1527
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -07001528 msg = (struct nd_msg *)skb_transport_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001529
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -07001530 __skb_push(skb, skb->data - skb_transport_header(skb));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001531
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001532 if (ipv6_hdr(skb)->hop_limit != 255) {
Joe Perches675418d2012-05-16 19:28:38 +00001533 ND_PRINTK(2, warn, "NDISC: invalid hop-limit: %d\n",
1534 ipv6_hdr(skb)->hop_limit);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001535 return 0;
1536 }
1537
1538 if (msg->icmph.icmp6_code != 0) {
Joe Perches675418d2012-05-16 19:28:38 +00001539 ND_PRINTK(2, warn, "NDISC: invalid ICMPv6 code: %d\n",
1540 msg->icmph.icmp6_code);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001541 return 0;
1542 }
1543
Patrick McHardya61bbcf2005-08-14 17:24:31 -07001544 memset(NEIGH_CB(skb), 0, sizeof(struct neighbour_cb));
1545
Linus Torvalds1da177e2005-04-16 15:20:36 -07001546 switch (msg->icmph.icmp6_type) {
1547 case NDISC_NEIGHBOUR_SOLICITATION:
1548 ndisc_recv_ns(skb);
1549 break;
1550
1551 case NDISC_NEIGHBOUR_ADVERTISEMENT:
1552 ndisc_recv_na(skb);
1553 break;
1554
1555 case NDISC_ROUTER_SOLICITATION:
1556 ndisc_recv_rs(skb);
1557 break;
1558
1559 case NDISC_ROUTER_ADVERTISEMENT:
1560 ndisc_router_discovery(skb);
1561 break;
1562
1563 case NDISC_REDIRECT:
1564 ndisc_redirect_rcv(skb);
1565 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -07001566 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001567
1568 return 0;
1569}
1570
1571static int ndisc_netdev_event(struct notifier_block *this, unsigned long event, void *ptr)
1572{
1573 struct net_device *dev = ptr;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001574 struct net *net = dev_net(dev);
Hannes Frederic Sowa5cb04432012-11-06 16:46:20 +00001575 struct inet6_dev *idev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001576
1577 switch (event) {
1578 case NETDEV_CHANGEADDR:
1579 neigh_changeaddr(&nd_tbl, dev);
Daniel Lezcano5b7c9312008-03-03 23:28:58 -08001580 fib6_run_gc(~0UL, net);
Hannes Frederic Sowa5cb04432012-11-06 16:46:20 +00001581 idev = in6_dev_get(dev);
1582 if (!idev)
1583 break;
1584 if (idev->cnf.ndisc_notify)
1585 ndisc_send_unsol_na(dev);
1586 in6_dev_put(idev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001587 break;
1588 case NETDEV_DOWN:
1589 neigh_ifdown(&nd_tbl, dev);
Daniel Lezcano5b7c9312008-03-03 23:28:58 -08001590 fib6_run_gc(~0UL, net);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001591 break;
Ben Hutchingsf47b9462011-04-15 13:46:02 +00001592 case NETDEV_NOTIFY_PEERS:
1593 ndisc_send_unsol_na(dev);
1594 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001595 default:
1596 break;
1597 }
1598
1599 return NOTIFY_DONE;
1600}
1601
1602static struct notifier_block ndisc_netdev_notifier = {
1603 .notifier_call = ndisc_netdev_event,
1604};
1605
1606#ifdef CONFIG_SYSCTL
1607static void ndisc_warn_deprecated_sysctl(struct ctl_table *ctl,
1608 const char *func, const char *dev_name)
1609{
1610 static char warncomm[TASK_COMM_LEN];
1611 static int warned;
1612 if (strcmp(warncomm, current->comm) && warned < 5) {
1613 strcpy(warncomm, current->comm);
Joe Perchesf3213832012-05-15 14:11:53 +00001614 pr_warn("process `%s' is using deprecated sysctl (%s) net.ipv6.neigh.%s.%s - use net.ipv6.neigh.%s.%s_ms instead\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001615 warncomm, func,
1616 dev_name, ctl->procname,
1617 dev_name, ctl->procname);
1618 warned++;
1619 }
1620}
1621
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001622int ndisc_ifinfo_sysctl_change(struct ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *ppos)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001623{
1624 struct net_device *dev = ctl->extra1;
1625 struct inet6_dev *idev;
1626 int ret;
1627
Eric W. Biedermand12af672007-10-18 03:05:25 -07001628 if ((strcmp(ctl->procname, "retrans_time") == 0) ||
1629 (strcmp(ctl->procname, "base_reachable_time") == 0))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001630 ndisc_warn_deprecated_sysctl(ctl, "syscall", dev ? dev->name : "default");
1631
Eric W. Biedermand12af672007-10-18 03:05:25 -07001632 if (strcmp(ctl->procname, "retrans_time") == 0)
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001633 ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
Eric W. Biedermand12af672007-10-18 03:05:25 -07001634
1635 else if (strcmp(ctl->procname, "base_reachable_time") == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001636 ret = proc_dointvec_jiffies(ctl, write,
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001637 buffer, lenp, ppos);
Eric W. Biedermand12af672007-10-18 03:05:25 -07001638
1639 else if ((strcmp(ctl->procname, "retrans_time_ms") == 0) ||
YOSHIFUJI Hideakiad02ac12007-10-29 01:32:23 -07001640 (strcmp(ctl->procname, "base_reachable_time_ms") == 0))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001641 ret = proc_dointvec_ms_jiffies(ctl, write,
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001642 buffer, lenp, ppos);
Eric W. Biedermand12af672007-10-18 03:05:25 -07001643 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001644 ret = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001645
1646 if (write && ret == 0 && dev && (idev = in6_dev_get(dev)) != NULL) {
Eric W. Biedermand12af672007-10-18 03:05:25 -07001647 if (ctl->data == &idev->nd_parms->base_reachable_time)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001648 idev->nd_parms->reachable_time = neigh_rand_reach_time(idev->nd_parms->base_reachable_time);
1649 idev->tstamp = jiffies;
1650 inet6_ifinfo_notify(RTM_NEWLINK, idev);
1651 in6_dev_put(idev);
1652 }
1653 return ret;
1654}
1655
Linus Torvalds1da177e2005-04-16 15:20:36 -07001656
1657#endif
1658
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00001659static int __net_init ndisc_net_init(struct net *net)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001660{
1661 struct ipv6_pinfo *np;
1662 struct sock *sk;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001663 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001664
Denis V. Lunev1ed85162008-04-03 14:31:03 -07001665 err = inet_ctl_sock_create(&sk, PF_INET6,
1666 SOCK_RAW, IPPROTO_ICMPV6, net);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001667 if (err < 0) {
Joe Perches675418d2012-05-16 19:28:38 +00001668 ND_PRINTK(0, err,
1669 "NDISC: Failed to initialize the control socket (err %d)\n",
1670 err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001671 return err;
1672 }
1673
Denis V. Lunev1ed85162008-04-03 14:31:03 -07001674 net->ipv6.ndisc_sk = sk;
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001675
Linus Torvalds1da177e2005-04-16 15:20:36 -07001676 np = inet6_sk(sk);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001677 np->hop_limit = 255;
1678 /* Do not loopback ndisc messages */
1679 np->mc_loop = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001680
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001681 return 0;
1682}
1683
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00001684static void __net_exit ndisc_net_exit(struct net *net)
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001685{
Denis V. Lunev1ed85162008-04-03 14:31:03 -07001686 inet_ctl_sock_destroy(net->ipv6.ndisc_sk);
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001687}
1688
1689static struct pernet_operations ndisc_net_ops = {
1690 .init = ndisc_net_init,
1691 .exit = ndisc_net_exit,
1692};
1693
1694int __init ndisc_init(void)
1695{
1696 int err;
1697
1698 err = register_pernet_subsys(&ndisc_net_ops);
1699 if (err)
1700 return err;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001701 /*
1702 * Initialize the neighbour table
1703 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001704 neigh_table_init(&nd_tbl);
1705
1706#ifdef CONFIG_SYSCTL
Eric W. Biederman54716e32010-02-14 03:27:03 +00001707 err = neigh_sysctl_register(NULL, &nd_tbl.parms, "ipv6",
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001708 &ndisc_ifinfo_sysctl_change);
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001709 if (err)
1710 goto out_unregister_pernet;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001711#endif
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001712 err = register_netdevice_notifier(&ndisc_netdev_notifier);
1713 if (err)
1714 goto out_unregister_sysctl;
1715out:
1716 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001717
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001718out_unregister_sysctl:
1719#ifdef CONFIG_SYSCTL
1720 neigh_sysctl_unregister(&nd_tbl.parms);
1721out_unregister_pernet:
1722#endif
1723 unregister_pernet_subsys(&ndisc_net_ops);
1724 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001725}
1726
1727void ndisc_cleanup(void)
1728{
Dmitry Mishin36f73d02006-11-03 16:08:19 -08001729 unregister_netdevice_notifier(&ndisc_netdev_notifier);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001730#ifdef CONFIG_SYSCTL
1731 neigh_sysctl_unregister(&nd_tbl.parms);
1732#endif
1733 neigh_table_clear(&nd_tbl);
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001734 unregister_pernet_subsys(&ndisc_net_ops);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001735}