/*
 * $Id: nit.c,v 1.2 2005/02/01 18:06:29 c4chris Exp $
 */

#include "os.h"

#ifdef USE_NIT
/*
 * nit.c - routines for messing with the network interface tap.
 *
 * David A. Curry
 * Purdue University
 * Engineering Computer Network
 * 1285 Electrical Engineering Building
 * West Lafayette, IN 47907-1285
 * davy@ecn.purdue.edu
 * 
 */
#include <sys/param.h>
#include <sys/stropts.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <sys/file.h>
#include <net/if.h>
#include <signal.h>
#include <stdio.h>

#include <net/nit_if.h>
#include <net/nit_buf.h>

#include "nfswatch.h"
#include "externs.h"

#if NOFILE <= 64
#define SUNOS40 1
#endif

/*
 * setup_nit_dev - set up the network interface tap.
 */
int
setup_nit_dev(device)
char **device;
{
	int n, s, fd;
	u_int chunksz;
	u_long if_flags;
	char buf[BUFSIZ];
	struct ifconf ifc;
	struct strioctl si;
	struct timeval timeout;
	struct ifreq ifr, *ifrp;

	/*
	 * If the interface device was not specified,
	 * get the default one.
	 */
	if (*device == NULL) {
		/*
		 * Grab a socket.
		 */
		if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
			error("socket");
			finish(-1);
		}

		ifc.ifc_buf = buf;
		ifc.ifc_len = sizeof(buf);

		/*
		 * See what devices we've got.
		 */
		if (ioctl(s, SIOCGIFCONF, (char *) &ifc) < 0) {
			error("ioctl: SIOCGIFCONF");
			finish(-1);
		}

		/*
		 * Take the first device we encounter.
		 */
		ifrp = ifc.ifc_req;
		for (n = ifc.ifc_len/sizeof(struct ifreq); n > 0; n--,ifrp++) {
			/*
			 * Skip the loopback interface.
			 */
			if (strcmp(ifrp->ifr_name, "lo0") == 0)
				continue;

			*device = strdup(ifrp->ifr_name);
			break;
		}

		(void) close(s);
	}

	/*
	 * We want the ethernet in promiscuous mode if we're looking
	 * at nodes other than ourselves, and we want to know about 
	 * dropped packets.
	 */
	if (allflag || dstflag)
		if_flags = NI_DROPS | NI_PROMISC | NI_TIMESTAMP;
	else
		if_flags = NI_DROPS | NI_TIMESTAMP;

	/*
	 * Open the network interface tap.
	 */
	if ((fd = open(NIT_DEV, O_RDONLY)) < 0) {
		error("nit: open");
		finish(-1);
	}

	/*
	 * Arrange to get discrete messages.
	 */
	if (ioctl(fd, I_SRDOPT, (char *) RMSGD) < 0) {
		error("ioctl: I_SRDOPT");
		finish(-1);
	}

	/*
	 * Push and configure the nit buffering module.
	 */
	if (ioctl(fd, I_PUSH, NIT_BUF) < 0) {
		error("ioctl: I_PUSH NIT_BUF");
		finish(-1);
	}

	/*
	 * Set the read timeout.
	 */
	timeout.tv_sec = 1;
	timeout.tv_usec = 0;

	si.ic_cmd = NIOCSTIME;
	si.ic_timout = INFTIM;
	si.ic_len = sizeof(timeout);
	si.ic_dp = (char *) &timeout;

	if (ioctl(fd, I_STR, (char *) &si) < 0) {
		error("ioctl: I_STR NIOCSTIME");
		finish(-1);
	}

	/*
	 * Set the chunk size.
	 */
	chunksz = NIT_CHUNKSIZE;

	si.ic_cmd = NIOCSCHUNK;
	si.ic_len = sizeof(chunksz);
	si.ic_dp = (char *) &chunksz;

	if (ioctl(fd, I_STR, (char *) &si) < 0) {
		error("ioctl: I_STR NIOCSCHUNK");
		finish(-1);
	}

	/*
	 * Configure the network interface tap by binding it
	 * to the underlying interface, setting the snapshot
	 * length, and setting the flags.
	 */
	(void) strncpy(ifr.ifr_name, *device, sizeof(ifr.ifr_name));
	ifr.ifr_name[sizeof(ifr.ifr_name)-1] = '\0';

	si.ic_cmd = NIOCBIND;
	si.ic_len = sizeof(ifr);
	si.ic_dp = (char *) &ifr;

	/*
	 * If the bind fails, there's no such device.
	 */
	if (ioctl(fd, I_STR, (char *) &si) < 0) {
		close(fd);
		return(-1);
	}

	/*
	 * SNAP is buggy on SunOS 4.0.x
	 */
#ifndef SUNOS40
	si.ic_cmd = NIOCSSNAP;
	si.ic_len = sizeof(truncation);
	si.ic_dp = (char *) &truncation;

	if (ioctl(fd, I_STR, (char *) &si) < 0) {
		error("ioctl: I_STR NIOCSSNAP");
		finish(-1);
	}
#endif

	si.ic_cmd = NIOCSFLAGS;
	si.ic_len = sizeof(if_flags);
	si.ic_dp = (char *) &if_flags;

	if (ioctl(fd, I_STR, (char *) &si) < 0) {
		error("ioctl: I_STR NIOCSFLAGS");
		finish(-1);
	}

	return(fd);
}

/*
 * flush_nit - flush data from the nit.
 */
void
flush_nit(fd)
int fd;
{
	if (ioctl(fd, I_FLUSH, (char *) FLUSHR) < 0) {
		error("ioctl: I_FLUSH");
		finish(-1);
	}
}

/*
 * nit_devtype - determine the type of device we're looking at.
 */
int
nit_devtype(device)
char *device;
{
	/*
	 * This whole routine is a kludge.  Ultrix does it the
	 * right way; see pfilt.c.
	 */

	if (strncmp(device, "le", 2) == 0 || strncmp(device, "ie", 2) == 0)
		return(DLT_EN10MB);
	
	if (strncmp(device, "fddi", 4) == 0)
		return(DLT_FDDI);

	fprintf(stderr, "Unknown device type: %s -- assuming ethernet.\n",
		device);
	return(DLT_EN10MB);
}
#endif /* USE_NIT */
