[comp.sources.unix] v20i060: User-level interface to Ethernet, Part03/03

rsalz@uunet.uu.net (Rich Salz) (10/26/89)

Submitted-by: Alexander Dupuy <dupuy@cs.columbia.edu>
Posting-number: Volume 20, Issue 60
Archive-name: etherlib/part03

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 3 (of 3)."
# Contents:  ./ether.3n ./src/nit3open.c ./src/nit4open.c ./tests/ctp.c
#   ./tests/ethertest.c
# Wrapped by rsalz@papaya.bbn.com on Wed Oct 25 16:37:32 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f './ether.3n' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'./ether.3n'\"
else
echo shar: Extracting \"'./ether.3n'\" \(7564 characters\)
sed "s/^X//" >'./ether.3n' <<'END_OF_FILE'
X.TH ETHER 3N  "29 June 1989"
X.SH NAME
Xether \- raw ethernet access functions
X.SH SYNOPSIS
X.LP
X.B #include <ether.h>
X.LP
X.nf
X.ft B
Xtypedef union etheraddr {
X	char bytes[6];
X	char shorts[3];
X} ether_addr;
X
Xextern ether_addr ether_bcast_addr;
X
Xtypedef struct etherpacket {
X	ether_addr dest;
X	ether_addr src;
X	char type[2];
X	unsigned short pktlen;
X	char \(**pktbuf;
X} ether_packet;
X
Xtypedef struct ethervec {
X	ether_addr dest;
X	ether_addr src;
X	char type[2];
X	unsigned short iovcnt;
X	struct iovec \(**iov;
X} ether_vec;
X.ft R
X.fi
X.LP
X.nf
X.ft B
Xint ether_open(name, type, address)
Xchar \(**name;
Xint type;
Xether_addr \(**address;
X.ft R
X.fi
X.LP
X.nf
X.ft B
Xchar \(**\(**ether_interfaces();
X.ft R
X.fi
X.LP
X.nf
X.ft B
Xether_addr \(**ether_address(fd, address)
Xint fd;
Xether_addr \(**address;
X.ft R
X.fi
X.LP
X.nf
X.ft B
Xether_addr \(**ether_intfaddr(name, address)
Xchar \(**name;
Xether_addr \(**address;
X.ft R
X.fi
X.LP
X.nf
X.ft B
Xint ether_write(fd, packet)
Xint fd;
Xether_packet \(**packet;
X.ft R
X.fi
X.LP
X.nf
X.ft B
Xint ether_read(fd, packet)
Xint fd;
Xether_packet \(**packet;
X.ft R
X.fi
X.LP
X.nf
X.ft B
Xint ether_blocking(fd, state)
Xint fd;
Xint state;
X.ft R
X.fi
X.LP
X.nf
X.ft B
Xint ether_send_self(fd)
Xint fd;
X.ft R
X.fi
X.LP
X.nf
X.ft B
Xint ether_mcast_self(fd)
Xint fd;
X.ft R
X.fi
X.LP
X.nf
X.ft B
Xint ether_bcast_self(fd)
Xint fd;
X.ft R
X.fi
X.LP
X.nf
X.ft B
X#include <sys/types.h>
X#include <sys/uio.h>
X.ft R
X.fi
X.LP
X.nf
X.ft B
Xint ether_writev(fd, packetvec)
Xint fd;
Xether_vec \(**packetvec;
X.ft R
X.fi
X.LP
X.nf
X.ft B
Xint ether_readv(fd, packetvec)
Xint fd;
Xether_vec \(**packetvec;
X.ft R
X.fi
X.SH DESCRIPTION
X.LP
XThese functions provide access to the raw ethernet for user-level programs.  On
XSuns, they are implemented using
X.BR \s-1NIT\s0 (4p)
X(network interface tap).  While they do not provide the full
Xfunctionality of
X.SM NIT
X, these functions do run on both the socket- and streams-based 
X.SM NIT
Ximplementations.  On Ultrix systems, they are implemented using
X.SM DLI
X(data link interface).  On 4.3 BSD systems, they are implemented using the
XStanford enetfilter device driver in the user-contributed software.  These
Xfunctions are not designed to be used for ethernet monitoring, but rather for
Xprograms implementing ethernet protocols such as
X.SM RARP
X, or the Ethernet configuration test protocol. 
X.LP
XThe function ether_open returns a file descriptor for the ethernet device
Xspecified by
X.I name
X(such as "le0" or "ie1").  If no name is given, the default ethernet interface
Xis used.  Packets for the ethernet address
X.I address
Xwill be received in addition to packets for the local ethernet address and
Xbroadcasts; this is useful for multicast protocols.  Superuser privilege is
Xneeded to use this feature.  Only packets with a protocol type of
X.I type
Xwill be received or sent.  This value should be passed in host byte order, not
Xin network byte order. 
X.LP
XIn order to allow some basic ethernet monitoring capability on Sun
X.SM NIT
X-based systems and BSD enetfilter systems, if
X.I address
Xis all zeros, or if
X.I type
Xis ETHER_ALLTYPES (defined in ether.h), address and/or type matching will be
Xdisabled.  This will not work on Ultrix
X.SM DLI
X-based systems, so using the
X.SM NIT
Xinterface directly is nearly as portable, and gives better filtering and
Xbuffering. 
X.LP
XThe ether_interfaces function can be used to determine the valid interface
Xnames available.  It returns an array of strings (with the last element set to
X\s-1NULL\s0), each entry of which is an ethernet interface name valid for use
Xin ether_open. 
X.LP
XThe ether_address function returns the local ethernet address for the ethernet
Xinterface on the file descriptor
X.IR fd . 
XThe result is stored in the structure given by
X.IR address ;
Xif none is given,
X.IR malloc (3)
Xis used to allocate space. 
X.LP
XThe ether_read and ether_write functions read or write a single ethernet
Xpacket. When writing, the 
X.IR dest ,
X.I pktlen
Xand
X.I pktbuf
Xfields must be provided; the
X.I src
Xand
X.I type
Xfields are set to the local ethernet address and the ethernet type passed
Xto 
X.IR ether_open (\|). 
XWhen reading, only 
X.I pktlen
Xand
X.I pktbuf
Xare looked at; the
X.IR src ,
X.IR dest ,
Xand
X.I type
Xfields are set from the packet on a successful return. If
X.I pktbuf
Xis non-\s-1NULL\s0, up to 
X.I pktlen
Xbytes will be read in to the buffer space specified by
X.IR pktbuf . 
XIf 
X.I pktbuf
Xis \s-1NULL\s0, 
X.I pktlen
Xis ignored and
X.IR malloc (3)
Xis used to allocate enough space for the packet.  The value of
X.I pktlen
Xis set to the lesser of its original value and the actual packet size
X(exclusive of the ethernet header).  The size of the original ethernet packet
Xis returned. 
X.LP
XThe ether_readv and ether_writev functions operate in the same way as
Xether_read and ether_write, but use an interface like that provided by the
Xreadv and writev system calls.  The
X.I iov
Xand
X.I iovcnt
Xfields specify the length and location of an array of struct iovec.  Each iovec
Xentry specifies the base address and length of an area in memory where data is
Xread from or written to.
X.LP
XThe ether_blocking function can be used to make the ethernet file descriptor
X.I fd
Xnon-blocking.  If 
X.I state
Xis zero, the file descriptor is set non-blocking with
X.IR fcntl (2);
Xif 
X.I state
Xis non-zero, the file descriptor is set to be blocking. 
X.LP
XThe ether_send_self, ether_mcast_self and ether_bcast_self functions return 1
Xif the interface will receive packets that it sends to itself, multicasts or
Xbroadcasts, respectively, and will return 0 if the interface will not receive
Xsuch packets.
X.LP
XIn addition, there are a number of auxiliary routines for manipulating ethernet
Xaddresses.  Although they are not documented here, the ether.h header file
Xdescribes them.
X.SH DIAGNOSTICS
XAll functions which return an integer value return a negative number if there
Xis an error.  A zero return indicates no error.  The ether_interfaces and
Xether_addresses functions return \s-1NULL\s0 if there is an error.  All
Xfunctions leave an error code in the external variable
X.I errno
Xif an error occurs.  If an attempt to read an ethernet file descriptor which
Xhas been set to non-blocking mode fails because the operation would block,
Xerrno will be set to EAGAIN (not EWOULDBLOCK). 
X.SH BUGS
XDoesn't support IEEE 802.3 based protocols. 
X.LP
XBecause of restrictions in the
X.SM DLI
Xand socket based
X.SM NIT
Ximplementations, the ether_open function may require superuser privilege on
Xsome machines.  On SunOS 4.0 machines, it is sufficient to be able to open
Xthe /dev/nit special file.  On machines with the Stanford enetfilter, it is
Xsufficient to be able to open the interface's special file in /dev/enet/.
X.LP
XDue to limitations in the Stanford enetfilter, using select to determine if
Xan ethernet device is ready to accept writes will always return true.  Also,
Xusing ether_blocking(0) will not prevent blocking on writes.  Since writes
Xalmost always complete quickly, this isn't a major problem, but applications
Xmay block if the ethernet is jammed.
X.LP
XThe basic monitoring capabilities provided are a hack.  Something like
X.SM NIT
Xshould be standardized as a monitoring interface. 
X.LP
XThe auxiliary address routines should be better documented.
X.SH SEE ALSO
X.BR nit (4p),
X.BR nit_if (4p),
X.BR nit_pf (4p),
X.BR enet (4),
X.BR fcntl (2),
X.BR fcntl (5)
X.SH AUTHORS
XAlexander Dupuy, Robert Mokry, Columbia University Computer Science Department.
XThanks to Charlie Kim for ideas stolen from his ethernet access implementation.
XSome of the auxiliary ethernet address routines are derived from code written
Xby Philip Budne, Boston University Computer Science Department.
END_OF_FILE
if test 7564 -ne `wc -c <'./ether.3n'`; then
    echo shar: \"'./ether.3n'\" unpacked with wrong size!
fi
# end of './ether.3n'
fi
if test -f './src/nit3open.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'./src/nit3open.c'\"
else
echo shar: Extracting \"'./src/nit3open.c'\" \(6958 characters\)
sed "s/^X//" >'./src/nit3open.c' <<'END_OF_FILE'
X/* $Id: nit3open.c,v 2.2 89/10/24 17:53:27 dupuy Exp $ */
X
X#include <sys/param.h>			/* NOFILE */
X#include <sys/time.h>			/* timeval */
X#include <sys/socket.h>			/* sockaddr_nit (sockaddr) */
X#include <sys/ioctl.h>			/* ioctl */
X
X#include <net/nit.h>			/* sockaddr_nit */
X#include <netinet/in.h>			/* htons */
X
X#include <strings.h>			/* strncpy */
X#include <errno.h>			/* EWOULDBLOCK/EINVAL */
X
X#include "libether.h"
X
Xstruct timeval *ether_timeout;
Xstruct timeval *ether_timestamp;
X
Xstatic ether_addr promiscuous;
X
Xstatic fd_set multicast;
Xstatic gotlocal;
Xstatic ether_addr local_addr;
X
Xunsigned _ether_types[NOFILE];
Xether_addr _ether_multi_addrs[NOFILE];
X
X#define ether_type (_ether_types[fd])
X#define multi_addr (_ether_multi_addrs[fd])
X
X/*
X * Returns file descriptor for ethernet device by name ("ie0", "le0", etc.).
X * If name is NULL, uses primary ethernet interface.  Will only receive
X * packets of type specified.  Will receive packets for the ethernet address
X * specified, or local ethernet address if NULL.  If there is an error,
X * returns (-1) and the appropriate value is left in errno.  Normal return
X * status zero. Requires superuser privilege.
X */
X
Xint
Xether_open (name, type, address)
Xchar *name;
Xunsigned type;
Xether_addr *address;
X{
X    int fd;
X    struct sockaddr_nit snit;
X    struct nit_ioc nioc;
X    char **interfaces;
X    int saved_errno;
X
X    if (name == 0)
X    {					/* get default ethernet interface */
X	interfaces = ether_interfaces ();
X	if (interfaces == 0 || *interfaces == 0)
X	    return (-1);
X
X	name = *interfaces;		/* just use the first name in list */
X    }
X
X    if ((fd = socket (AF_NIT, SOCK_RAW, NITPROTO_RAW)) < 0)
X    {
X#ifdef DEBUG
X	perror ("ether_open: socket");
X#endif
X	return (-1);
X    }
X
X    snit.snit_family = AF_NIT;
X    (void) strncpy (snit.snit_ifname, name, NITIFSIZ);
X
X    if (bind (fd, (struct sockaddr *) &snit, sizeof (snit)) != 0)
X    {
X	saved_errno = errno;
X#ifdef DEBUG
X	perror ("ether_open: bind");
X#endif
X	(void) close (fd);
X	errno = saved_errno;
X	return (-1);
X    }
X
X    bzero ((char *) &nioc, sizeof (nioc));
X
X    if (type != ETHER_ALLTYPES && type > ETHER_MAXTYPE)
X    {
X	(void) close (fd);
X	errno = EINVAL;
X	return (-1);
X    }
X
X    nioc.nioc_bufalign = sizeof (long);
X    nioc.nioc_chunksize = nioc.nioc_bufspace = ETHER_BUFSIZ;
X    nioc.nioc_typetomatch =
X	(type == ETHER_ALLTYPES) ? type : htons ((u_short) type);
X    nioc.nioc_snaplen = ETHER_PKT + ETHER_MAX;
X
X    if (address == 0)			/* not a multicast address */
X    {
X	multicast.fds_bits[0] &= ~(1 << fd);
X	nioc.nioc_flags = NF_TIMEOUT;
X    }
X    else				/* a multicast address */
X    {
X	if (ether_cmp (address, &promiscuous))
X	{
X	    if (!ETHER_MCAST (address))
X	    {
X#ifdef DEBUG
X		(void) printf ("rejecting non-multicast address argument\n");
X#endif
X		(void) close (fd);
X		errno = EINVAL;
X		return (-1);
X	    }
X
X	    multicast.fds_bits[0] |= (1 << fd);
X	    multi_addr = *address;
X	    if (gotlocal == 0)
X		if (ether_address (fd, &local_addr) != NULL)
X		    gotlocal = 1;
X	}
X	else
X	    multicast.fds_bits[0] &= ~(1 << fd);
X
X	nioc.nioc_flags = NF_TIMEOUT | NF_PROMISC;
X    }
X
X    if (ether_timeout != 0)		/* use specified value of timeout */
X	nioc.nioc_timeout = *ether_timeout;
X    else
X    {					/* then use default value of timeout */
X	nioc.nioc_timeout.tv_sec = 0;
X	nioc.nioc_timeout.tv_usec = 10000;
X    }
X
X    if (ioctl (fd, SIOCSNIT, (char *) &nioc) != 0)
X    {
X	saved_errno = errno;
X#ifdef DEBUG
X	perror ("ether_open: ioctl SIOCSNIT");
X#endif
X	(void) close (fd);
X	errno = saved_errno;
X	return (-1);
X    }
X
X    ether_type = type;
X    return (fd);
X}
X
X
X#ifdef DEBUG
X
Xvoid
Xdump_nit_hdr (nitbuf)
Xstruct nit_hdr *nitbuf;
X{
X    switch (nitbuf->nh_state)
X    {
X      case NIT_QUIET:
X	(void) printf ("ether_read: nit quiet\n");
X	break;
X
X      case NIT_CATCH:
X	(void) printf ("ether_read: nit catch: size %d\n", nitbuf->nh_datalen);
X	break;
X
X      case NIT_NOMBUF:
X	(void) printf ("ether_read: nit no mbufs: %d lost\n",
X		       nitbuf->nh_dropped);
X	break;
X
X      case NIT_NOCLUSTER:
X	(void) printf ("ether_read: nit no mclusters: %d lost\n",
X		       nitbuf->nh_dropped);
X	break;
X
X      case NIT_NOSPACE:
X	(void) printf ("ether_read: nit no bufspace: %d lost\n",
X		       nitbuf->nh_dropped);
X	break;
X
X      case NIT_SEQNO:
X	(void) printf ("ether_read: nit sequence # %d\n", nitbuf->nh_seqno);
X	break;
X
X      default:
X	(void) printf ("ether_read: bad nit header state: %d\n",
X		       nitbuf->nh_state);
X	break;
X    }
X}
X
X#endif
X
X
X/*
X * Reads and returns a single packet, filling in all fields.  If pktbuf is
X * NULL, a buffer is allocated for it.	If pktbuf is not NULL, the function
X * assumes that pktbuf is large enough to hold pktlen bytes.  Since read() may
X * return several packets at a time, ether_read() has to buffer them as well.
X * Since socket based nit doesn't handle multicast addresses, we have to check
X * them at the user level.
X *
X * NOTE - the buffering code is non-reentrant if the same fd is used.
X */
X
Xstatic long ether_buf[NOFILE][ETHER_BUFSIZ / sizeof (long)];
Xstatic int ether_buflen[NOFILE];
Xstatic char *ether_bufptr[NOFILE];
X
X#define buf	(ether_buf[fd])
X#define buflen	(ether_buflen[fd])
X#define bufptr	(ether_bufptr[fd])
X
X_ether_next_packet (fd, bufp)
Xint fd;
Xchar **bufp;
X{
X    struct nit_hdr *nitbuf;
X    ether_addr *pbuf;
X
X    if (bufptr == 0)			/* easier than static initialization */
X	bufptr = (char *) buf;
X
X    for (;;)
X    {
X	if ((bufptr - (char *) buf) >= buflen)
X	{				/* then buffer is used up */
X	    buflen = read (fd, (char *) buf, ETHER_BUFSIZ);
X	    bufptr = (char *) buf;
X	    if (buflen <= 0)
X	    {
X		if (errno == EWOULDBLOCK)
X		    errno = EAGAIN;
X		return (-1);
X	    }
X	}
X
X	/*
X	 * This assignment is safe, since nit guarantees requested alignment
X	 */
X
X	nitbuf = (struct nit_hdr *) bufptr;
X	while (nitbuf->nh_state != NIT_CATCH)
X	{
X#ifdef DEBUG
X	    dump_nit_hdr (nitbuf);
X#endif
X	    nitbuf++;
X	    if ((char *) nitbuf - (char *) buf > buflen)
X	    {
X#ifdef DEBUG
X		(void) printf ("truncated nit_hdr struct in buffer\n");
X#endif
X		continue;
X	    }
X	}
X#ifdef DEBUG
X	dump_nit_hdr (nitbuf);
X#endif
X	/*
X	 * Set bufptr to next nit_hdr, pbuf to start of ether packet
X	 */
X
X	bufptr = ((char *) (pbuf = (ether_addr *) (nitbuf + 1)))
X	    + nitbuf->nh_datalen + pad (nitbuf->nh_datalen);
X
X	/*
X	 * Check for local, broadcast or multicast packet 
X	 */
X
X	if ((multicast.fds_bits[0] & (1 << fd))
X	    && (ETHER_MCAST (pbuf) ? (ether_cmp (pbuf, &ether_bcast_addr)
X				      && ether_cmp (pbuf, &multi_addr))
X		: ether_cmp (pbuf, &local_addr)))
X	{
X#ifdef DEBUG
X	    (void) printf ("discarding incorrectly addressed packet\n");
X#endif
X	    continue;
X	}
X
X	if (nitbuf->nh_wirelen > nitbuf->nh_datalen)
X	{
X#ifdef DEBUG
X	    (void) printf ("discarding truncated packet\n");
X#endif
X	    continue;
X	}
X	break;
X    }
X
X    if (nitbuf->nh_wirelen - ETHER_PKT < 0)
X	errno = EBADMSG;
X    else
X	*bufp = (char *) pbuf;
X
X    ether_timestamp = &nitbuf->nh_timestamp;
X
X    return (nitbuf->nh_wirelen - ETHER_PKT);
X}
END_OF_FILE
if test 6958 -ne `wc -c <'./src/nit3open.c'`; then
    echo shar: \"'./src/nit3open.c'\" unpacked with wrong size!
fi
# end of './src/nit3open.c'
fi
if test -f './src/nit4open.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'./src/nit4open.c'\"
else
echo shar: Extracting \"'./src/nit4open.c'\" \(5614 characters\)
sed "s/^X//" >'./src/nit4open.c' <<'END_OF_FILE'
X/* $Id: nit4open.c,v 2.1 89/10/23 15:42:59 dupuy Exp $ */
X
X#include <sys/file.h>			/* O_RDWR */
X#include <sys/stropts.h>		/* RMSGD */
X#include <sys/types.h>			/* NIOCBIND (u_long) */
X#include <sys/time.h>			/* NIOCBIND (timeval) */
X#include <sys/socket.h>			/* ifreq (sockaddr) */
X
X#include <net/if.h>			/* ifreq */
X#include <net/nit_if.h>			/* NIOCBIND */
X#include <net/nit_pf.h>			/* NIOCSETF */
X#include <net/packetfilt.h>		/* packetfilt */
X
X#include <netinet/in.h>			/* htons */
X
X#include <strings.h>			/* strncpy */
X#include <errno.h>			/* EMFILE/EINVAL */
X
X#include "libether.h"
X
X#ifndef NIT_DEV
X#define NIT_DEV "/dev/nit"
X#endif
X
Xstatic ether_addr promiscuous;
X
Xunsigned _ether_types[FD_SETSIZE];
X
X#define ether_type (_ether_types[fd])
X
Xstatic int
Xnioctl (fd, cmd, ptr)
Xint fd;
Xint cmd;
Xchar *ptr;
X{
X    int saved_errno;
X
X    if (ioctl (fd, cmd, ptr) < 0)
X    {
X	saved_errno = errno;
X#ifdef DEBUG
X	switch (cmd)
X	{
X	  case I_SRDOPT:
X	    perror ("ether_open: ioctl (I_SRDOPT)");
X	    break;
X	  case I_PUSH:
X	    perror ("ether_open: ioctl (I_PUSH)");
X	    break;
X	  case NIOCSETF:
X	    perror ("ether_open: ioctl (NIOCSETF)");
X	    break;
X	  case NIOCBIND:
X	    perror ("ether_open: ioctl (NIOCBIND)");
X	    break;
X	  case NIOCSFLAGS:
X	    perror ("ether_open: ioctl (NIOCSFLAGS)");
X	    break;
X	  default:
X	    perror ("ether_open: ioctl (????)");
X	    break;
X	}
X#endif
X	(void) close (fd);
X	errno = saved_errno;
X	return (-1);
X    }
X
X    return (0);
X}
X
X
X/*
X * Returns file descriptor for ethernet device by name ("ie0", "le0", etc.).
X * If name is NULL, uses primary ethernet interface.  Will only receive
X * packets of type specified.  Will receive packets for the ethernet address
X * specified, or local ethernet address if NULL.  If there is an error,
X * returns (-1) and the appropriate value is left in errno.  Normal return
X * status zero. Requires superuser privilege.
X */
X
Xint
Xether_open (name, type, address)
Xchar *name;
Xunsigned type;
Xether_addr *address;
X{
X    int fd;
X    struct ifreq ifr;
X    struct packetfilt filter;
X    unsigned short *fptr = 0;
X    char **interfaces;
X    register int i;
X
X    if (name == 0)			/* get default ethernet interface */
X    {
X	interfaces = ether_interfaces ();
X	if (interfaces == 0 || *interfaces == 0)
X	    return (-1);
X
X	name = *interfaces;		/* just use the first name in list */
X    }
X
X    if ((fd = open (NIT_DEV, O_RDWR)) < 0)
X    {
X#ifdef DEBUG
X	perror (NIT_DEV);
X#endif
X	return (-1);
X    }
X
X    if (fd >= FD_SETSIZE)		/* not worth making it work here */
X    {
X	(void) close (fd);
X	errno = EMFILE;
X	return (-1);
X    }
X
X    /* arrange to get discrete messages from the stream */
X
X    if (nioctl (fd, I_SRDOPT, (char *) RMSGD) < 0)
X	return (-1);
X
X    /* set up filter */
X
X    if (type != ETHER_ALLTYPES)		/* hack for NIT features */
X    {
X	if (type > ETHER_MAXTYPE)
X	{
X	    (void) close (fd);
X	    errno = EINVAL;
X	    return (-1);
X	}
X
X	if (nioctl (fd, I_PUSH, "pf") < 0)
X	    return (-1);
X
X	fptr = &filter.Pf_Filter[0];
X
X	*fptr++ = ENF_PUSHWORD + ETHER_TYPE / sizeof (short);
X	*fptr++ = ENF_PUSHLIT | ENF_EQ;
X	*fptr++ = htons ((u_short) type);
X
X	filter.Pf_FilterLen = fptr - &filter.Pf_Filter[0];
X	filter.Pf_Priority = 1;		/* unimportant, so long as < 2 */
X
X	if (nioctl (fd, NIOCSETF, (char *) &filter) < 0)
X	    return (-1);
X    }
X
X    /*
X     * We defer the bind until after we've pushed the filter to prevent our
X     * being flooded with extraneous packets
X     */
X
X    (void) strncpy (ifr.ifr_name, name, sizeof (ifr.ifr_name));
X
X    if (nioctl (fd, NIOCBIND, (char *) &ifr) < 0)
X	return (-1);
X
X    if (address != 0)
X    {
X	if (ether_cmp (address, &promiscuous))
X	{
X#ifndef MULTICAST
X	    ether_addr local_addr;
X#endif
X	    if (!ETHER_MCAST (address))
X	    {
X#ifdef DEBUG
X		(void) printf ("rejecting non-multicast address argument\n");
X#endif
X		(void) close (fd);
X		errno = EINVAL;
X		return (-1);
X	    }
X
X#ifdef MULTICAST
X	    /* #error not clear how to set multicast addresses */
X	    exit (-1);			/* die die die */
X#else
X
X	    /*
X	     * Enable address matching filter before we go into promiscuous
X	     * mode.  We have to do this after the bind, since we can't get
X	     * the interface address until then.
X	     */
X
X	    (void) ether_address (fd, &local_addr);
X
X	    /* may not have pushed packet filtering module yet */
X
X	    if (fptr == 0)
X		if (nioctl (fd, I_PUSH, "pf") < 0)
X		    return (-1);
X
X	    fptr = &filter.Pf_Filter[0];
X
X	    if (type != ETHER_ALLTYPES)	/* hack for NIT features */
X	    {
X		*fptr++ = ENF_PUSHWORD + ETHER_TYPE / sizeof (short);
X		*fptr++ = ENF_PUSHLIT;
X		*fptr++ = htons ((u_short) type);
X		*fptr++ = ENF_CAND;
X	    }
X
X	    for (i = 0; i < 3; i++)	/* compare addresses in 3 shorts */
X	    {
X		*fptr++ = ENF_PUSHWORD + ETHER_DST / sizeof (short) + i;
X		*fptr++ = ENF_PUSHLIT | ENF_EQ;
X		*fptr++ = address->shorts[i];
X		*fptr++ = ENF_PUSHWORD + ETHER_DST / sizeof (short) + i;
X		*fptr++ = ENF_PUSHLIT | ENF_EQ;
X		*fptr++ = ether_bcast_addr.shorts[i];
X		*fptr++ = ENF_OR;
X		*fptr++ = ENF_PUSHWORD + ETHER_DST / sizeof (short) + i;
X		*fptr++ = ENF_PUSHLIT | ENF_EQ;
X		*fptr++ = local_addr.shorts[i];
X		*fptr++ = ENF_CNOR;
X	    }
X
X	    filter.Pf_FilterLen = fptr - &filter.Pf_Filter[0];
X	    filter.Pf_Priority = 1;	/* unimportant, so long as < 2 */
X
X	    if (nioctl (fd, NIOCSETF, (char *) &filter) < 0)
X		return (-1);
X
X#endif					/* MULTICAST */
X
X	}
X
X#ifdef MULTICAST
X	else				/* only promiscuous if requested */
X#endif
X
X	{				/* go into promiscuous mode */
X	    long flag;
X
X	    flag = NI_PROMISC;
X
X	    if (nioctl (fd, NIOCSFLAGS, (char *) &flag) < 0)
X		return (-1);
X	}
X    }
X
X    ether_type = type;
X    return (fd);
X}
END_OF_FILE
if test 5614 -ne `wc -c <'./src/nit4open.c'`; then
    echo shar: \"'./src/nit4open.c'\" unpacked with wrong size!
fi
# end of './src/nit4open.c'
fi
if test -f './tests/ctp.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'./tests/ctp.c'\"
else
echo shar: Extracting \"'./tests/ctp.c'\" \(14485 characters\)
sed "s/^X//" >'./tests/ctp.c' <<'END_OF_FILE'
X/* $Id: ctp.c,v 1.3 89/10/24 17:54:23 dupuy Exp $ */
X
X/*
X * ctp - ethernet configuration test protocol program
X */
X
X/*
X * derived from ethertools/ctp.c
X *
X * Copyright (c) 1988 Philip L. Budne and The Trustees of Boston University All
X * Rights Reserved
X *
X * Permission is granted to any individual or institution to use, copy, or
X * redistribute this software so long as it is not sold for profit, provided
X * that this notice and the original copyright notices are retained.  Boston
X * University makes no representations about the suitability of this software
X * for any purpose.  It is provided "as is" without express or implied
X * warranty.
X */
X
X#include <stdio.h>
X#include <signal.h>
X#include <errno.h>
X
Xextern int errno;
X
X#include "ctp.h"
X
X#ifdef SIGABRT
X#define sigtype void
X#else
X#define sigtype int
X#endif
X
Xstruct timeval *ether_timestamp;	/* timestamp from 3.x NIT, maybe */
X
X/*
X * Ethernet related variables
X */
X
Xint nomulticast = 0;
X
X#ifdef __STDC__
Xether_addr ctpmulticast = {{0xcf, 0x0, 0x0, 0x0, 0x0, 0x0}};
X
X#else
Xether_addr ctpmulticast;
Xunsigned char bytes[6] = {0xcf, 0x0, 0x0, 0x0, 0x0, 0x0};
X
X#endif
X
Xether_addr localaddr;			/* our ethernet address */
X
X/*
X * CTP statistics
X */
X
X#define NBUCKETS 733			/* prime # of buckets */
X#define hash(addr) \
X (((addr)->shorts[1] + ((addr)->shorts[1] ^ (addr)->shorts[2])) % NBUCKETS)
X
Xint nsent = 0;
X
Xstruct
X{
X    int nrecvd;				/* total count */
X    int highseq;			/* highest seq seen */
X    int outofseq;			/* number recvd out of sequence */
X    int min;
X    int max;				/* min and max times */
X    int sum;				/* sum of times */
X
X    int valid;				/* is this a used entry */
X    ether_addr eaddr;			/* ethernet address for this count */
X}
X       stats[NBUCKETS + 1];
X
Xint lastseq = 0;
Xint seq = 0;
X
X/*
X * Program options and arguments
X */
X
Xchar *interface = NULL;			/* interface to use */
Xint count = -1;				/* number of packets to send */
Xint noreturn = 0;			/* don't force final forward back */
Xint packlen = ETHER_MIN;		/* default total packet length */
Xint slptim = 1000;			/* sleep time in ms */
X
X/*
X * Other functions and variables
X */
X
Xint pid;				/* our pid */
Xint tosend = 0;
X
Xshort swaps ();
Xsigtype resend ();
Xsigtype die ();
Xvoid printstats ();
Xctp_reply *output_packet ();
Xvoid input_packet ();
X
X
Xmain (argc, argv)
Xint argc;
Xchar *argv[];
X{
X    int efd;
X    int optind;
X    ether_addr path[MAXPATH];
X    int pathcount = 0;
X    ether_packet outpkt;
X    ctp_packet outpktbuf;
X    ctp_reply *reply;			/* pointer to ctp_reply in outpkt */
X    struct itimerval interval;
X
X    /*
X     * Get program options
X     */
X
X    if ((optind = getoptions (argc, argv)) < 0 || optind >= argc)
X    {
X	(void) fprintf (stderr,
X	  "Usage: %s [-c count] [-n] [-i intf] [-l len] [-s sleep] addr ...\n",
X			argv[0]);
X	exit (1);
X    }
X
X    /*
X     * Open ethernet interface
X     */
X
X    if (interface == NULL)
X    {
X	char **intfs;
X
X	if ((intfs = ether_interfaces ()) == NULL)
X	{
X	    perror ("ether_interfaces");
X	    exit (1);
X	}
X
X	interface = *intfs;
X    }
X
X#ifndef __STDC__
X    (void) bcopy ((char *) bytes, (char *) ctpmulticast.bytes, sizeof (bytes));
X#endif
X
X    if ((efd = ether_open (interface, CTPTYPE, &ctpmulticast)) < 0)
X    {
X	/* try again w/o multicast, for enetfilter systems */
X
X	nomulticast++;
X
X	if ((efd = ether_open (interface, CTPTYPE, (ether_addr *) 0)) < 0)
X	{
X	    perror (interface);
X	    exit (1);
X	}
X    }
X
X    /*
X     * Set non-blocking, so that we can select and loop
X     */
X
X    if (ether_blocking (efd, 0) != 0)
X    {
X	perror ("ether_block");
X	exit (1);
X    }
X
X    /*
X     * Get local interface address and process id
X     */
X
X    if (ether_address (efd, &localaddr) == NULL)
X    {
X	perror ("ether_address");
X	exit (1);
X    }
X
X    (void) printf ("%s address %s\n", interface, ether_ntoa (&localaddr));
X
X    pid = getpid ();			/* so we can identify our packets */
X
X    /*
X     * Set up forwarding path for CTP packet
X     */
X
X    pathcount = 0;
X    while (optind < argc && pathcount < MAXPATH && argv[optind] != NULL)
X    {
X	if (strcmp (argv[optind], "MULTICAST") == 0)
X	    path[pathcount] = ctpmulticast;
X
X	else if (strcmp (argv[optind], "BROADCAST") == 0)
X	    path[pathcount] = ether_bcast_addr;
X
X	else if (!ether_host2e (argv[optind], &path[pathcount]))
X	{
X	    extern char *sys_errlist[];
X	    int saved_errno = errno;
X
X	    (void) puts ("");
X	    (void) fflush (stdout);
X	    if (errno == ENOENT)
X		(void) fprintf (stderr, "%s: unknown host %s\n",
X				argv[0], argv[optind]);
X	    else
X		(void) fprintf (stderr, "%s: can't get address for %s: %s\n",
X				argv[0], argv[optind],
X				sys_errlist[saved_errno]);
X
X	    exit (1);
X	}
X
X	(void) printf ("%s%s (%s)", ((pathcount > 0) ? "\n    " : "CTP "),
X		       argv[optind], ether_ntoa (&path[pathcount]));
X
X	pathcount++;
X	optind++;
X    }
X
X    if (pathcount == MAXPATH)
X    {
X	fputs (": ", stdout);
X	(void) fflush (stdout);
X	(void) fprintf (stderr, "Too many hosts in path (%d)\n", MAXPATH);
X	exit (1);
X    }
X
X    /*
X     * Build output packet
X     */
X
X    reply = output_packet (efd, &outpkt, &outpktbuf, path, pathcount);
X
X    /*
X     * Set up signal handlers and timers
X     */
X
X    (void) signal (SIGALRM, resend);
X    (void) signal (SIGINT, die);
X    (void) signal (SIGHUP, die);
X    (void) signal (SIGTERM, die);
X
X    interval.it_interval.tv_sec = slptim / 1000;
X    interval.it_interval.tv_usec = slptim % 1000;
X    interval.it_value.tv_sec = slptim / 1000;
X    interval.it_value.tv_usec = slptim % 1000;
X
X    if (setitimer (ITIMER_REAL, &interval, (struct itimerval *) 0) < 0)
X    {
X	perror ("setitimer");
X	exit (1);
X    }
X
X    /*
X     * Loop checking for packets
X     */
X
X    while (count != 0)
X    {
X	fd_set fdset;
X
X	if (tosend > 0)
X	{
X	    tosend--;
X
X	    reply->seq = seq++;
X
X	    /*
X	     * It's safe to cast reply->sendt, since that will be long aligned
X	     */
X
X	    (void) gettimeofday ((struct timeval *) reply->sendt,
X				 (struct timezone *) 0);
X
X	    if (ether_write (efd, &outpkt) < 0)
X	    {
X		perror ("ether_read");
X		exit (1);
X	    }
X
X	    nsent++;
X	    count--;
X	}
X
X	FD_ZERO (&fdset);
X	FD_SET (efd, &fdset);
X
X	while (tosend == 0)
X	{
X	    ether_packet pkt;
X	    ctp_packet pktbuf;
X	    struct timeval tv;
X
X	    pkt.pktbuf = (char *) &pktbuf;
X	    pkt.pktlen = sizeof (pktbuf);
X
X	    if (select (efd + 1, param (&fdset), param (0), param (0),
X			(struct timeval *) 0) < 0)
X	    {
X		if (errno == EINTR)
X		    break;		/* from while loop */
X
X		perror ("select");
X		exit (1);
X	    }
X
X	    for (;;)			/* while packets can be read */
X	    {
X		if (ether_read (efd, &pkt) < 0)
X		{
X		    if (errno == EAGAIN)
X			break;		/* from for loop */
X
X		    perror ("ether_read");
X		    exit (1);
X		}
X
X		/*
X		 * Use 3.x NIT timestamp if present, since latency is terrible
X		 */
X
X		if (ether_timestamp)
X		    tv = *ether_timestamp;
X		else
X		    (void) gettimeofday (&tv, (struct timezone *) 0);
X
X		input_packet (&pkt, &pktbuf, &tv);
X	    }
X	}
X    }
X
X    printstats ();
X    exit (0);
X}
X
Xctp_reply *
Xoutput_packet (efd, pkt, ctp, path, pathcount)
Xint efd;
Xether_packet *pkt;
Xctp_packet *ctp;
Xether_addr path[];
Xint pathcount;
X{
X    ctp_forward *forward;
X    ctp_reply *reply;
X    int i;
X
X    ctp->skip = 0;
X
X    /*
X     * Since ctp is long aligned, and the data offset is 2, we are safe
X     */
X
X    forward = (ctp_forward *) ctp->data;
X    for (i = 1; i < pathcount; i++)
X    {
X	forward->function = swaps (CTP_FWD);
X	forward->addr = path[i];
X	forward++;
X    }
X
X    /* ensure packet gets back -- append forward to ourselves if needed */
X
X    if (!noreturn && ether_cmp (&path[pathcount - 1], &localaddr)
X	&& (ether_cmp (&path[pathcount - 1], &ether_bcast_addr)
X	    || !ether_bcast_self (efd) || pathcount == 1
X	    || ether_cmp (&path[pathcount - 2], &localaddr))
X	&& (nomulticast || ether_cmp (&path[pathcount - 1], &ctpmulticast)
X	    || !ether_mcast_self (efd) || pathcount == 1
X	    || ether_cmp (&path[pathcount - 2], &localaddr)))
X    {
X	forward->function = swaps (CTP_FWD);
X	forward->addr = localaddr;
X	forward++;
X    }
X
X    reply = (ctp_reply *) forward;
X
X    i = ((char *) (reply + 1)) - (char *) ctp;
X
X    if (i > packlen)
X	if (i > ETHER_MAX)
X	{
X	    (void) fputs (": ", stdout);
X	    (void) fflush (stdout);
X	    (void) fprintf (stderr, "Packet too large without data (%d)\n", i);
X	    exit (1);
X	}
X	else
X	    packlen = i;
X
X    (void) printf (": %d data bytes (%d total)\n", packlen - i, packlen + 14);
X    (void) fflush (stdout);
X
X    reply->function = swaps (CTP_REP);	/* create reply data */
X    reply->pid = pid;
X
X    pkt->dest = path[0];		/* fill in ether packet header */
X    pkt->pktbuf = (char *) ctp;
X    pkt->pktlen = packlen;
X
X    return (reply);			/* return pointer to reply struct */
X}
X
X
Xsigtype
Xresend ()
X{
X    tosend++;
X}
X
Xsigtype
Xdie ()
X{
X    puts ("");
X    printstats ();
X    exit (0);
X}
X
Xvoid
Xprintstats ()
X{
X    int bucket = 0;
X    int lastbucket = -1;
X    int min = ((unsigned) -1) >> 1;
X    int max = -1;
X    int nrecvd = 0;
X    int sum = 0;
X
X    (void) printf ("----CTP Statistics----\n");
X    (void) printf ("%d packet%s transmitted", nsent,
X		   (nsent == 1 ? "" : "s"));
X
X    for (;;)
X    {
X	while (stats[bucket].valid == 0 && bucket < NBUCKETS)
X	    bucket++;
X
X	if (lastbucket != -1
X	    && (bucket != NBUCKETS || stats[lastbucket].nrecvd != nrecvd))
X	{
X	    char hostaddr[ETHERSTRLEN];
X	    char host[MAXHOSTNAMELEN];
X
X	    (void) ether_e2a (&stats[lastbucket].eaddr, hostaddr);
X
X	    (void) printf ("\n  %d received from ", stats[lastbucket].nrecvd);
X
X	    if (ether_e2host (&stats[lastbucket].eaddr, host) == NULL)
X		(void) fputs (hostaddr, stdout);
X	    else
X		(void) printf ("%s (%s)", host, hostaddr);
X
X	    if (nsent - stats[lastbucket].nrecvd < 0)
X		(void) puts ("");
X	    else
X		(void) printf (", %d%% packet loss\n", 100 *
X			       (nsent - stats[lastbucket].nrecvd) / nsent);
X	    if (stats[lastbucket].nrecvd > 0)
X		(void)
X		    printf ("  round-trip (ms)  min/avg/max = %d/%d/%d",
X			    stats[lastbucket].min,
X			    stats[lastbucket].sum / stats[lastbucket].nrecvd,
X			    stats[lastbucket].max);
X	}
X
X	if (bucket == NBUCKETS)
X	    break;			/* done with loop */
X
X	lastbucket = bucket;
X
X	if (min > stats[bucket].min)
X	    min = stats[bucket].min;
X	if (max < stats[bucket].max)
X	    max = stats[bucket].max;
X
X	nrecvd += stats[bucket].nrecvd;
X	sum += stats[bucket].sum;
X
X	bucket++;
X    }
X
X    if (lastbucket == -1 || stats[lastbucket].nrecvd == nrecvd)
X    {
X	(void) printf (", %d received", nrecvd);
X
X	if (nsent - nrecvd < 0)
X	    (void) puts ("");
X	else
X	    (void) printf (", %d%% packet loss\n",
X			   100 * (nsent - nrecvd) / nsent);
X    }
X    else
X	fputs ("\noverall ", stdout);
X
X    if (nrecvd > 0)
X	(void) printf ("round-trip (ms)	 min/avg/max = %d/%d/%d\n",
X		       min, sum / nrecvd, max);
X}
X
Xvoid
Xinput_packet (pkt, ctp, tv)
Xether_packet *pkt;
Xctp_packet *ctp;
Xstruct timeval *tv;
X{
X    short skip;
X    ctp_reply *reply;
X    int ms;
X    int bucket;
X
X    skip = swaps (ctp->skip);		/* fetch skip */
X
X    if (skip & 1)			/* if skip is odd, we are unaligned */
X	return;
X
X    /*
X     * Since ctp is long aligned, and skip is not odd, this is safe
X     */
X
X    reply = (ctp_reply *) &ctp->data[skip];
X
X    if (pkt->pktlen - skip - 2 < 2)	/* no room for function */
X	return;
X
X    switch (swaps (reply->function))
X    {
X      case CTP_REP:			/* reply */
X	break;
X
X      case CTP_FWD:			/* XXX - should really process this */
X	return;
X
X      default:				/* probably a byteswapped function */
X	return;
X    }
X
X    if (pkt->pktlen - skip - 2 < sizeof (ctp_reply))
X	return;
X
X    if (reply->pid != pid)		/* not our packet */
X	return;
X
X    (void) printf ("%d bytes from %s: seq=%d time=", pkt->pktlen + ETHER_PKT,
X		   ether_ntoa (&pkt->src), reply->seq);
X
X    /*
X     * It's safe to cast reply->sendt, since that will be long aligned
X     */
X
X    if ((ms = delta (tv, (struct timeval *) reply->sendt)) < 0)
X    {
X	(void) printf ("(%d ms)!!!\n", ms);
X	ms = 0;				/* time must have jumped back, yuck */
X    }
X    else
X	(void) printf ("%d ms\n", ms);
X
X    (void) fflush (stdout);
X
X    bucket = hash (&pkt->src);
X
X    while (stats[bucket].valid && ether_cmp (&stats[bucket].eaddr, &pkt->src))
X	if (++bucket >= NBUCKETS)
X	    bucket = 0;
X
X    if (!stats[bucket].valid)		/* initialize new bucket */
X    {
X	stats[bucket].nrecvd = 0;
X	stats[bucket].highseq = 0;
X	stats[bucket].outofseq = 0;
X	stats[bucket].min = ((unsigned) -1) >> 1;
X	stats[bucket].max = -1;
X	stats[bucket].sum = 0;
X	stats[bucket].valid = 1;
X	stats[bucket].eaddr = pkt->src;
X    }
X
X    stats[bucket].nrecvd++;
X
X    if (reply->seq != lastseq && reply->seq != lastseq + 1)
X	stats[bucket].outofseq++;
X    lastseq = reply->seq;
X    if (reply->seq > stats[bucket].highseq)
X	stats[bucket].highseq = reply->seq;
X
X    if (ms > stats[bucket].max)
X	stats[bucket].max = ms;
X    if (ms < stats[bucket].min)
X	stats[bucket].min = ms;
X    stats[bucket].sum += ms;
X}
X
Xdelta (a, b)
Xstruct timeval *a;
Xstruct timeval *b;
X{
X    long usec;
X    long sec;
X
X    usec = a->tv_usec - b->tv_usec;
X    sec = a->tv_sec - b->tv_sec;
X
X    if (usec < 0)
X    {
X	sec--;
X	usec += 1000000;
X    }
X
X    usec += 500;			/* round to ms */
X    sec = sec * 1000 + usec / 1000;	/* get ms */
X
X    return (sec);
X}					/* delta */
X
X
Xgetoptions (argc, argv)
Xint argc;
Xchar *argv[];
X{
X    extern char *optarg;
X    extern int optind;
X    int opt;
X    int errs;
X
X    errs = 0;
X    while ((opt = getopt (argc, argv, "c:i:l:ns:")) != EOF)
X    {
X	switch (opt)
X	{
X	  case 'c':
X	    if ((count = atoi (optarg)) <= 0)
X	    {
X		errs++;
X		(void) fprintf (stderr, "invalid packet count: %s\n", optarg);
X	    }
X	    break;
X
X	  case 'i':
X	    interface = optarg;
X	    break;
X
X	  case 'l':
X	    if ((packlen = atoi (optarg)) <= 0)
X	    {
X		errs++;
X		(void) fprintf (stderr, "invalid packet length: %s\n", optarg);
X	    }
X	    else if (packlen < ETHER_MIN)
X	    {
X		(void) fprintf (stderr,
X				"packet length %s increased to minimum %d\n",
X				optarg, ETHER_MIN);
X		packlen = ETHER_MIN;
X	    }
X	    else if (packlen > ETHER_MAX)
X	    {
X		(void) fprintf (stderr,
X				"packet length %s decreased to maximum %d\n",
X				optarg, ETHER_MAX);
X		packlen = ETHER_MAX;
X	    }
X	    break;
X
X	  case 'n':
X	    noreturn ^= 1;
X	    break;
X
X	  case 's':
X	    slptim = atoi (optarg);
X	    break;
X
X	  default:
X	    errs++;
X	}
X    }
X
X    if (errs)
X	return (-1);
X    else
X	return (optind);
X}
X
X
Xshort
Xswaps (word)
Xunsigned short word;
X{
X    static int swap = -1;
X
X    if (swap < 0)
X    {
X	short test = 0x0100;
X	swap = *(char *) &test;
X    }
X
X    if (swap)
X	return (word >> 8 | (word & 0xff) << 8);
X    else
X	return (word);
X}
END_OF_FILE
if test 14485 -ne `wc -c <'./tests/ctp.c'`; then
    echo shar: \"'./tests/ctp.c'\" unpacked with wrong size!
fi
# end of './tests/ctp.c'
fi
if test -f './tests/ethertest.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'./tests/ethertest.c'\"
else
echo shar: Extracting \"'./tests/ethertest.c'\" \(9445 characters\)
sed "s/^X//" >'./tests/ethertest.c' <<'END_OF_FILE'
X/* $Id: ethertest.c,v 1.3 89/10/24 17:54:39 dupuy Exp $ */
X
X#include <stdio.h>
X#include <strings.h>			/* strcmp */
X
X#include <sys/param.h>			/* MAXHOSTNAMELEN */
X#include <sys/uio.h>			/* iov */
X#include <sys/socket.h>
X
X#include <errno.h>			/* EAGAIN */
X#include <netdb.h>			/* hostent */
X
X#include <netinet/in.h>			/* in_addr */
X
X#include "ether.h"
X
X#if defined(lint) && defined (ultrix)
Xvoid bcopy ();
Xvoid perror ();
Xvoid exit ();
Xunsigned sleep ();
X#endif
X
X#if !defined (sun) && !defined (ultrix)
Xextern uid_t getuid ();
X#endif
X
X#define offsetof(type,mem) ((unsigned) &(((type *) 0)->mem))
X
Xchar hello[] = "hello,";
Xchar world[] = " world";
Xstruct iovec iov[2] = {{hello, 6}, {world, 7}};
Xchar helloworld[] = "hello, world";
Xstruct iovec iov2[1] = {{helloworld, 13}};
X
X#ifdef __STDC__
Xether_addr multicast = {{ 0x0f, 0x0f, 0x0f, 0xfe, 0xfe, 0x00 }};
X#else
Xether_addr multicast;
Xunsigned char bytes[6] = { 0x0f, 0x0f, 0x0f, 0xfe, 0xfe, 0x00 };
X#endif
Xchar mcastname1[] = "0f:0F:f:Fe:fE:0";
Xchar mcastname2[] = "0f-0F-f-Fe-fE-0";
X
Xether_vec packetv;
Xether_packet packet;
X
Xchar *inet_n2a ();
X
Xextern int errno;
X
Xmain ()
X{
X    char hostname[MAXHOSTNAMELEN + 1];
X    char *hname = NULL;
X    struct hostent *hdata;
X    ether_addr eaddress;
X    ether_addr eaddress2;
X    ether_addr eaddress3;
X    char **intfs;
X    int uid;
X    int i = ETHER_SRC;
X
X    /*
X     * Initialize union
X     */
X
X#ifndef __STDC__
X    (void) bcopy ((char *) bytes, (char *) multicast.bytes, sizeof (bytes));
X#endif
X
X    /*
X     * Test some structures for compiler sanity
X     */
X
X    if (i != sizeof (ether_addr))
X	(void) fprintf (stderr, "WARNING!!! sizeof (ether_addr) = %d\n",
X			sizeof (ether_addr));
X    if (ETHER_PKT != offsetof (ether_packet, type[2]))
X	(void) fprintf (stderr, "WARNING!!! offset of ether_packet = %d\n",
X			offsetof (ether_packet, type[2]));
X
X#ifdef __GNUC__
X    (void) printf ("ether_addr alignment = %d, ether_packet alignment = %d\n",
X		   __alignof (ether_addr), __alignof (ether_packet));
X#endif
X
X    /*
X     * Get the canonical hostname
X     */
X
X    if (gethostname (hostname, sizeof (hostname)) < 0)
X	perror ("gethostname");
X    else
X    {
X	hname = hostname;
X
X	if ((hdata = gethostbyname (hostname)) == 0)
X	    (void) fprintf (stderr, "host info for %s not found\n", hostname);
X	else
X	{
X	    hname = hdata->h_name;
X
X	    if ((hdata = gethostbyaddr (hdata->h_addr, hdata->h_length,
X					hdata->h_addrtype)) == 0)
X		(void) fprintf (stderr, "%s address does not match\n", hname);
X	    else
X		hname = hdata->h_name;
X
X	}
X    }
X
X    if (hname)
X	(void) printf ("%s ethertest\n", hname);
X
X    /*
X     * Get the ethernet interface names and test some utility functions
X     */
X
X    if ((intfs = ether_interfaces ()) == NULL)
X    {
X	perror ("ether_interfaces");
X	exit (1);
X    }
X
X    if (hname)
X    {
X	if (ether_host2e (hostname, &eaddress) == NULL)
X	    (void) fprintf (stderr, "ether_h2e failed\n");
X	else
X	    (void) printf ("%s: %s\n", hostname, ether_ntoa (&eaddress));
X    }
X
X    if (hdata)
X    {
X	struct in_addr ipaddr;
X
X	(void) bcopy (hdata->h_addr, (char *) &ipaddr, sizeof (ipaddr));
X	if (ether_hostent2e (hdata, &eaddress2) == NULL)
X	    (void) fprintf (stderr, "ether_he2e failed\n");
X
X	else if (bcmp ((char *) &eaddress, (char *) &eaddress2,
X		       sizeof (ether_addr)))
X	    (void) printf ("%s: %s\n", hdata->h_name, ether_ntoa (&eaddress2));
X
X	if (ether_ip2e (&ipaddr, &eaddress3) == NULL)
X	    perror ("ether_ip2e");
X
X	else if (bcmp ((char *) &eaddress3, (char *) &eaddress2,
X		       sizeof (ether_addr)))
X	    (void) printf ("%s: %s\n", inet_n2a (ipaddr),
X			   ether_ntoa (&eaddress3));
X    }
X
X    uid = getuid ();
X
X    /*
X     * Loop through the interfaces, testing functions
X     */
X
X    for (i = 0; intfs[i]; i++)
X    {
X	ether_addr address;
X	int fd;
X	int self;
X
X	(void) printf ("%s: ", intfs[i]);
X	(void) fflush (stdout);
X
X	if ((fd = ether_open (intfs[i], 0xf0f0, (ether_addr *) NULL)) < 0)
X	    perror ("open");
X	else
X	{
X	    struct in_addr ipaddr;
X	    char hostname2[MAXHOSTNAMELEN + 1];
X
X	    (void) setreuid (0, uid);
X
X	    if (ether_address (fd, &address) == NULL)
X		perror ("address");
X	    else
X		(void) printf ("%s\n", ether_ntoa (&address));
X
X	    hostname2[MAXHOSTNAMELEN] = '\0';
X	    if (ether_e2host (&address, hostname2) == NULL)
X	    {
X		(void) fprintf (stderr, "ether_e2host failed\n");
X		if (ether_e2ip (&address, &ipaddr) == NULL)
X		    (void) fprintf (stderr, "ether_e2ip failed\n");
X	    }
X	    else
X	    {
X		if (ether_e2ip (&address, &ipaddr) == NULL)
X		    (void) fprintf (stderr, "ether_e2ip failed\n");
X		else
X		    (void) printf ("%s: %s\n", hostname2, inet_n2a (ipaddr));
X	    }
X
X	    self = ether_bcast_self (fd);
X
X	    packet.pktbuf = "dlrow ,olleh";
X	    packet.pktlen = 13;
X	    bcopy ((char *) &ether_bcast_addr, (char *) &packet.dest,
X		   sizeof (address));
X	    if (ether_write (fd, &packet) < 0)
X		perror ("ether_write");
X
X	    if (ether_blocking (fd, 0))
X		perror ("ether_blocking");
X
X	    (void) sleep (1);
X
X	    packetv.iov = iov2;
X	    packetv.iovcnt = sizeof (iov2) / sizeof (struct iovec);
X
X	    if (ether_readv (fd, &packetv) < 0)
X	    {
X		if (errno != EAGAIN)
X		    perror ("ether_readv");
X		else if (self == 0)
X		    (void)
X			(void) printf ("%s",
X				  "interface cannot receive own broadcasts\n");
X		else
X		    (void) fprintf (stderr, "%s %s\n",
X				    "interface cannot receive own broadcasts",
X				    "but thinks that it can!");
X	    }
X	    else if (strcmp (helloworld, "dlrow ,olleh"))
X		(void) fprintf (stderr,
X				"bcast_self test received garbled packet\n");
X
X	    else if (bcmp ((char *) &packetv.src, (char *) &address,
X			   sizeof (ether_addr)))
X		    (void) fprintf (stderr, "WARNING: source address %s!\n",
X				    ether_e2a (&packetv.src, (char *) 0));
X	    else if (self == 1)
X	    {
X		(void) printf ("%s",
X			       "interface will receive own broadcasts\n");
X	    }
X	    else
X	    {
X		(void) fprintf (stderr, "%s %s\n",
X				"interface will receive own broadcasts",
X				"but doesn't think that it can!");
X	    }
X
X	    self = ether_send_self (fd);
X
X	    packetv.iov = iov;
X	    packetv.iovcnt = sizeof (iov) / sizeof (struct iovec);
X
X	    bcopy ((char *) &address, (char *) &packetv.dest, sizeof (address));
X	    if (ether_writev (fd, &packetv) < 0)
X		perror ("ether_writev");
X
X	    (void) sleep (1);
X
X	    packet.pktbuf = NULL;
X	    if (ether_read (fd, &packet) < 0)
X	    {
X		if (errno != EAGAIN)
X		    perror ("ether_read");
X		else if (self == 0)
X		    (void) printf ("%s",
X				   "interface cannot send packets to self\n");
X		else
X		    (void) fprintf (stderr, "%s %s\n",
X				    "interface cannot send packets to self",
X				    "but thinks that it can!");
X	    }
X	    else if (strcmp (packet.pktbuf, "hello, world"))
X		(void) fprintf (stderr,
X				"send_self test received garbled packet\n");
X
X	    else if (!bcmp ((char *) &packetv.src, (char *) &address,
X			    sizeof (ether_addr)))
X		    (void) fprintf (stderr, "WARNING: source address %s!\n",
X				    ether_ntoa (&packetv.src));
X
X	    else if (self == 1)
X	    {
X		(void) printf ("%s", "interface can send packets to self\n");
X	    }
X	    else
X	    {
X		(void) fprintf (stderr, "%s %s\n",
X				"interface can send packets to self",
X				"but doesn't think that it can!");
X	    }
X
X	    (void) close (fd);
X
X	    (void) setreuid (uid, 0);
X
X	    if ((fd = ether_open (intfs[i], 0xf0f0, &multicast)) < 0)
X		(void) printf (
X			   "interface does not support multicast reception\n");
X	    else
X	    {
X		(void) setreuid (0, uid);
X
X		self = ether_mcast_self (fd);
X
X		packet.pktbuf = "dlrow ,olleh";
X		packet.pktlen = 13;
X		bcopy ((char *) &multicast, (char *) &packet.dest,
X		       sizeof (address));
X		if (ether_write (fd, &packet) < 0)
X		    perror ("ether_write");
X
X		if (ether_blocking (fd, 0))
X		    perror ("ether_blocking");
X
X		(void) sleep (1);
X
X		packetv.iov = iov2;
X		packetv.iovcnt = sizeof (iov2) / sizeof (struct iovec);
X
X		if (ether_readv (fd, &packetv) < 0)
X		{
X		    if (errno != EAGAIN)
X			perror ("ether_readv");
X		else if (self == 0)
X		    (void)
X			(void) printf ("%s",
X				  "interface cannot receive own multicasts\n");
X		else
X		    (void) fprintf (stderr, "%s %s\n",
X				    "interface cannot receive own multicasts",
X				    "but thinks that it can!");
X		}
X		else if (strcmp (helloworld, "dlrow ,olleh"))
X		    (void) fprintf (stderr,
X				  "mcast_self test received garbled packet\n");
X
X		else if (bcmp ((char *) &packetv.src, (char *) &address,
X			       sizeof (ether_addr)))
X		    (void) fprintf (stderr, "WARNING: source address %s!\n",
X				    ether_ntoa (&packetv.src));
X
X		else if (self == 1)
X		{
X		    (void) printf ("%s",
X				   "interface will receive own multicasts\n");
X		}
X		else
X		{
X		    (void) fprintf (stderr, "%s %s\n",
X				    "interface will receive own multicasts",
X				    "but doesn't think that it can!");
X		}
X
X		(void) setreuid (uid, 0);
X	    }
X	}
X    }
X
X    if (ether_cmp (&multicast, ether_aton (mcastname1)))
X	(void) fprintf (stderr, "ether_aton failed (%s)\n",
X			ether_ntoa (ether_aton (mcastname1)));
X
X    if (ether_cmp (&multicast, ether_a2e (mcastname2, &eaddress)))
X	(void) fprintf (stderr, "ether_a2e failed (%s)\n",
X			ether_ntoa (&eaddress));
X
X    return (0);
X}
X
Xchar *
Xinet_n2a (addr)
Xstruct in_addr addr;
X{
X#ifdef lint
X    char *sprintf ();
X#endif
X    static char buffer[16];
X    unsigned char *bytes;
X
X    bytes = (unsigned char *) &addr;
X    (void) sprintf (buffer, "%d.%d.%d.%d",
X		    bytes[0], bytes[1], bytes[2], bytes[3]);
X
X    return (buffer);
X}
END_OF_FILE
if test 9445 -ne `wc -c <'./tests/ethertest.c'`; then
    echo shar: \"'./tests/ethertest.c'\" unpacked with wrong size!
fi
# end of './tests/ethertest.c'
fi
echo shar: End of archive 3 \(of 3\).
cp /dev/null ark3isdone
MISSING=""
for I in 1 2 3 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 3 archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0
-- 
Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.
Use a domain-based address or give alternate paths, or you may lose out.