Subject: rfc: gre over udp [patch]
To: None <tech-net@netbsd.org>
From: David Young <dyoung@pobox.com>
List: tech-net
Date: 08/26/2006 02:18:04
--5oH/S/bF6lOfqCQb
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
I need to tunnel packets through NAT routers to a tunnel concentrator at
my office. To that end, I am extending gre(4) to put tunnel packets into
UDP datagrams. I have attached a patch that contains my work in progress.
I request your feedback.
In UDP mode, gre(4) puts a GRE header onto transmitted packets, and hands
them to a UDP socket for transmission. That is, the encapsulation looks
like IP+UDP+GRE+encapsulated packet.
There are two ways to set up a UDP gre(4) instance. One way is to tell
the source and destination IP+port to gre(4), and let gre(4) create
the socket:
# ifconfig gre0 create link2
# ifconfig gre0 tunnel 192.168.1.107,1025 192.168.1.101,1025
# ifconfig gre0 inet 192.168.49.2 192.168.49.1
# ifconfig gre0
gre0: flags=d051<UP,POINTOPOINT,RUNNING,LINK0,LINK2,MULTICAST> mtu 1476
tunnel inet 192.168.1.107,1025 --> 192.168.1.101,1025
inet 192.168.49.2 -> 192.168.49.1 netmask 0xffffff00
inet6 fe80::202:6fff:fe20:f62e%gre0 -> prefixlen 64 scopeid 0x6
Note that to specify the UDP port number, some ifconfig(8) changes
were necessary. Those are in the patch.
The other way to create a UDP instance is for userland to "delegate" a
UDP socket to the kernel. I have only started to program that feature;
see case GRESSOCK in gre_ioctl(). I intend to use this feature in a
server that will run on the tunnel concentrator, authenticating remote
tunnel endpoints and setting up tunnel privacy (perhaps using IPSec)
before delegating the socket to the tunnel interface.
Dave
--
David Young OJC Technologies
dyoung@ojctech.com Urbana, IL * (217) 278-3933
--5oH/S/bF6lOfqCQb
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="gre.patch"
Index: sys/net/if_gre.c
===================================================================
RCS file: /cvsroot/src/sys/net/if_gre.c,v
retrieving revision 1.61
diff -u -p -u -p -r1.61 if_gre.c
--- sys/net/if_gre.c 23 Jul 2006 22:06:12 -0000 1.61
+++ sys/net/if_gre.c 26 Aug 2006 07:11:14 -0000
@@ -56,11 +56,14 @@ __KERNEL_RCSID(0, "$NetBSD: if_gre.c,v 1
#ifdef INET
#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/filedesc.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/proc.h>
#include <sys/protosw.h>
#include <sys/socket.h>
+#include <sys/socketvar.h>
#include <sys/ioctl.h>
#include <sys/queue.h>
#if __NetBSD__
@@ -68,6 +71,8 @@ __KERNEL_RCSID(0, "$NetBSD: if_gre.c,v 1
#include <sys/kauth.h>
#endif
+#include <sys/kthread.h>
+
#include <machine/cpu.h>
#include <net/ethertypes.h>
@@ -126,6 +131,42 @@ static int gre_ioctl(struct ifnet *, u_l
static int gre_compute_route(struct gre_softc *sc);
+static int gre_getsockname(struct socket *, struct mbuf *, struct lwp *);
+#ifdef GRESSOCK
+static int gre_getpeername(struct socket *, struct mbuf *, struct lwp *);
+static int gre_getnames(struct socket *, struct lwp *, struct sockaddr_in *,
+ struct sockaddr_in *);
+#endif /* GRESSOCK */
+
+static void
+gre_stop(int *running)
+{
+ *running = 0;
+ wakeup(running);
+}
+
+static void
+gre_join(int *running)
+{
+ int s;
+
+ s = splnet();
+ while (*running != 0) {
+ splx(s);
+ tsleep(running, PSOCK, "grejoin", 0);
+ s = splnet();
+ }
+ splx(s);
+}
+
+static void
+gre_wakeup(int *waitchan)
+{
+ printf("%s: enter\n", __func__);
+ *waitchan = 1;
+ wakeup(waitchan);
+}
+
static int
gre_clone_create(struct if_clone *ifc, int unit)
{
@@ -146,7 +187,10 @@ gre_clone_create(struct if_clone *ifc, i
sc->sc_if.if_output = gre_output;
sc->sc_if.if_ioctl = gre_ioctl;
sc->g_dst.s_addr = sc->g_src.s_addr = INADDR_ANY;
+ sc->g_dstport = sc->g_srcport = 0;
sc->g_proto = IPPROTO_GRE;
+ IFQ_SET_READY(&sc->sc_snd);
+ IFQ_SET_MAXLEN(&sc->sc_snd, IFQ_MAXLEN);
sc->sc_if.if_flags |= IFF_LINK0;
if_attach(&sc->sc_if);
if_alloc_sadl(&sc->sc_if);
@@ -167,11 +211,365 @@ gre_clone_destroy(struct ifnet *ifp)
bpfdetach(ifp);
#endif
if_detach(ifp);
+ gre_wakeup(&sc->sc_waitchan);
+ gre_join(&sc->sc_thread);
free(sc, M_DEVBUF);
return (0);
}
+static void
+gre_receive(struct socket *so, caddr_t arg, int waitflag)
+{
+ struct gre_softc *sc = (struct gre_softc *)arg;
+
+ printf("%s: enter\n", __func__);
+
+ gre_wakeup(&sc->sc_waitchan);
+}
+
+static void
+gre_sodestroy(struct socket **sop)
+{
+ printf("%s: shutting down\n", __func__);
+ soshutdown(*sop, SHUT_RDWR);
+ printf("%s: closing\n", __func__);
+ soclose(*sop);
+ *sop = NULL;
+}
+
+static struct mbuf *
+gre_getsockmbuf(struct socket *so)
+{
+ struct mbuf *m;
+
+ m = m_get(M_WAIT, MT_SONAME);
+ if (m != NULL)
+ MCLAIM(m, so->so_mowner);
+ return m;
+}
+
+static void
+gre_upcallsetup(struct socket *so, caddr_t arg)
+{
+ /* XXX What if the kernel already set an upcall? */
+ so->so_upcallarg = arg;
+ so->so_upcall = gre_receive;
+ so->so_rcv.sb_flags |= SB_UPCALL;
+}
+
+static int
+gre_socreate1(struct gre_softc *sc, struct lwp *l, struct gre_soparm *sp,
+ struct socket **sop)
+{
+ int rc;
+ struct mbuf *m;
+ struct sockaddr_in *sin;
+ struct socket *so;
+
+ printf("%s: enter\n", __func__);
+ rc = socreate(AF_INET, sop, SOCK_DGRAM, IPPROTO_UDP, l);
+ if (rc != 0) {
+ printf("%s: socreate failed\n", __func__);
+ return rc;
+ }
+
+ so = *sop;
+
+ gre_upcallsetup(so, (caddr_t)sc);
+ if ((m = gre_getsockmbuf(so)) == NULL) {
+ rc = ENOBUFS;
+ goto out;
+ }
+ sin = mtod(m, struct sockaddr_in *);
+ sin->sin_len = m->m_len = sizeof(struct sockaddr_in);
+ sin->sin_family = AF_INET;
+ sin->sin_addr = sc->g_src;
+ sin->sin_port = sc->g_srcport;
+
+ printf("%s: bind 0x%08" PRIx32 " port %d\n", __func__,
+ sin->sin_addr.s_addr, ntohs(sin->sin_port));
+ if ((rc = sobind(so, m, l)) != 0) {
+ printf("%s: sobind failed\n", __func__);
+ goto out;
+ }
+
+ if (sc->g_srcport == 0) {
+ if (gre_getsockname(so, m, l) != 0) {
+ printf("%s: gre_getsockname failed\n", __func__);
+ goto out;
+ }
+ sc->g_srcport = sin->sin_port;
+ }
+
+ sin->sin_addr = sc->g_dst;
+ sin->sin_port = sc->g_dstport;
+
+ rc = soconnect(so, m, l);
+
+ if (rc != 0) {
+ printf("%s: soconnect failed\n", __func__);
+ goto out;
+ }
+
+ *mtod(m, int *) = ip_gre_ttl;
+ rc = (*so->so_proto->pr_ctloutput)(PRCO_SETOPT, so, IPPROTO_IP, IP_TTL,
+ &m);
+ m = NULL;
+ if (rc != 0) {
+ printf("%s: setopt ttl failed\n", __func__);
+ rc = 0;
+ }
+out:
+ m_freem(m);
+
+ if (rc != 0)
+ gre_sodestroy(sop);
+ else
+ *sp = sc->sc_sp;
+
+ return rc;
+}
+
+static void
+gre_thread1(struct gre_softc *sc, struct lwp *l)
+{
+ int flags, rc, s;
+ const struct gre_h *gh;
+ struct ifnet *ifp = &sc->sc_if;
+ struct mbuf *m;
+ struct socket *so;
+ struct uio uio;
+ struct gre_soparm sp;
+
+ printf("%s: enter\n", __func__);
+ s = splnet();
+
+ if (gre_socreate1(sc, l, &sp, &so) != 0)
+ goto out;
+
+ memset(&sp, 0, sizeof(sp));
+ memset(&uio, 0, sizeof(uio));
+
+ ifp->if_flags |= IFF_RUNNING;
+
+ for (;;) {
+ printf("%s: sleeping\n", __func__);
+ while (sc->sc_waitchan == 0) {
+ splx(s);
+ tsleep(&sc->sc_waitchan, PSOCK, "grewait", 0);
+ s = splnet();
+ }
+ sc->sc_waitchan = 0;
+ printf("%s: woke\n", __func__);
+ if ((ifp->if_flags & IFF_UP) != IFF_UP) {
+ printf("%s: not up & running; exiting\n", __func__);
+ break;
+ }
+ if (sc->g_proto != IPPROTO_UDP) {
+ printf("%s: not udp; exiting\n", __func__);
+ break;
+ }
+ /* XXX optimize */
+ if (memcmp(&sp, &sc->sc_sp, sizeof(sp)) != 0) {
+ printf("%s: parameters changed\n", __func__);
+#ifdef GRESSOCK
+ if (sp.sp_fp != NULL) {
+ FILE_UNUSE(sp.sp_fp, NULL);
+ sp.sp_fp = NULL;
+ so = NULL;
+ } else
+#endif /* GRESSOCK */
+ gre_sodestroy(&so);
+#ifdef GRESSOCK
+ if (sc->sc_fp != NULL) {
+ so = (struct socket *)sc->sc_fp->f_data;
+ gre_upcallsetup(so, (caddr_t)sc);
+ sp = sc->sc_sp;
+ FILE_USE(sp.sp_fp);
+ } else
+#endif /* GRESSOCK */
+ if (gre_socreate1(sc, l, &sp, &so) != 0)
+ goto out;
+ }
+ for (;;) {
+ flags = MSG_DONTWAIT;
+ uio.uio_resid = 1000000;
+ rc = (*so->so_receive)(so, NULL, &uio, &m, NULL,
+ &flags);
+ /* TBD Back off if ECONNREFUSED (indicates
+ * ICMP Port Unreachable)?
+ */
+ if (rc == EWOULDBLOCK) {
+ printf("%s: so_receive EWOULDBLOCK\n",
+ __func__);
+ break;
+ } else if (rc != 0 || m == NULL) {
+ printf("%s: rc %d m %p\n",
+ ifp->if_xname, rc, (void *)m);
+ break;
+ }
+ if (m->m_len < sizeof(*gh) &&
+ (m = m_pullup(m, sizeof(*gh))) == NULL) {
+ printf("%s: m_pullup failed\n", __func__);
+ continue;
+ }
+ gh = mtod(m, const struct gre_h *);
+
+ if (gre_input3(sc, m, 0, IPPROTO_GRE, gh) == 0) {
+ printf("%s: dropping unsupported\n", __func__);
+ m_freem(m);
+ }
+ }
+ for (;;) {
+ IF_DEQUEUE(&sc->sc_snd, m);
+ if (m == NULL)
+ break;
+ printf("%s: dequeue\n", __func__);
+ if ((so->so_state & SS_ISCONNECTED) == 0) {
+ printf("%s: not connected\n", __func__);
+ m_freem(m);
+ continue;
+ }
+ rc = (*so->so_send)(so, NULL, NULL, m, NULL, 0, l);
+ /* XXX handle ENOBUFS? */
+ if (rc != 0)
+ printf("%s: so_send failed\n", __func__);
+ }
+ }
+#ifdef GRESSOCK
+ if (sp.sp_fp != NULL) {
+ FILE_UNUSE(sp.sp_fp, NULL);
+ sp.sp_fp = NULL;
+ } else
+#endif /* GRESSOCK */
+ gre_sodestroy(&so);
+out:
+ printf("%s: stopping\n", __func__);
+ if (sc->g_proto == IPPROTO_UDP)
+ ifp->if_flags &= ~IFF_RUNNING;
+ IFQ_PURGE(&sc->sc_snd);
+ gre_stop(&sc->sc_thread);
+ /* must not touch sc after this! */
+ printf("%s: restore ipl\n", __func__);
+ splx(s);
+}
+
+static void
+gre_thread(void *arg)
+{
+ struct gre_softc *sc = (struct gre_softc *)arg;
+
+ gre_thread1(sc, curlwp);
+ /* must not touch sc after this! */
+ kthread_exit(0);
+}
+
+int
+gre_input3(struct gre_softc *sc, struct mbuf *m, int hlen, u_char proto,
+ const struct gre_h *gh)
+{
+ u_int16_t flags;
+#if NBPFILTER > 0
+ u_int32_t af = AF_INET; /* af passed to BPF tap */
+#endif
+ int s, isr;
+ struct ifqueue *ifq;
+
+ sc->sc_if.if_ipackets++;
+ sc->sc_if.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(gh->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 (0);
+ if (flags & GRE_KP)
+ hlen += 4;
+ if (flags & GRE_SP)
+ hlen += 4;
+
+ switch (ntohs(gh->ptype)) { /* ethertypes */
+ case ETHERTYPE_IP: /* shouldn't need a schednetisr(), as */
+ ifq = &ipintrq; /* we are in ip_input */
+ isr = NETISR_IP;
+ break;
+#ifdef NS
+ case ETHERTYPE_NS:
+ ifq = &nsintrq;
+ isr = NETISR_NS;
+#if NBPFILTER > 0
+ af = AF_NS;
+#endif
+ break;
+#endif
+#ifdef NETATALK
+ case ETHERTYPE_ATALK:
+ ifq = &atintrq1;
+ isr = NETISR_ATALK;
+#if NBPFILTER > 0
+ af = AF_APPLETALK;
+#endif
+ break;
+#endif
+#ifdef INET6
+ case ETHERTYPE_IPV6:
+#ifdef GRE_DEBUG
+ printf("%s: IPv6 packet\n", __func__);
+#endif
+ ifq = &ip6intrq;
+ isr = NETISR_IPV6;
+#if NBPFILTER > 0
+ af = AF_INET6;
+#endif
+ break;
+#endif
+ default: /* others not yet supported */
+ printf("%s: unhandled ethertype 0x%04x\n", __func__,
+ ntohs(gh->ptype));
+ return (0);
+ }
+ break;
+ default:
+ /* others not yet supported */
+ return (0);
+ }
+
+ if (hlen > m->m_pkthdr.len) {
+ m_freem(m);
+ return (EINVAL);
+ }
+ m_adj(m, hlen);
+
+#if NBPFILTER > 0
+ if (sc->sc_if.if_bpf != NULL)
+ bpf_mtap_af(sc->sc_if.if_bpf, af, m);
+#endif /*NBPFILTER > 0*/
+
+ m->m_pkthdr.rcvif = &sc->sc_if;
+
+ s = splnet(); /* possible */
+ if (IF_QFULL(ifq)) {
+ IF_DROP(ifq);
+ m_freem(m);
+ } else {
+ IF_ENQUEUE(ifq, m);
+ }
+ /* we need schednetisr since the address family may change */
+ schednetisr(isr);
+ splx(s);
+
+ return (1); /* packet is done, no further processing needed */
+}
+
/*
* The output routine. Takes a packet and encapsulates it in the protocol
* given by sc->g_proto. See also RFC 1701 and RFC 2004
@@ -180,10 +578,11 @@ static int
gre_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
struct rtentry *rt)
{
- int error = 0;
+ int error = 0, hlen;
struct gre_softc *sc = ifp->if_softc;
- struct greip *gh;
- struct ip *ip;
+ struct greip *gi;
+ struct gre_h *gh;
+ struct ip *eip, *ip;
u_int8_t ip_tos = 0;
u_int16_t etype = 0;
struct mobile_h mob_h;
@@ -195,7 +594,7 @@ gre_output(struct ifnet *ifp, struct mbu
goto end;
}
- gh = NULL;
+ gi = NULL;
ip = NULL;
#if NBPFILTER >0
@@ -205,11 +604,16 @@ gre_output(struct ifnet *ifp, struct mbu
m->m_flags &= ~(M_BCAST|M_MCAST);
- if (sc->g_proto == IPPROTO_MOBILE) {
+ switch (sc->g_proto) {
+ case IPPROTO_MOBILE:
if (dst->sa_family == AF_INET) {
- struct mbuf *m0;
int msiz;
+ if (M_UNWRITABLE(m, sizeof(*ip)) &&
+ (m = m_pullup(m, sizeof(*ip))) == NULL) {
+ error = ENOBUFS;
+ goto end;
+ }
ip = mtod(m, struct ip *);
memset(&mob_h, 0, MOB_H_SIZ_L);
@@ -233,31 +637,16 @@ gre_output(struct ifnet *ifp, struct mbu
HTONS(mob_h.proto);
mob_h.hcrc = gre_in_cksum((u_int16_t *)&mob_h, msiz);
- if ((m->m_data - msiz) < m->m_pktdat) {
- /* need new mbuf */
- MGETHDR(m0, M_DONTWAIT, MT_HEADER);
- if (m0 == NULL) {
- IF_DROP(&ifp->if_snd);
- m_freem(m);
- error = ENOBUFS;
- goto end;
- }
- m0->m_next = m;
- m->m_data += sizeof(struct ip);
- m->m_len -= sizeof(struct ip);
- m0->m_pkthdr.len = m->m_pkthdr.len + msiz;
- m0->m_len = msiz + sizeof(struct ip);
- m0->m_data += max_linkhdr;
- memcpy(mtod(m0, caddr_t), (caddr_t)ip,
- sizeof(struct ip));
- m = m0;
- } else { /* we have some space left in the old one */
- m->m_data -= msiz;
- m->m_len += msiz;
- m->m_pkthdr.len += msiz;
- memmove(mtod(m, caddr_t), ip,
- sizeof(struct ip));
+ M_PREPEND(m, msiz, M_DONTWAIT);
+ if (m == NULL) {
+ error = ENOBUFS;
+ goto end;
}
+ /* XXX Assuming that ip does not dangle after
+ * M_PREPEND. In practice, that's true, but
+ * that's in M_PREPEND's contract.
+ */
+ memmove(mtod(m, caddr_t), ip, sizeof(*ip));
ip = mtod(m, struct ip *);
memcpy((caddr_t)(ip + 1), &mob_h, (unsigned)msiz);
ip->ip_len = htons(ntohs(ip->ip_len) + msiz);
@@ -267,10 +656,11 @@ gre_output(struct ifnet *ifp, struct mbu
error = EINVAL;
goto end;
}
- } else if (sc->g_proto == IPPROTO_GRE) {
+ break;
+ case IPPROTO_UDP:
+ case IPPROTO_GRE:
#ifdef GRE_DEBUG
- printf( "start gre_output/GRE, dst->sa_family=%d\n",
- dst->sa_family );
+ printf("%s: dst->sa_family=%d\n", __func__, dst->sa_family);
#endif
switch (dst->sa_family) {
case AF_INET:
@@ -299,52 +689,184 @@ gre_output(struct ifnet *ifp, struct mbu
error = EAFNOSUPPORT;
goto end;
}
- M_PREPEND(m, sizeof(struct greip), M_DONTWAIT);
- } else {
+ break;
+ default:
IF_DROP(&ifp->if_snd);
m_freem(m);
error = EINVAL;
goto end;
}
- if (m == NULL) { /* impossible */
+ switch (sc->g_proto) {
+ case IPPROTO_GRE:
+ hlen = sizeof(struct greip);
+ break;
+ case IPPROTO_UDP:
+ hlen = sizeof(struct gre_h);
+ break;
+ default:
+ hlen = 0;
+ break;
+ }
+
+ M_PREPEND(m, hlen, M_DONTWAIT);
+
+ if (m == NULL) {
IF_DROP(&ifp->if_snd);
error = ENOBUFS;
goto end;
}
- gh = mtod(m, struct greip *);
- if (sc->g_proto == IPPROTO_GRE) {
+ switch (sc->g_proto) {
+ case IPPROTO_UDP:
+ gh = mtod(m, struct gre_h *);
+ memset(gh, 0, sizeof(*gh));
+ gh->ptype = htons(etype);
+ /* XXX Need to handle IP ToS. Look at how I handle IP TTL. */
+ break;
+ case IPPROTO_GRE:
+ gi = mtod(m, struct greip *);
+ gh = &gi->gi_g;
+ eip = &gi->gi_i;
/* we don't have any GRE flags for now */
-
- memset((void *)&gh->gi_g, 0, sizeof(struct gre_h));
- gh->gi_ptype = htons(etype);
- }
-
- gh->gi_pr = sc->g_proto;
- if (sc->g_proto != IPPROTO_MOBILE) {
- gh->gi_src = sc->g_src;
- gh->gi_dst = sc->g_dst;
- ((struct ip*)gh)->ip_hl = (sizeof(struct ip)) >> 2;
- ((struct ip*)gh)->ip_ttl = ip_gre_ttl;
- ((struct ip*)gh)->ip_tos = ip_tos;
- gh->gi_len = htons(m->m_pkthdr.len);
+ memset(gh, 0, sizeof(*gh));
+ gh->ptype = htons(etype);
+ eip->ip_src = sc->g_src;
+ eip->ip_dst = sc->g_dst;
+ eip->ip_hl = (sizeof(struct ip)) >> 2;
+ eip->ip_ttl = ip_gre_ttl;
+ eip->ip_tos = ip_tos;
+ eip->ip_len = htons(m->m_pkthdr.len);
+ eip->ip_p = sc->g_proto;
+ break;
+ case IPPROTO_MOBILE:
+ eip = mtod(m, struct ip *);
+ eip->ip_p = sc->g_proto;
+ break;
+ default:
+ error = EPROTONOSUPPORT;
+ m_freem(m);
+ goto end;
}
ifp->if_opackets++;
ifp->if_obytes += m->m_pkthdr.len;
+
/* send it off */
- error = ip_output(m, NULL, &sc->route, 0,
- (struct ip_moptions *)NULL, (struct socket *)NULL);
+ if (sc->g_proto == IPPROTO_UDP) {
+ if (IF_QFULL(&sc->sc_snd)) {
+ IF_DROP(&sc->sc_snd);
+ error = ENOBUFS;
+ m_freem(m);
+ } else {
+ IF_ENQUEUE(&sc->sc_snd, m);
+ gre_wakeup(&sc->sc_waitchan);
+ error = 0;
+ }
+ } else {
+ error = ip_output(m, NULL, &sc->route, 0,
+ (struct ip_moptions *)NULL, (struct socket *)NULL);
+ }
end:
if (error)
ifp->if_oerrors++;
return (error);
}
+/* Must be called at IPL_NET. */
+static int
+gre_kick(struct gre_softc *sc)
+{
+ int rc;
+ struct ifnet *ifp = &sc->sc_if;
+
+ if (sc->g_proto == IPPROTO_UDP && (ifp->if_flags & IFF_UP) == IFF_UP &&
+ !sc->sc_thread) {
+ sc->sc_thread = 1;
+ rc = kthread_create1(gre_thread, (void *)sc, NULL,
+ ifp->if_xname);
+ if (rc != 0)
+ gre_stop(&sc->sc_thread);
+ return rc;
+ } else {
+ gre_wakeup(&sc->sc_waitchan);
+ return 0;
+ }
+}
+
+static int
+gre_getname(struct socket *so, int req, struct mbuf *nam, struct lwp *l)
+{
+ int s, error;
+
+ printf("%s: enter\n", __func__);
+ s = splsoftnet();
+ error = (*so->so_proto->pr_usrreq)(so, req, (struct mbuf *)0,
+ nam, (struct mbuf *)0, l);
+ splx(s);
+ return error;
+}
+
+static int
+gre_getsockname(struct socket *so, struct mbuf *nam, struct lwp *l)
+{
+ printf("%s: enter\n", __func__);
+ return gre_getname(so, PRU_SOCKADDR, nam, l);
+}
+
+#ifdef GRESSOCK
+static int
+gre_getpeername(struct socket *so, struct mbuf *nam, struct lwp *l)
+{
+ printf("%s: enter\n", __func__);
+ return gre_getname(so, PRU_PEERADDR, nam, l);
+}
+
+static int
+gre_getnames(struct socket *so, struct lwp *l, struct sockaddr_in *src,
+ struct sockaddr_in *dst)
+{
+ struct mbuf *m;
+ struct sockaddr_in *sin;
+ int rc;
+
+ if ((m = gre_getsockmbuf(so)) == NULL)
+ return ENOBUFS;
+
+ sin = mtod(m, struct sockaddr_in *);
+
+ if ((rc = gre_getsockname(so, m, l)) != 0)
+ goto out;
+ if (sin->sin_family != AF_INET) {
+ rc = EAFNOSUPPORT;
+ goto out;
+ }
+ *src = *sin;
+
+ if ((rc = gre_getpeername(so, m, l)) != 0)
+ goto out;
+ if (sin->sin_family != AF_INET) {
+ rc = EAFNOSUPPORT;
+ goto out;
+ }
+ *dst = *sin;
+
+out:
+ m_freem(m);
+ return rc;
+}
+#endif /* GRESSOCK */
+
static int
gre_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
{
+ u_char oproto;
+#ifdef GRESSOCK
+ struct file *fp, *ofp;
+ struct socket *so;
+ struct sockaddr_in dst, src;
+#endif /* GRESSOCK */
+ struct proc *p = curproc; /* XXX */
struct lwp *l = curlwp; /* XXX */
struct ifreq *ifr = (struct ifreq *)data;
struct if_laddrreq *lifr = (struct if_laddrreq *)data;
@@ -360,6 +882,8 @@ gre_ioctl(struct ifnet *ifp, u_long cmd,
case GRESPROTO:
case GRESADDRD:
case GRESADDRS:
+ case GRESSOCK:
+ case GREDSOCK:
case SIOCSLIFPHYADDR:
case SIOCDIFPHYADDR:
if ((error = kauth_authorize_generic(l->l_cred,
@@ -375,14 +899,28 @@ gre_ioctl(struct ifnet *ifp, u_long cmd,
switch (cmd) {
case SIOCSIFADDR:
ifp->if_flags |= IFF_UP;
+ error = gre_kick(sc);
break;
case SIOCSIFDSTADDR:
break;
case SIOCSIFFLAGS:
- if ((ifr->ifr_flags & IFF_LINK0) != 0)
+ oproto = sc->g_proto;
+ switch (ifr->ifr_flags & (IFF_LINK0|IFF_LINK2)) {
+ case IFF_LINK0|IFF_LINK2:
+ sc->g_proto = IPPROTO_UDP;
+ if (oproto != IPPROTO_UDP)
+ ifp->if_flags &= ~IFF_RUNNING;
+ error = gre_kick(sc);
+ break;
+ case IFF_LINK0:
sc->g_proto = IPPROTO_GRE;
- else
+ gre_wakeup(&sc->sc_waitchan);
+ goto recompute;
+ case 0:
sc->g_proto = IPPROTO_MOBILE;
+ gre_wakeup(&sc->sc_waitchan);
+ goto recompute;
+ }
break;
case SIOCSIFMTU:
if (ifr->ifr_mtu < 576) {
@@ -415,14 +953,22 @@ gre_ioctl(struct ifnet *ifp, u_long cmd,
}
break;
case GRESPROTO:
+ oproto = sc->g_proto;
sc->g_proto = ifr->ifr_flags;
switch (sc->g_proto) {
+ case IPPROTO_UDP:
+ ifp->if_flags |= IFF_LINK0|IFF_LINK2;
+ if (oproto != IPPROTO_UDP)
+ ifp->if_flags &= ~IFF_RUNNING;
+ error = gre_kick(sc);
+ break;
case IPPROTO_GRE:
ifp->if_flags |= IFF_LINK0;
- break;
+ ifp->if_flags &= ~IFF_LINK2;
+ goto recompute;
case IPPROTO_MOBILE:
- ifp->if_flags &= ~IFF_LINK0;
- break;
+ ifp->if_flags &= ~(IFF_LINK0|IFF_LINK2);
+ goto recompute;
default:
error = EPROTONOSUPPORT;
break;
@@ -438,16 +984,36 @@ gre_ioctl(struct ifnet *ifp, u_long cmd,
* to the remote end and mark if as up
*/
sa = &ifr->ifr_addr;
- if (cmd == GRESADDRS)
+ if (cmd == GRESADDRS) {
sc->g_src = (satosin(sa))->sin_addr;
- if (cmd == GRESADDRD)
+ sc->g_srcport = satosin(sa)->sin_port;
+ }
+ if (cmd == GRESADDRD) {
+ if (sc->g_proto == IPPROTO_UDP &&
+ satosin(sa)->sin_port == 0) {
+ error = EINVAL;
+ break;
+ }
sc->g_dst = (satosin(sa))->sin_addr;
+ sc->g_dstport = satosin(sa)->sin_port;
+ }
recompute:
- if ((sc->g_src.s_addr != INADDR_ANY) &&
- (sc->g_dst.s_addr != INADDR_ANY)) {
- if (sc->route.ro_rt != 0) /* free old route */
+ if (sc->g_proto == IPPROTO_UDP ||
+ (sc->g_src.s_addr != INADDR_ANY &&
+ sc->g_dst.s_addr != INADDR_ANY)) {
+#ifdef GRESSOCK
+ if (sc->sc_fp != NULL) {
+ FILE_UNUSE(sc->sc_fp, NULL);
+ sc->sc_fp = NULL;
+ }
+#endif /* GRESSOCK */
+ if (sc->route.ro_rt != NULL) {
RTFREE(sc->route.ro_rt);
- if (gre_compute_route(sc) == 0)
+ sc->route.ro_rt = NULL;
+ }
+ if (sc->g_proto == IPPROTO_UDP)
+ error = gre_kick(sc);
+ else if (gre_compute_route(sc) == 0)
ifp->if_flags |= IFF_RUNNING;
else
ifp->if_flags &= ~IFF_RUNNING;
@@ -469,6 +1035,47 @@ gre_ioctl(struct ifnet *ifp, u_long cmd,
sa = sintosa(&si);
ifr->ifr_addr = *sa;
break;
+#ifdef GRESSOCK
+ case GREDSOCK:
+ if (sc->g_proto != IPPROTO_UDP)
+ return EINVAL;
+ FILE_UNUSE(sc->sc_fp, NULL);
+ sc->sc_fp = NULL;
+ error = gre_kick(sc);
+ break;
+ case GRESSOCK:
+ if (sc->g_proto != IPPROTO_UDP)
+ return EINVAL;
+ /* getsock() will use the descriptor for us */
+ if ((error = getsock(p->p_fd, (int)ifr->ifr_value, &fp)) != 0)
+ break;
+ so = (struct socket *)fp->f_data;
+ if (so->so_type != SOCK_DGRAM) {
+ FILE_UNUSE(fp, NULL);
+ error = EINVAL;
+ break;
+ }
+ /* check address */
+ if ((error = gre_getnames(so, curlwp, &src, &dst)) != 0) {
+ FILE_UNUSE(fp, NULL);
+ break;
+ }
+
+ ofp = sc->sc_fp;
+ sc->sc_fp = fp;
+ if ((error = gre_kick(sc)) != 0) {
+ FILE_UNUSE(fp, NULL);
+ sc->sc_fp = ofp;
+ break;
+ }
+ sc->g_src = src.sin_addr;
+ sc->g_srcport = src.sin_port;
+ sc->g_dst = dst.sin_addr;
+ sc->g_dstport = dst.sin_port;
+ if (ofp != NULL)
+ FILE_UNUSE(ofp, NULL);
+ break;
+#endif /* GRESSOCK */
case SIOCSLIFPHYADDR:
if (lifr->addr.ss_family != AF_INET ||
lifr->dstaddr.ss_family != AF_INET) {
@@ -480,14 +1087,15 @@ gre_ioctl(struct ifnet *ifp, u_long cmd,
error = EINVAL;
break;
}
- sc->g_src = (satosin((struct sockadrr *)&lifr->addr))->sin_addr;
- sc->g_dst =
- (satosin((struct sockadrr *)&lifr->dstaddr))->sin_addr;
+ sc->g_src = satosin(&lifr->addr)->sin_addr;
+ sc->g_dst = satosin(&lifr->dstaddr)->sin_addr;
+ sc->g_srcport = satosin(&lifr->addr)->sin_port;
+ sc->g_dstport = satosin(&lifr->dstaddr)->sin_port;
goto recompute;
case SIOCDIFPHYADDR:
sc->g_src.s_addr = INADDR_ANY;
sc->g_dst.s_addr = INADDR_ANY;
- break;
+ goto recompute;
case SIOCGLIFPHYADDR:
if (sc->g_src.s_addr == INADDR_ANY ||
sc->g_dst.s_addr == INADDR_ANY) {
@@ -497,16 +1105,19 @@ gre_ioctl(struct ifnet *ifp, u_long cmd,
memset(&si, 0, sizeof(si));
si.sin_family = AF_INET;
si.sin_len = sizeof(struct sockaddr_in);
- si.sin_addr.s_addr = sc->g_src.s_addr;
+ si.sin_addr = sc->g_src;
+ if (sc->g_proto == IPPROTO_UDP)
+ si.sin_port = sc->g_srcport;
memcpy(&lifr->addr, &si, sizeof(si));
- si.sin_addr.s_addr = sc->g_dst.s_addr;
+ si.sin_addr = sc->g_dst;
+ if (sc->g_proto == IPPROTO_UDP)
+ si.sin_port = sc->g_dstport;
memcpy(&lifr->dstaddr, &si, sizeof(si));
break;
default:
error = EINVAL;
break;
}
-
splx(s);
return (error);
}
@@ -580,9 +1191,8 @@ gre_compute_route(struct gre_softc *sc)
((struct sockaddr_in *)&ro->ro_dst)->sin_addr = sc->g_dst;
#ifdef DIAGNOSTIC
- printf(", choosing %s with gateway %s", ro->ro_rt->rt_ifp->if_xname,
+ printf(", choosing %s with gateway %s\n", ro->ro_rt->rt_ifp->if_xname,
inet_ntoa(((struct sockaddr_in *)(ro->ro_rt->rt_gateway))->sin_addr));
- printf("\n");
#endif
return 0;
Index: sys/net/if_gre.h
===================================================================
RCS file: /cvsroot/src/sys/net/if_gre.h,v
retrieving revision 1.16
diff -u -p -u -p -r1.16 if_gre.h
--- sys/net/if_gre.h 11 Dec 2005 23:05:25 -0000 1.16
+++ sys/net/if_gre.h 26 Aug 2006 07:11:14 -0000
@@ -41,18 +41,37 @@
#include <sys/queue.h>
+#if 1
+#define GRESSOCK _IOW('i' , 107, struct ifreq)
+#define GREDSOCK _IOW('i' , 108, struct ifreq)
+#endif
+
+struct gre_soparm {
+ struct in_addr sp_src; /* source address of gre packets */
+ struct in_addr sp_dst; /* destination address of gre packets */
+ in_port_t sp_srcport; /* source port of gre packets */
+ in_port_t sp_dstport; /* destination port of gre packets */
+#ifdef GRESSOCK
+ struct file *sp_fp;
+#endif /* GRESSOCK */
+};
+
struct gre_softc {
- struct ifnet sc_if;
+ struct ifnet sc_if;
+ int sc_waitchan;
+ int sc_thread;
+ struct ifqueue sc_snd;
+ struct gre_soparm sc_sp;
LIST_ENTRY(gre_softc) sc_list;
- int gre_unit;
- int gre_flags;
- struct in_addr g_src; /* source address of gre packets */
- struct in_addr g_dst; /* destination address of gre packets */
struct route route; /* routing entry that determines, where a
encapsulated packet should go */
u_char g_proto; /* protocol of encapsulator */
};
-
+#define g_src sc_sp.sp_src
+#define g_srcport sc_sp.sp_srcport
+#define g_dst sc_sp.sp_dst
+#define g_dstport sc_sp.sp_dstport
+#define sc_fp sc_sp.sp_fp
struct gre_h {
u_int16_t flags; /* GRE flags */
@@ -152,6 +171,8 @@ LIST_HEAD(gre_softc_head, gre_softc);
extern struct gre_softc_head gre_softc_list;
u_int16_t gre_in_cksum(u_short *, u_int);
+int gre_input3(struct gre_softc *, struct mbuf *, int, u_char,
+ const struct gre_h *);
#endif /* _KERNEL */
#endif /* !_NET_IF_GRE_H_ */
Index: sys/netinet/ip_gre.c
===================================================================
RCS file: /cvsroot/src/sys/netinet/ip_gre.c,v
retrieving revision 1.40
diff -u -p -u -p -r1.40 ip_gre.c
--- sys/netinet/ip_gre.c 28 Jul 2006 17:34:13 -0000 1.40
+++ sys/netinet/ip_gre.c 26 Aug 2006 07:11:14 -0000
@@ -147,13 +147,7 @@ int
gre_input2(struct mbuf *m, int hlen, u_char proto)
{
const struct greip *gip;
- int s, isr;
- struct ifqueue *ifq;
struct gre_softc *sc;
- u_int16_t flags;
-#if NBPFILTER > 0
- u_int32_t af = AF_INET; /* af passed to BPF tap */
-#endif
if ((sc = gre_lookup(m, proto)) == NULL) {
/* No matching tunnel or tunnel is down. */
@@ -167,97 +161,7 @@ gre_input2(struct mbuf *m, int hlen, u_c
}
gip = mtod(m, const struct greip *);
- sc->sc_if.if_ipackets++;
- sc->sc_if.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 (0);
- if (flags & GRE_KP)
- hlen += 4;
- if (flags & GRE_SP)
- hlen += 4;
-
- switch (ntohs(gip->gi_ptype)) { /* ethertypes */
- case ETHERTYPE_IP: /* shouldn't need a schednetisr(), as */
- ifq = &ipintrq; /* we are in ip_input */
- isr = NETISR_IP;
- break;
-#ifdef NS
- case ETHERTYPE_NS:
- ifq = &nsintrq;
- isr = NETISR_NS;
-#if NBPFILTER > 0
- af = AF_NS;
-#endif
- break;
-#endif
-#ifdef NETATALK
- case ETHERTYPE_ATALK:
- ifq = &atintrq1;
- isr = NETISR_ATALK;
-#if NBPFILTER > 0
- af = AF_APPLETALK;
-#endif
- break;
-#endif
-#ifdef INET6
- case ETHERTYPE_IPV6:
-#ifdef GRE_DEBUG
- printf( "ip_gre.c/gre_input2: IPv6 packet\n" );
-#endif
- ifq = &ip6intrq;
- isr = NETISR_IPV6;
-#if NBPFILTER > 0
- af = AF_INET6;
-#endif
- break;
-#endif
- default: /* others not yet supported */
- printf( "ip_gre.c/gre_input2: unhandled ethertype 0x%04x\n", (int) ntohs(gip->gi_ptype) );
- return (0);
- }
- break;
- default:
- /* others not yet supported */
- return (0);
- }
-
- if (hlen > m->m_pkthdr.len) {
- m_freem(m);
- return (EINVAL);
- }
- m_adj(m, hlen);
-
-#if NBPFILTER > 0
- if (sc->sc_if.if_bpf != NULL)
- bpf_mtap_af(sc->sc_if.if_bpf, af, m);
-#endif /*NBPFILTER > 0*/
-
- m->m_pkthdr.rcvif = &sc->sc_if;
-
- s = splnet(); /* possible */
- if (IF_QFULL(ifq)) {
- IF_DROP(ifq);
- m_freem(m);
- } else {
- IF_ENQUEUE(ifq, m);
- }
- /* we need schednetisr since the address family may change */
- schednetisr(isr);
- splx(s);
-
- return (1); /* packet is done, no further processing needed */
+ return gre_input3(sc, m, hlen, proto, &gip->gi_g);
}
/*
@@ -273,6 +177,7 @@ gre_mobile_input(struct mbuf *m, ...)
struct mobip_h *mip;
struct ifqueue *ifq;
struct gre_softc *sc;
+ uint8_t *hdr;
int hlen, s;
va_list ap;
int msiz;
@@ -293,6 +198,7 @@ gre_mobile_input(struct mbuf *m, ...)
return;
}
ip = mtod(m, struct ip *);
+ /* XXX what if there are IP options? */
mip = mtod(m, struct mobip_h *);
sc->sc_if.if_ipackets++;
@@ -304,8 +210,8 @@ gre_mobile_input(struct mbuf *m, ...)
} 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_UNWRITABLE(m, hlen + msiz)) {
+ m = m_pullup(m, hlen + msiz);
if (m == NULL)
return;
ip = mtod(m, struct ip *);
@@ -320,14 +226,14 @@ gre_mobile_input(struct mbuf *m, ...)
return;
}
- memmove(ip + (ip->ip_hl << 2), ip + (ip->ip_hl << 2) + msiz,
- m->m_len - msiz - (ip->ip_hl << 2));
+ hdr = mtod(m, uint8_t *);
+ memmove(hdr + hlen, hdr + hlen + msiz, m->m_len - msiz - hlen);
m->m_len -= msiz;
ip->ip_len = htons(ntohs(ip->ip_len) - msiz);
m->m_pkthdr.len -= msiz;
ip->ip_sum = 0;
- ip->ip_sum = in_cksum(m, (ip->ip_hl << 2));
+ ip->ip_sum = in_cksum(m, hlen);
#if NBPFILTER > 0
if (sc->sc_if.if_bpf != NULL)
Index: sbin/ifconfig/tunnel.c
===================================================================
RCS file: /cvsroot/src/sbin/ifconfig/tunnel.c,v
retrieving revision 1.6
diff -u -p -u -p -r1.6 tunnel.c
--- sbin/ifconfig/tunnel.c 16 Jun 2006 23:48:35 -0000 1.6
+++ sbin/ifconfig/tunnel.c 26 Aug 2006 07:11:14 -0000
@@ -50,6 +50,7 @@ __RCSID("$NetBSD: tunnel.c,v 1.6 2006/06
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
+#include <util.h>
#include "extern.h"
#include "tunnel.h"
@@ -59,9 +60,10 @@ __RCSID("$NetBSD: tunnel.c,v 1.6 2006/06
#endif
void
-settunnel(const char *src, const char *dst)
+settunnel(const char *src0, const char *dst0)
{
struct addrinfo hints, *srcres, *dstres;
+ char *dst, *dstport, *src, *srcport;
int ecode;
struct if_laddrreq req;
@@ -69,11 +71,21 @@ settunnel(const char *src, const char *d
hints.ai_family = afp->af_af;
hints.ai_socktype = SOCK_DGRAM; /*dummy*/
- if ((ecode = getaddrinfo(src, NULL, &hints, &srcres)) != 0)
+ if ((src = strdup(src0)) == NULL)
+ err(EXIT_FAILURE, "%s: strdup", __func__);
+ if ((dst = strdup(dst0)) == NULL)
+ err(EXIT_FAILURE, "%s: strdup", __func__);
+
+ srcport = src;
+ (void)strsep(&srcport, ",");
+ dstport = dst;
+ (void)strsep(&dstport, ",");
+
+ if ((ecode = getaddrinfo(src, srcport, &hints, &srcres)) != 0)
errx(EXIT_FAILURE, "error in parsing address string: %s",
gai_strerror(ecode));
- if ((ecode = getaddrinfo(dst, NULL, &hints, &dstres)) != 0)
+ if ((ecode = getaddrinfo(dst, dstport, &hints, &dstres)) != 0)
errx(EXIT_FAILURE, "error in parsing address string: %s",
gai_strerror(ecode));
@@ -123,6 +135,8 @@ settunnel(const char *src, const char *d
freeaddrinfo(srcres);
freeaddrinfo(dstres);
+ free(src);
+ free(dst);
}
/*ARGSUSED*/
@@ -134,9 +148,19 @@ deletetunnel(const char *ifname, int par
err(EXIT_FAILURE, "SIOCDIFPHYADDR");
}
+#if 0
+static int
+goodport(const char *port)
+{
+ return strcmp(port, ",0") != 0 && strcmp(port, ",N/A") != 0;
+}
+#endif
+
void
tunnel_status(void)
{
+ char dstserv[sizeof(",65535")];
+ char srcserv[sizeof(",65535")];
char psrcaddr[NI_MAXHOST];
char pdstaddr[NI_MAXHOST];
const int niflag = NI_NUMERICHOST;
@@ -155,15 +179,20 @@ tunnel_status(void)
in6_fillscopeid((struct sockaddr_in6 *)&req.addr);
#endif /* INET6 */
getnameinfo((struct sockaddr *)&req.addr, req.addr.ss_len,
- psrcaddr, sizeof(psrcaddr), 0, 0, niflag);
+ psrcaddr, sizeof(psrcaddr), &srcserv[1], sizeof(srcserv) - 1,
+ niflag);
#ifdef INET6
if (req.dstaddr.ss_family == AF_INET6)
in6_fillscopeid((struct sockaddr_in6 *)&req.dstaddr);
#endif
getnameinfo((struct sockaddr *)&req.dstaddr, req.dstaddr.ss_len,
- pdstaddr, sizeof(pdstaddr), 0, 0, niflag);
+ pdstaddr, sizeof(pdstaddr), &dstserv[1], sizeof(dstserv) - 1,
+ niflag);
+
+ srcserv[0] = (strcmp(srcserv, "0") == 0) ? '\0' : ',';
+ dstserv[0] = (strcmp(dstserv, "0") == 0) ? '\0' : ',';
- printf("\ttunnel %s %s --> %s\n", lafp ? lafp->af_name : "???",
- psrcaddr, pdstaddr);
+ printf("\ttunnel %s %s%s --> %s%s\n", lafp ? lafp->af_name : "???",
+ psrcaddr, srcserv, pdstaddr, dstserv);
}
--5oH/S/bF6lOfqCQb--