On Tue, May 24, 2016 at 09:27:54AM +0200, Martin Pieuchot wrote:
> On 18/05/16(Wed) 22:56, Martin Pieuchot wrote:
> > Back in the old cool days you could simply call ether_output() with a
> > stuffed sockaddr and it will do the L2 resolution for you... Well as
> > long as you do not care about IPv6 it's true.
> >
> > During the last years I tried to reduce the number of places where
> > ether_output() was called without passing it a "struct rtentry" as
> > last argument. The reason for this move is to remove a potential
> > route insertion in arpresolve(), done in read-only context (hot path).
>
> New diff with ND bits. Please test and report back.
Running this on my laptop, no issues so far. OK bluhm@
>
> Index: net/if_ethersubr.c
> ===================================================================
> RCS file: /cvs/src/sys/net/if_ethersubr.c,v
> retrieving revision 1.236
> diff -u -p -r1.236 if_ethersubr.c
> --- net/if_ethersubr.c 18 May 2016 20:15:14 -0000 1.236
> +++ net/if_ethersubr.c 23 May 2016 12:21:45 -0000
> @@ -193,8 +193,12 @@ ether_output(struct ifnet *ifp, struct m
> struct mbuf *mcopy = NULL;
> struct ether_header *eh;
> struct arpcom *ac = (struct arpcom *)ifp;
> + sa_family_t af = dst->sa_family;
> int error = 0;
>
> + KASSERT(rt != NULL || ISSET(m->m_flags, M_MCAST|M_BCAST) ||
> + af == AF_UNSPEC || af == pseudo_AF_HDRCMPLT);
> +
> #ifdef DIAGNOSTIC
> if (ifp->if_rdomain != rtable_l2(m->m_pkthdr.ph_rtableid)) {
> printf("%s: trying to send packet on wrong domain. "
> @@ -208,7 +212,7 @@ ether_output(struct ifnet *ifp, struct m
> if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING))
> senderr(ENETDOWN);
>
> - switch (dst->sa_family) {
> + switch (af) {
> case AF_INET:
> error = arpresolve(ifp, rt, m, dst, edst);
> if (error)
> Index: netinet/if_ether.c
> ===================================================================
> RCS file: /cvs/src/sys/netinet/if_ether.c,v
> retrieving revision 1.209
> diff -u -p -r1.209 if_ether.c
> --- netinet/if_ether.c 23 May 2016 09:23:43 -0000 1.209
> +++ netinet/if_ether.c 23 May 2016 12:21:45 -0000
> @@ -281,9 +281,8 @@ arpresolve(struct ifnet *ifp, struct rte
> struct llinfo_arp *la = NULL;
> struct sockaddr_dl *sdl;
> struct rtentry *rt = NULL;
> - struct mbuf *mh;
> char addr[INET_ADDRSTRLEN];
> - int error, created = 0;
> + int error;
>
> if (m->m_flags & M_BCAST) { /* broadcast */
> memcpy(desten, etherbroadcastaddr, sizeof(etherbroadcastaddr));
> @@ -294,41 +293,20 @@ arpresolve(struct ifnet *ifp, struct rte
> return (0);
> }
>
> - if (rt0 != NULL) {
> - error = rt_checkgate(rt0, &rt);
> - if (error) {
> - m_freem(m);
> - return (error);
> - }
> -
> - if ((rt->rt_flags & RTF_LLINFO) == 0) {
> - log(LOG_DEBUG, "%s: %s: route contains no arp"
> - " information\n", __func__, inet_ntop(AF_INET,
> - &satosin(rt_key(rt))->sin_addr, addr,
> - sizeof(addr)));
> - m_freem(m);
> - return (EINVAL);
> - }
> + error = rt_checkgate(rt0, &rt);
> + if (error) {
> + m_freem(m);
> + return (error);
> + }
>
> - la = (struct llinfo_arp *)rt->rt_llinfo;
> - if (la == NULL)
> - log(LOG_DEBUG, "%s: %s: route without link "
> - "local address\n", __func__, inet_ntop(AF_INET,
> - &satosin(dst)->sin_addr, addr, sizeof(addr)));
> - } else {
> - rt = arplookup(&satosin(dst)->sin_addr, 1, 0, ifp->if_rdomain);
> - if (rt != NULL) {
> - created = 1;
> - la = ((struct llinfo_arp *)rt->rt_llinfo);
> - }
> - if (la == NULL)
> - log(LOG_DEBUG, "%s: %s: can't allocate llinfo\n",
> - __func__,
> - inet_ntop(AF_INET, &satosin(dst)->sin_addr,
> - addr, sizeof(addr)));
> + if (!ISSET(rt->rt_flags, RTF_LLINFO)) {
> + log(LOG_DEBUG, "%s: %s: route contains no arp information\n",
> + __func__, inet_ntop(AF_INET, &satosin(rt_key(rt))->sin_addr,
> + addr, sizeof(addr)));
> + m_freem(m);
> + return (EINVAL);
> }
> - if (la == NULL || rt == NULL)
> - goto bad;
> +
> sdl = satosdl(rt->rt_gateway);
> if (sdl->sdl_alen > 0 && sdl->sdl_alen != ETHER_ADDR_LEN) {
> log(LOG_DEBUG, "%s: %s: incorrect arp information\n", __func__,
> @@ -336,6 +314,7 @@ arpresolve(struct ifnet *ifp, struct rte
> addr, sizeof(addr)));
> goto bad;
> }
> +
> /*
> * Check the address family and length is valid, the address
> * is resolved; otherwise, try to resolve.
> @@ -343,10 +322,9 @@ arpresolve(struct ifnet *ifp, struct rte
> if ((rt->rt_expire == 0 || rt->rt_expire > time_second) &&
> sdl->sdl_family == AF_LINK && sdl->sdl_alen != 0) {
> memcpy(desten, LLADDR(sdl), sdl->sdl_alen);
> - if (created)
> - rtfree(rt);
> return (0);
> }
> +
> if (ifp->if_flags & IFF_NOARP)
> goto bad;
>
> @@ -355,7 +333,11 @@ arpresolve(struct ifnet *ifp, struct rte
> * response yet. Insert mbuf in hold queue if below limit
> * if above the limit free the queue without queuing the new packet.
> */
> + la = (struct llinfo_arp *)rt->rt_llinfo;
> + KASSERT(la != NULL);
> if (la_hold_total < LA_HOLD_TOTAL && la_hold_total < nmbclust / 64) {
> + struct mbuf *mh;
> +
> if (ml_len(&la->la_ml) >= LA_HOLD_QUEUE) {
> mh = ml_dequeue(&la->la_ml);
> la_hold_total--;
> @@ -396,14 +378,11 @@ arpresolve(struct ifnet *ifp, struct rte
> }
> }
> }
> - if (created)
> - rtfree(rt);
> +
> return (EAGAIN);
>
> bad:
> m_freem(m);
> - if (created)
> - rtfree(rt);
> return (EINVAL);
> }
>
> Index: netinet6/nd6.c
> ===================================================================
> RCS file: /cvs/src/sys/netinet6/nd6.c,v
> retrieving revision 1.179
> diff -u -p -r1.179 nd6.c
> --- netinet6/nd6.c 17 May 2016 08:29:14 -0000 1.179
> +++ netinet6/nd6.c 23 May 2016 12:38:56 -0000
> @@ -1507,20 +1507,15 @@ nd6_output(struct ifnet *ifp, struct mbu
> struct mbuf *m = m0;
> struct rtentry *rt = rt0;
> struct llinfo_nd6 *ln = NULL;
> - int created = 0, error = 0;
> + int error = 0;
>
> if (IN6_IS_ADDR_MULTICAST(&dst->sin6_addr))
> goto sendpkt;
>
> - /*
> - * next hop determination.
> - */
> - if (rt0 != NULL) {
> - error = rt_checkgate(rt0, &rt);
> - if (error) {
> - m_freem(m);
> - return (error);
> - }
> + error = rt_checkgate(rt0, &rt);
> + if (error) {
> + m_freem(m);
> + return (error);
> }
>
> if (nd6_need_cache(ifp) == 0)
> @@ -1532,42 +1527,17 @@ nd6_output(struct ifnet *ifp, struct mbu
> * At this point, the destination of the packet must be a unicast
> * or an anycast address(i.e. not a multicast).
> */
> -
> - /* Look up the neighbor cache for the nexthop */
> - if (rt != NULL && (rt->rt_flags & RTF_LLINFO) != 0)
> - ln = (struct llinfo_nd6 *)rt->rt_llinfo;
> - else {
> - /*
> - * Since nd6_is_addr_neighbor() internally calls nd6_lookup(),
> - * the condition below is not very efficient. But we believe
> - * it is tolerable, because this should be a rare case.
> - */
> - if (nd6_is_addr_neighbor(dst, ifp)) {
> - rt = nd6_lookup(&dst->sin6_addr, 1, ifp,
> - ifp->if_rdomain);
> - if (rt != NULL) {
> - created = 1;
> - ln = (struct llinfo_nd6 *)rt->rt_llinfo;
> - }
> - }
> + if (!ISSET(rt->rt_flags, RTF_LLINFO)) {
> + char addr[INET6_ADDRSTRLEN];
> + log(LOG_DEBUG, "%s: %s: route contains no ND information\n",
> + __func__, inet_ntop(AF_INET6,
> + &satosin6(rt_key(rt))->sin6_addr, addr, sizeof(addr)));
> + m_freem(m);
> + return (EINVAL);
> }
> - if (ln == NULL || rt == NULL) {
> - if ((ND_IFINFO(ifp)->flags & ND6_IFF_PERFORMNUD) == 0) {
> - char addr[INET6_ADDRSTRLEN];
> -
> - log(LOG_DEBUG, "%s: can't allocate llinfo for %s "
> - "(ln=%p, rt=%p)\n", __func__,
> - inet_ntop(AF_INET6, &dst->sin6_addr,
> - addr, sizeof(addr)),
> - ln, rt);
> - m_freem(m);
> - if (created)
> - rtfree(rt);
> - return (EIO); /* XXX: good error? */
> - }
>
> - goto sendpkt; /* send anyway */
> - }
> + ln = (struct llinfo_nd6 *)rt->rt_llinfo;
> + KASSERT(ln != NULL);
>
> /*
> * Move this entry to the head of the queue so that it is less likely
> @@ -1617,14 +1587,10 @@ nd6_output(struct ifnet *ifp, struct mbu
> (long)ND_IFINFO(ifp)->retrans * hz / 1000);
> nd6_ns_output(ifp, NULL, &dst->sin6_addr, ln, 0);
> }
> - if (created)
> - rtfree(rt);
> return (0);
>
> sendpkt:
> error = ifp->if_output(ifp, m, sin6tosa(dst), rt);
> - if (created)
> - rtfree(rt);
> return (error);
> }
>
> @@ -1665,12 +1631,6 @@ nd6_storelladdr(struct ifnet *ifp, struc
> m_freem(m);
> return (EINVAL);
> }
> - }
> -
> - if (rt0 == NULL) {
> - /* this could happen, if we could not allocate memory */
> - m_freem(m);
> - return (ENOMEM);
> }
>
> error = rt_checkgate(rt0, &rt);