NIT(4P)

NAME
nit - Network Interface Tap

CONFIG
pseudo-device clone
pseudo-device snit
pseudo-device pf
pseudo-device nbuf

SYNOPSIS
#include <sys/file.h>
#include <sys/ioctl.h>
#include <net/nit_pf.h>
#include <net/nit_buf.h>

	fd = open("/dev/nit", mode);
	ioctl(fd, I_PUSH, "pf");
	ioctl(fd, I_PUSH, "nbuf");

DESCRIPTION

NIT (the Network Interface Tap) is a facility composed of several STREAMS modules and drivers. These components collectively provide facilities for constructing applications that require link-level network access. Examples of such applications include rarpd(8C), which is a user-level implementation of the Reverse ARP protocol, and etherfind(8C), which is a network monitoring and trouble-shooting program.

NIT consists of several components that are summarized below. See their Reference Manual entries for detailed information about their specification and operation.

nit_if(4M)
    This component is a STREAMS device driver that interacts directly with the system's Ethernet drivers. After opening an instance of this device it must be bound to a specific Ethernet interface before becoming usable. Subsequently, nit_if transcribes packets arriving on the interface to the read side of its associated stream and delivers messages reaching it on the write side of its stream to the raw packet output code for transmission over the interface.

nit_pf(4M)
    This module provides packet-filtering services, allowing uninteresting incoming packets to be discarded with minimal loss of efficiency. It passes through unaltered all outgoing messages (those on the stream's write side).

nit_buf(4M)
    This module buffers incoming messages into larger aggregates, thereby reducing the overhead incurred by repeated read(2V) system calls.

NIT clients mix and match these components, based on their particular requirements. For example, the reverse ARP daemon concerns itself only with packets of a specific type and deals with low traffic volumes. Thus, it uses nit_if for access to the network and nit_pf to filter out all incoming packets except reverse ARP packets, but omits the nit_buf buffering module since traffic is not high enough to justify the additional complexity of unpacking buffered packets. On the other hand, the etherd(8C) program, which collects Ethernet statistics for traffic(1C) to display, must examine every packet on the network. Therefore, it omits the nit_pf module, since there is nothing it wishes to screen out, and includes the nit_buf module, since most networks have very heavy aggregate packet traffic.

EXAMPLES

The following code fragments outline how to program against parts of the NIT interface. For the sake of brevity, all error-handling code has been elided.

initdevice comes from etherfind and sets up its input stream configuration.

initdevice(if_flags, snaplen, chunksize)
	u_long	if_flags,
		snaplen,
		chunksize;
{
	struct strioctl	si;
	struct ifreq	ifr;
	struct timeval	timeout;
	if_fd = open(NIT_DEV, O_RDONLY);
	/* Arrange to get discrete messages from the stream. */
	ioctl(if_fd, I_SRDOPT, (char *)RMSGD);
	si.ic_timout = INFTIM;
	/* Push and configure the buffering module. */
	ioctl(if_fd, I_PUSH, "nbuf");
	timeout.tv_sec = 1;
	timeout.tv_usec = 0;
	si.ic_cmd = NIOCSTIME;
	si.ic_len = sizeof timeout;
	si.ic_dp = (char *)&timeout;
	ioctl(if_fd, I_STR, (char *)&si);
	si.ic_cmd = NIOCSCHUNK;
	si.ic_len = sizeof chunksize;
	si.ic_dp = (char *)&chunksize;
	ioctl(if_fd, I_STR, (char *)&si);
	/* Configure the nit device, binding it to the proper
	   underlying interface, setting the snapshot length,
	   and setting nit_if-level flags. */
	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;
	ioctl(if_fd, I_STR, (char *)&si);
	if (snaplen > 0) {
		si.ic_cmd = NIOCSSNAP;
		si.ic_len = sizeof snaplen;
		si.ic_dp = (char *)&snaplen;
		ioctl(if_fd, I_STR, (char *)&si);
	}
	if (if_flags != 0) {
		si.ic_cmd = NIOCSFLAGS;
		si.ic_len = sizeof if_flags;
		si.ic_dp = (char *)&if_flags;
		ioctl(if_fd, I_STR, (char *)&si);
	}
	/* Flush the read queue, to get rid of anything that accumulated
	   before the device reached its final configuration. */
	ioctl(if_fd, I_FLUSH, (char *)FLUSHR);
}

Here is the skeleton of the packet reading loop from etherfind. It illustrates how to cope with dismantling the headers the various NIT components glue on.

	while ((cc = read(if_fd, buf, chunksize)) >= 0) {
		register u_char	*bp = buf,
				*bufstop = buf + cc;
		/* Loop through each message in the chunk. */
		while (bp < bufstop) {
			register u_char		*cp = bp;
			struct nit_bufhdr	*hdrp;
			struct timeval		*tvp = NULL;
			u_long			drops = 0;
			u_long			pktlen;
			/* Extract information from the successive objects
			   embedded in the current message.  Which ones we
			   have depends on how we set up the stream (and
			   therefore on what command line flags were set).

			   If snaplen is positive then the packet was truncated
			   before the buffering module saw it, so we must
			   obtain its length from the nit_if-level nit_iflen
			   header.  Otherwise the value in *hdrp suffices. */
			hdrp = (struct nit_bufhdr *)cp;
			cp += sizeof *hdrp;
			if (tflag) {
				struct nit_iftime	*ntp;
				ntp = (struct nit_iftime *)cp;
				cp += sizeof *ntp;
				tvp = &ntp->nh_timestamp;
			}
			if (dflag) {
				struct nit_ifdrops	*ndp;
				ndp = (struct nit_ifdrops *)cp;
				cp += sizeof *ndp;
				drops = ndp->nh_drops;
			}
			if (snaplen > 0) {
				struct nit_iflen	*nlp;
				nlp = (struct nit_iflen *)cp;
				cp += sizeof *nlp;
				pktlen = nlp->nh_pktlen;
			}
			else
				pktlen = hdrp->nhb_msglen;
			sp = (struct sample *)cp;
			bp += hdrp->nhb_totlen;
			/* Process the packet. */
		}
	}

FILES
/dev/nit
    clone device instance referring to nit_if

SEE ALSO
traffic(1C), read(2V), nit_if(4M), nit_pf(4M), nit_buf(4M), etherd(8C), etherfind(8C), rarpd(8C)