summaryrefslogtreecommitdiffstats
path: root/sys/net/if_tun.c
diff options
context:
space:
mode:
authoramurai <amurai@FreeBSD.org>1995-01-31 06:15:49 +0000
committeramurai <amurai@FreeBSD.org>1995-01-31 06:15:49 +0000
commit74b5a342fdaa61a48241c521c2b54ab517204f92 (patch)
tree261e7c18326fb1524bcdefd1440fb99d77d1cadd /sys/net/if_tun.c
downloadFreeBSD-src-74b5a342fdaa61a48241c521c2b54ab517204f92.zip
FreeBSD-src-74b5a342fdaa61a48241c521c2b54ab517204f92.tar.gz
Diffstat (limited to 'sys/net/if_tun.c')
-rw-r--r--sys/net/if_tun.c615
1 files changed, 615 insertions, 0 deletions
diff --git a/sys/net/if_tun.c b/sys/net/if_tun.c
new file mode 100644
index 0000000..d8f6ac3
--- /dev/null
+++ b/sys/net/if_tun.c
@@ -0,0 +1,615 @@
+/*
+ * Copyright (c) 1988, Julian Onions <jpo@cs.nott.ac.uk>
+ * Nottingham University 1987.
+ *
+ * This source may be freely distributed, however I would be interested
+ * in any changes that are made.
+ *
+ * This driver takes packets off the IP i/f and hands them up to a
+ * user process to have it's wicked way with. This driver has it's
+ * roots in a similar driver written by Phil Cockcroft (formerly) at
+ * UCL. This driver is based much more on read/write/select mode of
+ * operation though.
+ *
+ * $Id: if_tun.c,v 1.9 1993/12/24 03:20:59 deraadt Exp $
+ */
+
+#include "tun.h"
+#if NTUN > 0
+
+#include "param.h"
+#include "kernel.h" /* sigh */
+#include "proc.h"
+#include "systm.h"
+#include "mbuf.h"
+#include "buf.h"
+#include "protosw.h"
+#include "socket.h"
+#include "ioctl.h"
+#include "errno.h"
+#include "syslog.h"
+#include <sys/file.h>
+
+#include <machine/cpu.h>
+
+#include <net/if.h>
+#include <net/netisr.h>
+#include <net/route.h>
+
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/ip.h>
+#include <netinet/if_ether.h>
+#endif
+
+#ifdef NS
+#include <netns/ns.h>
+#include <netns/ns_if.h>
+#endif
+
+#include "bpfilter.h"
+#if NBPFILTER > 0
+#include <sys/time.h>
+#include <net/bpf.h>
+#endif
+
+#include <net/if_tun.h>
+
+
+#ifndef MIN
+#define MIN(a,b) (((a)<(b))?(a):(b))
+#endif
+
+#define TUNDEBUG if (tundebug) printf
+int tundebug = 0;
+
+struct tun_softc tunctl[NTUN];
+extern int ifqmaxlen;
+
+int tunopen __P((dev_t, int, int, struct proc *));
+int tunoutput __P((struct ifnet *, struct mbuf *, struct sockaddr *, struct rtentry *rt));
+int tunselect __P((dev_t, int, struct proc *));
+int tunifioctl __P((struct ifnet *, int, caddr_t));
+int tunioctl __P((dev_t, int, caddr_t, int));
+
+static int tuninit __P((int));
+
+void
+tunattach(void)
+{
+ register int i;
+ struct ifnet *ifp;
+ struct sockaddr_in *sin;
+
+ for (i = 0; i < NTUN; i++) {
+ tunctl[i].tun_flags = TUN_INITED;
+
+ ifp = &tunctl[i].tun_if;
+ ifp->if_unit = i;
+ ifp->if_name = "tun";
+ ifp->if_mtu = TUNMTU;
+ ifp->if_ioctl = tunifioctl;
+ ifp->if_output = tunoutput;
+ ifp->if_flags = IFF_POINTOPOINT;
+ ifp->if_snd.ifq_maxlen = ifqmaxlen;
+ ifp->if_collisions = 0;
+ ifp->if_ierrors = 0;
+ ifp->if_oerrors = 0;
+ ifp->if_ipackets = 0;
+ ifp->if_opackets = 0;
+ if_attach(ifp);
+#if NBPFILTER > 0
+ bpfattach(&tunctl[i].tun_bpf, ifp, DLT_NULL, sizeof(u_int));
+#endif
+ }
+}
+
+TEXT_SET(pseudo_set, tunattach);
+
+/*
+ * tunnel open - must be superuser & the device must be
+ * configured in
+ */
+int
+tunopen(dev, flag, mode, p)
+ dev_t dev;
+ int flag, mode;
+ struct proc *p;
+{
+ struct ifnet *ifp;
+ struct tun_softc *tp;
+ register int unit, error;
+
+
+ if (error = suser(p->p_ucred, &p->p_acflag))
+ return (error);
+
+ if ((unit = minor(dev)) >= NTUN)
+ return (ENXIO);
+ tp = &tunctl[unit];
+ if (tp->tun_flags & TUN_OPEN)
+ return ENXIO;
+ ifp = &tp->tun_if;
+ tp->tun_flags |= TUN_OPEN;
+
+ TUNDEBUG("%s%d: open\n", ifp->if_name, ifp->if_unit);
+ return (0);
+}
+
+/*
+ * tunclose - close the device - mark i/f down & delete
+ * routing info
+ */
+int
+tunclose(dev, flag)
+ dev_t dev;
+ int flag;
+{
+ register int unit = minor(dev), s;
+ struct tun_softc *tp = &tunctl[unit];
+ struct ifnet *ifp = &tp->tun_if;
+ struct mbuf *m;
+ int rcoll;
+
+ rcoll = tp->tun_flags & TUN_RCOLL;
+ tp->tun_flags &= ~TUN_OPEN;
+
+ /*
+ * junk all pending output
+ */
+ do {
+ s = splimp();
+ IF_DEQUEUE(&ifp->if_snd, m);
+ splx(s);
+ if (m)
+ m_freem(m);
+ } while (m);
+
+ if (ifp->if_flags & IFF_UP) {
+ s = splimp();
+ if_down(ifp);
+#ifdef notdef
+ if (ifp->if_flags & IFF_RUNNING) {
+ rtinit(ifp->if_addrlist, (int)RTM_DELETE,
+ tp->tun_flags & TUN_DSTADDR ? RTF_HOST : 0);
+ }
+#endif
+ splx(s);
+ }
+ tp->tun_pgrp = 0;
+#if BSD >= 199103
+ selwakeup(&tp->tun_sel);
+ /* XXX */
+ tp->tun_sel.si_pid = 0;
+#else
+ if (tp->tun_rsel)
+ selwakeup(tp->tun_rsel->p_pid, rcoll);
+ tp -> tun_rsel = tp -> tun_wsel = (struct proc *)0;
+#endif
+ TUNDEBUG ("%s%d: closed\n", ifp->if_name, ifp->if_unit);
+ return (0);
+}
+
+static int
+tuninit(unit)
+ int unit;
+{
+ struct tun_softc *tp = &tunctl[unit];
+ struct ifnet *ifp = &tp->tun_if;
+ register struct ifaddr *ifa;
+
+ TUNDEBUG("%s%d: tuninit\n", ifp->if_name, ifp->if_unit);
+
+ ifp->if_flags |= IFF_UP | IFF_RUNNING;
+
+ for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next) {
+ struct sockaddr_in *si;
+
+ si = (struct sockaddr_in *)ifa->ifa_addr;
+ if (si && si->sin_addr.s_addr)
+ tp->tun_flags |= TUN_IASET;
+
+ si = (struct sockaddr_in *)ifa->ifa_dstaddr;
+ if (si && si->sin_addr.s_addr)
+ tp->tun_flags |= TUN_DSTADDR;
+ }
+
+ return 0;
+}
+
+/*
+ * Process an ioctl request.
+ */
+int
+tunifioctl(ifp, cmd, data)
+ struct ifnet *ifp;
+ int cmd;
+ caddr_t data;
+{
+ struct tun_softc *tp = &tunctl[ifp->if_unit];
+ int error = 0, s;
+
+ s = splimp();
+ switch(cmd) {
+ case SIOCSIFADDR:
+ tuninit(ifp->if_unit);
+ break;
+ case SIOCSIFDSTADDR:
+ tp->tun_flags |= TUN_DSTADDR;
+ TUNDEBUG("%s%d: destination address set\n", ifp->if_name,
+ ifp->if_unit);
+ break;
+ default:
+ error = EINVAL;
+ }
+ splx(s);
+ return (error);
+}
+
+/*
+ * tunoutput - queue packets from higher level ready to put out.
+ */
+int
+tunoutput(ifp, m0, dst, rt)
+ struct ifnet *ifp;
+ struct mbuf *m0;
+ struct sockaddr *dst;
+ struct rtentry *rt;
+{
+ struct tun_softc *tp = &tunctl[ifp->if_unit];
+ struct proc *p;
+ int s;
+
+ TUNDEBUG ("%s%d: tunoutput\n", ifp->if_name, ifp->if_unit);
+
+ if ((tp->tun_flags & TUN_READY) != TUN_READY) {
+ TUNDEBUG ("%s%d: not ready 0%o\n", ifp->if_name,
+ ifp->if_unit, tp->tun_flags);
+ m_freem (m0);
+ return EHOSTDOWN;
+ }
+
+#if NBPFILTER > 0
+ if (tp->tun_bpf) {
+ /*
+ * We need to prepend the address family as
+ * a four byte field. Cons up a dummy header
+ * to pacify bpf. This is safe because bpf
+ * will only read from the mbuf (i.e., it won't
+ * try to free it or keep a pointer to it).
+ */
+ struct mbuf m;
+ u_int af = dst->sa_family;
+
+ m.m_next = m0;
+ m.m_len = 4;
+ m.m_data = (char *)&af;
+
+ bpf_mtap(tp->tun_bpf, &m);
+ }
+#endif
+
+ switch(dst->sa_family) {
+#ifdef INET
+ case AF_INET:
+ s = splimp();
+ if (IF_QFULL(&ifp->if_snd)) {
+ IF_DROP(&ifp->if_snd);
+ m_freem(m0);
+ splx(s);
+ ifp->if_collisions++;
+ return (ENOBUFS);
+ }
+ IF_ENQUEUE(&ifp->if_snd, m0);
+ splx(s);
+ ifp->if_opackets++;
+ break;
+#endif
+ default:
+ m_freem(m0);
+ return EAFNOSUPPORT;
+ }
+
+ if (tp->tun_flags & TUN_RWAIT) {
+ tp->tun_flags &= ~TUN_RWAIT;
+ wakeup((caddr_t)tp);
+ }
+ if (tp->tun_flags & TUN_ASYNC && tp->tun_pgrp) {
+ if (tp->tun_pgrp > 0)
+ gsignal(tp->tun_pgrp, SIGIO);
+ else if (p = pfind(-tp->tun_pgrp))
+ psignal(p, SIGIO);
+ }
+#if BSD >= 199103
+ selwakeup(&tp->tun_sel);
+ /* XXX */
+ tp->tun_sel.si_pid = 0;
+#else
+ if (tp->tun_rsel) {
+ selwakeup(tp->tun_rsel->p_pid, tp->tun_flags & TUN_RCOLL);
+ tp->tun_flags &= ~TUN_RCOLL;
+ tp->tun_rsel = (struct proc *)0;
+ }
+#endif
+ return 0;
+}
+
+/*
+ * the cdevsw interface is now pretty minimal.
+ */
+int
+tunioctl(dev, cmd, data, flag)
+ dev_t dev;
+ int cmd;
+ caddr_t data;
+ int flag;
+{
+ int unit = minor(dev), s;
+ struct tun_softc *tp = &tunctl[unit];
+ struct tuninfo *tunp;
+
+ switch (cmd) {
+ case TUNSIFINFO:
+ tunp = (struct tuninfo *)data;
+ tp->tun_if.if_mtu = tunp->tif_mtu;
+ tp->tun_if.if_type = tunp->tif_type;
+ tp->tun_if.if_baudrate = tunp->tif_baudrate;
+ break;
+ case TUNGIFINFO:
+ tunp = (struct tuninfo *)data;
+ tunp->tif_mtu = tp->tun_if.if_mtu;
+ tunp->tif_type = tp->tun_if.if_type;
+ tunp->tif_baudrate = tp->tun_if.if_baudrate;
+ break;
+ case TUNSDEBUG:
+ tundebug = *(int *)data;
+ break;
+ case TUNGDEBUG:
+ *(int *)data = tundebug;
+ break;
+ case FIONBIO:
+ if (*(int *)data)
+ tp->tun_flags |= TUN_NBIO;
+ else
+ tp->tun_flags &= ~TUN_NBIO;
+ break;
+ case FIOASYNC:
+ if (*(int *)data)
+ tp->tun_flags |= TUN_ASYNC;
+ else
+ tp->tun_flags &= ~TUN_ASYNC;
+ break;
+ case FIONREAD:
+ s = splimp();
+ if (tp->tun_if.if_snd.ifq_head)
+ *(int *)data = tp->tun_if.if_snd.ifq_head->m_len;
+ else
+ *(int *)data = 0;
+ splx(s);
+ break;
+ case TIOCSPGRP:
+ tp->tun_pgrp = *(int *)data;
+ break;
+ case TIOCGPGRP:
+ *(int *)data = tp->tun_pgrp;
+ break;
+ default:
+ return (ENOTTY);
+ }
+ return (0);
+}
+
+/*
+ * The cdevsw read interface - reads a packet at a time, or at
+ * least as much of a packet as can be read.
+ */
+int
+tunread(dev, uio)
+ dev_t dev;
+ struct uio *uio;
+{
+ int unit = minor(dev);
+ struct tun_softc *tp = &tunctl[unit];
+ struct ifnet *ifp = &tp->tun_if;
+ struct mbuf *m, *m0;
+ int error=0, len, s;
+
+ TUNDEBUG ("%s%d: read\n", ifp->if_name, ifp->if_unit);
+ if ((tp->tun_flags & TUN_READY) != TUN_READY) {
+ TUNDEBUG ("%s%d: not ready 0%o\n", ifp->if_name,
+ ifp->if_unit, tp->tun_flags);
+ return EHOSTDOWN;
+ }
+
+ tp->tun_flags &= ~TUN_RWAIT;
+
+ s = splimp();
+ do {
+ IF_DEQUEUE(&ifp->if_snd, m0);
+ if (m0 == 0) {
+ if (tp->tun_flags & TUN_NBIO) {
+ splx(s);
+ return EWOULDBLOCK;
+ }
+ tp->tun_flags |= TUN_RWAIT;
+ tsleep((caddr_t)tp, PZERO + 1, "tunread", 0);
+ }
+ } while (m0 == 0);
+ splx(s);
+
+ while (m0 && uio->uio_resid > 0 && error == 0) {
+ len = MIN(uio->uio_resid, m0->m_len);
+ if (len == 0)
+ break;
+ error = uiomove(mtod(m0, caddr_t), len, uio);
+ MFREE(m0, m);
+ m0 = m;
+ }
+
+ if (m0) {
+ TUNDEBUG("Dropping mbuf\n");
+ m_freem(m0);
+ }
+ return error;
+}
+
+/*
+ * the cdevsw write interface - an atomic write is a packet - or else!
+ */
+int
+tunwrite(dev, uio)
+ dev_t dev;
+ struct uio *uio;
+{
+ int unit = minor (dev);
+ struct ifnet *ifp = &tunctl[unit].tun_if;
+ struct mbuf *top, **mp, *m;
+ int error=0, s, tlen, mlen;
+
+ TUNDEBUG("%s%d: tunwrite\n", ifp->if_name, ifp->if_unit);
+
+ if (uio->uio_resid < 0 || uio->uio_resid > TUNMTU) {
+ TUNDEBUG("%s%d: len=%d!\n", ifp->if_name, ifp->if_unit,
+ uio->uio_resid);
+ return EIO;
+ }
+ tlen = uio->uio_resid;
+
+ /* get a header mbuf */
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == NULL)
+ return ENOBUFS;
+ mlen = MHLEN;
+
+ top = 0;
+ mp = &top;
+ while (error == 0 && uio->uio_resid > 0) {
+ m->m_len = MIN (mlen, uio->uio_resid);
+ error = uiomove(mtod (m, caddr_t), m->m_len, uio);
+ *mp = m;
+ mp = &m->m_next;
+ if (uio->uio_resid > 0) {
+ MGET (m, M_DONTWAIT, MT_DATA);
+ if (m == 0) {
+ error = ENOBUFS;
+ break;
+ }
+ mlen = MLEN;
+ }
+ }
+ if (error) {
+ if (top)
+ m_freem (top);
+ return error;
+ }
+
+ top->m_pkthdr.len = tlen;
+ top->m_pkthdr.rcvif = ifp;
+
+#if NBPFILTER > 0
+ if (tunctl[unit].tun_bpf) {
+ /*
+ * We need to prepend the address family as
+ * a four byte field. Cons up a dummy header
+ * to pacify bpf. This is safe because bpf
+ * will only read from the mbuf (i.e., it won't
+ * try to free it or keep a pointer to it).
+ */
+ struct mbuf m;
+ u_int af = AF_INET;
+
+ m.m_next = top;
+ m.m_len = 4;
+ m.m_data = (char *)&af;
+
+ bpf_mtap(tunctl[unit].tun_bpf, &m);
+ }
+#endif
+
+ s = splimp();
+ if (IF_QFULL (&ipintrq)) {
+ IF_DROP(&ipintrq);
+ splx(s);
+ ifp->if_collisions++;
+ m_freem(top);
+ return ENOBUFS;
+ }
+ IF_ENQUEUE(&ipintrq, top);
+ splx(s);
+ ifp->if_ipackets++;
+ schednetisr(NETISR_IP);
+ return error;
+}
+
+/*
+ * The new select interface passes down the proc pointer; the old select
+ * stubs had to grab it out of the user struct. This glue allows either case.
+ */
+#if BSD >= 199103
+#define tun_select tunselect
+#else
+int
+tunselect(dev, rw)
+ register dev_t dev;
+ int rw;
+{
+ return (tun_select(dev, rw, u.u_procp));
+}
+#endif
+
+/*
+ * tunselect - the select interface, this is only useful on reads
+ * really. The write detect always returns true, write never blocks
+ * anyway, it either accepts the packet or drops it.
+ */
+int
+tun_select(dev, rw, p)
+ dev_t dev;
+ int rw;
+ struct proc *p;
+{
+ int unit = minor(dev), s;
+ struct tun_softc *tp = &tunctl[unit];
+ struct ifnet *ifp = &tp->tun_if;
+
+ s = splimp();
+ TUNDEBUG("%s%d: tunselect\n", ifp->if_name, ifp->if_unit);
+#if BSD >= 199103
+ if (rw != FREAD) {
+ splx(s);
+ return 1;
+ }
+ if (ifp->if_snd.ifq_len > 0) {
+ splx(s);
+ TUNDEBUG("%s%d: tunselect q=%d\n", ifp->if_name,
+ ifp->if_unit, ifp->if_snd.ifq_len);
+ return 1;
+ }
+ selrecord(p, &tp->tun_sel);
+#else
+ switch (rw) {
+ case FREAD:
+ if (ifp->if_snd.ifq_len > 0) {
+ splx(s);
+ TUNDEBUG("%s%d: tunselect q=%d\n", ifp->if_name,
+ ifp->if_unit, ifp->if_snd.ifq_len);
+ return 1;
+ }
+ if (tp->tun_rsel && tp->tun_rsel == p)
+ tp->tun_flags |= TUN_RCOLL;
+ else
+ tp->tun_rsel = p;
+ break;
+ case FWRITE:
+ splx(s);
+ return 1;
+ }
+#endif
+ splx(s);
+ TUNDEBUG("%s%d: tunselect waiting\n", ifp->if_name, ifp->if_unit);
+ return 0;
+}
+
+#endif /* NTUN */
OpenPOWER on IntegriCloud