/*
 * Copyright (c) 1990, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 2000, 2009, 2010, 2011, 2012, 2013, 2016, 2019, 2022, 2023
 * The Regents of the University of California. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the University nor the names of its contributors
 *       may be used to endorse or promote products derived from this software
 *       without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 REGENTS OR 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.
 */
#ifndef lint
static const char copyright[] __attribute__((unused)) =
    "Copyright (c) 1990, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 2000, 2009, 2010, 2011, 2012, 2013, 2016, 2019, 2022, 2023\n\
The Regents of the University of California.  All rights reserved.\n";
static const char rcsid[] __attribute__((unused)) =
    "@(#) $Id: arpwatch.c 1540 2023-12-03 18:09:52Z leres $ (LBL)";
#endif

/*
 * arpwatch - keep track of ethernet/ip address pairings, report changes
 */

#include <sys/param.h>
#include <sys/types.h>				/* concession to AIX */
#include <sys/file.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/time.h>

#include <stdarg.h>

#if __STDC__
struct mbuf;
struct rtentry;
#endif
#include <net/if.h>

#include <netinet/in.h>
#include <netinet/if_ether.h>

#include <arpa/inet.h>

#include <ctype.h>
#include <errno.h>
#ifdef HAVE_MEMORY_H
#include <memory.h>
#endif
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>

#include <pcap.h>

#include "gnuc.h"
#ifdef HAVE_OS_PROTO_H
#include "os-proto.h"
#endif

#include "arpwatch.h"
#include "db.h"
#include "ec.h"
#include "fddi.h"
#include "file.h"
#include "machdep.h"
#include "setsignal.h"
#include "util.h"
#include "version.h"

/* Some systems don't define these */
#ifndef ETHERTYPE_REVARP
#define ETHERTYPE_REVARP	0x8035
#endif

#ifndef REVARP_REQUEST
#define REVARP_REQUEST		3
#define REVARP_REPLY		4
#endif

#ifndef ETHERTYPE_TRAIL
#define ETHERTYPE_TRAIL		0x1000
#endif

#ifndef ETHERTYPE_APOLLO
#define ETHERTYPE_APOLLO	0x8019
#endif

#ifndef IN_CLASSD_NET
#define IN_CLASSD_NET		0xf0000000
#endif

#ifndef max
#define max(a,b) ((b)>(a)?(b):(a))
#endif

/* Globals */
char *prog;
int quiet;
int suppress;
const char *watcher = WATCHER;
const char *watchee = WATCHEE;

/* Locals */
int can_checkpoint = 1;
int docheckpoint;
int foreground;
int nobogons;
int promisc = 1;
int swapped;

static u_int32_t net;
static u_int32_t netmask;

struct nets {
	u_int32_t net;
	u_int32_t netmask;
};

enum action {
	ADD = 1,
	SKIP
};

/* Locals */
static struct nets *nets;
static int nets_ind;
static int nets_size;
static struct nets *skipnets;
static int skipnets_ind;
static int skipnets_size;
static const char *pidfile;

extern int optind;
extern int opterr;
extern char *optarg;

/* Forwards */
int	addnet(enum action action, const char *);
void	checkpoint(int);
void	die(int);
void	hup(int);
int	isbogon(u_int32_t);
int	main(int, char **);
void	process_ether(u_char *, const struct pcap_pkthdr *, const u_char *);
void	process_fddi(u_char *, const struct pcap_pkthdr *, const u_char *);
int	readsnmp(char *);
int	snmp_add(u_int32_t, u_char *, time_t, char *);
int	sanity_ether(struct ether_header *, struct ether_arp *, int);
int	sanity_fddi(struct fddi_header *, struct ether_arp *, int);
int	toskip(u_int32_t);
void	usage(void) __attribute__((noreturn));

