This is venet1.c in view mode; [Download] [Up]
/*
* LKS for EtherTalk Phase 1 (and AARP)
* Eric P. Scott, San Francisco State University, February 1993
* Copyright 1993 by Eric P. Scott. All rights reserved.
*
* Portions based on sample code Copyright 1990 by NeXT Computer, Inc.
* Portions derived from code developed by the University of California
* at Berkeley.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notices, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notices, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. All advertising materials mentioning features or use of this
* software, must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the author, the Universities, nor the names of
* additional contributors may be used to endorse or promote products
* derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR, REGENTS, TRUSTEES, OR ADDITIONAL
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* (Amended notice Copyright (c) 1989 The Regents of the University of
* California.)
*
*
* For uniprocessor systems only!
*
* To compile for NeXT Software Release 2.0/2.1/2.2/2.2a:
* cc -c -DKERNEL -DKERNEL_FEATURES -DMACH -O venet1.c
*
* To compile for NeXT Software Release 3.0:
* cc -c -DKERNEL -DMACH_USER_API -DMACH -O2 -Wall venet1.c
*
* Optional features:
* -DFASTER_QUEUE => smaller code, faster (but incompatible) input queue
* -DSEGREGATE => support for etI[AE]? minor devices
*
*
* HISTORY
* 1.0 3/ 8/93 EPS Initial nonrelease.
* 1.1 3/13/93 EPS Added SEGREGATE option.
*
* @(#)venet1.c 1.1 (SFSU) 3/13/93
*/
static char sccsid[]={ /* for "what" command */
'@', '(', '#', ')',
'v', 'e', 'n', 'e', 't', '1', '.', 'c', '\t',
'1', '.', '1',
#ifdef FASTER_QUEUE
'-', 'F', 'Q',
#endif
#ifdef SEGREGATE
'-', 'E', 'A',
#endif
' ', '(', 'S', 'F', 'S', 'U', ')', ' ',
'3', '/', '1', '3', '/', '9', '3', '\0'
};
/* Maximum number of Ethernet interfaces (1..64) */
#ifndef ET1_MAXMIN
#define ET1_MAXMIN 4
#endif
#include <kernserv/kern_server_types.h>
#include <kernserv/kern_server_reply.h>
#ifdef MACH_USER_API
#include <bsd/dev/ldd.h>
#include <bsd/sys/conf.h>
#include <bsd/sys/systm.h>
#include <bsd/sys/file.h>
/* <bsd/sys/user.h> erroneously imports <kern/lock.h>, generates a warning */
#include <bsd/sys/proc.h>
#include <bsd/sys/socket.h>
#include <bsd/netinet/in.h>
#ifdef m68k
#include <kernserv/m68k/spl.h>
#else
#include <bsd/machine/spl.h>
#endif
#include <bsd/sys/mbuf.h>
#include <bsd/net/if.h>
#include <bsd/netinet/if_ether.h>
extern int selthreadcache(void **);
extern void selthreadclear(void **);
extern int selwakeup(void *, int);
#else
#include <nextdev/ldd.h>
extern int strcmp(const char *, const char *);
#include <sys/conf.h>
#include <sys/systm.h>
#include <sys/file.h>
#include <sys/proc.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <machine/spl.h>
#include <sys/mbuf.h>
#include <net/if.h>
#include <netinet/if_ether.h>
extern int selwakeup(thread_t, int);
#endif
extern void gsignal(int, int);
extern int uiomove(caddr_t, int, enum uio_rw, struct uio *);
extern void m_freem(struct mbuf *);
extern void mclput(struct mbuf *);
#ifdef FASTER_QUEUE
#undef IF_DEQUEUEIF
#undef IF_ADJ
#endif
#define ETHERTYPE_ET1 0x809b /* EtherTalk Phase 1 protocol */
#define ETHERTYPE_AARP 0x80f3 /* AppleTalk AARP */
#define ETYPEOFFSET 12
const char IFTYPE_ET1[]="EtherTalk Phase 1 Protocol Access";
#ifndef NULL
#define NULL ((void *)0)
#endif
#ifndef PRINET
#define PRINET 26 /* interruptible */
#endif
typedef struct {
struct ether_addr vp_enaddr; /* real device's Ethernet address */
#define ET1_ISOPEN 1
#define ET1_WAKE 2
#define ET1_RCOLL 4
#define ET1_WCOLL 8
#define ET1_NBIO 16
#define ET1_ASYNC 32
short vp_state; /* per-unit state */
netif_t vp_rifp; /* real device's netif */
struct ifqueue vp_intrq; /* input queue */
off_t vp_nread; /* number of bytes queued */
#ifdef MACH_USER_API
void *vp_selr; /* thread selecting for read */
void *vp_selw; /* thread selecting for write */
#else
thread_t vp_selr; /* thread selecting for read */
thread_t vp_selw; /* thread selecting for write */
#endif
short vp_pgrp; /* process|group for SIGIO */
#ifdef SEGREGATE
/*
* This option extends the driver to support two additional minor
* devices per real interface. The "base" device operates as it always
* has, returning both EtherTalk Phase 1 and AARP datagrams in sequence.
* The etIE? (?+64) and etIA? (?+128) minors allow access to the two
* types separately. etI? and etIE? share data since they can't be
* opened simultaneously. Also, the SIOCGIFADDR and SIOCGIFBRDADDR
* ioctls are extended to return ether_type when applied to a single-
* type minor.
*
* [Unfortunately, doing this made the code truly horrid, not to mention
* difficult to understand (but silenced certain whiners, whose names
* I don't care to mention). So, if you're looking for good sample
* code, rip out everything where SEGREGATE is true...]
*
*/
#define ET1_EOPEN 64
#define ET1_AOPEN 128
#define ET1_AWAKE 256
#define ET1_ARCOLL 512
#define ET1_AWCOLL 1024
#define ET1_ANBIO 2048
#define ET1_AASYNC 4096
struct ifqueue vp_aintrq; /* alternate input queue */
off_t vp_anread; /* number of bytes queued */
#ifdef MACH_USER_API
void *vp_aselr; /* thread selecting for read */
void *vp_aselw; /* thread selecting for write */
#else
thread_t vp_aselr; /* thread selecting for read */
thread_t vp_aselw; /* thread selecting for write */
#endif
short vp_apgrp; /* process|group for SIGIO */
#endif /* SEGREGATE */
} venet1_private_t;
static inline venet1_private_t *VENET1_PRIVATE(netif_t ifp)
{
return((venet1_private_t *)if_private(ifp));
}
static inline struct ether_addr *VENET1_ENADDRP(netif_t ifp)
{
return(&((venet1_private_t *)if_private(ifp))->vp_enaddr);
}
static inline netif_t VENET1_RIF(netif_t ifp)
{
return(((venet1_private_t *)if_private(ifp))->vp_rifp);
}
static inline short VENET1_STATE(netif_t ifp)
{
return(((venet1_private_t *)if_private(ifp))->vp_state);
}
static inline void VENET1_STATE_BIS(netif_t ifp, short bits)
{
((venet1_private_t *)if_private(ifp))->vp_state|=bits;
}
static inline void VENET1_STATE_BIC(netif_t ifp, short bits)
{
((venet1_private_t *)if_private(ifp))->vp_state&=~bits;
}
static inline struct ifqueue *VENET1_INTRQP(netif_t ifp)
{
return(&((venet1_private_t *)if_private(ifp))->vp_intrq);
}
static inline off_t VENET1_NREAD(netif_t ifp)
{
return(((venet1_private_t *)if_private(ifp))->vp_nread);
}
static inline short VENET1_PGRP(netif_t ifp)
{
return(((venet1_private_t *)if_private(ifp))->vp_pgrp);
}
static inline void VENET1_PGRP_SET(netif_t ifp, short pgrp)
{
((venet1_private_t *)if_private(ifp))->vp_pgrp=pgrp;
}
#ifdef SEGREGATE
static inline struct ifqueue *VENET1_AINTRQP(netif_t ifp)
{
return(&((venet1_private_t *)if_private(ifp))->vp_aintrq);
}
static inline off_t VENET1_ANREAD(netif_t ifp)
{
return(((venet1_private_t *)if_private(ifp))->vp_anread);
}
static inline short VENET1_APGRP(netif_t ifp)
{
return(((venet1_private_t *)if_private(ifp))->vp_apgrp);
}
static inline void VENET1_APGRP_SET(netif_t ifp, short pgrp)
{
((venet1_private_t *)if_private(ifp))->vp_apgrp=pgrp;
}
#endif /* SEGREGATE */
extern int nulldev(dev_t, ...), nodev(dev_t, ...), seltrue(dev_t, int);
struct {
kern_server_t kern_server;
int et1maj; /* our major device number */
struct cdevsw saved_cdevsw; /* saved cdevsw entry */
netif_t et1if[ET1_MAXMIN]; /* pointers to "our" netifs */
int et1nif; /* number of ifs/minor devices */
int et1unloading; /* set when we're going away */
} instance;
int et1qmaxlen=IFQ_MAXLEN; /* maximum incoming datagrams we'll queue */
#ifndef NOTDEF
int et1verbose=1;
#else
extern int verbose;
#define et1verbose verbose
#endif
int et1open(dev_t, int, dev_t *), et1close(dev_t, int),
et1read(dev_t, struct uio *), et1write(dev_t, struct uio *),
et1ioctl(dev_t, int, caddr_t, int), et1select(dev_t, int);
struct cdevsw my_cdevsw={ /*19*/
(int (*)())et1open, (int (*)())et1close, (int (*)())et1read,
(int (*)())et1write, (int (*)())et1ioctl, (int (*)())nodev,
(int (*)())nulldev, (int (*)())et1select, (int (*)())nodev,
#ifdef NeXT
(int (*)())nodev, (int (*)())nodev
#endif
};
/* Call from Load_Commands.sect with major device number */
void
venet1_init(int idx)
{
void venet1_config(void);
extern int nchrdev;
if (idx<=0||idx>=nchrdev||cdevsw[idx].d_open!=(int (*)())nodev||
cdevsw[idx].d_reset!=(int (*)())nodev)
kern_serv_panic(kern_serv_bootstrap_port(&instance.kern_server),
"venet1_init: requested major device number unavailable");
else {
instance.et1unloading=0;
venet1_config();
instance.et1maj=idx;
instance.saved_cdevsw=cdevsw[idx];
cdevsw[idx]=my_cdevsw;
}
}
/* Call from Unload_Commands.sect */
/*ARGSUSED*/
void
venet1_signoff(int idx)
{
void venet1_detach(void);
register int i;
instance.et1unloading=1;
i=instance.et1maj;
cdevsw[i]=instance.saved_cdevsw;
for (i=0;i<instance.et1nif;i++) {
register netif_t ni;
int s;
ni=instance.et1if[i];
if (ni&&(VENET1_STATE(ni)&ET1_ISOPEN)) {
/* we are in deep, deep yogurt */
struct ifqueue *ifq;
printf("ET1 protocol access was open for interface %s%u!\n",
if_name(ni), if_unit(ni));
s=splimp();
if (VENET1_STATE(ni)&ET1_WAKE) {
VENET1_STATE_BIC(ni, ET1_WAKE);
wakeup((caddr_t)VENET1_INTRQP(ni));
}
if (VENET1_PRIVATE(ni)->vp_selr) {
(void)selwakeup(VENET1_PRIVATE(ni)->vp_selr,
VENET1_STATE(ni)&ET1_RCOLL);
#ifdef MACH_USER_API
selthreadclear(&VENET1_PRIVATE(ni)->vp_selr);
#else
VENET1_PRIVATE(ni)->vp_selr=THREAD_NULL;
#endif
VENET1_STATE_BIC(ni, ET1_RCOLL);
}
if (VENET1_PRIVATE(ni)->vp_selw) {
(void)selwakeup(VENET1_PRIVATE(ni)->vp_selw,
VENET1_STATE(ni)&ET1_WCOLL);
#ifdef MACH_USER_API
selthreadclear(&VENET1_PRIVATE(ni)->vp_selw);
#else
VENET1_PRIVATE(ni)->vp_selw=THREAD_NULL;
#endif
VENET1_STATE_BIC(ni, ET1_WCOLL);
}
#ifdef SEGREGATE
if (VENET1_STATE(ni)&ET1_AWAKE) {
VENET1_STATE_BIC(ni, ET1_AWAKE);
wakeup((caddr_t)VENET1_AINTRQP(ni));
}
if (VENET1_PRIVATE(ni)->vp_aselr) {
(void)selwakeup(VENET1_PRIVATE(ni)->vp_aselr,
VENET1_STATE(ni)&ET1_ARCOLL);
#ifdef MACH_USER_API
selthreadclear(&VENET1_PRIVATE(ni)->vp_aselr);
#else
VENET1_PRIVATE(ni)->vp_aselr=THREAD_NULL;
#endif
VENET1_STATE_BIC(ni, ET1_ARCOLL);
}
if (VENET1_PRIVATE(ni)->vp_aselw) {
(void)selwakeup(VENET1_PRIVATE(ni)->vp_aselw,
VENET1_STATE(ni)&ET1_AWCOLL);
#ifdef MACH_USER_API
selthreadclear(&VENET1_PRIVATE(ni)->vp_aselw);
#else
VENET1_PRIVATE(ni)->vp_aselw=THREAD_NULL;
#endif
VENET1_STATE_BIC(ni, ET1_AWCOLL);
}
#endif /* SEGREGATE */
/* maybe send SIGIO here? */
(void)splx(s);
/* sigh, whimper */
assert_wait(0, FALSE);
thread_set_timeout(hz*4);
thread_block();
/* 4... 3... 2... 1... */
s=splimp();
ifq=VENET1_INTRQP(ni);
for (;;) { /* empty input queue */
register struct mbuf *m;
IF_DEQUEUE(ifq, m);
if (!m) break;
#ifndef FASTER_QUEUE
IF_ADJ(m);
if (m)
#endif
VENET1_PRIVATE(ni)->vp_nread-=(off_t)m->m_len;
(void)splx(s);
m_freem(m);
s=splimp();
}
#ifdef SEGREGATE
ifq=VENET1_AINTRQP(ni);
for (;;) { /* empty input queue */
register struct mbuf *m;
IF_DEQUEUE(ifq, m);
if (!m) break;
#ifndef FASTER_QUEUE
IF_ADJ(m);
if (m)
#endif
VENET1_PRIVATE(ni)->vp_anread-=(off_t)m->m_len;
(void)splx(s);
m_freem(m);
s=splimp();
}
#endif /* SEGREGATE */
(void)splx(s);
}
}
venet1_detach();
}
/* Open pseudo-device. Fails with EBUSY if someone already has it. */
/*ARGSUSED*/
int
et1open(dev_t dev, int flags, dev_t *newdev)
{
register netif_t ni;
int s;
#ifdef SEGREGATE
s=dev&0x3f;
if (s>=instance.et1nif) return(ENXIO);
ni=instance.et1if[s];
#else
if (minor(dev)>=instance.et1nif) return(ENXIO);
ni=instance.et1if[minor(dev)];
#endif /* SEGREGATE */
#ifdef NOTDEF
if (!ni) return(ENXIO);
#endif
s=splimp();
#ifdef SEGREGATE
switch ((dev>>6)&3) {
case 0:
if (VENET1_STATE(ni)&ET1_ISOPEN) {
(void)splx(s);
return(EBUSY); /* exclusive open */
}
VENET1_STATE_BIS(ni, ET1_ISOPEN);
#ifdef NOTDEF
if (!VENET1_PGRP(ni)) VENET1_PGRP_SET(ni, (u.u_procp)->p_pid);
#endif
break;
case 1:
switch (VENET1_STATE(ni)&(ET1_ISOPEN|ET1_EOPEN|ET1_AOPEN)) {
case 0:
case ET1_ISOPEN|ET1_AOPEN:
break;
default:
(void)splx(s);
return(EBUSY); /* exclusive open/interlock */
}
VENET1_STATE_BIS(ni, ET1_ISOPEN|ET1_EOPEN);
#ifdef NOTDEF
if (!VENET1_PGRP(ni)) VENET1_PGRP_SET(ni, (u.u_procp)->p_pid);
#endif
break;
case 2:
switch (VENET1_STATE(ni)&(ET1_ISOPEN|ET1_EOPEN|ET1_AOPEN)) {
case 0:
case ET1_ISOPEN|ET1_EOPEN:
break;
default:
(void)splx(s);
return(EBUSY); /* exclusive open/interlock */
}
VENET1_STATE_BIS(ni, ET1_ISOPEN|ET1_AOPEN);
#ifdef NOTDEF
if (!VENET1_APGRP(ni)) VENET1_APGRP_SET(ni, (u.u_procp)->p_pid);
#endif
break;
default:
/*
* I'd like to reserve the unused quarter of minor device space
* for [future] etIs? (status) devices: these would allow
* multiple opens for access to status ioctls only (no read/
* write, would always select true, etc.). For SNMP agents and
* the like. --EPS
*/
(void)splx(s);
return(ENXIO); /* not yet implemented */
}
#else /* SEGREGATE */
if (VENET1_STATE(ni)&ET1_ISOPEN) {
(void)splx(s);
return(EBUSY); /* exclusive open */
}
VENET1_STATE_BIS(ni, ET1_ISOPEN);
#ifdef NOTDEF
if (!VENET1_PGRP(ni)) VENET1_PGRP_SET(ni, (u.u_procp)->p_pid);
#endif
#endif /* SEGREGATE */
(void)splx(s);
return(0);
}
/* Last close on pseudo-device. Discards any unread datagrams. */
/*ARGSUSED*/
int
et1close(dev_t dev, int flags)
{
register netif_t ni;
register struct mbuf *m;
struct ifqueue *ifq;
int s;
#ifdef MACH_USER_API
register void **selp;
#endif
#ifdef SEGREGATE
s=dev&0x3f;
if (s>=instance.et1nif) return(ENXIO);
ni=instance.et1if[s];
#else
if (minor(dev)>=instance.et1nif) return(ENXIO);
ni=instance.et1if[minor(dev)];
#endif /* SEGREGATE */
#ifdef NOTDEF
if (!ni) return(ENXIO);
#endif
s=splimp();
#ifdef SEGREGATE
switch ((dev>>6)&3) {
case 0:
VENET1_STATE_BIC(ni, ET1_ISOPEN);
VENET1_PGRP_SET(ni, 0);
ifq=VENET1_INTRQP(ni);
break;
case 1:
VENET1_STATE_BIC(ni, (VENET1_STATE(ni)&ET1_AOPEN) ? ET1_EOPEN :
ET1_ISOPEN|ET1_EOPEN);
VENET1_PGRP_SET(ni, 0);
ifq=VENET1_INTRQP(ni);
break;
default:
VENET1_STATE_BIC(ni, (VENET1_STATE(ni)&ET1_EOPEN) ? ET1_AOPEN :
ET1_ISOPEN|ET1_AOPEN);
VENET1_APGRP_SET(ni, 0);
ifq=VENET1_AINTRQP(ni);
break;
}
#else
VENET1_STATE_BIC(ni, ET1_ISOPEN);
VENET1_PGRP_SET(ni, 0);
ifq=VENET1_INTRQP(ni);
#endif /* SEGREGATE */
for (;;) { /* empty input queue */
IF_DEQUEUE(ifq, m);
if (!m) break;
#ifndef FASTER_QUEUE
IF_ADJ(m);
#endif
(void)splx(s);
m_freem(m);
s=splimp();
}
#ifdef SEGREGATE
if (dev&128) {
VENET1_PRIVATE(ni)->vp_anread=0L;
(void)splx(s);
#ifdef MACH_USER_API
selp= &VENET1_PRIVATE(ni)->vp_aselr;
if (*selp) selthreadclear(selp);
selp= &VENET1_PRIVATE(ni)->vp_aselw;
if (*selp) selthreadclear(selp);
#else
VENET1_PRIVATE(ni)->vp_aselr=THREAD_NULL;
VENET1_PRIVATE(ni)->vp_aselw=THREAD_NULL;
#endif
}
else {
#endif /* SEGREGATE */
VENET1_PRIVATE(ni)->vp_nread=0L;
(void)splx(s);
#ifdef MACH_USER_API
selp= &VENET1_PRIVATE(ni)->vp_selr;
if (*selp) selthreadclear(selp);
selp= &VENET1_PRIVATE(ni)->vp_selw;
if (*selp) selthreadclear(selp);
#else
VENET1_PRIVATE(ni)->vp_selr=THREAD_NULL;
VENET1_PRIVATE(ni)->vp_selw=THREAD_NULL;
#endif
#ifdef SEGREGATE
}
#endif /* SEGREGATE */
return(0);
}
/* Read one datagram (atomic). If user requests fewer bytes
* than the datagram offers, some data will be discarded.
*
* N.B. this is guaranteed to be called with uio->uio_resid!=0
*/
int
et1read(dev_t dev, register struct uio *uio)
{
/* no way to peek :-( */
register netif_t ni;
register struct mbuf *m;
struct ifqueue *ifq;
int s;
int error;
#ifdef SEGREGATE
s=dev&0x3f;
if (s>=instance.et1nif) return(ENXIO);
ni=instance.et1if[s];
#else
if (minor(dev)>=instance.et1nif) return(ENXIO);
ni=instance.et1if[minor(dev)];
#endif
#ifdef NOTDEF
if (!ni) return(ENXIO);
#endif
error=0;
s=splimp();
#ifdef SEGREGATE
if (dev&128) {
ifq=VENET1_AINTRQP(ni);
for (;;) {
IF_DEQUEUE(ifq, m);
if (!m) {
if (VENET1_STATE(ni)&ET1_ANBIO) error=EWOULDBLOCK;
else {
VENET1_STATE_BIS(ni, ET1_AWAKE);
sleep((caddr_t)ifq, PRINET);
if (instance.et1unloading==0) continue;
error=ENXIO;
}
}
else {
#ifndef FASTER_QUEUE
IF_ADJ(m);
if (m)
#endif
VENET1_PRIVATE(ni)->vp_anread-=(off_t)m->m_len;
}
break;
}
}
else {
#endif /* SEGREGATE */
ifq=VENET1_INTRQP(ni);
for (;;) {
IF_DEQUEUE(ifq, m);
if (!m) {
if (VENET1_STATE(ni)&ET1_NBIO) error=EWOULDBLOCK;
else {
VENET1_STATE_BIS(ni, ET1_WAKE);
sleep((caddr_t)ifq, PRINET);
if (instance.et1unloading==0) continue;
error=ENXIO;
}
}
else {
#ifndef FASTER_QUEUE
IF_ADJ(m);
if (m)
#endif
VENET1_PRIVATE(ni)->vp_nread-=(off_t)m->m_len;
}
break;
}
#ifdef SEGREGATE
}
#endif /* SEGREGATE */
(void)splx(s);
if (m) {
do {
if ((error=uiomove(mtod(m, caddr_t), min(uio->uio_resid, m->m_len),
UIO_READ, uio))!=0) break;
m=m_free(m);
if (!m) goto done;
} while (uio->uio_resid>0);
m_freem(m);
}
done:
return(error);
}
/* Write one datagram (atomic). Ethernet addresses and datagram
* type must immediately precede data. Fails with EINVAL if
* fewer than 14 bytes provided (of course, you should provide
* at least ETHERMIN); EMSGSIZE if too long. Fails with
* EADDRNOTAVAIL if ether_type is something we don't understand.
*
* Nonblocking output is not implemented.
*/
int
et1write(dev_t dev, register struct uio *uio)
{
register netif_t ni;
netbuf_t nb;
struct sockaddr dst;
int error;
#ifdef SEGREGATE
register int s;
s=dev&0x3f;
if (s>=instance.et1nif) return(ENXIO);
ni=instance.et1if[s];
#else
if (minor(dev)>=instance.et1nif) return(ENXIO);
ni=instance.et1if[minor(dev)];
#endif /* SEGREGATE */
#ifdef NOTDEF
if (!ni) return(ENXIO);
#endif
if (uio->uio_resid<ETHERHDRSIZE) return(EINVAL);
if (uio->uio_resid>if_mtu(ni)+ETHERHDRSIZE) return(EMSGSIZE);
if (!(nb=if_getbuf(ni))) return(ENOBUFS);
error=uiomove((caddr_t)&dst.sa_data, ETHERHDRSIZE, UIO_WRITE, uio);
if (error==0) {
unsigned int n, nbmax;
nbmax=nb_size(nb);
n=uio->uio_resid;
if (n>nbmax) error=EMSGSIZE;
else {
(void)nb_shrink_bot(nb, nbmax-n);
error=uiomove((caddr_t)nb_map(nb), (int)n, UIO_WRITE, uio);
if (error==0) {
register int s;
switch (ntohs(((struct ether_header *)&dst.sa_data)->
ether_type)) {
#ifdef SEGREGATE
case ETHERTYPE_AARP:
if (dev&64) goto badtype;
goto typeok;
case ETHERTYPE_ET1:
if (dev&128) goto badtype;
typeok:
#else
case ETHERTYPE_ET1:
case ETHERTYPE_AARP:
#endif /* SEGREGATE */
dst.sa_family=AF_UNSPEC;
s=splnet();
error=if_output(ni, nb, (void *)&dst);
(void)splx(s);
/* if_output _nearly_ always frees its netbuf (grr!) */
if (error!=ENXIO) return(error);
break;
default:
#ifdef SEGREGATE
badtype:
#endif /* SEGREGATE */
error=EADDRNOTAVAIL;
break;
}
}
}
}
nb_free(nb);
return(error);
}
/* stuff device name and unit in an ifreq a la SIOCGIFCONF */
static void
stuffname(netif_t ni, struct ifreq *ifrp)
{
register char *o, *e;
register const char *i;
o=ifrp->ifr_name; e=o+IFNAMSIZ-2;
i=if_name(ni);
if (i) {
while (o<e&&*i) *o++= *i++;
*o++=if_unit(ni)+'0'; /* no one has 11 Ethernet interfaces :-) */
}
*o='\0';
}
/* The usual device controls and a few surprises.
* A few things one normally associates with sockets work here,
* but with slightly different semantics. In particular,
* (struct ifreq *)->ifr_name is an output parameter.
*
* There should be some way to retrieve statistics (e.g. queue drops).
*/
/*ARGSUSED*/
int
et1ioctl(dev_t dev, int cmd, caddr_t data, int flag)
{
register netif_t ni;
int error;
off_t n;
int s;
#ifdef SEGREGATE
s=dev&0x3f;
if (s>=instance.et1nif) return(ENXIO);
ni=instance.et1if[s];
#else
if (minor(dev)>=instance.et1nif) return(ENXIO);
ni=instance.et1if[minor(dev)];
#endif /* SEGREGATE */
#ifdef NOTDEF
if (!ni) return(ENXIO);
#endif
error=0;
switch (cmd) {
case FIONREAD:
s=splimp();
#ifdef SEGREGATE
n=(dev&128) ? VENET1_ANREAD(ni) : VENET1_NREAD(ni);
#else
n=VENET1_NREAD(ni);
#endif /* SEGREGATE */
(void)splx(s);
*(off_t *)data=n; /* <sys/ioctl.h> says int?!? */
break;
case FIONBIO:
#ifdef SEGREGATE
if (dev&128) {
if (*(int *)data) VENET1_STATE_BIS(ni, ET1_ANBIO);
else VENET1_STATE_BIC(ni, ET1_ANBIO);
}
else
#endif /* SEGREGATE */
if (*(int *)data) VENET1_STATE_BIS(ni, ET1_NBIO);
else VENET1_STATE_BIC(ni, ET1_NBIO);
break;
case FIOASYNC:
#ifdef SEGREGATE
if (dev&128) {
if (*(int *)data) VENET1_STATE_BIS(ni, ET1_AASYNC);
else VENET1_STATE_BIC(ni, ET1_AASYNC);
}
else
#endif /* SEGREGATE */
if (*(int *)data) VENET1_STATE_BIS(ni, ET1_ASYNC);
else VENET1_STATE_BIC(ni, ET1_ASYNC);
break;
case TIOCGPGRP:
#ifdef SEGREGATE
*(int *)data=(dev&128) ? VENET1_APGRP(ni) : VENET1_PGRP(ni);
#else
*(int *)data=VENET1_PGRP(ni);
#endif /* SEGREGATE */
break;
case TIOCSPGRP:
if (*(int *)data>0&&!pfind(*(int *)data)) return(ESRCH);
switch (*(int *)data) { /* EPS' paranoia */
case -2: /* mach_init */
case -1: /* various daemons */
case 1: /* init */
case 2: /* mach_init */
case 3: /* kern_loader */
if (!suser()) return(EPERM);
break;
default:
/* I could be more obnoxious and only allow my pid and my pgrp */
break;
}
#ifdef SEGREGATE
if (dev&128) VENET1_APGRP_SET(ni, (short)*(int *)data);
else
#endif
VENET1_PGRP_SET(ni, (short)*(int *)data);
break;
case SIOCGIFADDR:
stuffname(ni, (struct ifreq *)data);
((struct ifreq *)data)->ifr_addr.sa_family=AF_UNSPEC;
bcopy((void *)VENET1_ENADDRP(ni)->ether_addr_octet, (void *)
((struct ether_header *)&((struct ifreq *)data)->ifr_addr.sa_data)->
ether_shost, sizeof (struct ether_addr));
#ifdef SEGREGATE
if (dev&64) ((struct ether_header *)
&((struct ifreq *)data)->ifr_addr.sa_data)->
ether_type=ETHERTYPE_ET1;
else if (dev&128) ((struct ether_header *)
&((struct ifreq *)data)->ifr_addr.sa_data)->
ether_type=ETHERTYPE_AARP;
#endif
break;
case SIOCGIFFLAGS:
stuffname(ni, (struct ifreq *)data);
((struct ifreq *)data)->ifr_flags=if_flags(ni);
break;
case SIOCGIFBRDADDR:
stuffname(ni, (struct ifreq *)data);
((struct ifreq *)data)->ifr_addr.sa_family=AF_UNSPEC;
bcopy((void *)etherbroadcastaddr, (void *)
((struct ether_header *)&((struct ifreq *)data)->ifr_broadaddr.sa_data)->
ether_dhost, sizeof (struct ether_addr));
#ifdef SEGREGATE
if (dev&64) ((struct ether_header *)
&((struct ifreq *)data)->ifr_addr.sa_data)->
ether_type=ETHERTYPE_ET1;
else if (dev&128) ((struct ether_header *)
&((struct ifreq *)data)->ifr_addr.sa_data)->
ether_type=ETHERTYPE_AARP;
#endif
break;
case SIOCGIFMTU:
stuffname(ni, (struct ifreq *)data);
((struct ifreq *)data)->ifr_mtu=if_mtu(ni);
break;
default:
switch (((cmd)>>8)&0xff) {
case 't':
error=ENOTTY;
break;
case 'i':
case 'r':
error=EINVAL;
break;
default:
error=if_ioctl(ni, cmd, data);
break;
}
break;
}
return(error);
}
/* Lame select() routine. Only read does anything useful. */
int
et1select(register dev_t dev, int rw)
{
register netif_t ni;
int s, ret;
#ifdef SEGREGATE
s=dev&0x3f;
if (s>=instance.et1nif) return(ENXIO);
ni=instance.et1if[s];
#else
if (minor(dev)>=instance.et1nif) return(ENXIO);
ni=instance.et1if[minor(dev)];
#endif
#ifdef NOTDEF
if (!ni) return(ENXIO);
#endif
ret=0;
s=splimp();
switch (rw) {
case FREAD:
#ifdef SEGREGATE
if (dev&128) {
if (VENET1_ANREAD(ni)>0L) ret=1;
else {
#ifdef MACH_USER_API
if (selthreadcache(&VENET1_PRIVATE(ni)->vp_aselr))
VENET1_STATE_BIS(ni, ET1_ARCOLL);
#else
register thread_t t=VENET1_PRIVATE(ni)->vp_aselr;
if (t&&t->wait_event==(int)&selwait)
VENET1_STATE_BIS(ni, ET1_ARCOLL);
else VENET1_PRIVATE(ni)->vp_aselr=current_thread();
#endif
}
}
else
#endif /* SEGREGATE */
if (VENET1_NREAD(ni)>0L) ret=1;
else {
#ifdef MACH_USER_API
if (selthreadcache(&VENET1_PRIVATE(ni)->vp_selr))
VENET1_STATE_BIS(ni, ET1_RCOLL);
#else
register thread_t t=VENET1_PRIVATE(ni)->vp_selr;
if (t&&t->wait_event==(int)&selwait)
VENET1_STATE_BIS(ni, ET1_RCOLL);
else VENET1_PRIVATE(ni)->vp_selr=current_thread();
#endif
}
break;
case FWRITE:
#ifndef NOTDEF
ret=1; /* no support for nonblocking write (yet?) */
#else /* NOTDEF */
if (!ret) {
#ifdef MACH_USER_API
if (selthreadcache(&VENET1_PRIVATE(ni)->vp_selw))
VENET1_STATE_BIS(ni, ET1_WCOLL);
#else
register thread_t t=VENET1_PRIVATE(ni)->vp_selw;
if (t&&t->wait_event==(int)&selwait)
VENET1_STATE_BIS(ni, ET1_WCOLL);
else VENET1_PRIVATE(ni)->vp_selw=current_thread();
#endif
}
#endif /* NOTDEF */
break;
}
(void)splx(s);
return(ret);
}
/* most of what follows is cribbed from _Writing Loadable Kernel Servers_ */
static int
venet1_output(netif_t ifp, netbuf_t nb, void *addr)
{
struct sockaddr *dst=(struct sockaddr *)addr;
struct ether_header eh;
int error;
switch (dst->sa_family) {
case AF_UNSPEC:
bcopy(dst->sa_data, &eh, sizeof(eh));
break;
default:
nb_free(nb);
return (EAFNOSUPPORT);
}
nb_grow_top(nb, ETHERHDRSIZE);
nb_write(nb, ETYPEOFFSET, sizeof(eh.ether_type), (void *)&eh.ether_type);
error=if_output(VENET1_RIF(ifp), nb, (void *)&eh.ether_dhost);
if (error==0) if_opackets_set(ifp, if_opackets(ifp) + 1);
else if_oerrors_set(ifp, if_oerrors(ifp) + 1);
return (error);
}
static int
venet1_control(netif_t ifp, const char *command, void *data)
{
if (strcmp(command, IFCONTROL_AUTOADDR)==0||
strcmp(command, IFCONTROL_SETADDR)==0)
return (EAFNOSUPPORT);
else {
/*
* Let lower layer handle
*/
return (if_control(VENET1_RIF(ifp), command, data));
}
return (0);
}
static netbuf_t
venet1_getbuf(netif_t ifp)
{
netbuf_t nb;
nb=if_getbuf(VENET1_RIF(ifp));
if (nb) nb_shrink_top(nb, ETHERHDRSIZE);
return(nb);
}
/* inet_queue() analog that places incoming datagram on private queue */
/* [based on BSD loopback interface driver] */
/*
* BEGIN: BSD copyrighted material
*/
/*
* Copyright (c) 1982, 1986 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by the University of California, Berkeley. The name of the
* University may not be used to endorse or promote products derived
* from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
*/
static void
et1_queue(netif_t netif, netbuf_t netbuf) {
register struct mbuf *m0;
register struct ifqueue *ifq;
register int s;
off_t n;
m0=(struct mbuf *)netbuf; /* netbufs are really loaned mbufs! */
ifq=VENET1_INTRQP(netif);
n=(off_t)nb_size(netbuf);
s=splimp();
if (!IF_QFULL(ifq)) {
#ifndef FASTER_QUEUE
/*
* Place interface pointer before the data
* for the receiving protocol.
*/
if (m0->m_off<=MMAXOFF&&m0->m_off>=MMINOFF+sizeof (netif_t)) {
m0->m_off-=sizeof (netif_t);
m0->m_len+=sizeof (netif_t);
} else {
register struct mbuf *m;
MGET(m, M_DONTWAIT, MT_HEADER);
if (!m) goto nobufs;
m->m_off=MMINOFF;
m->m_len=sizeof (netif_t);
m->m_next=m0;
m0=m;
}
*mtod(m0, netif_t *)=netif;
#endif
IF_ENQUEUE(ifq, m0);
VENET1_PRIVATE(netif)->vp_nread+=n;
if (VENET1_PRIVATE(netif)->vp_selr) {
(void)selwakeup(VENET1_PRIVATE(netif)->vp_selr,
VENET1_STATE(netif)&ET1_RCOLL);
#ifdef MACH_USER_API
selthreadclear(&VENET1_PRIVATE(netif)->vp_selr);
#else
VENET1_PRIVATE(netif)->vp_selr=THREAD_NULL;
#endif
VENET1_STATE_BIC(netif, ET1_RCOLL);
}
if (VENET1_STATE(netif)&ET1_WAKE) {
VENET1_STATE_BIC(netif, ET1_WAKE);
wakeup((caddr_t)ifq);
}
if (VENET1_STATE(netif)&ET1_ASYNC) {
register struct proc *p;
register short pgrp;
pgrp=VENET1_PGRP(netif);
if (pgrp<0) gsignal(-pgrp, SIGIO);
else if (pgrp>0&&(p=pfind(pgrp))!=0) psignal(p, SIGIO);
#ifdef NOTDEF
VENET1_STATE_BIC(netif, ET1_ASYNC); /* hmm... */
#endif
}
}
else {
IF_DROP(ifq);
#ifndef FASTER_QUEUE
nobufs:
#endif
m_freem(m0);
}
(void)splx(s);
}
#ifdef SEGREGATE
/* "It's like deja vu all over again!" */
static void
et1_aqueue(netif_t netif, netbuf_t netbuf) {
register struct mbuf *m0;
register struct ifqueue *ifq;
register int s;
off_t n;
m0=(struct mbuf *)netbuf; /* netbufs are really loaned mbufs! */
ifq=VENET1_AINTRQP(netif);
n=(off_t)nb_size(netbuf);
s=splimp();
if (!IF_QFULL(ifq)) {
#ifndef FASTER_QUEUE
/*
* Place interface pointer before the data
* for the receiving protocol.
*/
if (m0->m_off<=MMAXOFF&&m0->m_off>=MMINOFF+sizeof (netif_t)) {
m0->m_off-=sizeof (netif_t);
m0->m_len+=sizeof (netif_t);
} else {
register struct mbuf *m;
MGET(m, M_DONTWAIT, MT_HEADER);
if (!m) goto nobufs;
m->m_off=MMINOFF;
m->m_len=sizeof (netif_t);
m->m_next=m0;
m0=m;
}
*mtod(m0, netif_t *)=netif;
#endif
IF_ENQUEUE(ifq, m0);
VENET1_PRIVATE(netif)->vp_anread+=n;
if (VENET1_PRIVATE(netif)->vp_aselr) {
(void)selwakeup(VENET1_PRIVATE(netif)->vp_aselr,
VENET1_STATE(netif)&ET1_ARCOLL);
#ifdef MACH_USER_API
selthreadclear(&VENET1_PRIVATE(netif)->vp_aselr);
#else
VENET1_PRIVATE(netif)->vp_aselr=THREAD_NULL;
#endif
VENET1_STATE_BIC(netif, ET1_ARCOLL);
}
if (VENET1_STATE(netif)&ET1_AWAKE) {
VENET1_STATE_BIC(netif, ET1_AWAKE);
wakeup((caddr_t)ifq);
}
if (VENET1_STATE(netif)&ET1_AASYNC) {
register struct proc *p;
register short pgrp;
pgrp=VENET1_APGRP(netif);
if (pgrp<0) gsignal(-pgrp, SIGIO);
else if (pgrp>0&&(p=pfind(pgrp))!=0) psignal(p, SIGIO);
#ifdef NOTDEF
VENET1_STATE_BIC(netif, ET1_AASYNC); /* hmm... */
#endif
}
}
else {
IF_DROP(ifq);
#ifndef FASTER_QUEUE
nobufs:
#endif
m_freem(m0);
}
(void)splx(s);
}
#endif /* SEGREGATE */
/*
* END: BSD copyrighted material
*/
static int
venet1_input(netif_t ifp, netif_t rifp, netbuf_t nb, void *extra)
{
unsigned short etype;
/* Do we want packets from this driver? */
if (VENET1_RIF(ifp)!=rifp) return (EAFNOSUPPORT);
/*
* Check fields in the packet to see whether they match
* the protocol we understand.
*/
nb_read(nb, ETYPEOFFSET, sizeof(etype), &etype);
etype=ntohs(etype);
#ifdef NOTDEF
/*
* We don't handle ethernet trailer protocol.
*/
if (etype >= ETHERTYPE_TRAIL &&
etype < ETHERTYPE_TRAIL + ETHERTYPE_NTRAILER)
return (EAFNOSUPPORT);
/* Nor 802.3 crap */
if ((unsigned)etype<=1500) return (EAFNOSUPPORT);
#endif
switch (etype) {
#ifdef SEGREGATE
case ETHERTYPE_ET1:
/* if no one has us open, pretend we're not here */
switch (VENET1_STATE(ifp)&(ET1_ISOPEN|ET1_EOPEN|ET1_AOPEN)) {
case ET1_ISOPEN:
case ET1_ISOPEN|ET1_EOPEN:
case ET1_ISOPEN|ET1_EOPEN|ET1_AOPEN:
if_ipackets_set(ifp, if_ipackets(ifp) + 1);
et1_queue(ifp, nb);
break;
default:
return(EAFNOSUPPORT);
}
break;
case ETHERTYPE_AARP:
/* if no one has us open, pretend we're not here */
switch (VENET1_STATE(ifp)&(ET1_ISOPEN|ET1_EOPEN|ET1_AOPEN)) {
case ET1_ISOPEN:
if_ipackets_set(ifp, if_ipackets(ifp) + 1);
et1_queue(ifp, nb);
break;
case ET1_ISOPEN|ET1_AOPEN:
case ET1_ISOPEN|ET1_EOPEN|ET1_AOPEN:
if_ipackets_set(ifp, if_ipackets(ifp) + 1);
et1_aqueue(ifp, nb);
break;
default:
return(EAFNOSUPPORT);
}
break;
#else
case ETHERTYPE_ET1:
case ETHERTYPE_AARP:
/* if no one has us open, pretend we're not here */
if (!(VENET1_STATE(ifp)&ET1_ISOPEN)) return(EAFNOSUPPORT);
if_ipackets_set(ifp, if_ipackets(ifp) + 1);
et1_queue(ifp, nb);
break;
#endif /* SEGREGATE */
default:
/*
* Do not free buf: let others handle it
*/
return (EAFNOSUPPORT);
}
return (0);
}
/* Instantiate a new minor to correspond to a real device*/
static void
venet1_attach(void *private, netif_t rifp)
{
netif_t ifp;
const char *name;
unsigned int unit;
void *ifprivate;
int s;
if (strcmp(if_type(rifp), IFTYPE_ETHERNET)!=0) return;
if (instance.et1nif<ET1_MAXMIN) {
ifprivate=(void *)kalloc(sizeof(venet1_private_t));
bzero(ifprivate, sizeof(venet1_private_t));
name=if_name(rifp);
unit=if_unit(rifp);
ifp=if_attach(NULL, venet1_input, venet1_output,
venet1_getbuf, venet1_control, name, unit, IFTYPE_ET1,
if_mtu(rifp), IFF_BROADCAST|IFF_NOTRAILERS, NETIFCLASS_VIRTUAL,
ifprivate);
((venet1_private_t *)if_private(ifp))->vp_rifp=rifp;
((venet1_private_t *)if_private(ifp))->vp_intrq.ifq_maxlen=et1qmaxlen;
#ifdef SEGREGATE
((venet1_private_t *)if_private(ifp))->vp_aintrq.ifq_maxlen=et1qmaxlen;
#endif
if_control(rifp, IFCONTROL_GETADDR, VENET1_ENADDRP(ifp));
s=splimp();
if_flags_set(ifp, if_flags(ifp)|IFF_UP);
(void)splx(s);
instance.et1if[instance.et1nif++]=ifp;
if (et1verbose) printf(
"ET1 protocol access enabled for interface %s%u, type \"%s\"\n",
name, unit, IFTYPE_ETHERNET);
}
return;
}
void
venet1_config(void)
{
/*
* You can't use if_registervirtual() in any protocol module
* that can be unloaded, otherwise the next time you load a
* network device driver, if_attach() will attempt to call
* your [nonexistent] attach routine, with disastrous
* results. 3.0's "fix" is the addition of the DETACH
* Load Command, but what's really needed is an
* if_unregistervirtual().
*/
#ifndef NOTDEF
#ifdef MACH_USER_API
register netif_t ni;
#else
register struct ifnet *ifp;
#endif
#endif
instance.et1nif=0;
#ifdef NOTDEF
if_registervirtual(venet1_attach, NULL);
#else
/*
* Call back our attach routine for each network interface,
* but don't register. This means that we can't connect with
* any network devices that come along later, but that's
* better than crashing.
*/
#ifdef MACH_USER_API
for (ni=iflist_first();ni;ni=iflist_next(ni))
if (if_class(ni)==NETIFCLASS_REAL) venet1_attach(NULL, ni);
#else
for (ifp=ifnet;ifp;ifp=ifp->if_next)
if (ifp->if_class==NETIFCLASS_REAL)
venet1_attach(NULL, (netif_t)ifp);
#endif
#endif
}
void
venet1_detach(void)
{
#ifdef MACH_USER_API
netif_t ni;
/* 3.0 recycles used netifs */
for (ni=iflist_first();ni;) {
netif_t nin;
nin=iflist_next(ni);
if (if_type(ni)==IFTYPE_ET1) {
if (et1verbose)
printf("ET1 protocol access disabled for interface %s%u.\n",
if_name(ni), if_unit(ni));
kfree(if_private(ni), sizeof(venet1_private_t));
if_detach(ni);
}
ni=nin;
}
#else
register struct ifnet **ifpp;
int s;
/* pre-3.0 is a bit more fragile... */
s=splimp();
for (ifpp= &ifnet;*ifpp;) {
register struct ifnet *ifp;
ifp= *ifpp;
if (ifp->if_type==IFTYPE_ET1) {
if (et1verbose)
printf("ET1 protocol access disabled for interface %s%u.\n",
if_name((netif_t)ifp), if_unit((netif_t)ifp));
*ifpp=ifp->if_next;
kfree(if_private((netif_t)ifp), sizeof(venet1_private_t));
kfree((void *)ifp, sizeof *ifp);
}
else ifpp= &ifp->if_next;
}
(void)splx(s);
#endif
}
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.