Subject: kern/2411: [dM] New tun driver
To: None <gnats-bugs@NetBSD.ORG>
From: der Mouse <mouse@Collatz.McRCIM.McGill.EDU>
List: netbsd-bugs
Date: 05/14/1996 13:23:25
>Number:         2411
>Category:       kern
>Synopsis:       [dM] New tun driver
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    kern-bug-people (Kernel Bug People)
>State:          open
>Class:          change-request
>Submitter-Id:   net
>Arrival-Date:   Tue May 14 13:50:01 1996
>Last-Modified:
>Originator:     der Mouse
>Organization:
	Dis-
>Release:        -current
>Environment:
	Any; I use a SPARC IPC and a Sun-3/150
>Description:
	The tun driver needs lotsa stuff it doesn't have.  It needs the
	capability to be a broadcast interface, it needs to be able to
	pass up address families and packet-sent-to addresses to
	user-land, there are other things that would be convenient even
	though not really necessary....
>How-To-Repeat:
	N/A
>Fix:
	Here's a new tun driver.  (Diffs from the previous one are
	bigger than simply quoting the new driver....)  I'm also
	including a start on a manpage, but I ran into problems with
	the mdoc macros, and my query to current-users produced no
	help, so I'm not going to hold off sending this in for people
	to take potshots at any longer in the hope of getting the
	manpage working.

