Index: sys/net/if_arcsubr.c =================================================================== --- sys/net/if_arcsubr.c +++ sys/net/if_arcsubr.c @@ -169,8 +169,13 @@ case AF_INET6: if ((m->m_flags & M_MCAST) != 0) adst = arcbroadcastaddr; /* ARCnet broadcast address */ - else - error = nd6_storelladdr(ifp, m, dst, (u_char *)&adst, NULL); + else { + is_gw = 0; + if (ro != NULL && ro->ro_rt != NULL && + (ro->ro_rt->rt_flags & RTF_GATEWAY) != 0) + is_gw = 1; + error = nd6_resolve(ifp, is_gw, m, dst, &adst, NULL); + } if (error) return (error); atype = ARCTYPE_INET6; Index: sys/net/if_ethersubr.c =================================================================== --- sys/net/if_ethersubr.c +++ sys/net/if_ethersubr.c @@ -225,7 +225,7 @@ if (lle != NULL && (pflags & LLE_VALID)) memcpy(edst, &lle->ll_addr.mac16, sizeof(edst)); else - error = nd6_storelladdr(ifp, m, dst, (u_char *)edst, + error = nd6_resolve(ifp, is_gw, m, dst, (u_char *)edst, &pflags); if (error) return error; Index: sys/net/if_fddisubr.c =================================================================== --- sys/net/if_fddisubr.c +++ sys/net/if_fddisubr.c @@ -161,7 +161,11 @@ #endif /* INET */ #ifdef INET6 case AF_INET6: - error = nd6_storelladdr(ifp, m, dst, (u_char *)edst, NULL); + is_gw = 0; + if (ro != NULL && ro->ro_rt != NULL && + (ro->ro_rt->rt_flags & RTF_GATEWAY) != 0) + is_gw = 1; + error = nd6_resolve(ifp, is_gw, m, dst, edst, NULL); if (error) return (error); /* Something bad happened */ type = htons(ETHERTYPE_IPV6); Index: sys/net/if_fwsubr.c =================================================================== --- sys/net/if_fwsubr.c +++ sys/net/if_fwsubr.c @@ -173,7 +173,11 @@ #ifdef INET6 case AF_INET6: if (unicast) { - error = nd6_storelladdr(fc->fc_ifp, m, dst, + is_gw = 0; + if (ro != NULL && ro->ro_rt != NULL && + (ro->ro_rt->rt_flags & RTF_GATEWAY) != 0) + is_gw = 1; + error = nd6_resolve(fc->fc_ifp, is_gw, m, dst, (u_char *) destfw, NULL); if (error) return (error); Index: sys/net/if_iso88025subr.c =================================================================== --- sys/net/if_iso88025subr.c +++ sys/net/if_iso88025subr.c @@ -293,7 +293,7 @@ #endif /* INET */ #ifdef INET6 case AF_INET6: - error = nd6_storelladdr(ifp, m, dst, (u_char *)edst, NULL); + error = nd6_resolve(ifp, m, dst, edst, NULL); if (error) return (error); snap_type = ETHERTYPE_IPV6; Index: sys/netinet6/ip6_forward.c =================================================================== --- sys/netinet6/ip6_forward.c +++ sys/netinet6/ip6_forward.c @@ -584,7 +584,7 @@ } pass: - error = nd6_output(rt->rt_ifp, origifp, m, dst, rt); + error = nd6_output_ifp(rt->rt_ifp, origifp, m, dst); if (error) { in6_ifstat_inc(rt->rt_ifp, ifs6_out_discard); IP6STAT_INC(ip6s_cantforward); Index: sys/netinet6/ip6_output.c =================================================================== --- sys/netinet6/ip6_output.c +++ sys/netinet6/ip6_output.c @@ -882,7 +882,7 @@ m->m_pkthdr.len); ifa_free(&ia6->ia_ifa); } - error = nd6_output(ifp, origifp, m, dst, ro->ro_rt); + error = nd6_output_ifp(ifp, origifp, m, dst); goto done; } @@ -1038,7 +1038,7 @@ counter_u64_add(ia->ia_ifa.ifa_obytes, m->m_pkthdr.len); } - error = nd6_output(ifp, origifp, m, dst, ro->ro_rt); + error = nd6_output_ifp(ifp, origifp, m, dst); } else m_freem(m); } Index: sys/netinet6/nd6.h =================================================================== --- sys/netinet6/nd6.h +++ sys/netinet6/nd6.h @@ -391,33 +391,30 @@ #endif struct nd_ifinfo *nd6_ifattach(struct ifnet *); void nd6_ifdetach(struct nd_ifinfo *); -int nd6_is_addr_neighbor(struct sockaddr_in6 *, struct ifnet *); +int nd6_is_addr_neighbor(const struct sockaddr_in6 *, struct ifnet *); void nd6_option_init(void *, int, union nd_opts *); struct nd_opt_hdr *nd6_option(union nd_opts *); int nd6_options(union nd_opts *); -struct llentry *nd6_lookup(struct in6_addr *, int, struct ifnet *); +struct llentry *nd6_lookup(const struct in6_addr *, int, struct ifnet *); void nd6_setmtu(struct ifnet *); void nd6_llinfo_settimer(struct llentry *, long); void nd6_llinfo_settimer_locked(struct llentry *, long); void nd6_timer(void *); void nd6_purge(struct ifnet *); void nd6_nud_hint(struct rtentry *, struct in6_addr *, int); -int nd6_resolve(struct ifnet *, struct rtentry *, struct mbuf *, - struct sockaddr *, u_char *); int nd6_ioctl(u_long, caddr_t, struct ifnet *); struct llentry *nd6_cache_lladdr(struct ifnet *, struct in6_addr *, char *, int, int, int); -int nd6_output(struct ifnet *, struct ifnet *, struct mbuf *, - struct sockaddr_in6 *, struct rtentry *); void nd6_grab_holdchain(struct llentry *, struct mbuf **, struct sockaddr_in6 *); int nd6_flush_holdchain(struct ifnet *, struct ifnet *, struct mbuf *, struct sockaddr_in6 *); -int nd6_need_cache(struct ifnet *); int nd6_add_ifa_lle(struct in6_ifaddr *); void nd6_rem_ifa_lle(struct in6_ifaddr *); -int nd6_storelladdr(struct ifnet *, struct mbuf *, - const struct sockaddr *, u_char *, uint32_t *); +int nd6_output_ifp(struct ifnet *, struct ifnet *, struct mbuf *, + struct sockaddr_in6 *); +int nd6_resolve(struct ifnet *, int, struct mbuf *, + const struct sockaddr *, u_char *, uint32_t *); /* nd6_nbr.c */ void nd6_na_input(struct mbuf *, int, int); Index: sys/netinet6/nd6.c =================================================================== --- sys/netinet6/nd6.c +++ sys/netinet6/nd6.c @@ -125,7 +125,7 @@ int (*send_sendso_input_hook)(struct mbuf *, struct ifnet *, int, int); -static int nd6_is_new_addr_neighbor(struct sockaddr_in6 *, +static int nd6_is_new_addr_neighbor(const struct sockaddr_in6 *, struct ifnet *); static void nd6_setmtu0(struct ifnet *, struct nd_ifinfo *); static void nd6_slowtimo(void *); @@ -134,10 +134,9 @@ static void nd6_llinfo_timer(void *); static void clear_llinfo_pqueue(struct llentry *); static void nd6_rtrequest(int, struct rtentry *, struct rt_addrinfo *); -static int nd6_output_lle(struct ifnet *, struct ifnet *, struct mbuf *, - struct sockaddr_in6 *); -static int nd6_output_ifp(struct ifnet *, struct ifnet *, struct mbuf *, - struct sockaddr_in6 *); +static int nd6_resolve_slow(struct ifnet *, struct mbuf *, + const struct sockaddr_in6 *, u_char *, uint32_t *); +static int nd6_need_cache(struct ifnet *); static VNET_DEFINE(struct callout, nd6_slowtimo_ch); #define V_nd6_slowtimo_ch VNET(nd6_slowtimo_ch) @@ -846,7 +845,7 @@ * Returns the llentry locked */ struct llentry * -nd6_lookup(struct in6_addr *addr6, int flags, struct ifnet *ifp) +nd6_lookup(const struct in6_addr *addr6, int flags, struct ifnet *ifp) { struct sockaddr_in6 sin6; struct llentry *ln; @@ -878,7 +877,7 @@ * to not reenter the routing code from within itself. */ static int -nd6_is_new_addr_neighbor(struct sockaddr_in6 *addr, struct ifnet *ifp) +nd6_is_new_addr_neighbor(const struct sockaddr_in6 *addr, struct ifnet *ifp) { struct nd_prefix *pr; struct ifaddr *dstaddr; @@ -979,7 +978,7 @@ * XXX: should take care of the destination of a p2p link? */ int -nd6_is_addr_neighbor(struct sockaddr_in6 *addr, struct ifnet *ifp) +nd6_is_addr_neighbor(const struct sockaddr_in6 *addr, struct ifnet *ifp) { struct llentry *lle; int rc = 0; @@ -1831,7 +1830,7 @@ } } -static int +int nd6_output_ifp(struct ifnet *ifp, struct ifnet *origifp, struct mbuf *m, struct sockaddr_in6 *dst) { @@ -1877,31 +1876,62 @@ } /* - * IPv6 packet output - light version. - * Checks if destination LLE exists and is in proper state - * (e.g no modification required). If not true, fall back to - * "heavy" version. + * Do L2 address resolution for @sa_dst address. Stores found + * address in @desten buffer. Copy of lle ln_flags can be also + * saved in @pflags if @pflags is non-NULL. + * + * If destination LLE does not exists or lle state modification + * is required, call "slow" version. + * + * Return values: + * - 0 on success (address copied to buffer). + * - EWOULDBLOCK (no local error, but address is still unresolved) + * - other errors (alloc failure, etc) */ int -nd6_output(struct ifnet *ifp, struct ifnet *origifp, struct mbuf *m, - struct sockaddr_in6 *dst, struct rtentry *rt0) +nd6_resolve(struct ifnet *ifp, int is_gw, struct mbuf *m, + const struct sockaddr *sa_dst, u_char *desten, uint32_t *pflags) { struct llentry *ln = NULL; + const struct sockaddr_in6 *dst6; + + if (pflags != NULL) + *pflags = 0; + + dst6 = (const struct sockaddr_in6 *)sa_dst; /* discard the packet if IPv6 operation is disabled on the interface */ if ((ND_IFINFO(ifp)->flags & ND6_IFF_IFDISABLED)) { - m_freem(m); + if (m != NULL) + m_freem(m); return (ENETDOWN); /* better error? */ } - if (IN6_IS_ADDR_MULTICAST(&dst->sin6_addr)) - goto sendpkt; + if (m != NULL && m->m_flags & M_MCAST) { + int i; - if (nd6_need_cache(ifp) == 0) - goto sendpkt; + switch (ifp->if_type) { + case IFT_ETHER: + case IFT_FDDI: +#ifdef IFT_L2VLAN + case IFT_L2VLAN: +#endif +#ifdef IFT_IEEE80211 + case IFT_IEEE80211: +#endif + case IFT_BRIDGE: + case IFT_ISO88025: + ETHER_MAP_IPV6_MULTICAST(&dst6->sin6_addr, + desten); + return (0); + default: + m_freem(m); + return (EAFNOSUPPORT); + } + } IF_AFDATA_RLOCK(ifp); - ln = nd6_lookup(&dst->sin6_addr, 0, ifp); + ln = nd6_lookup(&dst6->sin6_addr, 0, ifp); IF_AFDATA_RUNLOCK(ifp); /* @@ -1917,46 +1947,33 @@ /* Fall back to slow processing path */ if (ln != NULL) LLE_RUNLOCK(ln); - return (nd6_output_lle(ifp, origifp, m, dst)); + return (nd6_resolve_slow(ifp, m, dst6, desten, pflags)); } -sendpkt: - if (ln != NULL) - LLE_RUNLOCK(ln); - - return (nd6_output_ifp(ifp, origifp, m, dst)); + bcopy(&ln->ll_addr, desten, ifp->if_addrlen); + if (pflags != NULL) + *pflags = ln->la_flags; + LLE_RUNLOCK(ln); + return (0); } /* - * Output IPv6 packet - heavy version. - * Function assume that either - * 1) destination LLE does not exist, is invalid or stale, so - * ND6_EXCLUSIVE lock needs to be acquired - * 2) destination lle is provided (with ND6_EXCLUSIVE lock), - * in that case packets are queued in &chain. + * Do L2 address resolution for @sa_dst address. Stores found + * address in @desten buffer. Copy of lle ln_flags can be also + * saved in @pflags if @pflags is non-NULL. * + * Heavy version. + * Function assume that destination LLE does not exist, + * is invalid or stale, so ND6_EXCLUSIVE lock needs to be acquired. */ static int -nd6_output_lle(struct ifnet *ifp, struct ifnet *origifp, struct mbuf *m, - struct sockaddr_in6 *dst) +nd6_resolve_slow(struct ifnet *ifp, struct mbuf *m, + const struct sockaddr_in6 *dst, u_char *desten, uint32_t *pflags) { struct llentry *lle = NULL; int flags = 0; - KASSERT(m != NULL, ("NULL mbuf, nothing to send")); - /* discard the packet if IPv6 operation is disabled on the interface */ - if ((ND_IFINFO(ifp)->flags & ND6_IFF_IFDISABLED)) { - m_freem(m); - return (ENETDOWN); /* better error? */ - } - - if (IN6_IS_ADDR_MULTICAST(&dst->sin6_addr)) - goto sendpkt; - - if (nd6_need_cache(ifp) == 0) - goto sendpkt; - /* * Address resolution or Neighbor Unreachability Detection * for the next hop. @@ -1980,28 +1997,20 @@ } } if (lle == NULL) { - if ((ifp->if_flags & IFF_POINTOPOINT) == 0 && - !(ND_IFINFO(ifp)->flags & ND6_IFF_PERFORMNUD)) { + if (!(ND_IFINFO(ifp)->flags & ND6_IFF_PERFORMNUD)) { char ip6buf[INET6_ADDRSTRLEN]; log(LOG_DEBUG, - "nd6_output: can't allocate llinfo for %s " - "(ln=%p)\n", - ip6_sprintf(ip6buf, &dst->sin6_addr), lle); - m_freem(m); - return (ENOBUFS); + "nd6_resolve_slow: can't allocate llinfo for %s\n", + ip6_sprintf(ip6buf, &dst->sin6_addr)); } - goto sendpkt; /* send anyway */ + + if (m != NULL) + m_freem(m); + return (ENOBUFS); } LLE_WLOCK_ASSERT(lle); - /* We don't have to do link-layer address resolution on a p2p link. */ - if ((ifp->if_flags & IFF_POINTOPOINT) != 0 && - lle->ln_state < ND6_LLINFO_REACHABLE) { - lle->ln_state = ND6_LLINFO_STALE; - nd6_llinfo_settimer_locked(lle, (long)V_nd6_gctimer * hz); - } - /* * The first time we send a packet to a neighbor whose entry is * STALE, we have to change the state to DELAY and a sets a timer to @@ -2020,8 +2029,13 @@ * (i.e. its link-layer address is already resolved), just * send the packet. */ - if (lle->ln_state > ND6_LLINFO_INCOMPLETE) - goto sendpkt; + if (lle->ln_state > ND6_LLINFO_INCOMPLETE) { + bcopy(&lle->ll_addr, desten, ifp->if_addrlen); + if (pflags != NULL) + *pflags = lle->la_flags; + LLE_WUNLOCK(lle); + return (0); + } /* * There is a neighbor cache entry, but no ethernet address @@ -2071,13 +2085,7 @@ LLE_WUNLOCK(lle); } - return (0); - - sendpkt: - if (lle != NULL) - LLE_WUNLOCK(lle); - - return (nd6_output_ifp(ifp, origifp, m, dst)); + return (EWOULDBLOCK); } @@ -2103,15 +2111,13 @@ /* * XXX - * note that intermediate errors are blindly ignored - but this is - * the same convention as used with nd6_output when called by - * nd6_cache_lladdr + * note that intermediate errors are blindly ignored */ return (error); } -int +static int nd6_need_cache(struct ifnet *ifp) { /* @@ -2199,65 +2205,6 @@ (struct sockaddr *)&mask, LLE_STATIC); } -/* - * the callers of this function need to be re-worked to drop - * the lle lock, drop here for now - */ -int -nd6_storelladdr(struct ifnet *ifp, struct mbuf *m, - const struct sockaddr *dst, u_char *desten, uint32_t *pflags) -{ - struct llentry *ln; - - if (pflags != NULL) - *pflags = 0; - IF_AFDATA_UNLOCK_ASSERT(ifp); - if (m != NULL && m->m_flags & M_MCAST) { - switch (ifp->if_type) { - case IFT_ETHER: - case IFT_FDDI: -#ifdef IFT_L2VLAN - case IFT_L2VLAN: -#endif -#ifdef IFT_IEEE80211 - case IFT_IEEE80211: -#endif - case IFT_BRIDGE: - case IFT_ISO88025: - ETHER_MAP_IPV6_MULTICAST(&SIN6(dst)->sin6_addr, - desten); - return (0); - default: - m_freem(m); - return (EAFNOSUPPORT); - } - } - - - /* - * the entry should have been created in nd6_store_lladdr - */ - IF_AFDATA_RLOCK(ifp); - ln = lla_lookup(LLTABLE6(ifp), 0, dst); - IF_AFDATA_RUNLOCK(ifp); - if ((ln == NULL) || !(ln->la_flags & LLE_VALID)) { - if (ln != NULL) - LLE_RUNLOCK(ln); - /* this could happen, if we could not allocate memory */ - m_freem(m); - return (1); - } - - bcopy(&ln->ll_addr, desten, ifp->if_addrlen); - if (pflags != NULL) - *pflags = ln->la_flags; - LLE_RUNLOCK(ln); - /* - * A *small* use after free race exists here - */ - return (0); -} - static void clear_llinfo_pqueue(struct llentry *ln) { Index: sys/netpfil/pf/pf.c =================================================================== --- sys/netpfil/pf/pf.c +++ sys/netpfil/pf/pf.c @@ -5444,7 +5444,7 @@ if (IN6_IS_SCOPE_EMBED(&dst.sin6_addr)) dst.sin6_addr.s6_addr16[1] = htons(ifp->if_index); if ((u_long)m0->m_pkthdr.len <= ifp->if_mtu) - nd6_output(ifp, ifp, m0, &dst, NULL); + nd6_output_ifp(ifp, ifp, m0, &dst); else { in6_ifstat_inc(ifp, ifs6_in_toobig); if (r->rt != PF_DUPTO) Index: sys/ofed/drivers/infiniband/core/addr.c =================================================================== --- sys/ofed/drivers/infiniband/core/addr.c +++ sys/ofed/drivers/infiniband/core/addr.c @@ -472,7 +472,7 @@ #endif #ifdef INET6 case AF_INET6: - error = nd6_storelladdr(ifp, NULL, dst_in, (u_char *)edst,NULL); + error = nd6_resolve(ifp, is_gw, NULL, dst_in, edst, NULL); break; #endif default: Index: sys/ofed/drivers/infiniband/ulp/ipoib/ipoib_main.c =================================================================== --- sys/ofed/drivers/infiniband/ulp/ipoib/ipoib_main.c +++ sys/ofed/drivers/infiniband/ulp/ipoib/ipoib_main.c @@ -1332,7 +1332,7 @@ else if (m->m_flags & M_MCAST) ipv6_ib_mc_map(&((struct sockaddr_in6 *)dst)->sin6_addr, ifp->if_broadcastaddr, edst); else - error = nd6_storelladdr(ifp, m, dst, (u_char *)edst, NULL); + error = nd6_resolve(ifp, is_gw, m, dst, edst, NULL); if (error) return error; type = htons(ETHERTYPE_IPV6);