aboutsummaryrefslogtreecommitdiff
path: root/sys/netinet/ip_gre.c
diff options
context:
space:
mode:
authorAndrey V. Elsukov <ae@FreeBSD.org>2014-11-07 19:13:19 +0000
committerAndrey V. Elsukov <ae@FreeBSD.org>2014-11-07 19:13:19 +0000
commitf325335cafc940e54a1ea56560de6f25340c81b7 (patch)
tree63c3d4ed6b0c4afb2640b0e3d10ed1b045f2d63f /sys/netinet/ip_gre.c
parent833e8dc5ab711a9485790d950801e32798d55558 (diff)
downloadsrc-f325335cafc940e54a1ea56560de6f25340c81b7.tar.gz
src-f325335cafc940e54a1ea56560de6f25340c81b7.zip
Overhaul if_gre(4).
Split it into two modules: if_gre(4) for GRE encapsulation and if_me(4) for minimal encapsulation within IP. gre(4) changes: * convert to if_transmit; * rework locking: protect access to softc with rmlock, protect from concurrent ioctls with sx lock; * correct interface accounting for outgoing datagramms (count only payload size); * implement generic support for using IPv6 as delivery header; * make implementation conform to the RFC 2784 and partially to RFC 2890; * add support for GRE checksums - calculate for outgoing datagramms and check for inconming datagramms; * add support for sending sequence number in GRE header; * remove support of cached routes. This fixes problem, when gre(4) doesn't work at system startup. But this also removes support for having tunnels with the same addresses for inner and outer header. * deprecate support for various GREXXX ioctls, that doesn't used in FreeBSD. Use our standard ioctls for tunnels. me(4): * implementation conform to RFC 2004; * use if_transmit; * use the same locking model as gre(4); PR: 164475 Differential Revision: D1023 No objections from: net@ Relnotes: yes Sponsored by: Yandex LLC
Notes
Notes: svn path=/head/; revision=274246
Diffstat (limited to 'sys/netinet/ip_gre.c')
-rw-r--r--sys/netinet/ip_gre.c352
1 files changed, 96 insertions, 256 deletions
diff --git a/sys/netinet/ip_gre.c b/sys/netinet/ip_gre.c
index fc8732770077..9f2819131168 100644
--- a/sys/netinet/ip_gre.c
+++ b/sys/netinet/ip_gre.c
@@ -1,7 +1,6 @@
-/* $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
@@ -29,12 +28,8 @@
* 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>
@@ -52,281 +47,126 @@ __FBSDID("$FreeBSD$");
#include <sys/errno.h>
#include <sys/time.h>
#include <sys/kernel.h>
-#include <sys/syslog.h>
-#include <net/bpf.h>
+#include <sys/lock.h>
+#include <sys/rmlock.h>
+#include <sys/sysctl.h>
#include <net/ethernet.h>
#include <net/if.h>
#include <net/if_var.h>
-#include <net/netisr.h>
-#include <net/route.h>
-#include <net/raw_cb.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 requires INET"
-#endif
-/* Needs IP headers. */
-#include <net/if_gre.h>
-
-#include <machine/stdarg.h>
-
-#if 1
-void gre_inet_ntoa(struct in_addr in); /* XXX */
+#ifdef INET6
+#include <netinet/ip6.h>
#endif
-static struct gre_softc *gre_lookup(struct mbuf *, u_int8_t);
-
-static struct mbuf *gre_input2(struct mbuf *, int, u_char);
+#include <net/if_gre.h>
-/*
- * 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
- */
-int
-gre_input(struct mbuf **mp, int *offp, int proto)
+extern struct domain inetdomain;
+extern int gre_input(struct mbuf **, int *, int);
+
+int in_gre_attach(struct gre_softc *);
+int in_gre_output(struct mbuf *, int, int);
+
+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)
{
- struct mbuf *m;
- int off;
-
- m = *mp;
- off = *offp;
- *mp = NULL;
+ 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) {
- *mp = m;
- rip_input(mp, offp, proto);
- }
- return (IPPROTO_DONE);
-}
+ if (m->m_pkthdr.len < sizeof(struct greip) + sizeof(struct ip))
+ return (0);
-/*
- * 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 *);
+ GRE_RLOCK(sc);
+ if (sc->gre_family == 0)
+ goto bad;
- if_inc_counter(GRE2IFP(sc), IFCOUNTER_IPACKETS, 1);
- if_inc_counter(GRE2IFP(sc), IFCOUNTER_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
- 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 ((GRE2IFP(sc)->if_flags & IFF_MONITOR) != 0) {
- m_freem(m);
- return(NULL);
- }
+ KASSERT(sc->gre_family == AF_INET,
+ ("wrong gre_family: %d", sc->gre_family));
- m->m_pkthdr.rcvif = GRE2IFP(sc);
- m_clrprotoflags(m);
- netisr_queue(isr, m);
-
- /* 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
- */
-
int
-gre_mobile_input(struct mbuf **mp, int *offp, int proto)
+in_gre_output(struct mbuf *m, int af, int hlen)
{
- struct ip *ip;
- struct mobip_h *mip;
- struct mbuf *m;
- struct gre_softc *sc;
- int msiz;
-
- m = *mp;
- if ((sc = gre_lookup(m, IPPROTO_MOBILE)) == NULL) {
- /* No matching tunnel or tunnel is down. */
- m_freem(m);
- return (IPPROTO_DONE);
- }
-
- if (m->m_len < sizeof(*mip)) {
- m = m_pullup(m, sizeof(*mip));
- if (m == NULL)
- return (IPPROTO_DONE);
- }
- ip = mtod(m, struct ip *);
- mip = mtod(m, struct mobip_h *);
-
- if_inc_counter(GRE2IFP(sc), IFCOUNTER_IPACKETS, 1);
- if_inc_counter(GRE2IFP(sc), IFCOUNTER_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 (IPPROTO_DONE);
- 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 (IPPROTO_DONE);
- }
-
- 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
- * decreased by the lengh of IP header, however, ip_input()
- * expects it to be full size of IP packet, so adjust accordingly.
- */
- ip->ip_len = htons(ntohs(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 (IPPROTO_DONE);
+ 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 */
+ gi->gi_ip.ip_id = ip_newid();
+ break;
+#endif
}
-
- m->m_pkthdr.rcvif = GRE2IFP(sc);
-
- netisr_queue(NETISR_IP, m);
- return (IPPROTO_DONE);
+ 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;
-
- GRE_LIST_LOCK();
- for (sc = LIST_FIRST(&V_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)) {
- GRE_LIST_UNLOCK();
- return (sc);
- }
- }
- GRE_LIST_UNLOCK();
- 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);
}