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

#include "os.h"

#ifdef USE_SNOOP
/*
 * snoop.c - routines for snooping on network traffic via the SGI
 *	     provided snoop(7p) network monitoring protocol
 *	     -- works under IRIX 3.2.* and IRIX 3.3.[12]
 *
 * NOTE: must be "root" to use this
 *
 * Tim Hudson
 * Mincom Pty Ltd
 * Mincom Centre
 * Juliette Street
 * Greenslopes 4120
 * Brisbane Australia
 * tjh@mincom.oz.au
 *
 */
#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/soioctl.h>
#include <net/raw.h>
#include <netinet/if_ether.h>

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

/*
 * setup_snoop_dev - set up the network interface tap.
 */
int
setup_snoop_dev(device)
char **device;
{
	int n, s;
	int i, res;
	u_int chunksz;
	u_long if_flags;
	char buf[BUFSIZ];
	struct ifconf ifc;
	struct snoopfilter sf;
	struct timeval timeout;
	struct ifreq ifr, *ifrp;
	struct sockaddr_raw sr, sn;

	/*
	 * 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);
	}

	/*
	 * Make the raw socket.
	 */
	if ((s = socket(PF_RAW,SOCK_RAW,RAWPROTO_SNOOP)) < 0) {
		error("snoop: socket");
		finish(-1);
	}

	/*
	 * Set it up for the chosen interface.
	 */
	bzero((char *)&sr, sizeof(sr));
	sr.sr_family = AF_RAW;	
	sr.sr_port = 0;
	strncpy(sr.sr_ifname, *device, sizeof(sr.sr_ifname));

	/*
	 * If the bind fails, there's no such device.
	 */
	if (bind(s, &sr, sizeof(sr)) < 0) {
		close(s);
		return(-1);
	}

	/*
	 * Set up NULL filter - this is not necessary ...
	 */
	bzero((char *)&sf, sizeof(sf));

	if (ioctl(s, SIOCADDSNOOP, &sf) < 0) {
		error("snoop: add filter");
		finish(-1);
	}

	/*
	 * Beef up the socket buffer to minimise packet losses
	 */
	i = SNOOP_BUFFER_SIZE;
	if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *)&i, sizeof(i)) < 0) {
		error("snoop: set rcvbuf");
		finish(-1);
	}

	return(s);
}

/*
 * flush_snoop - flush data from the snoop.
 */
void
flush_snoop(fd)
int fd;
{
	int on = 1;
	int off = 0;

	/*
	 * Off then on should do a flush methinks
	 */
	ioctl(fd, SIOCSNOOPING, &off);

	if (ioctl(fd, SIOCSNOOPING, &on) < 0) {
		error("snoop: snoop on");
		finish(-1);
	}
}

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

	if (strncmp(device, "et", 2) == 0 || strncmp(device, "ec", 2) == 0 ||
	    strncmp(device, "fxp", 3) == 0 || strncmp(device, "enp", 3) == 0)
		return(DLT_EN10MB);
	
	if (strncmp(device, "ipg", 4) == 0)
		return(DLT_FDDI);

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