diff options
author | amurai <amurai@FreeBSD.org> | 1995-01-31 06:15:49 +0000 |
---|---|---|
committer | amurai <amurai@FreeBSD.org> | 1995-01-31 06:15:49 +0000 |
commit | 74b5a342fdaa61a48241c521c2b54ab517204f92 (patch) | |
tree | 261e7c18326fb1524bcdefd1440fb99d77d1cadd /sys/net/if_tun.c | |
download | FreeBSD-src-74b5a342fdaa61a48241c521c2b54ab517204f92.zip FreeBSD-src-74b5a342fdaa61a48241c521c2b54ab517204f92.tar.gz |
Diffstat (limited to 'sys/net/if_tun.c')
-rw-r--r-- | sys/net/if_tun.c | 615 |
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 *)⁡ + + 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 = ⊤ + 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 *)⁡ + + 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 */ |