int
main(int argc, char **argv)
{
	char *cp;
	int op, snaplen, timeout, linktype, status;
	pcap_t *pd;
	FILE *fp;
	pcap_if_t *alldevs;
	char *interface, *rfilename;
	struct bpf_program code;
	char errbuf[PCAP_ERRBUF_SIZE];

	if (argv[0] == NULL)
		prog = "arpwatch";
	else if ((cp = strrchr(argv[0], '/')) != NULL)
		prog = cp + 1;
	else
		prog = argv[0];

	if (abort_on_misalignment(errbuf) < 0) {
		(void)fprintf(stderr, "%s: %s\n", prog, errbuf);
		exit(1);
	}

	opterr = 0;
	interface = NULL;
	rfilename = NULL;
	pd = NULL;
	while ((op = getopt(argc, argv, "CdD:Ff:i:n:NpP:qr:svw:W:x:zZ")) != EOF)
		switch (op) {

		case 'C':
			zeropad = 0;
			break;

		case 'd':
			++debug;
#ifndef DEBUG
			(void)fprintf(stderr,
			    "%s: Warning: Not compiled with -DDEBUG\n", prog);
#endif
			break;

		case 'D':
			arpdir = optarg;
			break;

		case 'f':
			arpfile = optarg;
			break;

		case 'F':
			++foreground;
			break;

		case 'i':
			interface = optarg;
			break;

		case 'n':
			if (!addnet(ADD, optarg))
				usage();
			break;

		case 'N':
			++nobogons;
			break;

		case 'p':
			promisc = 0;
			break;

		case 'P':
			pidfile = optarg;
			break;

		case 'r':
			rfilename = optarg;
			break;

		case 'a':
			++quiet;
			break;

		case 's':
			++suppress;
			break;

		case 'v':
			++vrrpflag;
			break;

		case 'w':
			watcher = optarg;
			break;

		case 'W':
			watchee = optarg;
			break;

		case 'x':
			if (!addnet(SKIP, optarg))
				usage();
			break;

		case 'z':
			++zeroflag;
			break;

		case 'Z':
			zeropad = 1;
			break;

		default:
			usage();
		}

	if (optind != argc)
		usage();

	if (debug)
		++foreground;

	openlog(prog, 0, LOG_DAEMON);

	if (rfilename != NULL) {
		net = 0;
		netmask = 0;
		++foreground;
	} else {
		/* Determine interface if not specified */
		if (interface == NULL) {
#ifdef HAVE_PCAP_FINDALLDEVS
			alldevs = NULL;
			if (pcap_findalldevs(&alldevs, errbuf) < 0) {
				(void)fprintf(stderr,
				    "%s: pcap_findalldevs: %s\n", prog, errbuf);
				exit(1);
			}
			if (alldevs == NULL) {
				(void)fprintf(stderr, "%s: pcap_findalldevs:"
				    " no suitable devices found\n", prog);
				exit(1);
			}
			interface = savestr(alldevs->name);
			pcap_freealldevs(alldevs);
#else
			if (interface = pcap_lookupdev(errbuf)) == NULL) {
				(void)fprintf(stderr,
				    "%s: pcap_lookupdev: %s\n", prog, errbuf);
				exit(1);
			}
#endif
		}

		/* Determine network and netmask */
		if (pcap_lookupnet(interface, &net, &netmask, errbuf) < 0) {
			net = 0;
			netmask = 0;
			(void)fprintf(stderr, "%s: warning: %s\n",
			    prog, errbuf);
		}

		/* Drop into the background if not debugging */
		if (!foreground && daemon(0, 1) < 0) {
			(void)fprintf(stderr, "daemon(): %s\n",
			    strerror(errno));
			exit(1);
		}
	}

	if (chdir(arpdir) < 0) {
		lg(LOG_ERR, "chdir(%s): %s", arpdir, strerror(errno));
		lg(LOG_ERR, "(using current working directory)");
	}

	if (rfilename != NULL) {
		pd = pcap_open_offline(rfilename, errbuf);
		if (pd == NULL) {
			lg(LOG_ERR, "pcap open %s: %s", rfilename,  errbuf);
			exit(1);
		}
		swapped = pcap_is_swapped(pd);
	} else {
		snaplen = max(sizeof(struct ether_header),
		    sizeof(struct fddi_header)) + sizeof(struct ether_arp);
		timeout = 1000;
		pd = pcap_open_live(interface, snaplen, promisc, timeout,
		    errbuf);
		if (pd == NULL) {
			lg(LOG_ERR, "pcap open %s: %s", interface, errbuf);
			exit(1);
		}
#ifdef WORDS_BIGENDIAN
		swapped = 1;
#endif
	}

	/* Update pidfile */
	if (pidfile != NULL) {
		fp = fopen(pidfile, "w");
		if (fp == NULL)
			lg(LOG_ERR, "fopen: %s: %s", pidfile, strerror(errno));
		else {
			fprintf(fp, "%d\n", (int)getpid());
			fclose(fp);
		}
	}

	/*
	 * Revert to non-privileged user after opening sockets
	 * (not needed on most systems).
	 */
	setgid(getgid());
	setuid(getuid());

	/* Must be ethernet or fddi */
	linktype = pcap_datalink(pd);
	if (linktype != DLT_EN10MB && linktype != DLT_FDDI) {
		lg(LOG_ERR, "Link layer type %d not ethernet or fddi",
		    linktype);
		exit(1);
	}

	/* Compile and install filter */
	if (pcap_compile(pd, &code, "arp or rarp", 1, netmask) < 0) {
		lg(LOG_ERR, "pcap_compile: %s", pcap_geterr(pd));
		exit(1);
	}
	if (pcap_setfilter(pd, &code) < 0) {
		lg(LOG_ERR, "pcap_setfilter: %s", pcap_geterr(pd));
		exit(1);
	}
	if (rfilename == NULL)
		lg(LOG_INFO, "listening on %s", interface);

	/* Read in database */
	initializing = 1;
	if (!readdata())
		exit(1);
	sorteinfo();