#! /bin/sh
#
# Shar: Shell Archiver
#
# This archive created Tue May 14 12:48:41 1996
# Run this through sh to create:
#	tun.4
#	if_tun.h
#	if_tun.c
echo x - tun.4 \(11622 characters\)
sed 's/^X//' > tun.4 << \EOF
X.Dd March 10, 1996
X.Dt TUN 4
X.Os NetBSD 1.1
X.Sh NAME
X.Nm tun
X.\" Why doesn't this work right?  mdoc(7) nor mdoc.samples(7) lists
X.\" .Nd, except for a passing reference in the latter.
X.Nd Dq tunnel No software network interface
X.Sh SYNOPSIS
X.\" want to write
X.\" .Sy pseudo-device
X.\" .Nm tun
X.\" .Op Ar count
X.\" so that .Nm is just "tun", but if we do that we get a line break
X.\" after "pseudo-device", ick.
X.Nm pseudo-device tun
X.Op Ar count
X.Sh DESCRIPTION
XThe
X.Nm tun
Xinterface is a software loopback mechanism that can be loosely
Xdescribed as the network interface analog of the
X.Xr pty 4 ,
Xthat is,
X.Nm tun
Xdoes for network interfaces what the
X.Nm pty
Xdriver does for terminals.
X.Pp
XThe
X.Nm tun
Xdriver, like the
X.Nm pty
Xdriver, provides two interfaces: an interface like the usual facility
Xit is simulating (a network interface in the case of
X.Nm tun ,
Xor a terinal for
X.Nm pty ) ,
Xand a character-special device
X.Dq control
Xinterface.
X.Pp
XThe network interfaces are named
X.Sy tun Ns Ar 0 ,
X.Sy tun Ns Ar 1 ,
Xetc, as many in all as the
X.Ar count
Xfigure given on the
X.Sy pseudo-device
Xline.  Each one supports the usual network-interface
X.Xr ioctl 2 Ns s ,
Xsuch as
X.Dv SIOCSIFADDR
Xand
X.Dv SIOCSIFNETMASK ,
Xand thus can be used with
X.Xr ifconfig 8
Xlike any other interface.  At boot time, they are
X.Dv POINTOPOINT
Xinterfaces, but this can be changed; see the description of the control
Xdevice, below.  When the system chooses to transmit a packet on the
Xnetwork interface, the packet can be read from the control device (it
Xappears as
X.Dq input
Xthere); writing a packet to the control device generates an input
Xpacket on the network interface, as if the (nonexistent) hardware had
Xjust received it.
X.Pp
XThere are two control interfaces.  The
X.Em data
Xinterface, normally
X.Pa /dev/tun Ns Sy N ,
Xis exclusive-open (it cannot be opened if it is already open), is
Xnormally restricted to the super-user, and can
X.Dq transmit
Xand
X.Dq receive
Xpackets.  The
X.Em control
Xinterface, normally
X.Pa /dev/tunc Ns Sy N ,
Xcannot send and receive packets, but can be opened by many processes at
Xonce; it is intended for status queries and changes (many of which can
Xalso be implemented with
X.Fn ioctl
Xcalls on the data interface).  There are a number of status bits that
Xcan be set or cleared via the control interfaces; they are mentioned
Xbelow where applicable, and they are all summarized in the discussions
Xof the control interfaces.
X.\" Why isn't .Ss documented in mdoc(7) and mdoc.samples(7)?
X.Ss The data interface
XThe data interface supports
X.Xr read 2 ,
X.Xr write 2 ,
Xand
X.Xr ioctl 2
Xcalls to, respectively, collect
X.Dq output
Xpackets, generate
X.Dq input
Xpackets, and perform control functions.  As mentioned above, this
Xinterface is exclusive-open; if the
X.Dv SUONLY
Xbit is set (which it is by default), it cannot be opened at all except
Xby the super-user.  By default, a
X.Fn read
Xcall will return an error
X.Pf ( Er EHOSTDOWN )
Xif the interface is not
X.Dq ready
X(which means that the control device is open and the interface's
Xaddress has been set); if preferred, the
X.Dv RRWAIT
Xbit can be set, in which case a
X.Fn read
Xcall will block (even if non-blocking I/O has been enabled) until the
Xinterface is ready.  Once the interface is ready,
X.Fn read
Xwill return a packet if one is available; if not, it will either block
Xuntil one is or return
X.Er EWOULDBLOCK ,
Xdepending on whether non-blocking I/O has been enabled.  If the packet
Xis longer than is allowed for in the buffer passed to
X.Fn read ,
Xthe extra data will be silently dropped.
X.Pp
XThe first byte of data will always be the address family (eg,
X.Dv AF_INET )
Xof the packet.  By default, the packet data follows immediately, but if
Xthe
X.Dv PREPADDR
Xbit is set, the address to which the packet is to be sent is placed
Xafter the address family byte and before the packet data.  The size and
Xlayout of the address depends on the address family; for
X.Dv AF_INET ,
Xfor example, it is a
X.Va struct in_addr .
XA
X.Xr write 2
Xcall passes a packet in to be
X.Dq received
Xon the pseudo-interface.  Each
X.Fn write
Xcall supplies exactly one packet; the packet length is taken from the
Xamount of data provided to
X.Fn write .
XThe first byte must be the address family of the packet, much as in
Xpackets returned by
X.Fn read ;
Xthe packet data always follows immediately.
XA large number of
X.Xr ioctl 2
Xcalls are also supported.  They are defined in
X.Aq Pa net/if_tun.h Ns .
X.Bl -tag -offset indent
X.It Dv TUNSDEBUG
XThe argument should be a pointer to an
X.Va int ;
Xthis sets the internal debugging variable to that value.  What, if
Xanything, this variable controls is not documented here; see the source
Xcode.
X.It Dv TUNGDEBUG
XThe argument should be a pointer to an
X.Va int ;
Xthis stores the internal debugging variable's value into it.
X.It Dv TUNSMODE
XThe argument should be a pointer to an
X.Va int ;
Xits value must be
X.Dv IFF_POINTOPOINT
Xor
X.Dv IFF_BROADCAST .
XThe type of the corresponding
X.Em tun Ns Sy n
Xinterface is set to the supplied type.  If the value is anything else,
Xan
X.Er EINVAL
Xerror occurs.  The interface must be down at the time; if it is up, an
X.Er EBUSY
Xerror occurs.
X.\" X .It Dv TUNSFLAG
X.\" X The interface's flag bits are set as specified in the
X.\" X .Va int
X.\" X argument.  Only some of the bits can be modified; the rest are
X.\" X read-only.  The bits are defined in
X.\" X .Aq Pa net/if_tun.h
X.\" X with a
X.\" X .Dv TUN_
X.\" X prefix; for example, the bit called
X.\" X .Dv RRWAIT
X.\" X in this document would be referred to in source code as
X.\" X .Dv TUN_RRWAIT .
X.\" X The bits are:
X.\" X .\" Why isn't the way to create a table like this documented in mdoc(7)
X.\" X .\" or mdoc.samples(7)?!
X.\" X .Bl -column "TUN_PREPADDR" "RO/RW" -compact -indent-two
X.\" X .It Name Ta RO/RW Ta Meaning
X.\" X .It Dv TUN_OPEN Ta RO Ta "Data control device is open."
X.\" X .It Dv TUN_INITED Ta RO Ta "Initialized."
X.\" X .It Dv TUN_RCOLL Ta RO Ta "Select-for-read collision."
X.\" X .It Dv TUN_IASET Ta RO Ta "Address has been set."
X.\" X .It Dv TUN_DSTADDR Ta RO Ta "Destination address has been set."
X.\" X .It Dv TUN_RWAIT Ta RO Ta "A process is blocked in Fn read Ns ."
X.\" X .It Dv TUN_ASYNC Ta RO Ta "Generate Dv SIGIO No for readers."
X.\" X .It Dv TUN_NBIO Ta RO Ta "Non-blocking I/O for reads."
X.\" X .It Dv TUN_BRDADDR Ta RO Ta "Broadcast address has been set."
X.\" X .It Dv TUN_PREPADDR Ta RW Ta "Prepend sent-to address for reads."
X.\" X .It Dv TUN_STAYUP Ta RW Ta "Don't take interface down on close."
X.\" X .It Dv TUN_SUONLY Ta RW Ta "Data control device is super-user only."
X.\" X .It Dv TUN_RRWAIT Ta RW Ta "Wait for ready when reading."
X.\" X .El
X.\" X .It Dv TUNGFLAG
X.\" X The interface's flag bits are fetched into the argument
X.\" X .Va int .
X.\" X The flags and their meanings are as for
X.\" X .Dv TUNSFLAG .
X.\" X .It Dv FIONBIO
X.\" X Turn non-blocking I/O for reads off or on, according as the argument
X.\" X .Va int Ns 's
X.\" X value is or isn't zero.  (Writes are always nonblocking.)
X.\" X .It Dv FIOASYNC
X.\" X Turn asynchronous I/O for reads (ie, generation of
X.\" X .Dv SIGIO
X.\" X when data is available to be read) off or on, according as the argument
X.\" X .Va int Ns 's
X.\" X value is or isn't zero.
X.\" X .It Dv FIONREAD
X.\" X If any packets are queued to be read, store the size of the first one
X.\" X into the argument
X.\" X .Va int ;
X.\" X otherwise, store zero.
X.\" X .It Dv TIOCSPGRP
X.\" X Set the process group to receive
X.\" X .Dv SIGIO
X.\" X signals, when asynchronous I/O is enabled, to the argument
X.\" X .Va int
X.\" X value.
X.\" X .It Dv TIOCGPGRP
X.\" X Retrieve the process group value for
X.\" X .Dv SIGIO
X.\" X signals into the argument
X.\" X .Va int
X.\" X value.
X.El
XThe data control device also supports
X.Xr select 2
Xfor read; selecting for write is pointless, and always succeeds, since
Xwrites are always nonblocking (if the packet cannot be accepted for a
Xtransient reason (eg, no buffer space available), it is silently
Xdropped; if the reason is not transient (eg, packet too large), an
Xerror is returned).
X.Pp
XOn the last close of the data device, by default, the interface is
Xbrought down (as if with
X.Dq ifconfig tun Ns Sy n down ) ;
Xif the
X.Dv STAYUP
Xbit is set, this is not done.  In either case, all queued packets are
Xthrown away.  (If the interface is up when the data device is not open,
Xeither because of
X.Dv STAYUP
Xor because it was explicitly brought up, output packets are always
Xthrown away rather than letting them pile up.)
X.Ss The control interface
XThe alternative control interface is a text-based interface designed
Xfor shell-script or human use; it allows control of many of the things
Xthat can be done with
X.Fn ioctl
Xcalls on the data interface, and a few more as well.
X.Pp
X.Fn read Ns s
Xon the control interface always return a single line of text (or just
Xthe beginning of the line, if the buffer passed to
X.Xr read 2
Xwas too small to take the whole line).  The line contains items in the
Xgeneral format
X.Do
X.Li item=value
X.Dc ,
Xwhere
X.Li item
Xis a keyword and
X.Li value
Xis a value appropriate to the keyword.  This line is intended for human
Xuse; programs should use the
X.Fn ioctl
Xinterface.  Here is an actual example (broken because of width
Xrestrictions):
X.Bd -literal
Xunit=0 flags=(open,inited,!rcoll,iaset,!dstaddr,!rwait,!async,
X!nbio,!brdaddr,prepaddr,stayup,suonly,rrwait) type=broadcast
Xmtu=1500 coll=0 ipkts=0/0 opkts=0/0 pgrp=0
X.Ed
X.Pp
XNote that the current file offset is ignored for reads, so using a tool like
X.Xr cat 1
Xwill result in infinite output.  Use something more like
X.Dq head\ \&-1
Xfor command-line use.  It is possible to
X.Xr select 2
Xfor reading on this device, which will indicate that the device is
Xreadable whenever the state is changed.
X.Pp
XWrites to the control interface are interpreted as modifications to the
Xstate.  Each
X.Fn write
Xcall is treated separately.  The data written is broken at whitespace
X(blanks, tabs, newlines); each resulting fragment has its first
Xcharacter examined.  If this character is a
X.Ql \&+
Xor
X.Ql \&\- ,
Xthe rest of the fragment is taken as a flag name, and the flag is
Xturned on (for
X.Ql \&+ )
Xor off (for
X.Ql \&\- ) .
X(Flag names are as generated on reads; they are the same as the
X.Dv TUN_ Ns Em xxx
Xconstants, with the leading
X.Dv TUN_
Xremoved and the rest lowercased.)  If the first character is
X.Ql t ,
Xthe second character must be
X.Ql b
Xor
X.Ql p ,
Xand the interface type is set to
X.Dv IFF_BROADCAST
Xor
X.Dv IFF_POINTOPOINT ,
Xrespectively.  If the first character is
X.Ql g
Xor
X.Ql m ,
Xthe rest of the fragment is taken as a number in decimal (possibly with
Xa leading \&\- sign) and the result is taken as a new process group,
Xfor
X.Ql g
Xor MTU, for
X.Ql m .
X(The MTU must not be less than 1; attempts to set it so return
X.Er EIO . )
X.Pp
XThis interface is useful for command-line reconfiguration, such as
Xsetting the interface type at boot time, with 
X.Sh SEE ALSO
X.Xr intro 4 ,
X.Xr inet 4
X.Sh BUGS
XCurrently is IP-only.
X.Pp
XThe
X.Dv SUONLY
Xbit is a botch, especially since the control interface, which is never
Xrestricted by the kernel, can change it.  Access control really should
Xbe handled by the permission bits on the
X.Pa /dev
Xentries for the data and control devices; this bit is a historical
Xartifact.
X.Pp
XThe process-group values for
X.Dv SIGIO
Xsignals should be checked; as it stands, the driver can be used (by
Xanyone who can open the control or data device) to send any desired
Xsignal to an arbitrary process or process group.  (Until this is fixed,
Xyou should be careful to set the permisison bits to allow only root to
Xopen the control device, and either do the same for the data device or
Xleave the
X.Dv SUONLY
Xbit set.)
EOF
if test 11622 -ne "`wc -c tun.4`"
then
echo shar: error transmitting tun.4 \(should have been 11622 characters\)
fi
echo x - if_tun.h \(1613 characters\)
sed 's/^X//' > if_tun.h << \EOF
X/*	$NetBSD: if_tun.h,v 1.5 1994/06/29 06:36:27 cgd Exp $	*/
X
X/*
X * Copyright (c) 1988, Julian Onions <jpo@cs.nott.ac.uk>
X * Nottingham University 1987.
X *
X * This source may be freely distributed, however I would be interested
X * in any changes that are made.
X *
X * This driver takes packets off the IP i/f and hands them up to a
X * user process to have it's wicked way with. This driver has it's
X * roots in a similar driver written by Phil Cockcroft (formerly) at
X * UCL. This driver is based much more on read/write/select mode of
X * operation though.
X * 
X * : $Header: if_tnreg.h,v 1.1.2.1 1992/07/16 22:39:16 friedl Exp
X */
X
X#ifndef _NET_IF_TUN_H_
X#define _NET_IF_TUN_H_
X
X#include <sys/ioccom.h>
X
X#define TUN_OPEN     0x0001
X#define TUN_INITED   0x0002
X#define TUN_RCOLL    0x0004
X#define TUN_IASET    0x0008
X#define TUN_DSTADDR  0x0010
X#define TUN_RWAIT    0x0040
X#define TUN_ASYNC    0x0080
X#define TUN_NBIO     0x0100
X#define TUN_BRDADDR  0x0200
X#define TUN_PREPADDR 0x0400
X#define TUN_STAYUP   0x0800
X#define TUN_SUONLY   0x1000
X#define TUN_RRWAIT   0x2000
X
X#define TUN_READY (TUN_OPEN | TUN_INITED | TUN_IASET)
X#define TUN_UCHG (TUN_PREPADDR | TUN_STAYUP | TUN_SUONLY | TUN_RRWAIT)
X
X#define TUNU_UNIT 0x7f
X#define TUNU_TYPE 0x80
X#define TUNU_T_DATA 0x00
X#define TUNU_T_CTL  0x80
X
X/* Maximum packet size */
X#define TUNMTU 1500
X
X/* ioctls for get/set debug */
X#define TUNSDEBUG _IOW('t',90,int)
X#define TUNGDEBUG _IOR('t',89,int)
X
X/* ioctls for changing interface modes */
X#define TUNSMODE _IOW('t',88,int)
X#define TUNSFLAG _IOW('t',87,int)
X#define TUNGFLAG _IOR('t',86,int)
X
X#endif /* !_NET_IF_TUN_H_ */
EOF
if test 1613 -ne "`wc -c if_tun.h`"
then
echo shar: error transmitting if_tun.h \(should have been 1613 characters\)
fi
echo x - if_tun.c \(20460 characters\)
sed 's/^X//' > if_tun.c << \EOF
X/*	$NetBSD: if_tun.c,v 1.19 1995/12/13 23:47:40 pk Exp $	*/
X
X/*
X * Copyright (c) 1988, Julian Onions <jpo@cs.nott.ac.uk>
X * Nottingham University 1987.
X *
X * This source may be freely distributed, however I would be interested
X * in any changes that are made.
X *
X * This driver takes packets off the IP i/f and hands them up to a
X * user process to have its wicked way with. This driver has its
X * roots in a similar driver written by Phil Cockcroft (formerly) at
X * UCL. This driver is based much more on read/write/select mode of
X * operation though.
X */
X
X#include "tun.h"
X#if NTUN > 0
X
X#include <sys/param.h>
X#include <sys/proc.h>
X#include <sys/systm.h>
X#include <sys/mbuf.h>
X#include <sys/buf.h>
X#include <sys/protosw.h>
X#include <sys/socket.h>
X#include <sys/ioctl.h>
X#include <sys/errno.h>
X#include <sys/syslog.h>
X#include <sys/select.h>
X#include <sys/file.h>
X#include <sys/signalvar.h>
X
X#include <machine/cpu.h>
X
X#include <net/if.h>
X#include <net/netisr.h>
X#include <net/route.h>
X#include <net/if_types.h>
X
X#ifdef INET
X#include <netinet/in.h>
X#include <netinet/in_systm.h>
X#include <netinet/in_var.h>
X#include <netinet/ip.h>
X#include <netinet/if_ether.h>
X#endif
X
X#ifdef NS
X#include <netns/ns.h>
X#include <netns/ns_if.h>
X#endif
X
X#ifdef ISO
X#include <netiso/iso.h>
X#include <netiso/iso_var.h>
X#endif
X
X#include "bpfilter.h"
X#if NBPFILTER > 0
X#include <sys/time.h>
X#include <net/bpf.h>
X#endif
X
X#include <net/if_tun.h>
X
Xstruct tun_softc {
X	u_short	tun_flags;		/* misc flags */
X	struct	ifnet tun_if;		/* the interface */
X	int	tun_pgrp;		/* the process group - if any */
X	struct	selinfo	tun_rsel_d;	/* read select, data */
X	struct	selinfo	tun_rsel_c;	/* read select, ctl */
X#if NBPFILTER > 0
X	caddr_t		tun_bpf;
X#endif
X};
X
Xstatic struct {
X	 const char *name;
X	 unsigned int bit;
X	 } flagbits[] = { { "open", TUN_OPEN },
X			  { "inited", TUN_INITED },
X			  { "rcoll", TUN_RCOLL },
X			  { "iaset", TUN_IASET },
X			  { "dstaddr", TUN_DSTADDR },
X			  { "rwait", TUN_RWAIT },
X			  { "async", TUN_ASYNC },
X			  { "nbio", TUN_NBIO },
X			  { "brdaddr", TUN_BRDADDR },
X			  { "prepaddr", TUN_PREPADDR },
X			  { "stayup", TUN_STAYUP },
X			  { "suonly", TUN_SUONLY },
X			  { "rrwait", TUN_RRWAIT },
X			  { 0 } };
X
X#define TUNDEBUG	if (tundebug) printf
Xint	tundebug = 0;
X
Xstruct tun_softc tunctl[NTUN];
Xextern int ifqmaxlen;
X
Xint	tunopen __P((dev_t, int, int, struct proc *));
Xint	tunclose __P((dev_t, int));
Xint	tunoutput __P((struct ifnet *, struct mbuf *, struct sockaddr *,
X	    struct rtentry *rt));
Xint	tunread __P((dev_t, struct uio *));
Xint	tunwrite __P((dev_t, struct uio *));
Xint	tunioctl __P((dev_t, u_long, caddr_t, int, struct proc *));
Xint	tunnioctl __P((struct ifnet *, u_long, caddr_t));
Xint	tunselect __P((dev_t, int));
Xvoid	tunattach __P((int));
X
Xstatic int tuninit __P((int));
X
Xstatic __inline__ int spacep(char) __attribute__ ((const));
Xstatic __inline__ int spacep(char c)
X{
X switch (c)
X  { case ' ': case '\t': case '\n': return(1);
X  }
X return(0);
X}
X
Xstatic __inline__ int digitp(char) __attribute__ ((const));
Xstatic __inline__ int digitp(char c)
X{
X switch (c)
X  { case '0': case '1': case '2': case '3': case '4':
X    case '5': case '6': case '7': case '8': case '9':
X       return(1);
X  }
X return(0);
X}
X
Xvoid
Xtunattach(unused)
X	int unused;
X{
X	register int i;
X	struct ifnet *ifp;
X
X	for (i = 0; i < NTUN; i++) {
X		tunctl[i].tun_flags = TUN_INITED | TUN_SUONLY;
X
X		ifp = &tunctl[i].tun_if;
X		ifp->if_type = IFT_OTHER;
X		ifp->if_unit = i;
X		ifp->if_name = "tun";
X		ifp->if_mtu = TUNMTU;
X		ifp->if_ioctl = tunnioctl;
X		ifp->if_output = tunoutput;
X		ifp->if_flags = IFF_POINTOPOINT;
X#define X 0
X#if defined(INET) && (X < 4) /* XXX 4 should be sizeof(struct in_addr) */
X#undef X
X#define X 4
X#endif
X		ifp->if_hdrlen = X + sizeof(int);
X#undef X
X		ifp->if_snd.ifq_maxlen = ifqmaxlen;
X		ifp->if_collisions = 0;
X		ifp->if_ierrors = 0;
X		ifp->if_oerrors = 0;
X		ifp->if_ipackets = 0;
X		ifp->if_opackets = 0;
X		if_attach(ifp);
X#if NBPFILTER > 0
X		bpfattach(&tunctl[i].tun_bpf, ifp, DLT_NULL, sizeof(u_int32_t));
X#endif
X	}
X}
X
X/*
X * open - if opening the real device, enforce exclusive-open and
X *  check the root-only bit.
X */
Xint
Xtunopen(dev,flag,mode,p)
Xdev_t dev;
Xint flag;
Xint mode;
Xstruct proc *p;
X{
X struct ifnet *ifp;
X struct tun_softc *tp;
X int unit;
X int error;
X
X unit = minor(dev);
X if ((unit & TUNU_UNIT) >= NTUN) return(ENXIO);
X tp = &tunctl[unit&TUNU_UNIT];
X switch (unit & TUNU_TYPE)
X  { case TUNU_T_DATA:
X       if (tp->tun_flags & TUN_SUONLY)
X	{ error = suser(p->p_ucred,&p->p_acflag);
X	  if (error) return(error);
X	}
X       if (tp->tun_flags & TUN_OPEN) return(EBUSY);
X       ifp = &tp->tun_if;
X       tp->tun_flags |= TUN_OPEN;
X       selwakeup(&tp->tun_rsel_c);
X       TUNDEBUG("%s%d: open\n",ifp->if_name,ifp->if_unit);
X       break;
X    case TUNU_T_CTL:
X       break;
X    default:
X       return(ENXIO);
X       break;
X  }
X return(0);
X}
X
X/*
X * close - close the device; if closing the real device, flush pending
X *  output and (unless set STAYUP) bring down the interface.
X */
Xint
Xtunclose(dev,flag)
Xdev_t dev;
Xint flag;
X{
X int unit;
X int s;
X struct tun_softc *tp;
X struct ifnet *ifp;
X struct mbuf *m;
X
X unit = minor(dev);
X tp = &tunctl[unit&TUNU_UNIT];
X ifp = &tp->tun_if;
X
X switch (unit & TUNU_TYPE)
X  { case TUNU_T_DATA:
X       tp->tun_flags &= ~TUN_OPEN;
X       do
X	{ s = splimp();
X	  IF_DEQUEUE(&ifp->if_snd,m);
X	  splx(s);
X	  if (m) m_freem(m);
X	} while(m);
X       if ((ifp->if_flags & IFF_UP) && !(tp->tun_flags & TUN_STAYUP))
X	{ s = splimp();
X	  if_down(ifp);
X	  if (ifp->if_flags & IFF_RUNNING)
X	   { struct ifaddr *ifa;
X	     for (ifa=ifp->if_addrlist.tqh_first;ifa;ifa=ifa->ifa_list.tqe_next)
X	      { if (ifa->ifa_addr->sa_family == AF_INET)
X		 { rtinit(ifa,RTM_DELETE,(tp->tun_flags&TUN_DSTADDR)?RTF_HOST:0);
X		 }
X	      }
X	   }
X	  splx(s);
X	}
X       tp->tun_pgrp = 0;
X       selwakeup(&tp->tun_rsel_d);
X       selwakeup(&tp->tun_rsel_c);
X       TUNDEBUG("%s%d: closed\n",ifp->if_name,ifp->if_unit);
X       break;
X    case TUNU_T_CTL:
X       break;
X    default:
X       panic("tunclose");
X       break;
X  }
X return(0);
X}
X
Xstatic int
Xtuninit(unit)
X	int	unit;
X{
X	struct tun_softc *tp = &tunctl[unit];
X	struct ifnet	*ifp = &tp->tun_if;
X	register struct ifaddr *ifa;
X
X	TUNDEBUG("%s%d: tuninit\n", ifp->if_name, ifp->if_unit);
X
X	ifp->if_flags |= IFF_UP | IFF_RUNNING;
X
X	tp->tun_flags &= ~(TUN_IASET|TUN_DSTADDR|TUN_BRDADDR);
X	for (ifa = ifp->if_addrlist.tqh_first; ifa != 0;
X	    ifa = ifa->ifa_list.tqe_next) {
X		if (ifa->ifa_addr->sa_family == AF_INET) {
X			struct sockaddr_in *sin;
X
X			sin = satosin(ifa->ifa_addr);
X			if (sin && sin->sin_addr.s_addr)
X				tp->tun_flags |= TUN_IASET;
X
X			if (ifp->if_flags & IFF_POINTOPOINT) {
X				sin = satosin(ifa->ifa_dstaddr);
X				if (sin && sin->sin_addr.s_addr)
X					tp->tun_flags |= TUN_DSTADDR;
X			} else
X				tp->tun_flags &= ~TUN_DSTADDR;
X
X			if (ifp->if_flags & IFF_BROADCAST) {
X				sin = satosin(ifa->ifa_broadaddr);
X				if (sin && sin->sin_addr.s_addr)
X					tp->tun_flags |= TUN_BRDADDR;
X			} else
X				tp->tun_flags &= ~TUN_BRDADDR;
X		}
X	}
X
X	selwakeup(&tp->tun_rsel_c);
X
X	return 0;
X}
X
X/*
X * Process a network-interface ioctl request.
X */
Xint
Xtunnioctl(ifp, cmd, data)
X	struct ifnet *ifp;
X	u_long	cmd;
X	caddr_t	data;
X{
X	int		error = 0, s;
X
X	s = splimp();
X	switch(cmd) {
X	case SIOCSIFADDR:
X		tuninit(ifp->if_unit);
X		TUNDEBUG("%s%d: address set\n",
X			 ifp->if_name, ifp->if_unit);
X		break;
X	case SIOCSIFDSTADDR:
X		tuninit(ifp->if_unit);
X		TUNDEBUG("%s%d: destination address set\n",
X			 ifp->if_name, ifp->if_unit);
X		break;
X	case SIOCSIFBRDADDR:
X		tuninit(ifp->if_unit);
X		TUNDEBUG("%s%d: broadcast address set\n",
X			 ifp->if_name, ifp->if_unit);
X		break;
X#if 0
X	case SIOCSIFMTU:
X		error = suser(p->p_ucred, &p->p_acflag);
X		if (error != 0)
X			break;
X		sc->sc_if.if_mtu = ifr->ifr_mtu;
X		break;
X	case SIOCGIFMTU:
X		ifr->ifr_mtu = sc->sc_if.if_mtu;
X		break;
X#endif
X	default:
X		error = EINVAL;
X	}
X	splx(s);
X	return (error);
X}
X
X/*
X * tunoutput - queue packets from higher level ready to put out.
X */
Xint
Xtunoutput(ifp, m0, dst, rt)
X	struct ifnet   *ifp;
X	struct mbuf    *m0;
X	struct sockaddr *dst;
X	struct rtentry *rt;
X{
X	struct tun_softc *tp = &tunctl[ifp->if_unit];
X	struct proc	*p;
X	int		s;
X
X	TUNDEBUG ("%s%d: tunoutput\n", ifp->if_name, ifp->if_unit);
X
X	if ((tp->tun_flags & TUN_READY) != TUN_READY) {
X		TUNDEBUG ("%s%d: not ready 0%o\n", ifp->if_name,
X			  ifp->if_unit, tp->tun_flags);
X		m_freem (m0);
X		return EHOSTDOWN;
X	}
X
X#if NBPFILTER > 0
X	if (tp->tun_bpf) {
X		/*
X		 * We need to prepend the address family as
X		 * a four byte field.  Cons up a dummy header
X		 * to pacify bpf.  This is safe because bpf
X		 * will only read from the mbuf (i.e., it won't
X		 * try to free it or keep a pointer to it).
X		 */
X		struct mbuf m;
X		u_int32_t af = dst->sa_family;
X
X		m.m_next = m0;
X		m.m_len = sizeof(af);
X		m.m_data = (char *)&af;
X
X		bpf_mtap(tp->tun_bpf, &m);
X	}
X#endif
X
X	switch(dst->sa_family) {
X#ifdef INET
X	case AF_INET:
X		if (tp->tun_flags & TUN_PREPADDR) {
X			M_PREPEND(m0,sizeof(struct in_addr),M_DONTWAIT);
X			if (! m0) {
X				IF_DROP(&ifp->if_snd);
X				return(ENOBUFS);
X			}
X			*mtod(m0,struct in_addr *) = ((struct sockaddr_in *)dst)->sin_addr;
X		}
X		m0 = m_prepend(m0,1,M_DONTWAIT);
X		if (! m0) {
X			IF_DROP(&ifp->if_snd);
X			return(ENOBUFS);
X		}
X		*mtod(m0,unsigned char *) = AF_INET;
X		s = splimp();
X		if (IF_QFULL(&ifp->if_snd)) {
X			IF_DROP(&ifp->if_snd);
X			m_freem(m0);
X			splx(s);
X			ifp->if_collisions++;
X			return (ENOBUFS);
X		}
X		IF_ENQUEUE(&ifp->if_snd, m0);
X		splx(s);
X		ifp->if_opackets++;
X		break;
X#endif
X	default:
X		m_freem(m0);
X		return EAFNOSUPPORT;
X	}
X
X	if (tp->tun_flags & TUN_RWAIT) {
X		tp->tun_flags &= ~TUN_RWAIT;
X		wakeup((caddr_t)tp);
X	}
X	if (tp->tun_flags & TUN_ASYNC && tp->tun_pgrp) {
X		if (tp->tun_pgrp > 0)
X			gsignal(tp->tun_pgrp, SIGIO);
X		else if ((p = pfind(-tp->tun_pgrp)))
X			psignal(p, SIGIO);
X	}
X	selwakeup(&tp->tun_rsel_d);
X	return 0;
X}
X
Xint
Xtunioctl(dev,cmd,data,flag,p)
Xdev_t dev;
Xu_long cmd;
Xcaddr_t data;
Xint flag;
Xstruct proc *p;
X{
X int unit;
X int s;
X struct tun_softc *tp;
X
X unit = minor(dev);
X tp = &tunctl[unit&TUNU_UNIT];
X switch (unit & TUNU_TYPE)
X  { case TUNU_T_DATA:
X       switch (cmd)
X	{ case TUNSDEBUG:
X	     tundebug = *(int *)data;
X	     break;
X	  case TUNGDEBUG:
X	     *(int *)data = tundebug;
X	     break;
X	  case TUNSMODE:
X	     switch (*(int *)data)
X	      { case IFF_POINTOPOINT:
X		case IFF_BROADCAST:
X		   s = splimp();
X		   if (tp->tun_if.if_flags & IFF_UP)
X		    { splx(s);
X		      return(EBUSY);
X		    }
X		   tp->tun_if.if_flags &= ~(IFF_BROADCAST|IFF_POINTOPOINT);
X		   tp->tun_if.if_flags |= *(int *)data;
X		   selwakeup(&tp->tun_rsel_c);
X		   splx(s);
X		   break;
X		default:
X		   return(EINVAL);
X		   break;
X	      }
X	     break;
X	  case TUNSFLAG:
X	     s = splimp();
X	     tp->tun_flags = (tp->tun_flags & ~TUN_UCHG) | (*(int *)data & TUN_UCHG);
X	     selwakeup(&tp->tun_rsel_c);
X	     splx(s);
X	     break;
X	  case TUNGFLAG:
X	     s = splimp();
X	     *(int *)data = tp->tun_flags; /* & TUN_UCHG maybe? */
X	     splx(s);
X	     break;
X	  case FIONBIO:
X	     s = splimp();
X	     if (*(int *)data) tp->tun_flags |= TUN_NBIO;
X	     else              tp->tun_flags &= ~TUN_NBIO;
X	     selwakeup(&tp->tun_rsel_c);
X	     splx(s);
X	     break;
X	  case FIOASYNC:
X	     s = splimp();
X	     if (*(int *)data) tp->tun_flags |= TUN_ASYNC;
X	     else              tp->tun_flags &= ~TUN_ASYNC;
X	     selwakeup(&tp->tun_rsel_c);
X	     splx(s);
X	     break;
X	  case FIONREAD:
X	     s = splimp();
X	     *(int *)data = tp->tun_if.if_snd.ifq_head ? tp->tun_if.if_snd.ifq_head->m_pkthdr.len : 0;
X	     splx(s);
X	     break;
X	  case TIOCSPGRP:
X	     s = splimp();
X	     tp->tun_pgrp = *(int *)data;
X	     selwakeup(&tp->tun_rsel_c);
X	     splx(s);
X	     break;
X	  case TIOCGPGRP:
X	     s = splimp();
X	     *(int *)data = tp->tun_pgrp;
X	     splx(s);
X	     break;
X	  default:
X	     return(ENOTTY);
X	     break;
X	}
X       break;
X    case TUNU_T_CTL:
X       return(ENOTTY);
X       break;
X    default:
X       panic("tunioctl");
X       break;
X  }
X return(0);
X}
X
X/*
X * cdev read - data, read a packet; ctl, read status info
X */
Xint
Xtunread(dev,uio)
Xdev_t dev;
Xstruct uio *uio;
X{
X int unit;
X struct tun_softc *tp;
X struct ifnet *ifp;
X struct mbuf *m;
X struct mbuf *m0;
X int error;
X int len;
X int s;
X
X unit = minor(dev);
X tp = &tunctl[unit&TUNU_UNIT];
X ifp = &tp->tun_if;
X switch (unit & TUNU_TYPE)
X  { case TUNU_T_DATA:
X       TUNDEBUG("%s%d: read\n",ifp->if_name,ifp->if_unit);
X       s = splimp();
X       if ( !(tp->tun_flags & TUN_RRWAIT) &&
X	    ((tp->tun_flags & TUN_READY) != TUN_READY) )
X	{ splx(s);
X	  TUNDEBUG("%s%d: not ready 0%o\n",ifp->if_name,ifp->if_unit,tp->tun_flags);
X	  return(EHOSTDOWN);
X	}
X       tp->tun_flags &= ~TUN_RWAIT;
X       do
X	{ while ((tp->tun_flags & TUN_READY) != TUN_READY)
X	   { if (tsleep((caddr_t)tp,(PZERO+1)|PCATCH,"tunread",0)) return(EINTR);
X	   }
X	  IF_DEQUEUE(&ifp->if_snd,m0);
X	  if (m0 == 0)
X	   { if (tp->tun_flags & TUN_NBIO)
X	      { splx(s);
X		return(EWOULDBLOCK);
X	      }
X	     tp->tun_flags |= TUN_RWAIT;
X	     if (tsleep((caddr_t)tp, (PZERO+1)|PCATCH, "tunread", 0)) return(EINTR);
X	   }
X	} while (! m0);
X       splx(s);
X       error = 0;
X       while (m0 && (uio->uio_resid > 0) && (error == 0))
X	{ len = min(uio->uio_resid,m0->m_len);
X	  if (len == 0) break;
X	  error = uiomove(mtod(m0,caddr_t),len,uio);
X	  MFREE(m0,m);
X	  m0 = m;
X	}
X       if (m0)
X	{ TUNDEBUG("Dropping mbuf\n");
X	  m_freem(m0);
X	}
X       if (error) ifp->if_oerrors ++;
X       return(error);
X       break;
X    case TUNU_T_CTL:
X	{ char buf[512];
X	  char *cp;
X	  int i;
X	  unsigned int f;
X	  s = splimp();
X	  f = tp->tun_flags;
X	  splx(s);
X	  cp = &buf[0];
X	  strcpy(cp,"unit=");
X	  cp += 5;
X	  sprintf(cp,"%d",(int)ifp->if_unit);
X	  cp += strlen(cp);
X	  strcpy(cp," flags=(");
X	  cp += 8;
X	  for (i=0;flagbits[i].name;i++)
X	   { if (i) *cp++ = ',';
X	     if (! (f & flagbits[i].bit)) *cp++ = '!';
X	     strcpy(cp,flagbits[i].name);
X	     cp += strlen(cp);
X	   }
X	  strcpy(cp,") type=");
X	  cp += 7;
X	  if (ifp->if_flags & IFF_POINTOPOINT)
X	   { strcpy(cp,"pointopoint");
X	     cp += 11;
X	   }
X	  if (ifp->if_flags & IFF_BROADCAST)
X	   { strcpy(cp,"broadcast");
X	     cp += 9;
X	   }
X	  strcpy(cp," mtu=");
X	  cp += 5;
X	  sprintf(cp,"%d",(int)ifp->if_mtu);
X	  cp += strlen(cp);
X	  strcpy(cp," coll=");
X	  cp += 6;
X	  sprintf(cp,"%d",(int)ifp->if_collisions);
X	  cp += strlen(cp);
X	  strcpy(cp," ipkts=");
X	  cp += 7;
X	  sprintf(cp,"%d",(int)ifp->if_ipackets);
X	  cp += strlen(cp);
X	  *cp++ = '/';
X	  sprintf(cp,"%d",(int)ifp->if_ierrors);
X	  cp += strlen(cp);
X	  strcpy(cp," opkts=");
X	  cp += 7;
X	  sprintf(cp,"%d",(int)ifp->if_opackets);
X	  cp += strlen(cp);
X	  *cp++ = '/';
X	  sprintf(cp,"%d",(int)ifp->if_oerrors);
X	  cp += strlen(cp);
X	  strcpy(cp," pgrp=");
X	  cp += 6;
X	  sprintf(cp,"%d",(int)tp->tun_pgrp);
X	  cp += strlen(cp);
X	  *cp++ = '\n';
X	  return(uiomove(&buf[0],min(uio->uio_resid,cp-&buf[0]),uio));
X	}
X       break;
X  }
X panic("tunread");
X}
X
X/*
X * cdev write - data, write a packet; ctl, frob flag or set value
X */
Xint
Xtunwrite(dev, uio)
Xdev_t dev;
Xstruct uio *uio;
X{
X int unit;
X struct tun_softc *tp;
X struct ifnet *ifp;
X int error;
X int s;
X
X unit = minor(dev);
X tp = &tunctl[unit&TUNU_UNIT];
X ifp = &tp->tun_if;
X error = 0;
X switch (unit & TUNU_TYPE)
X  { case TUNU_T_DATA:
X	{ int tlen;
X	  int mlen;
X	  struct mbuf *top;
X	  struct mbuf **mp;
X	  struct mbuf *m;
X	  unsigned char af;
X	  struct ifqueue *ifq;
X	  int isr;
X	  TUNDEBUG("%s%d: tunwrite\n",ifp->if_name,ifp->if_unit);
X	  if ((uio->uio_resid < 2) || (uio->uio_resid > TUNMTU))
X	   { TUNDEBUG("%s%d: len=%d!\n",ifp->if_name,ifp->if_unit,uio->uio_resid);
X	     return(EMSGSIZE);
X	   }
X	  error = uiomove(&af,1,uio);
X	  switch (af)
X	   {
X#ifdef INET
X	     case AF_INET:
X		ifq = &ipintrq;
X		isr = NETISR_IP;
X		break;
X#endif
X	     default:
X		return(EAFNOSUPPORT);
X		break;
X	   }
X	  if (error) return(error);
X	  tlen = uio->uio_resid;
X	  MGETHDR(m,M_DONTWAIT,MT_DATA);
X	  if (m == 0) return(ENOBUFS);
X	  mlen = MHLEN;
X	  mp = &top;
X	  while (uio->uio_resid > 0)
X	   { m->m_len = min(mlen,uio->uio_resid);
X	     error = uiomove(mtod(m,caddr_t),m->m_len,uio);
X	     if (error)
X	      { m_free(m);
X		break;
X	      }
X	     *mp = m;
X	     mp = &m->m_next;
X	     if (uio->uio_resid > 0)
X	      { MGET(m,M_DONTWAIT,MT_DATA);
X		if (m == 0)
X		 { error = ENOBUFS;
X		   break;
X		 }
X		mlen = MLEN;
X	      }
X	   }
X	  *mp = 0;
X	  if (error)
X	   { if (top) m_freem(top);
X	     ifp->if_ierrors ++;
X	     return(error);
X	   }
X	  top->m_pkthdr.len = tlen;
X	  top->m_pkthdr.rcvif = ifp;
X#if NBPFILTER > 0
X	  if (tunctl[unit].tun_bpf)
X	   { /*
X	      * Prepend the address family as a four byte field.
X	      * Fake it, trusting bpf to only read, not free/save.
X	      */
X	     struct mbuf m;
X	     u_int32_t iaf;
X	     iaf = af;
X	     m.m_next = top;
X	     m.m_len = sizeof(iaf);
X	     m.m_data = (char *)&iaf;
X	     bpf_mtap(tunctl[unit].tun_bpf,&m);
X	   }
X#endif
X	  s = splimp();
X	  if (IF_QFULL(ifq))
X	   { IF_DROP(ifq);
X	     splx(s);
X	     ifp->if_collisions ++;
X	     m_freem(top);
X	     return(ENOBUFS);
X	   }
X	  IF_ENQUEUE(ifq,top);
X	  splx(s);
X	  ifp->if_ipackets ++;
X	  schednetisr(isr);
X	  return(error);
X	}
X       break;
X    case TUNU_T_CTL:
X	{ char buf[512];
X	  int len;
X	  char *cp;
X	  char key;
X	  len = min(uio->uio_resid,sizeof(buf)-1);
X	  error = uiomove(&buf[0],len,uio);
X	  buf[len] = '\0';
X	  if (error) return(error);
X	  cp = &buf[0];
X	  while (1)
X	   { while (*cp && spacep(*cp)) cp ++;
X	     key = *cp++;
X	     switch (key)
X	      { case '\0':
X		   break;
X		case '+':
X		case '-':
X		    { char *f;
X		      char t;
X		      int i;
X		      while (*cp && spacep(*cp)) cp ++;
X		      f = cp;
X		      while (*cp && !spacep(*cp)) cp ++;
X		      t = *cp;
X		      *cp = '\0';
X		      for (i=0;flagbits[i].name;i++)
X		       { if (flagbits[i].bit & TUN_UCHG)
X			  { if (!strcmp(flagbits[i].name,f))
X			     { s = splimp();
X			       switch (key)
X				{ case '+': tp->tun_flags |= flagbits[i].bit; break;
X				  case '-': tp->tun_flags &= ~flagbits[i].bit; break;
X				}
X			       splx(s);
X			       f = 0;
X			       break;
X			     }
X			  }
X		       }
X		      if (f) return(EIO);
X		      *cp = t;
X		    }
X		   break;
X		case 't':
X		    { int t;
X		      while (*cp && spacep(*cp)) cp ++;
X		      switch (*cp++)
X		       { case 'b': t = IFF_BROADCAST; break;
X			 case 'p': t = IFF_POINTOPOINT; break;
X			 default: return(EIO); break;
X		       }
X		      s = splimp();
X		      if (ifp->if_flags & IFF_UP)
X		       { splx(s);
X			 return(EBUSY);
X		       }
X		      ifp->if_flags &= ~(IFF_BROADCAST|IFF_POINTOPOINT);
X		      ifp->if_flags |= t;
X		      splx(s);
X		    }
X		   break;
X		case 'g':
X		case 'm':
X		    { int v;
X		      int neg;
X		      neg = 0;
X		      while (*cp && spacep(*cp)) cp ++;
X		      if (*cp == '-')
X		       { neg = 1;
X			 cp ++;
X		       }
X		      if (! digitp(*cp)) return(EIO);
X		      v = *cp++ - '0';
X		      while (*cp && digitp(*cp)) v = (10 * v) + (*cp++ - '0');
X		      switch (key)
X		       { case 'g':
X			    s = splimp();
X			    tp->tun_pgrp = v;
X			    splx(s);
X			    break;
X			 case 'm':
X			    if (v < 1) return(EIO);
X			    s = splimp();
X			    ifp->if_mtu = v;
X			    splx(s);
X			    break;
X		       }
X		    }
X		   break;
X	      }
X	   }
X	  selwakeup(&tp->tun_rsel_c);
X	  return(0);
X	}
X       break;
X  }
X panic("tunwrite");
X}
X
X/*
X * tunselect - the select interface, this is only useful on reads
X * really. The write detect always returns true, write never blocks
X * anyway, it either accepts the packet or drops it.
X */
Xint
Xtunselect(dev,rw)
Xdev_t dev;
Xint rw;
X{
X int unit;
X int s;
X struct tun_softc *tp;
X struct ifnet *ifp;
X
X unit = minor(dev);
X tp = &tunctl[unit&TUNU_UNIT];
X ifp = &tp->tun_if;
X
X switch (unit & TUNU_TYPE)
X  { case TUNU_T_DATA:
X       s = splimp();
X       TUNDEBUG("%s%d: tunselect\n",ifp->if_name,ifp->if_unit);
X       switch (rw)
X	{ case FREAD:
X	     if (ifp->if_snd.ifq_len > 0)
X	      { splx(s);
X		TUNDEBUG("%s%d: tunselect q=%d\n",ifp->if_name,ifp->if_unit,ifp->if_snd.ifq_len);
X		return(1);
X	      }
X	     selrecord(curproc,&tp->tun_rsel_d);
X	     break;
X	  case FWRITE:
X	     splx(s);
X	     return(1);
X	}
X       splx(s);
X       TUNDEBUG("%s%d: tunselect waiting\n",ifp->if_name,ifp->if_unit);
X       return(0);
X       break;
X    case TUNU_T_CTL:
X       switch (rw)
X	{ case FREAD:
X	     s = splimp();
X	     selrecord(curproc,&tp->tun_rsel_c);
X	     splx(s);
X	     break;
X	  case FWRITE:
X	     return(1);
X	     break;
X	}
X       return(0);
X       break;
X  }
X panic("tunselect");
X}
X
X#endif  /* NTUN */
EOF
if test 20460 -ne "`wc -c if_tun.c`"
then
echo shar: error transmitting if_tun.c \(should have been 20460 characters\)
fi
exit 0
# end of shell archive

					der Mouse

			    mouse@collatz.mcrcim.mcgill.edu
>Audit-Trail:
>Unformatted: