Disable scope related ioctls. Remove in6_setscope(), sa6_embedscope(), sa6_recoverscope() calls. Use sa6_checkzone_ifp() to check user's input. Don't install routes for LLA. --- sys/netinet6/in6.c (svn+ssh://svn.freebsd.org/base/head) (revision 261548) +++ sys/netinet6/in6.c (working copy) @@ -319,6 +319,11 @@ in6_control(struct socket *so, u_long cmd, caddr_t switch (cmd) { case SIOCSSCOPE6: + case SIOCGSCOPE6: + case SIOCGSCOPE6DEF: + return (EOPNOTSUPP); +#if 0 + case SIOCSSCOPE6: if (td != NULL) { error = priv_check(td, PRIV_NETINET_SCOPE6); if (error) @@ -332,6 +337,7 @@ in6_control(struct socket *so, u_long cmd, caddr_t case SIOCGSCOPE6DEF: return (scope6_get_default((struct scope6_id *) ifr->ifr_ifru.ifru_scope_id)); +#endif } /* @@ -387,14 +393,11 @@ in6_control(struct socket *so, u_long cmd, caddr_t break; } if (sa6 && sa6->sin6_family == AF_INET6) { - if (sa6->sin6_scope_id != 0) - error = sa6_embedscope(sa6, 0); - else - error = in6_setscope(&sa6->sin6_addr, ifp, NULL); + error = sa6_checkzone_ifp(ifp, sa6); if (error != 0) return (error); if (td != NULL && (error = prison_check_ip6(td->td_ucred, - &sa6->sin6_addr)) != 0) + sa6)) != 0) return (error); ia = in6ifa_ifpwithaddr(ifp, &sa6->sin6_addr); } else @@ -490,8 +493,6 @@ in6_control(struct socket *so, u_long cmd, caddr_t switch (cmd) { case SIOCGIFADDR_IN6: ifr->ifr_addr = ia->ia_addr; - if ((error = sa6_recoverscope(&ifr->ifr_addr)) != 0) - goto out; break; case SIOCGIFDSTADDR_IN6: @@ -504,8 +505,6 @@ in6_control(struct socket *so, u_long cmd, caddr_t * an error? */ ifr->ifr_dstaddr = ia->ia_dstaddr; - if ((error = sa6_recoverscope(&ifr->ifr_dstaddr)) != 0) - goto out; break; case SIOCGIFNETMASK_IN6: @@ -787,27 +786,19 @@ in6_update_ifa_join_mc(struct ifnet *ifp, struct i struct in6_ifaddr *ia, int flags, struct in6_multi **in6m_sol) { char ip6buf[INET6_ADDRSTRLEN]; - struct sockaddr_in6 mltaddr, mltmask; - struct in6_addr llsol; + struct in6_addr mladdr; struct in6_multi_mship *imm; - struct rtentry *rt; int delay, error; KASSERT(in6m_sol != NULL, ("%s: in6m_sol is NULL", __func__)); /* Join solicited multicast addr for new host id. */ - bzero(&llsol, sizeof(struct in6_addr)); - llsol.s6_addr32[0] = IPV6_ADDR_INT32_MLL; - llsol.s6_addr32[1] = 0; - llsol.s6_addr32[2] = htonl(1); - llsol.s6_addr32[3] = ifra->ifra_addr.sin6_addr.s6_addr32[3]; - llsol.s6_addr8[12] = 0xff; - if ((error = in6_setscope(&llsol, ifp, NULL)) != 0) { - /* XXX: should not happen */ - log(LOG_ERR, "%s: in6_setscope failed\n", __func__); - goto cleanup; - } - delay = 0; + bzero(&mladdr, sizeof(struct in6_addr)); + mladdr.s6_addr32[0] = IPV6_ADDR_INT32_MLL; + mladdr.s6_addr32[2] = htonl(1); + mladdr.s6_addr32[3] = ifra->ifra_addr.sin6_addr.s6_addr32[3]; + mladdr.s6_addr8[12] = 0xff; + delay = error = 0; if ((flags & IN6_IFAUPDATE_DADDELAY)) { /* * We need a random delay for DAD on the address being @@ -817,64 +808,27 @@ in6_update_ifa_join_mc(struct ifnet *ifp, struct i */ delay = arc4random() % (MAX_RTR_SOLICITATION_DELAY * hz); } - imm = in6_joingroup(ifp, &llsol, &error, delay); + imm = in6_joingroup(ifp, &mladdr, &error, delay); if (imm == NULL) { nd6log((LOG_WARNING, "%s: addmulti failed for %s on %s " - "(errno=%d)\n", __func__, ip6_sprintf(ip6buf, &llsol), + "(errno=%d)\n", __func__, ip6_sprintf(ip6buf, &mladdr), if_name(ifp), error)); goto cleanup; } + /* XXX: locking */ LIST_INSERT_HEAD(&ia->ia6_memberships, imm, i6mm_chain); *in6m_sol = imm->i6mm_maddr; - - bzero(&mltmask, sizeof(mltmask)); - mltmask.sin6_len = sizeof(struct sockaddr_in6); - mltmask.sin6_family = AF_INET6; - mltmask.sin6_addr = in6mask32; -#define MLTMASK_LEN 4 /* mltmask's masklen (=32bit=4octet) */ - /* * Join link-local all-nodes address. */ - bzero(&mltaddr, sizeof(mltaddr)); - mltaddr.sin6_len = sizeof(struct sockaddr_in6); - mltaddr.sin6_family = AF_INET6; - mltaddr.sin6_addr = in6addr_linklocal_allnodes; - if ((error = in6_setscope(&mltaddr.sin6_addr, ifp, NULL)) != 0) - goto cleanup; /* XXX: should not fail */ - - /* - * XXX: do we really need this automatic routes? We should probably - * reconsider this stuff. Most applications actually do not need the - * routes, since they usually specify the outgoing interface. - */ - rt = in6_rtalloc1((struct sockaddr *)&mltaddr, 0, 0UL, RT_DEFAULT_FIB); - if (rt != NULL) { - /* XXX: only works in !SCOPEDROUTING case. */ - if (memcmp(&mltaddr.sin6_addr, - &((struct sockaddr_in6 *)rt_key(rt))->sin6_addr, - MLTMASK_LEN)) { - RTFREE_LOCKED(rt); - rt = NULL; - } - } - if (rt == NULL) { - error = in6_rtrequest(RTM_ADD, (struct sockaddr *)&mltaddr, - (struct sockaddr *)&ia->ia_addr, - (struct sockaddr *)&mltmask, RTF_UP, - (struct rtentry **)0, RT_DEFAULT_FIB); - if (error) - goto cleanup; - } else - RTFREE_LOCKED(rt); - - imm = in6_joingroup(ifp, &mltaddr.sin6_addr, &error, 0); + imm = in6_joingroup(ifp, &in6addr_linklocal_allnodes, &error, 0); if (imm == NULL) { nd6log((LOG_WARNING, "%s: addmulti failed for %s on %s " "(errno=%d)\n", __func__, ip6_sprintf(ip6buf, - &mltaddr.sin6_addr), if_name(ifp), error)); + &in6addr_linklocal_allnodes), if_name(ifp), error)); goto cleanup; } + /* XXX: locking */ LIST_INSERT_HEAD(&ia->ia6_memberships, imm, i6mm_chain); /* @@ -888,24 +842,24 @@ in6_update_ifa_join_mc(struct ifnet *ifp, struct i */ delay = arc4random() % (MAX_RTR_SOLICITATION_DELAY * hz); } - if (in6_nigroup(ifp, NULL, -1, &mltaddr.sin6_addr) == 0) { + if (in6_nigroup(ifp, NULL, -1, &mladdr) == 0) { /* XXX jinmei */ - imm = in6_joingroup(ifp, &mltaddr.sin6_addr, &error, delay); + imm = in6_joingroup(ifp, &mladdr, &error, delay); if (imm == NULL) nd6log((LOG_WARNING, "%s: addmulti failed for %s on %s " "(errno=%d)\n", __func__, ip6_sprintf(ip6buf, - &mltaddr.sin6_addr), if_name(ifp), error)); + &mladdr), if_name(ifp), error)); /* XXX not very fatal, go on... */ else LIST_INSERT_HEAD(&ia->ia6_memberships, imm, i6mm_chain); } if (V_icmp6_nodeinfo_oldmcprefix && - in6_nigroup_oldmcprefix(ifp, NULL, -1, &mltaddr.sin6_addr) == 0) { - imm = in6_joingroup(ifp, &mltaddr.sin6_addr, &error, delay); + in6_nigroup_oldmcprefix(ifp, NULL, -1, &mladdr) == 0) { + imm = in6_joingroup(ifp, &mladdr, &error, delay); if (imm == NULL) nd6log((LOG_WARNING, "%s: addmulti failed for %s on %s " "(errno=%d)\n", __func__, ip6_sprintf(ip6buf, - &mltaddr.sin6_addr), if_name(ifp), error)); + &mladdr), if_name(ifp), error)); /* XXX not very fatal, go on... */ else LIST_INSERT_HEAD(&ia->ia6_memberships, imm, i6mm_chain); @@ -915,38 +869,15 @@ in6_update_ifa_join_mc(struct ifnet *ifp, struct i * Join interface-local all-nodes address. * (ff01::1%ifN, and ff01::%ifN/32) */ - mltaddr.sin6_addr = in6addr_nodelocal_allnodes; - if ((error = in6_setscope(&mltaddr.sin6_addr, ifp, NULL)) != 0) - goto cleanup; /* XXX: should not fail */ - /* XXX: again, do we really need the route? */ - rt = in6_rtalloc1((struct sockaddr *)&mltaddr, 0, 0UL, RT_DEFAULT_FIB); - if (rt != NULL) { - if (memcmp(&mltaddr.sin6_addr, - &((struct sockaddr_in6 *)rt_key(rt))->sin6_addr, - MLTMASK_LEN)) { - RTFREE_LOCKED(rt); - rt = NULL; - } - } - if (rt == NULL) { - error = in6_rtrequest(RTM_ADD, (struct sockaddr *)&mltaddr, - (struct sockaddr *)&ia->ia_addr, - (struct sockaddr *)&mltmask, RTF_UP, - (struct rtentry **)0, RT_DEFAULT_FIB); - if (error) - goto cleanup; - } else - RTFREE_LOCKED(rt); - - imm = in6_joingroup(ifp, &mltaddr.sin6_addr, &error, 0); + imm = in6_joingroup(ifp, &in6addr_nodelocal_allnodes, &error, 0); if (imm == NULL) { nd6log((LOG_WARNING, "%s: addmulti failed for %s on %s " "(errno=%d)\n", __func__, ip6_sprintf(ip6buf, - &mltaddr.sin6_addr), if_name(ifp), error)); + &in6addr_nodelocal_allnodes), if_name(ifp), error)); goto cleanup; } + /* XXX: locking */ LIST_INSERT_HEAD(&ia->ia6_memberships, imm, i6mm_chain); -#undef MLTMASK_LEN cleanup: return (error); @@ -1012,9 +943,9 @@ static int in6_validate_ifra(struct ifnet *ifp, struct in6_aliasreq *ifra, struct in6_ifaddr *ia, int flags) { - int plen = -1; struct sockaddr_in6 dst6; struct in6_addrlifetime *lt; + int plen; char ip6buf[INET6_ADDRSTRLEN]; /* Validate parameters */ @@ -1036,8 +967,13 @@ in6_validate_ifra(struct ifnet *ifp, struct in6_al if (ifra->ifra_addr.sin6_len != sizeof(struct sockaddr_in6) || ifra->ifra_addr.sin6_family != AF_INET6) return (EINVAL); - /* + * Check and automatically fill scope zone index. + */ + plen = sa6_checkzone_ifp(ifp, &ifra->ifra_addr); + if (plen != 0) + return (plen); + /* * validate ifra_prefixmask. don't check sin6_family, netmask * does not carry fields other than sin6_len. */ @@ -1070,25 +1006,11 @@ in6_validate_ifra(struct ifnet *ifp, struct in6_al * zone identifier. */ dst6 = ifra->ifra_dstaddr; - if ((ifp->if_flags & (IFF_POINTOPOINT|IFF_LOOPBACK)) != 0 && - (dst6.sin6_family == AF_INET6)) { - struct in6_addr in6_tmp; - u_int32_t zoneid; - - in6_tmp = dst6.sin6_addr; - if (in6_setscope(&in6_tmp, ifp, &zoneid)) - return (EINVAL); /* XXX: should be impossible */ - - if (dst6.sin6_scope_id != 0) { - if (dst6.sin6_scope_id != zoneid) - return (EINVAL); - } else /* user omit to specify the ID. */ - dst6.sin6_scope_id = zoneid; - - /* convert into the internal form */ - if (sa6_embedscope(&dst6, 0)) - return (EINVAL); /* XXX: should be impossible */ - } + if ((ifp->if_flags & (IFF_POINTOPOINT | IFF_LOOPBACK)) != 0 && + dst6.sin6_family == AF_INET6 && dst6.sin6_scope_id != 0 && + dst6.sin6_scope_id != in6_getscopezone(ifp, + in6_addrscope(&dst6.sin6_addr))) + return (EINVAL); /* Modify original ifra_dstaddr to reflect changes */ ifra->ifra_dstaddr = dst6; @@ -1168,9 +1090,6 @@ in6_alloc_ifa(struct ifnet *ifp, struct in6_aliasr LIST_INIT(&ia->ia6_memberships); /* Initialize the address and masks, and put time stamp */ ia->ia_ifa.ifa_addr = (struct sockaddr *)&ia->ia_addr; - ia->ia_addr.sin6_family = AF_INET6; - ia->ia_addr.sin6_len = sizeof(ia->ia_addr); - /* XXX: Can we assign ,sin6_addr and skip the rest? */ ia->ia_addr = ifra->ifra_addr; ia->ia6_createtime = time_uptime; if ((ifp->if_flags & (IFF_POINTOPOINT | IFF_LOOPBACK)) != 0) { @@ -1351,135 +1270,17 @@ in6_broadcast_ifa(struct ifnet *ifp, struct in6_al } /* - * Leave multicast groups. Factored out from in6_purgeaddr(). - * This entire work should only be done once, for the default FIB. + * Leave from multicast groups we have joined for the interface. */ static int in6_purgeaddr_mc(struct ifnet *ifp, struct in6_ifaddr *ia, struct ifaddr *ifa0) { - struct sockaddr_in6 mltaddr, mltmask; struct in6_multi_mship *imm; - struct rtentry *rt; - struct sockaddr_in6 sin6; - int error; - /* - * Leave from multicast groups we have joined for the interface. - */ while ((imm = LIST_FIRST(&ia->ia6_memberships)) != NULL) { LIST_REMOVE(imm, i6mm_chain); in6_leavegroup(imm); } - - /* - * Remove the link-local all-nodes address. - */ - bzero(&mltmask, sizeof(mltmask)); - mltmask.sin6_len = sizeof(struct sockaddr_in6); - mltmask.sin6_family = AF_INET6; - mltmask.sin6_addr = in6mask32; - - bzero(&mltaddr, sizeof(mltaddr)); - mltaddr.sin6_len = sizeof(struct sockaddr_in6); - mltaddr.sin6_family = AF_INET6; - mltaddr.sin6_addr = in6addr_linklocal_allnodes; - - if ((error = in6_setscope(&mltaddr.sin6_addr, ifp, NULL)) != 0) - return (error); - - /* - * As for the mltaddr above, proactively prepare the sin6 to avoid - * rtentry un- and re-locking. - */ - if (ifa0 != NULL) { - bzero(&sin6, sizeof(sin6)); - sin6.sin6_len = sizeof(sin6); - sin6.sin6_family = AF_INET6; - memcpy(&sin6.sin6_addr, &satosin6(ifa0->ifa_addr)->sin6_addr, - sizeof(sin6.sin6_addr)); - error = in6_setscope(&sin6.sin6_addr, ifa0->ifa_ifp, NULL); - if (error != 0) - return (error); - } - - rt = in6_rtalloc1((struct sockaddr *)&mltaddr, 0, 0UL, RT_DEFAULT_FIB); - if (rt != NULL && rt->rt_gateway != NULL && - (memcmp(&satosin6(rt->rt_gateway)->sin6_addr, - &ia->ia_addr.sin6_addr, - sizeof(ia->ia_addr.sin6_addr)) == 0)) { - /* - * If no more IPv6 address exists on this interface then - * remove the multicast address route. - */ - if (ifa0 == NULL) { - memcpy(&mltaddr.sin6_addr, - &satosin6(rt_key(rt))->sin6_addr, - sizeof(mltaddr.sin6_addr)); - RTFREE_LOCKED(rt); - error = in6_rtrequest(RTM_DELETE, - (struct sockaddr *)&mltaddr, - (struct sockaddr *)&ia->ia_addr, - (struct sockaddr *)&mltmask, RTF_UP, - (struct rtentry **)0, RT_DEFAULT_FIB); - if (error) - log(LOG_INFO, "%s: link-local all-nodes " - "multicast address deletion error\n", - __func__); - } else { - /* - * Replace the gateway of the route. - */ - memcpy(rt->rt_gateway, &sin6, sizeof(sin6)); - RTFREE_LOCKED(rt); - } - } else { - if (rt != NULL) - RTFREE_LOCKED(rt); - } - - /* - * Remove the node-local all-nodes address. - */ - mltaddr.sin6_addr = in6addr_nodelocal_allnodes; - if ((error = in6_setscope(&mltaddr.sin6_addr, ifp, NULL)) != 0) - return (error); - - rt = in6_rtalloc1((struct sockaddr *)&mltaddr, 0, 0UL, RT_DEFAULT_FIB); - if (rt != NULL && rt->rt_gateway != NULL && - (memcmp(&satosin6(rt->rt_gateway)->sin6_addr, - &ia->ia_addr.sin6_addr, - sizeof(ia->ia_addr.sin6_addr)) == 0)) { - /* - * If no more IPv6 address exists on this interface then - * remove the multicast address route. - */ - if (ifa0 == NULL) { - memcpy(&mltaddr.sin6_addr, - &satosin6(rt_key(rt))->sin6_addr, - sizeof(mltaddr.sin6_addr)); - - RTFREE_LOCKED(rt); - error = in6_rtrequest(RTM_DELETE, - (struct sockaddr *)&mltaddr, - (struct sockaddr *)&ia->ia_addr, - (struct sockaddr *)&mltmask, RTF_UP, - (struct rtentry **)0, RT_DEFAULT_FIB); - if (error) - log(LOG_INFO, "%s: node-local all-nodes" - "multicast address deletion error\n", - __func__); - } else { - /* - * Replace the gateway of the route. - */ - memcpy(rt->rt_gateway, &sin6, sizeof(sin6)); - RTFREE_LOCKED(rt); - } - } else { - if (rt != NULL) - RTFREE_LOCKED(rt); - } - return (0); } @@ -1592,20 +1393,6 @@ in6_unlink_ifa(struct in6_ifaddr *ia, struct ifnet ifa_free(&ia->ia_ifa); /* in6_ifaddrhead */ } -void -in6_purgeif(struct ifnet *ifp) -{ - struct ifaddr *ifa, *nifa; - - TAILQ_FOREACH_SAFE(ifa, &ifp->if_addrhead, ifa_link, nifa) { - if (ifa->ifa_addr->sa_family != AF_INET6) - continue; - in6_purgeaddr(ifa); - } - - in6_ifdetach(ifp); -} - /* * Notifies other other subsystems about address change/arrival: * 1) Notifies device handler on first IPv6 address assignment @@ -1673,6 +1460,7 @@ in6_notify_ifa(struct ifnet *ifp, struct in6_ifadd int rtflags = RTF_UP | RTF_HOST; /* * Handle the case for ::1 . + * XXX: link-local destinations */ if (ifp->if_flags & IFF_LOOPBACK) ia->ia_flags |= IFA_RTSELF; @@ -1685,7 +1473,8 @@ in6_notify_ifa(struct ifnet *ifp, struct in6_ifadd /* * add a loopback route to self if not exists */ - if (!(ia->ia_flags & IFA_RTSELF) && V_nd6_useloopback) { + if (!IN6_IS_ADDR_LINKLOCAL(&ia->ia_addr.sin6_addr) && + (ia->ia_flags & IFA_RTSELF) == 0 && V_nd6_useloopback) { error = ifa_add_loopback_route((struct ifaddr *)ia, (struct sockaddr *)&ia->ia_addr); if (error == 0) @@ -1723,11 +1512,34 @@ in6ifa_ifpforlinklocal(struct ifnet *ifp, int igno /* + * find the internet address corresponding to a given address. + * ifaddr is returned referenced. + */ +struct in6_ifaddr * +in6ifa_ifwithaddr(const struct in6_addr *addr, uint32_t zoneid) +{ + struct in6_ifaddr *ia; + + IN6_IFADDR_RLOCK(); + LIST_FOREACH(ia, IN6ADDR_HASH(addr), ia6_hash) { + if (IN6_ARE_ADDR_EQUAL(IA6_IN6(ia), addr)) { + if (zoneid != 0 && + zoneid != ia->ia_addr.sin6_scope_id) + continue; + ifa_ref(&ia->ia_ifa); + break; + } + } + IN6_IFADDR_RUNLOCK(); + return (ia); +} + +/* * find the internet address corresponding to a given interface and address. * ifaddr is returned referenced. */ struct in6_ifaddr * -in6ifa_ifpwithaddr(struct ifnet *ifp, struct in6_addr *addr) +in6ifa_ifpwithaddr(struct ifnet *ifp, const struct in6_addr *addr) { struct ifaddr *ifa; @@ -1745,6 +1557,25 @@ struct in6_ifaddr * return ((struct in6_ifaddr *)ifa); } +struct in6_ifaddr * +in6ifa_ifpwithdstaddr(struct ifnet *ifp, const struct in6_addr *addr) +{ + struct ifaddr *ifa; + + IF_ADDR_RLOCK(ifp); + TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { + if (ifa->ifa_addr->sa_family != AF_INET6 || + ifa->ifa_dstaddr == NULL) + continue; + if (IN6_ARE_ADDR_EQUAL(addr, IFA_DSTIN6(ifa))) { + ifa_ref(ifa); + break; + } + } + IF_ADDR_RUNLOCK(ifp); + return ((struct in6_ifaddr *)ifa); +} + /* * Find a link-local scoped address on ifp and return it if any. */ @@ -1879,7 +1710,7 @@ in6_localaddr(struct in6_addr *in6) * on one of its interfaces. */ int -in6_localip(struct in6_addr *in6) +in6_localip(const struct in6_addr *in6) { struct in6_ifaddr *ia; @@ -2308,16 +2139,17 @@ in6_lltable_prefix_free(struct lltable *llt, const } static int -in6_lltable_rtcheck(struct ifnet *ifp, - u_int flags, - const struct sockaddr *l3addr) +in6_lltable_rtcheck(struct ifnet *ifp, u_int flags, + const struct sockaddr_in6 *l3addr) { struct rtentry *rt; char ip6buf[INET6_ADDRSTRLEN]; - KASSERT(l3addr->sa_family == AF_INET6, - ("sin_family %d", l3addr->sa_family)); + KASSERT(l3addr->sin6_family == AF_INET6, + ("sin_family %d", l3addr->sin6_family)); + if (IN6_IS_ADDR_LINKLOCAL(&l3addr->sin6_addr)) + return (0); /* Our local addresses are always only installed on the default FIB. */ /* XXX rtalloc1 should take a const param */ rt = in6_rtalloc1(__DECONST(struct sockaddr *, l3addr), 0, 0, @@ -2337,7 +2169,7 @@ static int return 0; } log(LOG_INFO, "IPv6 address: \"%s\" is not on the network\n", - ip6_sprintf(ip6buf, &((const struct sockaddr_in6 *)l3addr)->sin6_addr)); + ip6_sprintf(ip6buf, &l3addr->sin6_addr)); if (rt != NULL) RTFREE_LOCKED(rt); return EINVAL; @@ -2381,7 +2213,7 @@ in6_lltable_lookup(struct lltable *llt, u_int flag * verify this. */ if (!(flags & LLE_IFADDR) && - in6_lltable_rtcheck(ifp, flags, l3addr) != 0) + in6_lltable_rtcheck(ifp, flags, sin6) != 0) return NULL; lle = in6_lltable_new(l3addr, flags); @@ -2470,11 +2302,10 @@ in6_lltable_dump(struct lltable *llt, struct sysct ndpc.rtm.rtm_type = RTM_GET; ndpc.rtm.rtm_flags = RTF_UP; ndpc.rtm.rtm_addrs = RTA_DST | RTA_GATEWAY; - ndpc.sin6.sin6_family = AF_INET6; - ndpc.sin6.sin6_len = sizeof(ndpc.sin6); bcopy(L3_ADDR(lle), &ndpc.sin6, L3_ADDR_LEN(lle)); - if (V_deembed_scopeid) - sa6_recoverscope(&ndpc.sin6); + if (IN6_IS_ADDR_LINKLOCAL(&ndpc.sin6.sin6_addr)) + ndpc.sin6.sin6_scope_id = in6_getscopezone( + ifp, IPV6_ADDR_SCOPE_LINKLOCAL); /* publish */ if (lle->la_flags & LLE_PUB)