#ifdef DEBUG
	if (debug > 2) {
		debugdump();
		exit(0);
	}
#endif
	initializing = 0;

	(void)setsignal(SIGINT, die);
	(void)setsignal(SIGTERM, die);
	(void)setsignal(SIGHUP, hup);
	if (rfilename == NULL) {
		(void)setsignal(SIGQUIT, checkpoint);
		(void)setsignal(SIGALRM, checkpoint);
		(void)alarm(CHECKPOINT);
	}

	switch (linktype) {

	case DLT_EN10MB:
		status = pcap_loop(pd, 0, process_ether, NULL);
		break;

	case DLT_FDDI:
		status = pcap_loop(pd, 0, process_fddi, NULL);
		break;

	default:
		lg(LOG_ERR, "bad linktype %d (can't happen)", linktype);
		exit(1);
	}
	if (status < 0) {
		lg(LOG_ERR, "pcap_loop: %s", pcap_geterr(pd));
		exit(1);
	}
	pcap_close(pd);
	if (!dump())
		exit(1);
	exit(0);
}

/* Process an ethernet arp/rarp packet */
void
process_ether(u_char *u, const struct pcap_pkthdr *h, const u_char *p)
{
	struct ether_header *eh;
	struct ether_arp *ea;
	u_char *sea, *sha;
	time_t t;
	u_int32_t sia;

	eh = (struct ether_header *)p;
	ea = (struct ether_arp *)(eh + 1);

	if (!sanity_ether(eh, ea, h->caplen))
		return;

	/* Source hardware ethernet address */
	sea = (u_char *)ESRC(eh);

	/* Source ethernet address */
	sha = (u_char *)SHA(ea);

	/* Source ip address */
	memmove(&sia, SPA(ea), 4);

	/* Skip excluded nets */
	if (toskip(sia))
		return;

	/* Watch for bogons */
	if (isbogon(sia)) {
		if (!quiet)
			dosyslog(LOG_INFO, "bogon", sia, sea, sha);
		return;
	}

	/* Watch for ethernet broadcast */
	if (memcmp(sea, zero, 6) == 0 || memcmp(sea, allones, 6) == 0 ||
	    memcmp(sha, zero, 6) == 0 || memcmp(sha, allones, 6) == 0) {
		if (!quiet)
			dosyslog(LOG_INFO, "ethernet broadcast", sia, sea, sha);
		return;
	}

	/* Ignore check for CARP-generated ARP replies */
	if (vrrpflag && memcmp(sha, vrrp_prefix, sizeof(vrrp_prefix)) == 0)
		return;

	/* Double check ethernet addresses */
	if (memcmp(sea, sha, 6) != 0) {
		if (!quiet)
			dosyslog(LOG_INFO, "ethernet mismatch", sia, sea, sha);
		return;
	}

	/* Got a live one */
	t = h->ts.tv_sec;
	can_checkpoint = 0;
	if (!ent_add(sia, sea, t, NULL))
		lg(LOG_ERR, "ent_add(%s, %s, %ld) failed",
		    intoa(sia), e2str(sea), (u_long)t);
	can_checkpoint = 1;

	if (docheckpoint) {
		checkpoint(0);
		docheckpoint = 0;
	}
}

/* Perform sanity checks on an ethernet arp/rarp packet, return true if ok */
int
sanity_ether(struct ether_header *eh, struct ether_arp *ea, int len)
{
	/* XXX use bsd style ether_header to avoid messy ifdef's */
	struct bsd_ether_header {
		u_char  ether_dhost[6];
		u_char  ether_shost[6];
		u_short ether_type;
	};
	u_char *shost = ((struct bsd_ether_header *)eh)->ether_shost;

	eh->ether_type = ntohs(eh->ether_type);
	ea->arp_hrd = ntohs(ea->arp_hrd);
	ea->arp_pro = ntohs(ea->arp_pro);
	ea->arp_op = ntohs(ea->arp_op);

	if (len < sizeof(*eh) + sizeof(*ea)) {
		lg(LOG_ERR, "short (want %d)\n",
		    (int)(sizeof(*eh) + sizeof(*ea)));
		return(0);
	}

	/* XXX sysv r4 seems to use hardware format 6 */
	if (ea->arp_hrd != ARPHRD_ETHER && ea->arp_hrd != 6) {
		lg(LOG_ERR, "%s sent bad hardware format 0x%x\n",
		    e2str(shost), ea->arp_hrd);
		return(0);
	}

	/* XXX hds X terminals sometimes send trailer arp replies */
	if (ea->arp_pro != ETHERTYPE_IP && ea->arp_pro != ETHERTYPE_TRAIL) {
		lg(LOG_ERR, "%s sent packet not ETHERTYPE_IP (0x%x)\n",
		    e2str(shost), ea->arp_pro);
		return(0);
	}

	if (ea->arp_hln != 6 || ea->arp_pln != 4) {
		lg(LOG_ERR, "%s sent bad addr len (hard %d, prot %d)\n",
		    e2str(shost), ea->arp_hln, ea->arp_pln);
		return(0);
	}

	/*
	 * We're only interested in arp requests, arp replies
	 * and reverse arp replies
	 */
	if (eh->ether_type == ETHERTYPE_ARP) {
		if (ea->arp_op != ARPOP_REQUEST &&
		    ea->arp_op != ARPOP_REPLY) {
			lg(LOG_ERR, "%s sent wrong arp op %d\n",
			     e2str(shost), ea->arp_op);
			return(0);
		}
	} else if (eh->ether_type == ETHERTYPE_REVARP) {
		if (ea->arp_op == REVARP_REQUEST) {
			/* no useful information here */
			return(0);
		} else if (ea->arp_op != REVARP_REPLY) {
			if (debug)
				lg(LOG_ERR, "%s sent wrong revarp op %d\n",
				    e2str(shost), ea->arp_op);
			return(0);
		}
	} else {
		lg(LOG_ERR, "%s sent bad type 0x%x\n",
		    e2str(shost), eh->ether_type);
		return(0);
	}

	return(1);
}

static void
bit_reverse(u_char *p, unsigned len)
{
	unsigned i;
	u_char b;

	for (i = len; i; i--, p++) {
		b = ((*p & 0x01) ? 0x80 : 0)
			| ((*p & 0x02) ? 0x40 : 0)
			| ((*p & 0x04) ? 0x20 : 0)
			| ((*p & 0x08) ? 0x10 : 0)
			| ((*p & 0x10) ? 0x08 : 0)
			| ((*p & 0x20) ? 0x04 : 0)
			| ((*p & 0x40) ? 0x02 : 0)
			| ((*p & 0x80) ? 0x01 : 0);
		*p = b;
	}
}

