diff options
author | Claudio Jeker <claudio@cvs.openbsd.org> | 2010-05-11 09:36:08 +0000 |
---|---|---|
committer | Claudio Jeker <claudio@cvs.openbsd.org> | 2010-05-11 09:36:08 +0000 |
commit | 62fc95fd847703423c4902b3205c6d9517f20006 (patch) | |
tree | 7d05c62bdb72350f0f01022800ac767f57e999a4 /sys/net/if_gif.c | |
parent | 1b7752fdde26fac73c4c3bb416b412ce523cc945 (diff) |
Massiv cleanup of the gif(4) mess. Move encapsulation into gif_output()
where it is not necessary to guess protocols by looking at the first nibble.
in_gif_output() will encapsulate the packet but not send it. Because of
etherip support and the way the bridge works a minimal hack is needed in
gif_start() to ensure that the bridged packets are encapsulated as well.
This actually started with the idea to add MPLS support but that turned out
to be not as simple as in the gre(4) case.
Tested by myself (IP, IPv6, etherip, MPLS), sthen@ (IP, IPv6), naddy (IPv6)
OK sthen@
Diffstat (limited to 'sys/net/if_gif.c')
-rw-r--r-- | sys/net/if_gif.c | 229 |
1 files changed, 155 insertions, 74 deletions
diff --git a/sys/net/if_gif.c b/sys/net/if_gif.c index e6a28b87d5f..cb9ccba73e0 100644 --- a/sys/net/if_gif.c +++ b/sys/net/if_gif.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if_gif.c,v 1.53 2009/11/22 12:33:25 deraadt Exp $ */ +/* $OpenBSD: if_gif.c,v 1.54 2010/05/11 09:36:07 claudio Exp $ */ /* $KAME: if_gif.c,v 1.43 2001/02/20 08:51:07 itojun Exp $ */ /* @@ -49,6 +49,7 @@ #include <netinet/in_var.h> #include <netinet/in_gif.h> #include <netinet/ip.h> +#include <netinet/ip_var.h> #endif /* INET */ #ifdef INET6 @@ -56,6 +57,7 @@ #include <netinet/in.h> #endif #include <netinet/ip6.h> +#include <netinet6/ip6_var.h> #include <netinet6/in6_gif.h> #endif /* INET6 */ @@ -67,6 +69,7 @@ void gifattach(int); int gif_clone_create(struct if_clone *, int); int gif_clone_destroy(struct ifnet *); +int gif_checkloop(struct ifnet *, struct mbuf *); /* * gif global variable definitions @@ -145,24 +148,8 @@ gif_start(struct ifnet *ifp) { struct gif_softc *sc = (struct gif_softc*)ifp; struct mbuf *m; - struct m_tag *mtag; - int family; int s; - u_int8_t tp; - - /* is interface up and running? */ - if ((ifp->if_flags & (IFF_OACTIVE | IFF_UP)) != IFF_UP || - sc->gif_psrc == NULL || sc->gif_pdst == NULL) - return; - - /* are the tunnel endpoints valid? */ -#ifdef INET - if (sc->gif_psrc->sa_family != AF_INET) -#endif -#ifdef INET6 - if (sc->gif_psrc->sa_family != AF_INET6) -#endif - return; + sa_family_t family; while (1) { s = splnet(); @@ -172,77 +159,124 @@ gif_start(struct ifnet *ifp) if (m == NULL) break; - /* - * gif may cause infinite recursion calls when misconfigured. - * We'll prevent this by detecting loops. - */ - for (mtag = m_tag_find(m, PACKET_TAG_GIF, NULL); mtag; - mtag = m_tag_find(m, PACKET_TAG_GIF, mtag)) { - if (!bcmp((caddr_t)(mtag + 1), &ifp, - sizeof(struct ifnet *))) { - IF_DROP(&ifp->if_snd); - log(LOG_NOTICE, "gif_output: " - "recursively called too many times\n"); - m_freem(m); - break; - } - } - if (mtag) - continue; - - mtag = m_tag_get(PACKET_TAG_GIF, sizeof(caddr_t), M_NOWAIT); - if (mtag == NULL) { + /* is interface up and usable? */ + if ((ifp->if_flags & (IFF_OACTIVE | IFF_UP)) != IFF_UP || + sc->gif_psrc == NULL || sc->gif_pdst == NULL || + sc->gif_psrc->sa_family != sc->gif_pdst->sa_family) { m_freem(m); - break; + continue; } - bcopy(&ifp, mtag + 1, sizeof(caddr_t)); - m_tag_prepend(m, mtag); + + /* get tunnel address family */ + family = sc->gif_psrc->sa_family; /* - * Remove multicast and broadcast flags or encapsulated packet - * ends up as multicast or broadcast packet. + * Check if the packet is comming via bridge and needs + * etherip encapsulation or not. bridge(4) directly calls + * the start function and bypasses the if_output function + * so we need to do the encap here. */ - m->m_flags &= ~(M_BCAST|M_MCAST); - - /* extract address family */ - family = AF_UNSPEC; - tp = *mtod(m, u_int8_t *); - tp = (tp >> 4) & 0xff; /* Get the IP version number. */ + if (ifp->if_bridge && (m->m_flags & M_PROTO1)) { + int error = 0; + /* + * Remove multicast and broadcast flags or encapsulated + * packet ends up as multicast or broadcast packet. + */ + m->m_flags &= ~(M_BCAST|M_MCAST); + switch (sc->gif_psrc->sa_family) { #ifdef INET - if (tp == IPVERSION) - family = AF_INET; + case AF_INET: + error = in_gif_output(ifp, AF_LINK, &m); + break; #endif #ifdef INET6 - if (tp == (IPV6_VERSION >> 4)) - family = AF_INET6; + case AF_INET6: + error = in6_gif_output(ifp, AF_LINK, &m); + break; #endif - -#if NBRIDGE > 0 - /* - * Check if the packet is comming via bridge and needs - * etherip encapsulation or not. - */ - if (ifp->if_bridge && (m->m_flags & M_PROTO1)) { - m->m_flags &= ~M_PROTO1; - family = AF_LINK; + default: + error = EAFNOSUPPORT; + m_freem(m); + break; + } + if (error) + continue; + if (gif_checkloop(ifp, m)) + continue; } -#endif #if NBPFILTER > 0 - if (ifp->if_bpf) + if (ifp->if_bpf) { + int offset; + sa_family_t family; + u_int8_t proto; + + /* must decapsulate outer header for bpf */ + switch (sc->gif_psrc->sa_family) { +#ifdef INET + case AF_INET: + offset = sizeof(struct ip); + proto = mtod(m, struct ip *)->ip_p; + break; +#endif +#ifdef INET6 + case AF_INET6: + offset = sizeof(struct ip6_hdr); + proto = mtod(m, struct ip6_hdr *)->ip6_nxt; + break; +#endif + default: + proto = 0; + break; + } + switch (proto) { + case IPPROTO_IPV4: + family = AF_INET; + break; + case IPPROTO_IPV6: + family = AF_INET6; + break; + case IPPROTO_ETHERIP: + family = AF_LINK; + break; + case IPPROTO_MPLS: + family = AF_MPLS; + break; + default: + offset = 0; + family = sc->gif_psrc->sa_family; + break; + } + m->m_data += offset; + m->m_len -= offset; + m->m_pkthdr.len -= offset; bpf_mtap_af(ifp->if_bpf, family, m, BPF_DIRECTION_OUT); + m->m_data -= offset; + m->m_len += offset; + m->m_pkthdr.len += offset; + } #endif ifp->if_opackets++; + /* XXX we should cache the outgoing route */ + switch (sc->gif_psrc->sa_family) { #ifdef INET case AF_INET: - in_gif_output(ifp, family, m); + ip_output(m, (void *)NULL, (void *)NULL, 0, + (void *)NULL, (void *)NULL); break; #endif #ifdef INET6 case AF_INET6: - in6_gif_output(ifp, family, m); + /* + * 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. + */ + ip6_output(m, 0, NULL, IPV6_MINMTU, 0, NULL, + NULL); break; #endif default: @@ -259,44 +293,61 @@ gif_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst, struct gif_softc *sc = (struct gif_softc*)ifp; int error = 0; int s; + sa_family_t family = dst->sa_family; if (!(ifp->if_flags & IFF_UP) || - sc->gif_psrc == NULL || sc->gif_pdst == NULL) { + sc->gif_psrc == NULL || sc->gif_pdst == NULL || + sc->gif_psrc->sa_family != sc->gif_pdst->sa_family) { m_freem(m); error = ENETDOWN; goto end; } + /* + * Remove multicast and broadcast flags or encapsulated packet + * ends up as multicast or broadcast packet. + */ + m->m_flags &= ~(M_BCAST|M_MCAST); + + /* + * Encapsulate packet. Add IP or IP6 header depending on tunnel AF. + */ switch (sc->gif_psrc->sa_family) { #ifdef INET case AF_INET: + error = in_gif_output(ifp, family, &m); break; #endif #ifdef INET6 case AF_INET6: + error = in6_gif_output(ifp, family, &m); break; #endif default: m_freem(m); - error = ENETDOWN; - goto end; + error = EAFNOSUPPORT; + break; } - s = splnet(); + if (error) + goto end; + + if ((error = gif_checkloop(ifp, m))) + goto end; + /* - * Queue message on interface, and start output if interface - * not yet active. + * Queue message on interface, and start output. */ + s = splnet(); IFQ_ENQUEUE(&ifp->if_snd, m, NULL, error); if (error) { /* mbuf is already freed */ splx(s); - return (error); + goto end; } ifp->if_obytes += m->m_pkthdr.len; if_start(ifp); splx(s); - return (error); end: if (error) @@ -617,3 +668,33 @@ gif_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) bad: return (error); } + +int +gif_checkloop(struct ifnet *ifp, struct mbuf *m) +{ + struct m_tag *mtag; + + /* + * gif may cause infinite recursion calls when misconfigured. + * We'll prevent this by detecting loops. + */ + for (mtag = m_tag_find(m, PACKET_TAG_GIF, NULL); mtag; + mtag = m_tag_find(m, PACKET_TAG_GIF, mtag)) { + if (!bcmp((caddr_t)(mtag + 1), &ifp, + sizeof(struct ifnet *))) { + log(LOG_NOTICE, "gif_output: " + "recursively called too many times\n"); + m_freem(m); + return ENETUNREACH; + } + } + + mtag = m_tag_get(PACKET_TAG_GIF, sizeof(caddr_t), M_NOWAIT); + if (mtag == NULL) { + m_freem(m); + return ENOMEM; + } + bcopy(&ifp, mtag + 1, sizeof(caddr_t)); + m_tag_prepend(m, mtag); + return 0; +} |