Index: head/sys/net/flowtable.c =================================================================== --- head/sys/net/flowtable.c +++ head/sys/net/flowtable.c @@ -696,13 +696,8 @@ ro->ro_rt = fle->f_rt; ro->ro_flags |= RT_NORTREF; lle = fle->f_lle; - if (lle != NULL && (lle->la_flags & LLE_VALID)) { - ro->ro_prepend = lle->r_linkdata; - ro->ro_plen = lle->r_hdrlen; - ro->ro_flags |= RT_MAY_LOOP; - if (lle->la_flags & LLE_IFADDR) - ro->ro_flags |= RT_L2_ME; - } + if (lle != NULL && (lle->la_flags & LLE_VALID)) + ro->ro_lle = lle; /* share ref with fle->f_lle */ return (0); } Index: head/sys/net/if_arcsubr.c =================================================================== --- head/sys/net/if_arcsubr.c +++ head/sys/net/if_arcsubr.c @@ -129,7 +129,8 @@ else if (ifp->if_flags & IFF_NOARP) adst = ntohl(SIN(dst)->sin_addr.s_addr) & 0xFF; else { - error = arpresolve(ifp, is_gw, m, dst, &adst, NULL); + error = arpresolve(ifp, is_gw, m, dst, &adst, NULL, + NULL); if (error) return (error == EWOULDBLOCK ? 0 : error); } @@ -170,7 +171,8 @@ if ((m->m_flags & M_MCAST) != 0) adst = arcbroadcastaddr; /* ARCnet broadcast address */ else { - error = nd6_resolve(ifp, is_gw, m, dst, &adst, NULL); + error = nd6_resolve(ifp, is_gw, m, dst, &adst, NULL, + NULL); if (error != 0) return (error == EWOULDBLOCK ? 0 : error); } Index: head/sys/net/if_ethersubr.c =================================================================== --- head/sys/net/if_ethersubr.c +++ head/sys/net/if_ethersubr.c @@ -199,7 +199,7 @@ static int ether_resolve_addr(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst, struct route *ro, u_char *phdr, - uint32_t *pflags) + uint32_t *pflags, struct llentry **plle) { struct ether_header *eh; uint32_t lleflags = 0; @@ -208,13 +208,16 @@ uint16_t etype; #endif + if (plle) + *plle = NULL; eh = (struct ether_header *)phdr; switch (dst->sa_family) { #ifdef INET case AF_INET: if ((m->m_flags & (M_BCAST | M_MCAST)) == 0) - error = arpresolve(ifp, 0, m, dst, phdr, &lleflags); + error = arpresolve(ifp, 0, m, dst, phdr, &lleflags, + plle); else { if (m->m_flags & M_BCAST) memcpy(eh->ether_dhost, ifp->if_broadcastaddr, @@ -233,7 +236,8 @@ #ifdef INET6 case AF_INET6: if ((m->m_flags & M_MCAST) == 0) - error = nd6_resolve(ifp, 0, m, dst, phdr, &lleflags); + error = nd6_resolve(ifp, 0, m, dst, phdr, &lleflags, + plle); else { const struct in6_addr *a6; a6 = &(((const struct sockaddr_in6 *)dst)->sin6_addr); @@ -283,14 +287,40 @@ int loop_copy = 1; int hlen; /* link layer header length */ uint32_t pflags; + struct llentry *lle = NULL; + struct rtentry *rt0 = NULL; + int addref = 0; phdr = NULL; pflags = 0; if (ro != NULL) { - phdr = ro->ro_prepend; - hlen = ro->ro_plen; - pflags = ro->ro_flags; + /* XXX BPF uses ro_prepend */ + if (ro->ro_prepend != NULL) { + phdr = ro->ro_prepend; + hlen = ro->ro_plen; + } else if (!(m->m_flags & (M_BCAST | M_MCAST))) { + if ((ro->ro_flags & RT_LLE_CACHE) != 0) { + lle = ro->ro_lle; + if (lle != NULL && + (lle->la_flags & LLE_VALID) == 0) { + LLE_FREE(lle); + lle = NULL; /* redundant */ + ro->ro_lle = NULL; + } + if (lle == NULL) { + /* if we lookup, keep cache */ + addref = 1; + } + } + if (lle != NULL) { + phdr = lle->r_linkdata; + hlen = lle->r_hdrlen; + pflags = lle->r_flags; + } + } + rt0 = ro->ro_rt; } + #ifdef MAC error = mac_ifnet_check_transmit(ifp, m); if (error) @@ -308,7 +338,10 @@ /* No prepend data supplied. Try to calculate ourselves. */ phdr = linkhdr; hlen = ETHER_HDR_LEN; - error = ether_resolve_addr(ifp, m, dst, ro, phdr, &pflags); + error = ether_resolve_addr(ifp, m, dst, ro, phdr, &pflags, + addref ? &lle : NULL); + if (addref && lle != NULL) + ro->ro_lle = lle; if (error != 0) return (error == EWOULDBLOCK ? 0 : error); } Index: head/sys/net/if_fddisubr.c =================================================================== --- head/sys/net/if_fddisubr.c +++ head/sys/net/if_fddisubr.c @@ -126,7 +126,7 @@ switch (dst->sa_family) { #ifdef INET case AF_INET: { - error = arpresolve(ifp, is_gw, m, dst, edst, NULL); + error = arpresolve(ifp, is_gw, m, dst, edst, NULL, NULL); if (error) return (error == EWOULDBLOCK ? 0 : error); type = htons(ETHERTYPE_IP); @@ -162,7 +162,7 @@ #endif /* INET */ #ifdef INET6 case AF_INET6: - error = nd6_resolve(ifp, is_gw, m, dst, edst, NULL); + error = nd6_resolve(ifp, is_gw, m, dst, edst, NULL, NULL); if (error) return (error == EWOULDBLOCK ? 0 : error); type = htons(ETHERTYPE_IPV6); Index: head/sys/net/if_fwsubr.c =================================================================== --- head/sys/net/if_fwsubr.c +++ head/sys/net/if_fwsubr.c @@ -144,7 +144,8 @@ * doesn't fit into the arp model. */ if (unicast) { - error = arpresolve(ifp, is_gw, m, dst, (u_char *) destfw, NULL); + error = arpresolve(ifp, is_gw, m, dst, + (u_char *) destfw, NULL, NULL); if (error) return (error == EWOULDBLOCK ? 0 : error); } @@ -174,7 +175,7 @@ case AF_INET6: if (unicast) { error = nd6_resolve(fc->fc_ifp, is_gw, m, dst, - (u_char *) destfw, NULL); + (u_char *) destfw, NULL, NULL); if (error) return (error == EWOULDBLOCK ? 0 : error); } Index: head/sys/net/if_iso88025subr.c =================================================================== --- head/sys/net/if_iso88025subr.c +++ head/sys/net/if_iso88025subr.c @@ -254,7 +254,7 @@ switch (dst->sa_family) { #ifdef INET case AF_INET: - error = arpresolve(ifp, is_gw, m, dst, edst, NULL); + error = arpresolve(ifp, is_gw, m, dst, edst, NULL, NULL); if (error) return (error == EWOULDBLOCK ? 0 : error); snap_type = ETHERTYPE_IP; @@ -289,7 +289,7 @@ #endif /* INET */ #ifdef INET6 case AF_INET6: - error = nd6_resolve(ifp, is_gw, m, dst, edst, NULL); + error = nd6_resolve(ifp, is_gw, m, dst, edst, NULL, NULL); if (error) return (error == EWOULDBLOCK ? 0 : error); snap_type = ETHERTYPE_IPV6; Index: head/sys/net/if_llatbl.h =================================================================== --- head/sys/net/if_llatbl.h +++ head/sys/net/if_llatbl.h @@ -138,7 +138,6 @@ LLE_FREE_LOCKED(lle); \ } while (0) - typedef struct llentry *(llt_lookup_t)(struct lltable *, u_int flags, const struct sockaddr *l3addr); typedef struct llentry *(llt_alloc_t)(struct lltable *, u_int flags, Index: head/sys/net/route.h =================================================================== --- head/sys/net/route.h +++ head/sys/net/route.h @@ -50,6 +50,11 @@ */ struct route { struct rtentry *ro_rt; + struct llentry *ro_lle; + /* + * ro_prepend and ro_plen are only used for bpf to pass in a + * preformed header. They are not cacheable. + */ char *ro_prepend; uint16_t ro_plen; uint16_t ro_flags; @@ -71,6 +76,7 @@ #define RT_REJECT 0x0020 /* Destination is reject */ #define RT_BLACKHOLE 0x0040 /* Destination is blackhole */ #define RT_HAS_GW 0x0080 /* Destination has GW */ +#define RT_LLE_CACHE 0x0100 /* Cache link layer */ struct rt_metrics { u_long rmx_locks; /* Kernel must leave these values alone */ @@ -399,6 +405,7 @@ if ((_ro)->ro_flags & RT_NORTREF) { \ (_ro)->ro_flags &= ~RT_NORTREF; \ (_ro)->ro_rt = NULL; \ + (_ro)->ro_lle = NULL; \ } else { \ RT_LOCK((_ro)->ro_rt); \ RTFREE_LOCKED((_ro)->ro_rt); \ @@ -413,9 +420,11 @@ */ #define RT_VALIDATE(ro, cookiep, fibnum) do { \ rt_gen_t cookie = RT_GEN(fibnum, (ro)->ro_dst.sa_family); \ - if (*(cookiep) != cookie && (ro)->ro_rt != NULL) { \ - RTFREE((ro)->ro_rt); \ - (ro)->ro_rt = NULL; \ + if (*(cookiep) != cookie) { \ + if ((ro)->ro_rt != NULL) { \ + RTFREE((ro)->ro_rt); \ + (ro)->ro_rt = NULL; \ + } \ *(cookiep) = cookie; \ } \ } while (0) Index: head/sys/net/route.c =================================================================== --- head/sys/net/route.c +++ head/sys/net/route.c @@ -207,6 +207,8 @@ struct rib_head *rnh; rnh = *rt_tables_get_rnh_ptr(table, fam); + KASSERT(rnh != NULL, ("%s: NULL rib_head pointer table %d fam %d", + __func__, table, fam)); return (rnh->rnh_gen); } Index: head/sys/netinet/if_ether.h =================================================================== --- head/sys/netinet/if_ether.h +++ head/sys/netinet/if_ether.h @@ -113,11 +113,14 @@ extern u_char ether_ipmulticast_max[ETHER_ADDR_LEN]; struct ifaddr; +struct llentry; int arpresolve_addr(struct ifnet *ifp, int flags, - const struct sockaddr *dst, char *desten, uint32_t *pflags); + const struct sockaddr *dst, char *desten, uint32_t *pflags, + struct llentry **plle); int arpresolve(struct ifnet *ifp, int is_gw, struct mbuf *m, - const struct sockaddr *dst, u_char *desten, uint32_t *pflags); + const struct sockaddr *dst, u_char *desten, uint32_t *pflags, + struct llentry **plle); void arprequest(struct ifnet *, const struct in_addr *, const struct in_addr *, u_char *); void arp_ifinit(struct ifnet *, struct ifaddr *); Index: head/sys/netinet/if_ether.c =================================================================== --- head/sys/netinet/if_ether.c +++ head/sys/netinet/if_ether.c @@ -420,7 +420,8 @@ */ static int arpresolve_full(struct ifnet *ifp, int is_gw, int flags, struct mbuf *m, - const struct sockaddr *dst, u_char *desten, uint32_t *pflags) + const struct sockaddr *dst, u_char *desten, uint32_t *pflags, + struct llentry **plle) { struct llentry *la = NULL, *la_tmp; struct mbuf *curr = NULL; @@ -431,6 +432,8 @@ if (pflags != NULL) *pflags = 0; + if (plle != NULL) + *plle = NULL; if ((flags & LLE_CREATE) == 0) { IF_AFDATA_RLOCK(ifp); @@ -483,6 +486,10 @@ } if (pflags != NULL) *pflags = la->la_flags & (LLE_VALID|LLE_IFADDR); + if (plle) { + LLE_ADDREF(la); + *plle = la; + } LLE_WUNLOCK(la); return (0); } @@ -548,12 +555,12 @@ */ int arpresolve_addr(struct ifnet *ifp, int flags, const struct sockaddr *dst, - char *desten, uint32_t *pflags) + char *desten, uint32_t *pflags, struct llentry **plle) { int error; flags |= LLE_ADDRONLY; - error = arpresolve_full(ifp, 0, flags, NULL, dst, desten, pflags); + error = arpresolve_full(ifp, 0, flags, NULL, dst, desten, pflags, plle); return (error); } @@ -576,12 +583,15 @@ */ int arpresolve(struct ifnet *ifp, int is_gw, struct mbuf *m, - const struct sockaddr *dst, u_char *desten, uint32_t *pflags) + const struct sockaddr *dst, u_char *desten, uint32_t *pflags, + struct llentry **plle) { struct llentry *la = NULL; if (pflags != NULL) *pflags = 0; + if (plle != NULL) + *plle = NULL; if (m != NULL) { if (m->m_flags & M_BCAST) { @@ -616,7 +626,7 @@ IF_AFDATA_RUNLOCK(ifp); return (arpresolve_full(ifp, is_gw, la == NULL ? LLE_CREATE : 0, m, dst, - desten, pflags)); + desten, pflags, plle)); } /* Index: head/sys/netinet/in_pcb.c =================================================================== --- head/sys/netinet/in_pcb.c +++ head/sys/netinet/in_pcb.c @@ -73,6 +73,7 @@ #include #include #include +#include #include #include #include @@ -1302,6 +1303,8 @@ RTFREE(inp->inp_route.ro_rt); inp->inp_route.ro_rt = (struct rtentry *)NULL; } + if (inp->inp_route.ro_lle) + LLE_FREE(inp->inp_route.ro_lle); /* zeros ro_lle */ inp->inp_vflag = 0; inp->inp_flags2 |= INP_FREED; @@ -2243,6 +2246,8 @@ RTFREE(inp->inp_route.ro_rt); inp->inp_route.ro_rt = (struct rtentry *)NULL; } + if (inp->inp_route.ro_lle) + LLE_FREE(inp->inp_route.ro_lle); /* zeros ro_lle */ return; } Index: head/sys/netinet/ip_output.c =================================================================== --- head/sys/netinet/ip_output.c +++ head/sys/netinet/ip_output.c @@ -245,7 +245,8 @@ if (ro == NULL) { ro = &iproute; bzero(ro, sizeof (*ro)); - } + } else + ro->ro_flags |= RT_LLE_CACHE; #ifdef FLOWTABLE if (ro->ro_rt == NULL) @@ -311,6 +312,9 @@ dst->sin_addr.s_addr != ip->ip_dst.s_addr)) { RTFREE(rte); rte = ro->ro_rt = (struct rtentry *)NULL; + if (ro->ro_lle) + LLE_FREE(ro->ro_lle); /* zeros ro_lle */ + ro->ro_lle = (struct llentry *)NULL; } ia = NULL; have_ia_ref = 0; Index: head/sys/netinet/toecore.c =================================================================== --- head/sys/netinet/toecore.c +++ head/sys/netinet/toecore.c @@ -451,12 +451,12 @@ switch (sa->sa_family) { #ifdef INET case AF_INET: - rc = arpresolve(ifp, 0, NULL, sa, lladdr, NULL); + rc = arpresolve(ifp, 0, NULL, sa, lladdr, NULL, NULL); break; #endif #ifdef INET6 case AF_INET6: - rc = nd6_resolve(ifp, 0, NULL, sa, lladdr, NULL); + rc = nd6_resolve(ifp, 0, NULL, sa, lladdr, NULL, NULL); break; #endif default: Index: head/sys/netinet6/in6.h =================================================================== --- head/sys/netinet6/in6.h +++ head/sys/netinet6/in6.h @@ -375,6 +375,11 @@ #if __BSD_VISIBLE struct route_in6 { struct rtentry *ro_rt; + struct llentry *ro_lle; + /* + * ro_prepend and ro_plen are only used for bpf to pass in a + * preformed header. They are not cacheable. + */ char *ro_prepend; uint16_t ro_plen; uint16_t ro_flags; Index: head/sys/netinet6/in6_pcb.c =================================================================== --- head/sys/netinet6/in6_pcb.c +++ head/sys/netinet6/in6_pcb.c @@ -92,6 +92,7 @@ #include #include +#include #include #include @@ -831,6 +832,8 @@ RTFREE(in6p->inp_route6.ro_rt); in6p->inp_route6.ro_rt = (struct rtentry *)NULL; } + if (in6p->inp_route.ro_lle) + LLE_FREE(in6p->inp_route.ro_lle); /* zeros ro_lle */ return; } @@ -846,6 +849,8 @@ RTFREE(inp->inp_route6.ro_rt); inp->inp_route6.ro_rt = (struct rtentry *)NULL; } + if (inp->inp_route.ro_lle) + LLE_FREE(inp->inp_route.ro_lle); /* zeros ro_lle */ return inp; } Index: head/sys/netinet6/ip6_output.c =================================================================== --- head/sys/netinet6/ip6_output.c +++ head/sys/netinet6/ip6_output.c @@ -500,7 +500,8 @@ if (ro == NULL) { ro = &ip6route; bzero((caddr_t)ro, sizeof(*ro)); - } + } else + ro->ro_flags |= RT_LLE_CACHE; ro_pmtu = ro; if (opt && opt->ip6po_rthdr) ro = &opt->ip6po_route; Index: head/sys/netinet6/nd6.h =================================================================== --- head/sys/netinet6/nd6.h +++ head/sys/netinet6/nd6.h @@ -428,7 +428,7 @@ int nd6_resolve_addr(struct ifnet *ifp, int flags, const struct sockaddr *dst, char *desten, uint32_t *pflags); int nd6_resolve(struct ifnet *, int, struct mbuf *, - const struct sockaddr *, u_char *, uint32_t *); + const struct sockaddr *, u_char *, uint32_t *, struct llentry **); int nd6_ioctl(u_long, caddr_t, struct ifnet *); void nd6_cache_lladdr(struct ifnet *, struct in6_addr *, char *, int, int, int); Index: head/sys/netinet6/nd6.c =================================================================== --- head/sys/netinet6/nd6.c +++ head/sys/netinet6/nd6.c @@ -136,7 +136,7 @@ static void clear_llinfo_pqueue(struct llentry *); static void nd6_rtrequest(int, struct rtentry *, struct rt_addrinfo *); static int nd6_resolve_slow(struct ifnet *, int, struct mbuf *, - const struct sockaddr_in6 *, u_char *, uint32_t *); + const struct sockaddr_in6 *, u_char *, uint32_t *, struct llentry **); static int nd6_need_cache(struct ifnet *); @@ -2175,7 +2175,8 @@ */ int nd6_resolve(struct ifnet *ifp, int is_gw, struct mbuf *m, - const struct sockaddr *sa_dst, u_char *desten, uint32_t *pflags) + const struct sockaddr *sa_dst, u_char *desten, uint32_t *pflags, + struct llentry **plle) { struct llentry *ln = NULL; const struct sockaddr_in6 *dst6; @@ -2227,7 +2228,7 @@ } IF_AFDATA_RUNLOCK(ifp); - return (nd6_resolve_slow(ifp, 0, m, dst6, desten, pflags)); + return (nd6_resolve_slow(ifp, 0, m, dst6, desten, pflags, plle)); } @@ -2244,7 +2245,8 @@ */ static __noinline int nd6_resolve_slow(struct ifnet *ifp, int flags, struct mbuf *m, - const struct sockaddr_in6 *dst, u_char *desten, uint32_t *pflags) + const struct sockaddr_in6 *dst, u_char *desten, uint32_t *pflags, + struct llentry **plle) { struct llentry *lle = NULL, *lle_tmp; struct in6_addr *psrc, src; @@ -2331,6 +2333,10 @@ bcopy(lladdr, desten, ll_len); if (pflags != NULL) *pflags = lle->la_flags; + if (plle) { + LLE_ADDREF(lle); + *plle = lle; + } LLE_WUNLOCK(lle); return (0); } @@ -2405,7 +2411,7 @@ flags |= LLE_ADDRONLY; error = nd6_resolve_slow(ifp, flags, NULL, - (const struct sockaddr_in6 *)dst, desten, pflags); + (const struct sockaddr_in6 *)dst, desten, pflags, NULL); return (error); }