1 | /* $NetBSD: nd6_nbr.c,v 1.130 2016/11/15 21:17:07 mlelstv Exp $ */ |
2 | /* $KAME: nd6_nbr.c,v 1.61 2001/02/10 16:06:14 jinmei Exp $ */ |
3 | |
4 | /* |
5 | * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. |
6 | * All rights reserved. |
7 | * |
8 | * Redistribution and use in source and binary forms, with or without |
9 | * modification, are permitted provided that the following conditions |
10 | * are met: |
11 | * 1. Redistributions of source code must retain the above copyright |
12 | * notice, this list of conditions and the following disclaimer. |
13 | * 2. Redistributions in binary form must reproduce the above copyright |
14 | * notice, this list of conditions and the following disclaimer in the |
15 | * documentation and/or other materials provided with the distribution. |
16 | * 3. Neither the name of the project nor the names of its contributors |
17 | * may be used to endorse or promote products derived from this software |
18 | * without specific prior written permission. |
19 | * |
20 | * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND |
21 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
23 | * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE |
24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
26 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
27 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
28 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
29 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
30 | * SUCH DAMAGE. |
31 | */ |
32 | |
33 | #include <sys/cdefs.h> |
34 | __KERNEL_RCSID(0, "$NetBSD: nd6_nbr.c,v 1.130 2016/11/15 21:17:07 mlelstv Exp $" ); |
35 | |
36 | #ifdef _KERNEL_OPT |
37 | #include "opt_inet.h" |
38 | #include "opt_net_mpsafe.h" |
39 | #endif |
40 | |
41 | #include <sys/param.h> |
42 | #include <sys/systm.h> |
43 | #include <sys/malloc.h> |
44 | #include <sys/mbuf.h> |
45 | #include <sys/socket.h> |
46 | #include <sys/socketvar.h> |
47 | #include <sys/sockio.h> |
48 | #include <sys/time.h> |
49 | #include <sys/kernel.h> |
50 | #include <sys/errno.h> |
51 | #include <sys/ioctl.h> |
52 | #include <sys/syslog.h> |
53 | #include <sys/queue.h> |
54 | #include <sys/callout.h> |
55 | |
56 | #include <net/if.h> |
57 | #include <net/if_types.h> |
58 | #include <net/if_dl.h> |
59 | #include <net/route.h> |
60 | |
61 | #include <netinet/in.h> |
62 | #include <netinet/in_var.h> |
63 | #include <netinet6/in6_var.h> |
64 | #include <netinet6/in6_ifattach.h> |
65 | #include <netinet/ip6.h> |
66 | #include <netinet6/ip6_var.h> |
67 | #include <netinet6/scope6_var.h> |
68 | #include <netinet6/nd6.h> |
69 | #include <netinet/icmp6.h> |
70 | #include <netinet6/icmp6_private.h> |
71 | |
72 | #include "carp.h" |
73 | #if NCARP > 0 |
74 | #include <netinet/ip_carp.h> |
75 | #endif |
76 | |
77 | #include <net/net_osdep.h> |
78 | |
79 | struct dadq; |
80 | static struct dadq *nd6_dad_find(struct ifaddr *); |
81 | static void nd6_dad_starttimer(struct dadq *, int); |
82 | static void nd6_dad_stoptimer(struct dadq *); |
83 | static void nd6_dad_timer(struct ifaddr *); |
84 | static void nd6_dad_ns_output(struct dadq *, struct ifaddr *); |
85 | static void nd6_dad_ns_input(struct ifaddr *); |
86 | static void nd6_dad_na_input(struct ifaddr *); |
87 | |
88 | static int dad_ignore_ns = 0; /* ignore NS in DAD - specwise incorrect*/ |
89 | static int dad_maxtry = 15; /* max # of *tries* to transmit DAD packet */ |
90 | |
91 | /* |
92 | * Input a Neighbor Solicitation Message. |
93 | * |
94 | * Based on RFC 2461 |
95 | * Based on RFC 2462 (duplicate address detection) |
96 | */ |
97 | void |
98 | nd6_ns_input(struct mbuf *m, int off, int icmp6len) |
99 | { |
100 | struct ifnet *ifp; |
101 | struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); |
102 | struct nd_neighbor_solicit *nd_ns; |
103 | struct in6_addr saddr6 = ip6->ip6_src; |
104 | struct in6_addr daddr6 = ip6->ip6_dst; |
105 | struct in6_addr taddr6; |
106 | struct in6_addr myaddr6; |
107 | char *lladdr = NULL; |
108 | struct ifaddr *ifa = NULL; |
109 | int lladdrlen = 0; |
110 | int anycast = 0, proxy = 0, tentative = 0; |
111 | int router = ip6_forwarding; |
112 | int tlladdr; |
113 | union nd_opts ndopts; |
114 | const struct sockaddr_dl *proxydl = NULL; |
115 | struct psref psref; |
116 | struct psref psref_ia; |
117 | |
118 | ifp = m_get_rcvif_psref(m, &psref); |
119 | if (ifp == NULL) |
120 | goto freeit; |
121 | |
122 | IP6_EXTHDR_GET(nd_ns, struct nd_neighbor_solicit *, m, off, icmp6len); |
123 | if (nd_ns == NULL) { |
124 | ICMP6_STATINC(ICMP6_STAT_TOOSHORT); |
125 | m_put_rcvif_psref(ifp, &psref); |
126 | return; |
127 | } |
128 | ip6 = mtod(m, struct ip6_hdr *); /* adjust pointer for safety */ |
129 | taddr6 = nd_ns->nd_ns_target; |
130 | if (in6_setscope(&taddr6, ifp, NULL) != 0) |
131 | goto bad; |
132 | |
133 | if (ip6->ip6_hlim != 255) { |
134 | nd6log(LOG_ERR, "invalid hlim (%d) from %s to %s on %s\n" , |
135 | ip6->ip6_hlim, ip6_sprintf(&ip6->ip6_src), |
136 | ip6_sprintf(&ip6->ip6_dst), if_name(ifp)); |
137 | goto bad; |
138 | } |
139 | |
140 | if (IN6_IS_ADDR_UNSPECIFIED(&saddr6)) { |
141 | /* dst has to be a solicited node multicast address. */ |
142 | /* don't check ifindex portion */ |
143 | if (daddr6.s6_addr16[0] == IPV6_ADDR_INT16_MLL && |
144 | daddr6.s6_addr32[1] == 0 && |
145 | daddr6.s6_addr32[2] == IPV6_ADDR_INT32_ONE && |
146 | daddr6.s6_addr8[12] == 0xff) { |
147 | ; /* good */ |
148 | } else { |
149 | nd6log(LOG_INFO, "bad DAD packet (wrong ip6 dst)\n" ); |
150 | goto bad; |
151 | } |
152 | } else { |
153 | struct sockaddr_in6 ssin6; |
154 | |
155 | /* |
156 | * Make sure the source address is from a neighbor's address. |
157 | */ |
158 | sockaddr_in6_init(&ssin6, &saddr6, 0, 0, 0); |
159 | if (nd6_is_addr_neighbor(&ssin6, ifp) == 0) { |
160 | nd6log(LOG_INFO, |
161 | "NS packet from non-neighbor %s on %s\n" , |
162 | ip6_sprintf(&saddr6), if_name(ifp)); |
163 | goto bad; |
164 | } |
165 | } |
166 | |
167 | |
168 | if (IN6_IS_ADDR_MULTICAST(&taddr6)) { |
169 | nd6log(LOG_INFO, "bad NS target (multicast)\n" ); |
170 | goto bad; |
171 | } |
172 | |
173 | icmp6len -= sizeof(*nd_ns); |
174 | nd6_option_init(nd_ns + 1, icmp6len, &ndopts); |
175 | if (nd6_options(&ndopts) < 0) { |
176 | nd6log(LOG_INFO, "invalid ND option, ignored\n" ); |
177 | /* nd6_options have incremented stats */ |
178 | goto freeit; |
179 | } |
180 | |
181 | if (ndopts.nd_opts_src_lladdr) { |
182 | lladdr = (char *)(ndopts.nd_opts_src_lladdr + 1); |
183 | lladdrlen = ndopts.nd_opts_src_lladdr->nd_opt_len << 3; |
184 | } |
185 | |
186 | if (IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src) && lladdr) { |
187 | nd6log(LOG_INFO, |
188 | "bad DAD packet (link-layer address option)\n" ); |
189 | goto bad; |
190 | } |
191 | |
192 | /* |
193 | * Attaching target link-layer address to the NA? |
194 | * (RFC 2461 7.2.4) |
195 | * |
196 | * NS IP dst is multicast MUST add |
197 | * Otherwise MAY be omitted |
198 | * |
199 | * In this implementation, we omit the target link-layer address |
200 | * in the "MAY" case. |
201 | */ |
202 | #if 0 /* too much! */ |
203 | ifa = (struct ifaddr *)in6ifa_ifpwithaddr(ifp, &daddr6); |
204 | if (ifa && (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_ANYCAST)) |
205 | tlladdr = 0; |
206 | else |
207 | #endif |
208 | if (!IN6_IS_ADDR_MULTICAST(&daddr6)) |
209 | tlladdr = 0; |
210 | else |
211 | tlladdr = 1; |
212 | |
213 | /* |
214 | * Target address (taddr6) must be either: |
215 | * (1) Valid unicast/anycast address for my receiving interface, |
216 | * (2) Unicast address for which I'm offering proxy service, or |
217 | * (3) "tentative" address on which DAD is being performed. |
218 | */ |
219 | /* (1) and (3) check. */ |
220 | #if NCARP > 0 |
221 | if (ifp->if_carp && ifp->if_type != IFT_CARP) { |
222 | int s = pserialize_read_enter(); |
223 | ifa = carp_iamatch6(ifp->if_carp, &taddr6); |
224 | if (ifa != NULL) |
225 | ifa_acquire(ifa, &psref_ia); |
226 | pserialize_read_exit(s); |
227 | } else |
228 | ifa = NULL; |
229 | if (!ifa) |
230 | ifa = (struct ifaddr *)in6ifa_ifpwithaddr_psref(ifp, &taddr6, |
231 | &psref_ia); |
232 | #else |
233 | ifa = (struct ifaddr *)in6ifa_ifpwithaddr_psref(ifp, &taddr6, |
234 | &psref_ia); |
235 | #endif |
236 | |
237 | /* (2) check. */ |
238 | if (ifa == NULL) { |
239 | struct rtentry *rt; |
240 | struct sockaddr_in6 tsin6; |
241 | |
242 | sockaddr_in6_init(&tsin6, &taddr6, 0, 0, 0); |
243 | |
244 | rt = rtalloc1(sin6tosa(&tsin6), 0); |
245 | if (rt && (rt->rt_flags & RTF_ANNOUNCE) != 0 && |
246 | rt->rt_gateway->sa_family == AF_LINK) { |
247 | /* |
248 | * proxy NDP for single entry |
249 | */ |
250 | ifa = (struct ifaddr *)in6ifa_ifpforlinklocal_psref(ifp, |
251 | IN6_IFF_NOTREADY|IN6_IFF_ANYCAST, &psref_ia); |
252 | if (ifa) { |
253 | proxy = 1; |
254 | proxydl = satocsdl(rt->rt_gateway); |
255 | router = 0; /* XXX */ |
256 | } |
257 | } |
258 | if (rt) |
259 | rtfree(rt); |
260 | } |
261 | if (ifa == NULL) { |
262 | /* |
263 | * We've got an NS packet, and we don't have that address |
264 | * assigned for us. We MUST silently ignore it. |
265 | * See RFC2461 7.2.3. |
266 | */ |
267 | goto freeit; |
268 | } |
269 | myaddr6 = *IFA_IN6(ifa); |
270 | anycast = ((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_ANYCAST; |
271 | tentative = ((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_TENTATIVE; |
272 | if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DUPLICATED) |
273 | goto freeit; |
274 | |
275 | if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) { |
276 | nd6log(LOG_INFO, "lladdrlen mismatch for %s " |
277 | "(if %d, NS packet %d)\n" , |
278 | ip6_sprintf(&taddr6), ifp->if_addrlen, lladdrlen - 2); |
279 | goto bad; |
280 | } |
281 | |
282 | if (IN6_ARE_ADDR_EQUAL(&myaddr6, &saddr6)) { |
283 | nd6log(LOG_INFO, "duplicate IP6 address %s\n" , |
284 | ip6_sprintf(&saddr6)); |
285 | goto freeit; |
286 | } |
287 | |
288 | /* |
289 | * We have neighbor solicitation packet, with target address equals to |
290 | * one of my tentative address. |
291 | * |
292 | * src addr how to process? |
293 | * --- --- |
294 | * multicast of course, invalid (rejected in ip6_input) |
295 | * unicast somebody is doing address resolution -> ignore |
296 | * unspec dup address detection |
297 | * |
298 | * The processing is defined in RFC 2462. |
299 | */ |
300 | if (tentative) { |
301 | /* |
302 | * If source address is unspecified address, it is for |
303 | * duplicate address detection. |
304 | * |
305 | * If not, the packet is for addess resolution; |
306 | * silently ignore it. |
307 | */ |
308 | if (IN6_IS_ADDR_UNSPECIFIED(&saddr6)) |
309 | nd6_dad_ns_input(ifa); |
310 | ifa_release(ifa, &psref_ia); |
311 | ifa = NULL; |
312 | |
313 | goto freeit; |
314 | } |
315 | ifa_release(ifa, &psref_ia); |
316 | ifa = NULL; |
317 | |
318 | /* |
319 | * If the source address is unspecified address, entries must not |
320 | * be created or updated. |
321 | * It looks that sender is performing DAD. Output NA toward |
322 | * all-node multicast address, to tell the sender that I'm using |
323 | * the address. |
324 | * S bit ("solicited") must be zero. |
325 | */ |
326 | if (IN6_IS_ADDR_UNSPECIFIED(&saddr6)) { |
327 | struct in6_addr in6_all; |
328 | |
329 | in6_all = in6addr_linklocal_allnodes; |
330 | if (in6_setscope(&in6_all, ifp, NULL) != 0) |
331 | goto bad; |
332 | nd6_na_output(ifp, &in6_all, &taddr6, |
333 | ((anycast || proxy || !tlladdr) ? 0 : ND_NA_FLAG_OVERRIDE) | |
334 | (ip6_forwarding ? ND_NA_FLAG_ROUTER : 0), |
335 | tlladdr, (const struct sockaddr *)proxydl); |
336 | goto freeit; |
337 | } |
338 | |
339 | nd6_cache_lladdr(ifp, &saddr6, lladdr, lladdrlen, ND_NEIGHBOR_SOLICIT, 0); |
340 | |
341 | nd6_na_output(ifp, &saddr6, &taddr6, |
342 | ((anycast || proxy || !tlladdr) ? 0 : ND_NA_FLAG_OVERRIDE) | |
343 | (router ? ND_NA_FLAG_ROUTER : 0) | ND_NA_FLAG_SOLICITED, |
344 | tlladdr, (const struct sockaddr *)proxydl); |
345 | freeit: |
346 | ifa_release(ifa, &psref_ia); |
347 | m_put_rcvif_psref(ifp, &psref); |
348 | m_freem(m); |
349 | return; |
350 | |
351 | bad: |
352 | nd6log(LOG_ERR, "src=%s\n" , ip6_sprintf(&saddr6)); |
353 | nd6log(LOG_ERR, "dst=%s\n" , ip6_sprintf(&daddr6)); |
354 | nd6log(LOG_ERR, "tgt=%s\n" , ip6_sprintf(&taddr6)); |
355 | ICMP6_STATINC(ICMP6_STAT_BADNS); |
356 | ifa_release(ifa, &psref_ia); |
357 | m_put_rcvif_psref(ifp, &psref); |
358 | m_freem(m); |
359 | } |
360 | |
361 | /* |
362 | * Output a Neighbor Solicitation Message. Caller specifies: |
363 | * - ICMP6 header source IP6 address |
364 | * - ND6 header target IP6 address |
365 | * - ND6 header source datalink address |
366 | * |
367 | * Based on RFC 2461 |
368 | * Based on RFC 2462 (duplicate address detection) |
369 | */ |
370 | void |
371 | nd6_ns_output(struct ifnet *ifp, const struct in6_addr *daddr6, |
372 | const struct in6_addr *taddr6, |
373 | struct in6_addr *hsrc, |
374 | int dad /* duplicate address detection */) |
375 | { |
376 | struct mbuf *m; |
377 | struct ip6_hdr *ip6; |
378 | struct nd_neighbor_solicit *nd_ns; |
379 | struct in6_addr *src, src_in; |
380 | struct ip6_moptions im6o; |
381 | int icmp6len; |
382 | int maxlen; |
383 | const void *mac; |
384 | struct route ro; |
385 | |
386 | if (IN6_IS_ADDR_MULTICAST(taddr6)) |
387 | return; |
388 | |
389 | memset(&ro, 0, sizeof(ro)); |
390 | |
391 | /* estimate the size of message */ |
392 | maxlen = sizeof(*ip6) + sizeof(*nd_ns); |
393 | maxlen += (sizeof(struct nd_opt_hdr) + ifp->if_addrlen + 7) & ~7; |
394 | #ifdef DIAGNOSTIC |
395 | if (max_linkhdr + maxlen >= MCLBYTES) { |
396 | printf("nd6_ns_output: max_linkhdr + maxlen >= MCLBYTES " |
397 | "(%d + %d > %d)\n" , max_linkhdr, maxlen, MCLBYTES); |
398 | panic("nd6_ns_output: insufficient MCLBYTES" ); |
399 | /* NOTREACHED */ |
400 | } |
401 | #endif |
402 | |
403 | MGETHDR(m, M_DONTWAIT, MT_DATA); |
404 | if (m && max_linkhdr + maxlen >= MHLEN) { |
405 | MCLGET(m, M_DONTWAIT); |
406 | if ((m->m_flags & M_EXT) == 0) { |
407 | m_free(m); |
408 | m = NULL; |
409 | } |
410 | } |
411 | if (m == NULL) |
412 | return; |
413 | m_reset_rcvif(m); |
414 | |
415 | if (daddr6 == NULL || IN6_IS_ADDR_MULTICAST(daddr6)) { |
416 | m->m_flags |= M_MCAST; |
417 | im6o.im6o_multicast_if_index = if_get_index(ifp); |
418 | im6o.im6o_multicast_hlim = 255; |
419 | im6o.im6o_multicast_loop = 0; |
420 | } |
421 | |
422 | icmp6len = sizeof(*nd_ns); |
423 | m->m_pkthdr.len = m->m_len = sizeof(*ip6) + icmp6len; |
424 | m->m_data += max_linkhdr; /* or MH_ALIGN() equivalent? */ |
425 | |
426 | /* fill neighbor solicitation packet */ |
427 | ip6 = mtod(m, struct ip6_hdr *); |
428 | ip6->ip6_flow = 0; |
429 | ip6->ip6_vfc &= ~IPV6_VERSION_MASK; |
430 | ip6->ip6_vfc |= IPV6_VERSION; |
431 | /* ip6->ip6_plen will be set later */ |
432 | ip6->ip6_nxt = IPPROTO_ICMPV6; |
433 | ip6->ip6_hlim = 255; |
434 | if (daddr6) |
435 | ip6->ip6_dst = *daddr6; |
436 | else { |
437 | ip6->ip6_dst.s6_addr16[0] = IPV6_ADDR_INT16_MLL; |
438 | ip6->ip6_dst.s6_addr16[1] = 0; |
439 | ip6->ip6_dst.s6_addr32[1] = 0; |
440 | ip6->ip6_dst.s6_addr32[2] = IPV6_ADDR_INT32_ONE; |
441 | ip6->ip6_dst.s6_addr32[3] = taddr6->s6_addr32[3]; |
442 | ip6->ip6_dst.s6_addr8[12] = 0xff; |
443 | if (in6_setscope(&ip6->ip6_dst, ifp, NULL) != 0) |
444 | goto bad; |
445 | } |
446 | if (!dad) { |
447 | int s; |
448 | /* |
449 | * RFC2461 7.2.2: |
450 | * "If the source address of the packet prompting the |
451 | * solicitation is the same as one of the addresses assigned |
452 | * to the outgoing interface, that address SHOULD be placed |
453 | * in the IP Source Address of the outgoing solicitation. |
454 | * Otherwise, any one of the addresses assigned to the |
455 | * interface should be used." |
456 | * |
457 | * We use the source address for the prompting packet |
458 | * (hsrc), if: |
459 | * - hsrc is given from the caller (by giving "ln"), and |
460 | * - hsrc belongs to the outgoing interface. |
461 | * Otherwise, we perform the source address selection as usual. |
462 | */ |
463 | s = pserialize_read_enter(); |
464 | if (hsrc && in6ifa_ifpwithaddr(ifp, hsrc)) |
465 | src = hsrc; |
466 | else { |
467 | int error; |
468 | struct sockaddr_in6 dst_sa; |
469 | |
470 | sockaddr_in6_init(&dst_sa, &ip6->ip6_dst, 0, 0, 0); |
471 | |
472 | error = in6_selectsrc(&dst_sa, NULL, |
473 | NULL, &ro, NULL, NULL, NULL, &src_in); |
474 | if (error != 0) { |
475 | nd6log(LOG_DEBUG, "source can't be " |
476 | "determined: dst=%s, error=%d\n" , |
477 | ip6_sprintf(&dst_sa.sin6_addr), error); |
478 | pserialize_read_exit(s); |
479 | goto bad; |
480 | } |
481 | src = &src_in; |
482 | } |
483 | pserialize_read_exit(s); |
484 | } else { |
485 | /* |
486 | * Source address for DAD packet must always be IPv6 |
487 | * unspecified address. (0::0) |
488 | * We actually don't have to 0-clear the address (we did it |
489 | * above), but we do so here explicitly to make the intention |
490 | * clearer. |
491 | */ |
492 | memset(&src_in, 0, sizeof(src_in)); |
493 | src = &src_in; |
494 | } |
495 | ip6->ip6_src = *src; |
496 | nd_ns = (struct nd_neighbor_solicit *)(ip6 + 1); |
497 | nd_ns->nd_ns_type = ND_NEIGHBOR_SOLICIT; |
498 | nd_ns->nd_ns_code = 0; |
499 | nd_ns->nd_ns_reserved = 0; |
500 | nd_ns->nd_ns_target = *taddr6; |
501 | in6_clearscope(&nd_ns->nd_ns_target); /* XXX */ |
502 | |
503 | /* |
504 | * Add source link-layer address option. |
505 | * |
506 | * spec implementation |
507 | * --- --- |
508 | * DAD packet MUST NOT do not add the option |
509 | * there's no link layer address: |
510 | * impossible do not add the option |
511 | * there's link layer address: |
512 | * Multicast NS MUST add one add the option |
513 | * Unicast NS SHOULD add one add the option |
514 | */ |
515 | if (!dad && (mac = nd6_ifptomac(ifp))) { |
516 | int optlen = sizeof(struct nd_opt_hdr) + ifp->if_addrlen; |
517 | struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)(nd_ns + 1); |
518 | /* 8 byte alignments... */ |
519 | optlen = (optlen + 7) & ~7; |
520 | |
521 | m->m_pkthdr.len += optlen; |
522 | m->m_len += optlen; |
523 | icmp6len += optlen; |
524 | memset((void *)nd_opt, 0, optlen); |
525 | nd_opt->nd_opt_type = ND_OPT_SOURCE_LINKADDR; |
526 | nd_opt->nd_opt_len = optlen >> 3; |
527 | memcpy((void *)(nd_opt + 1), mac, ifp->if_addrlen); |
528 | } |
529 | |
530 | ip6->ip6_plen = htons((u_int16_t)icmp6len); |
531 | nd_ns->nd_ns_cksum = 0; |
532 | nd_ns->nd_ns_cksum = |
533 | in6_cksum(m, IPPROTO_ICMPV6, sizeof(*ip6), icmp6len); |
534 | |
535 | ip6_output(m, NULL, &ro, dad ? IPV6_UNSPECSRC : 0, &im6o, NULL, NULL); |
536 | icmp6_ifstat_inc(ifp, ifs6_out_msg); |
537 | icmp6_ifstat_inc(ifp, ifs6_out_neighborsolicit); |
538 | ICMP6_STATINC(ICMP6_STAT_OUTHIST + ND_NEIGHBOR_SOLICIT); |
539 | |
540 | rtcache_free(&ro); |
541 | return; |
542 | |
543 | bad: |
544 | rtcache_free(&ro); |
545 | m_freem(m); |
546 | return; |
547 | } |
548 | |
549 | /* |
550 | * Neighbor advertisement input handling. |
551 | * |
552 | * Based on RFC 2461 |
553 | * Based on RFC 2462 (duplicate address detection) |
554 | * |
555 | * the following items are not implemented yet: |
556 | * - proxy advertisement delay rule (RFC2461 7.2.8, last paragraph, SHOULD) |
557 | * - anycast advertisement delay rule (RFC2461 7.2.7, SHOULD) |
558 | */ |
559 | void |
560 | nd6_na_input(struct mbuf *m, int off, int icmp6len) |
561 | { |
562 | struct ifnet *ifp; |
563 | struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); |
564 | struct nd_neighbor_advert *nd_na; |
565 | struct in6_addr saddr6 = ip6->ip6_src; |
566 | struct in6_addr daddr6 = ip6->ip6_dst; |
567 | struct in6_addr taddr6; |
568 | int flags; |
569 | int is_router; |
570 | int is_solicited; |
571 | int is_override; |
572 | char *lladdr = NULL; |
573 | int lladdrlen = 0; |
574 | struct ifaddr *ifa; |
575 | struct llentry *ln = NULL; |
576 | union nd_opts ndopts; |
577 | struct sockaddr_in6 ssin6; |
578 | int rt_announce; |
579 | bool checklink = false; |
580 | struct psref psref; |
581 | struct psref psref_ia; |
582 | |
583 | ifp = m_get_rcvif_psref(m, &psref); |
584 | if (ifp == NULL) |
585 | goto freeit; |
586 | |
587 | if (ip6->ip6_hlim != 255) { |
588 | nd6log(LOG_ERR, |
589 | "invalid hlim (%d) from %s to %s on %s\n" , |
590 | ip6->ip6_hlim, ip6_sprintf(&ip6->ip6_src), |
591 | ip6_sprintf(&ip6->ip6_dst), if_name(ifp)); |
592 | goto bad; |
593 | } |
594 | |
595 | IP6_EXTHDR_GET(nd_na, struct nd_neighbor_advert *, m, off, icmp6len); |
596 | if (nd_na == NULL) { |
597 | m_put_rcvif_psref(ifp, &psref); |
598 | ICMP6_STATINC(ICMP6_STAT_TOOSHORT); |
599 | return; |
600 | } |
601 | |
602 | flags = nd_na->nd_na_flags_reserved; |
603 | is_router = ((flags & ND_NA_FLAG_ROUTER) != 0); |
604 | is_solicited = ((flags & ND_NA_FLAG_SOLICITED) != 0); |
605 | is_override = ((flags & ND_NA_FLAG_OVERRIDE) != 0); |
606 | |
607 | taddr6 = nd_na->nd_na_target; |
608 | if (in6_setscope(&taddr6, ifp, NULL)) { |
609 | m_put_rcvif_psref(ifp, &psref); |
610 | return; /* XXX: impossible */ |
611 | } |
612 | |
613 | if (IN6_IS_ADDR_MULTICAST(&taddr6)) { |
614 | nd6log(LOG_ERR, "invalid target address %s\n" , |
615 | ip6_sprintf(&taddr6)); |
616 | goto bad; |
617 | } |
618 | if (is_solicited && IN6_IS_ADDR_MULTICAST(&daddr6)) { |
619 | nd6log(LOG_ERR, "a solicited adv is multicasted\n" ); |
620 | goto bad; |
621 | } |
622 | |
623 | icmp6len -= sizeof(*nd_na); |
624 | nd6_option_init(nd_na + 1, icmp6len, &ndopts); |
625 | if (nd6_options(&ndopts) < 0) { |
626 | nd6log(LOG_INFO, "invalid ND option, ignored\n" ); |
627 | /* nd6_options have incremented stats */ |
628 | goto freeit; |
629 | } |
630 | |
631 | if (ndopts.nd_opts_tgt_lladdr) { |
632 | lladdr = (char *)(ndopts.nd_opts_tgt_lladdr + 1); |
633 | lladdrlen = ndopts.nd_opts_tgt_lladdr->nd_opt_len << 3; |
634 | } |
635 | |
636 | ifa = (struct ifaddr *)in6ifa_ifpwithaddr_psref(ifp, &taddr6, &psref_ia); |
637 | |
638 | /* |
639 | * Target address matches one of my interface address. |
640 | * |
641 | * If my address is tentative, this means that there's somebody |
642 | * already using the same address as mine. This indicates DAD failure. |
643 | * This is defined in RFC 2462. |
644 | * |
645 | * Otherwise, process as defined in RFC 2461. |
646 | */ |
647 | if (ifa |
648 | && (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_TENTATIVE)) { |
649 | nd6_dad_na_input(ifa); |
650 | ifa_release(ifa, &psref_ia); |
651 | ifa = NULL; |
652 | goto freeit; |
653 | } |
654 | |
655 | /* Just for safety, maybe unnecessary. */ |
656 | if (ifa) { |
657 | log(LOG_ERR, |
658 | "nd6_na_input: duplicate IP6 address %s\n" , |
659 | ip6_sprintf(&taddr6)); |
660 | ifa_release(ifa, &psref_ia); |
661 | ifa = NULL; |
662 | goto freeit; |
663 | } |
664 | |
665 | /* |
666 | * Make sure the source address is from a neighbor's address. |
667 | */ |
668 | sockaddr_in6_init(&ssin6, &saddr6, 0, 0, 0); |
669 | if (nd6_is_addr_neighbor(&ssin6, ifp) == 0) { |
670 | nd6log(LOG_INFO, "ND packet from non-neighbor %s on %s\n" , |
671 | ip6_sprintf(&saddr6), if_name(ifp)); |
672 | goto bad; |
673 | } |
674 | |
675 | if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) { |
676 | nd6log(LOG_INFO, "lladdrlen mismatch for %s " |
677 | "(if %d, NA packet %d)\n" , ip6_sprintf(&taddr6), |
678 | ifp->if_addrlen, lladdrlen - 2); |
679 | goto bad; |
680 | } |
681 | |
682 | /* |
683 | * If no neighbor cache entry is found, NA SHOULD silently be |
684 | * discarded. |
685 | */ |
686 | ln = nd6_lookup(&taddr6, ifp, true); |
687 | if (ln == NULL) |
688 | goto freeit; |
689 | |
690 | rt_announce = 0; |
691 | if (ln->ln_state == ND6_LLINFO_INCOMPLETE) { |
692 | /* |
693 | * If the link-layer has address, and no lladdr option came, |
694 | * discard the packet. |
695 | */ |
696 | if (ifp->if_addrlen && !lladdr) |
697 | goto freeit; |
698 | |
699 | /* |
700 | * Record link-layer address, and update the state. |
701 | */ |
702 | memcpy(&ln->ll_addr, lladdr, ifp->if_addrlen); |
703 | ln->la_flags |= LLE_VALID; |
704 | rt_announce = 1; |
705 | if (is_solicited) { |
706 | ln->ln_state = ND6_LLINFO_REACHABLE; |
707 | ln->ln_byhint = 0; |
708 | if (!ND6_LLINFO_PERMANENT(ln)) { |
709 | nd6_llinfo_settimer(ln, |
710 | ND_IFINFO(ln->lle_tbl->llt_ifp)->reachable * hz); |
711 | } |
712 | } else { |
713 | ln->ln_state = ND6_LLINFO_STALE; |
714 | nd6_llinfo_settimer(ln, nd6_gctimer * hz); |
715 | } |
716 | if ((ln->ln_router = is_router) != 0) { |
717 | /* |
718 | * This means a router's state has changed from |
719 | * non-reachable to probably reachable, and might |
720 | * affect the status of associated prefixes.. |
721 | */ |
722 | checklink = true; |
723 | } |
724 | } else { |
725 | int llchange; |
726 | |
727 | /* |
728 | * Check if the link-layer address has changed or not. |
729 | */ |
730 | if (lladdr == NULL) |
731 | llchange = 0; |
732 | else { |
733 | if (ln->la_flags & LLE_VALID) { |
734 | if (memcmp(lladdr, &ln->ll_addr, ifp->if_addrlen)) |
735 | llchange = rt_announce = 1; |
736 | else |
737 | llchange = 0; |
738 | } else |
739 | llchange = rt_announce = 1; |
740 | } |
741 | |
742 | /* |
743 | * This is VERY complex. Look at it with care. |
744 | * |
745 | * override solicit lladdr llchange action |
746 | * (L: record lladdr) |
747 | * |
748 | * 0 0 n -- (2c) |
749 | * 0 0 y n (2b) L |
750 | * 0 0 y y (1) REACHABLE->STALE |
751 | * 0 1 n -- (2c) *->REACHABLE |
752 | * 0 1 y n (2b) L *->REACHABLE |
753 | * 0 1 y y (1) REACHABLE->STALE |
754 | * 1 0 n -- (2a) |
755 | * 1 0 y n (2a) L |
756 | * 1 0 y y (2a) L *->STALE |
757 | * 1 1 n -- (2a) *->REACHABLE |
758 | * 1 1 y n (2a) L *->REACHABLE |
759 | * 1 1 y y (2a) L *->REACHABLE |
760 | */ |
761 | if (!is_override && lladdr != NULL && llchange) { /* (1) */ |
762 | /* |
763 | * If state is REACHABLE, make it STALE. |
764 | * no other updates should be done. |
765 | */ |
766 | if (ln->ln_state == ND6_LLINFO_REACHABLE) { |
767 | ln->ln_state = ND6_LLINFO_STALE; |
768 | nd6_llinfo_settimer(ln, nd6_gctimer * hz); |
769 | } |
770 | goto freeit; |
771 | } else if (is_override /* (2a) */ |
772 | || (!is_override && lladdr != NULL && !llchange) /* (2b) */ |
773 | || lladdr == NULL) { /* (2c) */ |
774 | /* |
775 | * Update link-local address, if any. |
776 | */ |
777 | if (lladdr != NULL) { |
778 | memcpy(&ln->ll_addr, lladdr, ifp->if_addrlen); |
779 | ln->la_flags |= LLE_VALID; |
780 | } |
781 | |
782 | /* |
783 | * If solicited, make the state REACHABLE. |
784 | * If not solicited and the link-layer address was |
785 | * changed, make it STALE. |
786 | */ |
787 | if (is_solicited) { |
788 | ln->ln_state = ND6_LLINFO_REACHABLE; |
789 | ln->ln_byhint = 0; |
790 | if (!ND6_LLINFO_PERMANENT(ln)) { |
791 | nd6_llinfo_settimer(ln, |
792 | ND_IFINFO(ifp)->reachable * hz); |
793 | } |
794 | } else { |
795 | if (lladdr && llchange) { |
796 | ln->ln_state = ND6_LLINFO_STALE; |
797 | nd6_llinfo_settimer(ln, |
798 | nd6_gctimer * hz); |
799 | } |
800 | } |
801 | } |
802 | |
803 | if (ln->ln_router && !is_router) { |
804 | /* |
805 | * The peer dropped the router flag. |
806 | * Remove the sender from the Default Router List and |
807 | * update the Destination Cache entries. |
808 | */ |
809 | struct nd_defrouter *dr; |
810 | const struct in6_addr *in6; |
811 | int s; |
812 | |
813 | in6 = &ln->r_l3addr.addr6; |
814 | |
815 | /* |
816 | * Lock to protect the default router list. |
817 | * XXX: this might be unnecessary, since this function |
818 | * is only called under the network software interrupt |
819 | * context. However, we keep it just for safety. |
820 | */ |
821 | s = splsoftnet(); |
822 | dr = defrouter_lookup(in6, ln->lle_tbl->llt_ifp); |
823 | if (dr) |
824 | defrtrlist_del(dr, NULL); |
825 | else if (!ip6_forwarding) { |
826 | /* |
827 | * Even if the neighbor is not in the default |
828 | * router list, the neighbor may be used |
829 | * as a next hop for some destinations |
830 | * (e.g. redirect case). So we must |
831 | * call rt6_flush explicitly. |
832 | */ |
833 | rt6_flush(&ip6->ip6_src, ln->lle_tbl->llt_ifp); |
834 | } |
835 | splx(s); |
836 | } |
837 | ln->ln_router = is_router; |
838 | } |
839 | /* |
840 | * XXX: does this matter? |
841 | * rt->rt_flags &= ~RTF_REJECT; |
842 | */ |
843 | ln->ln_asked = 0; |
844 | nd6_llinfo_release_pkts(ln, ifp); |
845 | /* FIXME */ |
846 | #if 0 |
847 | if (rt_announce) /* tell user process about any new lladdr */ |
848 | rt_newmsg(RTM_CHANGE, rt); |
849 | #endif |
850 | |
851 | freeit: |
852 | if (ln != NULL) |
853 | LLE_WUNLOCK(ln); |
854 | |
855 | if (checklink) |
856 | pfxlist_onlink_check(); |
857 | |
858 | m_put_rcvif_psref(ifp, &psref); |
859 | m_freem(m); |
860 | return; |
861 | |
862 | bad: |
863 | if (ln != NULL) |
864 | LLE_WUNLOCK(ln); |
865 | |
866 | ICMP6_STATINC(ICMP6_STAT_BADNA); |
867 | m_put_rcvif_psref(ifp, &psref); |
868 | m_freem(m); |
869 | } |
870 | |
871 | /* |
872 | * Neighbor advertisement output handling. |
873 | * |
874 | * Based on RFC 2461 |
875 | * |
876 | * the following items are not implemented yet: |
877 | * - proxy advertisement delay rule (RFC2461 7.2.8, last paragraph, SHOULD) |
878 | * - anycast advertisement delay rule (RFC2461 7.2.7, SHOULD) |
879 | */ |
880 | void |
881 | nd6_na_output( |
882 | struct ifnet *ifp, |
883 | const struct in6_addr *daddr6_0, |
884 | const struct in6_addr *taddr6, |
885 | u_long flags, |
886 | int tlladdr, /* 1 if include target link-layer address */ |
887 | const struct sockaddr *sdl0) /* sockaddr_dl (= proxy NA) or NULL */ |
888 | { |
889 | struct mbuf *m; |
890 | struct ip6_hdr *ip6; |
891 | struct nd_neighbor_advert *nd_na; |
892 | struct ip6_moptions im6o; |
893 | struct sockaddr *dst; |
894 | union { |
895 | struct sockaddr dst; |
896 | struct sockaddr_in6 dst6; |
897 | } u; |
898 | struct in6_addr daddr6; |
899 | int icmp6len, maxlen, error; |
900 | const void *mac; |
901 | struct route ro; |
902 | |
903 | mac = NULL; |
904 | memset(&ro, 0, sizeof(ro)); |
905 | |
906 | daddr6 = *daddr6_0; /* make a local copy for modification */ |
907 | |
908 | /* estimate the size of message */ |
909 | maxlen = sizeof(*ip6) + sizeof(*nd_na); |
910 | maxlen += (sizeof(struct nd_opt_hdr) + ifp->if_addrlen + 7) & ~7; |
911 | #ifdef DIAGNOSTIC |
912 | if (max_linkhdr + maxlen >= MCLBYTES) { |
913 | printf("nd6_na_output: max_linkhdr + maxlen >= MCLBYTES " |
914 | "(%d + %d > %d)\n" , max_linkhdr, maxlen, MCLBYTES); |
915 | panic("nd6_na_output: insufficient MCLBYTES" ); |
916 | /* NOTREACHED */ |
917 | } |
918 | #endif |
919 | |
920 | MGETHDR(m, M_DONTWAIT, MT_DATA); |
921 | if (m && max_linkhdr + maxlen >= MHLEN) { |
922 | MCLGET(m, M_DONTWAIT); |
923 | if ((m->m_flags & M_EXT) == 0) { |
924 | m_free(m); |
925 | m = NULL; |
926 | } |
927 | } |
928 | if (m == NULL) |
929 | return; |
930 | m_reset_rcvif(m); |
931 | |
932 | if (IN6_IS_ADDR_MULTICAST(&daddr6)) { |
933 | m->m_flags |= M_MCAST; |
934 | im6o.im6o_multicast_if_index = if_get_index(ifp); |
935 | im6o.im6o_multicast_hlim = 255; |
936 | im6o.im6o_multicast_loop = 0; |
937 | } |
938 | |
939 | icmp6len = sizeof(*nd_na); |
940 | m->m_pkthdr.len = m->m_len = sizeof(struct ip6_hdr) + icmp6len; |
941 | m->m_data += max_linkhdr; /* or MH_ALIGN() equivalent? */ |
942 | |
943 | /* fill neighbor advertisement packet */ |
944 | ip6 = mtod(m, struct ip6_hdr *); |
945 | ip6->ip6_flow = 0; |
946 | ip6->ip6_vfc &= ~IPV6_VERSION_MASK; |
947 | ip6->ip6_vfc |= IPV6_VERSION; |
948 | ip6->ip6_nxt = IPPROTO_ICMPV6; |
949 | ip6->ip6_hlim = 255; |
950 | if (IN6_IS_ADDR_UNSPECIFIED(&daddr6)) { |
951 | /* reply to DAD */ |
952 | daddr6.s6_addr16[0] = IPV6_ADDR_INT16_MLL; |
953 | daddr6.s6_addr16[1] = 0; |
954 | daddr6.s6_addr32[1] = 0; |
955 | daddr6.s6_addr32[2] = 0; |
956 | daddr6.s6_addr32[3] = IPV6_ADDR_INT32_ONE; |
957 | if (in6_setscope(&daddr6, ifp, NULL)) |
958 | goto bad; |
959 | |
960 | flags &= ~ND_NA_FLAG_SOLICITED; |
961 | } |
962 | ip6->ip6_dst = daddr6; |
963 | sockaddr_in6_init(&u.dst6, &daddr6, 0, 0, 0); |
964 | dst = &u.dst; |
965 | if (rtcache_setdst(&ro, dst) != 0) |
966 | goto bad; |
967 | |
968 | /* |
969 | * Select a source whose scope is the same as that of the dest. |
970 | */ |
971 | error = in6_selectsrc(satosin6(dst), NULL, NULL, &ro, NULL, NULL, NULL, |
972 | &ip6->ip6_src); |
973 | if (error != 0) { |
974 | nd6log(LOG_DEBUG, "source can't be " |
975 | "determined: dst=%s, error=%d\n" , |
976 | ip6_sprintf(&satocsin6(dst)->sin6_addr), error); |
977 | goto bad; |
978 | } |
979 | nd_na = (struct nd_neighbor_advert *)(ip6 + 1); |
980 | nd_na->nd_na_type = ND_NEIGHBOR_ADVERT; |
981 | nd_na->nd_na_code = 0; |
982 | nd_na->nd_na_target = *taddr6; |
983 | in6_clearscope(&nd_na->nd_na_target); /* XXX */ |
984 | |
985 | /* |
986 | * "tlladdr" indicates NS's condition for adding tlladdr or not. |
987 | * see nd6_ns_input() for details. |
988 | * Basically, if NS packet is sent to unicast/anycast addr, |
989 | * target lladdr option SHOULD NOT be included. |
990 | */ |
991 | if (tlladdr) { |
992 | /* |
993 | * sdl0 != NULL indicates proxy NA. If we do proxy, use |
994 | * lladdr in sdl0. If we are not proxying (sending NA for |
995 | * my address) use lladdr configured for the interface. |
996 | */ |
997 | if (sdl0 == NULL) |
998 | mac = nd6_ifptomac(ifp); |
999 | else if (sdl0->sa_family == AF_LINK) { |
1000 | const struct sockaddr_dl *sdl; |
1001 | sdl = satocsdl(sdl0); |
1002 | if (sdl->sdl_alen == ifp->if_addrlen) |
1003 | mac = CLLADDR(sdl); |
1004 | } |
1005 | } |
1006 | if (tlladdr && mac) { |
1007 | int optlen = sizeof(struct nd_opt_hdr) + ifp->if_addrlen; |
1008 | struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)(nd_na + 1); |
1009 | |
1010 | /* roundup to 8 bytes alignment! */ |
1011 | optlen = (optlen + 7) & ~7; |
1012 | |
1013 | m->m_pkthdr.len += optlen; |
1014 | m->m_len += optlen; |
1015 | icmp6len += optlen; |
1016 | memset((void *)nd_opt, 0, optlen); |
1017 | nd_opt->nd_opt_type = ND_OPT_TARGET_LINKADDR; |
1018 | nd_opt->nd_opt_len = optlen >> 3; |
1019 | memcpy((void *)(nd_opt + 1), mac, ifp->if_addrlen); |
1020 | } else |
1021 | flags &= ~ND_NA_FLAG_OVERRIDE; |
1022 | |
1023 | ip6->ip6_plen = htons((u_int16_t)icmp6len); |
1024 | nd_na->nd_na_flags_reserved = flags; |
1025 | nd_na->nd_na_cksum = 0; |
1026 | nd_na->nd_na_cksum = |
1027 | in6_cksum(m, IPPROTO_ICMPV6, sizeof(struct ip6_hdr), icmp6len); |
1028 | |
1029 | ip6_output(m, NULL, NULL, 0, &im6o, NULL, NULL); |
1030 | |
1031 | icmp6_ifstat_inc(ifp, ifs6_out_msg); |
1032 | icmp6_ifstat_inc(ifp, ifs6_out_neighboradvert); |
1033 | ICMP6_STATINC(ICMP6_STAT_OUTHIST + ND_NEIGHBOR_ADVERT); |
1034 | |
1035 | rtcache_free(&ro); |
1036 | return; |
1037 | |
1038 | bad: |
1039 | rtcache_free(&ro); |
1040 | m_freem(m); |
1041 | return; |
1042 | } |
1043 | |
1044 | const void * |
1045 | nd6_ifptomac(const struct ifnet *ifp) |
1046 | { |
1047 | switch (ifp->if_type) { |
1048 | case IFT_ARCNET: |
1049 | case IFT_ETHER: |
1050 | case IFT_FDDI: |
1051 | case IFT_IEEE1394: |
1052 | case IFT_PROPVIRTUAL: |
1053 | case IFT_CARP: |
1054 | case IFT_L2VLAN: |
1055 | case IFT_IEEE80211: |
1056 | return CLLADDR(ifp->if_sadl); |
1057 | default: |
1058 | return NULL; |
1059 | } |
1060 | } |
1061 | |
1062 | TAILQ_HEAD(dadq_head, dadq); |
1063 | struct dadq { |
1064 | TAILQ_ENTRY(dadq) dad_list; |
1065 | struct ifaddr *dad_ifa; |
1066 | int dad_count; /* max NS to send */ |
1067 | int dad_ns_tcount; /* # of trials to send NS */ |
1068 | int dad_ns_ocount; /* NS sent so far */ |
1069 | int dad_ns_icount; |
1070 | int dad_na_icount; |
1071 | struct callout dad_timer_ch; |
1072 | }; |
1073 | |
1074 | static struct dadq_head dadq; |
1075 | static int dad_init = 0; |
1076 | static kmutex_t nd6_dad_lock; |
1077 | |
1078 | static struct dadq * |
1079 | nd6_dad_find(struct ifaddr *ifa) |
1080 | { |
1081 | struct dadq *dp; |
1082 | |
1083 | KASSERT(mutex_owned(&nd6_dad_lock)); |
1084 | |
1085 | TAILQ_FOREACH(dp, &dadq, dad_list) { |
1086 | if (dp->dad_ifa == ifa) |
1087 | return dp; |
1088 | } |
1089 | return NULL; |
1090 | } |
1091 | |
1092 | static void |
1093 | nd6_dad_starttimer(struct dadq *dp, int ticks) |
1094 | { |
1095 | |
1096 | callout_reset(&dp->dad_timer_ch, ticks, |
1097 | (void (*)(void *))nd6_dad_timer, (void *)dp->dad_ifa); |
1098 | } |
1099 | |
1100 | static void |
1101 | nd6_dad_stoptimer(struct dadq *dp) |
1102 | { |
1103 | |
1104 | #ifdef NET_MPSAFE |
1105 | callout_halt(&dp->dad_timer_ch, NULL); |
1106 | #else |
1107 | callout_halt(&dp->dad_timer_ch, softnet_lock); |
1108 | #endif |
1109 | } |
1110 | |
1111 | /* |
1112 | * Start Duplicate Address Detection (DAD) for specified interface address. |
1113 | * |
1114 | * Note that callout is used when xtick > 0 and not when xtick == 0. |
1115 | * |
1116 | * xtick: minimum delay ticks for IFF_UP event |
1117 | */ |
1118 | void |
1119 | nd6_dad_start(struct ifaddr *ifa, int xtick) |
1120 | { |
1121 | struct in6_ifaddr *ia = (struct in6_ifaddr *)ifa; |
1122 | struct dadq *dp; |
1123 | |
1124 | if (!dad_init) { |
1125 | TAILQ_INIT(&dadq); |
1126 | mutex_init(&nd6_dad_lock, MUTEX_DEFAULT, IPL_NONE); |
1127 | dad_init++; |
1128 | } |
1129 | |
1130 | /* |
1131 | * If we don't need DAD, don't do it. |
1132 | * There are several cases: |
1133 | * - DAD is disabled (ip6_dad_count == 0) |
1134 | * - the interface address is anycast |
1135 | */ |
1136 | if (!(ia->ia6_flags & IN6_IFF_TENTATIVE)) { |
1137 | log(LOG_DEBUG, |
1138 | "nd6_dad_start: called with non-tentative address " |
1139 | "%s(%s)\n" , |
1140 | ip6_sprintf(&ia->ia_addr.sin6_addr), |
1141 | ifa->ifa_ifp ? if_name(ifa->ifa_ifp) : "???" ); |
1142 | return; |
1143 | } |
1144 | if (ia->ia6_flags & IN6_IFF_ANYCAST || !ip6_dad_count) { |
1145 | ia->ia6_flags &= ~IN6_IFF_TENTATIVE; |
1146 | rt_newaddrmsg(RTM_NEWADDR, ifa, 0, NULL); |
1147 | return; |
1148 | } |
1149 | KASSERT(ifa->ifa_ifp != NULL); |
1150 | if (!(ifa->ifa_ifp->if_flags & IFF_UP)) |
1151 | return; |
1152 | |
1153 | mutex_enter(&nd6_dad_lock); |
1154 | if (nd6_dad_find(ifa) != NULL) { |
1155 | mutex_exit(&nd6_dad_lock); |
1156 | /* DAD already in progress */ |
1157 | return; |
1158 | } |
1159 | |
1160 | dp = malloc(sizeof(*dp), M_IP6NDP, M_NOWAIT); |
1161 | if (dp == NULL) { |
1162 | mutex_exit(&nd6_dad_lock); |
1163 | log(LOG_ERR, "nd6_dad_start: memory allocation failed for " |
1164 | "%s(%s)\n" , |
1165 | ip6_sprintf(&ia->ia_addr.sin6_addr), |
1166 | ifa->ifa_ifp ? if_name(ifa->ifa_ifp) : "???" ); |
1167 | return; |
1168 | } |
1169 | memset(dp, 0, sizeof(*dp)); |
1170 | callout_init(&dp->dad_timer_ch, CALLOUT_MPSAFE); |
1171 | |
1172 | /* |
1173 | * Send NS packet for DAD, ip6_dad_count times. |
1174 | * Note that we must delay the first transmission, if this is the |
1175 | * first packet to be sent from the interface after interface |
1176 | * (re)initialization. |
1177 | */ |
1178 | dp->dad_ifa = ifa; |
1179 | ifaref(ifa); /* just for safety */ |
1180 | dp->dad_count = ip6_dad_count; |
1181 | dp->dad_ns_icount = dp->dad_na_icount = 0; |
1182 | dp->dad_ns_ocount = dp->dad_ns_tcount = 0; |
1183 | TAILQ_INSERT_TAIL(&dadq, (struct dadq *)dp, dad_list); |
1184 | |
1185 | nd6log(LOG_DEBUG, "%s: starting DAD for %s\n" , if_name(ifa->ifa_ifp), |
1186 | ip6_sprintf(&ia->ia_addr.sin6_addr)); |
1187 | |
1188 | if (xtick == 0) { |
1189 | nd6_dad_ns_output(dp, ifa); |
1190 | nd6_dad_starttimer(dp, |
1191 | (long)ND_IFINFO(ifa->ifa_ifp)->retrans * hz / 1000); |
1192 | } else |
1193 | nd6_dad_starttimer(dp, xtick); |
1194 | mutex_exit(&nd6_dad_lock); |
1195 | } |
1196 | |
1197 | /* |
1198 | * terminate DAD unconditionally. used for address removals. |
1199 | */ |
1200 | void |
1201 | nd6_dad_stop(struct ifaddr *ifa) |
1202 | { |
1203 | struct dadq *dp; |
1204 | |
1205 | if (!dad_init) |
1206 | return; |
1207 | |
1208 | mutex_enter(&nd6_dad_lock); |
1209 | dp = nd6_dad_find(ifa); |
1210 | if (dp == NULL) { |
1211 | mutex_exit(&nd6_dad_lock); |
1212 | /* DAD wasn't started yet */ |
1213 | return; |
1214 | } |
1215 | |
1216 | /* Prevent the timer from running anymore. */ |
1217 | TAILQ_REMOVE(&dadq, dp, dad_list); |
1218 | mutex_exit(&nd6_dad_lock); |
1219 | |
1220 | nd6_dad_stoptimer(dp); |
1221 | |
1222 | free(dp, M_IP6NDP); |
1223 | dp = NULL; |
1224 | ifafree(ifa); |
1225 | } |
1226 | |
1227 | static void |
1228 | nd6_dad_timer(struct ifaddr *ifa) |
1229 | { |
1230 | struct in6_ifaddr *ia = (struct in6_ifaddr *)ifa; |
1231 | struct dadq *dp; |
1232 | int duplicate = 0; |
1233 | |
1234 | #ifndef NET_MPSAFE |
1235 | mutex_enter(softnet_lock); |
1236 | KERNEL_LOCK(1, NULL); |
1237 | #endif |
1238 | mutex_enter(&nd6_dad_lock); |
1239 | |
1240 | /* Sanity check */ |
1241 | if (ia == NULL) { |
1242 | log(LOG_ERR, "nd6_dad_timer: called with null parameter\n" ); |
1243 | goto done; |
1244 | } |
1245 | dp = nd6_dad_find(ifa); |
1246 | if (dp == NULL) { |
1247 | /* DAD seems to be stopping, so do nothing. */ |
1248 | goto done; |
1249 | } |
1250 | if (ia->ia6_flags & IN6_IFF_DUPLICATED) { |
1251 | log(LOG_ERR, "nd6_dad_timer: called with duplicate address " |
1252 | "%s(%s)\n" , |
1253 | ip6_sprintf(&ia->ia_addr.sin6_addr), |
1254 | ifa->ifa_ifp ? if_name(ifa->ifa_ifp) : "???" ); |
1255 | goto done; |
1256 | } |
1257 | if ((ia->ia6_flags & IN6_IFF_TENTATIVE) == 0) { |
1258 | log(LOG_ERR, "nd6_dad_timer: called with non-tentative address " |
1259 | "%s(%s)\n" , |
1260 | ip6_sprintf(&ia->ia_addr.sin6_addr), |
1261 | ifa->ifa_ifp ? if_name(ifa->ifa_ifp) : "???" ); |
1262 | goto done; |
1263 | } |
1264 | |
1265 | /* timeouted with IFF_{RUNNING,UP} check */ |
1266 | if (dp->dad_ns_tcount > dad_maxtry) { |
1267 | nd6log(LOG_INFO, "%s: could not run DAD, driver problem?\n" , |
1268 | if_name(ifa->ifa_ifp)); |
1269 | |
1270 | TAILQ_REMOVE(&dadq, dp, dad_list); |
1271 | free(dp, M_IP6NDP); |
1272 | dp = NULL; |
1273 | ifafree(ifa); |
1274 | goto done; |
1275 | } |
1276 | |
1277 | /* Need more checks? */ |
1278 | if (dp->dad_ns_ocount < dp->dad_count) { |
1279 | /* |
1280 | * We have more NS to go. Send NS packet for DAD. |
1281 | */ |
1282 | nd6_dad_ns_output(dp, ifa); |
1283 | nd6_dad_starttimer(dp, |
1284 | (long)ND_IFINFO(ifa->ifa_ifp)->retrans * hz / 1000); |
1285 | } else { |
1286 | /* |
1287 | * We have transmitted sufficient number of DAD packets. |
1288 | * See what we've got. |
1289 | */ |
1290 | if (dp->dad_na_icount) { |
1291 | /* |
1292 | * the check is in nd6_dad_na_input(), |
1293 | * but just in case |
1294 | */ |
1295 | duplicate++; |
1296 | } |
1297 | |
1298 | if (dp->dad_ns_icount) { |
1299 | /* We've seen NS, means DAD has failed. */ |
1300 | duplicate++; |
1301 | } |
1302 | |
1303 | if (duplicate) { |
1304 | /* (*dp) will be freed in nd6_dad_duplicated() */ |
1305 | dp = NULL; |
1306 | } else { |
1307 | /* |
1308 | * We are done with DAD. No NA came, no NS came. |
1309 | * No duplicate address found. |
1310 | */ |
1311 | ia->ia6_flags &= ~IN6_IFF_TENTATIVE; |
1312 | rt_newaddrmsg(RTM_NEWADDR, ifa, 0, NULL); |
1313 | |
1314 | nd6log(LOG_DEBUG, |
1315 | "%s: DAD complete for %s - no duplicates found\n" , |
1316 | if_name(ifa->ifa_ifp), |
1317 | ip6_sprintf(&ia->ia_addr.sin6_addr)); |
1318 | |
1319 | TAILQ_REMOVE(&dadq, dp, dad_list); |
1320 | free(dp, M_IP6NDP); |
1321 | dp = NULL; |
1322 | ifafree(ifa); |
1323 | } |
1324 | } |
1325 | |
1326 | done: |
1327 | mutex_exit(&nd6_dad_lock); |
1328 | |
1329 | if (duplicate) |
1330 | nd6_dad_duplicated(ifa); |
1331 | |
1332 | #ifndef NET_MPSAFE |
1333 | KERNEL_UNLOCK_ONE(NULL); |
1334 | mutex_exit(softnet_lock); |
1335 | #endif |
1336 | } |
1337 | |
1338 | void |
1339 | nd6_dad_duplicated(struct ifaddr *ifa) |
1340 | { |
1341 | struct in6_ifaddr *ia = (struct in6_ifaddr *)ifa; |
1342 | struct ifnet *ifp; |
1343 | struct dadq *dp; |
1344 | |
1345 | mutex_enter(&nd6_dad_lock); |
1346 | dp = nd6_dad_find(ifa); |
1347 | if (dp == NULL) { |
1348 | mutex_exit(&nd6_dad_lock); |
1349 | /* DAD seems to be stopping, so do nothing. */ |
1350 | return; |
1351 | } |
1352 | |
1353 | ifp = ifa->ifa_ifp; |
1354 | log(LOG_ERR, "%s: DAD detected duplicate IPv6 address %s: " |
1355 | "NS in/out=%d/%d, NA in=%d\n" , |
1356 | if_name(ifp), ip6_sprintf(&ia->ia_addr.sin6_addr), |
1357 | dp->dad_ns_icount, dp->dad_ns_ocount, dp->dad_na_icount); |
1358 | |
1359 | ia->ia6_flags &= ~IN6_IFF_TENTATIVE; |
1360 | ia->ia6_flags |= IN6_IFF_DUPLICATED; |
1361 | |
1362 | /* We are done with DAD, with duplicated address found. (failure) */ |
1363 | nd6_dad_stoptimer(dp); |
1364 | |
1365 | log(LOG_ERR, "%s: DAD complete for %s - duplicate found\n" , |
1366 | if_name(ifp), ip6_sprintf(&ia->ia_addr.sin6_addr)); |
1367 | log(LOG_ERR, "%s: manual intervention required\n" , |
1368 | if_name(ifp)); |
1369 | |
1370 | /* Inform the routing socket that DAD has completed */ |
1371 | rt_newaddrmsg(RTM_NEWADDR, ifa, 0, NULL); |
1372 | |
1373 | /* |
1374 | * If the address is a link-local address formed from an interface |
1375 | * identifier based on the hardware address which is supposed to be |
1376 | * uniquely assigned (e.g., EUI-64 for an Ethernet interface), IP |
1377 | * operation on the interface SHOULD be disabled. |
1378 | * [rfc2462bis-03 Section 5.4.5] |
1379 | */ |
1380 | if (IN6_IS_ADDR_LINKLOCAL(&ia->ia_addr.sin6_addr)) { |
1381 | struct in6_addr in6; |
1382 | |
1383 | /* |
1384 | * To avoid over-reaction, we only apply this logic when we are |
1385 | * very sure that hardware addresses are supposed to be unique. |
1386 | */ |
1387 | switch (ifp->if_type) { |
1388 | case IFT_ETHER: |
1389 | case IFT_FDDI: |
1390 | case IFT_ATM: |
1391 | case IFT_IEEE1394: |
1392 | #ifdef IFT_IEEE80211 |
1393 | case IFT_IEEE80211: |
1394 | #endif |
1395 | in6 = ia->ia_addr.sin6_addr; |
1396 | if (in6_get_hw_ifid(ifp, &in6) == 0 && |
1397 | IN6_ARE_ADDR_EQUAL(&ia->ia_addr.sin6_addr, &in6)) { |
1398 | ND_IFINFO(ifp)->flags |= ND6_IFF_IFDISABLED; |
1399 | log(LOG_ERR, "%s: possible hardware address " |
1400 | "duplication detected, disable IPv6\n" , |
1401 | if_name(ifp)); |
1402 | } |
1403 | break; |
1404 | } |
1405 | } |
1406 | |
1407 | TAILQ_REMOVE(&dadq, dp, dad_list); |
1408 | mutex_exit(&nd6_dad_lock); |
1409 | |
1410 | free(dp, M_IP6NDP); |
1411 | dp = NULL; |
1412 | ifafree(ifa); |
1413 | } |
1414 | |
1415 | static void |
1416 | nd6_dad_ns_output(struct dadq *dp, struct ifaddr *ifa) |
1417 | { |
1418 | struct in6_ifaddr *ia = (struct in6_ifaddr *)ifa; |
1419 | struct ifnet *ifp = ifa->ifa_ifp; |
1420 | |
1421 | dp->dad_ns_tcount++; |
1422 | if ((ifp->if_flags & IFF_UP) == 0) { |
1423 | #if 0 |
1424 | printf("%s: interface down?\n" , if_name(ifp)); |
1425 | #endif |
1426 | return; |
1427 | } |
1428 | if ((ifp->if_flags & IFF_RUNNING) == 0) { |
1429 | #if 0 |
1430 | printf("%s: interface not running?\n" , if_name(ifp)); |
1431 | #endif |
1432 | return; |
1433 | } |
1434 | |
1435 | dp->dad_ns_tcount = 0; |
1436 | dp->dad_ns_ocount++; |
1437 | nd6_ns_output(ifp, NULL, &ia->ia_addr.sin6_addr, NULL, 1); |
1438 | } |
1439 | |
1440 | static void |
1441 | nd6_dad_ns_input(struct ifaddr *ifa) |
1442 | { |
1443 | struct in6_ifaddr *ia; |
1444 | const struct in6_addr *taddr6; |
1445 | struct dadq *dp; |
1446 | int duplicate; |
1447 | |
1448 | if (ifa == NULL) |
1449 | panic("ifa == NULL in nd6_dad_ns_input" ); |
1450 | |
1451 | ia = (struct in6_ifaddr *)ifa; |
1452 | taddr6 = &ia->ia_addr.sin6_addr; |
1453 | duplicate = 0; |
1454 | |
1455 | mutex_enter(&nd6_dad_lock); |
1456 | dp = nd6_dad_find(ifa); |
1457 | |
1458 | /* Quickhack - completely ignore DAD NS packets */ |
1459 | if (dad_ignore_ns) { |
1460 | nd6log(LOG_INFO, "ignoring DAD NS packet for " |
1461 | "address %s(%s)\n" , ip6_sprintf(taddr6), |
1462 | if_name(ifa->ifa_ifp)); |
1463 | return; |
1464 | } |
1465 | |
1466 | /* |
1467 | * if I'm yet to start DAD, someone else started using this address |
1468 | * first. I have a duplicate and you win. |
1469 | */ |
1470 | if (dp == NULL || dp->dad_ns_ocount == 0) |
1471 | duplicate++; |
1472 | |
1473 | /* XXX more checks for loopback situation - see nd6_dad_timer too */ |
1474 | |
1475 | if (duplicate) { |
1476 | dp = NULL; /* will be freed in nd6_dad_duplicated() */ |
1477 | mutex_exit(&nd6_dad_lock); |
1478 | nd6_dad_duplicated(ifa); |
1479 | } else { |
1480 | /* |
1481 | * not sure if I got a duplicate. |
1482 | * increment ns count and see what happens. |
1483 | */ |
1484 | if (dp) |
1485 | dp->dad_ns_icount++; |
1486 | mutex_exit(&nd6_dad_lock); |
1487 | } |
1488 | } |
1489 | |
1490 | static void |
1491 | nd6_dad_na_input(struct ifaddr *ifa) |
1492 | { |
1493 | struct dadq *dp; |
1494 | |
1495 | if (ifa == NULL) |
1496 | panic("ifa == NULL in nd6_dad_na_input" ); |
1497 | |
1498 | mutex_enter(&nd6_dad_lock); |
1499 | dp = nd6_dad_find(ifa); |
1500 | if (dp) |
1501 | dp->dad_na_icount++; |
1502 | mutex_exit(&nd6_dad_lock); |
1503 | |
1504 | /* remove the address. */ |
1505 | nd6_dad_duplicated(ifa); |
1506 | } |
1507 | |