* Remove route caching in if_stf.

* Copy necessary in6_ifa on stack instead of playing with refcounts.
This commit is contained in:
Alexander V. Chernikov 2014-10-17 15:07:04 +00:00
parent 0e53ac55b7
commit d74b9a2c6a

View File

@ -124,10 +124,6 @@
SYSCTL_DECL(_net_link);
static SYSCTL_NODE(_net_link, IFT_STF, stf, CTLFLAG_RW, 0, "6to4 Interface");
static int stf_route_cache = 1;
SYSCTL_INT(_net_link_stf, OID_AUTO, route_cache, CTLFLAG_RW,
&stf_route_cache, 0, "Caching of IPv4 routes for 6to4 Output");
static int stf_permit_rfc1918 = 0;
SYSCTL_INT(_net_link_stf, OID_AUTO, permit_rfc1918, CTLFLAG_RWTUN,
&stf_permit_rfc1918, 0, "Permit the use of private IPv4 addresses");
@ -144,11 +140,6 @@ SYSCTL_INT(_net_link_stf, OID_AUTO, permit_rfc1918, CTLFLAG_RWTUN,
struct stf_softc {
struct ifnet *sc_ifp;
union {
struct route __sc_ro4;
struct route_in6 __sc_ro6; /* just for safety */
} __sc_ro46;
#define sc_ro __sc_ro46.__sc_ro4
struct mtx sc_ro_mtx;
u_int sc_fibnum;
const struct encaptab *encap_cookie;
@ -180,7 +171,7 @@ static char *stfnames[] = {"stf0", "stf", "6to4", NULL};
static int stfmodevent(module_t, int, void *);
static int stf_encapcheck(const struct mbuf *, int, int, void *);
static struct in6_ifaddr *stf_getsrcifa6(struct ifnet *);
static int stf_getsrcifa6(struct ifnet *, struct in6_addr *, struct in6_addr *);
static int stf_output(struct ifnet *, struct mbuf *, const struct sockaddr *,
struct route *);
static int isrfc1918addr(struct in_addr *);
@ -320,9 +311,9 @@ stf_encapcheck(m, off, proto, arg)
void *arg;
{
struct ip ip;
struct in6_ifaddr *ia6;
struct stf_softc *sc;
struct in_addr a, b, mask;
struct in6_addr addr6, mask6;
sc = (struct stf_softc *)arg;
if (sc == NULL)
@ -344,20 +335,16 @@ stf_encapcheck(m, off, proto, arg)
if (ip.ip_v != 4)
return 0;
ia6 = stf_getsrcifa6(STF2IFP(sc));
if (ia6 == NULL)
return 0;
if (stf_getsrcifa6(STF2IFP(sc), &addr6, &mask6) != 0)
return (0);
/*
* check if IPv4 dst matches the IPv4 address derived from the
* local 6to4 address.
* success on: dst = 10.1.1.1, ia6->ia_addr = 2002:0a01:0101:...
*/
if (bcmp(GET_V4(&ia6->ia_addr.sin6_addr), &ip.ip_dst,
sizeof(ip.ip_dst)) != 0) {
ifa_free(&ia6->ia_ifa);
if (bcmp(GET_V4(&addr6), &ip.ip_dst, sizeof(ip.ip_dst)) != 0)
return 0;
}
/*
* check if IPv4 src matches the IPv4 address derived from the
@ -366,9 +353,8 @@ stf_encapcheck(m, off, proto, arg)
* fail on: src = 10.1.1.1, ia6->ia_addr = 2002:0b00:.../24
*/
bzero(&a, sizeof(a));
bcopy(GET_V4(&ia6->ia_addr.sin6_addr), &a, sizeof(a));
bcopy(GET_V4(&ia6->ia_prefixmask.sin6_addr), &mask, sizeof(mask));
ifa_free(&ia6->ia_ifa);
bcopy(GET_V4(&addr6), &a, sizeof(a));
bcopy(GET_V4(&mask6), &mask, sizeof(mask));
a.s_addr &= mask.s_addr;
b = ip.ip_src;
b.s_addr &= mask.s_addr;
@ -379,12 +365,12 @@ stf_encapcheck(m, off, proto, arg)
return 32;
}
static struct in6_ifaddr *
stf_getsrcifa6(ifp)
struct ifnet *ifp;
static int
stf_getsrcifa6(struct ifnet *ifp, struct in6_addr *addr, struct in6_addr *mask)
{
struct ifaddr *ia;
struct in_ifaddr *ia4;
struct in6_ifaddr *ia6;
struct sockaddr_in6 *sin6;
struct in_addr in;
@ -403,13 +389,16 @@ stf_getsrcifa6(ifp)
if (ia4 == NULL)
continue;
ifa_ref(ia);
ia6 = (struct in6_ifaddr *)ia;
*addr = sin6->sin6_addr;
*mask = ia6->ia_prefixmask.sin6_addr;
if_addr_runlock(ifp);
return (struct in6_ifaddr *)ia;
return (0);
}
if_addr_runlock(ifp);
return NULL;
return (ENOENT);
}
static int
@ -418,14 +407,12 @@ stf_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst,
{
struct stf_softc *sc;
const struct sockaddr_in6 *dst6;
struct route *cached_route;
struct in_addr in4;
const void *ptr;
struct sockaddr_in *dst4;
u_int8_t tos;
struct ip *ip;
struct ip6_hdr *ip6;
struct in6_ifaddr *ia6;
struct in6_addr addr6, mask6;
int error;
#ifdef MAC
@ -451,8 +438,7 @@ stf_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst,
* we shouldn't generate output. Without this check, we'll end up
* using wrong IPv4 source.
*/
ia6 = stf_getsrcifa6(ifp);
if (ia6 == NULL) {
if (stf_getsrcifa6(ifp, &addr6, &mask6) != 0) {
m_freem(m);
if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
return ENETDOWN;
@ -461,7 +447,6 @@ stf_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst,
if (m->m_len < sizeof(*ip6)) {
m = m_pullup(m, sizeof(*ip6));
if (!m) {
ifa_free(&ia6->ia_ifa);
if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
return ENOBUFS;
}
@ -479,7 +464,6 @@ stf_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst,
else if (IN6_IS_ADDR_6TO4(&dst6->sin6_addr))
ptr = GET_V4(&dst6->sin6_addr);
else {
ifa_free(&ia6->ia_ifa);
m_freem(m);
if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
return ENETUNREACH;
@ -502,7 +486,6 @@ stf_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst,
if (m && m->m_len < sizeof(struct ip))
m = m_pullup(m, sizeof(struct ip));
if (m == NULL) {
ifa_free(&ia6->ia_ifa);
if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
return ENOBUFS;
}
@ -510,9 +493,7 @@ stf_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst,
bzero(ip, sizeof(*ip));
bcopy(GET_V4(&((struct sockaddr_in6 *)&ia6->ia_addr)->sin6_addr),
&ip->ip_src, sizeof(ip->ip_src));
ifa_free(&ia6->ia_ifa);
bcopy(GET_V4(&addr6), &ip->ip_src, sizeof(ip->ip_src));
bcopy(&in4, &ip->ip_dst, sizeof(ip->ip_dst));
ip->ip_p = IPPROTO_IPV6;
ip->ip_ttl = ip_stf_ttl;
@ -522,46 +503,10 @@ stf_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst,
else
ip_ecn_ingress(ECN_NOCARE, &ip->ip_tos, &tos);
if (!stf_route_cache) {
cached_route = NULL;
goto sendit;
}
/*
* Do we have a cached route?
*/
mtx_lock(&(sc)->sc_ro_mtx);
dst4 = (struct sockaddr_in *)&sc->sc_ro.ro_dst;
if (dst4->sin_family != AF_INET ||
bcmp(&dst4->sin_addr, &ip->ip_dst, sizeof(ip->ip_dst)) != 0) {
/* cache route doesn't match */
dst4->sin_family = AF_INET;
dst4->sin_len = sizeof(struct sockaddr_in);
bcopy(&ip->ip_dst, &dst4->sin_addr, sizeof(dst4->sin_addr));
if (sc->sc_ro.ro_rt) {
RTFREE(sc->sc_ro.ro_rt);
sc->sc_ro.ro_rt = NULL;
}
}
if (sc->sc_ro.ro_rt == NULL) {
rtalloc_fib(&sc->sc_ro, sc->sc_fibnum);
if (sc->sc_ro.ro_rt == NULL) {
m_freem(m);
mtx_unlock(&(sc)->sc_ro_mtx);
if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
return ENETUNREACH;
}
}
cached_route = &sc->sc_ro;
sendit:
M_SETFIB(m, sc->sc_fibnum);
if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
error = ip_output(m, NULL, cached_route, 0, NULL, NULL);
error = ip_output(m, NULL, NULL, 0, NULL, NULL);
if (cached_route != NULL)
mtx_unlock(&(sc)->sc_ro_mtx);
return error;
}