blob: 53a545f326254995f62e365bf605face5e053305 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * Neighbour Discovery for IPv6
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09003 * Linux INET6 implementation
Linus Torvalds1da177e2005-04-16 15:20:36 -07004 *
5 * Authors:
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09006 * Pedro Roque <roque@di.fc.ul.pt>
Linus Torvalds1da177e2005-04-16 15:20:36 -07007 * Mike Shaver <shaver@ingenia.com>
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version
12 * 2 of the License, or (at your option) any later version.
13 */
14
15/*
16 * Changes:
17 *
Alexey I. Froloffe35f30c2012-04-06 05:50:58 +000018 * Alexey I. Froloff : RFC6106 (DNSSL) support
Pierre Ynard31910572007-10-10 21:22:05 -070019 * Pierre Ynard : export userland ND options
20 * through netlink (RDNSS support)
Linus Torvalds1da177e2005-04-16 15:20:36 -070021 * Lars Fenneberg : fixed MTU setting on receipt
22 * of an RA.
Linus Torvalds1da177e2005-04-16 15:20:36 -070023 * Janos Farkas : kmalloc failure checks
24 * Alexey Kuznetsov : state machine reworked
25 * and moved to net/core.
26 * Pekka Savola : RFC2461 validation
27 * YOSHIFUJI Hideaki @USAGI : Verify ND options properly
28 */
29
Joe Perches675418d2012-05-16 19:28:38 +000030#define pr_fmt(fmt) "ICMPv6: " fmt
Linus Torvalds1da177e2005-04-16 15:20:36 -070031
32#include <linux/module.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070033#include <linux/errno.h>
34#include <linux/types.h>
35#include <linux/socket.h>
36#include <linux/sockios.h>
37#include <linux/sched.h>
38#include <linux/net.h>
39#include <linux/in6.h>
40#include <linux/route.h>
41#include <linux/init.h>
42#include <linux/rcupdate.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090043#include <linux/slab.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070044#ifdef CONFIG_SYSCTL
45#include <linux/sysctl.h>
46#endif
47
Thomas Graf18237302006-08-04 23:04:54 -070048#include <linux/if_addr.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070049#include <linux/if_arp.h>
50#include <linux/ipv6.h>
51#include <linux/icmpv6.h>
52#include <linux/jhash.h>
53
54#include <net/sock.h>
55#include <net/snmp.h>
56
57#include <net/ipv6.h>
58#include <net/protocol.h>
59#include <net/ndisc.h>
60#include <net/ip6_route.h>
61#include <net/addrconf.h>
62#include <net/icmp.h>
63
Pierre Ynard31910572007-10-10 21:22:05 -070064#include <net/netlink.h>
65#include <linux/rtnetlink.h>
66
Linus Torvalds1da177e2005-04-16 15:20:36 -070067#include <net/flow.h>
68#include <net/ip6_checksum.h>
Denis V. Lunev1ed85162008-04-03 14:31:03 -070069#include <net/inet_common.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070070#include <linux/proc_fs.h>
71
72#include <linux/netfilter.h>
73#include <linux/netfilter_ipv6.h>
74
Joe Perches675418d2012-05-16 19:28:38 +000075/* Set to 3 to get tracing... */
76#define ND_DEBUG 1
77
78#define ND_PRINTK(val, level, fmt, ...) \
79do { \
80 if (val <= ND_DEBUG) \
81 net_##level##_ratelimited(fmt, ##__VA_ARGS__); \
82} while (0)
83
Eric Dumazetd6bf7812010-10-04 06:15:44 +000084static u32 ndisc_hash(const void *pkey,
85 const struct net_device *dev,
David S. Miller2c2aba62011-12-28 15:06:58 -050086 __u32 *hash_rnd);
Linus Torvalds1da177e2005-04-16 15:20:36 -070087static int ndisc_constructor(struct neighbour *neigh);
88static void ndisc_solicit(struct neighbour *neigh, struct sk_buff *skb);
89static void ndisc_error_report(struct neighbour *neigh, struct sk_buff *skb);
90static int pndisc_constructor(struct pneigh_entry *n);
91static void pndisc_destructor(struct pneigh_entry *n);
92static void pndisc_redo(struct sk_buff *skb);
93
Stephen Hemminger89d69d22009-09-01 11:13:19 +000094static const struct neigh_ops ndisc_generic_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -070095 .family = AF_INET6,
96 .solicit = ndisc_solicit,
97 .error_report = ndisc_error_report,
98 .output = neigh_resolve_output,
99 .connected_output = neigh_connected_output,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100};
101
Stephen Hemminger89d69d22009-09-01 11:13:19 +0000102static const struct neigh_ops ndisc_hh_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700103 .family = AF_INET6,
104 .solicit = ndisc_solicit,
105 .error_report = ndisc_error_report,
106 .output = neigh_resolve_output,
107 .connected_output = neigh_resolve_output,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108};
109
110
Stephen Hemminger89d69d22009-09-01 11:13:19 +0000111static const struct neigh_ops ndisc_direct_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700112 .family = AF_INET6,
David S. Miller8f40b162011-07-17 13:34:11 -0700113 .output = neigh_direct_output,
114 .connected_output = neigh_direct_output,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700115};
116
117struct neigh_table nd_tbl = {
118 .family = AF_INET6,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700119 .key_len = sizeof(struct in6_addr),
120 .hash = ndisc_hash,
121 .constructor = ndisc_constructor,
122 .pconstructor = pndisc_constructor,
123 .pdestructor = pndisc_destructor,
124 .proxy_redo = pndisc_redo,
125 .id = "ndisc_cache",
126 .parms = {
Shan Weib6720832010-12-01 18:05:12 +0000127 .tbl = &nd_tbl,
128 .base_reachable_time = ND_REACHABLE_TIME,
129 .retrans_time = ND_RETRANS_TIMER,
130 .gc_staletime = 60 * HZ,
131 .reachable_time = ND_REACHABLE_TIME,
132 .delay_probe_time = 5 * HZ,
Eric Dumazet8b5c1712011-11-09 12:07:14 +0000133 .queue_len_bytes = 64*1024,
Shan Weib6720832010-12-01 18:05:12 +0000134 .ucast_probes = 3,
135 .mcast_probes = 3,
136 .anycast_delay = 1 * HZ,
137 .proxy_delay = (8 * HZ) / 10,
138 .proxy_qlen = 64,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700139 },
140 .gc_interval = 30 * HZ,
141 .gc_thresh1 = 128,
142 .gc_thresh2 = 512,
143 .gc_thresh3 = 1024,
144};
145
YOSHIFUJI Hideaki / 吉藤英明315ff092013-01-21 06:47:50 +0000146static u8 *ndisc_fill_addr_option(u8 *opt, int type, void *data,
147 struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148{
YOSHIFUJI Hideaki / 吉藤英明315ff092013-01-21 06:47:50 +0000149 int pad = ndisc_addr_option_pad(dev->type);
150 int data_len = dev->addr_len;
YOSHIFUJI Hideaki / 吉藤英明c558e9f2013-01-21 06:47:56 +0000151 int space = ndisc_opt_addr_space(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700152
153 opt[0] = type;
154 opt[1] = space>>3;
155
156 memset(opt + 2, 0, pad);
157 opt += pad;
158 space -= pad;
159
160 memcpy(opt+2, data, data_len);
161 data_len += 2;
162 opt += data_len;
163 if ((space -= data_len) > 0)
164 memset(opt, 0, space);
165 return opt + space;
166}
167
168static struct nd_opt_hdr *ndisc_next_option(struct nd_opt_hdr *cur,
169 struct nd_opt_hdr *end)
170{
171 int type;
172 if (!cur || !end || cur >= end)
173 return NULL;
174 type = cur->nd_opt_type;
175 do {
176 cur = ((void *)cur) + (cur->nd_opt_len << 3);
177 } while(cur < end && cur->nd_opt_type != type);
Eric Dumazeta02cec22010-09-22 20:43:57 +0000178 return cur <= end && cur->nd_opt_type == type ? cur : NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700179}
180
Pierre Ynard31910572007-10-10 21:22:05 -0700181static inline int ndisc_is_useropt(struct nd_opt_hdr *opt)
182{
Alexey I. Froloffe35f30c2012-04-06 05:50:58 +0000183 return opt->nd_opt_type == ND_OPT_RDNSS ||
184 opt->nd_opt_type == ND_OPT_DNSSL;
Pierre Ynard31910572007-10-10 21:22:05 -0700185}
186
187static struct nd_opt_hdr *ndisc_next_useropt(struct nd_opt_hdr *cur,
188 struct nd_opt_hdr *end)
189{
190 if (!cur || !end || cur >= end)
191 return NULL;
192 do {
193 cur = ((void *)cur) + (cur->nd_opt_len << 3);
194 } while(cur < end && !ndisc_is_useropt(cur));
Eric Dumazeta02cec22010-09-22 20:43:57 +0000195 return cur <= end && ndisc_is_useropt(cur) ? cur : NULL;
Pierre Ynard31910572007-10-10 21:22:05 -0700196}
197
David S. Miller30f2a5f2012-07-11 23:26:46 -0700198struct ndisc_options *ndisc_parse_options(u8 *opt, int opt_len,
199 struct ndisc_options *ndopts)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700200{
201 struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)opt;
202
203 if (!nd_opt || opt_len < 0 || !ndopts)
204 return NULL;
205 memset(ndopts, 0, sizeof(*ndopts));
206 while (opt_len) {
207 int l;
208 if (opt_len < sizeof(struct nd_opt_hdr))
209 return NULL;
210 l = nd_opt->nd_opt_len << 3;
211 if (opt_len < l || l == 0)
212 return NULL;
213 switch (nd_opt->nd_opt_type) {
214 case ND_OPT_SOURCE_LL_ADDR:
215 case ND_OPT_TARGET_LL_ADDR:
216 case ND_OPT_MTU:
217 case ND_OPT_REDIRECT_HDR:
218 if (ndopts->nd_opt_array[nd_opt->nd_opt_type]) {
Joe Perches675418d2012-05-16 19:28:38 +0000219 ND_PRINTK(2, warn,
220 "%s: duplicated ND6 option found: type=%d\n",
221 __func__, nd_opt->nd_opt_type);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700222 } else {
223 ndopts->nd_opt_array[nd_opt->nd_opt_type] = nd_opt;
224 }
225 break;
226 case ND_OPT_PREFIX_INFO:
227 ndopts->nd_opts_pi_end = nd_opt;
Stephen Hemmingercfcabdc2007-10-09 01:59:42 -0700228 if (!ndopts->nd_opt_array[nd_opt->nd_opt_type])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700229 ndopts->nd_opt_array[nd_opt->nd_opt_type] = nd_opt;
230 break;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800231#ifdef CONFIG_IPV6_ROUTE_INFO
232 case ND_OPT_ROUTE_INFO:
233 ndopts->nd_opts_ri_end = nd_opt;
234 if (!ndopts->nd_opts_ri)
235 ndopts->nd_opts_ri = nd_opt;
236 break;
237#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700238 default:
Pierre Ynard31910572007-10-10 21:22:05 -0700239 if (ndisc_is_useropt(nd_opt)) {
240 ndopts->nd_useropts_end = nd_opt;
241 if (!ndopts->nd_useropts)
242 ndopts->nd_useropts = nd_opt;
243 } else {
244 /*
245 * Unknown options must be silently ignored,
246 * to accommodate future extension to the
247 * protocol.
248 */
Joe Perches675418d2012-05-16 19:28:38 +0000249 ND_PRINTK(2, notice,
250 "%s: ignored unsupported option; type=%d, len=%d\n",
251 __func__,
252 nd_opt->nd_opt_type,
253 nd_opt->nd_opt_len);
Pierre Ynard31910572007-10-10 21:22:05 -0700254 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700255 }
256 opt_len -= l;
257 nd_opt = ((void *)nd_opt) + l;
258 }
259 return ndopts;
260}
261
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000262int ndisc_mc_map(const struct in6_addr *addr, char *buf, struct net_device *dev, int dir)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700263{
264 switch (dev->type) {
265 case ARPHRD_ETHER:
266 case ARPHRD_IEEE802: /* Not sure. Check it later. --ANK */
267 case ARPHRD_FDDI:
268 ipv6_eth_mc_map(addr, buf);
269 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700270 case ARPHRD_ARCNET:
271 ipv6_arcnet_mc_map(addr, buf);
272 return 0;
273 case ARPHRD_INFINIBAND:
Rolf Manderscheida9e527e2007-12-10 13:38:41 -0700274 ipv6_ib_mc_map(addr, dev->broadcast, buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700275 return 0;
Timo Teräs93ca3bb2011-03-28 22:40:53 +0000276 case ARPHRD_IPGRE:
277 return ipv6_ipgre_mc_map(addr, dev->broadcast, buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700278 default:
279 if (dir) {
280 memcpy(buf, dev->broadcast, dev->addr_len);
281 return 0;
282 }
283 }
284 return -EINVAL;
285}
286
YOSHIFUJI Hideaki71590392007-02-22 22:05:40 +0900287EXPORT_SYMBOL(ndisc_mc_map);
288
Eric Dumazetd6bf7812010-10-04 06:15:44 +0000289static u32 ndisc_hash(const void *pkey,
290 const struct net_device *dev,
David S. Miller2c2aba62011-12-28 15:06:58 -0500291 __u32 *hash_rnd)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292{
David S. Miller2c2aba62011-12-28 15:06:58 -0500293 return ndisc_hashfn(pkey, dev, hash_rnd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700294}
295
296static int ndisc_constructor(struct neighbour *neigh)
297{
298 struct in6_addr *addr = (struct in6_addr*)&neigh->primary_key;
299 struct net_device *dev = neigh->dev;
300 struct inet6_dev *in6_dev;
301 struct neigh_parms *parms;
Eric Dumazeta50feda2012-05-18 18:57:34 +0000302 bool is_multicast = ipv6_addr_is_multicast(addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303
Linus Torvalds1da177e2005-04-16 15:20:36 -0700304 in6_dev = in6_dev_get(dev);
305 if (in6_dev == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700306 return -EINVAL;
307 }
308
309 parms = in6_dev->nd_parms;
310 __neigh_parms_put(neigh->parms);
311 neigh->parms = neigh_parms_clone(parms);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700312
313 neigh->type = is_multicast ? RTN_MULTICAST : RTN_UNICAST;
Stephen Hemminger3b04ddd2007-10-09 01:40:57 -0700314 if (!dev->header_ops) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700315 neigh->nud_state = NUD_NOARP;
316 neigh->ops = &ndisc_direct_ops;
David S. Miller8f40b162011-07-17 13:34:11 -0700317 neigh->output = neigh_direct_output;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700318 } else {
319 if (is_multicast) {
320 neigh->nud_state = NUD_NOARP;
321 ndisc_mc_map(addr, neigh->ha, dev, 1);
322 } else if (dev->flags&(IFF_NOARP|IFF_LOOPBACK)) {
323 neigh->nud_state = NUD_NOARP;
324 memcpy(neigh->ha, dev->dev_addr, dev->addr_len);
325 if (dev->flags&IFF_LOOPBACK)
326 neigh->type = RTN_LOCAL;
327 } else if (dev->flags&IFF_POINTOPOINT) {
328 neigh->nud_state = NUD_NOARP;
329 memcpy(neigh->ha, dev->broadcast, dev->addr_len);
330 }
Stephen Hemminger3b04ddd2007-10-09 01:40:57 -0700331 if (dev->header_ops->cache)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700332 neigh->ops = &ndisc_hh_ops;
333 else
334 neigh->ops = &ndisc_generic_ops;
335 if (neigh->nud_state&NUD_VALID)
336 neigh->output = neigh->ops->connected_output;
337 else
338 neigh->output = neigh->ops->output;
339 }
340 in6_dev_put(in6_dev);
341 return 0;
342}
343
344static int pndisc_constructor(struct pneigh_entry *n)
345{
346 struct in6_addr *addr = (struct in6_addr*)&n->key;
347 struct in6_addr maddr;
348 struct net_device *dev = n->dev;
349
350 if (dev == NULL || __in6_dev_get(dev) == NULL)
351 return -EINVAL;
352 addrconf_addr_solict_mult(addr, &maddr);
353 ipv6_dev_mc_inc(dev, &maddr);
354 return 0;
355}
356
357static void pndisc_destructor(struct pneigh_entry *n)
358{
359 struct in6_addr *addr = (struct in6_addr*)&n->key;
360 struct in6_addr maddr;
361 struct net_device *dev = n->dev;
362
363 if (dev == NULL || __in6_dev_get(dev) == NULL)
364 return;
365 addrconf_addr_solict_mult(addr, &maddr);
366 ipv6_dev_mc_dec(dev, &maddr);
367}
368
YOSHIFUJI Hideakifd0ea7d2012-12-13 02:40:26 +0900369static struct sk_buff *ndisc_build_skb(struct net_device *dev,
370 const struct in6_addr *daddr,
371 const struct in6_addr *saddr,
372 struct icmp6hdr *icmp6h,
373 const struct in6_addr *target,
374 int llinfo)
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900375{
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900376 struct net *net = dev_net(dev);
Daniel Lezcano1762f7e2008-03-07 11:15:34 -0800377 struct sock *sk = net->ipv6.ndisc_sk;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900378 struct sk_buff *skb;
379 struct icmp6hdr *hdr;
Herbert Xua7ae1992011-11-18 02:20:04 +0000380 int hlen = LL_RESERVED_SPACE(dev);
381 int tlen = dev->needed_tailroom;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900382 int len;
383 int err;
Brian Haley305d5522008-11-04 17:51:14 -0800384 u8 *opt;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900385
386 if (!dev->addr_len)
387 llinfo = 0;
388
389 len = sizeof(struct icmp6hdr) + (target ? sizeof(*target) : 0);
390 if (llinfo)
391 len += ndisc_opt_addr_space(dev);
392
393 skb = sock_alloc_send_skb(sk,
YOSHIFUJI Hideaki / 吉藤英明b7dc8c32013-01-04 03:58:04 +0000394 (sizeof(struct ipv6hdr) +
Herbert Xua7ae1992011-11-18 02:20:04 +0000395 len + hlen + tlen),
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900396 1, &err);
397 if (!skb) {
Joe Perches675418d2012-05-16 19:28:38 +0000398 ND_PRINTK(0, err, "ND: %s failed to allocate an skb, err=%d\n",
399 __func__, err);
Brian Haley305d5522008-11-04 17:51:14 -0800400 return NULL;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900401 }
402
Herbert Xua7ae1992011-11-18 02:20:04 +0000403 skb_reserve(skb, hlen);
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900404 ip6_nd_hdr(sk, skb, dev, saddr, daddr, IPPROTO_ICMPV6, len);
405
406 skb->transport_header = skb->tail;
407 skb_put(skb, len);
408
409 hdr = (struct icmp6hdr *)skb_transport_header(skb);
410 memcpy(hdr, icmp6h, sizeof(*hdr));
411
412 opt = skb_transport_header(skb) + sizeof(struct icmp6hdr);
413 if (target) {
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +0000414 *(struct in6_addr *)opt = *target;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900415 opt += sizeof(*target);
416 }
417
418 if (llinfo)
YOSHIFUJI Hideaki / 吉藤英明315ff092013-01-21 06:47:50 +0000419 ndisc_fill_addr_option(opt, llinfo, dev->dev_addr, dev);
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900420
421 hdr->icmp6_cksum = csum_ipv6_magic(saddr, daddr, len,
422 IPPROTO_ICMPV6,
Joe Perches07f07572008-11-19 15:44:53 -0800423 csum_partial(hdr,
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900424 len, 0));
425
Brian Haley305d5522008-11-04 17:51:14 -0800426 return skb;
427}
428
YOSHIFUJI Hideakifd0ea7d2012-12-13 02:40:26 +0900429static void ndisc_send_skb(struct sk_buff *skb, struct net_device *dev,
YOSHIFUJI Hideakifd0ea7d2012-12-13 02:40:26 +0900430 const struct in6_addr *daddr,
431 const struct in6_addr *saddr,
432 struct icmp6hdr *icmp6h)
Brian Haley305d5522008-11-04 17:51:14 -0800433{
David S. Miller4c9483b2011-03-12 16:22:43 -0500434 struct flowi6 fl6;
Brian Haley305d5522008-11-04 17:51:14 -0800435 struct dst_entry *dst;
436 struct net *net = dev_net(dev);
437 struct sock *sk = net->ipv6.ndisc_sk;
438 struct inet6_dev *idev;
439 int err;
440 u8 type;
441
442 type = icmp6h->icmp6_type;
443
David S. Miller4c9483b2011-03-12 16:22:43 -0500444 icmpv6_flow_init(sk, &fl6, type, saddr, daddr, dev->ifindex);
YOSHIFUJI Hideaki / 吉藤英明12fd84f2013-01-18 02:00:24 +0000445 dst = icmp6_dst_alloc(dev, &fl6);
David S. Miller452edd52011-03-02 13:27:41 -0800446 if (IS_ERR(dst)) {
Brian Haley305d5522008-11-04 17:51:14 -0800447 kfree_skb(skb);
448 return;
449 }
450
Eric Dumazetadf30902009-06-02 05:19:30 +0000451 skb_dst_set(skb, dst);
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900452
Eric Dumazetcfdf7642011-07-27 21:13:03 +0000453 rcu_read_lock();
454 idev = __in6_dev_get(dst->dev);
Neil Hormanedf391f2009-04-27 02:45:02 -0700455 IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUT, skb->len);
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900456
Jan Engelhardtb2e0b382010-03-23 04:09:07 +0100457 err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, skb, NULL, dst->dev,
Patrick McHardy6e23ae22007-11-19 18:53:30 -0800458 dst_output);
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900459 if (!err) {
Denis V. Lunev5c5d2442008-10-08 10:33:50 -0700460 ICMP6MSGOUT_INC_STATS(net, idev, type);
Denis V. Luneva862f6a2008-10-08 10:33:06 -0700461 ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS);
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900462 }
463
Eric Dumazetcfdf7642011-07-27 21:13:03 +0000464 rcu_read_unlock();
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900465}
466
Brian Haley305d5522008-11-04 17:51:14 -0800467/*
468 * Send a Neighbour Discover packet
469 */
470static void __ndisc_send(struct net_device *dev,
Brian Haley305d5522008-11-04 17:51:14 -0800471 const struct in6_addr *daddr,
472 const struct in6_addr *saddr,
473 struct icmp6hdr *icmp6h, const struct in6_addr *target,
474 int llinfo)
475{
476 struct sk_buff *skb;
477
478 skb = ndisc_build_skb(dev, daddr, saddr, icmp6h, target, llinfo);
479 if (!skb)
480 return;
481
YOSHIFUJI Hideaki / 吉藤英明12fd84f2013-01-18 02:00:24 +0000482 ndisc_send_skb(skb, dev, daddr, saddr, icmp6h);
Brian Haley305d5522008-11-04 17:51:14 -0800483}
484
Linus Torvalds1da177e2005-04-16 15:20:36 -0700485static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +0900486 const struct in6_addr *daddr,
487 const struct in6_addr *solicited_addr,
YOSHIFUJI Hideaki / 吉藤英明fb568632013-01-20 07:39:18 +0000488 bool router, bool solicited, bool override, bool inc_opt)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700489{
490 struct in6_addr tmpaddr;
491 struct inet6_ifaddr *ifp;
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +0900492 const struct in6_addr *src_addr;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900493 struct icmp6hdr icmp6h = {
494 .icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT,
495 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700496
497 /* for anycast or proxy, solicited_addr != src_addr */
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900498 ifp = ipv6_get_ifaddr(dev_net(dev), solicited_addr, dev, 1);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900499 if (ifp) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500 src_addr = solicited_addr;
Neil Horman95c385b2007-04-25 17:08:10 -0700501 if (ifp->flags & IFA_F_OPTIMISTIC)
502 override = 0;
stephen hemminger9f888162010-06-21 11:00:13 +0000503 inc_opt |= ifp->idev->cnf.force_tllao;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700504 in6_ifa_put(ifp);
505 } else {
Brian Haley191cd582008-08-14 15:33:21 -0700506 if (ipv6_dev_get_saddr(dev_net(dev), dev, daddr,
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900507 inet6_sk(dev_net(dev)->ipv6.ndisc_sk)->srcprefs,
YOSHIFUJI Hideaki7cbca672008-03-25 09:37:42 +0900508 &tmpaddr))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700509 return;
510 src_addr = &tmpaddr;
511 }
512
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900513 icmp6h.icmp6_router = router;
514 icmp6h.icmp6_solicited = solicited;
515 icmp6h.icmp6_override = override;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700516
YOSHIFUJI Hideaki / 吉藤英明12fd84f2013-01-18 02:00:24 +0000517 __ndisc_send(dev, daddr, src_addr, &icmp6h, solicited_addr,
David L Stevens14878f72007-09-16 16:52:35 -0700518 inc_opt ? ND_OPT_TARGET_LL_ADDR : 0);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900519}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700520
Ben Hutchingsf47b9462011-04-15 13:46:02 +0000521static void ndisc_send_unsol_na(struct net_device *dev)
522{
523 struct inet6_dev *idev;
524 struct inet6_ifaddr *ifa;
Ben Hutchingsf47b9462011-04-15 13:46:02 +0000525
526 idev = in6_dev_get(dev);
527 if (!idev)
528 return;
529
530 read_lock_bh(&idev->lock);
531 list_for_each_entry(ifa, &idev->addr_list, if_list) {
YOSHIFUJI Hideaki / 吉藤英明9fafd652012-11-12 07:50:17 +0000532 ndisc_send_na(dev, NULL, &in6addr_linklocal_allnodes, &ifa->addr,
Ben Hutchingsf47b9462011-04-15 13:46:02 +0000533 /*router=*/ !!idev->cnf.forwarding,
534 /*solicited=*/ false, /*override=*/ true,
535 /*inc_opt=*/ true);
536 }
537 read_unlock_bh(&idev->lock);
538
539 in6_dev_put(idev);
540}
541
Linus Torvalds1da177e2005-04-16 15:20:36 -0700542void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +0900543 const struct in6_addr *solicit,
544 const struct in6_addr *daddr, const struct in6_addr *saddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700545{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700546 struct in6_addr addr_buf;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900547 struct icmp6hdr icmp6h = {
548 .icmp6_type = NDISC_NEIGHBOUR_SOLICITATION,
549 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700550
551 if (saddr == NULL) {
Neil Horman95c385b2007-04-25 17:08:10 -0700552 if (ipv6_get_lladdr(dev, &addr_buf,
553 (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700554 return;
555 saddr = &addr_buf;
556 }
557
YOSHIFUJI Hideaki / 吉藤英明12fd84f2013-01-18 02:00:24 +0000558 __ndisc_send(dev, daddr, saddr, &icmp6h, solicit,
David L Stevens14878f72007-09-16 16:52:35 -0700559 !ipv6_addr_any(saddr) ? ND_OPT_SOURCE_LL_ADDR : 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700560}
561
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +0900562void ndisc_send_rs(struct net_device *dev, const struct in6_addr *saddr,
563 const struct in6_addr *daddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564{
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900565 struct icmp6hdr icmp6h = {
566 .icmp6_type = NDISC_ROUTER_SOLICITATION,
567 };
Neil Horman95c385b2007-04-25 17:08:10 -0700568 int send_sllao = dev->addr_len;
Neil Horman95c385b2007-04-25 17:08:10 -0700569
570#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
571 /*
572 * According to section 2.2 of RFC 4429, we must not
573 * send router solicitations with a sllao from
574 * optimistic addresses, but we may send the solicitation
575 * if we don't include the sllao. So here we check
576 * if our address is optimistic, and if so, we
Joe Perchesbea85192007-12-20 14:01:35 -0800577 * suppress the inclusion of the sllao.
Neil Horman95c385b2007-04-25 17:08:10 -0700578 */
579 if (send_sllao) {
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900580 struct inet6_ifaddr *ifp = ipv6_get_ifaddr(dev_net(dev), saddr,
Daniel Lezcano1cab3da2008-01-10 22:44:09 -0800581 dev, 1);
Neil Horman95c385b2007-04-25 17:08:10 -0700582 if (ifp) {
583 if (ifp->flags & IFA_F_OPTIMISTIC) {
YOSHIFUJI Hideakica043562007-02-28 23:13:20 +0900584 send_sllao = 0;
Neil Horman95c385b2007-04-25 17:08:10 -0700585 }
YOSHIFUJI Hideakica043562007-02-28 23:13:20 +0900586 in6_ifa_put(ifp);
Neil Horman95c385b2007-04-25 17:08:10 -0700587 } else {
588 send_sllao = 0;
589 }
590 }
591#endif
YOSHIFUJI Hideaki / 吉藤英明12fd84f2013-01-18 02:00:24 +0000592 __ndisc_send(dev, daddr, saddr, &icmp6h, NULL,
David L Stevens14878f72007-09-16 16:52:35 -0700593 send_sllao ? ND_OPT_SOURCE_LL_ADDR : 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700594}
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900595
Linus Torvalds1da177e2005-04-16 15:20:36 -0700596
597static void ndisc_error_report(struct neighbour *neigh, struct sk_buff *skb)
598{
599 /*
600 * "The sender MUST return an ICMP
601 * destination unreachable"
602 */
603 dst_link_failure(skb);
604 kfree_skb(skb);
605}
606
607/* Called with locked neigh: either read or both */
608
609static void ndisc_solicit(struct neighbour *neigh, struct sk_buff *skb)
610{
611 struct in6_addr *saddr = NULL;
612 struct in6_addr mcaddr;
613 struct net_device *dev = neigh->dev;
614 struct in6_addr *target = (struct in6_addr *)&neigh->primary_key;
615 int probes = atomic_read(&neigh->probes);
616
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900617 if (skb && ipv6_chk_addr(dev_net(dev), &ipv6_hdr(skb)->saddr, dev, 1))
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -0700618 saddr = &ipv6_hdr(skb)->saddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700619
620 if ((probes -= neigh->parms->ucast_probes) < 0) {
621 if (!(neigh->nud_state & NUD_VALID)) {
Joe Perches675418d2012-05-16 19:28:38 +0000622 ND_PRINTK(1, dbg,
623 "%s: trying to ucast probe in NUD_INVALID: %pI6\n",
624 __func__, target);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700625 }
626 ndisc_send_ns(dev, neigh, target, target, saddr);
627 } else if ((probes -= neigh->parms->app_probes) < 0) {
628#ifdef CONFIG_ARPD
629 neigh_app_ns(neigh);
630#endif
631 } else {
632 addrconf_addr_solict_mult(target, &mcaddr);
633 ndisc_send_ns(dev, NULL, target, &mcaddr, saddr);
634 }
635}
636
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900637static int pndisc_is_router(const void *pkey,
638 struct net_device *dev)
Pavel Emelyanovfa86d322008-03-24 14:48:59 -0700639{
640 struct pneigh_entry *n;
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900641 int ret = -1;
Pavel Emelyanovfa86d322008-03-24 14:48:59 -0700642
643 read_lock_bh(&nd_tbl.lock);
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900644 n = __pneigh_lookup(&nd_tbl, dev_net(dev), pkey, dev);
645 if (n)
646 ret = !!(n->flags & NTF_ROUTER);
Pavel Emelyanovfa86d322008-03-24 14:48:59 -0700647 read_unlock_bh(&nd_tbl.lock);
648
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900649 return ret;
Pavel Emelyanovfa86d322008-03-24 14:48:59 -0700650}
651
Linus Torvalds1da177e2005-04-16 15:20:36 -0700652static void ndisc_recv_ns(struct sk_buff *skb)
653{
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -0700654 struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb);
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000655 const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
656 const struct in6_addr *daddr = &ipv6_hdr(skb)->daddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700657 u8 *lladdr = NULL;
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -0700658 u32 ndoptlen = skb->tail - (skb->transport_header +
659 offsetof(struct nd_msg, opt));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700660 struct ndisc_options ndopts;
661 struct net_device *dev = skb->dev;
662 struct inet6_ifaddr *ifp;
663 struct inet6_dev *idev = NULL;
664 struct neighbour *neigh;
665 int dad = ipv6_addr_any(saddr);
Eric Dumazeta50feda2012-05-18 18:57:34 +0000666 bool inc;
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900667 int is_router = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700668
YOSHIFUJI Hideaki / 吉藤英明115b0aa2013-01-18 02:05:03 +0000669 if (skb->len < sizeof(struct nd_msg)) {
670 ND_PRINTK(2, warn, "NS: packet too short\n");
671 return;
672 }
673
Linus Torvalds1da177e2005-04-16 15:20:36 -0700674 if (ipv6_addr_is_multicast(&msg->target)) {
Joe Perches675418d2012-05-16 19:28:38 +0000675 ND_PRINTK(2, warn, "NS: multicast target address\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700676 return;
677 }
678
679 /*
680 * RFC2461 7.1.1:
681 * DAD has to be destined for solicited node multicast address.
682 */
YOSHIFUJI Hideaki / 吉藤英明ca97a642013-01-20 07:39:00 +0000683 if (dad && !ipv6_addr_is_solict_mult(daddr)) {
Joe Perches675418d2012-05-16 19:28:38 +0000684 ND_PRINTK(2, warn, "NS: bad DAD packet (wrong destination)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700685 return;
686 }
687
688 if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts)) {
Joe Perches675418d2012-05-16 19:28:38 +0000689 ND_PRINTK(2, warn, "NS: invalid ND options\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700690 return;
691 }
692
693 if (ndopts.nd_opts_src_lladdr) {
694 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr, dev);
695 if (!lladdr) {
Joe Perches675418d2012-05-16 19:28:38 +0000696 ND_PRINTK(2, warn,
697 "NS: invalid link-layer address length\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700698 return;
699 }
700
701 /* RFC2461 7.1.1:
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900702 * If the IP source address is the unspecified address,
703 * there MUST NOT be source link-layer address option
Linus Torvalds1da177e2005-04-16 15:20:36 -0700704 * in the message.
705 */
706 if (dad) {
Joe Perches675418d2012-05-16 19:28:38 +0000707 ND_PRINTK(2, warn,
708 "NS: bad DAD packet (link-layer address option)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700709 return;
710 }
711 }
712
713 inc = ipv6_addr_is_multicast(daddr);
714
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900715 ifp = ipv6_get_ifaddr(dev_net(dev), &msg->target, dev, 1);
Daniel Lezcanoa18bc692008-03-07 11:14:49 -0800716 if (ifp) {
Neil Horman95c385b2007-04-25 17:08:10 -0700717
718 if (ifp->flags & (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)) {
719 if (dad) {
Neil Horman95c385b2007-04-25 17:08:10 -0700720 /*
721 * We are colliding with another node
722 * who is doing DAD
723 * so fail our DAD process
724 */
725 addrconf_dad_failure(ifp);
Denis V. Lunev9e3be4b2007-09-11 11:04:49 +0200726 return;
Neil Horman95c385b2007-04-25 17:08:10 -0700727 } else {
728 /*
729 * This is not a dad solicitation.
730 * If we are an optimistic node,
731 * we should respond.
732 * Otherwise, we should ignore it.
733 */
734 if (!(ifp->flags & IFA_F_OPTIMISTIC))
735 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700736 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700737 }
738
739 idev = ifp->idev;
740 } else {
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700741 struct net *net = dev_net(dev);
742
Linus Torvalds1da177e2005-04-16 15:20:36 -0700743 idev = in6_dev_get(dev);
744 if (!idev) {
745 /* XXX: count this drop? */
746 return;
747 }
748
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700749 if (ipv6_chk_acast_addr(net, dev, &msg->target) ||
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900750 (idev->cnf.forwarding &&
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700751 (net->ipv6.devconf_all->proxy_ndp || idev->cnf.proxy_ndp) &&
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900752 (is_router = pndisc_is_router(&msg->target, dev)) >= 0)) {
Patrick McHardya61bbcf2005-08-14 17:24:31 -0700753 if (!(NEIGH_CB(skb)->flags & LOCALLY_ENQUEUED) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700754 skb->pkt_type != PACKET_HOST &&
755 inc != 0 &&
756 idev->nd_parms->proxy_delay != 0) {
757 /*
758 * for anycast or proxy,
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900759 * sender should delay its response
760 * by a random time between 0 and
Linus Torvalds1da177e2005-04-16 15:20:36 -0700761 * MAX_ANYCAST_DELAY_TIME seconds.
762 * (RFC2461) -- yoshfuji
763 */
764 struct sk_buff *n = skb_clone(skb, GFP_ATOMIC);
765 if (n)
766 pneigh_enqueue(&nd_tbl, idev->nd_parms, n);
767 goto out;
768 }
769 } else
770 goto out;
771 }
772
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900773 if (is_router < 0)
YOSHIFUJI Hideaki / 吉藤英明fb568632013-01-20 07:39:18 +0000774 is_router = idev->cnf.forwarding;
Ville Nuorvala62dd9312006-09-22 14:43:19 -0700775
Linus Torvalds1da177e2005-04-16 15:20:36 -0700776 if (dad) {
YOSHIFUJI Hideakif3ee4012008-04-10 15:42:11 +0900777 ndisc_send_na(dev, NULL, &in6addr_linklocal_allnodes, &msg->target,
YOSHIFUJI Hideaki / 吉藤英明fb568632013-01-20 07:39:18 +0000778 !!is_router, false, (ifp != NULL), true);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700779 goto out;
780 }
781
782 if (inc)
783 NEIGH_CACHE_STAT_INC(&nd_tbl, rcv_probes_mcast);
784 else
785 NEIGH_CACHE_STAT_INC(&nd_tbl, rcv_probes_ucast);
786
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900787 /*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700788 * update / create cache entry
789 * for the source address
790 */
791 neigh = __neigh_lookup(&nd_tbl, saddr, dev,
792 !inc || lladdr || !dev->addr_len);
793 if (neigh)
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900794 neigh_update(neigh, lladdr, NUD_STALE,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700795 NEIGH_UPDATE_F_WEAK_OVERRIDE|
796 NEIGH_UPDATE_F_OVERRIDE);
Stephen Hemminger3b04ddd2007-10-09 01:40:57 -0700797 if (neigh || !dev->header_ops) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700798 ndisc_send_na(dev, neigh, saddr, &msg->target,
YOSHIFUJI Hideaki / 吉藤英明fb568632013-01-20 07:39:18 +0000799 !!is_router,
800 true, (ifp != NULL && inc), inc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700801 if (neigh)
802 neigh_release(neigh);
803 }
804
805out:
806 if (ifp)
807 in6_ifa_put(ifp);
808 else
809 in6_dev_put(idev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700810}
811
812static void ndisc_recv_na(struct sk_buff *skb)
813{
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -0700814 struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb);
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000815 const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
816 const struct in6_addr *daddr = &ipv6_hdr(skb)->daddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700817 u8 *lladdr = NULL;
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -0700818 u32 ndoptlen = skb->tail - (skb->transport_header +
819 offsetof(struct nd_msg, opt));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700820 struct ndisc_options ndopts;
821 struct net_device *dev = skb->dev;
822 struct inet6_ifaddr *ifp;
823 struct neighbour *neigh;
824
825 if (skb->len < sizeof(struct nd_msg)) {
Joe Perches675418d2012-05-16 19:28:38 +0000826 ND_PRINTK(2, warn, "NA: packet too short\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700827 return;
828 }
829
830 if (ipv6_addr_is_multicast(&msg->target)) {
Joe Perches675418d2012-05-16 19:28:38 +0000831 ND_PRINTK(2, warn, "NA: target address is multicast\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700832 return;
833 }
834
835 if (ipv6_addr_is_multicast(daddr) &&
836 msg->icmph.icmp6_solicited) {
Joe Perches675418d2012-05-16 19:28:38 +0000837 ND_PRINTK(2, warn, "NA: solicited NA is multicasted\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700838 return;
839 }
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900840
Linus Torvalds1da177e2005-04-16 15:20:36 -0700841 if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts)) {
Joe Perches675418d2012-05-16 19:28:38 +0000842 ND_PRINTK(2, warn, "NS: invalid ND option\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700843 return;
844 }
845 if (ndopts.nd_opts_tgt_lladdr) {
846 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr, dev);
847 if (!lladdr) {
Joe Perches675418d2012-05-16 19:28:38 +0000848 ND_PRINTK(2, warn,
849 "NA: invalid link-layer address length\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700850 return;
851 }
852 }
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900853 ifp = ipv6_get_ifaddr(dev_net(dev), &msg->target, dev, 1);
Daniel Lezcanoa18bc692008-03-07 11:14:49 -0800854 if (ifp) {
Daniel Walterbd015922011-04-13 21:09:25 +0000855 if (skb->pkt_type != PACKET_LOOPBACK
856 && (ifp->flags & IFA_F_TENTATIVE)) {
857 addrconf_dad_failure(ifp);
858 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700859 }
860 /* What should we make now? The advertisement
861 is invalid, but ndisc specs say nothing
862 about it. It could be misconfiguration, or
863 an smart proxy agent tries to help us :-)
Jan Sembera24fc7b82008-12-09 15:48:32 -0800864
865 We should not print the error if NA has been
866 received from loopback - it is just our own
867 unsolicited advertisement.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700868 */
Jan Sembera24fc7b82008-12-09 15:48:32 -0800869 if (skb->pkt_type != PACKET_LOOPBACK)
Joe Perches675418d2012-05-16 19:28:38 +0000870 ND_PRINTK(1, warn,
871 "NA: someone advertises our address %pI6 on %s!\n",
872 &ifp->addr, ifp->idev->dev->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700873 in6_ifa_put(ifp);
874 return;
875 }
876 neigh = neigh_lookup(&nd_tbl, &msg->target, dev);
877
878 if (neigh) {
879 u8 old_flags = neigh->flags;
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700880 struct net *net = dev_net(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700881
882 if (neigh->nud_state & NUD_FAILED)
883 goto out;
884
Ville Nuorvala5f3e6e92006-09-22 14:42:46 -0700885 /*
886 * Don't update the neighbor cache entry on a proxy NA from
887 * ourselves because either the proxied node is off link or it
888 * has already sent a NA to us.
889 */
890 if (lladdr && !memcmp(lladdr, dev->dev_addr, dev->addr_len) &&
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700891 net->ipv6.devconf_all->forwarding && net->ipv6.devconf_all->proxy_ndp &&
892 pneigh_lookup(&nd_tbl, net, &msg->target, dev, 0)) {
Nicolas Dichtelb20b6d92012-11-07 05:05:38 +0000893 /* XXX: idev->cnf.proxy_ndp */
Ville Nuorvala5f3e6e92006-09-22 14:42:46 -0700894 goto out;
YOSHIFUJI Hideakifbea49e2006-09-22 14:43:49 -0700895 }
Ville Nuorvala5f3e6e92006-09-22 14:42:46 -0700896
Linus Torvalds1da177e2005-04-16 15:20:36 -0700897 neigh_update(neigh, lladdr,
898 msg->icmph.icmp6_solicited ? NUD_REACHABLE : NUD_STALE,
899 NEIGH_UPDATE_F_WEAK_OVERRIDE|
900 (msg->icmph.icmp6_override ? NEIGH_UPDATE_F_OVERRIDE : 0)|
901 NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
902 (msg->icmph.icmp6_router ? NEIGH_UPDATE_F_ISROUTER : 0));
903
904 if ((old_flags & ~neigh->flags) & NTF_ROUTER) {
905 /*
906 * Change: router to host
907 */
908 struct rt6_info *rt;
909 rt = rt6_get_dflt_router(saddr, dev);
910 if (rt)
Thomas Grafe0a1ad732006-08-22 00:00:21 -0700911 ip6_del_rt(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700912 }
913
914out:
915 neigh_release(neigh);
916 }
917}
918
919static void ndisc_recv_rs(struct sk_buff *skb)
920{
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -0700921 struct rs_msg *rs_msg = (struct rs_msg *)skb_transport_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700922 unsigned long ndoptlen = skb->len - sizeof(*rs_msg);
923 struct neighbour *neigh;
924 struct inet6_dev *idev;
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000925 const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700926 struct ndisc_options ndopts;
927 u8 *lladdr = NULL;
928
929 if (skb->len < sizeof(*rs_msg))
930 return;
931
Eric Dumazetcfdf7642011-07-27 21:13:03 +0000932 idev = __in6_dev_get(skb->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700933 if (!idev) {
Joe Perches675418d2012-05-16 19:28:38 +0000934 ND_PRINTK(1, err, "RS: can't find in6 device\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700935 return;
936 }
937
938 /* Don't accept RS if we're not in router mode */
939 if (!idev->cnf.forwarding)
940 goto out;
941
942 /*
943 * Don't update NCE if src = ::;
944 * this implies that the source node has no ip address assigned yet.
945 */
946 if (ipv6_addr_any(saddr))
947 goto out;
948
949 /* Parse ND options */
950 if (!ndisc_parse_options(rs_msg->opt, ndoptlen, &ndopts)) {
Joe Perches675418d2012-05-16 19:28:38 +0000951 ND_PRINTK(2, notice, "NS: invalid ND option, ignored\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700952 goto out;
953 }
954
955 if (ndopts.nd_opts_src_lladdr) {
956 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr,
957 skb->dev);
958 if (!lladdr)
959 goto out;
960 }
961
962 neigh = __neigh_lookup(&nd_tbl, saddr, skb->dev, 1);
963 if (neigh) {
964 neigh_update(neigh, lladdr, NUD_STALE,
965 NEIGH_UPDATE_F_WEAK_OVERRIDE|
966 NEIGH_UPDATE_F_OVERRIDE|
967 NEIGH_UPDATE_F_OVERRIDE_ISROUTER);
968 neigh_release(neigh);
969 }
970out:
Eric Dumazetcfdf7642011-07-27 21:13:03 +0000971 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700972}
973
Pierre Ynard31910572007-10-10 21:22:05 -0700974static void ndisc_ra_useropt(struct sk_buff *ra, struct nd_opt_hdr *opt)
975{
976 struct icmp6hdr *icmp6h = (struct icmp6hdr *)skb_transport_header(ra);
977 struct sk_buff *skb;
978 struct nlmsghdr *nlh;
979 struct nduseroptmsg *ndmsg;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900980 struct net *net = dev_net(ra->dev);
Pierre Ynard31910572007-10-10 21:22:05 -0700981 int err;
982 int base_size = NLMSG_ALIGN(sizeof(struct nduseroptmsg)
983 + (opt->nd_opt_len << 3));
984 size_t msg_size = base_size + nla_total_size(sizeof(struct in6_addr));
985
986 skb = nlmsg_new(msg_size, GFP_ATOMIC);
987 if (skb == NULL) {
988 err = -ENOBUFS;
989 goto errout;
990 }
991
992 nlh = nlmsg_put(skb, 0, 0, RTM_NEWNDUSEROPT, base_size, 0);
993 if (nlh == NULL) {
994 goto nla_put_failure;
995 }
996
997 ndmsg = nlmsg_data(nlh);
998 ndmsg->nduseropt_family = AF_INET6;
Pierre Ynarddbb2ed22007-11-12 17:58:35 -0800999 ndmsg->nduseropt_ifindex = ra->dev->ifindex;
Pierre Ynard31910572007-10-10 21:22:05 -07001000 ndmsg->nduseropt_icmp_type = icmp6h->icmp6_type;
1001 ndmsg->nduseropt_icmp_code = icmp6h->icmp6_code;
1002 ndmsg->nduseropt_opts_len = opt->nd_opt_len << 3;
1003
1004 memcpy(ndmsg + 1, opt, opt->nd_opt_len << 3);
1005
David S. Millerc78679e2012-04-01 20:27:33 -04001006 if (nla_put(skb, NDUSEROPT_SRCADDR, sizeof(struct in6_addr),
1007 &ipv6_hdr(ra)->saddr))
1008 goto nla_put_failure;
Pierre Ynard31910572007-10-10 21:22:05 -07001009 nlmsg_end(skb, nlh);
1010
Pablo Neira Ayuso1ce85fe2009-02-24 23:18:28 -08001011 rtnl_notify(skb, net, 0, RTNLGRP_ND_USEROPT, NULL, GFP_ATOMIC);
Pierre Ynard31910572007-10-10 21:22:05 -07001012 return;
1013
1014nla_put_failure:
1015 nlmsg_free(skb);
1016 err = -EMSGSIZE;
1017errout:
Daniel Lezcanoa18bc692008-03-07 11:14:49 -08001018 rtnl_set_sk_err(net, RTNLGRP_ND_USEROPT, err);
Pierre Ynard31910572007-10-10 21:22:05 -07001019}
1020
Linus Torvalds1da177e2005-04-16 15:20:36 -07001021static void ndisc_router_discovery(struct sk_buff *skb)
1022{
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -07001023 struct ra_msg *ra_msg = (struct ra_msg *)skb_transport_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001024 struct neighbour *neigh = NULL;
1025 struct inet6_dev *in6_dev;
YOSHIFUJI Hideaki65f5c7c2006-03-20 16:55:08 -08001026 struct rt6_info *rt = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001027 int lifetime;
1028 struct ndisc_options ndopts;
1029 int optlen;
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001030 unsigned int pref = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001031
1032 __u8 * opt = (__u8 *)(ra_msg + 1);
1033
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -07001034 optlen = (skb->tail - skb->transport_header) - sizeof(struct ra_msg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001035
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001036 if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) {
Joe Perches675418d2012-05-16 19:28:38 +00001037 ND_PRINTK(2, warn, "RA: source address is not link-local\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001038 return;
1039 }
1040 if (optlen < 0) {
Joe Perches675418d2012-05-16 19:28:38 +00001041 ND_PRINTK(2, warn, "RA: packet too short\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001042 return;
1043 }
1044
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001045#ifdef CONFIG_IPV6_NDISC_NODETYPE
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001046 if (skb->ndisc_nodetype == NDISC_NODETYPE_HOST) {
Joe Perches675418d2012-05-16 19:28:38 +00001047 ND_PRINTK(2, warn, "RA: from host or unauthorized router\n");
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001048 return;
1049 }
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001050#endif
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001051
Linus Torvalds1da177e2005-04-16 15:20:36 -07001052 /*
1053 * set the RA_RECV flag in the interface
1054 */
1055
Eric Dumazetcfdf7642011-07-27 21:13:03 +00001056 in6_dev = __in6_dev_get(skb->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001057 if (in6_dev == NULL) {
Joe Perches675418d2012-05-16 19:28:38 +00001058 ND_PRINTK(0, err, "RA: can't find inet6 device for %s\n",
1059 skb->dev->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001060 return;
1061 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001062
1063 if (!ndisc_parse_options(opt, optlen, &ndopts)) {
Joe Perches675418d2012-05-16 19:28:38 +00001064 ND_PRINTK(2, warn, "RA: invalid ND options\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001065 return;
1066 }
1067
Shmulik Ladkaniaeaf6e92012-11-30 10:25:59 +00001068 if (!ipv6_accept_ra(in6_dev))
David Ward31ce8c72009-08-29 00:04:09 -07001069 goto skip_linkparms;
1070
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001071#ifdef CONFIG_IPV6_NDISC_NODETYPE
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001072 /* skip link-specific parameters from interior routers */
1073 if (skb->ndisc_nodetype == NDISC_NODETYPE_NODEFAULT)
1074 goto skip_linkparms;
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001075#endif
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001076
Linus Torvalds1da177e2005-04-16 15:20:36 -07001077 if (in6_dev->if_flags & IF_RS_SENT) {
1078 /*
1079 * flag that an RA was received after an RS was sent
1080 * out on this interface.
1081 */
1082 in6_dev->if_flags |= IF_RA_RCVD;
1083 }
1084
1085 /*
1086 * Remember the managed/otherconf flags from most recently
1087 * received RA message (RFC 2462) -- yoshfuji
1088 */
1089 in6_dev->if_flags = (in6_dev->if_flags & ~(IF_RA_MANAGED |
1090 IF_RA_OTHERCONF)) |
1091 (ra_msg->icmph.icmp6_addrconf_managed ?
1092 IF_RA_MANAGED : 0) |
1093 (ra_msg->icmph.icmp6_addrconf_other ?
1094 IF_RA_OTHERCONF : 0);
1095
YOSHIFUJI Hideaki65f5c7c2006-03-20 16:55:08 -08001096 if (!in6_dev->cnf.accept_ra_defrtr)
1097 goto skip_defrtr;
1098
Andreas Hofmeister9f562202011-10-24 19:13:15 -04001099 if (ipv6_chk_addr(dev_net(in6_dev->dev), &ipv6_hdr(skb)->saddr, NULL, 0))
1100 goto skip_defrtr;
1101
Linus Torvalds1da177e2005-04-16 15:20:36 -07001102 lifetime = ntohs(ra_msg->icmph.icmp6_rt_lifetime);
1103
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001104#ifdef CONFIG_IPV6_ROUTER_PREF
1105 pref = ra_msg->icmph.icmp6_router_pref;
1106 /* 10b is handled as if it were 00b (medium) */
YOSHIFUJI Hideaki930d6ff2006-03-20 17:05:30 -08001107 if (pref == ICMPV6_ROUTER_PREF_INVALID ||
YOSHIFUJI Hideaki6d5b78c2007-06-22 16:07:04 -07001108 !in6_dev->cnf.accept_ra_rtr_pref)
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001109 pref = ICMPV6_ROUTER_PREF_MEDIUM;
1110#endif
1111
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001112 rt = rt6_get_dflt_router(&ipv6_hdr(skb)->saddr, skb->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001113
David S. Millereb857182012-01-27 15:07:56 -08001114 if (rt) {
1115 neigh = dst_neigh_lookup(&rt->dst, &ipv6_hdr(skb)->saddr);
1116 if (!neigh) {
Joe Perches675418d2012-05-16 19:28:38 +00001117 ND_PRINTK(0, err,
1118 "RA: %s got default router without neighbour\n",
1119 __func__);
Amerigo Wang94e187c2012-10-29 00:13:19 +00001120 ip6_rt_put(rt);
David S. Millereb857182012-01-27 15:07:56 -08001121 return;
1122 }
1123 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001124 if (rt && lifetime == 0) {
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001125 ip6_del_rt(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001126 rt = NULL;
1127 }
1128
1129 if (rt == NULL && lifetime) {
Joe Perches675418d2012-05-16 19:28:38 +00001130 ND_PRINTK(3, dbg, "RA: adding default router\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001131
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001132 rt = rt6_add_dflt_router(&ipv6_hdr(skb)->saddr, skb->dev, pref);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001133 if (rt == NULL) {
Joe Perches675418d2012-05-16 19:28:38 +00001134 ND_PRINTK(0, err,
1135 "RA: %s failed to add default route\n",
1136 __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001137 return;
1138 }
1139
David S. Millereb857182012-01-27 15:07:56 -08001140 neigh = dst_neigh_lookup(&rt->dst, &ipv6_hdr(skb)->saddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001141 if (neigh == NULL) {
Joe Perches675418d2012-05-16 19:28:38 +00001142 ND_PRINTK(0, err,
1143 "RA: %s got default router without neighbour\n",
1144 __func__);
Amerigo Wang94e187c2012-10-29 00:13:19 +00001145 ip6_rt_put(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001146 return;
1147 }
1148 neigh->flags |= NTF_ROUTER;
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001149 } else if (rt) {
Pedro Ribeiro22441cf2008-10-15 15:47:49 -07001150 rt->rt6i_flags = (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001151 }
1152
1153 if (rt)
Gao feng1716a962012-04-06 00:13:10 +00001154 rt6_set_expires(rt, jiffies + (HZ * lifetime));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001155 if (ra_msg->icmph.icmp6_hop_limit) {
1156 in6_dev->cnf.hop_limit = ra_msg->icmph.icmp6_hop_limit;
1157 if (rt)
David S. Millerdefb3512010-12-08 21:16:57 -08001158 dst_metric_set(&rt->dst, RTAX_HOPLIMIT,
1159 ra_msg->icmph.icmp6_hop_limit);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001160 }
1161
YOSHIFUJI Hideaki65f5c7c2006-03-20 16:55:08 -08001162skip_defrtr:
1163
Linus Torvalds1da177e2005-04-16 15:20:36 -07001164 /*
1165 * Update Reachable Time and Retrans Timer
1166 */
1167
1168 if (in6_dev->nd_parms) {
1169 unsigned long rtime = ntohl(ra_msg->retrans_timer);
1170
1171 if (rtime && rtime/1000 < MAX_SCHEDULE_TIMEOUT/HZ) {
1172 rtime = (rtime*HZ)/1000;
1173 if (rtime < HZ/10)
1174 rtime = HZ/10;
1175 in6_dev->nd_parms->retrans_time = rtime;
1176 in6_dev->tstamp = jiffies;
1177 inet6_ifinfo_notify(RTM_NEWLINK, in6_dev);
1178 }
1179
1180 rtime = ntohl(ra_msg->reachable_time);
1181 if (rtime && rtime/1000 < MAX_SCHEDULE_TIMEOUT/(3*HZ)) {
1182 rtime = (rtime*HZ)/1000;
1183
1184 if (rtime < HZ/10)
1185 rtime = HZ/10;
1186
1187 if (rtime != in6_dev->nd_parms->base_reachable_time) {
1188 in6_dev->nd_parms->base_reachable_time = rtime;
1189 in6_dev->nd_parms->gc_staletime = 3 * rtime;
1190 in6_dev->nd_parms->reachable_time = neigh_rand_reach_time(rtime);
1191 in6_dev->tstamp = jiffies;
1192 inet6_ifinfo_notify(RTM_NEWLINK, in6_dev);
1193 }
1194 }
1195 }
1196
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001197skip_linkparms:
1198
Linus Torvalds1da177e2005-04-16 15:20:36 -07001199 /*
1200 * Process options.
1201 */
1202
1203 if (!neigh)
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001204 neigh = __neigh_lookup(&nd_tbl, &ipv6_hdr(skb)->saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001205 skb->dev, 1);
1206 if (neigh) {
1207 u8 *lladdr = NULL;
1208 if (ndopts.nd_opts_src_lladdr) {
1209 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr,
1210 skb->dev);
1211 if (!lladdr) {
Joe Perches675418d2012-05-16 19:28:38 +00001212 ND_PRINTK(2, warn,
1213 "RA: invalid link-layer address length\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001214 goto out;
1215 }
1216 }
1217 neigh_update(neigh, lladdr, NUD_STALE,
1218 NEIGH_UPDATE_F_WEAK_OVERRIDE|
1219 NEIGH_UPDATE_F_OVERRIDE|
1220 NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
1221 NEIGH_UPDATE_F_ISROUTER);
1222 }
1223
Shmulik Ladkaniaeaf6e92012-11-30 10:25:59 +00001224 if (!ipv6_accept_ra(in6_dev))
David Ward31ce8c72009-08-29 00:04:09 -07001225 goto out;
1226
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001227#ifdef CONFIG_IPV6_ROUTE_INFO
Andreas Hofmeister9f562202011-10-24 19:13:15 -04001228 if (ipv6_chk_addr(dev_net(in6_dev->dev), &ipv6_hdr(skb)->saddr, NULL, 0))
1229 goto skip_routeinfo;
1230
YOSHIFUJI Hideaki09c884d2006-03-20 17:07:03 -08001231 if (in6_dev->cnf.accept_ra_rtr_pref && ndopts.nd_opts_ri) {
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001232 struct nd_opt_hdr *p;
1233 for (p = ndopts.nd_opts_ri;
1234 p;
1235 p = ndisc_next_option(p, ndopts.nd_opts_ri_end)) {
YOSHIFUJI Hideaki6294e002008-03-15 23:56:52 -04001236 struct route_info *ri = (struct route_info *)p;
1237#ifdef CONFIG_IPV6_NDISC_NODETYPE
1238 if (skb->ndisc_nodetype == NDISC_NODETYPE_NODEFAULT &&
1239 ri->prefix_len == 0)
1240 continue;
1241#endif
1242 if (ri->prefix_len > in6_dev->cnf.accept_ra_rt_info_max_plen)
YOSHIFUJI Hideaki09c884d2006-03-20 17:07:03 -08001243 continue;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001244 rt6_route_rcv(skb->dev, (u8*)p, (p->nd_opt_len) << 3,
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001245 &ipv6_hdr(skb)->saddr);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001246 }
1247 }
Andreas Hofmeister9f562202011-10-24 19:13:15 -04001248
1249skip_routeinfo:
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001250#endif
1251
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001252#ifdef CONFIG_IPV6_NDISC_NODETYPE
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001253 /* skip link-specific ndopts from interior routers */
1254 if (skb->ndisc_nodetype == NDISC_NODETYPE_NODEFAULT)
1255 goto out;
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001256#endif
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001257
YOSHIFUJI Hideakic4fd30e2006-03-20 16:55:26 -08001258 if (in6_dev->cnf.accept_ra_pinfo && ndopts.nd_opts_pi) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001259 struct nd_opt_hdr *p;
1260 for (p = ndopts.nd_opts_pi;
1261 p;
1262 p = ndisc_next_option(p, ndopts.nd_opts_pi_end)) {
Neil Hormane6bff992012-01-04 10:49:15 +00001263 addrconf_prefix_rcv(skb->dev, (u8 *)p,
1264 (p->nd_opt_len) << 3,
1265 ndopts.nd_opts_src_lladdr != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001266 }
1267 }
1268
1269 if (ndopts.nd_opts_mtu) {
Al Viroe69a4adc2006-11-14 20:56:00 -08001270 __be32 n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001271 u32 mtu;
1272
Al Viroe69a4adc2006-11-14 20:56:00 -08001273 memcpy(&n, ((u8*)(ndopts.nd_opts_mtu+1))+2, sizeof(mtu));
1274 mtu = ntohl(n);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001275
1276 if (mtu < IPV6_MIN_MTU || mtu > skb->dev->mtu) {
Joe Perches675418d2012-05-16 19:28:38 +00001277 ND_PRINTK(2, warn, "RA: invalid mtu: %d\n", mtu);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001278 } else if (in6_dev->cnf.mtu6 != mtu) {
1279 in6_dev->cnf.mtu6 = mtu;
1280
1281 if (rt)
David S. Millerdefb3512010-12-08 21:16:57 -08001282 dst_metric_set(&rt->dst, RTAX_MTU, mtu);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001283
1284 rt6_mtu_change(skb->dev, mtu);
1285 }
1286 }
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001287
Pierre Ynard31910572007-10-10 21:22:05 -07001288 if (ndopts.nd_useropts) {
YOSHIFUJI Hideaki61cf46ad2008-01-22 17:32:53 +09001289 struct nd_opt_hdr *p;
1290 for (p = ndopts.nd_useropts;
1291 p;
1292 p = ndisc_next_useropt(p, ndopts.nd_useropts_end)) {
1293 ndisc_ra_useropt(skb, p);
Pierre Ynard31910572007-10-10 21:22:05 -07001294 }
1295 }
1296
Linus Torvalds1da177e2005-04-16 15:20:36 -07001297 if (ndopts.nd_opts_tgt_lladdr || ndopts.nd_opts_rh) {
Joe Perches675418d2012-05-16 19:28:38 +00001298 ND_PRINTK(2, warn, "RA: invalid RA options\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001299 }
1300out:
Amerigo Wang94e187c2012-10-29 00:13:19 +00001301 ip6_rt_put(rt);
David S. Millereb857182012-01-27 15:07:56 -08001302 if (neigh)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001303 neigh_release(neigh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001304}
1305
1306static void ndisc_redirect_rcv(struct sk_buff *skb)
1307{
Duan Jiong093d04d2012-12-14 02:59:59 +00001308 u8 *hdr;
1309 struct ndisc_options ndopts;
1310 struct rd_msg *msg = (struct rd_msg *)skb_transport_header(skb);
1311 u32 ndoptlen = skb->tail - (skb->transport_header +
1312 offsetof(struct rd_msg, opt));
1313
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001314#ifdef CONFIG_IPV6_NDISC_NODETYPE
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001315 switch (skb->ndisc_nodetype) {
1316 case NDISC_NODETYPE_HOST:
1317 case NDISC_NODETYPE_NODEFAULT:
Joe Perches675418d2012-05-16 19:28:38 +00001318 ND_PRINTK(2, warn,
1319 "Redirect: from host or unauthorized router\n");
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001320 return;
1321 }
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001322#endif
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001323
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001324 if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) {
Joe Perches675418d2012-05-16 19:28:38 +00001325 ND_PRINTK(2, warn,
1326 "Redirect: source address is not link-local\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001327 return;
1328 }
1329
Duan Jiong093d04d2012-12-14 02:59:59 +00001330 if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts))
1331 return;
1332
1333 if (!ndopts.nd_opts_rh)
1334 return;
1335
1336 hdr = (u8 *)ndopts.nd_opts_rh;
1337 hdr += 8;
1338 if (!pskb_pull(skb, hdr - skb_transport_header(skb)))
1339 return;
1340
David S. Millerb94f1c02012-07-12 00:33:37 -07001341 icmpv6_notify(skb, NDISC_REDIRECT, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001342}
1343
YOSHIFUJI Hideaki / 吉藤英明9c86daf2013-01-21 06:48:09 +00001344static u8 *ndisc_fill_redirect_hdr_option(u8 *opt, struct sk_buff *orig_skb,
1345 int rd_len)
1346{
1347 memset(opt, 0, 8);
1348 *(opt++) = ND_OPT_REDIRECT_HDR;
1349 *(opt++) = (rd_len >> 3);
1350 opt += 6;
1351
1352 memcpy(opt, ipv6_hdr(orig_skb), rd_len - 8);
1353
1354 return opt + rd_len - 8;
1355}
1356
David S. Miller49919692012-01-27 15:30:48 -08001357void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001358{
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001359 struct net_device *dev = skb->dev;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001360 struct net *net = dev_net(dev);
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001361 struct sock *sk = net->ipv6.ndisc_sk;
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00001362 int len = sizeof(struct rd_msg);
David S. Millerfbfe95a2012-06-08 23:24:18 -07001363 struct inet_peer *peer;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001364 struct sk_buff *buff;
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00001365 struct rd_msg *msg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001366 struct in6_addr saddr_buf;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001367 struct rt6_info *rt;
1368 struct dst_entry *dst;
1369 struct inet6_dev *idev;
David S. Miller4c9483b2011-03-12 16:22:43 -05001370 struct flowi6 fl6;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001371 u8 *opt;
Herbert Xua7ae1992011-11-18 02:20:04 +00001372 int hlen, tlen;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001373 int rd_len;
1374 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001375 u8 ha_buf[MAX_ADDR_LEN], *ha = NULL;
David S. Miller1d861aa2012-07-10 03:58:16 -07001376 bool ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001377
Neil Horman95c385b2007-04-25 17:08:10 -07001378 if (ipv6_get_lladdr(dev, &saddr_buf, IFA_F_TENTATIVE)) {
Joe Perches675418d2012-05-16 19:28:38 +00001379 ND_PRINTK(2, warn, "Redirect: no link-local address on %s\n",
1380 dev->name);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001381 return;
1382 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001383
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001384 if (!ipv6_addr_equal(&ipv6_hdr(skb)->daddr, target) &&
Brian Haleybf0b48d2007-10-08 00:12:05 -07001385 ipv6_addr_type(target) != (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) {
Joe Perches675418d2012-05-16 19:28:38 +00001386 ND_PRINTK(2, warn,
1387 "Redirect: target address is not link-local unicast\n");
Li Yewang29556522007-01-30 14:33:20 -08001388 return;
1389 }
1390
David S. Miller4c9483b2011-03-12 16:22:43 -05001391 icmpv6_flow_init(sk, &fl6, NDISC_REDIRECT,
YOSHIFUJI Hideaki95e41e92007-12-06 15:43:30 -08001392 &saddr_buf, &ipv6_hdr(skb)->saddr, dev->ifindex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001393
David S. Miller4c9483b2011-03-12 16:22:43 -05001394 dst = ip6_route_output(net, NULL, &fl6);
RongQing.Li5095d642012-02-21 22:10:49 +00001395 if (dst->error) {
1396 dst_release(dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001397 return;
RongQing.Li5095d642012-02-21 22:10:49 +00001398 }
David S. Miller4c9483b2011-03-12 16:22:43 -05001399 dst = xfrm_lookup(net, dst, flowi6_to_flowi(&fl6), NULL, 0);
David S. Miller452edd52011-03-02 13:27:41 -08001400 if (IS_ERR(dst))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001401 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001402
1403 rt = (struct rt6_info *) dst;
1404
1405 if (rt->rt6i_flags & RTF_GATEWAY) {
Joe Perches675418d2012-05-16 19:28:38 +00001406 ND_PRINTK(2, warn,
1407 "Redirect: destination is not a neighbour\n");
Ilpo Järvinend73f0802009-02-06 23:47:37 -08001408 goto release;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001409 }
David S. Miller1d861aa2012-07-10 03:58:16 -07001410 peer = inet_getpeer_v6(net->ipv6.peers, &rt->rt6i_dst.addr, 1);
1411 ret = inet_peer_xrlim_allow(peer, 1*HZ);
1412 if (peer)
1413 inet_putpeer(peer);
1414 if (!ret)
Ilpo Järvinend73f0802009-02-06 23:47:37 -08001415 goto release;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001416
1417 if (dev->addr_len) {
David S. Miller49919692012-01-27 15:30:48 -08001418 struct neighbour *neigh = dst_neigh_lookup(skb_dst(skb), target);
1419 if (!neigh) {
Joe Perches675418d2012-05-16 19:28:38 +00001420 ND_PRINTK(2, warn,
1421 "Redirect: no neigh for target address\n");
David S. Miller49919692012-01-27 15:30:48 -08001422 goto release;
1423 }
1424
Linus Torvalds1da177e2005-04-16 15:20:36 -07001425 read_lock_bh(&neigh->lock);
1426 if (neigh->nud_state & NUD_VALID) {
1427 memcpy(ha_buf, neigh->ha, dev->addr_len);
1428 read_unlock_bh(&neigh->lock);
1429 ha = ha_buf;
1430 len += ndisc_opt_addr_space(dev);
1431 } else
1432 read_unlock_bh(&neigh->lock);
David S. Miller49919692012-01-27 15:30:48 -08001433
1434 neigh_release(neigh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001435 }
1436
1437 rd_len = min_t(unsigned int,
1438 IPV6_MIN_MTU-sizeof(struct ipv6hdr)-len, skb->len + 8);
1439 rd_len &= ~0x7;
1440 len += rd_len;
1441
Herbert Xua7ae1992011-11-18 02:20:04 +00001442 hlen = LL_RESERVED_SPACE(dev);
1443 tlen = dev->needed_tailroom;
David S. Millerd54a81d2006-12-02 21:00:06 -08001444 buff = sock_alloc_send_skb(sk,
YOSHIFUJI Hideaki / 吉藤英明b7dc8c32013-01-04 03:58:04 +00001445 (sizeof(struct ipv6hdr) +
Herbert Xua7ae1992011-11-18 02:20:04 +00001446 len + hlen + tlen),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001447 1, &err);
1448 if (buff == NULL) {
Joe Perches675418d2012-05-16 19:28:38 +00001449 ND_PRINTK(0, err,
1450 "Redirect: %s failed to allocate an skb, err=%d\n",
1451 __func__, err);
Ilpo Järvinend73f0802009-02-06 23:47:37 -08001452 goto release;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001453 }
1454
Herbert Xua7ae1992011-11-18 02:20:04 +00001455 skb_reserve(buff, hlen);
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001456 ip6_nd_hdr(sk, buff, dev, &saddr_buf, &ipv6_hdr(skb)->saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001457 IPPROTO_ICMPV6, len);
1458
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -07001459 skb_set_transport_header(buff, skb_tail_pointer(buff) - buff->data);
Arnaldo Carvalho de Melod10ba342007-03-14 21:05:37 -03001460 skb_put(buff, len);
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00001461 msg = (struct rd_msg *)icmp6_hdr(buff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001462
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00001463 memset(&msg->icmph, 0, sizeof(struct icmp6hdr));
1464 msg->icmph.icmp6_type = NDISC_REDIRECT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001465
1466 /*
1467 * copy target and destination addresses
1468 */
1469
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00001470 msg->target = *target;
1471 msg->dest = ipv6_hdr(skb)->daddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001472
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00001473 opt = msg->opt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001474
1475 /*
1476 * include target_address option
1477 */
1478
1479 if (ha)
YOSHIFUJI Hideaki / 吉藤英明315ff092013-01-21 06:47:50 +00001480 opt = ndisc_fill_addr_option(opt, ND_OPT_TARGET_LL_ADDR, ha, dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001481
1482 /*
1483 * build redirect option and copy skb over to the new packet.
1484 */
1485
YOSHIFUJI Hideaki / 吉藤英明9c86daf2013-01-21 06:48:09 +00001486 if (rd_len)
1487 opt = ndisc_fill_redirect_hdr_option(opt, skb, rd_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001488
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00001489 msg->icmph.icmp6_cksum = csum_ipv6_magic(&saddr_buf, &ipv6_hdr(skb)->saddr,
1490 len, IPPROTO_ICMPV6,
1491 csum_partial(msg, len, 0));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001492
Eric Dumazetadf30902009-06-02 05:19:30 +00001493 skb_dst_set(buff, dst);
Eric Dumazetcfdf7642011-07-27 21:13:03 +00001494 rcu_read_lock();
1495 idev = __in6_dev_get(dst->dev);
Neil Hormanedf391f2009-04-27 02:45:02 -07001496 IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUT, skb->len);
Jan Engelhardtb2e0b382010-03-23 04:09:07 +01001497 err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, buff, NULL, dst->dev,
Patrick McHardy6e23ae22007-11-19 18:53:30 -08001498 dst_output);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001499 if (!err) {
Denis V. Lunev5c5d2442008-10-08 10:33:50 -07001500 ICMP6MSGOUT_INC_STATS(net, idev, NDISC_REDIRECT);
Denis V. Luneva862f6a2008-10-08 10:33:06 -07001501 ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001502 }
1503
Eric Dumazetcfdf7642011-07-27 21:13:03 +00001504 rcu_read_unlock();
Ilpo Järvinend73f0802009-02-06 23:47:37 -08001505 return;
1506
1507release:
1508 dst_release(dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001509}
1510
1511static void pndisc_redo(struct sk_buff *skb)
1512{
YOSHIFUJI Hideaki140e26fc2005-10-05 12:11:41 -07001513 ndisc_recv_ns(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001514 kfree_skb(skb);
1515}
1516
1517int ndisc_rcv(struct sk_buff *skb)
1518{
1519 struct nd_msg *msg;
1520
YOSHIFUJI Hideaki / 吉藤英明6bce6b42013-01-21 06:48:03 +00001521 if (skb_linearize(skb))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001522 return 0;
1523
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -07001524 msg = (struct nd_msg *)skb_transport_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001525
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -07001526 __skb_push(skb, skb->data - skb_transport_header(skb));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001527
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001528 if (ipv6_hdr(skb)->hop_limit != 255) {
Joe Perches675418d2012-05-16 19:28:38 +00001529 ND_PRINTK(2, warn, "NDISC: invalid hop-limit: %d\n",
1530 ipv6_hdr(skb)->hop_limit);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001531 return 0;
1532 }
1533
1534 if (msg->icmph.icmp6_code != 0) {
Joe Perches675418d2012-05-16 19:28:38 +00001535 ND_PRINTK(2, warn, "NDISC: invalid ICMPv6 code: %d\n",
1536 msg->icmph.icmp6_code);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001537 return 0;
1538 }
1539
Patrick McHardya61bbcf2005-08-14 17:24:31 -07001540 memset(NEIGH_CB(skb), 0, sizeof(struct neighbour_cb));
1541
Linus Torvalds1da177e2005-04-16 15:20:36 -07001542 switch (msg->icmph.icmp6_type) {
1543 case NDISC_NEIGHBOUR_SOLICITATION:
1544 ndisc_recv_ns(skb);
1545 break;
1546
1547 case NDISC_NEIGHBOUR_ADVERTISEMENT:
1548 ndisc_recv_na(skb);
1549 break;
1550
1551 case NDISC_ROUTER_SOLICITATION:
1552 ndisc_recv_rs(skb);
1553 break;
1554
1555 case NDISC_ROUTER_ADVERTISEMENT:
1556 ndisc_router_discovery(skb);
1557 break;
1558
1559 case NDISC_REDIRECT:
1560 ndisc_redirect_rcv(skb);
1561 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -07001562 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001563
1564 return 0;
1565}
1566
1567static int ndisc_netdev_event(struct notifier_block *this, unsigned long event, void *ptr)
1568{
1569 struct net_device *dev = ptr;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001570 struct net *net = dev_net(dev);
Hannes Frederic Sowa5cb04432012-11-06 16:46:20 +00001571 struct inet6_dev *idev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001572
1573 switch (event) {
1574 case NETDEV_CHANGEADDR:
1575 neigh_changeaddr(&nd_tbl, dev);
Daniel Lezcano5b7c9312008-03-03 23:28:58 -08001576 fib6_run_gc(~0UL, net);
Hannes Frederic Sowa5cb04432012-11-06 16:46:20 +00001577 idev = in6_dev_get(dev);
1578 if (!idev)
1579 break;
1580 if (idev->cnf.ndisc_notify)
1581 ndisc_send_unsol_na(dev);
1582 in6_dev_put(idev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001583 break;
1584 case NETDEV_DOWN:
1585 neigh_ifdown(&nd_tbl, dev);
Daniel Lezcano5b7c9312008-03-03 23:28:58 -08001586 fib6_run_gc(~0UL, net);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001587 break;
Ben Hutchingsf47b9462011-04-15 13:46:02 +00001588 case NETDEV_NOTIFY_PEERS:
1589 ndisc_send_unsol_na(dev);
1590 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001591 default:
1592 break;
1593 }
1594
1595 return NOTIFY_DONE;
1596}
1597
1598static struct notifier_block ndisc_netdev_notifier = {
1599 .notifier_call = ndisc_netdev_event,
1600};
1601
1602#ifdef CONFIG_SYSCTL
1603static void ndisc_warn_deprecated_sysctl(struct ctl_table *ctl,
1604 const char *func, const char *dev_name)
1605{
1606 static char warncomm[TASK_COMM_LEN];
1607 static int warned;
1608 if (strcmp(warncomm, current->comm) && warned < 5) {
1609 strcpy(warncomm, current->comm);
Joe Perchesf3213832012-05-15 14:11:53 +00001610 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 -07001611 warncomm, func,
1612 dev_name, ctl->procname,
1613 dev_name, ctl->procname);
1614 warned++;
1615 }
1616}
1617
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001618int 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 -07001619{
1620 struct net_device *dev = ctl->extra1;
1621 struct inet6_dev *idev;
1622 int ret;
1623
Eric W. Biedermand12af672007-10-18 03:05:25 -07001624 if ((strcmp(ctl->procname, "retrans_time") == 0) ||
1625 (strcmp(ctl->procname, "base_reachable_time") == 0))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001626 ndisc_warn_deprecated_sysctl(ctl, "syscall", dev ? dev->name : "default");
1627
Eric W. Biedermand12af672007-10-18 03:05:25 -07001628 if (strcmp(ctl->procname, "retrans_time") == 0)
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001629 ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
Eric W. Biedermand12af672007-10-18 03:05:25 -07001630
1631 else if (strcmp(ctl->procname, "base_reachable_time") == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001632 ret = proc_dointvec_jiffies(ctl, write,
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001633 buffer, lenp, ppos);
Eric W. Biedermand12af672007-10-18 03:05:25 -07001634
1635 else if ((strcmp(ctl->procname, "retrans_time_ms") == 0) ||
YOSHIFUJI Hideakiad02ac12007-10-29 01:32:23 -07001636 (strcmp(ctl->procname, "base_reachable_time_ms") == 0))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001637 ret = proc_dointvec_ms_jiffies(ctl, write,
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001638 buffer, lenp, ppos);
Eric W. Biedermand12af672007-10-18 03:05:25 -07001639 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001640 ret = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001641
1642 if (write && ret == 0 && dev && (idev = in6_dev_get(dev)) != NULL) {
Eric W. Biedermand12af672007-10-18 03:05:25 -07001643 if (ctl->data == &idev->nd_parms->base_reachable_time)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001644 idev->nd_parms->reachable_time = neigh_rand_reach_time(idev->nd_parms->base_reachable_time);
1645 idev->tstamp = jiffies;
1646 inet6_ifinfo_notify(RTM_NEWLINK, idev);
1647 in6_dev_put(idev);
1648 }
1649 return ret;
1650}
1651
Linus Torvalds1da177e2005-04-16 15:20:36 -07001652
1653#endif
1654
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00001655static int __net_init ndisc_net_init(struct net *net)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001656{
1657 struct ipv6_pinfo *np;
1658 struct sock *sk;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001659 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001660
Denis V. Lunev1ed85162008-04-03 14:31:03 -07001661 err = inet_ctl_sock_create(&sk, PF_INET6,
1662 SOCK_RAW, IPPROTO_ICMPV6, net);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001663 if (err < 0) {
Joe Perches675418d2012-05-16 19:28:38 +00001664 ND_PRINTK(0, err,
1665 "NDISC: Failed to initialize the control socket (err %d)\n",
1666 err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001667 return err;
1668 }
1669
Denis V. Lunev1ed85162008-04-03 14:31:03 -07001670 net->ipv6.ndisc_sk = sk;
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001671
Linus Torvalds1da177e2005-04-16 15:20:36 -07001672 np = inet6_sk(sk);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001673 np->hop_limit = 255;
1674 /* Do not loopback ndisc messages */
1675 np->mc_loop = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001676
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001677 return 0;
1678}
1679
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00001680static void __net_exit ndisc_net_exit(struct net *net)
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001681{
Denis V. Lunev1ed85162008-04-03 14:31:03 -07001682 inet_ctl_sock_destroy(net->ipv6.ndisc_sk);
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001683}
1684
1685static struct pernet_operations ndisc_net_ops = {
1686 .init = ndisc_net_init,
1687 .exit = ndisc_net_exit,
1688};
1689
1690int __init ndisc_init(void)
1691{
1692 int err;
1693
1694 err = register_pernet_subsys(&ndisc_net_ops);
1695 if (err)
1696 return err;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001697 /*
1698 * Initialize the neighbour table
1699 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001700 neigh_table_init(&nd_tbl);
1701
1702#ifdef CONFIG_SYSCTL
Eric W. Biederman54716e32010-02-14 03:27:03 +00001703 err = neigh_sysctl_register(NULL, &nd_tbl.parms, "ipv6",
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001704 &ndisc_ifinfo_sysctl_change);
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001705 if (err)
1706 goto out_unregister_pernet;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001707#endif
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001708 err = register_netdevice_notifier(&ndisc_netdev_notifier);
1709 if (err)
1710 goto out_unregister_sysctl;
1711out:
1712 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001713
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001714out_unregister_sysctl:
1715#ifdef CONFIG_SYSCTL
1716 neigh_sysctl_unregister(&nd_tbl.parms);
1717out_unregister_pernet:
1718#endif
1719 unregister_pernet_subsys(&ndisc_net_ops);
1720 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001721}
1722
1723void ndisc_cleanup(void)
1724{
Dmitry Mishin36f73d02006-11-03 16:08:19 -08001725 unregister_netdevice_notifier(&ndisc_netdev_notifier);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001726#ifdef CONFIG_SYSCTL
1727 neigh_sysctl_unregister(&nd_tbl.parms);
1728#endif
1729 neigh_table_clear(&nd_tbl);
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001730 unregister_pernet_subsys(&ndisc_net_ops);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001731}