for the validity of a given outgoing route entry into a single function.
This change is inspired from FreeBSD r111767. The function introduced
here, rt_checkgate(), should hopefully die in a near future. Why should
it die? Well, it is way too late to do such validity checks: if your
kernel can ends up in ether_output() with an invalid route, please do
not let it try to find a new one that might do the job.
Go read this function if you're wondering why you're getting messages
like:
"arpresolve: X.X.X.X: route without link local address"
Since this horrible logic has survived 20 years of copy & past and small
modifications for workarounds without a single clear commit message, let's
assume it is full of dragons and try to play safe. This factorization is
not intended to change any behavior.
With much inputs from bluhm@, tested by weerd@ and florian@ on setups
with p2p IPv6 interfaces.
ok bluhm@, benno@, florian@
-/* $OpenBSD: if_ethersubr.c,v 1.184 2014/12/19 17:14:39 tedu Exp $ */
+/* $OpenBSD: if_ethersubr.c,v 1.185 2015/01/08 14:29:18 mpi Exp $ */
/* $NetBSD: if_ethersubr.c,v 1.19 1996/05/07 02:40:30 thorpej Exp $ */
/*
*/
int
ether_output(struct ifnet *ifp0, struct mbuf *m0, struct sockaddr *dst,
- struct rtentry *rt0)
+ struct rtentry *rt)
{
u_int16_t etype;
int s, len, error = 0;
u_char edst[ETHER_ADDR_LEN];
u_char *esrc;
struct mbuf *m = m0;
- struct rtentry *rt;
struct mbuf *mcopy = NULL;
struct ether_header *eh;
struct arpcom *ac = (struct arpcom *)ifp0;
if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING))
senderr(ENETDOWN);
- if ((rt = rt0) != NULL) {
- if ((rt->rt_flags & RTF_UP) == 0) {
- if ((rt0 = rt = rtalloc(dst, RT_REPORT|RT_RESOLVE,
- m->m_pkthdr.ph_rtableid)) != NULL)
- rt->rt_refcnt--;
- else
- senderr(EHOSTUNREACH);
- }
- if (rt->rt_flags & RTF_GATEWAY) {
- if (rt->rt_gwroute == NULL)
- goto lookup;
- if (((rt = rt->rt_gwroute)->rt_flags & RTF_UP) == 0) {
- rtfree(rt);
- rt = rt0;
- lookup:
- rt->rt_gwroute = rtalloc(rt->rt_gateway,
- RT_REPORT|RT_RESOLVE, ifp->if_rdomain);
- if ((rt = rt->rt_gwroute) == NULL)
- senderr(EHOSTUNREACH);
- }
- }
- if (rt->rt_flags & RTF_REJECT)
- if (rt->rt_rmx.rmx_expire == 0 ||
- time_second < rt->rt_rmx.rmx_expire)
- senderr(rt == rt0 ? EHOSTDOWN : EHOSTUNREACH);
- }
switch (dst->sa_family) {
-
case AF_INET:
- if (!arpresolve(ac, rt, m, dst, edst))
- return (0); /* if not yet resolved */
+ error = arpresolve(ac, rt, m, dst, edst);
+ if (error)
+ return (error == EAGAIN ? 0 : error);
/* If broadcasting on a simplex interface, loopback a copy */
if ((m->m_flags & M_BCAST) && (ifp->if_flags & IFF_SIMPLEX) &&
!m->m_pkthdr.pf.routed)
break;
#ifdef INET6
case AF_INET6:
- if (!nd6_storelladdr(ifp, rt, m, dst, (u_char *)edst))
- return (0); /* it must be impossible, but... */
+ error = nd6_storelladdr(ifp, rt, m, dst, (u_char *)edst);
+ if (error)
+ return (error);
etype = htons(ETHERTYPE_IPV6);
break;
#endif
sizeof(edst));
break;
case AF_INET:
- if (!arpresolve(ac, rt, m, dst, edst))
- return (0); /* if not yet resolved */
+ error = arpresolve(ac, rt, m, dst, edst);
+ if (error)
+ return (error == EAGAIN ? 0 : error);
break;
default:
senderr(EHOSTUNREACH);
-/* $OpenBSD: route.c,v 1.196 2014/12/29 11:53:58 mpi Exp $ */
+/* $OpenBSD: route.c,v 1.197 2015/01/08 14:29:18 mpi Exp $ */
/* $NetBSD: route.c,v 1.14 1996/02/13 22:00:46 christos Exp $ */
/*
return (0);
}
+int
+rt_checkgate(struct ifnet *ifp, struct rtentry *rt, struct sockaddr *dst,
+ unsigned int rtableid, struct rtentry **rtp)
+{
+ struct rtentry *rt0;
+
+ KASSERT(rt != NULL);
+
+ if ((rt->rt_flags & RTF_UP) == 0) {
+ rt = rtalloc(dst, RT_REPORT|RT_RESOLVE, rtableid);
+ rt->rt_refcnt--;
+ if (rt == NULL || rt->rt_ifp != ifp)
+ return (EHOSTUNREACH);
+ }
+
+ rt0 = rt;
+
+ if (rt->rt_flags & RTF_GATEWAY) {
+ if (rt->rt_gwroute && !(rt->rt_gwroute->rt_flags & RTF_UP)) {
+ rtfree(rt->rt_gwroute);
+ rt->rt_gwroute = NULL;
+ }
+ if (rt->rt_gwroute == NULL) {
+ rt->rt_gwroute = rtalloc(rt->rt_gateway,
+ RT_REPORT|RT_RESOLVE, rtableid);
+ if (rt->rt_gwroute == NULL)
+ return (EHOSTUNREACH);
+ }
+ rt = rt->rt_gwroute;
+ }
+
+ if (rt->rt_flags & RTF_REJECT)
+ if (rt->rt_expire == 0 || time_second < rt->rt_expire)
+ return (rt == rt0 ? EHOSTDOWN : EHOSTUNREACH);
+
+ *rtp = rt;
+ return (0);
+}
+
void
rt_maskedcopy(struct sockaddr *src, struct sockaddr *dst,
struct sockaddr *netmask)
-/* $OpenBSD: route.h,v 1.101 2014/11/24 12:43:54 mpi Exp $ */
+/* $OpenBSD: route.h,v 1.102 2015/01/08 14:29:18 mpi Exp $ */
/* $NetBSD: route.h,v 1.9 1996/02/13 22:00:49 christos Exp $ */
/*
u_int16_t rt_labelid; /* route label ID */
u_int8_t rt_priority; /* routing priority to use */
};
-#define rt_use rt_rmx.rmx_pksent
+#define rt_use rt_rmx.rmx_pksent
+#define rt_expire rt_rmx.rmx_expire
#endif /* _KERNEL */
void rt_newaddrmsg(int, struct ifaddr *, int, struct rtentry *);
int rt_setgate(struct rtentry *, struct sockaddr *,
struct sockaddr *, u_int);
+int rt_checkgate(struct ifnet *, struct rtentry *, struct sockaddr *,
+ unsigned int, struct rtentry **);
void rt_setmetrics(u_long, struct rt_metrics *, struct rt_kmetrics *);
void rt_getmetrics(struct rt_kmetrics *, struct rt_metrics *);
-/* $OpenBSD: if_ether.c,v 1.139 2014/12/19 17:14:40 tedu Exp $ */
+/* $OpenBSD: if_ether.c,v 1.140 2015/01/08 14:29:18 mpi Exp $ */
/* $NetBSD: if_ether.c,v 1.31 1996/05/11 12:59:58 mycroft Exp $ */
/*
int arpt_prune = (5*60*1); /* walk list every 5 minutes */
int arpt_keep = (20*60); /* once resolved, good for 20 more minutes */
int arpt_down = 20; /* once declared down, don't send for 20 secs */
-#define rt_expire rt_rmx.rmx_expire
void arptfree(struct llinfo_arp *);
void arptimer(void *);
* desten is filled in. If there is no entry in arptab,
* set one up and broadcast a request for the IP address.
* Hold onto this mbuf and resend it once the address
- * is finally resolved. A return value of 1 indicates
+ * is finally resolved. A return value of 0 indicates
* that desten has been filled in and the packet should be sent
- * normally; a 0 return indicates that the packet has been
- * taken over here, either now or for later transmission.
+ * normally; A return value of EAGAIN indicates that the packet
+ * has been taken over here, either now or for later transmission.
+ * Any other return value indicates an error.
*/
int
-arpresolve(struct arpcom *ac, struct rtentry *rt, struct mbuf *m,
+arpresolve(struct arpcom *ac, struct rtentry *rt0, struct mbuf *m,
struct sockaddr *dst, u_char *desten)
{
struct llinfo_arp *la;
struct sockaddr_dl *sdl;
+ struct rtentry *rt;
struct mbuf *mh;
char addr[INET_ADDRSTRLEN];
+ int error;
if (m->m_flags & M_BCAST) { /* broadcast */
memcpy(desten, etherbroadcastaddr, sizeof(etherbroadcastaddr));
- return (1);
+ return (0);
}
if (m->m_flags & M_MCAST) { /* multicast */
ETHER_MAP_IP_MULTICAST(&satosin(dst)->sin_addr, desten);
- return (1);
+ return (0);
}
- if (rt) {
+
+ if (rt0 != NULL) {
+ error = rt_checkgate(&ac->ac_if, rt0, dst,
+ m->m_pkthdr.ph_rtableid, &rt);
+ if (error) {
+ m_freem(m);
+ return (error);
+ }
+
la = (struct llinfo_arp *)rt->rt_llinfo;
if (la == NULL)
log(LOG_DEBUG, "arpresolve: %s: route without link "
}
if (la == 0 || rt == 0) {
m_freem(m);
- return (0);
+ return (EINVAL);
}
sdl = SDL(rt->rt_gateway);
/*
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);
- return 1;
+ return (0);
}
if (((struct ifnet *)ac)->if_flags & IFF_NOARP) {
m_freem(m);
- return 0;
+ return (EINVAL);
}
/*
}
}
}
- return (0);
+ return (EAGAIN);
}
/*
-/* $OpenBSD: nd6.c,v 1.128 2014/12/22 11:05:53 mpi Exp $ */
+/* $OpenBSD: nd6.c,v 1.129 2015/01/08 14:29:18 mpi Exp $ */
/* $KAME: nd6.c,v 1.280 2002/06/08 19:52:07 itojun Exp $ */
/*
{
struct mbuf *m = m0;
struct rtentry *rt = rt0;
- struct sockaddr_in6 *gw6 = NULL;
struct llinfo_nd6 *ln = NULL;
int error = 0;
#ifdef IPSEC
goto sendpkt;
/*
- * next hop determination. This routine is derived from ether_output.
+ * next hop determination.
*/
- if (rt) {
- if ((rt->rt_flags & RTF_UP) == 0) {
- if ((rt0 = rt = rtalloc(sin6tosa(dst),
- RT_REPORT|RT_RESOLVE,
- m->m_pkthdr.ph_rtableid)) != NULL)
- {
- rt->rt_refcnt--;
- if (rt->rt_ifp != ifp)
- senderr(EHOSTUNREACH);
- } else
- senderr(EHOSTUNREACH);
+ if (rt0 != NULL) {
+ error = rt_checkgate(ifp, rt0, sin6tosa(dst),
+ m->m_pkthdr.ph_rtableid, &rt);
+ if (error) {
+ m_freem(m);
+ return (error);
}
- if (rt->rt_flags & RTF_GATEWAY) {
- gw6 = satosin6(rt->rt_gateway);
-
- /*
- * We skip link-layer address resolution and NUD
- * if the gateway is not a neighbor from ND point
- * of view, regardless of the value of nd_ifinfo.flags.
- * The second condition is a bit tricky; we skip
- * if the gateway is our own address, which is
- * sometimes used to install a route to a p2p link.
- */
- if (!nd6_is_addr_neighbor(gw6, ifp) ||
- in6ifa_ifpwithaddr(ifp, &gw6->sin6_addr)) {
- /*
- * We allow this kind of tricky route only
- * when the outgoing interface is p2p.
- * XXX: we may need a more generic rule here.
- */
- if ((ifp->if_flags & IFF_POINTOPOINT) == 0)
- senderr(EHOSTUNREACH);
-
- goto sendpkt;
- }
-
- if (rt->rt_gwroute == 0)
- goto lookup;
- if (((rt = rt->rt_gwroute)->rt_flags & RTF_UP) == 0) {
- rtfree(rt); rt = rt0;
- lookup:
- rt->rt_gwroute = rtalloc(rt->rt_gateway,
- RT_REPORT|RT_RESOLVE,
- m->m_pkthdr.ph_rtableid);
- if ((rt = rt->rt_gwroute) == 0)
- senderr(EHOSTUNREACH);
- }
- }
+ /*
+ * We skip link-layer address resolution and NUD
+ * if the gateway is not a neighbor from ND point
+ * of view, regardless of the value of nd_ifinfo.flags.
+ * The second condition is a bit tricky; we skip
+ * if the gateway is our own address, which is
+ * sometimes used to install a route to a p2p link.
+ */
+ if ((ifp->if_flags & IFF_POINTOPOINT) &&
+ ((nd6_is_addr_neighbor(satosin6(rt_key(rt)), ifp) == 0) ||
+ in6ifa_ifpwithaddr(ifp, &satosin6(rt_key(rt))->sin6_addr)))
+ goto sendpkt;
}
/*
}
int
-nd6_storelladdr(struct ifnet *ifp, struct rtentry *rt, struct mbuf *m,
+nd6_storelladdr(struct ifnet *ifp, struct rtentry *rt0, struct mbuf *m,
struct sockaddr *dst, u_char *desten)
{
struct sockaddr_dl *sdl;
+ struct rtentry *rt;
+ int error;
if (m->m_flags & M_MCAST) {
switch (ifp->if_type) {
case IFT_ETHER:
ETHER_MAP_IPV6_MULTICAST(&satosin6(dst)->sin6_addr,
desten);
- return (1);
+ return (0);
break;
default:
m_freem(m);
- return (0);
+ return (EINVAL);
}
}
- if (rt == NULL) {
+ if (rt0 == NULL) {
/* this could happen, if we could not allocate memory */
m_freem(m);
- return (0);
+ return (ENOMEM);
}
+
+ error = rt_checkgate(ifp, rt0, dst, m->m_pkthdr.ph_rtableid, &rt);
+ if (error) {
+ m_freem(m);
+ return (error);
+ }
+
if (rt->rt_gateway->sa_family != AF_LINK) {
printf("nd6_storelladdr: something odd happens\n");
m_freem(m);
- return (0);
+ return (EINVAL);
}
sdl = SDL(rt->rt_gateway);
if (sdl->sdl_alen == 0) {
addr, sizeof(addr)),
ifp->if_xname);
m_freem(m);
- return (0);
+ return (EINVAL);
}
bcopy(LLADDR(sdl), desten, sdl->sdl_alen);
- return (1);
+ return (0);
}
/*