diff options
Diffstat (limited to 'sys/netinet6/in6_gif.c')
-rw-r--r-- | sys/netinet6/in6_gif.c | 153 |
1 files changed, 117 insertions, 36 deletions
diff --git a/sys/netinet6/in6_gif.c b/sys/netinet6/in6_gif.c index 35c4088..e554f5a 100644 --- a/sys/netinet6/in6_gif.c +++ b/sys/netinet6/in6_gif.c @@ -1,3 +1,6 @@ +/* $FreeBSD$ */ +/* $KAME: in6_gif.c,v 1.37 2000/06/17 20:34:25 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. @@ -25,8 +28,6 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ /* @@ -34,6 +35,7 @@ */ #include "opt_inet.h" +#include "opt_inet6.h" #include <sys/param.h> #include <sys/systm.h> @@ -41,7 +43,6 @@ #include <sys/sockio.h> #include <sys/mbuf.h> #include <sys/errno.h> -#include <sys/protosw.h> #include <net/if.h> #include <net/route.h> @@ -51,17 +52,26 @@ #ifdef INET #include <netinet/ip.h> #endif -#include <netinet6/ip6.h> +#include <netinet/ip_encap.h> +#ifdef INET6 +#include <netinet/ip6.h> #include <netinet6/ip6_var.h> #include <netinet6/in6_gif.h> -#include <netinet6/ip6.h> +#include <netinet6/in6_var.h> +#endif #include <netinet/ip_ecn.h> +#ifdef INET6 #include <netinet6/ip6_ecn.h> +#endif #include <net/if_gif.h> #include <net/net_osdep.h> +#ifndef offsetof +#define offsetof(s, e) ((int)&((s *)0)->e) +#endif + int in6_gif_output(ifp, family, m, rt) struct ifnet *ifp; @@ -101,6 +111,7 @@ in6_gif_output(ifp, family, m, rt) break; } #endif +#ifdef INET6 case AF_INET6: { struct ip6_hdr *ip6; @@ -114,15 +125,16 @@ in6_gif_output(ifp, family, m, rt) itos = (ntohl(ip6->ip6_flow) >> 20) & 0xff; break; } +#endif default: -#ifdef DIAGNOSTIC +#ifdef DEBUG printf("in6_gif_output: warning: unknown family %d passed\n", family); #endif m_freem(m); return EAFNOSUPPORT; } - + /* prepend new IP header */ M_PREPEND(m, sizeof(struct ip6_hdr), M_DONTWAIT); if (m && m->m_len < sizeof(struct ip6_hdr)) @@ -134,7 +146,8 @@ in6_gif_output(ifp, family, m, rt) ip6 = mtod(m, struct ip6_hdr *); ip6->ip6_flow = 0; - ip6->ip6_vfc = IPV6_VERSION; + 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 = ip6_gif_hlim; @@ -144,6 +157,10 @@ in6_gif_output(ifp, family, m, rt) if (!IN6_IS_ADDR_UNSPECIFIED(&sin6_dst->sin6_addr)) ip6->ip6_dst = sin6_dst->sin6_addr; else if (rt) { + if (family != AF_INET6) { + m_freem(m); + return EINVAL; /*XXX*/ + } ip6->ip6_dst = ((struct sockaddr_in6 *)(rt->rt_gateway))->sin6_addr; } else { m_freem(m); @@ -175,6 +192,9 @@ in6_gif_output(ifp, family, m, rt) RTFREE(sc->gif_ro6.ro_rt); sc->gif_ro6.ro_rt = NULL; } +#if 0 + sc->gif_if.if_mtu = GIF_MTU; +#endif } if (sc->gif_ro6.ro_rt == NULL) { @@ -183,9 +203,28 @@ in6_gif_output(ifp, family, m, rt) 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 + } + +#ifdef IPV6_MINMTU + /* + * 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. + */ + return(ip6_output(m, 0, &sc->gif_ro6, IPV6_MINMTU, 0, NULL)); +#else return(ip6_output(m, 0, &sc->gif_ro6, 0, 0, NULL)); +#endif } int in6_gif_input(mp, offp, proto) @@ -193,44 +232,21 @@ int in6_gif_input(mp, offp, proto) int *offp, proto; { struct mbuf *m = *mp; - struct gif_softc *sc; struct ifnet *gifp = NULL; struct ip6_hdr *ip6; - int i; int af = 0; u_int32_t otos; ip6 = mtod(m, struct ip6_hdr *); -#define satoin6(sa) (((struct sockaddr_in6 *)(sa))->sin6_addr) - for (i = 0, sc = gif; i < ngif; i++, sc++) { - if (sc->gif_psrc == NULL || - sc->gif_pdst == NULL || - sc->gif_psrc->sa_family != AF_INET6 || - sc->gif_pdst->sa_family != AF_INET6) { - continue; - } - if ((sc->gif_if.if_flags & IFF_UP) == 0) - continue; - if ((sc->gif_if.if_flags & IFF_LINK0) && - IN6_ARE_ADDR_EQUAL(&satoin6(sc->gif_psrc), &ip6->ip6_dst) && - IN6_IS_ADDR_UNSPECIFIED(&satoin6(sc->gif_pdst))) { - gifp = &sc->gif_if; - continue; - } - if (IN6_ARE_ADDR_EQUAL(&satoin6(sc->gif_psrc), &ip6->ip6_dst) && - IN6_ARE_ADDR_EQUAL(&satoin6(sc->gif_pdst), &ip6->ip6_src)) { - gifp = &sc->gif_if; - break; - } - } + gifp = (struct ifnet *)encap_getarg(m); - if (gifp == NULL) { + if (gifp == NULL || (gifp->if_flags & IFF_UP) == 0) { m_freem(m); ip6stat.ip6s_nogif++; return IPPROTO_DONE; } - + otos = ip6->ip6_flow; m_adj(m, *offp); @@ -253,6 +269,7 @@ int in6_gif_input(mp, offp, proto) break; } #endif /* INET */ +#ifdef INET6 case IPPROTO_IPV6: { struct ip6_hdr *ip6; @@ -267,12 +284,76 @@ int in6_gif_input(mp, offp, proto) ip6_ecn_egress(ECN_ALLOWED, &otos, &ip6->ip6_flow); break; } +#endif default: ip6stat.ip6s_nogif++; m_freem(m); return IPPROTO_DONE; } - + gif_input(m, af, gifp); return IPPROTO_DONE; } + +/* + * we know that we are in IFF_UP, outer address available, and outer family + * matched the physical addr family. see gif_encapcheck(). + */ +int +gif_encapcheck6(m, off, proto, arg) + const struct mbuf *m; + int off; + int proto; + void *arg; +{ + struct ip6_hdr ip6; + struct gif_softc *sc; + struct sockaddr_in6 *src, *dst; + int addrmatch; + + /* sanity check done in caller */ + sc = (struct gif_softc *)arg; + src = (struct sockaddr_in6 *)sc->gif_psrc; + dst = (struct sockaddr_in6 *)sc->gif_pdst; + + /* LINTED const cast */ + m_copydata((struct mbuf *)m, 0, sizeof(ip6), (caddr_t)&ip6); + + /* check for address match */ + addrmatch = 0; + if (IN6_ARE_ADDR_EQUAL(&src->sin6_addr, &ip6.ip6_dst)) + addrmatch |= 1; + if (IN6_ARE_ADDR_EQUAL(&dst->sin6_addr, &ip6.ip6_src)) + addrmatch |= 2; + else if ((sc->gif_if.if_flags & IFF_LINK0) != 0 && + IN6_IS_ADDR_UNSPECIFIED(&dst->sin6_addr)) { + addrmatch |= 2; /* we accept any source */ + } + if (addrmatch != 3) + return 0; + + /* martian filters on outer source - done in ip6_input */ + + /* ingress filters on outer source */ + if ((m->m_flags & M_PKTHDR) != 0 && m->m_pkthdr.rcvif) { + struct sockaddr_in6 sin6; + struct rtentry *rt; + + bzero(&sin6, sizeof(sin6)); + sin6.sin6_family = AF_INET6; + sin6.sin6_len = sizeof(struct sockaddr_in6); + sin6.sin6_addr = ip6.ip6_src; + /* XXX scopeid */ + rt = rtalloc1((struct sockaddr *)&sin6, 0, 0UL); + if (!rt) + return 0; + if (rt->rt_ifp != m->m_pkthdr.rcvif) { + rtfree(rt); + return 0; + } + rtfree(rt); + } + + /* prioritize: IFF_LINK0 mode is less preferred */ + return (sc->gif_if.if_flags & IFF_LINK0) ? 128 : 128 * 2; +} |