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.