void
process_fddi(u_char *u, const struct pcap_pkthdr *h, const u_char *p)
{
	struct fddi_header *fh;
	struct ether_arp *ea;
	u_char *sea, *sha;
	time_t t;
	u_int32_t sia;

	fh = (struct fddi_header *)p;
	ea = (struct ether_arp *)(fh + 1);

	if (!swapped) {
		bit_reverse(fh->src, 6);
		bit_reverse(fh->dst, 6);
	}
	if (!sanity_fddi(fh, ea, h->caplen))
		return;

	/* Source MAC hardware ethernet address */
	sea = (u_char *)fh->src;

	/* Source ARP ethernet address */
	sha = (u_char *)SHA(ea);

	/* Source ARP ip address */
	memmove(&sia, SPA(ea), 4);

	/* Skip excluded nets */
	if (toskip(sia))
		return;

	/* Watch for bogons */
	if (isbogon(sia)) {
		if (!quiet)
			dosyslog(LOG_INFO, "bogon", sia, sea, sha);
		return;
	}

	/* Watch for ethernet broadcast */
	if (memcmp(sea, zero, 6) == 0 || memcmp(sea, allones, 6) == 0 ||
	    memcmp(sha, zero, 6) == 0 || memcmp(sha, allones, 6) == 0) {
		if (!quiet)
			dosyslog(LOG_INFO, "ethernet broadcast", sia, sea, sha);
		return;
	}

	/* Double check ethernet addresses */
	if (memcmp(sea, sha, 6) != 0) {
		if (!quiet)
			dosyslog(LOG_INFO, "ethernet mismatch", sia, sea, sha);
		return;
	}

	/* Got a live one */
	t = h->ts.tv_sec;
	can_checkpoint = 0;
	if (!ent_add(sia, sea, t, NULL))
		lg(LOG_ERR, "ent_add(%s, %s, %ld) failed",
		    intoa(sia), e2str(sea), (u_long)t);
	can_checkpoint = 1;

	if (docheckpoint) {
		checkpoint(0);
		docheckpoint = 0;
	}
}

/* Perform sanity checks on arp/rarp packet, return true if ok */
int
sanity_fddi(struct fddi_header *fh, struct ether_arp *ea, int len)
{
	u_char *shost = fh->src;
	u_short type, hrd, pro, op;

	/* This rather clunky copy stuff is needed because the fddi header
	 * has an odd (i.e. not even) length, causing memory alignment
	 * errors when attempts are made to access the arp header fields
	 * as shorts */

	memmove(&type, fh->snap.snap_type, sizeof(u_short));
	memmove(&hrd, &(ea->arp_hrd), sizeof(hrd));
	memmove(&pro, &(ea->arp_pro), sizeof(pro));
	memmove(&op, &(ea->arp_op), sizeof(op));

	type = ntohs(type);
	hrd = ntohs(hrd);
	pro = ntohs(pro);
	op = ntohs(op);

	if (len < sizeof(*fh) + sizeof(*ea)) {
		lg(LOG_ERR, "short (want %d)\n",
		    (int)(sizeof(*fh) + sizeof(*ea)));
		return(0);
	}

	/* XXX sysv r4 seems to use hardware format 6 */
	if (hrd != ARPHRD_ETHER && hrd != 6) {
		lg(LOG_ERR, "%s sent bad hardware format 0x%x\n",
		    e2str(shost), hrd);
		return(0);
	}

	/* XXX hds X terminals sometimes send trailer arp replies */
	if (pro != ETHERTYPE_IP &&
	    pro != ETHERTYPE_TRAIL &&
	    pro != ETHERTYPE_APOLLO) {
		lg(LOG_ERR, "%s sent packet not ETHERTYPE_IP (0x%x)\n",
		    e2str(shost), pro);
		return(0);
	}

	if (ea->arp_hln != 6 || ea->arp_pln != 4) {
		lg(LOG_ERR, "%s sent bad addr len (hard %d, prot %d)\n",
		    e2str(shost), ea->arp_hln, ea->arp_pln);
		return(0);
	}

	/*
	 * We're only interested in arp requests, arp replies
	 * and reverse arp replies
	 */
	if (type == ETHERTYPE_ARP) {
		if (op != ARPOP_REQUEST &&
		    op != ARPOP_REPLY) {
			lg(LOG_ERR, "%s sent wrong arp op %d\n",
			     e2str(shost), op);
			return(0);
		}
	} else if (type == ETHERTYPE_REVARP) {
		if (op == REVARP_REQUEST) {
			/* no useful information here */
			return(0);
		} else if (op != REVARP_REPLY) {
			if (debug)
				lg(LOG_ERR, "%s sent wrong revarp op %d\n",
				    e2str(shost), op);
			return(0);
		}
	} else {
		lg(LOG_ERR, "%s sent bad type 0x%x\n", e2str(shost), type);
		return(0);
	}
	return(1);
}

