diff options
author | ae <ae@FreeBSD.org> | 2014-10-14 13:31:47 +0000 |
---|---|---|
committer | ae <ae@FreeBSD.org> | 2014-10-14 13:31:47 +0000 |
commit | 88b7be7ff6be519bd6675ab6520d04dcd5c912d7 (patch) | |
tree | 998749dd61508a9da582fc4a3dc83fa0e7a8a948 /sys/netinet6/in6_gif.c | |
parent | 046b4d46a2986d583304a2e601e7a59b100c29ac (diff) | |
download | FreeBSD-src-88b7be7ff6be519bd6675ab6520d04dcd5c912d7.zip FreeBSD-src-88b7be7ff6be519bd6675ab6520d04dcd5c912d7.tar.gz |
Overhaul if_gif(4):
o convert to if_transmit;
o use rmlock to protect access to gif_softc;
o use sx lock to protect from concurrent ioctls;
o remove a lot of unneeded and duplicated code;
o remove cached route support (it won't work with concurrent io);
o style fixes.
Reviewed by: melifaro
Obtained from: Yandex LLC
MFC after: 1 month
Sponsored by: Yandex LLC
Diffstat (limited to 'sys/netinet6/in6_gif.c')
-rw-r--r-- | sys/netinet6/in6_gif.c | 309 |
1 files changed, 45 insertions, 264 deletions
diff --git a/sys/netinet6/in6_gif.c b/sys/netinet6/in6_gif.c index b792ed5..6d48b5d 100644 --- a/sys/netinet6/in6_gif.c +++ b/sys/netinet6/in6_gif.c @@ -36,6 +36,8 @@ __FBSDID("$FreeBSD$"); #include "opt_inet6.h" #include <sys/param.h> +#include <sys/lock.h> +#include <sys/rmlock.h> #include <sys/systm.h> #include <sys/socket.h> #include <sys/sockio.h> @@ -95,113 +97,26 @@ struct protosw in6_gif_protosw = { }; int -in6_gif_output(struct ifnet *ifp, - int family, /* family of the packet to be encapsulate */ - struct mbuf *m) +in6_gif_output(struct ifnet *ifp, struct mbuf *m, int proto, uint8_t ecn) { + GIF_RLOCK_TRACKER; struct gif_softc *sc = ifp->if_softc; - struct sockaddr_in6 *dst = (struct sockaddr_in6 *)&sc->gif_ro6.ro_dst; - struct sockaddr_in6 *sin6_src = (struct sockaddr_in6 *)sc->gif_psrc; - struct sockaddr_in6 *sin6_dst = (struct sockaddr_in6 *)sc->gif_pdst; struct ip6_hdr *ip6; - struct etherip_header eiphdr; - int error, len, proto; - u_int8_t itos, otos; - - GIF_LOCK_ASSERT(sc); - - if (sin6_src == NULL || sin6_dst == NULL || - sin6_src->sin6_family != AF_INET6 || - sin6_dst->sin6_family != AF_INET6) { - m_freem(m); - return EAFNOSUPPORT; - } - - switch (family) { -#ifdef INET - case AF_INET: - { - struct ip *ip; - - proto = IPPROTO_IPV4; - if (m->m_len < sizeof(*ip)) { - m = m_pullup(m, sizeof(*ip)); - if (!m) - return ENOBUFS; - } - ip = mtod(m, struct ip *); - itos = ip->ip_tos; - break; - } -#endif -#ifdef INET6 - case AF_INET6: - { - struct ip6_hdr *ip6; - proto = IPPROTO_IPV6; - if (m->m_len < sizeof(*ip6)) { - m = m_pullup(m, sizeof(*ip6)); - if (!m) - return ENOBUFS; - } - ip6 = mtod(m, struct ip6_hdr *); - itos = (ntohl(ip6->ip6_flow) >> 20) & 0xff; - break; - } -#endif - case AF_LINK: - proto = IPPROTO_ETHERIP; - - /* - * GIF_SEND_REVETHIP (disabled by default) intentionally - * sends an EtherIP packet with revered version field in - * the header. This is a knob for backward compatibility - * with FreeBSD 7.2R or prior. - */ - if ((sc->gif_options & GIF_SEND_REVETHIP)) { - eiphdr.eip_ver = 0; - eiphdr.eip_resvl = ETHERIP_VERSION; - eiphdr.eip_resvh = 0; - } else { - eiphdr.eip_ver = ETHERIP_VERSION; - eiphdr.eip_resvl = 0; - eiphdr.eip_resvh = 0; - } - /* prepend Ethernet-in-IP header */ - M_PREPEND(m, sizeof(struct etherip_header), M_NOWAIT); - if (m && m->m_len < sizeof(struct etherip_header)) - m = m_pullup(m, sizeof(struct etherip_header)); - if (m == NULL) - return ENOBUFS; - bcopy(&eiphdr, mtod(m, struct etherip_header *), - sizeof(struct etherip_header)); - itos = 0; - break; - - default: -#ifdef DEBUG - printf("in6_gif_output: warning: unknown family %d passed\n", - family); -#endif - m_freem(m); - return EAFNOSUPPORT; - } + int len; /* prepend new IP header */ len = sizeof(struct ip6_hdr); #ifndef __NO_STRICT_ALIGNMENT - if (family == AF_LINK) + if (proto == IPPROTO_ETHERIP) len += ETHERIP_ALIGN; #endif M_PREPEND(m, len, M_NOWAIT); if (m != NULL && m->m_len < len) m = m_pullup(m, len); - if (m == NULL) { - printf("ENOBUFS in in6_gif_output %d\n", __LINE__); - return ENOBUFS; - } + if (m == NULL) + return (ENOBUFS); #ifndef __NO_STRICT_ALIGNMENT - if (family == AF_LINK) { + if (proto == IPPROTO_ETHERIP) { len = mtod(m, vm_offset_t) & 3; KASSERT(len == 0 || len == ETHERIP_ALIGN, ("in6_gif_output: unexpected misalignment")); @@ -211,166 +126,52 @@ in6_gif_output(struct ifnet *ifp, #endif ip6 = mtod(m, struct ip6_hdr *); - ip6->ip6_flow = 0; - ip6->ip6_vfc &= ~IPV6_VERSION_MASK; - ip6->ip6_vfc |= IPV6_VERSION; - ip6->ip6_plen = htons((u_short)m->m_pkthdr.len); - ip6->ip6_nxt = proto; - ip6->ip6_hlim = V_ip6_gif_hlim; - ip6->ip6_src = sin6_src->sin6_addr; - /* bidirectional configured tunnel mode */ - if (!IN6_IS_ADDR_UNSPECIFIED(&sin6_dst->sin6_addr)) - ip6->ip6_dst = sin6_dst->sin6_addr; - else { + GIF_RLOCK(sc); + if (sc->gif_family != AF_INET6) { m_freem(m); - return ENETUNREACH; + GIF_RUNLOCK(sc); + return (ENETDOWN); } - ip_ecn_ingress((ifp->if_flags & IFF_LINK1) ? ECN_ALLOWED : ECN_NOCARE, - &otos, &itos); - ip6->ip6_flow &= ~htonl(0xff << 20); - ip6->ip6_flow |= htonl((u_int32_t)otos << 20); + bcopy(sc->gif_ip6hdr, ip6, sizeof(struct ip6_hdr)); + GIF_RUNLOCK(sc); - M_SETFIB(m, sc->gif_fibnum); - - if (dst->sin6_family != sin6_dst->sin6_family || - !IN6_ARE_ADDR_EQUAL(&dst->sin6_addr, &sin6_dst->sin6_addr)) { - /* cache route doesn't match */ - bzero(dst, sizeof(*dst)); - dst->sin6_family = sin6_dst->sin6_family; - dst->sin6_len = sizeof(struct sockaddr_in6); - dst->sin6_addr = sin6_dst->sin6_addr; - if (sc->gif_ro6.ro_rt) { - RTFREE(sc->gif_ro6.ro_rt); - sc->gif_ro6.ro_rt = NULL; - } -#if 0 - GIF2IFP(sc)->if_mtu = GIF_MTU; -#endif - } - - if (sc->gif_ro6.ro_rt == NULL) { - in6_rtalloc(&sc->gif_ro6, sc->gif_fibnum); - if (sc->gif_ro6.ro_rt == NULL) { - m_freem(m); - return ENETUNREACH; - } - - /* if it constitutes infinite encapsulation, punt. */ - if (sc->gif_ro.ro_rt->rt_ifp == ifp) { - m_freem(m); - return ENETUNREACH; /*XXX*/ - } -#if 0 - ifp->if_mtu = sc->gif_ro6.ro_rt->rt_ifp->if_mtu - - sizeof(struct ip6_hdr); -#endif - } - - m->m_flags &= ~(M_BCAST|M_MCAST); -#ifdef IPV6_MINMTU + ip6->ip6_flow |= htonl((uint32_t)ecn << 20); + ip6->ip6_nxt = proto; + ip6->ip6_hlim = V_ip6_gif_hlim; /* * force fragmentation to minimum MTU, to avoid path MTU discovery. * it is too painful to ask for resend of inner packet, to achieve * path MTU discovery for encapsulated packets. */ - error = ip6_output(m, 0, &sc->gif_ro6, IPV6_MINMTU, 0, NULL, NULL); -#else - error = ip6_output(m, 0, &sc->gif_ro6, 0, 0, NULL, NULL); -#endif - - if (!(GIF2IFP(sc)->if_flags & IFF_LINK0) && - sc->gif_ro6.ro_rt != NULL) { - RTFREE(sc->gif_ro6.ro_rt); - sc->gif_ro6.ro_rt = NULL; - } - - return (error); + return (ip6_output(m, 0, NULL, IPV6_MINMTU, 0, NULL, NULL)); } int in6_gif_input(struct mbuf **mp, int *offp, int proto) { struct mbuf *m = *mp; - struct ifnet *gifp = NULL; + struct ifnet *gifp; struct gif_softc *sc; struct ip6_hdr *ip6; - int af = 0; - u_int32_t otos; + uint8_t ecn; - ip6 = mtod(m, struct ip6_hdr *); - - sc = (struct gif_softc *)encap_getarg(m); + sc = encap_getarg(m); if (sc == NULL) { m_freem(m); IP6STAT_INC(ip6s_nogif); - return IPPROTO_DONE; + return (IPPROTO_DONE); } - gifp = GIF2IFP(sc); - if (gifp == NULL || (gifp->if_flags & IFF_UP) == 0) { - m_freem(m); - IP6STAT_INC(ip6s_nogif); - return IPPROTO_DONE; - } - - otos = ip6->ip6_flow; - m_adj(m, *offp); - - switch (proto) { -#ifdef INET - case IPPROTO_IPV4: - { - struct ip *ip; - u_int8_t otos8; - af = AF_INET; - otos8 = (ntohl(otos) >> 20) & 0xff; - if (m->m_len < sizeof(*ip)) { - m = m_pullup(m, sizeof(*ip)); - if (!m) - return IPPROTO_DONE; - } - ip = mtod(m, struct ip *); - if (ip_ecn_egress((gifp->if_flags & IFF_LINK1) ? - ECN_ALLOWED : ECN_NOCARE, - &otos8, &ip->ip_tos) == 0) { - m_freem(m); - return IPPROTO_DONE; - } - break; - } -#endif /* INET */ -#ifdef INET6 - case IPPROTO_IPV6: - { - struct ip6_hdr *ip6; - af = AF_INET6; - if (m->m_len < sizeof(*ip6)) { - m = m_pullup(m, sizeof(*ip6)); - if (!m) - return IPPROTO_DONE; - } + if ((gifp->if_flags & IFF_UP) != 0) { ip6 = mtod(m, struct ip6_hdr *); - if (ip6_ecn_egress((gifp->if_flags & IFF_LINK1) ? - ECN_ALLOWED : ECN_NOCARE, - &otos, &ip6->ip6_flow) == 0) { - m_freem(m); - return IPPROTO_DONE; - } - break; - } -#endif - case IPPROTO_ETHERIP: - af = AF_LINK; - break; - - default: - IP6STAT_INC(ip6s_nogif); + ecn = (ntohl(ip6->ip6_flow) >> 20) & 0xff; + m_adj(m, *offp); + gif_input(m, gifp, proto, ecn); + } else { m_freem(m); - return IPPROTO_DONE; + IP6STAT_INC(ip6s_nogif); } - - gif_input(m, af, gifp); - return IPPROTO_DONE; + return (IPPROTO_DONE); } /* @@ -380,19 +181,16 @@ static int gif_validate6(const struct ip6_hdr *ip6, struct gif_softc *sc, struct ifnet *ifp) { - struct sockaddr_in6 *src, *dst; - - src = (struct sockaddr_in6 *)sc->gif_psrc; - dst = (struct sockaddr_in6 *)sc->gif_pdst; + GIF_RLOCK_ASSERT(sc); /* * Check for address match. Note that the check is for an incoming * packet. We should compare the *source* address in our configuration * and the *destination* address of the packet, and vice versa. */ - if (!IN6_ARE_ADDR_EQUAL(&src->sin6_addr, &ip6->ip6_dst) || - !IN6_ARE_ADDR_EQUAL(&dst->sin6_addr, &ip6->ip6_src)) - return 0; + if (!IN6_ARE_ADDR_EQUAL(&sc->gif_ip6hdr->ip6_src, &ip6->ip6_dst) || + !IN6_ARE_ADDR_EQUAL(&sc->gif_ip6hdr->ip6_dst, &ip6->ip6_src)) + return (0); /* martian filters on outer source - done in ip6_input */ @@ -410,29 +208,22 @@ gif_validate6(const struct ip6_hdr *ip6, struct gif_softc *sc, rt = in6_rtalloc1((struct sockaddr *)&sin6, 0, 0UL, sc->gif_fibnum); if (!rt || rt->rt_ifp != ifp) { -#if 0 - char ip6buf[INET6_ADDRSTRLEN]; - log(LOG_WARNING, "%s: packet from %s dropped " - "due to ingress filter\n", if_name(GIF2IFP(sc)), - ip6_sprintf(ip6buf, &sin6.sin6_addr)); -#endif if (rt) RTFREE_LOCKED(rt); - return 0; + return (0); } RTFREE_LOCKED(rt); } - return 128 * 2; + return (128 * 2); } /* * we know that we are in IFF_UP, outer address available, and outer family * matched the physical addr family. see gif_encapcheck(). - * sanity check for arg should have been done in the caller. */ int -gif_encapcheck6(const struct mbuf *m, int off, int proto, void *arg) +in6_gif_encapcheck(const struct mbuf *m, int off, int proto, void *arg) { struct ip6_hdr ip6; struct gif_softc *sc; @@ -440,31 +231,21 @@ gif_encapcheck6(const struct mbuf *m, int off, int proto, void *arg) /* sanity check done in caller */ sc = (struct gif_softc *)arg; + GIF_RLOCK_ASSERT(sc); - /* LINTED const cast */ m_copydata(m, 0, sizeof(ip6), (caddr_t)&ip6); ifp = ((m->m_flags & M_PKTHDR) != 0) ? m->m_pkthdr.rcvif : NULL; - - return gif_validate6(&ip6, sc, ifp); + return (gif_validate6(&ip6, sc, ifp)); } int in6_gif_attach(struct gif_softc *sc) { - sc->encap_cookie6 = encap_attach_func(AF_INET6, -1, gif_encapcheck, - (void *)&in6_gif_protosw, sc); - if (sc->encap_cookie6 == NULL) - return EEXIST; - return 0; -} - -int -in6_gif_detach(struct gif_softc *sc) -{ - int error; - error = encap_detach(sc->encap_cookie6); - if (error == 0) - sc->encap_cookie6 = NULL; - return error; + KASSERT(sc->gif_ecookie == NULL, ("gif_ecookie isn't NULL")); + sc->gif_ecookie = encap_attach_func(AF_INET6, -1, gif_encapcheck, + (void *)&in6_gif_protosw, sc); + if (sc->gif_ecookie == NULL) + return (EEXIST); + return (0); } |