/* UNFINISHED CONVERSION TO 386BSD -wfj */
/* if_tun.c - tunnel interface module & driver */
* Copyright (c) 1988, Julian Onions.
* This source may be freely distributed, however I would be interested
* in any changes that are made.
* 90/02/06 15:03 - Fixed a bug in where TIOCGPGRP and TIOCSPGRP were
* mixed up. Anders Klemets - klemets@sics.se
* $Header: if_tun.c,v 1.13 88/07/11 08:28:51 jpo Exp $
* Revision 1.13 88/07/11 08:28:51 jpo
* Revision 1.12 87/12/10 09:16:29 jpo
* Decided the vax/mtpr was unnecessay.
* Revision 1.11 87/12/10 09:10:36 jpo
* Fixed some minor things & 1 major bug.
* Revision 1.10 87/11/04 14:27:41 jpo
* A few sanity checks added.
* Revision 1.9 87/11/04 14:13:45 jpo
* Added some ioctls for non-blocking & async I/O
* Revision 1.8 87/10/19 10:28:14 jpo
* Another touch up (sigh)
* Revision 1.7 87/10/19 10:25:48 jpo
* Revision 1.6 87/10/19 09:15:14 jpo
* Revision 1.5 87/10/19 08:34:51 jpo
* General clean up - plus sun specific fixes
* Revision 1.4 87/10/16 17:10:12 jpo
* Purged all ioctl read/writes and non-standard routing stuff.
* Revision 1.3 87/10/05 11:57:09 jpo
* More debugging - in error mainly.
* Revision 1.2 87/10/04 18:29:45 jpo
* Select & read/write working.
* 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
* Julian Onions <jpo@cs.nott.ac.uk>
* Nottingham University 1987.
#include "netinet/in_systm.h"
#include "netinet/in_var.h"
#include "netinet/if_ether.h"
#define TUNDEBUG if (tundebug) printf
u_short tun_flags
; /* misc flags */
struct ifnet tun_if
; /* the interface */
int tun_pgrp
; /* the process group - if any */
struct proc
*tun_rsel
; /* read select */
struct proc
*tun_wsel
; /* write select (not used) */
int tunoutput (), tunioctl (), tuninit ();
/* tunnel open - must be superuser & the device must be configured in */
register struct ifnet
*ifp
;
if ((unit
= minor (dev
)) >= NTUN
)
if (tp
->tun_flags
& TUN_OPEN
)
if ((tp
->tun_flags
& TUN_INITED
) == 0) {
tp
->tun_flags
= TUN_INITED
;
tp
->tun_flags
|= TUN_OPEN
;
TUNDEBUG ("%s%d: open\n", ifp
->if_name
, ifp
->if_unit
);
/* tunclose - close the device - mark i/f down & delete routing info */
register int unit
= minor (dev
);
register struct tunctl
*tp
= &tunctl
[unit
];
register struct ifnet
*ifp
= &tp
->tun_if
;
rcoll
= tp
->tun_flags
& TUN_RCOLL
;
tp
->tun_flags
&= TUN_INITED
;
* junk all pending output
IF_DEQUEUE (&ifp
->if_snd
, m
);
if (m
) /* actually - m_freem checks this - but what the hell */
if (ifp
->if_flags
& IFF_UP
) {
if (ifp
->if_flags
& IFF_RUNNING
)
rtinit (ifp
->if_addrlist
, (int)SIOCDELRT
, RTF_HOST
);
selwakeup (tp
->tun_rsel
, rcoll
);
tp
-> tun_rsel
= tp
-> tun_wsel
= (struct proc
*)0;
TUNDEBUG ("%s%d: closed\n", ifp
->if_name
, ifp
->if_unit
);
* attach an interface N.B. argument is not same as other drivers
register struct ifnet
*ifp
= &tunctl
[unit
].tun_if
;
register struct sockaddr_in
*sin
;
ifp
->if_ioctl
= tunioctl
;
ifp
->if_output
= tunoutput
;
sin
= (struct sockaddr_in
*) & ifp
->if_addr
;
sin
->sin_family
= AF_INET
;
ifp
->if_flags
= IFF_POINTOPOINT
;
ifp
->if_snd
.ifq_maxlen
= ifqmaxlen
;
ifp
->if_collisions
= ifp
->if_ierrors
= ifp
->if_oerrors
= 0;
ifp
->if_ipackets
= ifp
->if_opackets
= 0;
TUNDEBUG ("%s%d: tunattach\n", ifp
->if_name
, ifp
->if_unit
);
register struct tunctl
*tp
= &tunctl
[unit
];
register struct ifnet
*ifp
= &tp
->tun_if
;
register struct sockaddr_in
*sin
;
sin
= (struct sockaddr_in
*) & ifp
->if_addr
;
if (sin
->sin_addr
.s_addr
== 0) /* if address still unknown */
ifp
->if_flags
|= IFF_UP
| IFF_RUNNING
;
tp
->tun_flags
|= TUN_IASET
;
TUNDEBUG ("%s%d: tuninit\n", ifp
->if_name
, ifp
->if_unit
);
* Process an ioctl request.
* The problem here is 4.2 pass a struct ifreq * to this routine,
* sun only passes a struct sockaddr * since 3.2 at least. This is
* rather upsetting! Also, sun doesn't pass the SIOCDSTADDR ioctl through
* so we can't detect this being set directly. This is the reason for
* Under 4.3 and SunOs 4.0 we now get the SIOCSIFDSTADDR ioctl, and we get a
* struct in_ifaddr * for data. (tte)
#if !defined(BSD4_3) && defined(sun)
* workaround for not being able to detect DSTADDR being set.
struct tunctl
*tp
= &tunctl
[ifp
->if_unit
];
sin
= (struct sockaddr_in
*) &ifp
->if_dstaddr
;
if (sin
->sin_addr
.s_addr
== 0)
tp
-> tun_flags
|= TUN_DSTADDR
;
#define tuncheckready(dummy) 1
#endif /* !defined(BSD4_3) && defined(sun) */
tunioctl (ifp
, cmd
, data
)
register struct ifnet
*ifp
;
struct sockaddr_in
*sin
= (struct sockaddr_in
*) data
;
struct ifreq
*ifr
= (struct ifreq
*) data
;
int s
= splimp (), error
= 0;
struct tunctl
*tp
= &tunctl
[ifp
->if_unit
];
if (ifp
->if_flags
& IFF_RUNNING
)
if_rtinit (ifp
, -1); /* delete previous route */
sin
= (struct sockaddr_in
*)&ifr
-> ifr_addr
;
ifp
->if_addr
= *((struct sockaddr
*) sin
);
ifp
->if_net
= in_netof (sin
->sin_addr
);
ifp
->if_host
[0] = in_lnaof (sin
->sin_addr
);
tp
->tun_flags
|= TUN_DSTADDR
;
sin
= (struct sockaddr_in
*)&ifr
-> ifr_addr
;
ifp
->if_dstaddr
= *((struct sockaddr
*)sin
);
TUNDEBUG ("%s%d: destination addres set\n", ifp
->if_name
,
* tunoutput - queue packets from higher level ready to put out.
TUNDEBUG ("%s%d: tunoutput\n", ifp
->if_name
, ifp
->if_unit
);
tp
= &tunctl
[ifp
->if_unit
];
if ((tp
->tun_flags
& TUN_READY
) != TUN_READY
) {
if(tuncheckready(ifp
) == 0) {
TUNDEBUG ("%s%d: not ready 0%o\n", ifp
->if_name
,
ifp
->if_unit
, tp
->tun_flags
);
switch (dst
->sa_family
) {
if (IF_QFULL (&ifp
->if_snd
))
IF_ENQUEUE (&ifp
->if_snd
, m0
);
if (tp
->tun_flags
& TUN_RWAIT
) {
tp
->tun_flags
&= ~TUN_RWAIT
;
if (tp
->tun_flags
& TUN_ASYNC
&& tp
-> tun_pgrp
!= 0) {
gsignal (tp
->tun_pgrp
, SIGIO
);
else if ((p
= pfind (-tp
->tun_pgrp
)) != 0)
selwakeup (tp
->tun_rsel
, tp
->tun_flags
& TUN_RCOLL
);
tp
->tun_flags
&= ~TUN_RCOLL
;
tp
->tun_rsel
= (struct proc
*) 0;
* the cdevsw interface is now pretty minimal.
tuncioctl (dev
, cmd
, data
, flag
)
struct tunctl
*tp
= &tunctl
[unit
];
tp
->tun_flags
|= TUN_NBIO
;
tp
->tun_flags
&= ~TUN_NBIO
;
tp
->tun_flags
|= TUN_ASYNC
;
tp
->tun_flags
&= ~TUN_ASYNC
;
if (tp
->tun_if
.if_snd
.ifq_head
)
*(int *)data
= tp
->tun_if
.if_snd
.ifq_head
->m_len
;
tp
->tun_pgrp
= *(int *)data
;
*(int *)data
= tp
->tun_pgrp
;
* The cdevsw read interface - reads a packet at a time, or at least as much
* of a packet as can be read.
register struct ifnet
*ifp
;
register struct mbuf
*m
, *m0
;
TUNDEBUG ("%s%d: read\n", ifp
->if_name
, ifp
->if_unit
);
if ((tp
->tun_flags
& TUN_READY
) != TUN_READY
) {
if(tuncheckready(ifp
) == 0) {
TUNDEBUG ("%s%d: not ready 0%o\n", ifp
->if_name
,
ifp
->if_unit
, tp
->tun_flags
);
tp
->tun_flags
&= ~TUN_RWAIT
;
IF_DEQUEUE (&ifp
->if_snd
, m0
);
if (tp
-> tun_flags
& TUN_NBIO
) {
tp
->tun_flags
|= TUN_RWAIT
;
sleep ((caddr_t
) tp
, PZERO
+ 1);
while (m0
&& uio
->uio_resid
> 0 && error
== 0) {
len
= MIN (uio
->uio_resid
, m0
->m_len
);
error
= uiomove (mtod (m0
, caddr_t
), len
,
TUNDEBUG ("Dropping mbuf\n");
* the cdevsw write interface - an atomic write is a packet - or else!
struct mbuf
*top
, **mp
, *m
;
struct ifnet
*ifp
= &(tunctl
[unit
].tun_if
);
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
,
while (error
== 0 && uio
->uio_resid
> 0) {
MGET (m
, M_DONTWAIT
, MT_DATA
);
m
->m_len
= MIN (MLEN
, uio
->uio_resid
);
error
= uiomove (mtod (m
, caddr_t
), m
->m_len
, UIO_WRITE
, uio
);
* Place interface pointer before the data
* for the receiving protocol.
if (top
->m_off
<= MMAXOFF
&&
top
->m_off
>= MMINOFF
+ sizeof(struct ifnet
*)) {
top
->m_off
-= sizeof(struct ifnet
*);
top
->m_len
+= sizeof(struct ifnet
*);
MGET(m
, M_DONTWAIT
, MT_HEADER
);
if (m
== (struct mbuf
*)0)
m
->m_len
= sizeof(struct ifnet
*);
*(mtod(top
, struct ifnet
**)) = ifp
;
if (IF_QFULL (&ipintrq
)) {
IF_ENQUEUE (&ipintrq
, top
);
* 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.
register struct tunctl
*tp
= &tunctl
[unit
];
struct ifnet
*ifp
= &tp
->tun_if
;
TUNDEBUG ("%s%d: tunselect\n", ifp
->if_name
, ifp
->if_unit
);
if (ifp
->if_snd
.ifq_len
> 0) {
TUNDEBUG ("%s%d: tunselect q=%d\n", ifp
->if_name
,
ifp
->if_unit
, ifp
->if_snd
.ifq_len
);
if (tp
->tun_rsel
&& tp
->tun_rsel
->p_wchan
==
tp
->tun_flags
|= TUN_RCOLL
;
tp
->tun_rsel
= u
.u_procp
;
TUNDEBUG ("%s%d: tunselect waiting\n", ifp
->if_name
, ifp
->if_unit
);