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.
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);