blob: a3189baa9f4f2ead71b9a1d79ae2415d5cd577d8 [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 -0700146#define NDISC_OPT_SPACE(len) (((len)+2+7)&~7)
147
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148static inline int ndisc_opt_addr_space(struct net_device *dev)
149{
150 return NDISC_OPT_SPACE(dev->addr_len + ndisc_addr_option_pad(dev->type));
151}
152
153static u8 *ndisc_fill_addr_option(u8 *opt, int type, void *data, int data_len,
154 unsigned short addr_type)
155{
156 int space = NDISC_OPT_SPACE(data_len);
157 int pad = ndisc_addr_option_pad(addr_type);
158
159 opt[0] = type;
160 opt[1] = space>>3;
161
162 memset(opt + 2, 0, pad);
163 opt += pad;
164 space -= pad;
165
166 memcpy(opt+2, data, data_len);
167 data_len += 2;
168 opt += data_len;
169 if ((space -= data_len) > 0)
170 memset(opt, 0, space);
171 return opt + space;
172}
173
174static struct nd_opt_hdr *ndisc_next_option(struct nd_opt_hdr *cur,
175 struct nd_opt_hdr *end)
176{
177 int type;
178 if (!cur || !end || cur >= end)
179 return NULL;
180 type = cur->nd_opt_type;
181 do {
182 cur = ((void *)cur) + (cur->nd_opt_len << 3);
183 } while(cur < end && cur->nd_opt_type != type);
Eric Dumazeta02cec22010-09-22 20:43:57 +0000184 return cur <= end && cur->nd_opt_type == type ? cur : NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185}
186
Pierre Ynard31910572007-10-10 21:22:05 -0700187static inline int ndisc_is_useropt(struct nd_opt_hdr *opt)
188{
Alexey I. Froloffe35f30c2012-04-06 05:50:58 +0000189 return opt->nd_opt_type == ND_OPT_RDNSS ||
190 opt->nd_opt_type == ND_OPT_DNSSL;
Pierre Ynard31910572007-10-10 21:22:05 -0700191}
192
193static struct nd_opt_hdr *ndisc_next_useropt(struct nd_opt_hdr *cur,
194 struct nd_opt_hdr *end)
195{
196 if (!cur || !end || cur >= end)
197 return NULL;
198 do {
199 cur = ((void *)cur) + (cur->nd_opt_len << 3);
200 } while(cur < end && !ndisc_is_useropt(cur));
Eric Dumazeta02cec22010-09-22 20:43:57 +0000201 return cur <= end && ndisc_is_useropt(cur) ? cur : NULL;
Pierre Ynard31910572007-10-10 21:22:05 -0700202}
203
David S. Miller30f2a5f2012-07-11 23:26:46 -0700204struct ndisc_options *ndisc_parse_options(u8 *opt, int opt_len,
205 struct ndisc_options *ndopts)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700206{
207 struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)opt;
208
209 if (!nd_opt || opt_len < 0 || !ndopts)
210 return NULL;
211 memset(ndopts, 0, sizeof(*ndopts));
212 while (opt_len) {
213 int l;
214 if (opt_len < sizeof(struct nd_opt_hdr))
215 return NULL;
216 l = nd_opt->nd_opt_len << 3;
217 if (opt_len < l || l == 0)
218 return NULL;
219 switch (nd_opt->nd_opt_type) {
220 case ND_OPT_SOURCE_LL_ADDR:
221 case ND_OPT_TARGET_LL_ADDR:
222 case ND_OPT_MTU:
223 case ND_OPT_REDIRECT_HDR:
224 if (ndopts->nd_opt_array[nd_opt->nd_opt_type]) {
Joe Perches675418d2012-05-16 19:28:38 +0000225 ND_PRINTK(2, warn,
226 "%s: duplicated ND6 option found: type=%d\n",
227 __func__, nd_opt->nd_opt_type);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700228 } else {
229 ndopts->nd_opt_array[nd_opt->nd_opt_type] = nd_opt;
230 }
231 break;
232 case ND_OPT_PREFIX_INFO:
233 ndopts->nd_opts_pi_end = nd_opt;
Stephen Hemmingercfcabdc2007-10-09 01:59:42 -0700234 if (!ndopts->nd_opt_array[nd_opt->nd_opt_type])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700235 ndopts->nd_opt_array[nd_opt->nd_opt_type] = nd_opt;
236 break;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800237#ifdef CONFIG_IPV6_ROUTE_INFO
238 case ND_OPT_ROUTE_INFO:
239 ndopts->nd_opts_ri_end = nd_opt;
240 if (!ndopts->nd_opts_ri)
241 ndopts->nd_opts_ri = nd_opt;
242 break;
243#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244 default:
Pierre Ynard31910572007-10-10 21:22:05 -0700245 if (ndisc_is_useropt(nd_opt)) {
246 ndopts->nd_useropts_end = nd_opt;
247 if (!ndopts->nd_useropts)
248 ndopts->nd_useropts = nd_opt;
249 } else {
250 /*
251 * Unknown options must be silently ignored,
252 * to accommodate future extension to the
253 * protocol.
254 */
Joe Perches675418d2012-05-16 19:28:38 +0000255 ND_PRINTK(2, notice,
256 "%s: ignored unsupported option; type=%d, len=%d\n",
257 __func__,
258 nd_opt->nd_opt_type,
259 nd_opt->nd_opt_len);
Pierre Ynard31910572007-10-10 21:22:05 -0700260 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700261 }
262 opt_len -= l;
263 nd_opt = ((void *)nd_opt) + l;
264 }
265 return ndopts;
266}
267
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000268int ndisc_mc_map(const struct in6_addr *addr, char *buf, struct net_device *dev, int dir)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700269{
270 switch (dev->type) {
271 case ARPHRD_ETHER:
272 case ARPHRD_IEEE802: /* Not sure. Check it later. --ANK */
273 case ARPHRD_FDDI:
274 ipv6_eth_mc_map(addr, buf);
275 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700276 case ARPHRD_ARCNET:
277 ipv6_arcnet_mc_map(addr, buf);
278 return 0;
279 case ARPHRD_INFINIBAND:
Rolf Manderscheida9e527e2007-12-10 13:38:41 -0700280 ipv6_ib_mc_map(addr, dev->broadcast, buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281 return 0;
Timo Teräs93ca3bb2011-03-28 22:40:53 +0000282 case ARPHRD_IPGRE:
283 return ipv6_ipgre_mc_map(addr, dev->broadcast, buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700284 default:
285 if (dir) {
286 memcpy(buf, dev->broadcast, dev->addr_len);
287 return 0;
288 }
289 }
290 return -EINVAL;
291}
292
YOSHIFUJI Hideaki71590392007-02-22 22:05:40 +0900293EXPORT_SYMBOL(ndisc_mc_map);
294
Eric Dumazetd6bf7812010-10-04 06:15:44 +0000295static u32 ndisc_hash(const void *pkey,
296 const struct net_device *dev,
David S. Miller2c2aba62011-12-28 15:06:58 -0500297 __u32 *hash_rnd)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298{
David S. Miller2c2aba62011-12-28 15:06:58 -0500299 return ndisc_hashfn(pkey, dev, hash_rnd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700300}
301
302static int ndisc_constructor(struct neighbour *neigh)
303{
304 struct in6_addr *addr = (struct in6_addr*)&neigh->primary_key;
305 struct net_device *dev = neigh->dev;
306 struct inet6_dev *in6_dev;
307 struct neigh_parms *parms;
Eric Dumazeta50feda2012-05-18 18:57:34 +0000308 bool is_multicast = ipv6_addr_is_multicast(addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700309
Linus Torvalds1da177e2005-04-16 15:20:36 -0700310 in6_dev = in6_dev_get(dev);
311 if (in6_dev == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700312 return -EINVAL;
313 }
314
315 parms = in6_dev->nd_parms;
316 __neigh_parms_put(neigh->parms);
317 neigh->parms = neigh_parms_clone(parms);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700318
319 neigh->type = is_multicast ? RTN_MULTICAST : RTN_UNICAST;
Stephen Hemminger3b04ddd2007-10-09 01:40:57 -0700320 if (!dev->header_ops) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700321 neigh->nud_state = NUD_NOARP;
322 neigh->ops = &ndisc_direct_ops;
David S. Miller8f40b162011-07-17 13:34:11 -0700323 neigh->output = neigh_direct_output;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700324 } else {
325 if (is_multicast) {
326 neigh->nud_state = NUD_NOARP;
327 ndisc_mc_map(addr, neigh->ha, dev, 1);
328 } else if (dev->flags&(IFF_NOARP|IFF_LOOPBACK)) {
329 neigh->nud_state = NUD_NOARP;
330 memcpy(neigh->ha, dev->dev_addr, dev->addr_len);
331 if (dev->flags&IFF_LOOPBACK)
332 neigh->type = RTN_LOCAL;
333 } else if (dev->flags&IFF_POINTOPOINT) {
334 neigh->nud_state = NUD_NOARP;
335 memcpy(neigh->ha, dev->broadcast, dev->addr_len);
336 }
Stephen Hemminger3b04ddd2007-10-09 01:40:57 -0700337 if (dev->header_ops->cache)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338 neigh->ops = &ndisc_hh_ops;
339 else
340 neigh->ops = &ndisc_generic_ops;
341 if (neigh->nud_state&NUD_VALID)
342 neigh->output = neigh->ops->connected_output;
343 else
344 neigh->output = neigh->ops->output;
345 }
346 in6_dev_put(in6_dev);
347 return 0;
348}
349
350static int pndisc_constructor(struct pneigh_entry *n)
351{
352 struct in6_addr *addr = (struct in6_addr*)&n->key;
353 struct in6_addr maddr;
354 struct net_device *dev = n->dev;
355
356 if (dev == NULL || __in6_dev_get(dev) == NULL)
357 return -EINVAL;
358 addrconf_addr_solict_mult(addr, &maddr);
359 ipv6_dev_mc_inc(dev, &maddr);
360 return 0;
361}
362
363static void pndisc_destructor(struct pneigh_entry *n)
364{
365 struct in6_addr *addr = (struct in6_addr*)&n->key;
366 struct in6_addr maddr;
367 struct net_device *dev = n->dev;
368
369 if (dev == NULL || __in6_dev_get(dev) == NULL)
370 return;
371 addrconf_addr_solict_mult(addr, &maddr);
372 ipv6_dev_mc_dec(dev, &maddr);
373}
374
Brian Haley305d5522008-11-04 17:51:14 -0800375struct sk_buff *ndisc_build_skb(struct net_device *dev,
376 const struct in6_addr *daddr,
377 const struct in6_addr *saddr,
378 struct icmp6hdr *icmp6h,
379 const struct in6_addr *target,
380 int llinfo)
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900381{
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900382 struct net *net = dev_net(dev);
Daniel Lezcano1762f7e2008-03-07 11:15:34 -0800383 struct sock *sk = net->ipv6.ndisc_sk;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900384 struct sk_buff *skb;
385 struct icmp6hdr *hdr;
Herbert Xua7ae1992011-11-18 02:20:04 +0000386 int hlen = LL_RESERVED_SPACE(dev);
387 int tlen = dev->needed_tailroom;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900388 int len;
389 int err;
Brian Haley305d5522008-11-04 17:51:14 -0800390 u8 *opt;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900391
392 if (!dev->addr_len)
393 llinfo = 0;
394
395 len = sizeof(struct icmp6hdr) + (target ? sizeof(*target) : 0);
396 if (llinfo)
397 len += ndisc_opt_addr_space(dev);
398
399 skb = sock_alloc_send_skb(sk,
400 (MAX_HEADER + sizeof(struct ipv6hdr) +
Herbert Xua7ae1992011-11-18 02:20:04 +0000401 len + hlen + tlen),
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900402 1, &err);
403 if (!skb) {
Joe Perches675418d2012-05-16 19:28:38 +0000404 ND_PRINTK(0, err, "ND: %s failed to allocate an skb, err=%d\n",
405 __func__, err);
Brian Haley305d5522008-11-04 17:51:14 -0800406 return NULL;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900407 }
408
Herbert Xua7ae1992011-11-18 02:20:04 +0000409 skb_reserve(skb, hlen);
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900410 ip6_nd_hdr(sk, skb, dev, saddr, daddr, IPPROTO_ICMPV6, len);
411
412 skb->transport_header = skb->tail;
413 skb_put(skb, len);
414
415 hdr = (struct icmp6hdr *)skb_transport_header(skb);
416 memcpy(hdr, icmp6h, sizeof(*hdr));
417
418 opt = skb_transport_header(skb) + sizeof(struct icmp6hdr);
419 if (target) {
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +0000420 *(struct in6_addr *)opt = *target;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900421 opt += sizeof(*target);
422 }
423
424 if (llinfo)
425 ndisc_fill_addr_option(opt, llinfo, dev->dev_addr,
426 dev->addr_len, dev->type);
427
428 hdr->icmp6_cksum = csum_ipv6_magic(saddr, daddr, len,
429 IPPROTO_ICMPV6,
Joe Perches07f07572008-11-19 15:44:53 -0800430 csum_partial(hdr,
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900431 len, 0));
432
Brian Haley305d5522008-11-04 17:51:14 -0800433 return skb;
434}
435
436EXPORT_SYMBOL(ndisc_build_skb);
437
438void ndisc_send_skb(struct sk_buff *skb,
439 struct net_device *dev,
440 struct neighbour *neigh,
441 const struct in6_addr *daddr,
442 const struct in6_addr *saddr,
443 struct icmp6hdr *icmp6h)
444{
David S. Miller4c9483b2011-03-12 16:22:43 -0500445 struct flowi6 fl6;
Brian Haley305d5522008-11-04 17:51:14 -0800446 struct dst_entry *dst;
447 struct net *net = dev_net(dev);
448 struct sock *sk = net->ipv6.ndisc_sk;
449 struct inet6_dev *idev;
450 int err;
451 u8 type;
452
453 type = icmp6h->icmp6_type;
454
David S. Miller4c9483b2011-03-12 16:22:43 -0500455 icmpv6_flow_init(sk, &fl6, type, saddr, daddr, dev->ifindex);
David S. Miller87a11572011-12-06 17:04:13 -0500456 dst = icmp6_dst_alloc(dev, neigh, &fl6);
David S. Miller452edd52011-03-02 13:27:41 -0800457 if (IS_ERR(dst)) {
Brian Haley305d5522008-11-04 17:51:14 -0800458 kfree_skb(skb);
459 return;
460 }
461
Eric Dumazetadf30902009-06-02 05:19:30 +0000462 skb_dst_set(skb, dst);
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900463
Eric Dumazetcfdf7642011-07-27 21:13:03 +0000464 rcu_read_lock();
465 idev = __in6_dev_get(dst->dev);
Neil Hormanedf391f2009-04-27 02:45:02 -0700466 IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUT, skb->len);
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900467
Jan Engelhardtb2e0b382010-03-23 04:09:07 +0100468 err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, skb, NULL, dst->dev,
Patrick McHardy6e23ae22007-11-19 18:53:30 -0800469 dst_output);
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900470 if (!err) {
Denis V. Lunev5c5d2442008-10-08 10:33:50 -0700471 ICMP6MSGOUT_INC_STATS(net, idev, type);
Denis V. Luneva862f6a2008-10-08 10:33:06 -0700472 ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS);
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900473 }
474
Eric Dumazetcfdf7642011-07-27 21:13:03 +0000475 rcu_read_unlock();
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900476}
477
Brian Haley305d5522008-11-04 17:51:14 -0800478EXPORT_SYMBOL(ndisc_send_skb);
479
480/*
481 * Send a Neighbour Discover packet
482 */
483static void __ndisc_send(struct net_device *dev,
484 struct neighbour *neigh,
485 const struct in6_addr *daddr,
486 const struct in6_addr *saddr,
487 struct icmp6hdr *icmp6h, const struct in6_addr *target,
488 int llinfo)
489{
490 struct sk_buff *skb;
491
492 skb = ndisc_build_skb(dev, daddr, saddr, icmp6h, target, llinfo);
493 if (!skb)
494 return;
495
496 ndisc_send_skb(skb, dev, neigh, daddr, saddr, icmp6h);
497}
498
Linus Torvalds1da177e2005-04-16 15:20:36 -0700499static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +0900500 const struct in6_addr *daddr,
501 const struct in6_addr *solicited_addr,
502 int router, int solicited, int override, int inc_opt)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700503{
504 struct in6_addr tmpaddr;
505 struct inet6_ifaddr *ifp;
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +0900506 const struct in6_addr *src_addr;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900507 struct icmp6hdr icmp6h = {
508 .icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT,
509 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700510
511 /* for anycast or proxy, solicited_addr != src_addr */
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900512 ifp = ipv6_get_ifaddr(dev_net(dev), solicited_addr, dev, 1);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900513 if (ifp) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514 src_addr = solicited_addr;
Neil Horman95c385b2007-04-25 17:08:10 -0700515 if (ifp->flags & IFA_F_OPTIMISTIC)
516 override = 0;
stephen hemminger9f888162010-06-21 11:00:13 +0000517 inc_opt |= ifp->idev->cnf.force_tllao;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518 in6_ifa_put(ifp);
519 } else {
Brian Haley191cd582008-08-14 15:33:21 -0700520 if (ipv6_dev_get_saddr(dev_net(dev), dev, daddr,
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900521 inet6_sk(dev_net(dev)->ipv6.ndisc_sk)->srcprefs,
YOSHIFUJI Hideaki7cbca672008-03-25 09:37:42 +0900522 &tmpaddr))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700523 return;
524 src_addr = &tmpaddr;
525 }
526
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900527 icmp6h.icmp6_router = router;
528 icmp6h.icmp6_solicited = solicited;
529 icmp6h.icmp6_override = override;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700530
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900531 __ndisc_send(dev, neigh, daddr, src_addr,
532 &icmp6h, solicited_addr,
David L Stevens14878f72007-09-16 16:52:35 -0700533 inc_opt ? ND_OPT_TARGET_LL_ADDR : 0);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900534}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535
Ben Hutchingsf47b9462011-04-15 13:46:02 +0000536static void ndisc_send_unsol_na(struct net_device *dev)
537{
538 struct inet6_dev *idev;
539 struct inet6_ifaddr *ifa;
540 struct in6_addr mcaddr;
541
542 idev = in6_dev_get(dev);
543 if (!idev)
544 return;
545
546 read_lock_bh(&idev->lock);
547 list_for_each_entry(ifa, &idev->addr_list, if_list) {
548 addrconf_addr_solict_mult(&ifa->addr, &mcaddr);
549 ndisc_send_na(dev, NULL, &mcaddr, &ifa->addr,
550 /*router=*/ !!idev->cnf.forwarding,
551 /*solicited=*/ false, /*override=*/ true,
552 /*inc_opt=*/ true);
553 }
554 read_unlock_bh(&idev->lock);
555
556 in6_dev_put(idev);
557}
558
Linus Torvalds1da177e2005-04-16 15:20:36 -0700559void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +0900560 const struct in6_addr *solicit,
561 const struct in6_addr *daddr, const struct in6_addr *saddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700562{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700563 struct in6_addr addr_buf;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900564 struct icmp6hdr icmp6h = {
565 .icmp6_type = NDISC_NEIGHBOUR_SOLICITATION,
566 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700567
568 if (saddr == NULL) {
Neil Horman95c385b2007-04-25 17:08:10 -0700569 if (ipv6_get_lladdr(dev, &addr_buf,
570 (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700571 return;
572 saddr = &addr_buf;
573 }
574
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900575 __ndisc_send(dev, neigh, daddr, saddr,
576 &icmp6h, solicit,
David L Stevens14878f72007-09-16 16:52:35 -0700577 !ipv6_addr_any(saddr) ? ND_OPT_SOURCE_LL_ADDR : 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578}
579
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +0900580void ndisc_send_rs(struct net_device *dev, const struct in6_addr *saddr,
581 const struct in6_addr *daddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700582{
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900583 struct icmp6hdr icmp6h = {
584 .icmp6_type = NDISC_ROUTER_SOLICITATION,
585 };
Neil Horman95c385b2007-04-25 17:08:10 -0700586 int send_sllao = dev->addr_len;
Neil Horman95c385b2007-04-25 17:08:10 -0700587
588#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
589 /*
590 * According to section 2.2 of RFC 4429, we must not
591 * send router solicitations with a sllao from
592 * optimistic addresses, but we may send the solicitation
593 * if we don't include the sllao. So here we check
594 * if our address is optimistic, and if so, we
Joe Perchesbea85192007-12-20 14:01:35 -0800595 * suppress the inclusion of the sllao.
Neil Horman95c385b2007-04-25 17:08:10 -0700596 */
597 if (send_sllao) {
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900598 struct inet6_ifaddr *ifp = ipv6_get_ifaddr(dev_net(dev), saddr,
Daniel Lezcano1cab3da2008-01-10 22:44:09 -0800599 dev, 1);
Neil Horman95c385b2007-04-25 17:08:10 -0700600 if (ifp) {
601 if (ifp->flags & IFA_F_OPTIMISTIC) {
YOSHIFUJI Hideakica043562007-02-28 23:13:20 +0900602 send_sllao = 0;
Neil Horman95c385b2007-04-25 17:08:10 -0700603 }
YOSHIFUJI Hideakica043562007-02-28 23:13:20 +0900604 in6_ifa_put(ifp);
Neil Horman95c385b2007-04-25 17:08:10 -0700605 } else {
606 send_sllao = 0;
607 }
608 }
609#endif
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900610 __ndisc_send(dev, NULL, daddr, saddr,
611 &icmp6h, NULL,
David L Stevens14878f72007-09-16 16:52:35 -0700612 send_sllao ? ND_OPT_SOURCE_LL_ADDR : 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700613}
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900614
Linus Torvalds1da177e2005-04-16 15:20:36 -0700615
616static void ndisc_error_report(struct neighbour *neigh, struct sk_buff *skb)
617{
618 /*
619 * "The sender MUST return an ICMP
620 * destination unreachable"
621 */
622 dst_link_failure(skb);
623 kfree_skb(skb);
624}
625
626/* Called with locked neigh: either read or both */
627
628static void ndisc_solicit(struct neighbour *neigh, struct sk_buff *skb)
629{
630 struct in6_addr *saddr = NULL;
631 struct in6_addr mcaddr;
632 struct net_device *dev = neigh->dev;
633 struct in6_addr *target = (struct in6_addr *)&neigh->primary_key;
634 int probes = atomic_read(&neigh->probes);
635
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900636 if (skb && ipv6_chk_addr(dev_net(dev), &ipv6_hdr(skb)->saddr, dev, 1))
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -0700637 saddr = &ipv6_hdr(skb)->saddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700638
639 if ((probes -= neigh->parms->ucast_probes) < 0) {
640 if (!(neigh->nud_state & NUD_VALID)) {
Joe Perches675418d2012-05-16 19:28:38 +0000641 ND_PRINTK(1, dbg,
642 "%s: trying to ucast probe in NUD_INVALID: %pI6\n",
643 __func__, target);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700644 }
645 ndisc_send_ns(dev, neigh, target, target, saddr);
646 } else if ((probes -= neigh->parms->app_probes) < 0) {
647#ifdef CONFIG_ARPD
648 neigh_app_ns(neigh);
649#endif
650 } else {
651 addrconf_addr_solict_mult(target, &mcaddr);
652 ndisc_send_ns(dev, NULL, target, &mcaddr, saddr);
653 }
654}
655
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900656static int pndisc_is_router(const void *pkey,
657 struct net_device *dev)
Pavel Emelyanovfa86d322008-03-24 14:48:59 -0700658{
659 struct pneigh_entry *n;
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900660 int ret = -1;
Pavel Emelyanovfa86d322008-03-24 14:48:59 -0700661
662 read_lock_bh(&nd_tbl.lock);
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900663 n = __pneigh_lookup(&nd_tbl, dev_net(dev), pkey, dev);
664 if (n)
665 ret = !!(n->flags & NTF_ROUTER);
Pavel Emelyanovfa86d322008-03-24 14:48:59 -0700666 read_unlock_bh(&nd_tbl.lock);
667
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900668 return ret;
Pavel Emelyanovfa86d322008-03-24 14:48:59 -0700669}
670
Linus Torvalds1da177e2005-04-16 15:20:36 -0700671static void ndisc_recv_ns(struct sk_buff *skb)
672{
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -0700673 struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb);
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000674 const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
675 const struct in6_addr *daddr = &ipv6_hdr(skb)->daddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700676 u8 *lladdr = NULL;
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -0700677 u32 ndoptlen = skb->tail - (skb->transport_header +
678 offsetof(struct nd_msg, opt));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700679 struct ndisc_options ndopts;
680 struct net_device *dev = skb->dev;
681 struct inet6_ifaddr *ifp;
682 struct inet6_dev *idev = NULL;
683 struct neighbour *neigh;
684 int dad = ipv6_addr_any(saddr);
Eric Dumazeta50feda2012-05-18 18:57:34 +0000685 bool inc;
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900686 int is_router = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700687
688 if (ipv6_addr_is_multicast(&msg->target)) {
Joe Perches675418d2012-05-16 19:28:38 +0000689 ND_PRINTK(2, warn, "NS: multicast target address\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700690 return;
691 }
692
693 /*
694 * RFC2461 7.1.1:
695 * DAD has to be destined for solicited node multicast address.
696 */
697 if (dad &&
698 !(daddr->s6_addr32[0] == htonl(0xff020000) &&
699 daddr->s6_addr32[1] == htonl(0x00000000) &&
700 daddr->s6_addr32[2] == htonl(0x00000001) &&
701 daddr->s6_addr [12] == 0xff )) {
Joe Perches675418d2012-05-16 19:28:38 +0000702 ND_PRINTK(2, warn, "NS: bad DAD packet (wrong destination)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700703 return;
704 }
705
706 if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts)) {
Joe Perches675418d2012-05-16 19:28:38 +0000707 ND_PRINTK(2, warn, "NS: invalid ND options\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700708 return;
709 }
710
711 if (ndopts.nd_opts_src_lladdr) {
712 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr, dev);
713 if (!lladdr) {
Joe Perches675418d2012-05-16 19:28:38 +0000714 ND_PRINTK(2, warn,
715 "NS: invalid link-layer address length\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700716 return;
717 }
718
719 /* RFC2461 7.1.1:
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900720 * If the IP source address is the unspecified address,
721 * there MUST NOT be source link-layer address option
Linus Torvalds1da177e2005-04-16 15:20:36 -0700722 * in the message.
723 */
724 if (dad) {
Joe Perches675418d2012-05-16 19:28:38 +0000725 ND_PRINTK(2, warn,
726 "NS: bad DAD packet (link-layer address option)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700727 return;
728 }
729 }
730
731 inc = ipv6_addr_is_multicast(daddr);
732
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900733 ifp = ipv6_get_ifaddr(dev_net(dev), &msg->target, dev, 1);
Daniel Lezcanoa18bc692008-03-07 11:14:49 -0800734 if (ifp) {
Neil Horman95c385b2007-04-25 17:08:10 -0700735
736 if (ifp->flags & (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)) {
737 if (dad) {
Neil Horman95c385b2007-04-25 17:08:10 -0700738 /*
739 * We are colliding with another node
740 * who is doing DAD
741 * so fail our DAD process
742 */
743 addrconf_dad_failure(ifp);
Denis V. Lunev9e3be4b2007-09-11 11:04:49 +0200744 return;
Neil Horman95c385b2007-04-25 17:08:10 -0700745 } else {
746 /*
747 * This is not a dad solicitation.
748 * If we are an optimistic node,
749 * we should respond.
750 * Otherwise, we should ignore it.
751 */
752 if (!(ifp->flags & IFA_F_OPTIMISTIC))
753 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700754 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700755 }
756
757 idev = ifp->idev;
758 } else {
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700759 struct net *net = dev_net(dev);
760
Linus Torvalds1da177e2005-04-16 15:20:36 -0700761 idev = in6_dev_get(dev);
762 if (!idev) {
763 /* XXX: count this drop? */
764 return;
765 }
766
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700767 if (ipv6_chk_acast_addr(net, dev, &msg->target) ||
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900768 (idev->cnf.forwarding &&
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700769 (net->ipv6.devconf_all->proxy_ndp || idev->cnf.proxy_ndp) &&
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900770 (is_router = pndisc_is_router(&msg->target, dev)) >= 0)) {
Patrick McHardya61bbcf2005-08-14 17:24:31 -0700771 if (!(NEIGH_CB(skb)->flags & LOCALLY_ENQUEUED) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700772 skb->pkt_type != PACKET_HOST &&
773 inc != 0 &&
774 idev->nd_parms->proxy_delay != 0) {
775 /*
776 * for anycast or proxy,
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900777 * sender should delay its response
778 * by a random time between 0 and
Linus Torvalds1da177e2005-04-16 15:20:36 -0700779 * MAX_ANYCAST_DELAY_TIME seconds.
780 * (RFC2461) -- yoshfuji
781 */
782 struct sk_buff *n = skb_clone(skb, GFP_ATOMIC);
783 if (n)
784 pneigh_enqueue(&nd_tbl, idev->nd_parms, n);
785 goto out;
786 }
787 } else
788 goto out;
789 }
790
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900791 if (is_router < 0)
792 is_router = !!idev->cnf.forwarding;
Ville Nuorvala62dd9312006-09-22 14:43:19 -0700793
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794 if (dad) {
YOSHIFUJI Hideakif3ee4012008-04-10 15:42:11 +0900795 ndisc_send_na(dev, NULL, &in6addr_linklocal_allnodes, &msg->target,
Ville Nuorvala62dd9312006-09-22 14:43:19 -0700796 is_router, 0, (ifp != NULL), 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700797 goto out;
798 }
799
800 if (inc)
801 NEIGH_CACHE_STAT_INC(&nd_tbl, rcv_probes_mcast);
802 else
803 NEIGH_CACHE_STAT_INC(&nd_tbl, rcv_probes_ucast);
804
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900805 /*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700806 * update / create cache entry
807 * for the source address
808 */
809 neigh = __neigh_lookup(&nd_tbl, saddr, dev,
810 !inc || lladdr || !dev->addr_len);
811 if (neigh)
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900812 neigh_update(neigh, lladdr, NUD_STALE,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700813 NEIGH_UPDATE_F_WEAK_OVERRIDE|
814 NEIGH_UPDATE_F_OVERRIDE);
Stephen Hemminger3b04ddd2007-10-09 01:40:57 -0700815 if (neigh || !dev->header_ops) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816 ndisc_send_na(dev, neigh, saddr, &msg->target,
Ville Nuorvala62dd9312006-09-22 14:43:19 -0700817 is_router,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700818 1, (ifp != NULL && inc), inc);
819 if (neigh)
820 neigh_release(neigh);
821 }
822
823out:
824 if (ifp)
825 in6_ifa_put(ifp);
826 else
827 in6_dev_put(idev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700828}
829
830static void ndisc_recv_na(struct sk_buff *skb)
831{
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -0700832 struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb);
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000833 const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
834 const struct in6_addr *daddr = &ipv6_hdr(skb)->daddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700835 u8 *lladdr = NULL;
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -0700836 u32 ndoptlen = skb->tail - (skb->transport_header +
837 offsetof(struct nd_msg, opt));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700838 struct ndisc_options ndopts;
839 struct net_device *dev = skb->dev;
840 struct inet6_ifaddr *ifp;
841 struct neighbour *neigh;
842
843 if (skb->len < sizeof(struct nd_msg)) {
Joe Perches675418d2012-05-16 19:28:38 +0000844 ND_PRINTK(2, warn, "NA: packet too short\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700845 return;
846 }
847
848 if (ipv6_addr_is_multicast(&msg->target)) {
Joe Perches675418d2012-05-16 19:28:38 +0000849 ND_PRINTK(2, warn, "NA: target address is multicast\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700850 return;
851 }
852
853 if (ipv6_addr_is_multicast(daddr) &&
854 msg->icmph.icmp6_solicited) {
Joe Perches675418d2012-05-16 19:28:38 +0000855 ND_PRINTK(2, warn, "NA: solicited NA is multicasted\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700856 return;
857 }
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900858
Linus Torvalds1da177e2005-04-16 15:20:36 -0700859 if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts)) {
Joe Perches675418d2012-05-16 19:28:38 +0000860 ND_PRINTK(2, warn, "NS: invalid ND option\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700861 return;
862 }
863 if (ndopts.nd_opts_tgt_lladdr) {
864 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr, dev);
865 if (!lladdr) {
Joe Perches675418d2012-05-16 19:28:38 +0000866 ND_PRINTK(2, warn,
867 "NA: invalid link-layer address length\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700868 return;
869 }
870 }
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900871 ifp = ipv6_get_ifaddr(dev_net(dev), &msg->target, dev, 1);
Daniel Lezcanoa18bc692008-03-07 11:14:49 -0800872 if (ifp) {
Daniel Walterbd015922011-04-13 21:09:25 +0000873 if (skb->pkt_type != PACKET_LOOPBACK
874 && (ifp->flags & IFA_F_TENTATIVE)) {
875 addrconf_dad_failure(ifp);
876 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700877 }
878 /* What should we make now? The advertisement
879 is invalid, but ndisc specs say nothing
880 about it. It could be misconfiguration, or
881 an smart proxy agent tries to help us :-)
Jan Sembera24fc7b82008-12-09 15:48:32 -0800882
883 We should not print the error if NA has been
884 received from loopback - it is just our own
885 unsolicited advertisement.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700886 */
Jan Sembera24fc7b82008-12-09 15:48:32 -0800887 if (skb->pkt_type != PACKET_LOOPBACK)
Joe Perches675418d2012-05-16 19:28:38 +0000888 ND_PRINTK(1, warn,
889 "NA: someone advertises our address %pI6 on %s!\n",
890 &ifp->addr, ifp->idev->dev->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700891 in6_ifa_put(ifp);
892 return;
893 }
894 neigh = neigh_lookup(&nd_tbl, &msg->target, dev);
895
896 if (neigh) {
897 u8 old_flags = neigh->flags;
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700898 struct net *net = dev_net(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700899
900 if (neigh->nud_state & NUD_FAILED)
901 goto out;
902
Ville Nuorvala5f3e6e92006-09-22 14:42:46 -0700903 /*
904 * Don't update the neighbor cache entry on a proxy NA from
905 * ourselves because either the proxied node is off link or it
906 * has already sent a NA to us.
907 */
908 if (lladdr && !memcmp(lladdr, dev->dev_addr, dev->addr_len) &&
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700909 net->ipv6.devconf_all->forwarding && net->ipv6.devconf_all->proxy_ndp &&
910 pneigh_lookup(&nd_tbl, net, &msg->target, dev, 0)) {
YOSHIFUJI Hideakifbea49e2006-09-22 14:43:49 -0700911 /* XXX: idev->cnf.prixy_ndp */
Ville Nuorvala5f3e6e92006-09-22 14:42:46 -0700912 goto out;
YOSHIFUJI Hideakifbea49e2006-09-22 14:43:49 -0700913 }
Ville Nuorvala5f3e6e92006-09-22 14:42:46 -0700914
Linus Torvalds1da177e2005-04-16 15:20:36 -0700915 neigh_update(neigh, lladdr,
916 msg->icmph.icmp6_solicited ? NUD_REACHABLE : NUD_STALE,
917 NEIGH_UPDATE_F_WEAK_OVERRIDE|
918 (msg->icmph.icmp6_override ? NEIGH_UPDATE_F_OVERRIDE : 0)|
919 NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
920 (msg->icmph.icmp6_router ? NEIGH_UPDATE_F_ISROUTER : 0));
921
922 if ((old_flags & ~neigh->flags) & NTF_ROUTER) {
923 /*
924 * Change: router to host
925 */
926 struct rt6_info *rt;
927 rt = rt6_get_dflt_router(saddr, dev);
928 if (rt)
Thomas Grafe0a1ad732006-08-22 00:00:21 -0700929 ip6_del_rt(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700930 }
931
932out:
933 neigh_release(neigh);
934 }
935}
936
937static void ndisc_recv_rs(struct sk_buff *skb)
938{
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -0700939 struct rs_msg *rs_msg = (struct rs_msg *)skb_transport_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700940 unsigned long ndoptlen = skb->len - sizeof(*rs_msg);
941 struct neighbour *neigh;
942 struct inet6_dev *idev;
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000943 const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700944 struct ndisc_options ndopts;
945 u8 *lladdr = NULL;
946
947 if (skb->len < sizeof(*rs_msg))
948 return;
949
Eric Dumazetcfdf7642011-07-27 21:13:03 +0000950 idev = __in6_dev_get(skb->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700951 if (!idev) {
Joe Perches675418d2012-05-16 19:28:38 +0000952 ND_PRINTK(1, err, "RS: can't find in6 device\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700953 return;
954 }
955
956 /* Don't accept RS if we're not in router mode */
957 if (!idev->cnf.forwarding)
958 goto out;
959
960 /*
961 * Don't update NCE if src = ::;
962 * this implies that the source node has no ip address assigned yet.
963 */
964 if (ipv6_addr_any(saddr))
965 goto out;
966
967 /* Parse ND options */
968 if (!ndisc_parse_options(rs_msg->opt, ndoptlen, &ndopts)) {
Joe Perches675418d2012-05-16 19:28:38 +0000969 ND_PRINTK(2, notice, "NS: invalid ND option, ignored\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700970 goto out;
971 }
972
973 if (ndopts.nd_opts_src_lladdr) {
974 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr,
975 skb->dev);
976 if (!lladdr)
977 goto out;
978 }
979
980 neigh = __neigh_lookup(&nd_tbl, saddr, skb->dev, 1);
981 if (neigh) {
982 neigh_update(neigh, lladdr, NUD_STALE,
983 NEIGH_UPDATE_F_WEAK_OVERRIDE|
984 NEIGH_UPDATE_F_OVERRIDE|
985 NEIGH_UPDATE_F_OVERRIDE_ISROUTER);
986 neigh_release(neigh);
987 }
988out:
Eric Dumazetcfdf7642011-07-27 21:13:03 +0000989 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700990}
991
Pierre Ynard31910572007-10-10 21:22:05 -0700992static void ndisc_ra_useropt(struct sk_buff *ra, struct nd_opt_hdr *opt)
993{
994 struct icmp6hdr *icmp6h = (struct icmp6hdr *)skb_transport_header(ra);
995 struct sk_buff *skb;
996 struct nlmsghdr *nlh;
997 struct nduseroptmsg *ndmsg;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900998 struct net *net = dev_net(ra->dev);
Pierre Ynard31910572007-10-10 21:22:05 -0700999 int err;
1000 int base_size = NLMSG_ALIGN(sizeof(struct nduseroptmsg)
1001 + (opt->nd_opt_len << 3));
1002 size_t msg_size = base_size + nla_total_size(sizeof(struct in6_addr));
1003
1004 skb = nlmsg_new(msg_size, GFP_ATOMIC);
1005 if (skb == NULL) {
1006 err = -ENOBUFS;
1007 goto errout;
1008 }
1009
1010 nlh = nlmsg_put(skb, 0, 0, RTM_NEWNDUSEROPT, base_size, 0);
1011 if (nlh == NULL) {
1012 goto nla_put_failure;
1013 }
1014
1015 ndmsg = nlmsg_data(nlh);
1016 ndmsg->nduseropt_family = AF_INET6;
Pierre Ynarddbb2ed22007-11-12 17:58:35 -08001017 ndmsg->nduseropt_ifindex = ra->dev->ifindex;
Pierre Ynard31910572007-10-10 21:22:05 -07001018 ndmsg->nduseropt_icmp_type = icmp6h->icmp6_type;
1019 ndmsg->nduseropt_icmp_code = icmp6h->icmp6_code;
1020 ndmsg->nduseropt_opts_len = opt->nd_opt_len << 3;
1021
1022 memcpy(ndmsg + 1, opt, opt->nd_opt_len << 3);
1023
David S. Millerc78679e2012-04-01 20:27:33 -04001024 if (nla_put(skb, NDUSEROPT_SRCADDR, sizeof(struct in6_addr),
1025 &ipv6_hdr(ra)->saddr))
1026 goto nla_put_failure;
Pierre Ynard31910572007-10-10 21:22:05 -07001027 nlmsg_end(skb, nlh);
1028
Pablo Neira Ayuso1ce85fe2009-02-24 23:18:28 -08001029 rtnl_notify(skb, net, 0, RTNLGRP_ND_USEROPT, NULL, GFP_ATOMIC);
Pierre Ynard31910572007-10-10 21:22:05 -07001030 return;
1031
1032nla_put_failure:
1033 nlmsg_free(skb);
1034 err = -EMSGSIZE;
1035errout:
Daniel Lezcanoa18bc692008-03-07 11:14:49 -08001036 rtnl_set_sk_err(net, RTNLGRP_ND_USEROPT, err);
Pierre Ynard31910572007-10-10 21:22:05 -07001037}
1038
Thomas Graf65e9b622010-09-03 02:59:14 +00001039static inline int accept_ra(struct inet6_dev *in6_dev)
1040{
1041 /*
1042 * If forwarding is enabled, RA are not accepted unless the special
1043 * hybrid mode (accept_ra=2) is enabled.
1044 */
1045 if (in6_dev->cnf.forwarding && in6_dev->cnf.accept_ra < 2)
1046 return 0;
1047
1048 return in6_dev->cnf.accept_ra;
1049}
1050
Linus Torvalds1da177e2005-04-16 15:20:36 -07001051static void ndisc_router_discovery(struct sk_buff *skb)
1052{
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -07001053 struct ra_msg *ra_msg = (struct ra_msg *)skb_transport_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001054 struct neighbour *neigh = NULL;
1055 struct inet6_dev *in6_dev;
YOSHIFUJI Hideaki65f5c7c2006-03-20 16:55:08 -08001056 struct rt6_info *rt = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001057 int lifetime;
1058 struct ndisc_options ndopts;
1059 int optlen;
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001060 unsigned int pref = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001061
1062 __u8 * opt = (__u8 *)(ra_msg + 1);
1063
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -07001064 optlen = (skb->tail - skb->transport_header) - sizeof(struct ra_msg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001065
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001066 if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) {
Joe Perches675418d2012-05-16 19:28:38 +00001067 ND_PRINTK(2, warn, "RA: source address is not link-local\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001068 return;
1069 }
1070 if (optlen < 0) {
Joe Perches675418d2012-05-16 19:28:38 +00001071 ND_PRINTK(2, warn, "RA: packet too short\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001072 return;
1073 }
1074
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001075#ifdef CONFIG_IPV6_NDISC_NODETYPE
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001076 if (skb->ndisc_nodetype == NDISC_NODETYPE_HOST) {
Joe Perches675418d2012-05-16 19:28:38 +00001077 ND_PRINTK(2, warn, "RA: from host or unauthorized router\n");
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001078 return;
1079 }
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001080#endif
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001081
Linus Torvalds1da177e2005-04-16 15:20:36 -07001082 /*
1083 * set the RA_RECV flag in the interface
1084 */
1085
Eric Dumazetcfdf7642011-07-27 21:13:03 +00001086 in6_dev = __in6_dev_get(skb->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001087 if (in6_dev == NULL) {
Joe Perches675418d2012-05-16 19:28:38 +00001088 ND_PRINTK(0, err, "RA: can't find inet6 device for %s\n",
1089 skb->dev->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001090 return;
1091 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001092
1093 if (!ndisc_parse_options(opt, optlen, &ndopts)) {
Joe Perches675418d2012-05-16 19:28:38 +00001094 ND_PRINTK(2, warn, "RA: invalid ND options\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001095 return;
1096 }
1097
Thomas Graf65e9b622010-09-03 02:59:14 +00001098 if (!accept_ra(in6_dev))
David Ward31ce8c72009-08-29 00:04:09 -07001099 goto skip_linkparms;
1100
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001101#ifdef CONFIG_IPV6_NDISC_NODETYPE
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001102 /* skip link-specific parameters from interior routers */
1103 if (skb->ndisc_nodetype == NDISC_NODETYPE_NODEFAULT)
1104 goto skip_linkparms;
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001105#endif
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001106
Linus Torvalds1da177e2005-04-16 15:20:36 -07001107 if (in6_dev->if_flags & IF_RS_SENT) {
1108 /*
1109 * flag that an RA was received after an RS was sent
1110 * out on this interface.
1111 */
1112 in6_dev->if_flags |= IF_RA_RCVD;
1113 }
1114
1115 /*
1116 * Remember the managed/otherconf flags from most recently
1117 * received RA message (RFC 2462) -- yoshfuji
1118 */
1119 in6_dev->if_flags = (in6_dev->if_flags & ~(IF_RA_MANAGED |
1120 IF_RA_OTHERCONF)) |
1121 (ra_msg->icmph.icmp6_addrconf_managed ?
1122 IF_RA_MANAGED : 0) |
1123 (ra_msg->icmph.icmp6_addrconf_other ?
1124 IF_RA_OTHERCONF : 0);
1125
YOSHIFUJI Hideaki65f5c7c2006-03-20 16:55:08 -08001126 if (!in6_dev->cnf.accept_ra_defrtr)
1127 goto skip_defrtr;
1128
Andreas Hofmeister9f562202011-10-24 19:13:15 -04001129 if (ipv6_chk_addr(dev_net(in6_dev->dev), &ipv6_hdr(skb)->saddr, NULL, 0))
1130 goto skip_defrtr;
1131
Linus Torvalds1da177e2005-04-16 15:20:36 -07001132 lifetime = ntohs(ra_msg->icmph.icmp6_rt_lifetime);
1133
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001134#ifdef CONFIG_IPV6_ROUTER_PREF
1135 pref = ra_msg->icmph.icmp6_router_pref;
1136 /* 10b is handled as if it were 00b (medium) */
YOSHIFUJI Hideaki930d6ff2006-03-20 17:05:30 -08001137 if (pref == ICMPV6_ROUTER_PREF_INVALID ||
YOSHIFUJI Hideaki6d5b78c2007-06-22 16:07:04 -07001138 !in6_dev->cnf.accept_ra_rtr_pref)
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001139 pref = ICMPV6_ROUTER_PREF_MEDIUM;
1140#endif
1141
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001142 rt = rt6_get_dflt_router(&ipv6_hdr(skb)->saddr, skb->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001143
David S. Millereb857182012-01-27 15:07:56 -08001144 if (rt) {
1145 neigh = dst_neigh_lookup(&rt->dst, &ipv6_hdr(skb)->saddr);
1146 if (!neigh) {
Joe Perches675418d2012-05-16 19:28:38 +00001147 ND_PRINTK(0, err,
1148 "RA: %s got default router without neighbour\n",
1149 __func__);
David S. Millereb857182012-01-27 15:07:56 -08001150 dst_release(&rt->dst);
1151 return;
1152 }
1153 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001154 if (rt && lifetime == 0) {
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001155 ip6_del_rt(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001156 rt = NULL;
1157 }
1158
1159 if (rt == NULL && lifetime) {
Joe Perches675418d2012-05-16 19:28:38 +00001160 ND_PRINTK(3, dbg, "RA: adding default router\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001161
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001162 rt = rt6_add_dflt_router(&ipv6_hdr(skb)->saddr, skb->dev, pref);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001163 if (rt == NULL) {
Joe Perches675418d2012-05-16 19:28:38 +00001164 ND_PRINTK(0, err,
1165 "RA: %s failed to add default route\n",
1166 __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001167 return;
1168 }
1169
David S. Millereb857182012-01-27 15:07:56 -08001170 neigh = dst_neigh_lookup(&rt->dst, &ipv6_hdr(skb)->saddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001171 if (neigh == NULL) {
Joe Perches675418d2012-05-16 19:28:38 +00001172 ND_PRINTK(0, err,
1173 "RA: %s got default router without neighbour\n",
1174 __func__);
Changli Gaod8d1f302010-06-10 23:31:35 -07001175 dst_release(&rt->dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001176 return;
1177 }
1178 neigh->flags |= NTF_ROUTER;
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001179 } else if (rt) {
Pedro Ribeiro22441cf2008-10-15 15:47:49 -07001180 rt->rt6i_flags = (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001181 }
1182
1183 if (rt)
Gao feng1716a962012-04-06 00:13:10 +00001184 rt6_set_expires(rt, jiffies + (HZ * lifetime));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001185 if (ra_msg->icmph.icmp6_hop_limit) {
1186 in6_dev->cnf.hop_limit = ra_msg->icmph.icmp6_hop_limit;
1187 if (rt)
David S. Millerdefb3512010-12-08 21:16:57 -08001188 dst_metric_set(&rt->dst, RTAX_HOPLIMIT,
1189 ra_msg->icmph.icmp6_hop_limit);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001190 }
1191
YOSHIFUJI Hideaki65f5c7c2006-03-20 16:55:08 -08001192skip_defrtr:
1193
Linus Torvalds1da177e2005-04-16 15:20:36 -07001194 /*
1195 * Update Reachable Time and Retrans Timer
1196 */
1197
1198 if (in6_dev->nd_parms) {
1199 unsigned long rtime = ntohl(ra_msg->retrans_timer);
1200
1201 if (rtime && rtime/1000 < MAX_SCHEDULE_TIMEOUT/HZ) {
1202 rtime = (rtime*HZ)/1000;
1203 if (rtime < HZ/10)
1204 rtime = HZ/10;
1205 in6_dev->nd_parms->retrans_time = rtime;
1206 in6_dev->tstamp = jiffies;
1207 inet6_ifinfo_notify(RTM_NEWLINK, in6_dev);
1208 }
1209
1210 rtime = ntohl(ra_msg->reachable_time);
1211 if (rtime && rtime/1000 < MAX_SCHEDULE_TIMEOUT/(3*HZ)) {
1212 rtime = (rtime*HZ)/1000;
1213
1214 if (rtime < HZ/10)
1215 rtime = HZ/10;
1216
1217 if (rtime != in6_dev->nd_parms->base_reachable_time) {
1218 in6_dev->nd_parms->base_reachable_time = rtime;
1219 in6_dev->nd_parms->gc_staletime = 3 * rtime;
1220 in6_dev->nd_parms->reachable_time = neigh_rand_reach_time(rtime);
1221 in6_dev->tstamp = jiffies;
1222 inet6_ifinfo_notify(RTM_NEWLINK, in6_dev);
1223 }
1224 }
1225 }
1226
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001227skip_linkparms:
1228
Linus Torvalds1da177e2005-04-16 15:20:36 -07001229 /*
1230 * Process options.
1231 */
1232
1233 if (!neigh)
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001234 neigh = __neigh_lookup(&nd_tbl, &ipv6_hdr(skb)->saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001235 skb->dev, 1);
1236 if (neigh) {
1237 u8 *lladdr = NULL;
1238 if (ndopts.nd_opts_src_lladdr) {
1239 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr,
1240 skb->dev);
1241 if (!lladdr) {
Joe Perches675418d2012-05-16 19:28:38 +00001242 ND_PRINTK(2, warn,
1243 "RA: invalid link-layer address length\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001244 goto out;
1245 }
1246 }
1247 neigh_update(neigh, lladdr, NUD_STALE,
1248 NEIGH_UPDATE_F_WEAK_OVERRIDE|
1249 NEIGH_UPDATE_F_OVERRIDE|
1250 NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
1251 NEIGH_UPDATE_F_ISROUTER);
1252 }
1253
Thomas Graf65e9b622010-09-03 02:59:14 +00001254 if (!accept_ra(in6_dev))
David Ward31ce8c72009-08-29 00:04:09 -07001255 goto out;
1256
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001257#ifdef CONFIG_IPV6_ROUTE_INFO
Andreas Hofmeister9f562202011-10-24 19:13:15 -04001258 if (ipv6_chk_addr(dev_net(in6_dev->dev), &ipv6_hdr(skb)->saddr, NULL, 0))
1259 goto skip_routeinfo;
1260
YOSHIFUJI Hideaki09c884d2006-03-20 17:07:03 -08001261 if (in6_dev->cnf.accept_ra_rtr_pref && ndopts.nd_opts_ri) {
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001262 struct nd_opt_hdr *p;
1263 for (p = ndopts.nd_opts_ri;
1264 p;
1265 p = ndisc_next_option(p, ndopts.nd_opts_ri_end)) {
YOSHIFUJI Hideaki6294e002008-03-15 23:56:52 -04001266 struct route_info *ri = (struct route_info *)p;
1267#ifdef CONFIG_IPV6_NDISC_NODETYPE
1268 if (skb->ndisc_nodetype == NDISC_NODETYPE_NODEFAULT &&
1269 ri->prefix_len == 0)
1270 continue;
1271#endif
1272 if (ri->prefix_len > in6_dev->cnf.accept_ra_rt_info_max_plen)
YOSHIFUJI Hideaki09c884d2006-03-20 17:07:03 -08001273 continue;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001274 rt6_route_rcv(skb->dev, (u8*)p, (p->nd_opt_len) << 3,
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001275 &ipv6_hdr(skb)->saddr);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001276 }
1277 }
Andreas Hofmeister9f562202011-10-24 19:13:15 -04001278
1279skip_routeinfo:
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001280#endif
1281
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001282#ifdef CONFIG_IPV6_NDISC_NODETYPE
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001283 /* skip link-specific ndopts from interior routers */
1284 if (skb->ndisc_nodetype == NDISC_NODETYPE_NODEFAULT)
1285 goto out;
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001286#endif
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001287
YOSHIFUJI Hideakic4fd30e2006-03-20 16:55:26 -08001288 if (in6_dev->cnf.accept_ra_pinfo && ndopts.nd_opts_pi) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001289 struct nd_opt_hdr *p;
1290 for (p = ndopts.nd_opts_pi;
1291 p;
1292 p = ndisc_next_option(p, ndopts.nd_opts_pi_end)) {
Neil Hormane6bff992012-01-04 10:49:15 +00001293 addrconf_prefix_rcv(skb->dev, (u8 *)p,
1294 (p->nd_opt_len) << 3,
1295 ndopts.nd_opts_src_lladdr != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001296 }
1297 }
1298
1299 if (ndopts.nd_opts_mtu) {
Al Viroe69a4adc2006-11-14 20:56:00 -08001300 __be32 n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001301 u32 mtu;
1302
Al Viroe69a4adc2006-11-14 20:56:00 -08001303 memcpy(&n, ((u8*)(ndopts.nd_opts_mtu+1))+2, sizeof(mtu));
1304 mtu = ntohl(n);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001305
1306 if (mtu < IPV6_MIN_MTU || mtu > skb->dev->mtu) {
Joe Perches675418d2012-05-16 19:28:38 +00001307 ND_PRINTK(2, warn, "RA: invalid mtu: %d\n", mtu);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001308 } else if (in6_dev->cnf.mtu6 != mtu) {
1309 in6_dev->cnf.mtu6 = mtu;
1310
1311 if (rt)
David S. Millerdefb3512010-12-08 21:16:57 -08001312 dst_metric_set(&rt->dst, RTAX_MTU, mtu);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001313
1314 rt6_mtu_change(skb->dev, mtu);
1315 }
1316 }
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001317
Pierre Ynard31910572007-10-10 21:22:05 -07001318 if (ndopts.nd_useropts) {
YOSHIFUJI Hideaki61cf46ad2008-01-22 17:32:53 +09001319 struct nd_opt_hdr *p;
1320 for (p = ndopts.nd_useropts;
1321 p;
1322 p = ndisc_next_useropt(p, ndopts.nd_useropts_end)) {
1323 ndisc_ra_useropt(skb, p);
Pierre Ynard31910572007-10-10 21:22:05 -07001324 }
1325 }
1326
Linus Torvalds1da177e2005-04-16 15:20:36 -07001327 if (ndopts.nd_opts_tgt_lladdr || ndopts.nd_opts_rh) {
Joe Perches675418d2012-05-16 19:28:38 +00001328 ND_PRINTK(2, warn, "RA: invalid RA options\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001329 }
1330out:
1331 if (rt)
Changli Gaod8d1f302010-06-10 23:31:35 -07001332 dst_release(&rt->dst);
David S. Millereb857182012-01-27 15:07:56 -08001333 if (neigh)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001334 neigh_release(neigh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001335}
1336
1337static void ndisc_redirect_rcv(struct sk_buff *skb)
1338{
1339 struct inet6_dev *in6_dev;
1340 struct icmp6hdr *icmph;
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001341 const struct in6_addr *dest;
1342 const struct in6_addr *target; /* new first hop to destination */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001343 struct neighbour *neigh;
1344 int on_link = 0;
1345 struct ndisc_options ndopts;
1346 int optlen;
1347 u8 *lladdr = NULL;
1348
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001349#ifdef CONFIG_IPV6_NDISC_NODETYPE
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001350 switch (skb->ndisc_nodetype) {
1351 case NDISC_NODETYPE_HOST:
1352 case NDISC_NODETYPE_NODEFAULT:
Joe Perches675418d2012-05-16 19:28:38 +00001353 ND_PRINTK(2, warn,
1354 "Redirect: from host or unauthorized router\n");
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001355 return;
1356 }
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001357#endif
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001358
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001359 if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) {
Joe Perches675418d2012-05-16 19:28:38 +00001360 ND_PRINTK(2, warn,
1361 "Redirect: source address is not link-local\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001362 return;
1363 }
1364
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -07001365 optlen = skb->tail - skb->transport_header;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001366 optlen -= sizeof(struct icmp6hdr) + 2 * sizeof(struct in6_addr);
1367
1368 if (optlen < 0) {
Joe Perches675418d2012-05-16 19:28:38 +00001369 ND_PRINTK(2, warn, "Redirect: packet too short\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001370 return;
1371 }
1372
Arnaldo Carvalho de Melocc70ab22007-03-13 14:03:22 -03001373 icmph = icmp6_hdr(skb);
Eric Dumazetb71d1d42011-04-22 04:53:02 +00001374 target = (const struct in6_addr *) (icmph + 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001375 dest = target + 1;
1376
1377 if (ipv6_addr_is_multicast(dest)) {
Joe Perches675418d2012-05-16 19:28:38 +00001378 ND_PRINTK(2, warn,
1379 "Redirect: destination address is multicast\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001380 return;
1381 }
1382
1383 if (ipv6_addr_equal(dest, target)) {
1384 on_link = 1;
Brian Haleybf0b48d2007-10-08 00:12:05 -07001385 } else if (ipv6_addr_type(target) !=
1386 (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) {
Joe Perches675418d2012-05-16 19:28:38 +00001387 ND_PRINTK(2, warn,
1388 "Redirect: target address is not link-local unicast\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001389 return;
1390 }
1391
Eric Dumazetcfdf7642011-07-27 21:13:03 +00001392 in6_dev = __in6_dev_get(skb->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001393 if (!in6_dev)
1394 return;
Eric Dumazetcfdf7642011-07-27 21:13:03 +00001395 if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001396 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001397
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001398 /* RFC2461 8.1:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001399 * The IP source address of the Redirect MUST be the same as the current
1400 * first-hop router for the specified ICMP Destination Address.
1401 */
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001402
Linus Torvalds1da177e2005-04-16 15:20:36 -07001403 if (!ndisc_parse_options((u8*)(dest + 1), optlen, &ndopts)) {
Joe Perches675418d2012-05-16 19:28:38 +00001404 ND_PRINTK(2, warn, "Redirect: invalid ND options\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001405 return;
1406 }
1407 if (ndopts.nd_opts_tgt_lladdr) {
1408 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr,
1409 skb->dev);
1410 if (!lladdr) {
Joe Perches675418d2012-05-16 19:28:38 +00001411 ND_PRINTK(2, warn,
1412 "Redirect: invalid link-layer address length\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001413 return;
1414 }
1415 }
1416
1417 neigh = __neigh_lookup(&nd_tbl, target, skb->dev, 1);
1418 if (neigh) {
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001419 rt6_redirect(dest, &ipv6_hdr(skb)->daddr,
1420 &ipv6_hdr(skb)->saddr, neigh, lladdr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001421 on_link);
1422 neigh_release(neigh);
1423 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001424}
1425
David S. Miller49919692012-01-27 15:30:48 -08001426void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001427{
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001428 struct net_device *dev = skb->dev;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001429 struct net *net = dev_net(dev);
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001430 struct sock *sk = net->ipv6.ndisc_sk;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001431 int len = sizeof(struct icmp6hdr) + 2 * sizeof(struct in6_addr);
David S. Millerfbfe95a2012-06-08 23:24:18 -07001432 struct inet_peer *peer;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001433 struct sk_buff *buff;
1434 struct icmp6hdr *icmph;
1435 struct in6_addr saddr_buf;
1436 struct in6_addr *addrp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001437 struct rt6_info *rt;
1438 struct dst_entry *dst;
1439 struct inet6_dev *idev;
David S. Miller4c9483b2011-03-12 16:22:43 -05001440 struct flowi6 fl6;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001441 u8 *opt;
Herbert Xua7ae1992011-11-18 02:20:04 +00001442 int hlen, tlen;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001443 int rd_len;
1444 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001445 u8 ha_buf[MAX_ADDR_LEN], *ha = NULL;
David S. Miller1d861aa2012-07-10 03:58:16 -07001446 bool ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001447
Neil Horman95c385b2007-04-25 17:08:10 -07001448 if (ipv6_get_lladdr(dev, &saddr_buf, IFA_F_TENTATIVE)) {
Joe Perches675418d2012-05-16 19:28:38 +00001449 ND_PRINTK(2, warn, "Redirect: no link-local address on %s\n",
1450 dev->name);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001451 return;
1452 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001453
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001454 if (!ipv6_addr_equal(&ipv6_hdr(skb)->daddr, target) &&
Brian Haleybf0b48d2007-10-08 00:12:05 -07001455 ipv6_addr_type(target) != (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) {
Joe Perches675418d2012-05-16 19:28:38 +00001456 ND_PRINTK(2, warn,
1457 "Redirect: target address is not link-local unicast\n");
Li Yewang29556522007-01-30 14:33:20 -08001458 return;
1459 }
1460
David S. Miller4c9483b2011-03-12 16:22:43 -05001461 icmpv6_flow_init(sk, &fl6, NDISC_REDIRECT,
YOSHIFUJI Hideaki95e41e92007-12-06 15:43:30 -08001462 &saddr_buf, &ipv6_hdr(skb)->saddr, dev->ifindex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001463
David S. Miller4c9483b2011-03-12 16:22:43 -05001464 dst = ip6_route_output(net, NULL, &fl6);
RongQing.Li5095d642012-02-21 22:10:49 +00001465 if (dst->error) {
1466 dst_release(dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001467 return;
RongQing.Li5095d642012-02-21 22:10:49 +00001468 }
David S. Miller4c9483b2011-03-12 16:22:43 -05001469 dst = xfrm_lookup(net, dst, flowi6_to_flowi(&fl6), NULL, 0);
David S. Miller452edd52011-03-02 13:27:41 -08001470 if (IS_ERR(dst))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001471 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001472
1473 rt = (struct rt6_info *) dst;
1474
1475 if (rt->rt6i_flags & RTF_GATEWAY) {
Joe Perches675418d2012-05-16 19:28:38 +00001476 ND_PRINTK(2, warn,
1477 "Redirect: destination is not a neighbour\n");
Ilpo Järvinend73f0802009-02-06 23:47:37 -08001478 goto release;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001479 }
David S. Miller1d861aa2012-07-10 03:58:16 -07001480 peer = inet_getpeer_v6(net->ipv6.peers, &rt->rt6i_dst.addr, 1);
1481 ret = inet_peer_xrlim_allow(peer, 1*HZ);
1482 if (peer)
1483 inet_putpeer(peer);
1484 if (!ret)
Ilpo Järvinend73f0802009-02-06 23:47:37 -08001485 goto release;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001486
1487 if (dev->addr_len) {
David S. Miller49919692012-01-27 15:30:48 -08001488 struct neighbour *neigh = dst_neigh_lookup(skb_dst(skb), target);
1489 if (!neigh) {
Joe Perches675418d2012-05-16 19:28:38 +00001490 ND_PRINTK(2, warn,
1491 "Redirect: no neigh for target address\n");
David S. Miller49919692012-01-27 15:30:48 -08001492 goto release;
1493 }
1494
Linus Torvalds1da177e2005-04-16 15:20:36 -07001495 read_lock_bh(&neigh->lock);
1496 if (neigh->nud_state & NUD_VALID) {
1497 memcpy(ha_buf, neigh->ha, dev->addr_len);
1498 read_unlock_bh(&neigh->lock);
1499 ha = ha_buf;
1500 len += ndisc_opt_addr_space(dev);
1501 } else
1502 read_unlock_bh(&neigh->lock);
David S. Miller49919692012-01-27 15:30:48 -08001503
1504 neigh_release(neigh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001505 }
1506
1507 rd_len = min_t(unsigned int,
1508 IPV6_MIN_MTU-sizeof(struct ipv6hdr)-len, skb->len + 8);
1509 rd_len &= ~0x7;
1510 len += rd_len;
1511
Herbert Xua7ae1992011-11-18 02:20:04 +00001512 hlen = LL_RESERVED_SPACE(dev);
1513 tlen = dev->needed_tailroom;
David S. Millerd54a81d2006-12-02 21:00:06 -08001514 buff = sock_alloc_send_skb(sk,
1515 (MAX_HEADER + sizeof(struct ipv6hdr) +
Herbert Xua7ae1992011-11-18 02:20:04 +00001516 len + hlen + tlen),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001517 1, &err);
1518 if (buff == NULL) {
Joe Perches675418d2012-05-16 19:28:38 +00001519 ND_PRINTK(0, err,
1520 "Redirect: %s failed to allocate an skb, err=%d\n",
1521 __func__, err);
Ilpo Järvinend73f0802009-02-06 23:47:37 -08001522 goto release;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001523 }
1524
Herbert Xua7ae1992011-11-18 02:20:04 +00001525 skb_reserve(buff, hlen);
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001526 ip6_nd_hdr(sk, buff, dev, &saddr_buf, &ipv6_hdr(skb)->saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001527 IPPROTO_ICMPV6, len);
1528
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -07001529 skb_set_transport_header(buff, skb_tail_pointer(buff) - buff->data);
Arnaldo Carvalho de Melod10ba342007-03-14 21:05:37 -03001530 skb_put(buff, len);
1531 icmph = icmp6_hdr(buff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001532
1533 memset(icmph, 0, sizeof(struct icmp6hdr));
1534 icmph->icmp6_type = NDISC_REDIRECT;
1535
1536 /*
1537 * copy target and destination addresses
1538 */
1539
1540 addrp = (struct in6_addr *)(icmph + 1);
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001541 *addrp = *target;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001542 addrp++;
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +00001543 *addrp = ipv6_hdr(skb)->daddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001544
1545 opt = (u8*) (addrp + 1);
1546
1547 /*
1548 * include target_address option
1549 */
1550
1551 if (ha)
1552 opt = ndisc_fill_addr_option(opt, ND_OPT_TARGET_LL_ADDR, ha,
1553 dev->addr_len, dev->type);
1554
1555 /*
1556 * build redirect option and copy skb over to the new packet.
1557 */
1558
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001559 memset(opt, 0, 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001560 *(opt++) = ND_OPT_REDIRECT_HDR;
1561 *(opt++) = (rd_len >> 3);
1562 opt += 6;
1563
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001564 memcpy(opt, ipv6_hdr(skb), rd_len - 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001565
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001566 icmph->icmp6_cksum = csum_ipv6_magic(&saddr_buf, &ipv6_hdr(skb)->saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001567 len, IPPROTO_ICMPV6,
Joe Perches07f07572008-11-19 15:44:53 -08001568 csum_partial(icmph, len, 0));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001569
Eric Dumazetadf30902009-06-02 05:19:30 +00001570 skb_dst_set(buff, dst);
Eric Dumazetcfdf7642011-07-27 21:13:03 +00001571 rcu_read_lock();
1572 idev = __in6_dev_get(dst->dev);
Neil Hormanedf391f2009-04-27 02:45:02 -07001573 IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUT, skb->len);
Jan Engelhardtb2e0b382010-03-23 04:09:07 +01001574 err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, buff, NULL, dst->dev,
Patrick McHardy6e23ae22007-11-19 18:53:30 -08001575 dst_output);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001576 if (!err) {
Denis V. Lunev5c5d2442008-10-08 10:33:50 -07001577 ICMP6MSGOUT_INC_STATS(net, idev, NDISC_REDIRECT);
Denis V. Luneva862f6a2008-10-08 10:33:06 -07001578 ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001579 }
1580
Eric Dumazetcfdf7642011-07-27 21:13:03 +00001581 rcu_read_unlock();
Ilpo Järvinend73f0802009-02-06 23:47:37 -08001582 return;
1583
1584release:
1585 dst_release(dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001586}
1587
1588static void pndisc_redo(struct sk_buff *skb)
1589{
YOSHIFUJI Hideaki140e26fc2005-10-05 12:11:41 -07001590 ndisc_recv_ns(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001591 kfree_skb(skb);
1592}
1593
1594int ndisc_rcv(struct sk_buff *skb)
1595{
1596 struct nd_msg *msg;
1597
1598 if (!pskb_may_pull(skb, skb->len))
1599 return 0;
1600
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -07001601 msg = (struct nd_msg *)skb_transport_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001602
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -07001603 __skb_push(skb, skb->data - skb_transport_header(skb));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001604
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001605 if (ipv6_hdr(skb)->hop_limit != 255) {
Joe Perches675418d2012-05-16 19:28:38 +00001606 ND_PRINTK(2, warn, "NDISC: invalid hop-limit: %d\n",
1607 ipv6_hdr(skb)->hop_limit);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001608 return 0;
1609 }
1610
1611 if (msg->icmph.icmp6_code != 0) {
Joe Perches675418d2012-05-16 19:28:38 +00001612 ND_PRINTK(2, warn, "NDISC: invalid ICMPv6 code: %d\n",
1613 msg->icmph.icmp6_code);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001614 return 0;
1615 }
1616
Patrick McHardya61bbcf2005-08-14 17:24:31 -07001617 memset(NEIGH_CB(skb), 0, sizeof(struct neighbour_cb));
1618
Linus Torvalds1da177e2005-04-16 15:20:36 -07001619 switch (msg->icmph.icmp6_type) {
1620 case NDISC_NEIGHBOUR_SOLICITATION:
1621 ndisc_recv_ns(skb);
1622 break;
1623
1624 case NDISC_NEIGHBOUR_ADVERTISEMENT:
1625 ndisc_recv_na(skb);
1626 break;
1627
1628 case NDISC_ROUTER_SOLICITATION:
1629 ndisc_recv_rs(skb);
1630 break;
1631
1632 case NDISC_ROUTER_ADVERTISEMENT:
1633 ndisc_router_discovery(skb);
1634 break;
1635
1636 case NDISC_REDIRECT:
1637 ndisc_redirect_rcv(skb);
1638 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -07001639 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001640
1641 return 0;
1642}
1643
1644static int ndisc_netdev_event(struct notifier_block *this, unsigned long event, void *ptr)
1645{
1646 struct net_device *dev = ptr;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001647 struct net *net = dev_net(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001648
1649 switch (event) {
1650 case NETDEV_CHANGEADDR:
1651 neigh_changeaddr(&nd_tbl, dev);
Daniel Lezcano5b7c9312008-03-03 23:28:58 -08001652 fib6_run_gc(~0UL, net);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001653 break;
1654 case NETDEV_DOWN:
1655 neigh_ifdown(&nd_tbl, dev);
Daniel Lezcano5b7c9312008-03-03 23:28:58 -08001656 fib6_run_gc(~0UL, net);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001657 break;
Ben Hutchingsf47b9462011-04-15 13:46:02 +00001658 case NETDEV_NOTIFY_PEERS:
1659 ndisc_send_unsol_na(dev);
1660 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001661 default:
1662 break;
1663 }
1664
1665 return NOTIFY_DONE;
1666}
1667
1668static struct notifier_block ndisc_netdev_notifier = {
1669 .notifier_call = ndisc_netdev_event,
1670};
1671
1672#ifdef CONFIG_SYSCTL
1673static void ndisc_warn_deprecated_sysctl(struct ctl_table *ctl,
1674 const char *func, const char *dev_name)
1675{
1676 static char warncomm[TASK_COMM_LEN];
1677 static int warned;
1678 if (strcmp(warncomm, current->comm) && warned < 5) {
1679 strcpy(warncomm, current->comm);
Joe Perchesf3213832012-05-15 14:11:53 +00001680 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 -07001681 warncomm, func,
1682 dev_name, ctl->procname,
1683 dev_name, ctl->procname);
1684 warned++;
1685 }
1686}
1687
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001688int 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 -07001689{
1690 struct net_device *dev = ctl->extra1;
1691 struct inet6_dev *idev;
1692 int ret;
1693
Eric W. Biedermand12af672007-10-18 03:05:25 -07001694 if ((strcmp(ctl->procname, "retrans_time") == 0) ||
1695 (strcmp(ctl->procname, "base_reachable_time") == 0))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001696 ndisc_warn_deprecated_sysctl(ctl, "syscall", dev ? dev->name : "default");
1697
Eric W. Biedermand12af672007-10-18 03:05:25 -07001698 if (strcmp(ctl->procname, "retrans_time") == 0)
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001699 ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
Eric W. Biedermand12af672007-10-18 03:05:25 -07001700
1701 else if (strcmp(ctl->procname, "base_reachable_time") == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001702 ret = proc_dointvec_jiffies(ctl, write,
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001703 buffer, lenp, ppos);
Eric W. Biedermand12af672007-10-18 03:05:25 -07001704
1705 else if ((strcmp(ctl->procname, "retrans_time_ms") == 0) ||
YOSHIFUJI Hideakiad02ac12007-10-29 01:32:23 -07001706 (strcmp(ctl->procname, "base_reachable_time_ms") == 0))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001707 ret = proc_dointvec_ms_jiffies(ctl, write,
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001708 buffer, lenp, ppos);
Eric W. Biedermand12af672007-10-18 03:05:25 -07001709 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001710 ret = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001711
1712 if (write && ret == 0 && dev && (idev = in6_dev_get(dev)) != NULL) {
Eric W. Biedermand12af672007-10-18 03:05:25 -07001713 if (ctl->data == &idev->nd_parms->base_reachable_time)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001714 idev->nd_parms->reachable_time = neigh_rand_reach_time(idev->nd_parms->base_reachable_time);
1715 idev->tstamp = jiffies;
1716 inet6_ifinfo_notify(RTM_NEWLINK, idev);
1717 in6_dev_put(idev);
1718 }
1719 return ret;
1720}
1721
Linus Torvalds1da177e2005-04-16 15:20:36 -07001722
1723#endif
1724
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00001725static int __net_init ndisc_net_init(struct net *net)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001726{
1727 struct ipv6_pinfo *np;
1728 struct sock *sk;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001729 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001730
Denis V. Lunev1ed85162008-04-03 14:31:03 -07001731 err = inet_ctl_sock_create(&sk, PF_INET6,
1732 SOCK_RAW, IPPROTO_ICMPV6, net);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001733 if (err < 0) {
Joe Perches675418d2012-05-16 19:28:38 +00001734 ND_PRINTK(0, err,
1735 "NDISC: Failed to initialize the control socket (err %d)\n",
1736 err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001737 return err;
1738 }
1739
Denis V. Lunev1ed85162008-04-03 14:31:03 -07001740 net->ipv6.ndisc_sk = sk;
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001741
Linus Torvalds1da177e2005-04-16 15:20:36 -07001742 np = inet6_sk(sk);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001743 np->hop_limit = 255;
1744 /* Do not loopback ndisc messages */
1745 np->mc_loop = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001746
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001747 return 0;
1748}
1749
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00001750static void __net_exit ndisc_net_exit(struct net *net)
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001751{
Denis V. Lunev1ed85162008-04-03 14:31:03 -07001752 inet_ctl_sock_destroy(net->ipv6.ndisc_sk);
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001753}
1754
1755static struct pernet_operations ndisc_net_ops = {
1756 .init = ndisc_net_init,
1757 .exit = ndisc_net_exit,
1758};
1759
1760int __init ndisc_init(void)
1761{
1762 int err;
1763
1764 err = register_pernet_subsys(&ndisc_net_ops);
1765 if (err)
1766 return err;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001767 /*
1768 * Initialize the neighbour table
1769 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001770 neigh_table_init(&nd_tbl);
1771
1772#ifdef CONFIG_SYSCTL
Eric W. Biederman54716e32010-02-14 03:27:03 +00001773 err = neigh_sysctl_register(NULL, &nd_tbl.parms, "ipv6",
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001774 &ndisc_ifinfo_sysctl_change);
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001775 if (err)
1776 goto out_unregister_pernet;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001777#endif
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001778 err = register_netdevice_notifier(&ndisc_netdev_notifier);
1779 if (err)
1780 goto out_unregister_sysctl;
1781out:
1782 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001783
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001784out_unregister_sysctl:
1785#ifdef CONFIG_SYSCTL
1786 neigh_sysctl_unregister(&nd_tbl.parms);
1787out_unregister_pernet:
1788#endif
1789 unregister_pernet_subsys(&ndisc_net_ops);
1790 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001791}
1792
1793void ndisc_cleanup(void)
1794{
Dmitry Mishin36f73d02006-11-03 16:08:19 -08001795 unregister_netdevice_notifier(&ndisc_netdev_notifier);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001796#ifdef CONFIG_SYSCTL
1797 neigh_sysctl_unregister(&nd_tbl.parms);
1798#endif
1799 neigh_table_clear(&nd_tbl);
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001800 unregister_pernet_subsys(&ndisc_net_ops);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001801}