blob: 49dfc2a73ee04bbc543d669e1a384a0476d8154c [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * Neighbour Discovery for IPv6
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09003 * Linux INET6 implementation
Linus Torvalds1da177e2005-04-16 15:20:36 -07004 *
5 * Authors:
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09006 * Pedro Roque <roque@di.fc.ul.pt>
Linus Torvalds1da177e2005-04-16 15:20:36 -07007 * Mike Shaver <shaver@ingenia.com>
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version
12 * 2 of the License, or (at your option) any later version.
13 */
14
15/*
16 * Changes:
17 *
Alexey I. Froloffe35f30c2012-04-06 05:50:58 +000018 * Alexey I. Froloff : RFC6106 (DNSSL) support
Pierre Ynard31910572007-10-10 21:22:05 -070019 * Pierre Ynard : export userland ND options
20 * through netlink (RDNSS support)
Linus Torvalds1da177e2005-04-16 15:20:36 -070021 * Lars Fenneberg : fixed MTU setting on receipt
22 * of an RA.
Linus Torvalds1da177e2005-04-16 15:20:36 -070023 * Janos Farkas : kmalloc failure checks
24 * Alexey Kuznetsov : state machine reworked
25 * and moved to net/core.
26 * Pekka Savola : RFC2461 validation
27 * YOSHIFUJI Hideaki @USAGI : Verify ND options properly
28 */
29
Joe Perches675418d2012-05-16 19:28:38 +000030#define pr_fmt(fmt) "ICMPv6: " fmt
Linus Torvalds1da177e2005-04-16 15:20:36 -070031
32#include <linux/module.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070033#include <linux/errno.h>
34#include <linux/types.h>
35#include <linux/socket.h>
36#include <linux/sockios.h>
37#include <linux/sched.h>
38#include <linux/net.h>
39#include <linux/in6.h>
40#include <linux/route.h>
41#include <linux/init.h>
42#include <linux/rcupdate.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090043#include <linux/slab.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070044#ifdef CONFIG_SYSCTL
45#include <linux/sysctl.h>
46#endif
47
Thomas Graf18237302006-08-04 23:04:54 -070048#include <linux/if_addr.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070049#include <linux/if_arp.h>
50#include <linux/ipv6.h>
51#include <linux/icmpv6.h>
52#include <linux/jhash.h>
53
54#include <net/sock.h>
55#include <net/snmp.h>
56
57#include <net/ipv6.h>
58#include <net/protocol.h>
59#include <net/ndisc.h>
60#include <net/ip6_route.h>
61#include <net/addrconf.h>
62#include <net/icmp.h>
63
Pierre Ynard31910572007-10-10 21:22:05 -070064#include <net/netlink.h>
65#include <linux/rtnetlink.h>
66
Linus Torvalds1da177e2005-04-16 15:20:36 -070067#include <net/flow.h>
68#include <net/ip6_checksum.h>
Denis V. Lunev1ed85162008-04-03 14:31:03 -070069#include <net/inet_common.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070070#include <linux/proc_fs.h>
71
72#include <linux/netfilter.h>
73#include <linux/netfilter_ipv6.h>
74
Joe Perches675418d2012-05-16 19:28:38 +000075/* Set to 3 to get tracing... */
76#define ND_DEBUG 1
77
78#define ND_PRINTK(val, level, fmt, ...) \
79do { \
80 if (val <= ND_DEBUG) \
81 net_##level##_ratelimited(fmt, ##__VA_ARGS__); \
82} while (0)
83
Eric Dumazetd6bf7812010-10-04 06:15:44 +000084static u32 ndisc_hash(const void *pkey,
85 const struct net_device *dev,
David S. Miller2c2aba62011-12-28 15:06:58 -050086 __u32 *hash_rnd);
Linus Torvalds1da177e2005-04-16 15:20:36 -070087static int ndisc_constructor(struct neighbour *neigh);
88static void ndisc_solicit(struct neighbour *neigh, struct sk_buff *skb);
89static void ndisc_error_report(struct neighbour *neigh, struct sk_buff *skb);
90static int pndisc_constructor(struct pneigh_entry *n);
91static void pndisc_destructor(struct pneigh_entry *n);
92static void pndisc_redo(struct sk_buff *skb);
93
Stephen Hemminger89d69d22009-09-01 11:13:19 +000094static const struct neigh_ops ndisc_generic_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -070095 .family = AF_INET6,
96 .solicit = ndisc_solicit,
97 .error_report = ndisc_error_report,
98 .output = neigh_resolve_output,
99 .connected_output = neigh_connected_output,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100};
101
Stephen Hemminger89d69d22009-09-01 11:13:19 +0000102static const struct neigh_ops ndisc_hh_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700103 .family = AF_INET6,
104 .solicit = ndisc_solicit,
105 .error_report = ndisc_error_report,
106 .output = neigh_resolve_output,
107 .connected_output = neigh_resolve_output,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108};
109
110
Stephen Hemminger89d69d22009-09-01 11:13:19 +0000111static const struct neigh_ops ndisc_direct_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700112 .family = AF_INET6,
David S. Miller8f40b162011-07-17 13:34:11 -0700113 .output = neigh_direct_output,
114 .connected_output = neigh_direct_output,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700115};
116
117struct neigh_table nd_tbl = {
118 .family = AF_INET6,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700119 .key_len = sizeof(struct in6_addr),
120 .hash = ndisc_hash,
121 .constructor = ndisc_constructor,
122 .pconstructor = pndisc_constructor,
123 .pdestructor = pndisc_destructor,
124 .proxy_redo = pndisc_redo,
125 .id = "ndisc_cache",
126 .parms = {
Shan Weib6720832010-12-01 18:05:12 +0000127 .tbl = &nd_tbl,
128 .base_reachable_time = ND_REACHABLE_TIME,
129 .retrans_time = ND_RETRANS_TIMER,
130 .gc_staletime = 60 * HZ,
131 .reachable_time = ND_REACHABLE_TIME,
132 .delay_probe_time = 5 * HZ,
Eric Dumazet8b5c1712011-11-09 12:07:14 +0000133 .queue_len_bytes = 64*1024,
Shan Weib6720832010-12-01 18:05:12 +0000134 .ucast_probes = 3,
135 .mcast_probes = 3,
136 .anycast_delay = 1 * HZ,
137 .proxy_delay = (8 * HZ) / 10,
138 .proxy_qlen = 64,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700139 },
140 .gc_interval = 30 * HZ,
141 .gc_thresh1 = 128,
142 .gc_thresh2 = 512,
143 .gc_thresh3 = 1024,
144};
145
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146static inline int ndisc_opt_addr_space(struct net_device *dev)
147{
148 return NDISC_OPT_SPACE(dev->addr_len + ndisc_addr_option_pad(dev->type));
149}
150
YOSHIFUJI Hideaki / 吉藤英明315ff092013-01-21 06:47:50 +0000151static u8 *ndisc_fill_addr_option(u8 *opt, int type, void *data,
152 struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700153{
YOSHIFUJI Hideaki / 吉藤英明315ff092013-01-21 06:47:50 +0000154 int pad = ndisc_addr_option_pad(dev->type);
155 int data_len = dev->addr_len;
YOSHIFUJI Hideaki / 吉藤英明7bdc1b42012-12-13 04:29:36 +0000156 int space = NDISC_OPT_SPACE(data_len + pad);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157
158 opt[0] = type;
159 opt[1] = space>>3;
160
161 memset(opt + 2, 0, pad);
162 opt += pad;
163 space -= pad;
164
165 memcpy(opt+2, data, data_len);
166 data_len += 2;
167 opt += data_len;
168 if ((space -= data_len) > 0)
169 memset(opt, 0, space);
170 return opt + space;
171}
172
173static struct nd_opt_hdr *ndisc_next_option(struct nd_opt_hdr *cur,
174 struct nd_opt_hdr *end)
175{
176 int type;
177 if (!cur || !end || cur >= end)
178 return NULL;
179 type = cur->nd_opt_type;
180 do {
181 cur = ((void *)cur) + (cur->nd_opt_len << 3);
182 } while(cur < end && cur->nd_opt_type != type);
Eric Dumazeta02cec22010-09-22 20:43:57 +0000183 return cur <= end && cur->nd_opt_type == type ? cur : NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184}
185
Pierre Ynard31910572007-10-10 21:22:05 -0700186static inline int ndisc_is_useropt(struct nd_opt_hdr *opt)
187{
Alexey I. Froloffe35f30c2012-04-06 05:50:58 +0000188 return opt->nd_opt_type == ND_OPT_RDNSS ||
189 opt->nd_opt_type == ND_OPT_DNSSL;
Pierre Ynard31910572007-10-10 21:22:05 -0700190}
191
192static struct nd_opt_hdr *ndisc_next_useropt(struct nd_opt_hdr *cur,
193 struct nd_opt_hdr *end)
194{
195 if (!cur || !end || cur >= end)
196 return NULL;
197 do {
198 cur = ((void *)cur) + (cur->nd_opt_len << 3);
199 } while(cur < end && !ndisc_is_useropt(cur));
Eric Dumazeta02cec22010-09-22 20:43:57 +0000200 return cur <= end && ndisc_is_useropt(cur) ? cur : NULL;
Pierre Ynard31910572007-10-10 21:22:05 -0700201}
202
David S. Miller30f2a5f2012-07-11 23:26:46 -0700203struct ndisc_options *ndisc_parse_options(u8 *opt, int opt_len,
204 struct ndisc_options *ndopts)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700205{
206 struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)opt;
207
208 if (!nd_opt || opt_len < 0 || !ndopts)
209 return NULL;
210 memset(ndopts, 0, sizeof(*ndopts));
211 while (opt_len) {
212 int l;
213 if (opt_len < sizeof(struct nd_opt_hdr))
214 return NULL;
215 l = nd_opt->nd_opt_len << 3;
216 if (opt_len < l || l == 0)
217 return NULL;
218 switch (nd_opt->nd_opt_type) {
219 case ND_OPT_SOURCE_LL_ADDR:
220 case ND_OPT_TARGET_LL_ADDR:
221 case ND_OPT_MTU:
222 case ND_OPT_REDIRECT_HDR:
223 if (ndopts->nd_opt_array[nd_opt->nd_opt_type]) {
Joe Perches675418d2012-05-16 19:28:38 +0000224 ND_PRINTK(2, warn,
225 "%s: duplicated ND6 option found: type=%d\n",
226 __func__, nd_opt->nd_opt_type);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700227 } else {
228 ndopts->nd_opt_array[nd_opt->nd_opt_type] = nd_opt;
229 }
230 break;
231 case ND_OPT_PREFIX_INFO:
232 ndopts->nd_opts_pi_end = nd_opt;
Stephen Hemmingercfcabdc2007-10-09 01:59:42 -0700233 if (!ndopts->nd_opt_array[nd_opt->nd_opt_type])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700234 ndopts->nd_opt_array[nd_opt->nd_opt_type] = nd_opt;
235 break;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -0800236#ifdef CONFIG_IPV6_ROUTE_INFO
237 case ND_OPT_ROUTE_INFO:
238 ndopts->nd_opts_ri_end = nd_opt;
239 if (!ndopts->nd_opts_ri)
240 ndopts->nd_opts_ri = nd_opt;
241 break;
242#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700243 default:
Pierre Ynard31910572007-10-10 21:22:05 -0700244 if (ndisc_is_useropt(nd_opt)) {
245 ndopts->nd_useropts_end = nd_opt;
246 if (!ndopts->nd_useropts)
247 ndopts->nd_useropts = nd_opt;
248 } else {
249 /*
250 * Unknown options must be silently ignored,
251 * to accommodate future extension to the
252 * protocol.
253 */
Joe Perches675418d2012-05-16 19:28:38 +0000254 ND_PRINTK(2, notice,
255 "%s: ignored unsupported option; type=%d, len=%d\n",
256 __func__,
257 nd_opt->nd_opt_type,
258 nd_opt->nd_opt_len);
Pierre Ynard31910572007-10-10 21:22:05 -0700259 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700260 }
261 opt_len -= l;
262 nd_opt = ((void *)nd_opt) + l;
263 }
264 return ndopts;
265}
266
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000267int ndisc_mc_map(const struct in6_addr *addr, char *buf, struct net_device *dev, int dir)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268{
269 switch (dev->type) {
270 case ARPHRD_ETHER:
271 case ARPHRD_IEEE802: /* Not sure. Check it later. --ANK */
272 case ARPHRD_FDDI:
273 ipv6_eth_mc_map(addr, buf);
274 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700275 case ARPHRD_ARCNET:
276 ipv6_arcnet_mc_map(addr, buf);
277 return 0;
278 case ARPHRD_INFINIBAND:
Rolf Manderscheida9e527e2007-12-10 13:38:41 -0700279 ipv6_ib_mc_map(addr, dev->broadcast, buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700280 return 0;
Timo Teräs93ca3bb2011-03-28 22:40:53 +0000281 case ARPHRD_IPGRE:
282 return ipv6_ipgre_mc_map(addr, dev->broadcast, buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283 default:
284 if (dir) {
285 memcpy(buf, dev->broadcast, dev->addr_len);
286 return 0;
287 }
288 }
289 return -EINVAL;
290}
291
YOSHIFUJI Hideaki71590392007-02-22 22:05:40 +0900292EXPORT_SYMBOL(ndisc_mc_map);
293
Eric Dumazetd6bf7812010-10-04 06:15:44 +0000294static u32 ndisc_hash(const void *pkey,
295 const struct net_device *dev,
David S. Miller2c2aba62011-12-28 15:06:58 -0500296 __u32 *hash_rnd)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700297{
David S. Miller2c2aba62011-12-28 15:06:58 -0500298 return ndisc_hashfn(pkey, dev, hash_rnd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700299}
300
301static int ndisc_constructor(struct neighbour *neigh)
302{
303 struct in6_addr *addr = (struct in6_addr*)&neigh->primary_key;
304 struct net_device *dev = neigh->dev;
305 struct inet6_dev *in6_dev;
306 struct neigh_parms *parms;
Eric Dumazeta50feda2012-05-18 18:57:34 +0000307 bool is_multicast = ipv6_addr_is_multicast(addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700308
Linus Torvalds1da177e2005-04-16 15:20:36 -0700309 in6_dev = in6_dev_get(dev);
310 if (in6_dev == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700311 return -EINVAL;
312 }
313
314 parms = in6_dev->nd_parms;
315 __neigh_parms_put(neigh->parms);
316 neigh->parms = neigh_parms_clone(parms);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700317
318 neigh->type = is_multicast ? RTN_MULTICAST : RTN_UNICAST;
Stephen Hemminger3b04ddd2007-10-09 01:40:57 -0700319 if (!dev->header_ops) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700320 neigh->nud_state = NUD_NOARP;
321 neigh->ops = &ndisc_direct_ops;
David S. Miller8f40b162011-07-17 13:34:11 -0700322 neigh->output = neigh_direct_output;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700323 } else {
324 if (is_multicast) {
325 neigh->nud_state = NUD_NOARP;
326 ndisc_mc_map(addr, neigh->ha, dev, 1);
327 } else if (dev->flags&(IFF_NOARP|IFF_LOOPBACK)) {
328 neigh->nud_state = NUD_NOARP;
329 memcpy(neigh->ha, dev->dev_addr, dev->addr_len);
330 if (dev->flags&IFF_LOOPBACK)
331 neigh->type = RTN_LOCAL;
332 } else if (dev->flags&IFF_POINTOPOINT) {
333 neigh->nud_state = NUD_NOARP;
334 memcpy(neigh->ha, dev->broadcast, dev->addr_len);
335 }
Stephen Hemminger3b04ddd2007-10-09 01:40:57 -0700336 if (dev->header_ops->cache)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337 neigh->ops = &ndisc_hh_ops;
338 else
339 neigh->ops = &ndisc_generic_ops;
340 if (neigh->nud_state&NUD_VALID)
341 neigh->output = neigh->ops->connected_output;
342 else
343 neigh->output = neigh->ops->output;
344 }
345 in6_dev_put(in6_dev);
346 return 0;
347}
348
349static int pndisc_constructor(struct pneigh_entry *n)
350{
351 struct in6_addr *addr = (struct in6_addr*)&n->key;
352 struct in6_addr maddr;
353 struct net_device *dev = n->dev;
354
355 if (dev == NULL || __in6_dev_get(dev) == NULL)
356 return -EINVAL;
357 addrconf_addr_solict_mult(addr, &maddr);
358 ipv6_dev_mc_inc(dev, &maddr);
359 return 0;
360}
361
362static void pndisc_destructor(struct pneigh_entry *n)
363{
364 struct in6_addr *addr = (struct in6_addr*)&n->key;
365 struct in6_addr maddr;
366 struct net_device *dev = n->dev;
367
368 if (dev == NULL || __in6_dev_get(dev) == NULL)
369 return;
370 addrconf_addr_solict_mult(addr, &maddr);
371 ipv6_dev_mc_dec(dev, &maddr);
372}
373
YOSHIFUJI Hideakifd0ea7d2012-12-13 02:40:26 +0900374static struct sk_buff *ndisc_build_skb(struct net_device *dev,
375 const struct in6_addr *daddr,
376 const struct in6_addr *saddr,
377 struct icmp6hdr *icmp6h,
378 const struct in6_addr *target,
379 int llinfo)
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900380{
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900381 struct net *net = dev_net(dev);
Daniel Lezcano1762f7e2008-03-07 11:15:34 -0800382 struct sock *sk = net->ipv6.ndisc_sk;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900383 struct sk_buff *skb;
384 struct icmp6hdr *hdr;
Herbert Xua7ae1992011-11-18 02:20:04 +0000385 int hlen = LL_RESERVED_SPACE(dev);
386 int tlen = dev->needed_tailroom;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900387 int len;
388 int err;
Brian Haley305d5522008-11-04 17:51:14 -0800389 u8 *opt;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900390
391 if (!dev->addr_len)
392 llinfo = 0;
393
394 len = sizeof(struct icmp6hdr) + (target ? sizeof(*target) : 0);
395 if (llinfo)
396 len += ndisc_opt_addr_space(dev);
397
398 skb = sock_alloc_send_skb(sk,
YOSHIFUJI Hideaki / 吉藤英明b7dc8c32013-01-04 03:58:04 +0000399 (sizeof(struct ipv6hdr) +
Herbert Xua7ae1992011-11-18 02:20:04 +0000400 len + hlen + tlen),
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900401 1, &err);
402 if (!skb) {
Joe Perches675418d2012-05-16 19:28:38 +0000403 ND_PRINTK(0, err, "ND: %s failed to allocate an skb, err=%d\n",
404 __func__, err);
Brian Haley305d5522008-11-04 17:51:14 -0800405 return NULL;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900406 }
407
Herbert Xua7ae1992011-11-18 02:20:04 +0000408 skb_reserve(skb, hlen);
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900409 ip6_nd_hdr(sk, skb, dev, saddr, daddr, IPPROTO_ICMPV6, len);
410
411 skb->transport_header = skb->tail;
412 skb_put(skb, len);
413
414 hdr = (struct icmp6hdr *)skb_transport_header(skb);
415 memcpy(hdr, icmp6h, sizeof(*hdr));
416
417 opt = skb_transport_header(skb) + sizeof(struct icmp6hdr);
418 if (target) {
Alexey Dobriyan4e3fd7a2011-11-21 03:39:03 +0000419 *(struct in6_addr *)opt = *target;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900420 opt += sizeof(*target);
421 }
422
423 if (llinfo)
YOSHIFUJI Hideaki / 吉藤英明315ff092013-01-21 06:47:50 +0000424 ndisc_fill_addr_option(opt, llinfo, dev->dev_addr, dev);
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900425
426 hdr->icmp6_cksum = csum_ipv6_magic(saddr, daddr, len,
427 IPPROTO_ICMPV6,
Joe Perches07f07572008-11-19 15:44:53 -0800428 csum_partial(hdr,
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900429 len, 0));
430
Brian Haley305d5522008-11-04 17:51:14 -0800431 return skb;
432}
433
YOSHIFUJI Hideakifd0ea7d2012-12-13 02:40:26 +0900434static void ndisc_send_skb(struct sk_buff *skb, struct net_device *dev,
YOSHIFUJI Hideakifd0ea7d2012-12-13 02:40:26 +0900435 const struct in6_addr *daddr,
436 const struct in6_addr *saddr,
437 struct icmp6hdr *icmp6h)
Brian Haley305d5522008-11-04 17:51:14 -0800438{
David S. Miller4c9483b2011-03-12 16:22:43 -0500439 struct flowi6 fl6;
Brian Haley305d5522008-11-04 17:51:14 -0800440 struct dst_entry *dst;
441 struct net *net = dev_net(dev);
442 struct sock *sk = net->ipv6.ndisc_sk;
443 struct inet6_dev *idev;
444 int err;
445 u8 type;
446
447 type = icmp6h->icmp6_type;
448
David S. Miller4c9483b2011-03-12 16:22:43 -0500449 icmpv6_flow_init(sk, &fl6, type, saddr, daddr, dev->ifindex);
YOSHIFUJI Hideaki / 吉藤英明12fd84f2013-01-18 02:00:24 +0000450 dst = icmp6_dst_alloc(dev, &fl6);
David S. Miller452edd52011-03-02 13:27:41 -0800451 if (IS_ERR(dst)) {
Brian Haley305d5522008-11-04 17:51:14 -0800452 kfree_skb(skb);
453 return;
454 }
455
Eric Dumazetadf30902009-06-02 05:19:30 +0000456 skb_dst_set(skb, dst);
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900457
Eric Dumazetcfdf7642011-07-27 21:13:03 +0000458 rcu_read_lock();
459 idev = __in6_dev_get(dst->dev);
Neil Hormanedf391f2009-04-27 02:45:02 -0700460 IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUT, skb->len);
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900461
Jan Engelhardtb2e0b382010-03-23 04:09:07 +0100462 err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, skb, NULL, dst->dev,
Patrick McHardy6e23ae22007-11-19 18:53:30 -0800463 dst_output);
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900464 if (!err) {
Denis V. Lunev5c5d2442008-10-08 10:33:50 -0700465 ICMP6MSGOUT_INC_STATS(net, idev, type);
Denis V. Luneva862f6a2008-10-08 10:33:06 -0700466 ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS);
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900467 }
468
Eric Dumazetcfdf7642011-07-27 21:13:03 +0000469 rcu_read_unlock();
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900470}
471
Brian Haley305d5522008-11-04 17:51:14 -0800472/*
473 * Send a Neighbour Discover packet
474 */
475static void __ndisc_send(struct net_device *dev,
Brian Haley305d5522008-11-04 17:51:14 -0800476 const struct in6_addr *daddr,
477 const struct in6_addr *saddr,
478 struct icmp6hdr *icmp6h, const struct in6_addr *target,
479 int llinfo)
480{
481 struct sk_buff *skb;
482
483 skb = ndisc_build_skb(dev, daddr, saddr, icmp6h, target, llinfo);
484 if (!skb)
485 return;
486
YOSHIFUJI Hideaki / 吉藤英明12fd84f2013-01-18 02:00:24 +0000487 ndisc_send_skb(skb, dev, daddr, saddr, icmp6h);
Brian Haley305d5522008-11-04 17:51:14 -0800488}
489
Linus Torvalds1da177e2005-04-16 15:20:36 -0700490static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +0900491 const struct in6_addr *daddr,
492 const struct in6_addr *solicited_addr,
YOSHIFUJI Hideaki / 吉藤英明fb568632013-01-20 07:39:18 +0000493 bool router, bool solicited, bool override, bool inc_opt)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700494{
495 struct in6_addr tmpaddr;
496 struct inet6_ifaddr *ifp;
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +0900497 const struct in6_addr *src_addr;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900498 struct icmp6hdr icmp6h = {
499 .icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT,
500 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501
502 /* for anycast or proxy, solicited_addr != src_addr */
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900503 ifp = ipv6_get_ifaddr(dev_net(dev), solicited_addr, dev, 1);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900504 if (ifp) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700505 src_addr = solicited_addr;
Neil Horman95c385b2007-04-25 17:08:10 -0700506 if (ifp->flags & IFA_F_OPTIMISTIC)
507 override = 0;
stephen hemminger9f888162010-06-21 11:00:13 +0000508 inc_opt |= ifp->idev->cnf.force_tllao;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700509 in6_ifa_put(ifp);
510 } else {
Brian Haley191cd582008-08-14 15:33:21 -0700511 if (ipv6_dev_get_saddr(dev_net(dev), dev, daddr,
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900512 inet6_sk(dev_net(dev)->ipv6.ndisc_sk)->srcprefs,
YOSHIFUJI Hideaki7cbca672008-03-25 09:37:42 +0900513 &tmpaddr))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514 return;
515 src_addr = &tmpaddr;
516 }
517
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900518 icmp6h.icmp6_router = router;
519 icmp6h.icmp6_solicited = solicited;
520 icmp6h.icmp6_override = override;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700521
YOSHIFUJI Hideaki / 吉藤英明12fd84f2013-01-18 02:00:24 +0000522 __ndisc_send(dev, daddr, src_addr, &icmp6h, solicited_addr,
David L Stevens14878f72007-09-16 16:52:35 -0700523 inc_opt ? ND_OPT_TARGET_LL_ADDR : 0);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900524}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700525
Ben Hutchingsf47b9462011-04-15 13:46:02 +0000526static void ndisc_send_unsol_na(struct net_device *dev)
527{
528 struct inet6_dev *idev;
529 struct inet6_ifaddr *ifa;
Ben Hutchingsf47b9462011-04-15 13:46:02 +0000530
531 idev = in6_dev_get(dev);
532 if (!idev)
533 return;
534
535 read_lock_bh(&idev->lock);
536 list_for_each_entry(ifa, &idev->addr_list, if_list) {
YOSHIFUJI Hideaki / 吉藤英明9fafd652012-11-12 07:50:17 +0000537 ndisc_send_na(dev, NULL, &in6addr_linklocal_allnodes, &ifa->addr,
Ben Hutchingsf47b9462011-04-15 13:46:02 +0000538 /*router=*/ !!idev->cnf.forwarding,
539 /*solicited=*/ false, /*override=*/ true,
540 /*inc_opt=*/ true);
541 }
542 read_unlock_bh(&idev->lock);
543
544 in6_dev_put(idev);
545}
546
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +0900548 const struct in6_addr *solicit,
549 const struct in6_addr *daddr, const struct in6_addr *saddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700550{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700551 struct in6_addr addr_buf;
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900552 struct icmp6hdr icmp6h = {
553 .icmp6_type = NDISC_NEIGHBOUR_SOLICITATION,
554 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700555
556 if (saddr == NULL) {
Neil Horman95c385b2007-04-25 17:08:10 -0700557 if (ipv6_get_lladdr(dev, &addr_buf,
558 (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700559 return;
560 saddr = &addr_buf;
561 }
562
YOSHIFUJI Hideaki / 吉藤英明12fd84f2013-01-18 02:00:24 +0000563 __ndisc_send(dev, daddr, saddr, &icmp6h, solicit,
David L Stevens14878f72007-09-16 16:52:35 -0700564 !ipv6_addr_any(saddr) ? ND_OPT_SOURCE_LL_ADDR : 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565}
566
YOSHIFUJI Hideaki9acd9f32008-04-10 15:42:10 +0900567void ndisc_send_rs(struct net_device *dev, const struct in6_addr *saddr,
568 const struct in6_addr *daddr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569{
YOSHIFUJI Hideakie1ec7842007-04-24 20:44:52 +0900570 struct icmp6hdr icmp6h = {
571 .icmp6_type = NDISC_ROUTER_SOLICITATION,
572 };
Neil Horman95c385b2007-04-25 17:08:10 -0700573 int send_sllao = dev->addr_len;
Neil Horman95c385b2007-04-25 17:08:10 -0700574
575#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
576 /*
577 * According to section 2.2 of RFC 4429, we must not
578 * send router solicitations with a sllao from
579 * optimistic addresses, but we may send the solicitation
580 * if we don't include the sllao. So here we check
581 * if our address is optimistic, and if so, we
Joe Perchesbea85192007-12-20 14:01:35 -0800582 * suppress the inclusion of the sllao.
Neil Horman95c385b2007-04-25 17:08:10 -0700583 */
584 if (send_sllao) {
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900585 struct inet6_ifaddr *ifp = ipv6_get_ifaddr(dev_net(dev), saddr,
Daniel Lezcano1cab3da2008-01-10 22:44:09 -0800586 dev, 1);
Neil Horman95c385b2007-04-25 17:08:10 -0700587 if (ifp) {
588 if (ifp->flags & IFA_F_OPTIMISTIC) {
YOSHIFUJI Hideakica043562007-02-28 23:13:20 +0900589 send_sllao = 0;
Neil Horman95c385b2007-04-25 17:08:10 -0700590 }
YOSHIFUJI Hideakica043562007-02-28 23:13:20 +0900591 in6_ifa_put(ifp);
Neil Horman95c385b2007-04-25 17:08:10 -0700592 } else {
593 send_sllao = 0;
594 }
595 }
596#endif
YOSHIFUJI Hideaki / 吉藤英明12fd84f2013-01-18 02:00:24 +0000597 __ndisc_send(dev, daddr, saddr, &icmp6h, NULL,
David L Stevens14878f72007-09-16 16:52:35 -0700598 send_sllao ? ND_OPT_SOURCE_LL_ADDR : 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700599}
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900600
Linus Torvalds1da177e2005-04-16 15:20:36 -0700601
602static void ndisc_error_report(struct neighbour *neigh, struct sk_buff *skb)
603{
604 /*
605 * "The sender MUST return an ICMP
606 * destination unreachable"
607 */
608 dst_link_failure(skb);
609 kfree_skb(skb);
610}
611
612/* Called with locked neigh: either read or both */
613
614static void ndisc_solicit(struct neighbour *neigh, struct sk_buff *skb)
615{
616 struct in6_addr *saddr = NULL;
617 struct in6_addr mcaddr;
618 struct net_device *dev = neigh->dev;
619 struct in6_addr *target = (struct in6_addr *)&neigh->primary_key;
620 int probes = atomic_read(&neigh->probes);
621
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900622 if (skb && ipv6_chk_addr(dev_net(dev), &ipv6_hdr(skb)->saddr, dev, 1))
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -0700623 saddr = &ipv6_hdr(skb)->saddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700624
625 if ((probes -= neigh->parms->ucast_probes) < 0) {
626 if (!(neigh->nud_state & NUD_VALID)) {
Joe Perches675418d2012-05-16 19:28:38 +0000627 ND_PRINTK(1, dbg,
628 "%s: trying to ucast probe in NUD_INVALID: %pI6\n",
629 __func__, target);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700630 }
631 ndisc_send_ns(dev, neigh, target, target, saddr);
632 } else if ((probes -= neigh->parms->app_probes) < 0) {
633#ifdef CONFIG_ARPD
634 neigh_app_ns(neigh);
635#endif
636 } else {
637 addrconf_addr_solict_mult(target, &mcaddr);
638 ndisc_send_ns(dev, NULL, target, &mcaddr, saddr);
639 }
640}
641
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900642static int pndisc_is_router(const void *pkey,
643 struct net_device *dev)
Pavel Emelyanovfa86d322008-03-24 14:48:59 -0700644{
645 struct pneigh_entry *n;
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900646 int ret = -1;
Pavel Emelyanovfa86d322008-03-24 14:48:59 -0700647
648 read_lock_bh(&nd_tbl.lock);
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900649 n = __pneigh_lookup(&nd_tbl, dev_net(dev), pkey, dev);
650 if (n)
651 ret = !!(n->flags & NTF_ROUTER);
Pavel Emelyanovfa86d322008-03-24 14:48:59 -0700652 read_unlock_bh(&nd_tbl.lock);
653
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900654 return ret;
Pavel Emelyanovfa86d322008-03-24 14:48:59 -0700655}
656
Linus Torvalds1da177e2005-04-16 15:20:36 -0700657static void ndisc_recv_ns(struct sk_buff *skb)
658{
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -0700659 struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb);
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000660 const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
661 const struct in6_addr *daddr = &ipv6_hdr(skb)->daddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700662 u8 *lladdr = NULL;
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -0700663 u32 ndoptlen = skb->tail - (skb->transport_header +
664 offsetof(struct nd_msg, opt));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700665 struct ndisc_options ndopts;
666 struct net_device *dev = skb->dev;
667 struct inet6_ifaddr *ifp;
668 struct inet6_dev *idev = NULL;
669 struct neighbour *neigh;
670 int dad = ipv6_addr_any(saddr);
Eric Dumazeta50feda2012-05-18 18:57:34 +0000671 bool inc;
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900672 int is_router = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700673
YOSHIFUJI Hideaki / 吉藤英明115b0aa2013-01-18 02:05:03 +0000674 if (skb->len < sizeof(struct nd_msg)) {
675 ND_PRINTK(2, warn, "NS: packet too short\n");
676 return;
677 }
678
Linus Torvalds1da177e2005-04-16 15:20:36 -0700679 if (ipv6_addr_is_multicast(&msg->target)) {
Joe Perches675418d2012-05-16 19:28:38 +0000680 ND_PRINTK(2, warn, "NS: multicast target address\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700681 return;
682 }
683
684 /*
685 * RFC2461 7.1.1:
686 * DAD has to be destined for solicited node multicast address.
687 */
YOSHIFUJI Hideaki / 吉藤英明ca97a642013-01-20 07:39:00 +0000688 if (dad && !ipv6_addr_is_solict_mult(daddr)) {
Joe Perches675418d2012-05-16 19:28:38 +0000689 ND_PRINTK(2, warn, "NS: bad DAD packet (wrong destination)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700690 return;
691 }
692
693 if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts)) {
Joe Perches675418d2012-05-16 19:28:38 +0000694 ND_PRINTK(2, warn, "NS: invalid ND options\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700695 return;
696 }
697
698 if (ndopts.nd_opts_src_lladdr) {
699 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr, dev);
700 if (!lladdr) {
Joe Perches675418d2012-05-16 19:28:38 +0000701 ND_PRINTK(2, warn,
702 "NS: invalid link-layer address length\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700703 return;
704 }
705
706 /* RFC2461 7.1.1:
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900707 * If the IP source address is the unspecified address,
708 * there MUST NOT be source link-layer address option
Linus Torvalds1da177e2005-04-16 15:20:36 -0700709 * in the message.
710 */
711 if (dad) {
Joe Perches675418d2012-05-16 19:28:38 +0000712 ND_PRINTK(2, warn,
713 "NS: bad DAD packet (link-layer address option)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700714 return;
715 }
716 }
717
718 inc = ipv6_addr_is_multicast(daddr);
719
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900720 ifp = ipv6_get_ifaddr(dev_net(dev), &msg->target, dev, 1);
Daniel Lezcanoa18bc692008-03-07 11:14:49 -0800721 if (ifp) {
Neil Horman95c385b2007-04-25 17:08:10 -0700722
723 if (ifp->flags & (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)) {
724 if (dad) {
Neil Horman95c385b2007-04-25 17:08:10 -0700725 /*
726 * We are colliding with another node
727 * who is doing DAD
728 * so fail our DAD process
729 */
730 addrconf_dad_failure(ifp);
Denis V. Lunev9e3be4b2007-09-11 11:04:49 +0200731 return;
Neil Horman95c385b2007-04-25 17:08:10 -0700732 } else {
733 /*
734 * This is not a dad solicitation.
735 * If we are an optimistic node,
736 * we should respond.
737 * Otherwise, we should ignore it.
738 */
739 if (!(ifp->flags & IFA_F_OPTIMISTIC))
740 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700741 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700742 }
743
744 idev = ifp->idev;
745 } else {
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700746 struct net *net = dev_net(dev);
747
Linus Torvalds1da177e2005-04-16 15:20:36 -0700748 idev = in6_dev_get(dev);
749 if (!idev) {
750 /* XXX: count this drop? */
751 return;
752 }
753
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700754 if (ipv6_chk_acast_addr(net, dev, &msg->target) ||
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900755 (idev->cnf.forwarding &&
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700756 (net->ipv6.devconf_all->proxy_ndp || idev->cnf.proxy_ndp) &&
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900757 (is_router = pndisc_is_router(&msg->target, dev)) >= 0)) {
Patrick McHardya61bbcf2005-08-14 17:24:31 -0700758 if (!(NEIGH_CB(skb)->flags & LOCALLY_ENQUEUED) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700759 skb->pkt_type != PACKET_HOST &&
760 inc != 0 &&
761 idev->nd_parms->proxy_delay != 0) {
762 /*
763 * for anycast or proxy,
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900764 * sender should delay its response
765 * by a random time between 0 and
Linus Torvalds1da177e2005-04-16 15:20:36 -0700766 * MAX_ANYCAST_DELAY_TIME seconds.
767 * (RFC2461) -- yoshfuji
768 */
769 struct sk_buff *n = skb_clone(skb, GFP_ATOMIC);
770 if (n)
771 pneigh_enqueue(&nd_tbl, idev->nd_parms, n);
772 goto out;
773 }
774 } else
775 goto out;
776 }
777
YOSHIFUJI Hideaki0736ffc2008-03-28 13:37:58 +0900778 if (is_router < 0)
YOSHIFUJI Hideaki / 吉藤英明fb568632013-01-20 07:39:18 +0000779 is_router = idev->cnf.forwarding;
Ville Nuorvala62dd9312006-09-22 14:43:19 -0700780
Linus Torvalds1da177e2005-04-16 15:20:36 -0700781 if (dad) {
YOSHIFUJI Hideakif3ee4012008-04-10 15:42:11 +0900782 ndisc_send_na(dev, NULL, &in6addr_linklocal_allnodes, &msg->target,
YOSHIFUJI Hideaki / 吉藤英明fb568632013-01-20 07:39:18 +0000783 !!is_router, false, (ifp != NULL), true);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700784 goto out;
785 }
786
787 if (inc)
788 NEIGH_CACHE_STAT_INC(&nd_tbl, rcv_probes_mcast);
789 else
790 NEIGH_CACHE_STAT_INC(&nd_tbl, rcv_probes_ucast);
791
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900792 /*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700793 * update / create cache entry
794 * for the source address
795 */
796 neigh = __neigh_lookup(&nd_tbl, saddr, dev,
797 !inc || lladdr || !dev->addr_len);
798 if (neigh)
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900799 neigh_update(neigh, lladdr, NUD_STALE,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700800 NEIGH_UPDATE_F_WEAK_OVERRIDE|
801 NEIGH_UPDATE_F_OVERRIDE);
Stephen Hemminger3b04ddd2007-10-09 01:40:57 -0700802 if (neigh || !dev->header_ops) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700803 ndisc_send_na(dev, neigh, saddr, &msg->target,
YOSHIFUJI Hideaki / 吉藤英明fb568632013-01-20 07:39:18 +0000804 !!is_router,
805 true, (ifp != NULL && inc), inc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700806 if (neigh)
807 neigh_release(neigh);
808 }
809
810out:
811 if (ifp)
812 in6_ifa_put(ifp);
813 else
814 in6_dev_put(idev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700815}
816
817static void ndisc_recv_na(struct sk_buff *skb)
818{
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -0700819 struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb);
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000820 const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
821 const struct in6_addr *daddr = &ipv6_hdr(skb)->daddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700822 u8 *lladdr = NULL;
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -0700823 u32 ndoptlen = skb->tail - (skb->transport_header +
824 offsetof(struct nd_msg, opt));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700825 struct ndisc_options ndopts;
826 struct net_device *dev = skb->dev;
827 struct inet6_ifaddr *ifp;
828 struct neighbour *neigh;
829
830 if (skb->len < sizeof(struct nd_msg)) {
Joe Perches675418d2012-05-16 19:28:38 +0000831 ND_PRINTK(2, warn, "NA: packet too short\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700832 return;
833 }
834
835 if (ipv6_addr_is_multicast(&msg->target)) {
Joe Perches675418d2012-05-16 19:28:38 +0000836 ND_PRINTK(2, warn, "NA: target address is multicast\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700837 return;
838 }
839
840 if (ipv6_addr_is_multicast(daddr) &&
841 msg->icmph.icmp6_solicited) {
Joe Perches675418d2012-05-16 19:28:38 +0000842 ND_PRINTK(2, warn, "NA: solicited NA is multicasted\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700843 return;
844 }
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +0900845
Linus Torvalds1da177e2005-04-16 15:20:36 -0700846 if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts)) {
Joe Perches675418d2012-05-16 19:28:38 +0000847 ND_PRINTK(2, warn, "NS: invalid ND option\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700848 return;
849 }
850 if (ndopts.nd_opts_tgt_lladdr) {
851 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr, dev);
852 if (!lladdr) {
Joe Perches675418d2012-05-16 19:28:38 +0000853 ND_PRINTK(2, warn,
854 "NA: invalid link-layer address length\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700855 return;
856 }
857 }
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900858 ifp = ipv6_get_ifaddr(dev_net(dev), &msg->target, dev, 1);
Daniel Lezcanoa18bc692008-03-07 11:14:49 -0800859 if (ifp) {
Daniel Walterbd015922011-04-13 21:09:25 +0000860 if (skb->pkt_type != PACKET_LOOPBACK
861 && (ifp->flags & IFA_F_TENTATIVE)) {
862 addrconf_dad_failure(ifp);
863 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700864 }
865 /* What should we make now? The advertisement
866 is invalid, but ndisc specs say nothing
867 about it. It could be misconfiguration, or
868 an smart proxy agent tries to help us :-)
Jan Sembera24fc7b82008-12-09 15:48:32 -0800869
870 We should not print the error if NA has been
871 received from loopback - it is just our own
872 unsolicited advertisement.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700873 */
Jan Sembera24fc7b82008-12-09 15:48:32 -0800874 if (skb->pkt_type != PACKET_LOOPBACK)
Joe Perches675418d2012-05-16 19:28:38 +0000875 ND_PRINTK(1, warn,
876 "NA: someone advertises our address %pI6 on %s!\n",
877 &ifp->addr, ifp->idev->dev->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700878 in6_ifa_put(ifp);
879 return;
880 }
881 neigh = neigh_lookup(&nd_tbl, &msg->target, dev);
882
883 if (neigh) {
884 u8 old_flags = neigh->flags;
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700885 struct net *net = dev_net(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700886
887 if (neigh->nud_state & NUD_FAILED)
888 goto out;
889
Ville Nuorvala5f3e6e92006-09-22 14:42:46 -0700890 /*
891 * Don't update the neighbor cache entry on a proxy NA from
892 * ourselves because either the proxied node is off link or it
893 * has already sent a NA to us.
894 */
895 if (lladdr && !memcmp(lladdr, dev->dev_addr, dev->addr_len) &&
YOSHIFUJI Hideaki53b79972008-07-19 22:35:03 -0700896 net->ipv6.devconf_all->forwarding && net->ipv6.devconf_all->proxy_ndp &&
897 pneigh_lookup(&nd_tbl, net, &msg->target, dev, 0)) {
Nicolas Dichtelb20b6d92012-11-07 05:05:38 +0000898 /* XXX: idev->cnf.proxy_ndp */
Ville Nuorvala5f3e6e92006-09-22 14:42:46 -0700899 goto out;
YOSHIFUJI Hideakifbea49e2006-09-22 14:43:49 -0700900 }
Ville Nuorvala5f3e6e92006-09-22 14:42:46 -0700901
Linus Torvalds1da177e2005-04-16 15:20:36 -0700902 neigh_update(neigh, lladdr,
903 msg->icmph.icmp6_solicited ? NUD_REACHABLE : NUD_STALE,
904 NEIGH_UPDATE_F_WEAK_OVERRIDE|
905 (msg->icmph.icmp6_override ? NEIGH_UPDATE_F_OVERRIDE : 0)|
906 NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
907 (msg->icmph.icmp6_router ? NEIGH_UPDATE_F_ISROUTER : 0));
908
909 if ((old_flags & ~neigh->flags) & NTF_ROUTER) {
910 /*
911 * Change: router to host
912 */
913 struct rt6_info *rt;
914 rt = rt6_get_dflt_router(saddr, dev);
915 if (rt)
Thomas Grafe0a1ad732006-08-22 00:00:21 -0700916 ip6_del_rt(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700917 }
918
919out:
920 neigh_release(neigh);
921 }
922}
923
924static void ndisc_recv_rs(struct sk_buff *skb)
925{
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -0700926 struct rs_msg *rs_msg = (struct rs_msg *)skb_transport_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700927 unsigned long ndoptlen = skb->len - sizeof(*rs_msg);
928 struct neighbour *neigh;
929 struct inet6_dev *idev;
Eric Dumazetb71d1d42011-04-22 04:53:02 +0000930 const struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700931 struct ndisc_options ndopts;
932 u8 *lladdr = NULL;
933
934 if (skb->len < sizeof(*rs_msg))
935 return;
936
Eric Dumazetcfdf7642011-07-27 21:13:03 +0000937 idev = __in6_dev_get(skb->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700938 if (!idev) {
Joe Perches675418d2012-05-16 19:28:38 +0000939 ND_PRINTK(1, err, "RS: can't find in6 device\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700940 return;
941 }
942
943 /* Don't accept RS if we're not in router mode */
944 if (!idev->cnf.forwarding)
945 goto out;
946
947 /*
948 * Don't update NCE if src = ::;
949 * this implies that the source node has no ip address assigned yet.
950 */
951 if (ipv6_addr_any(saddr))
952 goto out;
953
954 /* Parse ND options */
955 if (!ndisc_parse_options(rs_msg->opt, ndoptlen, &ndopts)) {
Joe Perches675418d2012-05-16 19:28:38 +0000956 ND_PRINTK(2, notice, "NS: invalid ND option, ignored\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700957 goto out;
958 }
959
960 if (ndopts.nd_opts_src_lladdr) {
961 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr,
962 skb->dev);
963 if (!lladdr)
964 goto out;
965 }
966
967 neigh = __neigh_lookup(&nd_tbl, saddr, skb->dev, 1);
968 if (neigh) {
969 neigh_update(neigh, lladdr, NUD_STALE,
970 NEIGH_UPDATE_F_WEAK_OVERRIDE|
971 NEIGH_UPDATE_F_OVERRIDE|
972 NEIGH_UPDATE_F_OVERRIDE_ISROUTER);
973 neigh_release(neigh);
974 }
975out:
Eric Dumazetcfdf7642011-07-27 21:13:03 +0000976 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700977}
978
Pierre Ynard31910572007-10-10 21:22:05 -0700979static void ndisc_ra_useropt(struct sk_buff *ra, struct nd_opt_hdr *opt)
980{
981 struct icmp6hdr *icmp6h = (struct icmp6hdr *)skb_transport_header(ra);
982 struct sk_buff *skb;
983 struct nlmsghdr *nlh;
984 struct nduseroptmsg *ndmsg;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900985 struct net *net = dev_net(ra->dev);
Pierre Ynard31910572007-10-10 21:22:05 -0700986 int err;
987 int base_size = NLMSG_ALIGN(sizeof(struct nduseroptmsg)
988 + (opt->nd_opt_len << 3));
989 size_t msg_size = base_size + nla_total_size(sizeof(struct in6_addr));
990
991 skb = nlmsg_new(msg_size, GFP_ATOMIC);
992 if (skb == NULL) {
993 err = -ENOBUFS;
994 goto errout;
995 }
996
997 nlh = nlmsg_put(skb, 0, 0, RTM_NEWNDUSEROPT, base_size, 0);
998 if (nlh == NULL) {
999 goto nla_put_failure;
1000 }
1001
1002 ndmsg = nlmsg_data(nlh);
1003 ndmsg->nduseropt_family = AF_INET6;
Pierre Ynarddbb2ed22007-11-12 17:58:35 -08001004 ndmsg->nduseropt_ifindex = ra->dev->ifindex;
Pierre Ynard31910572007-10-10 21:22:05 -07001005 ndmsg->nduseropt_icmp_type = icmp6h->icmp6_type;
1006 ndmsg->nduseropt_icmp_code = icmp6h->icmp6_code;
1007 ndmsg->nduseropt_opts_len = opt->nd_opt_len << 3;
1008
1009 memcpy(ndmsg + 1, opt, opt->nd_opt_len << 3);
1010
David S. Millerc78679e2012-04-01 20:27:33 -04001011 if (nla_put(skb, NDUSEROPT_SRCADDR, sizeof(struct in6_addr),
1012 &ipv6_hdr(ra)->saddr))
1013 goto nla_put_failure;
Pierre Ynard31910572007-10-10 21:22:05 -07001014 nlmsg_end(skb, nlh);
1015
Pablo Neira Ayuso1ce85fe2009-02-24 23:18:28 -08001016 rtnl_notify(skb, net, 0, RTNLGRP_ND_USEROPT, NULL, GFP_ATOMIC);
Pierre Ynard31910572007-10-10 21:22:05 -07001017 return;
1018
1019nla_put_failure:
1020 nlmsg_free(skb);
1021 err = -EMSGSIZE;
1022errout:
Daniel Lezcanoa18bc692008-03-07 11:14:49 -08001023 rtnl_set_sk_err(net, RTNLGRP_ND_USEROPT, err);
Pierre Ynard31910572007-10-10 21:22:05 -07001024}
1025
Linus Torvalds1da177e2005-04-16 15:20:36 -07001026static void ndisc_router_discovery(struct sk_buff *skb)
1027{
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -07001028 struct ra_msg *ra_msg = (struct ra_msg *)skb_transport_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001029 struct neighbour *neigh = NULL;
1030 struct inet6_dev *in6_dev;
YOSHIFUJI Hideaki65f5c7c2006-03-20 16:55:08 -08001031 struct rt6_info *rt = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001032 int lifetime;
1033 struct ndisc_options ndopts;
1034 int optlen;
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001035 unsigned int pref = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001036
1037 __u8 * opt = (__u8 *)(ra_msg + 1);
1038
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -07001039 optlen = (skb->tail - skb->transport_header) - sizeof(struct ra_msg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001040
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001041 if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) {
Joe Perches675418d2012-05-16 19:28:38 +00001042 ND_PRINTK(2, warn, "RA: source address is not link-local\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001043 return;
1044 }
1045 if (optlen < 0) {
Joe Perches675418d2012-05-16 19:28:38 +00001046 ND_PRINTK(2, warn, "RA: packet too short\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001047 return;
1048 }
1049
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001050#ifdef CONFIG_IPV6_NDISC_NODETYPE
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001051 if (skb->ndisc_nodetype == NDISC_NODETYPE_HOST) {
Joe Perches675418d2012-05-16 19:28:38 +00001052 ND_PRINTK(2, warn, "RA: from host or unauthorized router\n");
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001053 return;
1054 }
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001055#endif
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001056
Linus Torvalds1da177e2005-04-16 15:20:36 -07001057 /*
1058 * set the RA_RECV flag in the interface
1059 */
1060
Eric Dumazetcfdf7642011-07-27 21:13:03 +00001061 in6_dev = __in6_dev_get(skb->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001062 if (in6_dev == NULL) {
Joe Perches675418d2012-05-16 19:28:38 +00001063 ND_PRINTK(0, err, "RA: can't find inet6 device for %s\n",
1064 skb->dev->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001065 return;
1066 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001067
1068 if (!ndisc_parse_options(opt, optlen, &ndopts)) {
Joe Perches675418d2012-05-16 19:28:38 +00001069 ND_PRINTK(2, warn, "RA: invalid ND options\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001070 return;
1071 }
1072
Shmulik Ladkaniaeaf6e92012-11-30 10:25:59 +00001073 if (!ipv6_accept_ra(in6_dev))
David Ward31ce8c72009-08-29 00:04:09 -07001074 goto skip_linkparms;
1075
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001076#ifdef CONFIG_IPV6_NDISC_NODETYPE
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001077 /* skip link-specific parameters from interior routers */
1078 if (skb->ndisc_nodetype == NDISC_NODETYPE_NODEFAULT)
1079 goto skip_linkparms;
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001080#endif
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001081
Linus Torvalds1da177e2005-04-16 15:20:36 -07001082 if (in6_dev->if_flags & IF_RS_SENT) {
1083 /*
1084 * flag that an RA was received after an RS was sent
1085 * out on this interface.
1086 */
1087 in6_dev->if_flags |= IF_RA_RCVD;
1088 }
1089
1090 /*
1091 * Remember the managed/otherconf flags from most recently
1092 * received RA message (RFC 2462) -- yoshfuji
1093 */
1094 in6_dev->if_flags = (in6_dev->if_flags & ~(IF_RA_MANAGED |
1095 IF_RA_OTHERCONF)) |
1096 (ra_msg->icmph.icmp6_addrconf_managed ?
1097 IF_RA_MANAGED : 0) |
1098 (ra_msg->icmph.icmp6_addrconf_other ?
1099 IF_RA_OTHERCONF : 0);
1100
YOSHIFUJI Hideaki65f5c7c2006-03-20 16:55:08 -08001101 if (!in6_dev->cnf.accept_ra_defrtr)
1102 goto skip_defrtr;
1103
Andreas Hofmeister9f562202011-10-24 19:13:15 -04001104 if (ipv6_chk_addr(dev_net(in6_dev->dev), &ipv6_hdr(skb)->saddr, NULL, 0))
1105 goto skip_defrtr;
1106
Linus Torvalds1da177e2005-04-16 15:20:36 -07001107 lifetime = ntohs(ra_msg->icmph.icmp6_rt_lifetime);
1108
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001109#ifdef CONFIG_IPV6_ROUTER_PREF
1110 pref = ra_msg->icmph.icmp6_router_pref;
1111 /* 10b is handled as if it were 00b (medium) */
YOSHIFUJI Hideaki930d6ff2006-03-20 17:05:30 -08001112 if (pref == ICMPV6_ROUTER_PREF_INVALID ||
YOSHIFUJI Hideaki6d5b78c2007-06-22 16:07:04 -07001113 !in6_dev->cnf.accept_ra_rtr_pref)
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001114 pref = ICMPV6_ROUTER_PREF_MEDIUM;
1115#endif
1116
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001117 rt = rt6_get_dflt_router(&ipv6_hdr(skb)->saddr, skb->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001118
David S. Millereb857182012-01-27 15:07:56 -08001119 if (rt) {
1120 neigh = dst_neigh_lookup(&rt->dst, &ipv6_hdr(skb)->saddr);
1121 if (!neigh) {
Joe Perches675418d2012-05-16 19:28:38 +00001122 ND_PRINTK(0, err,
1123 "RA: %s got default router without neighbour\n",
1124 __func__);
Amerigo Wang94e187c2012-10-29 00:13:19 +00001125 ip6_rt_put(rt);
David S. Millereb857182012-01-27 15:07:56 -08001126 return;
1127 }
1128 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001129 if (rt && lifetime == 0) {
Thomas Grafe0a1ad732006-08-22 00:00:21 -07001130 ip6_del_rt(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001131 rt = NULL;
1132 }
1133
1134 if (rt == NULL && lifetime) {
Joe Perches675418d2012-05-16 19:28:38 +00001135 ND_PRINTK(3, dbg, "RA: adding default router\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001136
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001137 rt = rt6_add_dflt_router(&ipv6_hdr(skb)->saddr, skb->dev, pref);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001138 if (rt == NULL) {
Joe Perches675418d2012-05-16 19:28:38 +00001139 ND_PRINTK(0, err,
1140 "RA: %s failed to add default route\n",
1141 __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001142 return;
1143 }
1144
David S. Millereb857182012-01-27 15:07:56 -08001145 neigh = dst_neigh_lookup(&rt->dst, &ipv6_hdr(skb)->saddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001146 if (neigh == NULL) {
Joe Perches675418d2012-05-16 19:28:38 +00001147 ND_PRINTK(0, err,
1148 "RA: %s got default router without neighbour\n",
1149 __func__);
Amerigo Wang94e187c2012-10-29 00:13:19 +00001150 ip6_rt_put(rt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001151 return;
1152 }
1153 neigh->flags |= NTF_ROUTER;
YOSHIFUJI Hideakiebacaaa2006-03-20 17:04:53 -08001154 } else if (rt) {
Pedro Ribeiro22441cf2008-10-15 15:47:49 -07001155 rt->rt6i_flags = (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001156 }
1157
1158 if (rt)
Gao feng1716a962012-04-06 00:13:10 +00001159 rt6_set_expires(rt, jiffies + (HZ * lifetime));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001160 if (ra_msg->icmph.icmp6_hop_limit) {
1161 in6_dev->cnf.hop_limit = ra_msg->icmph.icmp6_hop_limit;
1162 if (rt)
David S. Millerdefb3512010-12-08 21:16:57 -08001163 dst_metric_set(&rt->dst, RTAX_HOPLIMIT,
1164 ra_msg->icmph.icmp6_hop_limit);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001165 }
1166
YOSHIFUJI Hideaki65f5c7c2006-03-20 16:55:08 -08001167skip_defrtr:
1168
Linus Torvalds1da177e2005-04-16 15:20:36 -07001169 /*
1170 * Update Reachable Time and Retrans Timer
1171 */
1172
1173 if (in6_dev->nd_parms) {
1174 unsigned long rtime = ntohl(ra_msg->retrans_timer);
1175
1176 if (rtime && rtime/1000 < MAX_SCHEDULE_TIMEOUT/HZ) {
1177 rtime = (rtime*HZ)/1000;
1178 if (rtime < HZ/10)
1179 rtime = HZ/10;
1180 in6_dev->nd_parms->retrans_time = rtime;
1181 in6_dev->tstamp = jiffies;
1182 inet6_ifinfo_notify(RTM_NEWLINK, in6_dev);
1183 }
1184
1185 rtime = ntohl(ra_msg->reachable_time);
1186 if (rtime && rtime/1000 < MAX_SCHEDULE_TIMEOUT/(3*HZ)) {
1187 rtime = (rtime*HZ)/1000;
1188
1189 if (rtime < HZ/10)
1190 rtime = HZ/10;
1191
1192 if (rtime != in6_dev->nd_parms->base_reachable_time) {
1193 in6_dev->nd_parms->base_reachable_time = rtime;
1194 in6_dev->nd_parms->gc_staletime = 3 * rtime;
1195 in6_dev->nd_parms->reachable_time = neigh_rand_reach_time(rtime);
1196 in6_dev->tstamp = jiffies;
1197 inet6_ifinfo_notify(RTM_NEWLINK, in6_dev);
1198 }
1199 }
1200 }
1201
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001202skip_linkparms:
1203
Linus Torvalds1da177e2005-04-16 15:20:36 -07001204 /*
1205 * Process options.
1206 */
1207
1208 if (!neigh)
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001209 neigh = __neigh_lookup(&nd_tbl, &ipv6_hdr(skb)->saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001210 skb->dev, 1);
1211 if (neigh) {
1212 u8 *lladdr = NULL;
1213 if (ndopts.nd_opts_src_lladdr) {
1214 lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr,
1215 skb->dev);
1216 if (!lladdr) {
Joe Perches675418d2012-05-16 19:28:38 +00001217 ND_PRINTK(2, warn,
1218 "RA: invalid link-layer address length\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001219 goto out;
1220 }
1221 }
1222 neigh_update(neigh, lladdr, NUD_STALE,
1223 NEIGH_UPDATE_F_WEAK_OVERRIDE|
1224 NEIGH_UPDATE_F_OVERRIDE|
1225 NEIGH_UPDATE_F_OVERRIDE_ISROUTER|
1226 NEIGH_UPDATE_F_ISROUTER);
1227 }
1228
Shmulik Ladkaniaeaf6e92012-11-30 10:25:59 +00001229 if (!ipv6_accept_ra(in6_dev))
David Ward31ce8c72009-08-29 00:04:09 -07001230 goto out;
1231
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001232#ifdef CONFIG_IPV6_ROUTE_INFO
Andreas Hofmeister9f562202011-10-24 19:13:15 -04001233 if (ipv6_chk_addr(dev_net(in6_dev->dev), &ipv6_hdr(skb)->saddr, NULL, 0))
1234 goto skip_routeinfo;
1235
YOSHIFUJI Hideaki09c884d2006-03-20 17:07:03 -08001236 if (in6_dev->cnf.accept_ra_rtr_pref && ndopts.nd_opts_ri) {
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001237 struct nd_opt_hdr *p;
1238 for (p = ndopts.nd_opts_ri;
1239 p;
1240 p = ndisc_next_option(p, ndopts.nd_opts_ri_end)) {
YOSHIFUJI Hideaki6294e002008-03-15 23:56:52 -04001241 struct route_info *ri = (struct route_info *)p;
1242#ifdef CONFIG_IPV6_NDISC_NODETYPE
1243 if (skb->ndisc_nodetype == NDISC_NODETYPE_NODEFAULT &&
1244 ri->prefix_len == 0)
1245 continue;
1246#endif
1247 if (ri->prefix_len > in6_dev->cnf.accept_ra_rt_info_max_plen)
YOSHIFUJI Hideaki09c884d2006-03-20 17:07:03 -08001248 continue;
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001249 rt6_route_rcv(skb->dev, (u8*)p, (p->nd_opt_len) << 3,
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001250 &ipv6_hdr(skb)->saddr);
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001251 }
1252 }
Andreas Hofmeister9f562202011-10-24 19:13:15 -04001253
1254skip_routeinfo:
YOSHIFUJI Hideaki70ceb4f2006-03-20 17:06:24 -08001255#endif
1256
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001257#ifdef CONFIG_IPV6_NDISC_NODETYPE
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001258 /* skip link-specific ndopts from interior routers */
1259 if (skb->ndisc_nodetype == NDISC_NODETYPE_NODEFAULT)
1260 goto out;
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001261#endif
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001262
YOSHIFUJI Hideakic4fd30e2006-03-20 16:55:26 -08001263 if (in6_dev->cnf.accept_ra_pinfo && ndopts.nd_opts_pi) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001264 struct nd_opt_hdr *p;
1265 for (p = ndopts.nd_opts_pi;
1266 p;
1267 p = ndisc_next_option(p, ndopts.nd_opts_pi_end)) {
Neil Hormane6bff992012-01-04 10:49:15 +00001268 addrconf_prefix_rcv(skb->dev, (u8 *)p,
1269 (p->nd_opt_len) << 3,
1270 ndopts.nd_opts_src_lladdr != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001271 }
1272 }
1273
1274 if (ndopts.nd_opts_mtu) {
Al Viroe69a4ad2006-11-14 20:56:00 -08001275 __be32 n;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001276 u32 mtu;
1277
Al Viroe69a4ad2006-11-14 20:56:00 -08001278 memcpy(&n, ((u8*)(ndopts.nd_opts_mtu+1))+2, sizeof(mtu));
1279 mtu = ntohl(n);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001280
1281 if (mtu < IPV6_MIN_MTU || mtu > skb->dev->mtu) {
Joe Perches675418d2012-05-16 19:28:38 +00001282 ND_PRINTK(2, warn, "RA: invalid mtu: %d\n", mtu);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001283 } else if (in6_dev->cnf.mtu6 != mtu) {
1284 in6_dev->cnf.mtu6 = mtu;
1285
1286 if (rt)
David S. Millerdefb3512010-12-08 21:16:57 -08001287 dst_metric_set(&rt->dst, RTAX_MTU, mtu);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001288
1289 rt6_mtu_change(skb->dev, mtu);
1290 }
1291 }
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001292
Pierre Ynard31910572007-10-10 21:22:05 -07001293 if (ndopts.nd_useropts) {
YOSHIFUJI Hideaki61cf46a2008-01-22 17:32:53 +09001294 struct nd_opt_hdr *p;
1295 for (p = ndopts.nd_useropts;
1296 p;
1297 p = ndisc_next_useropt(p, ndopts.nd_useropts_end)) {
1298 ndisc_ra_useropt(skb, p);
Pierre Ynard31910572007-10-10 21:22:05 -07001299 }
1300 }
1301
Linus Torvalds1da177e2005-04-16 15:20:36 -07001302 if (ndopts.nd_opts_tgt_lladdr || ndopts.nd_opts_rh) {
Joe Perches675418d2012-05-16 19:28:38 +00001303 ND_PRINTK(2, warn, "RA: invalid RA options\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001304 }
1305out:
Amerigo Wang94e187c2012-10-29 00:13:19 +00001306 ip6_rt_put(rt);
David S. Millereb857182012-01-27 15:07:56 -08001307 if (neigh)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001308 neigh_release(neigh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001309}
1310
1311static void ndisc_redirect_rcv(struct sk_buff *skb)
1312{
Duan Jiong093d04d42012-12-14 02:59:59 +00001313 u8 *hdr;
1314 struct ndisc_options ndopts;
1315 struct rd_msg *msg = (struct rd_msg *)skb_transport_header(skb);
1316 u32 ndoptlen = skb->tail - (skb->transport_header +
1317 offsetof(struct rd_msg, opt));
1318
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001319#ifdef CONFIG_IPV6_NDISC_NODETYPE
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001320 switch (skb->ndisc_nodetype) {
1321 case NDISC_NODETYPE_HOST:
1322 case NDISC_NODETYPE_NODEFAULT:
Joe Perches675418d2012-05-16 19:28:38 +00001323 ND_PRINTK(2, warn,
1324 "Redirect: from host or unauthorized router\n");
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001325 return;
1326 }
YOSHIFUJI Hideakide357cc2008-03-15 23:59:18 -04001327#endif
Templin, Fred Lfadf6bf2008-03-11 18:35:59 -04001328
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001329 if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) {
Joe Perches675418d2012-05-16 19:28:38 +00001330 ND_PRINTK(2, warn,
1331 "Redirect: source address is not link-local\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001332 return;
1333 }
1334
Duan Jiong093d04d42012-12-14 02:59:59 +00001335 if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts))
1336 return;
1337
1338 if (!ndopts.nd_opts_rh)
1339 return;
1340
1341 hdr = (u8 *)ndopts.nd_opts_rh;
1342 hdr += 8;
1343 if (!pskb_pull(skb, hdr - skb_transport_header(skb)))
1344 return;
1345
David S. Millerb94f1c02012-07-12 00:33:37 -07001346 icmpv6_notify(skb, NDISC_REDIRECT, 0, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001347}
1348
David S. Miller49919692012-01-27 15:30:48 -08001349void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001350{
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001351 struct net_device *dev = skb->dev;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001352 struct net *net = dev_net(dev);
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001353 struct sock *sk = net->ipv6.ndisc_sk;
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00001354 int len = sizeof(struct rd_msg);
David S. Millerfbfe95a2012-06-08 23:24:18 -07001355 struct inet_peer *peer;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001356 struct sk_buff *buff;
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00001357 struct rd_msg *msg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001358 struct in6_addr saddr_buf;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001359 struct rt6_info *rt;
1360 struct dst_entry *dst;
1361 struct inet6_dev *idev;
David S. Miller4c9483b2011-03-12 16:22:43 -05001362 struct flowi6 fl6;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001363 u8 *opt;
Herbert Xua7ae1992011-11-18 02:20:04 +00001364 int hlen, tlen;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001365 int rd_len;
1366 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001367 u8 ha_buf[MAX_ADDR_LEN], *ha = NULL;
David S. Miller1d861aa2012-07-10 03:58:16 -07001368 bool ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001369
Neil Horman95c385b2007-04-25 17:08:10 -07001370 if (ipv6_get_lladdr(dev, &saddr_buf, IFA_F_TENTATIVE)) {
Joe Perches675418d2012-05-16 19:28:38 +00001371 ND_PRINTK(2, warn, "Redirect: no link-local address on %s\n",
1372 dev->name);
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001373 return;
1374 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001375
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001376 if (!ipv6_addr_equal(&ipv6_hdr(skb)->daddr, target) &&
Brian Haleybf0b48d2007-10-08 00:12:05 -07001377 ipv6_addr_type(target) != (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) {
Joe Perches675418d2012-05-16 19:28:38 +00001378 ND_PRINTK(2, warn,
1379 "Redirect: target address is not link-local unicast\n");
Li Yewang29556522007-01-30 14:33:20 -08001380 return;
1381 }
1382
David S. Miller4c9483b2011-03-12 16:22:43 -05001383 icmpv6_flow_init(sk, &fl6, NDISC_REDIRECT,
YOSHIFUJI Hideaki95e41e92007-12-06 15:43:30 -08001384 &saddr_buf, &ipv6_hdr(skb)->saddr, dev->ifindex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001385
David S. Miller4c9483b2011-03-12 16:22:43 -05001386 dst = ip6_route_output(net, NULL, &fl6);
RongQing.Li5095d642012-02-21 22:10:49 +00001387 if (dst->error) {
1388 dst_release(dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001389 return;
RongQing.Li5095d642012-02-21 22:10:49 +00001390 }
David S. Miller4c9483b2011-03-12 16:22:43 -05001391 dst = xfrm_lookup(net, dst, flowi6_to_flowi(&fl6), NULL, 0);
David S. Miller452edd52011-03-02 13:27:41 -08001392 if (IS_ERR(dst))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001393 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001394
1395 rt = (struct rt6_info *) dst;
1396
1397 if (rt->rt6i_flags & RTF_GATEWAY) {
Joe Perches675418d2012-05-16 19:28:38 +00001398 ND_PRINTK(2, warn,
1399 "Redirect: destination is not a neighbour\n");
Ilpo Järvinend73f0802009-02-06 23:47:37 -08001400 goto release;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001401 }
David S. Miller1d861aa2012-07-10 03:58:16 -07001402 peer = inet_getpeer_v6(net->ipv6.peers, &rt->rt6i_dst.addr, 1);
1403 ret = inet_peer_xrlim_allow(peer, 1*HZ);
1404 if (peer)
1405 inet_putpeer(peer);
1406 if (!ret)
Ilpo Järvinend73f0802009-02-06 23:47:37 -08001407 goto release;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001408
1409 if (dev->addr_len) {
David S. Miller49919692012-01-27 15:30:48 -08001410 struct neighbour *neigh = dst_neigh_lookup(skb_dst(skb), target);
1411 if (!neigh) {
Joe Perches675418d2012-05-16 19:28:38 +00001412 ND_PRINTK(2, warn,
1413 "Redirect: no neigh for target address\n");
David S. Miller49919692012-01-27 15:30:48 -08001414 goto release;
1415 }
1416
Linus Torvalds1da177e2005-04-16 15:20:36 -07001417 read_lock_bh(&neigh->lock);
1418 if (neigh->nud_state & NUD_VALID) {
1419 memcpy(ha_buf, neigh->ha, dev->addr_len);
1420 read_unlock_bh(&neigh->lock);
1421 ha = ha_buf;
1422 len += ndisc_opt_addr_space(dev);
1423 } else
1424 read_unlock_bh(&neigh->lock);
David S. Miller49919692012-01-27 15:30:48 -08001425
1426 neigh_release(neigh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001427 }
1428
1429 rd_len = min_t(unsigned int,
1430 IPV6_MIN_MTU-sizeof(struct ipv6hdr)-len, skb->len + 8);
1431 rd_len &= ~0x7;
1432 len += rd_len;
1433
Herbert Xua7ae1992011-11-18 02:20:04 +00001434 hlen = LL_RESERVED_SPACE(dev);
1435 tlen = dev->needed_tailroom;
David S. Millerd54a81d2006-12-02 21:00:06 -08001436 buff = sock_alloc_send_skb(sk,
YOSHIFUJI Hideaki / 吉藤英明b7dc8c32013-01-04 03:58:04 +00001437 (sizeof(struct ipv6hdr) +
Herbert Xua7ae1992011-11-18 02:20:04 +00001438 len + hlen + tlen),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001439 1, &err);
1440 if (buff == NULL) {
Joe Perches675418d2012-05-16 19:28:38 +00001441 ND_PRINTK(0, err,
1442 "Redirect: %s failed to allocate an skb, err=%d\n",
1443 __func__, err);
Ilpo Järvinend73f0802009-02-06 23:47:37 -08001444 goto release;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001445 }
1446
Herbert Xua7ae1992011-11-18 02:20:04 +00001447 skb_reserve(buff, hlen);
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001448 ip6_nd_hdr(sk, buff, dev, &saddr_buf, &ipv6_hdr(skb)->saddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001449 IPPROTO_ICMPV6, len);
1450
Arnaldo Carvalho de Melo27a884d2007-04-19 20:29:13 -07001451 skb_set_transport_header(buff, skb_tail_pointer(buff) - buff->data);
Arnaldo Carvalho de Melod10ba342007-03-14 21:05:37 -03001452 skb_put(buff, len);
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00001453 msg = (struct rd_msg *)icmp6_hdr(buff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001454
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00001455 memset(&msg->icmph, 0, sizeof(struct icmp6hdr));
1456 msg->icmph.icmp6_type = NDISC_REDIRECT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001457
1458 /*
1459 * copy target and destination addresses
1460 */
1461
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00001462 msg->target = *target;
1463 msg->dest = ipv6_hdr(skb)->daddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001464
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00001465 opt = msg->opt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001466
1467 /*
1468 * include target_address option
1469 */
1470
1471 if (ha)
YOSHIFUJI Hideaki / 吉藤英明315ff092013-01-21 06:47:50 +00001472 opt = ndisc_fill_addr_option(opt, ND_OPT_TARGET_LL_ADDR, ha, dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001473
1474 /*
1475 * build redirect option and copy skb over to the new packet.
1476 */
1477
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001478 memset(opt, 0, 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001479 *(opt++) = ND_OPT_REDIRECT_HDR;
1480 *(opt++) = (rd_len >> 3);
1481 opt += 6;
1482
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001483 memcpy(opt, ipv6_hdr(skb), rd_len - 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001484
YOSHIFUJI Hideaki / 吉藤英明71bcdba2013-01-05 16:34:51 +00001485 msg->icmph.icmp6_cksum = csum_ipv6_magic(&saddr_buf, &ipv6_hdr(skb)->saddr,
1486 len, IPPROTO_ICMPV6,
1487 csum_partial(msg, len, 0));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001488
Eric Dumazetadf30902009-06-02 05:19:30 +00001489 skb_dst_set(buff, dst);
Eric Dumazetcfdf7642011-07-27 21:13:03 +00001490 rcu_read_lock();
1491 idev = __in6_dev_get(dst->dev);
Neil Hormanedf391f2009-04-27 02:45:02 -07001492 IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUT, skb->len);
Jan Engelhardtb2e0b382010-03-23 04:09:07 +01001493 err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, buff, NULL, dst->dev,
Patrick McHardy6e23ae22007-11-19 18:53:30 -08001494 dst_output);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001495 if (!err) {
Denis V. Lunev5c5d2442008-10-08 10:33:50 -07001496 ICMP6MSGOUT_INC_STATS(net, idev, NDISC_REDIRECT);
Denis V. Luneva862f6a2008-10-08 10:33:06 -07001497 ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001498 }
1499
Eric Dumazetcfdf7642011-07-27 21:13:03 +00001500 rcu_read_unlock();
Ilpo Järvinend73f0802009-02-06 23:47:37 -08001501 return;
1502
1503release:
1504 dst_release(dst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001505}
1506
1507static void pndisc_redo(struct sk_buff *skb)
1508{
YOSHIFUJI Hideaki140e26fc2005-10-05 12:11:41 -07001509 ndisc_recv_ns(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001510 kfree_skb(skb);
1511}
1512
1513int ndisc_rcv(struct sk_buff *skb)
1514{
1515 struct nd_msg *msg;
1516
1517 if (!pskb_may_pull(skb, skb->len))
1518 return 0;
1519
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -07001520 msg = (struct nd_msg *)skb_transport_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001521
Arnaldo Carvalho de Melo9c702202007-04-25 18:04:18 -07001522 __skb_push(skb, skb->data - skb_transport_header(skb));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001523
Arnaldo Carvalho de Melo0660e032007-04-25 17:54:47 -07001524 if (ipv6_hdr(skb)->hop_limit != 255) {
Joe Perches675418d2012-05-16 19:28:38 +00001525 ND_PRINTK(2, warn, "NDISC: invalid hop-limit: %d\n",
1526 ipv6_hdr(skb)->hop_limit);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001527 return 0;
1528 }
1529
1530 if (msg->icmph.icmp6_code != 0) {
Joe Perches675418d2012-05-16 19:28:38 +00001531 ND_PRINTK(2, warn, "NDISC: invalid ICMPv6 code: %d\n",
1532 msg->icmph.icmp6_code);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001533 return 0;
1534 }
1535
Patrick McHardya61bbcf2005-08-14 17:24:31 -07001536 memset(NEIGH_CB(skb), 0, sizeof(struct neighbour_cb));
1537
Linus Torvalds1da177e2005-04-16 15:20:36 -07001538 switch (msg->icmph.icmp6_type) {
1539 case NDISC_NEIGHBOUR_SOLICITATION:
1540 ndisc_recv_ns(skb);
1541 break;
1542
1543 case NDISC_NEIGHBOUR_ADVERTISEMENT:
1544 ndisc_recv_na(skb);
1545 break;
1546
1547 case NDISC_ROUTER_SOLICITATION:
1548 ndisc_recv_rs(skb);
1549 break;
1550
1551 case NDISC_ROUTER_ADVERTISEMENT:
1552 ndisc_router_discovery(skb);
1553 break;
1554
1555 case NDISC_REDIRECT:
1556 ndisc_redirect_rcv(skb);
1557 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -07001558 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001559
1560 return 0;
1561}
1562
1563static int ndisc_netdev_event(struct notifier_block *this, unsigned long event, void *ptr)
1564{
1565 struct net_device *dev = ptr;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001566 struct net *net = dev_net(dev);
Hannes Frederic Sowa5cb04432012-11-06 16:46:20 +00001567 struct inet6_dev *idev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001568
1569 switch (event) {
1570 case NETDEV_CHANGEADDR:
1571 neigh_changeaddr(&nd_tbl, dev);
Daniel Lezcano5b7c9312008-03-03 23:28:58 -08001572 fib6_run_gc(~0UL, net);
Hannes Frederic Sowa5cb04432012-11-06 16:46:20 +00001573 idev = in6_dev_get(dev);
1574 if (!idev)
1575 break;
1576 if (idev->cnf.ndisc_notify)
1577 ndisc_send_unsol_na(dev);
1578 in6_dev_put(idev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001579 break;
1580 case NETDEV_DOWN:
1581 neigh_ifdown(&nd_tbl, dev);
Daniel Lezcano5b7c9312008-03-03 23:28:58 -08001582 fib6_run_gc(~0UL, net);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001583 break;
Ben Hutchingsf47b9462011-04-15 13:46:02 +00001584 case NETDEV_NOTIFY_PEERS:
1585 ndisc_send_unsol_na(dev);
1586 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001587 default:
1588 break;
1589 }
1590
1591 return NOTIFY_DONE;
1592}
1593
1594static struct notifier_block ndisc_netdev_notifier = {
1595 .notifier_call = ndisc_netdev_event,
1596};
1597
1598#ifdef CONFIG_SYSCTL
1599static void ndisc_warn_deprecated_sysctl(struct ctl_table *ctl,
1600 const char *func, const char *dev_name)
1601{
1602 static char warncomm[TASK_COMM_LEN];
1603 static int warned;
1604 if (strcmp(warncomm, current->comm) && warned < 5) {
1605 strcpy(warncomm, current->comm);
Joe Perchesf3213832012-05-15 14:11:53 +00001606 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 -07001607 warncomm, func,
1608 dev_name, ctl->procname,
1609 dev_name, ctl->procname);
1610 warned++;
1611 }
1612}
1613
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001614int 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 -07001615{
1616 struct net_device *dev = ctl->extra1;
1617 struct inet6_dev *idev;
1618 int ret;
1619
Eric W. Biedermand12af672007-10-18 03:05:25 -07001620 if ((strcmp(ctl->procname, "retrans_time") == 0) ||
1621 (strcmp(ctl->procname, "base_reachable_time") == 0))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001622 ndisc_warn_deprecated_sysctl(ctl, "syscall", dev ? dev->name : "default");
1623
Eric W. Biedermand12af672007-10-18 03:05:25 -07001624 if (strcmp(ctl->procname, "retrans_time") == 0)
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001625 ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
Eric W. Biedermand12af672007-10-18 03:05:25 -07001626
1627 else if (strcmp(ctl->procname, "base_reachable_time") == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001628 ret = proc_dointvec_jiffies(ctl, write,
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001629 buffer, lenp, ppos);
Eric W. Biedermand12af672007-10-18 03:05:25 -07001630
1631 else if ((strcmp(ctl->procname, "retrans_time_ms") == 0) ||
YOSHIFUJI Hideakiad02ac12007-10-29 01:32:23 -07001632 (strcmp(ctl->procname, "base_reachable_time_ms") == 0))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001633 ret = proc_dointvec_ms_jiffies(ctl, write,
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001634 buffer, lenp, ppos);
Eric W. Biedermand12af672007-10-18 03:05:25 -07001635 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001636 ret = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001637
1638 if (write && ret == 0 && dev && (idev = in6_dev_get(dev)) != NULL) {
Eric W. Biedermand12af672007-10-18 03:05:25 -07001639 if (ctl->data == &idev->nd_parms->base_reachable_time)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001640 idev->nd_parms->reachable_time = neigh_rand_reach_time(idev->nd_parms->base_reachable_time);
1641 idev->tstamp = jiffies;
1642 inet6_ifinfo_notify(RTM_NEWLINK, idev);
1643 in6_dev_put(idev);
1644 }
1645 return ret;
1646}
1647
Linus Torvalds1da177e2005-04-16 15:20:36 -07001648
1649#endif
1650
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00001651static int __net_init ndisc_net_init(struct net *net)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001652{
1653 struct ipv6_pinfo *np;
1654 struct sock *sk;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001655 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001656
Denis V. Lunev1ed85162008-04-03 14:31:03 -07001657 err = inet_ctl_sock_create(&sk, PF_INET6,
1658 SOCK_RAW, IPPROTO_ICMPV6, net);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001659 if (err < 0) {
Joe Perches675418d2012-05-16 19:28:38 +00001660 ND_PRINTK(0, err,
1661 "NDISC: Failed to initialize the control socket (err %d)\n",
1662 err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001663 return err;
1664 }
1665
Denis V. Lunev1ed85162008-04-03 14:31:03 -07001666 net->ipv6.ndisc_sk = sk;
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001667
Linus Torvalds1da177e2005-04-16 15:20:36 -07001668 np = inet6_sk(sk);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001669 np->hop_limit = 255;
1670 /* Do not loopback ndisc messages */
1671 np->mc_loop = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001672
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001673 return 0;
1674}
1675
Alexey Dobriyan2c8c1e72010-01-17 03:35:32 +00001676static void __net_exit ndisc_net_exit(struct net *net)
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001677{
Denis V. Lunev1ed85162008-04-03 14:31:03 -07001678 inet_ctl_sock_destroy(net->ipv6.ndisc_sk);
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001679}
1680
1681static struct pernet_operations ndisc_net_ops = {
1682 .init = ndisc_net_init,
1683 .exit = ndisc_net_exit,
1684};
1685
1686int __init ndisc_init(void)
1687{
1688 int err;
1689
1690 err = register_pernet_subsys(&ndisc_net_ops);
1691 if (err)
1692 return err;
YOSHIFUJI Hideaki1ab14572007-02-09 23:24:49 +09001693 /*
1694 * Initialize the neighbour table
1695 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001696 neigh_table_init(&nd_tbl);
1697
1698#ifdef CONFIG_SYSCTL
Eric W. Biederman54716e32010-02-14 03:27:03 +00001699 err = neigh_sysctl_register(NULL, &nd_tbl.parms, "ipv6",
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001700 &ndisc_ifinfo_sysctl_change);
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001701 if (err)
1702 goto out_unregister_pernet;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001703#endif
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001704 err = register_netdevice_notifier(&ndisc_netdev_notifier);
1705 if (err)
1706 goto out_unregister_sysctl;
1707out:
1708 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001709
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001710out_unregister_sysctl:
1711#ifdef CONFIG_SYSCTL
1712 neigh_sysctl_unregister(&nd_tbl.parms);
1713out_unregister_pernet:
1714#endif
1715 unregister_pernet_subsys(&ndisc_net_ops);
1716 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001717}
1718
1719void ndisc_cleanup(void)
1720{
Dmitry Mishin36f73d02006-11-03 16:08:19 -08001721 unregister_netdevice_notifier(&ndisc_netdev_notifier);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001722#ifdef CONFIG_SYSCTL
1723 neigh_sysctl_unregister(&nd_tbl.parms);
1724#endif
1725 neigh_table_clear(&nd_tbl);
Daniel Lezcano1762f7e2008-03-07 11:15:34 -08001726 unregister_pernet_subsys(&ndisc_net_ops);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001727}