int
addnet(enum action action, const char *str)
{
	char *cp;
	int width;
	u_int32_t n, m;
	struct nets *np, *ns;
	char *cp2;
	char tstr[64];
	int size, ind;

	if (strlen(str) > sizeof(tstr) - 1)
		return(0);

	switch (action) {

	case ADD:
		size = nets_size;
		ind = nets_ind;
		ns = nets;
		break;

	case SKIP:
		size = skipnets_size;
		ind = skipnets_ind;
		ns = skipnets;
		break;

	default:
		(void)fprintf(stderr, "%s: addnet: unknown action: %d\n",
		    prog, action);
		exit(1);
	}

	if (size <= 0) {
		size = 8;
		ns = malloc(size * sizeof(*ns));
	} else if (size <= ind) {
		/* XXX debugging */
		size <<= 1;
		ns = realloc(ns, size * sizeof(*ns));
	}
	if (ns == NULL) {
		(void)fprintf(stderr, "%s: addnet: malloc/realloc: %s\n",
		    prog, strerror(errno));
		exit(1);
	}
	np = ns + ind;

	width = 0;
	strncpy(tstr, str, sizeof(tstr));
	tstr[sizeof(tstr) - 1] = '\0';
	cp = strchr(tstr, '/');
	if (cp != NULL) {
		*cp++ = '\0';
		width = strtol(cp, &cp2, 10);
		/* Trailing garbage */
		if (*cp2 != '\0')
			    return (0);
		if (width > 32)
			    return (0);
	}

	/* XXX hack */
	n = ntohl(inet_addr(tstr));
	while ((n & 0xff000000) == 0) {
		n <<= 8;
		if (n == 0)
			return (0);
	}
	n = htonl(n);

	if (width != 0) {
		m = ~0;
		m <<= 32 - width;
	} else if (IN_CLASSA(n))
		m = IN_CLASSA_NET;
	else if (IN_CLASSB(n))
		m = IN_CLASSB_NET;
	else if (IN_CLASSC(n))
		m = IN_CLASSC_NET;
	else if (IN_CLASSD(n))
		m = IN_CLASSD_NET;
	else
		return (0);
	m = htonl(m);

	np->net = n;
	np->netmask = m;
	++ind;

	if (action == ADD) {
		nets_size = size;
		nets_ind = ind;
		nets = ns;
	} else if (action == SKIP) {
		skipnets_size = size;
		skipnets_ind = ind;
		skipnets = ns;
	} else {
		(void)fprintf(stderr, "%s: addnet: unknown action: %d\n",
		    prog, action);
		exit(1);
	}

	return (1);
}

int
toskip(u_int32_t sia)
{
	int i;
	struct nets *np;

	for (i = 0, np = skipnets; i < skipnets_ind; ++i, ++np)
		if ((sia & np->netmask) == np->net)
			return (1);

	return (0);
}

int
isbogon(u_int32_t sia)
{
	int i;
	struct nets *np;

	if (nobogons)
		return (0);
	if ((sia & netmask) == net)
		return (0);
	for (i = 0, np = nets; i < nets_ind; ++i, ++np)
		if ((sia & np->netmask) == np->net)
			return (0);
	return (1);
}

void
checkpoint(int signo)
{
	if (!can_checkpoint)
		(void)alarm(1);
	else {
		(void)alarm(0);
		(void)dump();
		(void)alarm(CHECKPOINT);
	}
}

void
die(int signo)
{
	lg(LOG_DEBUG, "exiting");
	checkpoint(0);
	exit(1);
}

void
hup(int signo)
{
	if (can_checkpoint)
		checkpoint(0);
	else
		docheckpoint = 1;
}

void
lg(int pri, const char *fmt, ...)
{
	va_list ap;
	va_start(ap, fmt);
	vsyslog(pri, fmt, ap);
	va_end(ap);
}

void
usage(void)
{
	(void)fprintf(stderr, "Version %s\n", version);
	(void)fprintf(stderr,
	    "usage: %s [-CdFNpqsvzZ] [-D arpdir] [-f datafile]"
	    " [-i interface]\n\t"
	    " [-P pidfile] [-w watcher@email] [-W watchee@email]\n\t"
	    " [-n net[/width]] [-x net[/width]] [-r file]\n", prog);
	exit(1);
}
