diff options
Diffstat (limited to 'freebsd/sys/netinet/ip_gre.c')
-rw-r--r-- | freebsd/sys/netinet/ip_gre.c | 354 |
1 files changed, 92 insertions, 262 deletions
diff --git a/freebsd/sys/netinet/ip_gre.c b/freebsd/sys/netinet/ip_gre.c index 9289be96..36d3ed69 100644 --- a/freebsd/sys/netinet/ip_gre.c +++ b/freebsd/sys/netinet/ip_gre.c @@ -1,9 +1,8 @@ #include <machine/rtems-bsd-kernel-space.h> -/* $NetBSD: ip_gre.c,v 1.29 2003/09/05 23:02:43 itojun Exp $ */ - /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. + * Copyright (c) 2014 Andrey V. Elsukov <ae@FreeBSD.org> * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation @@ -31,19 +30,14 @@ * CONTRACT, STRICT 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. - */ - -/* - * deencapsulate tunneled packets and send them on - * output half is in net/if_gre.[ch] - * This currently handles IPPROTO_GRE, IPPROTO_MOBILE + * + * $NetBSD: ip_gre.c,v 1.29 2003/09/05 23:02:43 itojun Exp $ */ #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); #include <rtems/bsd/local/opt_inet.h> -#include <rtems/bsd/local/opt_atalk.h> #include <rtems/bsd/local/opt_inet6.h> #include <rtems/bsd/sys/param.h> @@ -55,285 +49,121 @@ __FBSDID("$FreeBSD$"); #include <rtems/bsd/sys/errno.h> #include <sys/time.h> #include <sys/kernel.h> -#include <sys/syslog.h> -#include <net/bpf.h> +#include <rtems/bsd/sys/lock.h> +#include <sys/rmlock.h> +#include <sys/sysctl.h> #include <net/ethernet.h> #include <net/if.h> -#include <net/netisr.h> -#include <net/route.h> -#include <net/raw_cb.h> +#include <net/if_var.h> +#include <net/vnet.h> -#ifdef INET #include <netinet/in.h> #include <netinet/in_var.h> -#include <netinet/in_systm.h> #include <netinet/ip.h> +#include <netinet/ip_encap.h> #include <netinet/ip_var.h> -#include <netinet/ip_gre.h> -#include <machine/in_cksum.h> -#else -#error ip_gre input without IP? -#endif -#ifdef NETATALK -#include <netatalk/at.h> -#include <netatalk/at_var.h> -#include <netatalk/at_extern.h> +#ifdef INET6 +#include <netinet/ip6.h> #endif -/* Needs IP headers. */ #include <net/if_gre.h> -#include <machine/stdarg.h> - -#if 1 -void gre_inet_ntoa(struct in_addr in); /* XXX */ -#endif - -static struct gre_softc *gre_lookup(struct mbuf *, u_int8_t); - -static struct mbuf *gre_input2(struct mbuf *, int, u_char); - -/* - * De-encapsulate a packet and feed it back through ip input (this - * routine is called whenever IP gets a packet with proto type - * IPPROTO_GRE and a local destination address). - * This really is simple - */ -void -gre_input(struct mbuf *m, int off) +extern struct domain inetdomain; +static const struct protosw in_gre_protosw = { + .pr_type = SOCK_RAW, + .pr_domain = &inetdomain, + .pr_protocol = IPPROTO_GRE, + .pr_flags = PR_ATOMIC|PR_ADDR, + .pr_input = gre_input, + .pr_output = rip_output, + .pr_ctlinput = rip_ctlinput, + .pr_ctloutput = rip_ctloutput, + .pr_usrreqs = &rip_usrreqs +}; + +#define GRE_TTL 30 +VNET_DEFINE(int, ip_gre_ttl) = GRE_TTL; +#define V_ip_gre_ttl VNET(ip_gre_ttl) +SYSCTL_INT(_net_inet_ip, OID_AUTO, grettl, CTLFLAG_VNET | CTLFLAG_RW, + &VNET_NAME(ip_gre_ttl), 0, ""); + +static int +in_gre_encapcheck(const struct mbuf *m, int off, int proto, void *arg) { - int proto; - - proto = (mtod(m, struct ip *))->ip_p; + GRE_RLOCK_TRACKER; + struct gre_softc *sc; + struct ip *ip; - m = gre_input2(m, off, proto); + sc = (struct gre_softc *)arg; + if ((GRE2IFP(sc)->if_flags & IFF_UP) == 0) + return (0); + M_ASSERTPKTHDR(m); /* - * If no matching tunnel that is up is found. We inject - * the mbuf to raw ip socket to see if anyone picks it up. + * We expect that payload contains at least IPv4 + * or IPv6 packet. */ - if (m != NULL) - rip_input(m, off); -} - -/* - * Decapsulate. Does the real work and is called from gre_input() - * (above). Returns an mbuf back if packet is not yet processed, - * and NULL if it needs no further processing. proto is the protocol - * number of the "calling" foo_input() routine. - */ -static struct mbuf * -gre_input2(struct mbuf *m ,int hlen, u_char proto) -{ - struct greip *gip; - int isr; - struct gre_softc *sc; - u_int16_t flags; - u_int32_t af; - - if ((sc = gre_lookup(m, proto)) == NULL) { - /* No matching tunnel or tunnel is down. */ - return (m); - } - - if (m->m_len < sizeof(*gip)) { - m = m_pullup(m, sizeof(*gip)); - if (m == NULL) - return (NULL); - } - gip = mtod(m, struct greip *); - - GRE2IFP(sc)->if_ipackets++; - GRE2IFP(sc)->if_ibytes += m->m_pkthdr.len; - - switch (proto) { - case IPPROTO_GRE: - hlen += sizeof(struct gre_h); - - /* process GRE flags as packet can be of variable len */ - flags = ntohs(gip->gi_flags); - - /* Checksum & Offset are present */ - if ((flags & GRE_CP) | (flags & GRE_RP)) - hlen += 4; - /* We don't support routing fields (variable length) */ - if (flags & GRE_RP) - return (m); - if (flags & GRE_KP) - hlen += 4; - if (flags & GRE_SP) - hlen += 4; - - switch (ntohs(gip->gi_ptype)) { /* ethertypes */ - case WCCP_PROTOCOL_TYPE: - if (sc->wccp_ver == WCCP_V2) - hlen += 4; - /* FALLTHROUGH */ - case ETHERTYPE_IP: /* shouldn't need a schednetisr(), */ - isr = NETISR_IP;/* as we are in ip_input */ - af = AF_INET; - break; -#ifdef INET6 - case ETHERTYPE_IPV6: - isr = NETISR_IPV6; - af = AF_INET6; - break; -#endif -#ifdef NETATALK - case ETHERTYPE_ATALK: - isr = NETISR_ATALK1; - af = AF_APPLETALK; - break; -#endif - default: - /* Others not yet supported. */ - return (m); - } - break; - default: - /* Others not yet supported. */ - return (m); - } - - if (hlen > m->m_pkthdr.len) { - m_freem(m); - return (NULL); - } - /* Unlike NetBSD, in FreeBSD m_adj() adjusts m->m_pkthdr.len as well */ - m_adj(m, hlen); - - if (bpf_peers_present(GRE2IFP(sc)->if_bpf)) { - bpf_mtap2(GRE2IFP(sc)->if_bpf, &af, sizeof(af), m); - } + if (m->m_pkthdr.len < sizeof(struct greip) + sizeof(struct ip)) + return (0); - if ((GRE2IFP(sc)->if_flags & IFF_MONITOR) != 0) { - m_freem(m); - return(NULL); - } - - m->m_pkthdr.rcvif = GRE2IFP(sc); + GRE_RLOCK(sc); + if (sc->gre_family == 0) + goto bad; - netisr_queue(isr, m); + KASSERT(sc->gre_family == AF_INET, + ("wrong gre_family: %d", sc->gre_family)); - /* Packet is done, no further processing needed. */ - return (NULL); + ip = mtod(m, struct ip *); + if (sc->gre_oip.ip_src.s_addr != ip->ip_dst.s_addr || + sc->gre_oip.ip_dst.s_addr != ip->ip_src.s_addr) + goto bad; + + GRE_RUNLOCK(sc); + return (32 * 2); +bad: + GRE_RUNLOCK(sc); + return (0); } -/* - * input routine for IPPRPOTO_MOBILE - * This is a little bit diffrent from the other modes, as the - * encapsulating header was not prepended, but instead inserted - * between IP header and payload - */ - -void -gre_mobile_input(struct mbuf *m, int hlen) +int +in_gre_output(struct mbuf *m, int af, int hlen) { - struct ip *ip; - struct mobip_h *mip; - struct gre_softc *sc; - int msiz; - - if ((sc = gre_lookup(m, IPPROTO_MOBILE)) == NULL) { - /* No matching tunnel or tunnel is down. */ - m_freem(m); - return; - } - - if (m->m_len < sizeof(*mip)) { - m = m_pullup(m, sizeof(*mip)); - if (m == NULL) - return; - } - ip = mtod(m, struct ip *); - mip = mtod(m, struct mobip_h *); - - GRE2IFP(sc)->if_ipackets++; - GRE2IFP(sc)->if_ibytes += m->m_pkthdr.len; - - if (ntohs(mip->mh.proto) & MOB_H_SBIT) { - msiz = MOB_H_SIZ_L; - mip->mi.ip_src.s_addr = mip->mh.osrc; - } else - msiz = MOB_H_SIZ_S; - - if (m->m_len < (ip->ip_hl << 2) + msiz) { - m = m_pullup(m, (ip->ip_hl << 2) + msiz); - if (m == NULL) - return; - ip = mtod(m, struct ip *); - mip = mtod(m, struct mobip_h *); - } - - mip->mi.ip_dst.s_addr = mip->mh.odst; - mip->mi.ip_p = (ntohs(mip->mh.proto) >> 8); - - if (gre_in_cksum((u_int16_t *)&mip->mh, msiz) != 0) { - m_freem(m); - return; - } - - bcopy((caddr_t)(ip) + (ip->ip_hl << 2) + msiz, (caddr_t)(ip) + - (ip->ip_hl << 2), m->m_len - msiz - (ip->ip_hl << 2)); - m->m_len -= msiz; - m->m_pkthdr.len -= msiz; - - /* - * On FreeBSD, rip_input() supplies us with ip->ip_len - * already converted into host byteorder and also decreases - * it by the lengh of IP header, however, ip_input() expects - * that this field is in the original format (network byteorder - * and full size of IP packet), so that adjust accordingly. - */ - ip->ip_len = htons(ip->ip_len + sizeof(struct ip) - msiz); - - ip->ip_sum = 0; - ip->ip_sum = in_cksum(m, (ip->ip_hl << 2)); - - if (bpf_peers_present(GRE2IFP(sc)->if_bpf)) { - u_int32_t af = AF_INET; - bpf_mtap2(GRE2IFP(sc)->if_bpf, &af, sizeof(af), m); - } - - if ((GRE2IFP(sc)->if_flags & IFF_MONITOR) != 0) { - m_freem(m); - return; + struct greip *gi; + + gi = mtod(m, struct greip *); + switch (af) { + case AF_INET: + /* + * gre_transmit() has used M_PREPEND() that doesn't guarantee + * m_data is contiguous more than hlen bytes. Use m_copydata() + * here to avoid m_pullup(). + */ + m_copydata(m, hlen + offsetof(struct ip, ip_tos), + sizeof(u_char), &gi->gi_ip.ip_tos); + m_copydata(m, hlen + offsetof(struct ip, ip_id), + sizeof(u_short), (caddr_t)&gi->gi_ip.ip_id); + break; +#ifdef INET6 + case AF_INET6: + gi->gi_ip.ip_tos = 0; /* XXX */ + ip_fillid(&gi->gi_ip); + break; +#endif } - - m->m_pkthdr.rcvif = GRE2IFP(sc); - - netisr_queue(NETISR_IP, m); + gi->gi_ip.ip_ttl = V_ip_gre_ttl; + gi->gi_ip.ip_len = htons(m->m_pkthdr.len); + return (ip_output(m, NULL, NULL, IP_FORWARDING, NULL, NULL)); } -/* - * Find the gre interface associated with our src/dst/proto set. - * - * XXXRW: Need some sort of drain/refcount mechanism so that the softc - * reference remains valid after it's returned from gre_lookup(). Right - * now, I'm thinking it should be reference-counted with a gre_dropref() - * when the caller is done with the softc. This is complicated by how - * to handle destroying the gre softc; probably using a gre_drain() in - * in_gre.c during destroy. - */ -static struct gre_softc * -gre_lookup(struct mbuf *m, u_int8_t proto) +int +in_gre_attach(struct gre_softc *sc) { - struct ip *ip = mtod(m, struct ip *); - struct gre_softc *sc; - - mtx_lock(&gre_mtx); - for (sc = LIST_FIRST(&gre_softc_list); sc != NULL; - sc = LIST_NEXT(sc, sc_list)) { - if ((sc->g_dst.s_addr == ip->ip_src.s_addr) && - (sc->g_src.s_addr == ip->ip_dst.s_addr) && - (sc->g_proto == proto) && - ((GRE2IFP(sc)->if_flags & IFF_UP) != 0)) { - mtx_unlock(&gre_mtx); - return (sc); - } - } - mtx_unlock(&gre_mtx); - return (NULL); + KASSERT(sc->gre_ecookie == NULL, ("gre_ecookie isn't NULL")); + sc->gre_ecookie = encap_attach_func(AF_INET, IPPROTO_GRE, + in_gre_encapcheck, &in_gre_protosw, sc); + if (sc->gre_ecookie == NULL) + return (EEXIST); + return (0); } |