nd6_nbr.c revision 151465
162587Sitojun/* $FreeBSD: head/sys/netinet6/nd6_nbr.c 151465 2005-10-19 10:09:19Z suz $ */ 295023Ssuz/* $KAME: nd6_nbr.c,v 1.86 2002/01/21 02:33:04 jinmei Exp $ */ 362587Sitojun 4139826Simp/*- 553541Sshin * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 653541Sshin * All rights reserved. 753541Sshin * 853541Sshin * Redistribution and use in source and binary forms, with or without 953541Sshin * modification, are permitted provided that the following conditions 1053541Sshin * are met: 1153541Sshin * 1. Redistributions of source code must retain the above copyright 1253541Sshin * notice, this list of conditions and the following disclaimer. 1353541Sshin * 2. Redistributions in binary form must reproduce the above copyright 1453541Sshin * notice, this list of conditions and the following disclaimer in the 1553541Sshin * documentation and/or other materials provided with the distribution. 1653541Sshin * 3. Neither the name of the project nor the names of its contributors 1753541Sshin * may be used to endorse or promote products derived from this software 1853541Sshin * without specific prior written permission. 1953541Sshin * 2053541Sshin * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 2153541Sshin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2253541Sshin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2353541Sshin * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 2453541Sshin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2553541Sshin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2653541Sshin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2753541Sshin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2853541Sshin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2953541Sshin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3053541Sshin * SUCH DAMAGE. 3153541Sshin */ 3253541Sshin 3362587Sitojun#include "opt_inet.h" 3462587Sitojun#include "opt_inet6.h" 35142215Sglebius#include "opt_ipsec.h" 36142215Sglebius#include "opt_carp.h" 3755009Sshin 3853541Sshin#include <sys/param.h> 3953541Sshin#include <sys/systm.h> 4053541Sshin#include <sys/malloc.h> 4153541Sshin#include <sys/mbuf.h> 4253541Sshin#include <sys/socket.h> 4353541Sshin#include <sys/sockio.h> 4453541Sshin#include <sys/time.h> 4553541Sshin#include <sys/kernel.h> 4653541Sshin#include <sys/errno.h> 4753541Sshin#include <sys/syslog.h> 4853541Sshin#include <sys/queue.h> 4978064Sume#include <sys/callout.h> 5053541Sshin 5153541Sshin#include <net/if.h> 5253541Sshin#include <net/if_types.h> 5353541Sshin#include <net/if_dl.h> 54147306Sbrooks#include <net/if_var.h> 5553541Sshin#include <net/route.h> 5653541Sshin 5753541Sshin#include <netinet/in.h> 5853541Sshin#include <netinet/in_var.h> 5953541Sshin#include <netinet6/in6_var.h> 6062587Sitojun#include <netinet/ip6.h> 6153541Sshin#include <netinet6/ip6_var.h> 62148385Sume#include <netinet6/scope6_var.h> 6353541Sshin#include <netinet6/nd6.h> 6462587Sitojun#include <netinet/icmp6.h> 6553541Sshin 66142215Sglebius#ifdef DEV_CARP 67142215Sglebius#include <netinet/ip_carp.h> 68142215Sglebius#endif 69142215Sglebius 7053541Sshin#include <net/net_osdep.h> 7153541Sshin 7262587Sitojun#define SDL(s) ((struct sockaddr_dl *)s) 7353541Sshin 7462587Sitojunstruct dadq; 7562587Sitojunstatic struct dadq *nd6_dad_find __P((struct ifaddr *)); 7678064Sumestatic void nd6_dad_starttimer __P((struct dadq *, int)); 7778064Sumestatic void nd6_dad_stoptimer __P((struct dadq *)); 7862587Sitojunstatic void nd6_dad_timer __P((struct ifaddr *)); 7962587Sitojunstatic void nd6_dad_ns_output __P((struct dadq *, struct ifaddr *)); 8062587Sitojunstatic void nd6_dad_ns_input __P((struct ifaddr *)); 8162587Sitojunstatic void nd6_dad_na_input __P((struct ifaddr *)); 8253541Sshin 8362587Sitojunstatic int dad_ignore_ns = 0; /* ignore NS in DAD - specwise incorrect*/ 8462587Sitojunstatic int dad_maxtry = 15; /* max # of *tries* to transmit DAD packet */ 8553541Sshin 8653541Sshin/* 87108470Sschweikh * Input a Neighbor Solicitation Message. 8853541Sshin * 8953541Sshin * Based on RFC 2461 90148987Sume * Based on RFC 2462 (duplicate address detection) 9153541Sshin */ 9253541Sshinvoid 9353541Sshinnd6_ns_input(m, off, icmp6len) 9453541Sshin struct mbuf *m; 9553541Sshin int off, icmp6len; 9653541Sshin{ 9753541Sshin struct ifnet *ifp = m->m_pkthdr.rcvif; 9853541Sshin struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); 9962587Sitojun struct nd_neighbor_solicit *nd_ns; 10053541Sshin struct in6_addr saddr6 = ip6->ip6_src; 10153541Sshin struct in6_addr daddr6 = ip6->ip6_dst; 10262587Sitojun struct in6_addr taddr6; 10353541Sshin struct in6_addr myaddr6; 10453541Sshin char *lladdr = NULL; 105142215Sglebius struct ifaddr *ifa = NULL; 10653541Sshin int lladdrlen = 0; 10753541Sshin int anycast = 0, proxy = 0, tentative = 0; 10853541Sshin int tlladdr; 10953541Sshin union nd_opts ndopts; 11062587Sitojun struct sockaddr_dl *proxydl = NULL; 11153541Sshin 11278064Sume#ifndef PULLDOWN_TEST 11378064Sume IP6_EXTHDR_CHECK(m, off, icmp6len,); 11478064Sume nd_ns = (struct nd_neighbor_solicit *)((caddr_t)ip6 + off); 11578064Sume#else 11678064Sume IP6_EXTHDR_GET(nd_ns, struct nd_neighbor_solicit *, m, off, icmp6len); 11778064Sume if (nd_ns == NULL) { 11878064Sume icmp6stat.icp6s_tooshort++; 11978064Sume return; 12078064Sume } 12178064Sume#endif 12278064Sume ip6 = mtod(m, struct ip6_hdr *); /* adjust pointer for safety */ 12378064Sume taddr6 = nd_ns->nd_ns_target; 124148385Sume if (in6_setscope(&taddr6, ifp, NULL) != 0) 125148385Sume goto bad; 12678064Sume 12753541Sshin if (ip6->ip6_hlim != 255) { 12878064Sume nd6log((LOG_ERR, 12978064Sume "nd6_ns_input: invalid hlim (%d) from %s to %s on %s\n", 13078064Sume ip6->ip6_hlim, ip6_sprintf(&ip6->ip6_src), 13178064Sume ip6_sprintf(&ip6->ip6_dst), if_name(ifp))); 13278064Sume goto bad; 13353541Sshin } 13453541Sshin 13553541Sshin if (IN6_IS_ADDR_UNSPECIFIED(&saddr6)) { 136148987Sume /* dst has to be a solicited node multicast address. */ 137120941Sume if (daddr6.s6_addr16[0] == IPV6_ADDR_INT16_MLL && 13895023Ssuz /* don't check ifindex portion */ 139120941Sume daddr6.s6_addr32[1] == 0 && 140120941Sume daddr6.s6_addr32[2] == IPV6_ADDR_INT32_ONE && 141120941Sume daddr6.s6_addr8[12] == 0xff) { 14295023Ssuz ; /* good */ 14353541Sshin } else { 14478064Sume nd6log((LOG_INFO, "nd6_ns_input: bad DAD packet " 145120941Sume "(wrong ip6 dst)\n")); 14653541Sshin goto bad; 14753541Sshin } 14853541Sshin } 14953541Sshin 15053541Sshin if (IN6_IS_ADDR_MULTICAST(&taddr6)) { 15178064Sume nd6log((LOG_INFO, "nd6_ns_input: bad NS target (multicast)\n")); 15253541Sshin goto bad; 15353541Sshin } 15453541Sshin 15553541Sshin icmp6len -= sizeof(*nd_ns); 15653541Sshin nd6_option_init(nd_ns + 1, icmp6len, &ndopts); 15753541Sshin if (nd6_options(&ndopts) < 0) { 15878064Sume nd6log((LOG_INFO, 15978064Sume "nd6_ns_input: invalid ND option, ignored\n")); 16078064Sume /* nd6_options have incremented stats */ 16178064Sume goto freeit; 16253541Sshin } 16353541Sshin 16453541Sshin if (ndopts.nd_opts_src_lladdr) { 16595023Ssuz lladdr = (char *)(ndopts.nd_opts_src_lladdr + 1); 16653541Sshin lladdrlen = ndopts.nd_opts_src_lladdr->nd_opt_len << 3; 16753541Sshin } 168120941Sume 16953541Sshin if (IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src) && lladdr) { 17078064Sume nd6log((LOG_INFO, "nd6_ns_input: bad DAD packet " 17178064Sume "(link-layer address option)\n")); 17253541Sshin goto bad; 17353541Sshin } 17453541Sshin 17553541Sshin /* 17653541Sshin * Attaching target link-layer address to the NA? 17753541Sshin * (RFC 2461 7.2.4) 17853541Sshin * 17953541Sshin * NS IP dst is unicast/anycast MUST NOT add 18053541Sshin * NS IP dst is solicited-node multicast MUST add 18153541Sshin * 18253541Sshin * In implementation, we add target link-layer address by default. 18353541Sshin * We do not add one in MUST NOT cases. 18453541Sshin */ 18562587Sitojun#if 0 /* too much! */ 18662587Sitojun ifa = (struct ifaddr *)in6ifa_ifpwithaddr(ifp, &daddr6); 18762587Sitojun if (ifa && (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_ANYCAST)) 18862587Sitojun tlladdr = 0; 18962587Sitojun else 19062587Sitojun#endif 19153541Sshin if (!IN6_IS_ADDR_MULTICAST(&daddr6)) 19253541Sshin tlladdr = 0; 19353541Sshin else 19453541Sshin tlladdr = 1; 19553541Sshin 19653541Sshin /* 19753541Sshin * Target address (taddr6) must be either: 19853541Sshin * (1) Valid unicast/anycast address for my receiving interface, 19953541Sshin * (2) Unicast address for which I'm offering proxy service, or 20053541Sshin * (3) "tentative" address on which DAD is being performed. 20153541Sshin */ 20253541Sshin /* (1) and (3) check. */ 203142215Sglebius#ifdef DEV_CARP 204142215Sglebius if (ifp->if_carp) 205142215Sglebius ifa = carp_iamatch6(ifp->if_carp, &taddr6); 206151465Ssuz if (ifa == NULL) 207142215Sglebius ifa = (struct ifaddr *)in6ifa_ifpwithaddr(ifp, &taddr6); 208142215Sglebius#else 20953541Sshin ifa = (struct ifaddr *)in6ifa_ifpwithaddr(ifp, &taddr6); 210142215Sglebius#endif 21153541Sshin 21253541Sshin /* (2) check. */ 213151465Ssuz if (ifa == NULL) { 21453541Sshin struct rtentry *rt; 21553541Sshin struct sockaddr_in6 tsin6; 216121765Ssam int need_proxy; 21753541Sshin 21853541Sshin bzero(&tsin6, sizeof tsin6); 21953541Sshin tsin6.sin6_len = sizeof(struct sockaddr_in6); 22053541Sshin tsin6.sin6_family = AF_INET6; 22153541Sshin tsin6.sin6_addr = taddr6; 22253541Sshin 22353541Sshin rt = rtalloc1((struct sockaddr *)&tsin6, 0, 0); 224121765Ssam need_proxy = (rt && (rt->rt_flags & RTF_ANNOUNCE) != 0 && 225121765Ssam rt->rt_gateway->sa_family == AF_LINK); 226121765Ssam if (rt) 227121765Ssam rtfree(rt); 228121765Ssam if (need_proxy) { 22953541Sshin /* 23062587Sitojun * proxy NDP for single entry 23153541Sshin */ 23262587Sitojun ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp, 23362587Sitojun IN6_IFF_NOTREADY|IN6_IFF_ANYCAST); 23462587Sitojun if (ifa) { 23553541Sshin proxy = 1; 23662587Sitojun proxydl = SDL(rt->rt_gateway); 23762587Sitojun } 23853541Sshin } 23953541Sshin } 24053541Sshin if (!ifa) { 24153541Sshin /* 24278064Sume * We've got an NS packet, and we don't have that adddress 24353541Sshin * assigned for us. We MUST silently ignore it. 24453541Sshin * See RFC2461 7.2.3. 24553541Sshin */ 24662587Sitojun goto freeit; 24753541Sshin } 24853541Sshin myaddr6 = *IFA_IN6(ifa); 24953541Sshin anycast = ((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_ANYCAST; 25053541Sshin tentative = ((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_TENTATIVE; 25153541Sshin if (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_DUPLICATED) 25262587Sitojun goto freeit; 25353541Sshin 25453541Sshin if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) { 255120941Sume nd6log((LOG_INFO, "nd6_ns_input: lladdrlen mismatch for %s " 25653541Sshin "(if %d, NS packet %d)\n", 257120941Sume ip6_sprintf(&taddr6), 258120941Sume ifp->if_addrlen, lladdrlen - 2)); 25978064Sume goto bad; 26053541Sshin } 26153541Sshin 26253541Sshin if (IN6_ARE_ADDR_EQUAL(&myaddr6, &saddr6)) { 263120941Sume nd6log((LOG_INFO, "nd6_ns_input: duplicate IP6 address %s\n", 264120941Sume ip6_sprintf(&saddr6))); 26562587Sitojun goto freeit; 26653541Sshin } 26753541Sshin 26853541Sshin /* 26953541Sshin * We have neighbor solicitation packet, with target address equals to 27053541Sshin * one of my tentative address. 27153541Sshin * 27253541Sshin * src addr how to process? 27353541Sshin * --- --- 27453541Sshin * multicast of course, invalid (rejected in ip6_input) 27553541Sshin * unicast somebody is doing address resolution -> ignore 27653541Sshin * unspec dup address detection 27753541Sshin * 27853541Sshin * The processing is defined in RFC 2462. 27953541Sshin */ 28053541Sshin if (tentative) { 28153541Sshin /* 28253541Sshin * If source address is unspecified address, it is for 283148987Sume * duplicate address detection. 28453541Sshin * 28553541Sshin * If not, the packet is for addess resolution; 28653541Sshin * silently ignore it. 28753541Sshin */ 28853541Sshin if (IN6_IS_ADDR_UNSPECIFIED(&saddr6)) 28953541Sshin nd6_dad_ns_input(ifa); 29053541Sshin 29162587Sitojun goto freeit; 29253541Sshin } 29353541Sshin 29453541Sshin /* 29553541Sshin * If the source address is unspecified address, entries must not 29653541Sshin * be created or updated. 29753541Sshin * It looks that sender is performing DAD. Output NA toward 29853541Sshin * all-node multicast address, to tell the sender that I'm using 29953541Sshin * the address. 30053541Sshin * S bit ("solicited") must be zero. 30153541Sshin */ 30253541Sshin if (IN6_IS_ADDR_UNSPECIFIED(&saddr6)) { 303148385Sume struct in6_addr in6_all; 304148385Sume 305148385Sume in6_all = in6addr_linklocal_allnodes; 306148385Sume if (in6_setscope(&in6_all, ifp, NULL) != 0) 307148385Sume goto bad; 308148385Sume nd6_na_output(ifp, &in6_all, &taddr6, 309120941Sume ((anycast || proxy || !tlladdr) ? 0 : ND_NA_FLAG_OVERRIDE) | 310120941Sume (ip6_forwarding ? ND_NA_FLAG_ROUTER : 0), 311120941Sume tlladdr, (struct sockaddr *)proxydl); 31262587Sitojun goto freeit; 31353541Sshin } 31453541Sshin 315120941Sume nd6_cache_lladdr(ifp, &saddr6, lladdr, lladdrlen, 316120941Sume ND_NEIGHBOR_SOLICIT, 0); 31753541Sshin 31853541Sshin nd6_na_output(ifp, &saddr6, &taddr6, 319120941Sume ((anycast || proxy || !tlladdr) ? 0 : ND_NA_FLAG_OVERRIDE) | 320120941Sume (ip6_forwarding ? ND_NA_FLAG_ROUTER : 0) | ND_NA_FLAG_SOLICITED, 321120941Sume tlladdr, (struct sockaddr *)proxydl); 32262587Sitojun freeit: 32362587Sitojun m_freem(m); 32453541Sshin return; 32553541Sshin 32653541Sshin bad: 32778064Sume nd6log((LOG_ERR, "nd6_ns_input: src=%s\n", ip6_sprintf(&saddr6))); 32878064Sume nd6log((LOG_ERR, "nd6_ns_input: dst=%s\n", ip6_sprintf(&daddr6))); 32978064Sume nd6log((LOG_ERR, "nd6_ns_input: tgt=%s\n", ip6_sprintf(&taddr6))); 33078064Sume icmp6stat.icp6s_badns++; 33162587Sitojun m_freem(m); 33253541Sshin} 33353541Sshin 33453541Sshin/* 335108470Sschweikh * Output a Neighbor Solicitation Message. Caller specifies: 33653541Sshin * - ICMP6 header source IP6 address 33753541Sshin * - ND6 header target IP6 address 33853541Sshin * - ND6 header source datalink address 33953541Sshin * 34053541Sshin * Based on RFC 2461 341148987Sume * Based on RFC 2462 (duplicate address detection) 34253541Sshin */ 34353541Sshinvoid 34453541Sshinnd6_ns_output(ifp, daddr6, taddr6, ln, dad) 34553541Sshin struct ifnet *ifp; 34678064Sume const struct in6_addr *daddr6, *taddr6; 34753541Sshin struct llinfo_nd6 *ln; /* for source address determination */ 348148987Sume int dad; /* duplicate address detection */ 34953541Sshin{ 35053541Sshin struct mbuf *m; 35153541Sshin struct ip6_hdr *ip6; 35253541Sshin struct nd_neighbor_solicit *nd_ns; 353148385Sume struct in6_addr *src, src_in; 35453541Sshin struct ip6_moptions im6o; 35553541Sshin int icmp6len; 35662587Sitojun int maxlen; 35753541Sshin caddr_t mac; 358148385Sume struct route_in6 ro; 359120941Sume 360148385Sume bzero(&ro, sizeof(ro)); 361148385Sume 36253541Sshin if (IN6_IS_ADDR_MULTICAST(taddr6)) 36353541Sshin return; 36453541Sshin 36562587Sitojun /* estimate the size of message */ 36662587Sitojun maxlen = sizeof(*ip6) + sizeof(*nd_ns); 36762587Sitojun maxlen += (sizeof(struct nd_opt_hdr) + ifp->if_addrlen + 7) & ~7; 36862587Sitojun if (max_linkhdr + maxlen >= MCLBYTES) { 36962587Sitojun#ifdef DIAGNOSTIC 37062587Sitojun printf("nd6_ns_output: max_linkhdr + maxlen >= MCLBYTES " 37162587Sitojun "(%d + %d > %d)\n", max_linkhdr, maxlen, MCLBYTES); 37262587Sitojun#endif 37353541Sshin return; 37462587Sitojun } 37553541Sshin 376111119Simp MGETHDR(m, M_DONTWAIT, MT_DATA); 37762587Sitojun if (m && max_linkhdr + maxlen >= MHLEN) { 378111119Simp MCLGET(m, M_DONTWAIT); 37962587Sitojun if ((m->m_flags & M_EXT) == 0) { 38062587Sitojun m_free(m); 38162587Sitojun m = NULL; 38262587Sitojun } 38362587Sitojun } 38462587Sitojun if (m == NULL) 38562587Sitojun return; 38678064Sume m->m_pkthdr.rcvif = NULL; 38762587Sitojun 38853541Sshin if (daddr6 == NULL || IN6_IS_ADDR_MULTICAST(daddr6)) { 38953541Sshin m->m_flags |= M_MCAST; 39053541Sshin im6o.im6o_multicast_ifp = ifp; 39153541Sshin im6o.im6o_multicast_hlim = 255; 39253541Sshin im6o.im6o_multicast_loop = 0; 39353541Sshin } 39453541Sshin 39553541Sshin icmp6len = sizeof(*nd_ns); 39653541Sshin m->m_pkthdr.len = m->m_len = sizeof(*ip6) + icmp6len; 39795023Ssuz m->m_data += max_linkhdr; /* or MH_ALIGN() equivalent? */ 39853541Sshin 39953541Sshin /* fill neighbor solicitation packet */ 40053541Sshin ip6 = mtod(m, struct ip6_hdr *); 40153541Sshin ip6->ip6_flow = 0; 40262587Sitojun ip6->ip6_vfc &= ~IPV6_VERSION_MASK; 40362587Sitojun ip6->ip6_vfc |= IPV6_VERSION; 40453541Sshin /* ip6->ip6_plen will be set later */ 40553541Sshin ip6->ip6_nxt = IPPROTO_ICMPV6; 40653541Sshin ip6->ip6_hlim = 255; 40753541Sshin if (daddr6) 40853541Sshin ip6->ip6_dst = *daddr6; 40953541Sshin else { 41053541Sshin ip6->ip6_dst.s6_addr16[0] = IPV6_ADDR_INT16_MLL; 411148385Sume ip6->ip6_dst.s6_addr16[1] = 0; 41253541Sshin ip6->ip6_dst.s6_addr32[1] = 0; 41353541Sshin ip6->ip6_dst.s6_addr32[2] = IPV6_ADDR_INT32_ONE; 41453541Sshin ip6->ip6_dst.s6_addr32[3] = taddr6->s6_addr32[3]; 41553541Sshin ip6->ip6_dst.s6_addr8[12] = 0xff; 416148385Sume if (in6_setscope(&ip6->ip6_dst, ifp, NULL) != 0) 417148385Sume goto bad; 41853541Sshin } 41953541Sshin if (!dad) { 42053541Sshin /* 42153541Sshin * RFC2461 7.2.2: 42253541Sshin * "If the source address of the packet prompting the 42353541Sshin * solicitation is the same as one of the addresses assigned 42453541Sshin * to the outgoing interface, that address SHOULD be placed 42553541Sshin * in the IP Source Address of the outgoing solicitation. 42653541Sshin * Otherwise, any one of the addresses assigned to the 42753541Sshin * interface should be used." 42853541Sshin * 42953541Sshin * We use the source address for the prompting packet 43053541Sshin * (saddr6), if: 43153541Sshin * - saddr6 is given from the caller (by giving "ln"), and 43253541Sshin * - saddr6 belongs to the outgoing interface. 433148385Sume * Otherwise, we perform the source address selection as usual. 43453541Sshin */ 43595023Ssuz struct ip6_hdr *hip6; /* hold ip6 */ 436148385Sume struct in6_addr *hsrc = NULL; 43753541Sshin 43853541Sshin if (ln && ln->ln_hold) { 439148987Sume /* 440148987Sume * assuming every packet in ln_hold has the same IP 441148987Sume * header 442148987Sume */ 44353541Sshin hip6 = mtod(ln->ln_hold, struct ip6_hdr *); 44453541Sshin /* XXX pullup? */ 44553541Sshin if (sizeof(*hip6) < ln->ln_hold->m_len) 446148385Sume hsrc = &hip6->ip6_src; 44753541Sshin else 448148385Sume hsrc = NULL; 449148385Sume } 450148385Sume if (hsrc && in6ifa_ifpwithaddr(ifp, hsrc)) 451148385Sume src = hsrc; 45253541Sshin else { 453148385Sume int error; 454148385Sume struct sockaddr_in6 dst_sa; 455148385Sume 456148385Sume bzero(&dst_sa, sizeof(dst_sa)); 457148385Sume dst_sa.sin6_family = AF_INET6; 458148385Sume dst_sa.sin6_len = sizeof(dst_sa); 459148385Sume dst_sa.sin6_addr = ip6->ip6_dst; 460148385Sume 461148385Sume src = in6_selectsrc(&dst_sa, NULL, 462148385Sume NULL, &ro, NULL, NULL, &error); 463148385Sume if (src == NULL) { 464148385Sume nd6log((LOG_DEBUG, 465148385Sume "nd6_ns_output: source can't be " 466148385Sume "determined: dst=%s, error=%d\n", 467148385Sume ip6_sprintf(&dst_sa.sin6_addr), error)); 468148385Sume goto bad; 46953541Sshin } 47053541Sshin } 47153541Sshin } else { 47253541Sshin /* 47353541Sshin * Source address for DAD packet must always be IPv6 47453541Sshin * unspecified address. (0::0) 475148385Sume * We actually don't have to 0-clear the address (we did it 476148385Sume * above), but we do so here explicitly to make the intention 477148385Sume * clearer. 47853541Sshin */ 479148385Sume bzero(&src_in, sizeof(src_in)); 480148385Sume src = &src_in; 48153541Sshin } 482148385Sume ip6->ip6_src = *src; 48353541Sshin nd_ns = (struct nd_neighbor_solicit *)(ip6 + 1); 48453541Sshin nd_ns->nd_ns_type = ND_NEIGHBOR_SOLICIT; 48553541Sshin nd_ns->nd_ns_code = 0; 48653541Sshin nd_ns->nd_ns_reserved = 0; 48753541Sshin nd_ns->nd_ns_target = *taddr6; 488121315Sume in6_clearscope(&nd_ns->nd_ns_target); /* XXX */ 48953541Sshin 49053541Sshin /* 49153541Sshin * Add source link-layer address option. 49253541Sshin * 49353541Sshin * spec implementation 49453541Sshin * --- --- 49553541Sshin * DAD packet MUST NOT do not add the option 49653541Sshin * there's no link layer address: 49753541Sshin * impossible do not add the option 49853541Sshin * there's link layer address: 49953541Sshin * Multicast NS MUST add one add the option 50053541Sshin * Unicast NS SHOULD add one add the option 50153541Sshin */ 50253541Sshin if (!dad && (mac = nd6_ifptomac(ifp))) { 50353541Sshin int optlen = sizeof(struct nd_opt_hdr) + ifp->if_addrlen; 50453541Sshin struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)(nd_ns + 1); 50553541Sshin /* 8 byte alignments... */ 50653541Sshin optlen = (optlen + 7) & ~7; 507120941Sume 50853541Sshin m->m_pkthdr.len += optlen; 50953541Sshin m->m_len += optlen; 51053541Sshin icmp6len += optlen; 51153541Sshin bzero((caddr_t)nd_opt, optlen); 51253541Sshin nd_opt->nd_opt_type = ND_OPT_SOURCE_LINKADDR; 51353541Sshin nd_opt->nd_opt_len = optlen >> 3; 51453541Sshin bcopy(mac, (caddr_t)(nd_opt + 1), ifp->if_addrlen); 51553541Sshin } 51653541Sshin 51753541Sshin ip6->ip6_plen = htons((u_short)icmp6len); 51853541Sshin nd_ns->nd_ns_cksum = 0; 519120941Sume nd_ns->nd_ns_cksum = 520120941Sume in6_cksum(m, IPPROTO_ICMPV6, sizeof(*ip6), icmp6len); 52153541Sshin 522148385Sume ip6_output(m, NULL, &ro, dad ? IPV6_DADOUTPUT : 0, &im6o, NULL, NULL); 523148385Sume icmp6_ifstat_inc(ifp, ifs6_out_msg); 524148385Sume icmp6_ifstat_inc(ifp, ifs6_out_neighborsolicit); 525148385Sume icmp6stat.icp6s_outhist[ND_NEIGHBOR_SOLICIT]++; 526148385Sume 527148385Sume if (ro.ro_rt) { /* we don't cache this route. */ 528148385Sume RTFREE(ro.ro_rt); 52953541Sshin } 530148385Sume return; 531148385Sume 532148385Sume bad: 533148385Sume if (ro.ro_rt) { 534148385Sume RTFREE(ro.ro_rt); 535148385Sume } 536148385Sume m_freem(m); 537148385Sume return; 53853541Sshin} 53953541Sshin 54053541Sshin/* 54153541Sshin * Neighbor advertisement input handling. 54253541Sshin * 54353541Sshin * Based on RFC 2461 544148987Sume * Based on RFC 2462 (duplicate address detection) 54562587Sitojun * 54662587Sitojun * the following items are not implemented yet: 54762587Sitojun * - proxy advertisement delay rule (RFC2461 7.2.8, last paragraph, SHOULD) 54862587Sitojun * - anycast advertisement delay rule (RFC2461 7.2.7, SHOULD) 54953541Sshin */ 55053541Sshinvoid 55153541Sshinnd6_na_input(m, off, icmp6len) 55253541Sshin struct mbuf *m; 55353541Sshin int off, icmp6len; 55453541Sshin{ 55553541Sshin struct ifnet *ifp = m->m_pkthdr.rcvif; 55653541Sshin struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); 55762587Sitojun struct nd_neighbor_advert *nd_na; 55853541Sshin struct in6_addr daddr6 = ip6->ip6_dst; 55962587Sitojun struct in6_addr taddr6; 56062587Sitojun int flags; 56162587Sitojun int is_router; 56262587Sitojun int is_solicited; 56362587Sitojun int is_override; 56453541Sshin char *lladdr = NULL; 56553541Sshin int lladdrlen = 0; 56653541Sshin struct ifaddr *ifa; 56753541Sshin struct llinfo_nd6 *ln; 56853541Sshin struct rtentry *rt; 56953541Sshin struct sockaddr_dl *sdl; 57053541Sshin union nd_opts ndopts; 57153541Sshin 57253541Sshin if (ip6->ip6_hlim != 255) { 57378064Sume nd6log((LOG_ERR, 57478064Sume "nd6_na_input: invalid hlim (%d) from %s to %s on %s\n", 57578064Sume ip6->ip6_hlim, ip6_sprintf(&ip6->ip6_src), 57678064Sume ip6_sprintf(&ip6->ip6_dst), if_name(ifp))); 57778064Sume goto bad; 57862587Sitojun } 57962587Sitojun 58062587Sitojun#ifndef PULLDOWN_TEST 58162587Sitojun IP6_EXTHDR_CHECK(m, off, icmp6len,); 58262587Sitojun nd_na = (struct nd_neighbor_advert *)((caddr_t)ip6 + off); 58362587Sitojun#else 58462587Sitojun IP6_EXTHDR_GET(nd_na, struct nd_neighbor_advert *, m, off, icmp6len); 58562587Sitojun if (nd_na == NULL) { 58662587Sitojun icmp6stat.icp6s_tooshort++; 58753541Sshin return; 58853541Sshin } 58962587Sitojun#endif 590148385Sume 59162587Sitojun flags = nd_na->nd_na_flags_reserved; 59262587Sitojun is_router = ((flags & ND_NA_FLAG_ROUTER) != 0); 59362587Sitojun is_solicited = ((flags & ND_NA_FLAG_SOLICITED) != 0); 59462587Sitojun is_override = ((flags & ND_NA_FLAG_OVERRIDE) != 0); 59553541Sshin 596148385Sume taddr6 = nd_na->nd_na_target; 597148385Sume if (in6_setscope(&taddr6, ifp, NULL)) 598150202Ssuz goto bad; /* XXX: impossible */ 59953541Sshin 60053541Sshin if (IN6_IS_ADDR_MULTICAST(&taddr6)) { 60178064Sume nd6log((LOG_ERR, 60253541Sshin "nd6_na_input: invalid target address %s\n", 60378064Sume ip6_sprintf(&taddr6))); 60478064Sume goto bad; 60553541Sshin } 60653541Sshin if (IN6_IS_ADDR_MULTICAST(&daddr6)) 60753541Sshin if (is_solicited) { 60878064Sume nd6log((LOG_ERR, 60978064Sume "nd6_na_input: a solicited adv is multicasted\n")); 61078064Sume goto bad; 61153541Sshin } 61253541Sshin 61353541Sshin icmp6len -= sizeof(*nd_na); 61453541Sshin nd6_option_init(nd_na + 1, icmp6len, &ndopts); 61553541Sshin if (nd6_options(&ndopts) < 0) { 61678064Sume nd6log((LOG_INFO, 61778064Sume "nd6_na_input: invalid ND option, ignored\n")); 61878064Sume /* nd6_options have incremented stats */ 61962587Sitojun goto freeit; 62053541Sshin } 62153541Sshin 62253541Sshin if (ndopts.nd_opts_tgt_lladdr) { 62353541Sshin lladdr = (char *)(ndopts.nd_opts_tgt_lladdr + 1); 62453541Sshin lladdrlen = ndopts.nd_opts_tgt_lladdr->nd_opt_len << 3; 62553541Sshin } 62653541Sshin 62753541Sshin ifa = (struct ifaddr *)in6ifa_ifpwithaddr(ifp, &taddr6); 62853541Sshin 62953541Sshin /* 63053541Sshin * Target address matches one of my interface address. 63153541Sshin * 63253541Sshin * If my address is tentative, this means that there's somebody 63353541Sshin * already using the same address as mine. This indicates DAD failure. 63453541Sshin * This is defined in RFC 2462. 63553541Sshin * 63653541Sshin * Otherwise, process as defined in RFC 2461. 63753541Sshin */ 63853541Sshin if (ifa 63953541Sshin && (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_TENTATIVE)) { 64053541Sshin nd6_dad_na_input(ifa); 64162587Sitojun goto freeit; 64253541Sshin } 64353541Sshin 64495023Ssuz /* Just for safety, maybe unnecessary. */ 64553541Sshin if (ifa) { 64653541Sshin log(LOG_ERR, 64753541Sshin "nd6_na_input: duplicate IP6 address %s\n", 64853541Sshin ip6_sprintf(&taddr6)); 64962587Sitojun goto freeit; 65053541Sshin } 65153541Sshin 65253541Sshin if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) { 653120941Sume nd6log((LOG_INFO, "nd6_na_input: lladdrlen mismatch for %s " 654120941Sume "(if %d, NA packet %d)\n", ip6_sprintf(&taddr6), 655120941Sume ifp->if_addrlen, lladdrlen - 2)); 65678064Sume goto bad; 65753541Sshin } 65853541Sshin 65953541Sshin /* 660120941Sume * If no neighbor cache entry is found, NA SHOULD silently be 661120941Sume * discarded. 66253541Sshin */ 66353541Sshin rt = nd6_lookup(&taddr6, 0, ifp); 66453541Sshin if ((rt == NULL) || 66553541Sshin ((ln = (struct llinfo_nd6 *)rt->rt_llinfo) == NULL) || 66653541Sshin ((sdl = SDL(rt->rt_gateway)) == NULL)) 66762587Sitojun goto freeit; 66853541Sshin 66953541Sshin if (ln->ln_state == ND6_LLINFO_INCOMPLETE) { 67053541Sshin /* 67153541Sshin * If the link-layer has address, and no lladdr option came, 67253541Sshin * discard the packet. 67353541Sshin */ 674151465Ssuz if (ifp->if_addrlen && lladdr == NULL) 67562587Sitojun goto freeit; 67653541Sshin 67753541Sshin /* 67853541Sshin * Record link-layer address, and update the state. 67953541Sshin */ 68053541Sshin sdl->sdl_alen = ifp->if_addrlen; 68153541Sshin bcopy(lladdr, LLADDR(sdl), ifp->if_addrlen); 68253541Sshin if (is_solicited) { 68353541Sshin ln->ln_state = ND6_LLINFO_REACHABLE; 68462587Sitojun ln->ln_byhint = 0; 685120941Sume if (ln->ln_expire) { 68653541Sshin ln->ln_expire = time_second + 687121161Sume ND_IFINFO(rt->rt_ifp)->reachable; 688120941Sume } 68978064Sume } else { 69053541Sshin ln->ln_state = ND6_LLINFO_STALE; 69178064Sume ln->ln_expire = time_second + nd6_gctimer; 69278064Sume } 69378064Sume if ((ln->ln_router = is_router) != 0) { 69478064Sume /* 69578064Sume * This means a router's state has changed from 69678064Sume * non-reachable to probably reachable, and might 69778064Sume * affect the status of associated prefixes.. 69878064Sume */ 69978064Sume pfxlist_onlink_check(); 70078064Sume } 70153541Sshin } else { 70253541Sshin int llchange; 70353541Sshin 70453541Sshin /* 70553541Sshin * Check if the link-layer address has changed or not. 70653541Sshin */ 707151465Ssuz if (lladdr == NULL) 70853541Sshin llchange = 0; 70953541Sshin else { 71053541Sshin if (sdl->sdl_alen) { 71153541Sshin if (bcmp(lladdr, LLADDR(sdl), ifp->if_addrlen)) 71253541Sshin llchange = 1; 71353541Sshin else 71453541Sshin llchange = 0; 71553541Sshin } else 71653541Sshin llchange = 1; 71753541Sshin } 71853541Sshin 71953541Sshin /* 72053541Sshin * This is VERY complex. Look at it with care. 72153541Sshin * 72253541Sshin * override solicit lladdr llchange action 72353541Sshin * (L: record lladdr) 72453541Sshin * 72553541Sshin * 0 0 n -- (2c) 72653541Sshin * 0 0 y n (2b) L 72753541Sshin * 0 0 y y (1) REACHABLE->STALE 72853541Sshin * 0 1 n -- (2c) *->REACHABLE 72953541Sshin * 0 1 y n (2b) L *->REACHABLE 73053541Sshin * 0 1 y y (1) REACHABLE->STALE 73153541Sshin * 1 0 n -- (2a) 73253541Sshin * 1 0 y n (2a) L 73353541Sshin * 1 0 y y (2a) L *->STALE 73453541Sshin * 1 1 n -- (2a) *->REACHABLE 73553541Sshin * 1 1 y n (2a) L *->REACHABLE 73653541Sshin * 1 1 y y (2a) L *->REACHABLE 73753541Sshin */ 738151465Ssuz if (!is_override && (lladdr != NULL && llchange)) { /* (1) */ 73953541Sshin /* 74053541Sshin * If state is REACHABLE, make it STALE. 74153541Sshin * no other updates should be done. 74253541Sshin */ 74378064Sume if (ln->ln_state == ND6_LLINFO_REACHABLE) { 74453541Sshin ln->ln_state = ND6_LLINFO_STALE; 74578064Sume ln->ln_expire = time_second + nd6_gctimer; 74678064Sume } 74762587Sitojun goto freeit; 74853541Sshin } else if (is_override /* (2a) */ 749151465Ssuz || (!is_override && (lladdr != NULL && !llchange)) /* (2b) */ 750151465Ssuz || lladdr == NULL) { /* (2c) */ 75153541Sshin /* 75253541Sshin * Update link-local address, if any. 75353541Sshin */ 754151465Ssuz if (lladdr != NULL) { 75553541Sshin sdl->sdl_alen = ifp->if_addrlen; 75653541Sshin bcopy(lladdr, LLADDR(sdl), ifp->if_addrlen); 75753541Sshin } 75853541Sshin 75953541Sshin /* 76053541Sshin * If solicited, make the state REACHABLE. 76153541Sshin * If not solicited and the link-layer address was 76253541Sshin * changed, make it STALE. 76353541Sshin */ 76453541Sshin if (is_solicited) { 76553541Sshin ln->ln_state = ND6_LLINFO_REACHABLE; 76662587Sitojun ln->ln_byhint = 0; 76753541Sshin if (ln->ln_expire) { 76853541Sshin ln->ln_expire = time_second + 769121161Sume ND_IFINFO(ifp)->reachable; 77053541Sshin } 77153541Sshin } else { 772151465Ssuz if (lladdr != NULL && llchange) { 77353541Sshin ln->ln_state = ND6_LLINFO_STALE; 77478064Sume ln->ln_expire = time_second + nd6_gctimer; 77578064Sume } 77653541Sshin } 77753541Sshin } 77853541Sshin 77953541Sshin if (ln->ln_router && !is_router) { 78053541Sshin /* 78153541Sshin * The peer dropped the router flag. 78253541Sshin * Remove the sender from the Default Router List and 78353541Sshin * update the Destination Cache entries. 78453541Sshin */ 78553541Sshin struct nd_defrouter *dr; 78653541Sshin struct in6_addr *in6; 78753541Sshin int s; 78853541Sshin 78953541Sshin in6 = &((struct sockaddr_in6 *)rt_key(rt))->sin6_addr; 79095023Ssuz 79195023Ssuz /* 79295023Ssuz * Lock to protect the default router list. 79395023Ssuz * XXX: this might be unnecessary, since this function 79495023Ssuz * is only called under the network software interrupt 795120941Sume * context. However, we keep it just for safety. 79695023Ssuz */ 79753541Sshin s = splnet(); 798128422Sluigi dr = defrouter_lookup(in6, ifp); 79953541Sshin if (dr) 80053541Sshin defrtrlist_del(dr); 80153541Sshin else if (!ip6_forwarding && ip6_accept_rtadv) { 80253541Sshin /* 80353541Sshin * Even if the neighbor is not in the default 80453541Sshin * router list, the neighbor may be used 80553541Sshin * as a next hop for some destinations 80653541Sshin * (e.g. redirect case). So we must 80753541Sshin * call rt6_flush explicitly. 80853541Sshin */ 809128422Sluigi rt6_flush(&ip6->ip6_src, ifp); 81053541Sshin } 81153541Sshin splx(s); 81253541Sshin } 81353541Sshin ln->ln_router = is_router; 81453541Sshin } 81553541Sshin rt->rt_flags &= ~RTF_REJECT; 81653541Sshin ln->ln_asked = 0; 81753541Sshin if (ln->ln_hold) { 81862587Sitojun /* 81995023Ssuz * we assume ifp is not a loopback here, so just set the 2nd 82062587Sitojun * argument as the 1st one. 82162587Sitojun */ 82262587Sitojun nd6_output(ifp, ifp, ln->ln_hold, 82353541Sshin (struct sockaddr_in6 *)rt_key(rt), rt); 824120941Sume ln->ln_hold = NULL; 82553541Sshin } 82662587Sitojun 82762587Sitojun freeit: 82862587Sitojun m_freem(m); 82978064Sume return; 83078064Sume 83178064Sume bad: 83278064Sume icmp6stat.icp6s_badna++; 83378064Sume m_freem(m); 83453541Sshin} 83553541Sshin 83653541Sshin/* 83753541Sshin * Neighbor advertisement output handling. 83853541Sshin * 83953541Sshin * Based on RFC 2461 84053541Sshin * 84162587Sitojun * the following items are not implemented yet: 84262587Sitojun * - proxy advertisement delay rule (RFC2461 7.2.8, last paragraph, SHOULD) 84362587Sitojun * - anycast advertisement delay rule (RFC2461 7.2.7, SHOULD) 84453541Sshin */ 84553541Sshinvoid 846148385Sumend6_na_output(ifp, daddr6_0, taddr6, flags, tlladdr, sdl0) 84753541Sshin struct ifnet *ifp; 848148385Sume const struct in6_addr *daddr6_0, *taddr6; 84953541Sshin u_long flags; 85062587Sitojun int tlladdr; /* 1 if include target link-layer address */ 85162587Sitojun struct sockaddr *sdl0; /* sockaddr_dl (= proxy NA) or NULL */ 85253541Sshin{ 85353541Sshin struct mbuf *m; 85453541Sshin struct ip6_hdr *ip6; 85553541Sshin struct nd_neighbor_advert *nd_na; 85653541Sshin struct ip6_moptions im6o; 857148385Sume struct in6_addr *src, daddr6; 858148385Sume struct sockaddr_in6 dst_sa; 859148385Sume int icmp6len, maxlen, error; 86092733Speter caddr_t mac = NULL; 861148385Sume struct route_in6 ro; 86262587Sitojun 863148385Sume bzero(&ro, sizeof(ro)); 864148385Sume 865148385Sume daddr6 = *daddr6_0; /* make a local copy for modification */ 866148385Sume 86762587Sitojun /* estimate the size of message */ 86862587Sitojun maxlen = sizeof(*ip6) + sizeof(*nd_na); 86962587Sitojun maxlen += (sizeof(struct nd_opt_hdr) + ifp->if_addrlen + 7) & ~7; 87062587Sitojun if (max_linkhdr + maxlen >= MCLBYTES) { 87162587Sitojun#ifdef DIAGNOSTIC 87262587Sitojun printf("nd6_na_output: max_linkhdr + maxlen >= MCLBYTES " 87362587Sitojun "(%d + %d > %d)\n", max_linkhdr, maxlen, MCLBYTES); 87462587Sitojun#endif 87553541Sshin return; 87662587Sitojun } 87753541Sshin 878111119Simp MGETHDR(m, M_DONTWAIT, MT_DATA); 87962587Sitojun if (m && max_linkhdr + maxlen >= MHLEN) { 880111119Simp MCLGET(m, M_DONTWAIT); 88162587Sitojun if ((m->m_flags & M_EXT) == 0) { 88262587Sitojun m_free(m); 88362587Sitojun m = NULL; 88462587Sitojun } 88562587Sitojun } 88662587Sitojun if (m == NULL) 88762587Sitojun return; 88878064Sume m->m_pkthdr.rcvif = NULL; 88962587Sitojun 890148385Sume if (IN6_IS_ADDR_MULTICAST(&daddr6)) { 89153541Sshin m->m_flags |= M_MCAST; 89253541Sshin im6o.im6o_multicast_ifp = ifp; 89353541Sshin im6o.im6o_multicast_hlim = 255; 89453541Sshin im6o.im6o_multicast_loop = 0; 89553541Sshin } 89653541Sshin 89753541Sshin icmp6len = sizeof(*nd_na); 89853541Sshin m->m_pkthdr.len = m->m_len = sizeof(struct ip6_hdr) + icmp6len; 89995023Ssuz m->m_data += max_linkhdr; /* or MH_ALIGN() equivalent? */ 90053541Sshin 90153541Sshin /* fill neighbor advertisement packet */ 90253541Sshin ip6 = mtod(m, struct ip6_hdr *); 90353541Sshin ip6->ip6_flow = 0; 90462587Sitojun ip6->ip6_vfc &= ~IPV6_VERSION_MASK; 90562587Sitojun ip6->ip6_vfc |= IPV6_VERSION; 90653541Sshin ip6->ip6_nxt = IPPROTO_ICMPV6; 90753541Sshin ip6->ip6_hlim = 255; 908148385Sume if (IN6_IS_ADDR_UNSPECIFIED(&daddr6)) { 90953541Sshin /* reply to DAD */ 91053541Sshin ip6->ip6_dst.s6_addr16[0] = IPV6_ADDR_INT16_MLL; 911148385Sume ip6->ip6_dst.s6_addr16[1] = 0; 91253541Sshin ip6->ip6_dst.s6_addr32[1] = 0; 91353541Sshin ip6->ip6_dst.s6_addr32[2] = 0; 91453541Sshin ip6->ip6_dst.s6_addr32[3] = IPV6_ADDR_INT32_ONE; 915148385Sume if (in6_setscope(&daddr6, ifp, NULL)) 916148385Sume goto bad; 917148385Sume 91853541Sshin flags &= ~ND_NA_FLAG_SOLICITED; 919148385Sume } 920148385Sume ip6->ip6_dst = daddr6; 921148385Sume bzero(&dst_sa, sizeof(struct sockaddr_in6)); 922148385Sume dst_sa.sin6_family = AF_INET6; 923148385Sume dst_sa.sin6_len = sizeof(struct sockaddr_in6); 924148385Sume dst_sa.sin6_addr = daddr6; 92553541Sshin 92653541Sshin /* 92753541Sshin * Select a source whose scope is the same as that of the dest. 92853541Sshin */ 929148385Sume bcopy(&dst_sa, &ro.ro_dst, sizeof(dst_sa)); 930148385Sume src = in6_selectsrc(&dst_sa, NULL, NULL, &ro, NULL, NULL, &error); 931148385Sume if (src == NULL) { 932148385Sume nd6log((LOG_DEBUG, "nd6_na_output: source can't be " 933148385Sume "determined: dst=%s, error=%d\n", 934148385Sume ip6_sprintf(&dst_sa.sin6_addr), error)); 935148385Sume goto bad; 93653541Sshin } 937148385Sume ip6->ip6_src = *src; 93853541Sshin nd_na = (struct nd_neighbor_advert *)(ip6 + 1); 93953541Sshin nd_na->nd_na_type = ND_NEIGHBOR_ADVERT; 94053541Sshin nd_na->nd_na_code = 0; 94153541Sshin nd_na->nd_na_target = *taddr6; 942121315Sume in6_clearscope(&nd_na->nd_na_target); /* XXX */ 94353541Sshin 94453541Sshin /* 94553541Sshin * "tlladdr" indicates NS's condition for adding tlladdr or not. 94653541Sshin * see nd6_ns_input() for details. 94753541Sshin * Basically, if NS packet is sent to unicast/anycast addr, 94853541Sshin * target lladdr option SHOULD NOT be included. 94953541Sshin */ 95062587Sitojun if (tlladdr) { 95162587Sitojun /* 95262587Sitojun * sdl0 != NULL indicates proxy NA. If we do proxy, use 95362587Sitojun * lladdr in sdl0. If we are not proxying (sending NA for 95462587Sitojun * my address) use lladdr configured for the interface. 95562587Sitojun */ 956142215Sglebius if (sdl0 == NULL) { 957142215Sglebius#ifdef DEV_CARP 958142215Sglebius if (ifp->if_carp) 959142215Sglebius mac = carp_macmatch6(ifp->if_carp, m, taddr6); 960142215Sglebius if (mac == NULL) 961142215Sglebius mac = nd6_ifptomac(ifp); 962142215Sglebius#else 96362587Sitojun mac = nd6_ifptomac(ifp); 964142215Sglebius#endif 965142215Sglebius } else if (sdl0->sa_family == AF_LINK) { 96662587Sitojun struct sockaddr_dl *sdl; 96762587Sitojun sdl = (struct sockaddr_dl *)sdl0; 96862587Sitojun if (sdl->sdl_alen == ifp->if_addrlen) 96962587Sitojun mac = LLADDR(sdl); 97062587Sitojun } 97162587Sitojun } 97262587Sitojun if (tlladdr && mac) { 97353541Sshin int optlen = sizeof(struct nd_opt_hdr) + ifp->if_addrlen; 97453541Sshin struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)(nd_na + 1); 975120941Sume 97653541Sshin /* roundup to 8 bytes alignment! */ 97753541Sshin optlen = (optlen + 7) & ~7; 97853541Sshin 97953541Sshin m->m_pkthdr.len += optlen; 98053541Sshin m->m_len += optlen; 98153541Sshin icmp6len += optlen; 98253541Sshin bzero((caddr_t)nd_opt, optlen); 98353541Sshin nd_opt->nd_opt_type = ND_OPT_TARGET_LINKADDR; 98453541Sshin nd_opt->nd_opt_len = optlen >> 3; 98553541Sshin bcopy(mac, (caddr_t)(nd_opt + 1), ifp->if_addrlen); 98653541Sshin } else 98753541Sshin flags &= ~ND_NA_FLAG_OVERRIDE; 98853541Sshin 98953541Sshin ip6->ip6_plen = htons((u_short)icmp6len); 99053541Sshin nd_na->nd_na_flags_reserved = flags; 99153541Sshin nd_na->nd_na_cksum = 0; 99253541Sshin nd_na->nd_na_cksum = 993120941Sume in6_cksum(m, IPPROTO_ICMPV6, sizeof(struct ip6_hdr), icmp6len); 99453541Sshin 995148385Sume ip6_output(m, NULL, &ro, 0, &im6o, NULL, NULL); 996148385Sume icmp6_ifstat_inc(ifp, ifs6_out_msg); 997148385Sume icmp6_ifstat_inc(ifp, ifs6_out_neighboradvert); 998148385Sume icmp6stat.icp6s_outhist[ND_NEIGHBOR_ADVERT]++; 999148385Sume 1000148385Sume if (ro.ro_rt) { /* we don't cache this route. */ 1001148385Sume RTFREE(ro.ro_rt); 100253541Sshin } 1003148385Sume return; 1004148385Sume 1005148385Sume bad: 1006148385Sume if (ro.ro_rt) { 1007148385Sume RTFREE(ro.ro_rt); 1008148385Sume } 1009148385Sume m_freem(m); 1010148385Sume return; 101153541Sshin} 101253541Sshin 101353541Sshincaddr_t 101453541Sshinnd6_ifptomac(ifp) 101553541Sshin struct ifnet *ifp; 101653541Sshin{ 101753541Sshin switch (ifp->if_type) { 101853541Sshin case IFT_ARCNET: 101953541Sshin case IFT_ETHER: 102053541Sshin case IFT_FDDI: 102178064Sume case IFT_IEEE1394: 102278468Ssumikawa#ifdef IFT_L2VLAN 102378468Ssumikawa case IFT_L2VLAN: 102478468Ssumikawa#endif 102578064Sume#ifdef IFT_IEEE80211 102678064Sume case IFT_IEEE80211: 102778064Sume#endif 1028142215Sglebius#ifdef IFT_CARP 1029142215Sglebius case IFT_CARP: 1030142215Sglebius#endif 1031149829Sthompsa case IFT_BRIDGE: 1032120049Smdodd case IFT_ISO88025: 1033147306Sbrooks return IF_LLADDR(ifp); 103453541Sshin default: 103553541Sshin return NULL; 103653541Sshin } 103753541Sshin} 103853541Sshin 103960938SjakeTAILQ_HEAD(dadq_head, dadq); 104053541Sshinstruct dadq { 104160938Sjake TAILQ_ENTRY(dadq) dad_list; 104253541Sshin struct ifaddr *dad_ifa; 104353541Sshin int dad_count; /* max NS to send */ 104462587Sitojun int dad_ns_tcount; /* # of trials to send NS */ 104553541Sshin int dad_ns_ocount; /* NS sent so far */ 104653541Sshin int dad_ns_icount; 104753541Sshin int dad_na_icount; 104878064Sume struct callout dad_timer_ch; 104953541Sshin}; 105053541Sshin 105153541Sshinstatic struct dadq_head dadq; 105278064Sumestatic int dad_init = 0; 105353541Sshin 105453541Sshinstatic struct dadq * 105553541Sshinnd6_dad_find(ifa) 105653541Sshin struct ifaddr *ifa; 105753541Sshin{ 105853541Sshin struct dadq *dp; 105953541Sshin 106062587Sitojun for (dp = dadq.tqh_first; dp; dp = dp->dad_list.tqe_next) { 106153541Sshin if (dp->dad_ifa == ifa) 106253541Sshin return dp; 106353541Sshin } 106453541Sshin return NULL; 106553541Sshin} 106653541Sshin 106778064Sumestatic void 106878064Sumend6_dad_starttimer(dp, ticks) 106978064Sume struct dadq *dp; 107078064Sume int ticks; 107178064Sume{ 107278064Sume 107378064Sume callout_reset(&dp->dad_timer_ch, ticks, 107478064Sume (void (*) __P((void *)))nd6_dad_timer, (void *)dp->dad_ifa); 107578064Sume} 107678064Sume 107778064Sumestatic void 107878064Sumend6_dad_stoptimer(dp) 107978064Sume struct dadq *dp; 108078064Sume{ 108178064Sume 108278064Sume callout_stop(&dp->dad_timer_ch); 108378064Sume} 108478064Sume 108553541Sshin/* 1086148987Sume * Start Duplicate Address Detection (DAD) for specified interface address. 108753541Sshin */ 108853541Sshinvoid 108953541Sshinnd6_dad_start(ifa, tick) 109053541Sshin struct ifaddr *ifa; 109153541Sshin int *tick; /* minimum delay ticks for IFF_UP event */ 109253541Sshin{ 109353541Sshin struct in6_ifaddr *ia = (struct in6_ifaddr *)ifa; 109453541Sshin struct dadq *dp; 109553541Sshin 109653541Sshin if (!dad_init) { 109753541Sshin TAILQ_INIT(&dadq); 109853541Sshin dad_init++; 109953541Sshin } 110053541Sshin 110153541Sshin /* 110253541Sshin * If we don't need DAD, don't do it. 110353541Sshin * There are several cases: 110453541Sshin * - DAD is disabled (ip6_dad_count == 0) 110553541Sshin * - the interface address is anycast 110653541Sshin */ 110753541Sshin if (!(ia->ia6_flags & IN6_IFF_TENTATIVE)) { 110862587Sitojun log(LOG_DEBUG, 110962587Sitojun "nd6_dad_start: called with non-tentative address " 111053541Sshin "%s(%s)\n", 111153541Sshin ip6_sprintf(&ia->ia_addr.sin6_addr), 111253541Sshin ifa->ifa_ifp ? if_name(ifa->ifa_ifp) : "???"); 111353541Sshin return; 111453541Sshin } 111553541Sshin if (ia->ia6_flags & IN6_IFF_ANYCAST) { 111653541Sshin ia->ia6_flags &= ~IN6_IFF_TENTATIVE; 111753541Sshin return; 111853541Sshin } 111953541Sshin if (!ip6_dad_count) { 112053541Sshin ia->ia6_flags &= ~IN6_IFF_TENTATIVE; 112153541Sshin return; 112253541Sshin } 1123151465Ssuz if (ifa->ifa_ifp == NULL) 112453541Sshin panic("nd6_dad_start: ifa->ifa_ifp == NULL"); 1125120941Sume if (!(ifa->ifa_ifp->if_flags & IFF_UP)) { 112653541Sshin return; 1127120941Sume } 112853541Sshin if (nd6_dad_find(ifa) != NULL) { 112953541Sshin /* DAD already in progress */ 113053541Sshin return; 113153541Sshin } 113253541Sshin 113353541Sshin dp = malloc(sizeof(*dp), M_IP6NDP, M_NOWAIT); 113453541Sshin if (dp == NULL) { 113562587Sitojun log(LOG_ERR, "nd6_dad_start: memory allocation failed for " 113653541Sshin "%s(%s)\n", 113753541Sshin ip6_sprintf(&ia->ia_addr.sin6_addr), 113853541Sshin ifa->ifa_ifp ? if_name(ifa->ifa_ifp) : "???"); 113953541Sshin return; 114053541Sshin } 114153541Sshin bzero(dp, sizeof(*dp)); 114278064Sume callout_init(&dp->dad_timer_ch, 0); 114353541Sshin TAILQ_INSERT_TAIL(&dadq, (struct dadq *)dp, dad_list); 114453541Sshin 114578064Sume nd6log((LOG_DEBUG, "%s: starting DAD for %s\n", if_name(ifa->ifa_ifp), 114678064Sume ip6_sprintf(&ia->ia_addr.sin6_addr))); 114753541Sshin 114853541Sshin /* 114953541Sshin * Send NS packet for DAD, ip6_dad_count times. 115053541Sshin * Note that we must delay the first transmission, if this is the 115153541Sshin * first packet to be sent from the interface after interface 115253541Sshin * (re)initialization. 115353541Sshin */ 115453541Sshin dp->dad_ifa = ifa; 115595023Ssuz IFAREF(ifa); /* just for safety */ 115653541Sshin dp->dad_count = ip6_dad_count; 115753541Sshin dp->dad_ns_icount = dp->dad_na_icount = 0; 115862587Sitojun dp->dad_ns_ocount = dp->dad_ns_tcount = 0; 115995023Ssuz if (tick == NULL) { 116062587Sitojun nd6_dad_ns_output(dp, ifa); 1161121161Sume nd6_dad_starttimer(dp, 1162121161Sume ND_IFINFO(ifa->ifa_ifp)->retrans * hz / 1000); 116353541Sshin } else { 116453541Sshin int ntick; 116553541Sshin 116653541Sshin if (*tick == 0) 1167121807Sume ntick = arc4random() % (MAX_RTR_SOLICITATION_DELAY * hz); 116853541Sshin else 1169121807Sume ntick = *tick + arc4random() % (hz / 2); 117053541Sshin *tick = ntick; 117178064Sume nd6_dad_starttimer(dp, ntick); 117253541Sshin } 117353541Sshin} 117453541Sshin 117578064Sume/* 117678064Sume * terminate DAD unconditionally. used for address removals. 117778064Sume */ 117878064Sumevoid 117978064Sumend6_dad_stop(ifa) 118078064Sume struct ifaddr *ifa; 118178064Sume{ 118278064Sume struct dadq *dp; 118378064Sume 118478064Sume if (!dad_init) 118578064Sume return; 118678064Sume dp = nd6_dad_find(ifa); 118778064Sume if (!dp) { 118878064Sume /* DAD wasn't started yet */ 118978064Sume return; 119078064Sume } 119178064Sume 119278064Sume nd6_dad_stoptimer(dp); 119378064Sume 119478064Sume TAILQ_REMOVE(&dadq, (struct dadq *)dp, dad_list); 119578064Sume free(dp, M_IP6NDP); 119678064Sume dp = NULL; 119778064Sume IFAFREE(ifa); 119878064Sume} 119978064Sume 120053541Sshinstatic void 120153541Sshinnd6_dad_timer(ifa) 120253541Sshin struct ifaddr *ifa; 120353541Sshin{ 120453541Sshin int s; 120553541Sshin struct in6_ifaddr *ia = (struct in6_ifaddr *)ifa; 120653541Sshin struct dadq *dp; 120753541Sshin 120895023Ssuz s = splnet(); /* XXX */ 120953541Sshin 121053541Sshin /* Sanity check */ 121153541Sshin if (ia == NULL) { 121262587Sitojun log(LOG_ERR, "nd6_dad_timer: called with null parameter\n"); 121353541Sshin goto done; 121453541Sshin } 121553541Sshin dp = nd6_dad_find(ifa); 121653541Sshin if (dp == NULL) { 121762587Sitojun log(LOG_ERR, "nd6_dad_timer: DAD structure not found\n"); 121853541Sshin goto done; 121953541Sshin } 122053541Sshin if (ia->ia6_flags & IN6_IFF_DUPLICATED) { 122162587Sitojun log(LOG_ERR, "nd6_dad_timer: called with duplicated address " 122253541Sshin "%s(%s)\n", 122353541Sshin ip6_sprintf(&ia->ia_addr.sin6_addr), 122453541Sshin ifa->ifa_ifp ? if_name(ifa->ifa_ifp) : "???"); 122553541Sshin goto done; 122653541Sshin } 122753541Sshin if ((ia->ia6_flags & IN6_IFF_TENTATIVE) == 0) { 122862587Sitojun log(LOG_ERR, "nd6_dad_timer: called with non-tentative address " 122953541Sshin "%s(%s)\n", 123053541Sshin ip6_sprintf(&ia->ia_addr.sin6_addr), 123153541Sshin ifa->ifa_ifp ? if_name(ifa->ifa_ifp) : "???"); 123253541Sshin goto done; 123353541Sshin } 123453541Sshin 123562587Sitojun /* timeouted with IFF_{RUNNING,UP} check */ 123662587Sitojun if (dp->dad_ns_tcount > dad_maxtry) { 123778064Sume nd6log((LOG_INFO, "%s: could not run DAD, driver problem?\n", 1238120941Sume if_name(ifa->ifa_ifp))); 123962587Sitojun 124062587Sitojun TAILQ_REMOVE(&dadq, (struct dadq *)dp, dad_list); 124162587Sitojun free(dp, M_IP6NDP); 124262587Sitojun dp = NULL; 124362587Sitojun IFAFREE(ifa); 124462587Sitojun goto done; 124562587Sitojun } 124662587Sitojun 124753541Sshin /* Need more checks? */ 124853541Sshin if (dp->dad_ns_ocount < dp->dad_count) { 124953541Sshin /* 125053541Sshin * We have more NS to go. Send NS packet for DAD. 125153541Sshin */ 125262587Sitojun nd6_dad_ns_output(dp, ifa); 1253120941Sume nd6_dad_starttimer(dp, 1254121161Sume ND_IFINFO(ifa->ifa_ifp)->retrans * hz / 1000); 125553541Sshin } else { 125653541Sshin /* 125753541Sshin * We have transmitted sufficient number of DAD packets. 125853541Sshin * See what we've got. 125953541Sshin */ 126053541Sshin int duplicate; 126153541Sshin 126253541Sshin duplicate = 0; 126353541Sshin 126453541Sshin if (dp->dad_na_icount) { 126553541Sshin /* 126653541Sshin * the check is in nd6_dad_na_input(), 126753541Sshin * but just in case 126853541Sshin */ 126953541Sshin duplicate++; 127053541Sshin } 127153541Sshin 127253541Sshin if (dp->dad_ns_icount) { 127353541Sshin /* We've seen NS, means DAD has failed. */ 127453541Sshin duplicate++; 127553541Sshin } 127653541Sshin 127753541Sshin if (duplicate) { 127853541Sshin /* (*dp) will be freed in nd6_dad_duplicated() */ 127953541Sshin dp = NULL; 128053541Sshin nd6_dad_duplicated(ifa); 128153541Sshin } else { 128253541Sshin /* 128353541Sshin * We are done with DAD. No NA came, no NS came. 1284148987Sume * No duplicate address found. 128553541Sshin */ 128653541Sshin ia->ia6_flags &= ~IN6_IFF_TENTATIVE; 128753541Sshin 128878064Sume nd6log((LOG_DEBUG, 128962587Sitojun "%s: DAD complete for %s - no duplicates found\n", 129062587Sitojun if_name(ifa->ifa_ifp), 129178064Sume ip6_sprintf(&ia->ia_addr.sin6_addr))); 129253541Sshin 129353541Sshin TAILQ_REMOVE(&dadq, (struct dadq *)dp, dad_list); 129453541Sshin free(dp, M_IP6NDP); 129553541Sshin dp = NULL; 129662587Sitojun IFAFREE(ifa); 129753541Sshin } 129853541Sshin } 129953541Sshin 130053541Sshindone: 130153541Sshin splx(s); 130253541Sshin} 130353541Sshin 130453541Sshinvoid 130553541Sshinnd6_dad_duplicated(ifa) 130653541Sshin struct ifaddr *ifa; 130753541Sshin{ 130853541Sshin struct in6_ifaddr *ia = (struct in6_ifaddr *)ifa; 130953541Sshin struct dadq *dp; 131053541Sshin 131153541Sshin dp = nd6_dad_find(ifa); 131253541Sshin if (dp == NULL) { 131362587Sitojun log(LOG_ERR, "nd6_dad_duplicated: DAD structure not found\n"); 131453541Sshin return; 131553541Sshin } 131653541Sshin 131778064Sume log(LOG_ERR, "%s: DAD detected duplicate IPv6 address %s: " 131878064Sume "NS in/out=%d/%d, NA in=%d\n", 131978064Sume if_name(ifa->ifa_ifp), ip6_sprintf(&ia->ia_addr.sin6_addr), 132078064Sume dp->dad_ns_icount, dp->dad_ns_ocount, dp->dad_na_icount); 132153541Sshin 132253541Sshin ia->ia6_flags &= ~IN6_IFF_TENTATIVE; 132353541Sshin ia->ia6_flags |= IN6_IFF_DUPLICATED; 132453541Sshin 1325148987Sume /* We are done with DAD, with duplicate address found. (failure) */ 132678064Sume nd6_dad_stoptimer(dp); 132753541Sshin 132862587Sitojun log(LOG_ERR, "%s: DAD complete for %s - duplicate found\n", 132953541Sshin if_name(ifa->ifa_ifp), ip6_sprintf(&ia->ia_addr.sin6_addr)); 133062587Sitojun log(LOG_ERR, "%s: manual intervention required\n", 133162587Sitojun if_name(ifa->ifa_ifp)); 133253541Sshin 133353541Sshin TAILQ_REMOVE(&dadq, (struct dadq *)dp, dad_list); 133453541Sshin free(dp, M_IP6NDP); 133553541Sshin dp = NULL; 133662587Sitojun IFAFREE(ifa); 133753541Sshin} 133853541Sshin 133962587Sitojunstatic void 134062587Sitojunnd6_dad_ns_output(dp, ifa) 134162587Sitojun struct dadq *dp; 134262587Sitojun struct ifaddr *ifa; 134362587Sitojun{ 134462587Sitojun struct in6_ifaddr *ia = (struct in6_ifaddr *)ifa; 134562587Sitojun struct ifnet *ifp = ifa->ifa_ifp; 134662587Sitojun 134762587Sitojun dp->dad_ns_tcount++; 134862587Sitojun if ((ifp->if_flags & IFF_UP) == 0) { 134962587Sitojun#if 0 135062587Sitojun printf("%s: interface down?\n", if_name(ifp)); 135162587Sitojun#endif 135262587Sitojun return; 135362587Sitojun } 1354148887Srwatson if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { 135562587Sitojun#if 0 135662587Sitojun printf("%s: interface not running?\n", if_name(ifp)); 135762587Sitojun#endif 135862587Sitojun return; 135962587Sitojun } 136062587Sitojun 136162587Sitojun dp->dad_ns_ocount++; 136262587Sitojun nd6_ns_output(ifp, NULL, &ia->ia_addr.sin6_addr, NULL, 1); 136362587Sitojun} 136462587Sitojun 136562587Sitojunstatic void 136653541Sshinnd6_dad_ns_input(ifa) 136753541Sshin struct ifaddr *ifa; 136853541Sshin{ 136953541Sshin struct in6_ifaddr *ia; 137053541Sshin struct ifnet *ifp; 137178064Sume const struct in6_addr *taddr6; 137253541Sshin struct dadq *dp; 137353541Sshin int duplicate; 137453541Sshin 1375151465Ssuz if (ifa == NULL) 137653541Sshin panic("ifa == NULL in nd6_dad_ns_input"); 137753541Sshin 137853541Sshin ia = (struct in6_ifaddr *)ifa; 137953541Sshin ifp = ifa->ifa_ifp; 138053541Sshin taddr6 = &ia->ia_addr.sin6_addr; 138153541Sshin duplicate = 0; 138253541Sshin dp = nd6_dad_find(ifa); 138353541Sshin 138453541Sshin /* Quickhack - completely ignore DAD NS packets */ 138553541Sshin if (dad_ignore_ns) { 138678064Sume nd6log((LOG_INFO, 138778064Sume "nd6_dad_ns_input: ignoring DAD NS packet for " 138853541Sshin "address %s(%s)\n", ip6_sprintf(taddr6), 138978064Sume if_name(ifa->ifa_ifp))); 139053541Sshin return; 139153541Sshin } 139253541Sshin 139353541Sshin /* 139453541Sshin * if I'm yet to start DAD, someone else started using this address 139553541Sshin * first. I have a duplicate and you win. 139653541Sshin */ 1397151465Ssuz if (dp == NULL || dp->dad_ns_ocount == 0) 139853541Sshin duplicate++; 139953541Sshin 140053541Sshin /* XXX more checks for loopback situation - see nd6_dad_timer too */ 140153541Sshin 140253541Sshin if (duplicate) { 140353541Sshin dp = NULL; /* will be freed in nd6_dad_duplicated() */ 140453541Sshin nd6_dad_duplicated(ifa); 140553541Sshin } else { 140653541Sshin /* 140753541Sshin * not sure if I got a duplicate. 140853541Sshin * increment ns count and see what happens. 140953541Sshin */ 141053541Sshin if (dp) 141153541Sshin dp->dad_ns_icount++; 141253541Sshin } 141353541Sshin} 141453541Sshin 141562587Sitojunstatic void 141653541Sshinnd6_dad_na_input(ifa) 141753541Sshin struct ifaddr *ifa; 141853541Sshin{ 141953541Sshin struct dadq *dp; 142053541Sshin 1421151465Ssuz if (ifa == NULL) 142253541Sshin panic("ifa == NULL in nd6_dad_na_input"); 142353541Sshin 142453541Sshin dp = nd6_dad_find(ifa); 142553541Sshin if (dp) 142653541Sshin dp->dad_na_icount++; 142753541Sshin 142853541Sshin /* remove the address. */ 142953541Sshin nd6_dad_duplicated(ifa); 143053541Sshin} 1431