/*
 * Copyright (c) 1996
 *	Ipsilon Networks, Inc.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. 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.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *      This product includes software developed by Ipsilon Networks, Inc.
 * 4. The name of Ipsilon Networks, Inc., may not be used to endorse or
 *    promote products derived from this software without specific prior
 *    written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY IPSILON NETWORKS, INC., ``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 IPSILON NETWORKS, INC., 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.
 *
 * $Id: tcpdpriv.c,v 1.3 2000/04/18 07:58:10 kjc Exp kjc $
 */

/*
 * tcpdpriv - make a tcpdump file private (so it can be shared)
 *
 * TODO:
 *
 *	1.  PRIVACY FOR LINK-LEVEL HEADER??? XXX ??? XXX ??? XXX ???
 *		(One method would be to have -L0 imply "convert to
 *		DLT_NULL; unforunately, libpcap doesn't support this.)
 *  	2.  -P|-T|-U >= 2
 *  	3.  Don't use tree for byte-wide counters; maybe not for 16-bit?
 *  	4.  If can tell via link hdr that is broadcast or multicast,
 *  	    	does that open up an attack on the destination net
 *  	    	encoding?
 *  	5.  Retain all zeros and all ones addresses?  (Actually, can
 *		you *safely* retain trailing 0s and trailing 1s?)
 *	6.  Use table to preserve classness.  (Actually, *without* this,
 *  	    	non class-D addresses may get mapped to class-D addresses?)
 *	8.  Should we retain local subnet broadcast information?
 *     11.  PRIVACY for TCP sequence numbers???
 *
 * $Id: tcpdpriv.c,v 1.3 2000/04/18 07:58:10 kjc Exp kjc $
 */

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#if	defined(SVR4)
#include <sys/statvfs.h>
#endif	/* defined(SVR4) */
#include <sys/param.h>
#include <sys/time.h>
#if	!defined(SVR4)
#include <sys/ucred.h>
#endif	/* !defined(SVR4) */
#include <sys/mount.h>
#include <sys/socket.h>
#if	defined(sun)
#include <sys/vfs.h>
#endif	/* defined(sun) */

#include <net/if.h>

#include <netinet/in.h>
#include <netinet/if_ether.h>
#if	!defined(SVR4)
#include <sys/mbuf.h>
#endif	/* !defined(SVR4) */
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#ifdef WIDE
#include <netinet/ip_icmp.h>
#include <arpa/nameser.h>
#endif

#if !defined(sun)
#include <net/slcompress.h>
#if	!defined(osf1)
#include <net/slip.h>
#endif	/* !defined(osf1) */
#include <netinet/if_fddi.h>
#include <net/if_llc.h>
#endif	/* !defined(sun) */

#include <netdb.h>

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#if	defined(sun)
#include <memory.h>
#endif	/* defined(sun) */
#include <signal.h>

#include <pcap.h>

/*
 * deal with systems in which bpf_int32 and bpf_u_int32 are not defined
 */
#if ((PCAP_VERSION_MAJOR < 2) || (PCAP_VERSION_MINOR < 4))
typedef int bpf_int32;
typedef u_int bpf_u_int32;
#endif /* ((PCAP_VERSION_MAJOR < 2) || (PCAP_VERSION_MINOR < 4)) */



/*
 * general defines...
 */

#define	GETNETSHORT(p)	((*(u_char *)(p)<<8)|(*(u_char *)((p)+1)))
#define	EXTRACT_BIT(value,bitno) (((value)>>(32-(bitno)))&1)

#define  NUM(a)  (sizeof (a)/(sizeof (a)[0]))

/*
 * Support for TTLs
 */

/* TTLs are <= (so, <= 128 --> continent-local) */
#define	MCAST_TTL_NODE_LOCAL		0
#define	MCAST_TTL_LINK_LOCAL		1
#define	MCAST_TTL_SITE_LOCAL		32
#define	MCAST_TTL_CONTINENT_LOCAL	128

#define	MCAST_OPT_NODE_LOCAL		90
#define	MCAST_OPT_LINK_LOCAL		80
#define	MCAST_OPT_SITE_LOCAL		70
#define	MCAST_OPT_CONTINENT_LOCAL	20
#define	MCAST_OPT_GLOBAL		10

/*
 * given a TTL, determine a value of opt_mcastaddr that will pass
 * an address with that TTL through unchanged.
 */
#define	ttlTOopt(ttl)	(\
	((ttl) <= MCAST_TTL_NODE_LOCAL) ? MCAST_OPT_NODE_LOCAL : \
	(((ttl) <= MCAST_TTL_LINK_LOCAL) ? MCAST_OPT_LINK_LOCAL : \
	(((ttl) <= MCAST_TTL_SITE_LOCAL) ? MCAST_OPT_SITE_LOCAL : \
	(((ttl) <= MCAST_TTL_CONTINENT_LOCAL) ? MCAST_OPT_CONTINENT_LOCAL : \
	MCAST_OPT_GLOBAL))))

#define	optTOttlLOW(opt) (\
	((opt) == MCAST_OPT_NODE_LOCAL) ? 0 : \
	(((opt) == MCAST_OPT_LINK_LOCAL) ? (MCAST_TTL_NODE_LOCAL+1) : \
	(((opt) == MCAST_OPT_SITE_LOCAL) ? (MCAST_TTL_LINK_LOCAL+1) : \
	(((opt) == MCAST_OPT_CONTINENT_LOCAL) ? (MCAST_TTL_SITE_LOCAL+1) : \
	(MCAST_TTL_CONTINENT_LOCAL+1)))))

#define	optTOttlHIGH(opt) (\
	((opt) == MCAST_OPT_NODE_LOCAL) ? MCAST_TTL_NODE_LOCAL : \
	(((opt) == MCAST_OPT_LINK_LOCAL) ? MCAST_TTL_LINK_LOCAL : \
	(((opt) == MCAST_OPT_SITE_LOCAL) ? MCAST_TTL_SITE_LOCAL : \
	(((opt) == MCAST_OPT_CONTINENT_LOCAL) ? MCAST_TTL_CONTINENT_LOCAL : \
	255))))

/*
 * byte ordering macros...
 */
#if	!defined(BYTE_ORDER)
/* OK, need to do byte order stuff... */
#define	LITTLE_ENDIAN	1234	/* LSB first: i386, vax */
#define	BIG_ENDIAN	4321	/* MSB first: 68000, ibm, net */

#if	defined(vax) || defined(i386)
#define	BYTE_ORDER	LITTLE_ENDIAN
#endif
#if	defined(mc68000) || defined(sparc)
#define	BYTE_ORDER	BIG_ENDIAN
#endif
#endif /* !defined(BYTE_ORDER) */

#if	!defined(NTOHL)
#if	(BYTE_ORDER == BIG_ENDIAN)

#define	NTOHL(x)
#define	NTOHS(x)
#define	HTONL(x)
#define	HTONS(x)

#endif	/* BYTE_ORDER == BIG_ENDIAN */

#if	(BYTE_ORDER == LITTLE_ENDIAN)

#define	NTOHL(x)	(x) = ntohl((u_long)x)
#define	NTOHS(x)	(x) = ntohs((u_short)x)
#define	HTONL(x)	(x) = htonl((u_long)x)
#define	HTONS(x)	(x) = htons((u_short)x)

#endif	/* (BYTE_ORDER == LITTLE_ENDIAN) */
#endif	/* !defined(NTOHL) */

#ifdef WIDE
/*
 * IPv6 support
 * required structures and macros are copied from IPv6 headers
 * in order to compile it on machines without IPv6 headers.
 */
#ifndef INET6_ADDRSTRLEN
/*
 * IPv6 address
 */
struct in6_addr {
	u_int8_t  s6_addr[16];		/* 128-bit IP6 address */
};

#define IN6_IS_ADDR_MULTICAST(a)	((a)->s6_addr[0] == 0xff)
#define IN6_IS_ADDR_LINKLOCAL(a)	\
	(((a)->s6_addr[0] == 0xfe) && (((a)->s6_addr[1] & 0xc0) == 0x80))
#define IN6_IS_ADDR_SITELOCAL(a)	\
	(((a)->s6_addr[0] == 0xfe) && (((a)->s6_addr[1] & 0xc0) == 0xc0))
#define IN6_IS_ADDR_UNSPECIFIED(a)	\
	((*(u_int32_t *)(&(a)->s6_addr[0]) == 0) &&	\
	 (*(u_int32_t *)(&(a)->s6_addr[4]) == 0) &&	\
	 (*(u_int32_t *)(&(a)->s6_addr[8]) == 0) &&	\
	 (*(u_int32_t *)(&(a)->s6_addr[12]) == 0))
#define IN6_IS_ADDR_LOOPBACK(a)		\
	((*(u_int32_t *)(&(a)->s6_addr[0]) == 0) &&	\
	 (*(u_int32_t *)(&(a)->s6_addr[4]) == 0) &&	\
	 (*(u_int32_t *)(&(a)->s6_addr[8]) == 0) &&	\
	 (*(u_int32_t *)(&(a)->s6_addr[12]) == ntohl(1)))
#define IN6_ARE_ADDR_EQUAL(a, b)			\
	(memcmp((a), (b), sizeof(struct in6_addr)) == 0)
#endif /* INET6_ADDRSTRLEN */

/* a macro to handle v6 address in 32-bit fields */
#define IN6ADDR32(a, i)	(*(u_int32_t *)(&(a)->s6_addr[(i)<<2]))

#ifndef IP6F_OFF_MASK
/*
 * Definition for internet protocol version 6.
 * RFC 2460
 */

struct ip6_hdr {
	union {
		struct ip6_hdrctl {
			u_int32_t ip6_un1_flow;	/* 20 bits of flow-ID */
			u_int16_t ip6_un1_plen;	/* payload length */
			u_int8_t  ip6_un1_nxt;	/* next header */
			u_int8_t  ip6_un1_hlim;	/* hop limit */
		} ip6_un1;
		u_int8_t ip6_un2_vfc;	/* 4 bits version, 4 bits class */
	} ip6_ctlun;
	struct in6_addr ip6_src;	/* source address */
	struct in6_addr ip6_dst;	/* destination address */
};

#define ip6_vfc		ip6_ctlun.ip6_un2_vfc
#define ip6_flow	ip6_ctlun.ip6_un1.ip6_un1_flow
#define ip6_plen	ip6_ctlun.ip6_un1.ip6_un1_plen
#define ip6_nxt		ip6_ctlun.ip6_un1.ip6_un1_nxt
#define ip6_hlim	ip6_ctlun.ip6_un1.ip6_un1_hlim
#define ip6_hops	ip6_ctlun.ip6_un1.ip6_un1_hlim
#endif /* IP6F_OFF_MASK */

#ifndef ICMP6_DST_UNREACH

struct icmp6_hdr {
	u_int8_t	icmp6_type;	/* type field */
	u_int8_t	icmp6_code;	/* code field */
	u_int16_t	icmp6_cksum;	/* checksum field */
	union {
		u_int32_t	icmp6_un_data32[1]; /* type-specific field */
		u_int16_t	icmp6_un_data16[2]; /* type-specific field */
		u_int8_t	icmp6_un_data8[4];  /* type-specific field */
	} icmp6_dataun;
};

#define ICMP6_DST_UNREACH		1	/* dest unreachable, codes: */
#define ICMP6_PACKET_TOO_BIG		2	/* packet too big */
#define ICMP6_TIME_EXCEEDED		3	/* time exceeded, code: */
#define ICMP6_PARAM_PROB		4	/* ip6 header bad */
#define ICMP6_ECHO_REQUEST		128	/* echo service */
#define ICMP6_ECHO_REPLY		129	/* echo reply */
#define ICMP6_MEMBERSHIP_QUERY		130	/* group membership query */
#define ICMP6_MEMBERSHIP_REPORT		131	/* group membership report */
#define ICMP6_MEMBERSHIP_REDUCTION	132	/* group membership termination */
#define ND_ROUTER_SOLICIT		133	/* router solicitation */
#define ND_ROUTER_ADVERT		134	/* router advertisment */
#define ND_NEIGHBOR_SOLICIT		135	/* neighbor solicitation */
#define ND_NEIGHBOR_ADVERT		136	/* neighbor advertisment */
#define ND_REDIRECT			137	/* redirect */

#endif /* ICMP6_DST_UNREACH */

#ifndef IPPROTO_IPV6
#define IPPROTO_IPV4		4 		/* IP header */
#define IPPROTO_IPV6		41		/* IP6 header */
#define IPPROTO_ICMPV6		58		/* ICMP6 */
#endif
#ifndef IPPROTO_HOPOPTS
#define IPPROTO_HOPOPTS		0		/* IP6 hop-by-hop options */
#define IPPROTO_ROUTING		43		/* IP6 routing header */
#define IPPROTO_FRAGMENT	44		/* IP6 fragmentation header */
#define IPPROTO_NONE		59		/* IP6 no next header */
#define IPPROTO_DSTOPTS		60		/* IP6 destination option */
#endif
#ifndef IPPROTO_ESP
#define	IPPROTO_ESP		50		/* SIPP Encap Sec. Payload */
#define	IPPROTO_AH		51		/* SIPP Auth Header */
#endif
#ifndef ETHERTYPE_IPV6
#define ETHERTYPE_IPV6		0x86dd	/* IPv6 */
#endif
#endif /* WIDE */

/*
 * function prototypes
 */

    /* macros for ansi/non-ansi compatibility */
#ifndef __P
#if defined(_USE_PROTOTYPES) && (defined(__STDC__) || defined(__cplusplus))
#define	__P(protos)	protos		/* full-blown ANSI C */
#else
#define	__P(protos)	()		/* traditional C preprocessor */
#endif
#endif

#if	defined(sun)
extern long random __P((void));
#endif	/* defined(sun) */


#if	defined(sun) && !defined(SVR4)
int 	fflush __P((FILE *stream));
int 	_flsbuf __P((int, FILE*));
int	fprintf __P((FILE *, const char *, ...));
int	getopt __P((int, char * const *, const char *));
extern int optind, opterr;
void	pcap_perror(pcap_t *, char *);
int	pclose __P((FILE *stream));
void	perror __P((const char *));
FILE	*popen __P((const char *command, const char *type));
int	printf __P((const char *, ...));
int 	setitimer __P((int which, struct itimerval value,
				    struct itimerval ovalue));
void	srandom __P((unsigned));
int	sscanf __P((const char *str, const char *format, ...));
int	statfs __P((const char *, struct statfs *));
#endif	/* defined(sun) && !defined(SVR4) */

#if	defined(sun)		/* why not defined in Solaris? */
int	gettimeofday __P((struct timeval *, struct timezone *));
#endif	/* defined(sun) */

#ifdef WIDE
static u_char *dumpip(u_char *p, int caplen, int length);
static u_char *dumpip6(u_char *p, int caplen, int length);
#endif

/*
 * typedefs...
 */

typedef struct node node_t, *node_p;	/* type of a tree node */

struct node {
    u_long
	input,		/* input value */
	output;		/* output value */
    node_p
	down[2];	/* children */
};

typedef struct nodehdr nodehdr_t, *nodehdr_p;	/* type of a tree */

struct nodehdr {
    u_long
	flags,		/* see below */
	addr_mask,	/* mask of bits to copy from input */
	counter,	/* for NH_FL_COUNTER */
	bump,		/* amount by which to bump counter */
	cur_input;	/* what address is currently being masked */
    node_p
	head;
};

#define	NH_FL_RANDOM_PROPAGATE	1	/* propagate random number down */
#define	NH_FL_COUNTER		2	/* bump a counter */

#ifdef WIDE
/* 128 bit version for IPv6 addresses */
typedef struct node6 node6_t, *node6_p;	/* type of a tree node */

struct node6 {
    struct in6_addr
	input,		/* input value */
	output;		/* output value */
    node6_p
	down[2];	/* children */
};

typedef struct node6hdr node6hdr_t, *node6hdr_p;	/* type of a tree */

struct node6hdr {
    u_long
	flags,		/* see below */
	addr_mask,	/* for ipv6, mask is used to preserve high oder bits */
	counter,	/* for NH_FL_COUNTER */
	bump;		/* amount by which to bump counter */
    struct in6_addr
	cur_input;	/* what address is currently being masked */
    node6_p
	head;
};

#endif /* WIDE */

/*
 * globally scoped variables
 */

/*
 * Trees for addressing.
 *
 * addr_propagate is for -A50.
 *
 * The 0x01000000 is to compensate for a bug in tcpdump (where
 * it has problems dealing with IP addresses that have zero (0)
 * in the high order byte).
 */

nodehdr_t
    addr_propagate = { NH_FL_RANDOM_PROPAGATE, 0xffffffff, 0x01000000 },
    addr_whole = { NH_FL_COUNTER, 0xffffffff, 0x01000000 },
	addr_upper = { NH_FL_COUNTER, 0xffff0000, 0x01000000 },
	addr_lower = { NH_FL_COUNTER, 0x0000ffff, 0},
	    addr_byte_0 = { NH_FL_COUNTER, 0xff000000, 0 },
	    addr_byte_1 = { NH_FL_COUNTER, 0x00ff0000, 0 },
	    addr_byte_2 = { NH_FL_COUNTER, 0x0000ff00, 0 },
	    addr_byte_3 = { NH_FL_COUNTER, 0x000000ff, 0 };
#ifdef WIDE
/* trees for IPv6 addresses */
/* currently, we preserve first 16 bits of original addresses */
node6hdr_t
    addr6_propagate = { NH_FL_RANDOM_PROPAGATE, 0xffff0000, 0 },
    addr6_whole = { NH_FL_COUNTER, 0xffff0000, 0 };
#endif

    /* trees for tcp ports */
nodehdr_t
    tcpport_whole,
    tcpport_byte_0, tcpport_byte_1;

    /* trees for udp ports */
nodehdr_t
    udpport_whole,
    udpport_byte_0, udpport_byte_1;

    /* options (from command line) */
#ifdef WIDE
/* set wide default values */
int opt_ipaddr = 50;
int opt_mcastaddr = 99;
int opt_tcpports = 99;
int opt_udpports = 99;
int opt_class = 4;
int opt_tcpipopts = 50;
#else
int
    opt_ipaddr, opt_mcastaddr, opt_tcpports, opt_udpports,
    opt_class;
#endif

int
    qflag = 0;		/* -q */

int
    pcap_dlt,		/* data link type of input file */
    pcap_snap;		/* snap length of input file */

    /* statistics */
int
    pktsin,		/* packets read in */
    pktsout,		/* packets written out */
    tooshort,		/* too short to be processed -- dropped */
    uncoded;		/* unsupported protocols -- dropped */

    /* FDDI support */
/*
 * This is a place where pcap is a bit messed up (should be two DLTs).
 */

#if defined(ultrix) || defined(__alpha)
#define FDDIPAD 3
#else
#define FDDIPAD 0
#endif

int fddipad = FDDIPAD;


#if !defined(FDDIFC_LLC_ASYNC)
/*
 * if we can't find any FDDI header files...
 */

struct fddi_header {
    u_char  fddi_fc;
    u_char  fddi_dhost[6];		/* destination */
    u_char  fddi_shost[6];		/* source */
};

#define	FDDIFC_LLC_ASYNC    0x50
#endif	/* !defined(FDDIFC_LLC_ASYNC) */

#if !defined(FDDIFC_CLFF)
#define	FDDIFC_CLFF 	    0xf0    	/* length/class/format bits */
#endif /* !defined(FDDIFC_CLFF) */


#if !defined(LLC_UI)
/*
 * if we can't find LLC header files...
 *
 * (this is a very minimal LLC header, sufficient only for our
 * limited needs.)
 */

struct llc {
    u_char  llc_dsap;	    	    	/* source SAP (service access point) */
    u_char  llc_ssap;	    	    	/* destination SAP */
    u_char  llc_control;		/* control byte (in some frames) */
};

#define	LLC_UI	    	0x03	    	/* this is an unnumbered info frame */
#define	LLC_SNAP_LSAP	0xaa		/* SNAP SAP */
#endif /* !defined(LLC_UI) */


    /* packet pointers */
u_char *pktbuffer;	/* where packet buffer is */
u_char *packetp;	/* where packet came from */
u_char *snapend;	/* last byte in packet */

pcap_t *pc;		/* our input file */


/*
 *   R   A   N   D   O   M
 */


/*
 * return 32-bits of random()
 *
 * (on most 32-bit machines, random() returns only 31 bits)
 */

static long
rand32()
{
#if	defined(SVR4)
    return ((lrand48()&0xffff)<<15)|(lrand48()&0xfff);
#else	/* defined(SVR4) */
    return ((random()&0xffff)<<16)|(random()&0xffff);
#endif	/* defined(SVR4) */
}

/*
 * run through an area, accumulating the values into a seed.
 */

static unsigned
rand_accum(unsigned prev, unsigned *px, int ints)
{
    /* now, sum it all, shifting all the time */
    while (ints--) {
	prev ^= *px++;
	prev = (prev<<1)|(prev>>31);
    }
    return prev;
}


/*
 * at startup, generate a seed for the random number generator
 *
 * (it is somewhat amusing how driven i am to say "sum = 0" and
 * "memset(&x, 0, sizeof x)" below, given that i *want* random
 * bits...)
 */

static void
rand_start(void)
{
    struct {
	struct timeval tv;
	struct timezone tz;
	uid_t uid;
	pid_t pid;
    } x;
    unsigned sum = 0;
    int n, gotline;
    unsigned line[200/sizeof (unsigned)];
    FILE *pfd;

    memset(&x, 0, sizeof x);

    if (gettimeofday(&x.tv, &x.tz) == -1) {
	perror("gettimeofday");
	exit(1);
    }
    x.uid = getuid();
    x.pid = getpid();

    /* now, sum it all, shifting all the time */
    sum = rand_accum(sum, (unsigned *)&x, sizeof x/sizeof(unsigned));

	/*
	 * we run through all the mounted file systems
	 * (as reported by mount, anyway) doing a stat
	 * on them.  note that SVR4 uses "mountpoint on device",
	 * whereas BSD uses "device on mountpoint".
	 */

    pfd = popen("/sbin/mount", "r");
    if (pfd == NULL) {
	pfd = popen("mount", "r");
	if (pfd == NULL) {
	    fprintf(stderr, "unable to popen() /sbin/mount or mount");
	    perror("");
	    exit(1);
	}
    }

    gotline = 0;
    while (fgets((char *)line, sizeof line, pfd) != NULL) {
#if	!defined(SVR4)
	struct statfs stat;
#else	/* !defined(SVR4) */
	struct statvfs stat;
#endif	/* !defined(SVR4) */
	char first[sizeof line], second[sizeof line];

	n = sscanf((char *)line, "%s on %s %*s\n", first, second);
	if (n != 2) {
	    fprintf(stderr, "ill-formatted output from mount(1) command\n");
	    exit(1);
	}
#if	!defined(SVR4)
	n = statfs(second, &stat);
#else	/* !defined(SVR4) */
	n = statvfs(first, &stat);
#endif	/* !defined(SVR4) */
	if (n == -1) {
#ifdef WIDE
		/* doesn't work with FreeBSD devfs.  just ignore. */
		continue;
#else
	    perror("statfs");
	    exit(1);
#endif
	}
	sum = rand_accum(sum, (unsigned *)&stat, sizeof stat/sizeof (unsigned));
	gotline = 1;
    }
    pclose(pfd);
    if (gotline == 0) {	/* nothing in output from mount command... */
	fprintf(stderr, "no output from mount(1) command\n");
	exit(1);
    }

	/*
	 * now, do the same as mount, but this time with "netstat -in"
	 */

    if (((pfd = popen("netstat -in", "r")) == NULL) &&
		((pfd = popen("/bin/netstat -in", "r")) == NULL) &&
		((pfd = popen("/usr/ucb/netstat -in", "r")) == NULL) &&
		((pfd = popen("/usr/sbin/netstat -in", "r")) == NULL) &&
		((pfd = popen("/usr/bin/netstat -in", "r")) == NULL)) {
	fprintf(stderr,
	   "unable to popen {,/bin/,/usr/ucb/,/usr/sbin,/usr/bin/}netstat -in");
	perror("");
	exit(1);
    }
    gotline = 0;
    while (fgets((char *)line, sizeof line, pfd) != NULL) {
	sum = rand_accum(sum, line, strlen((char *)line)/sizeof (unsigned));
	gotline = 1;
    }
    pclose(pfd);
    if (gotline == 0) {
	fprintf(stderr, "no output from 'netstat -in' command\n");
	exit(1);
    }

#if	defined(SVR4)
    srand48(sum);
#else	/* defined(SVR4) */
    srandom(sum);
#endif	/* defined(SVR4) */
}

/*
 *   U   T   I   L   I   Y         R   O   U   T   I   N   E   S
 */

/*
 * like ffs(3), but looking from the MSB.
 */

int
bi_ffs(u_long value)
{
    int add = 0;
    static u_char bvals[] = { 0, 4, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1 };

    if ((value&0xFFFF0000) == 0) {
	if (value == 0) {	/* zero input ==> zero output */
	    return 0;
	}
	add += 16;
    } else {
	value >>= 16;
    }
    if ((value&0xFF00) == 0) {
	add += 8;
    } else {
	value >>= 8;
    }
    if ((value&0xF0) == 0) {
	add += 4;
    } else {
	value >>= 4;
    }
    return add+bvals[value&0xf];
}

#ifdef WIDE
int is_normal_ipaddr(u_long addr)
{
    if (addr == INADDR_ANY || addr == INADDR_BROADCAST)
	return (0);

    /*
     * check private address range
     *  10.0.0.0 - 10.255.255.255
     *  172.16.0.0 - 172.31.255.255
     *  192.168.0.0 - 192.168.255.255
     */
    if ((addr & 0xff000000) == 0x0a000000 ||
	(addr & 0xfff00000) == 0xac100000 ||
	(addr & 0xffff0000) == 0xc0a80000)
	return (0);

    /* check localloop */
    if ((addr & 0xff000000) == 0x7f000000)
	return (0);
    
    /* check multicast */
    if (opt_mcastaddr >= 99 && IN_MULTICAST(addr))
	return (0);

    return (1);
}

int
bi_ffs6(struct in6_addr *addr)
{
    int value, i;

    for (i = 0; i < 4; i++) {
	    value = bi_ffs(ntohl(IN6ADDR32(addr, i)));
	    if (value != 0)
		return (i*32 + value);
    }
    return (0);
}

int extract_bit6(struct in6_addr *addr, int bitno)
{
    int value, bit, i;

    for (i = 0, bit = bitno; i < 4; i++, bit -= 32)
	if (bit <= 32) {
	    value = ntohl(IN6ADDR32(addr, i));
	    return ((value >> (32 - bit)) & 1);
	}
    return (0);
}

int is_normal_ip6addr(struct in6_addr *addr)
{
    if (IN6_IS_ADDR_UNSPECIFIED(addr))
	return (0);

    /* check localloop */
    if (IN6_IS_ADDR_LOOPBACK(addr))
	return (0);
    
    /* check multicast */
    if (opt_mcastaddr >= 99 && IN6_IS_ADDR_MULTICAST(addr))
	return (0);

    return (1);
}
#endif /* WIDE */

/*
 * Subtract a quantity from a standard IP checksum (network order)
 *
 * (We define the arguments u_*long* (rather than u_short)
 * to allow us operate inplace on [hopefully] 32 bit operands.)
 */

static u_short
cksum_subtract(u_long sum, u_long subtrahend)
{
    NTOHS(sum);
    NTOHL(subtrahend);
    subtrahend = ~subtrahend;
    subtrahend = (subtrahend&0xffff) + ((subtrahend>>16)&0xffff);
    sum = (0xffff&~sum) + subtrahend;
    sum = (sum&0xffff)+((sum>>16)&0xffff);
    sum = (sum&0xffff)+((sum>>16)&0xffff);      /* That's enough */

    sum = 0xffff&~sum;
    HTONS(sum);
    return (u_short)sum;
}


/*
 * Add a quantity to a standard IP checksum (network order)
 *
 * (We define the arguments u_*long* (rather than u_short)
 * to allow us operate inplace on [hopefully] 32 bit operands.)
 */

static u_short
cksum_add(u_long sum, u_long well)
{
    NTOHS(sum);
    NTOHL(well);
    well = (well&0xffff) + ((well>>16)&0xffff);
    sum = (0xffff&~sum) + well;
    sum = (sum&0xffff)+((sum>>16)&0xffff);
    sum = (sum&0xffff)+((sum>>16)&0xffff);      /* That's enough */

    sum = 0xffff&~sum;
    HTONS(sum);
    return (u_short)sum;
}

/*
 * Adjust the checksum based on a second sum (network order)
 */

static u_short
cksum_adjust(u_long sum, u_long sum2)
{
    return cksum_subtract(sum, sum2);		/* XXX just for now!!! */
}

/*
 * parse a string to determine the number of hh:mm:ss to run, and
 * then set an alarm to trigger after that number of seconds.
 */

void
setalarm(char *alarm)
{
    int n, aa, bb, cc, secs;
    struct itimerval itv;

#if	!defined(ITIMER_REAL)
#define	ITIMER_REAL	0
#endif	/* !defined(ITIMER_REAL) */

    n = sscanf(alarm, "%d:%d:%d", &aa, &bb, &cc);
    switch (n) {
    case 0:
	fprintf(stderr, "Invalid alarm value %s\n", alarm);
	exit(2);
	break;
    case 1:
	secs = aa;
	break;
    case 2:
	secs = (aa*60)+bb;
	break;
    case 3:
	secs = (aa*60*60)+(bb*60)+cc;
	break;
    default:
	fprintf(stderr, "Invalid alarm value %s\n", alarm);
	exit(2);
	break;
    }

    itv.it_value.tv_sec = secs;
    itv.it_value.tv_usec = 0;
    itv.it_interval.tv_usec = itv.it_interval.tv_sec = 0;

    if (setitimer(ITIMER_REAL, &itv, 0) < 0) {
	perror("setitimer");
	exit(2);
    }
}


/*
 *   T   R   E   E         R   O   U   T   I   N   E   S
 */

static node_p
newnode(void)
{
    node_p node;

    node = (node_p) malloc(sizeof *node);

    if (node == 0) {
	fprintf(stderr, "malloc failed %s:%d\n", __FILE__, __LINE__);
	exit(2);
    }
    return node;
}


static void
freetree(node_p node)
{
    node_p next;

    while (node) {
	next = node->down[0];
	if (node->down[1]){
	    freetree(node->down[1]);
	}
	free(node);
	node = next;
    }
}

#ifdef WIDE
static node6_p
newnode6(void)
{
    node6_p node;

    node = (node6_p) malloc(sizeof *node);

    if (node == 0) {
	fprintf(stderr, "malloc failed %s:%d\n", __FILE__, __LINE__);
	exit(2);
    }
    return node;
}

static void
freetree6(node6_p node)
{
    node6_p next;

    while (node) {
	next = node->down[0];
	if (node->down[1]){
	    freetree6(node->down[1]);
	}
	free(node);
	node = next;
    }
}
#endif /* WIDE */

/*
 *    M   A   S   K   I   N   G
 */

/*
 * figure out what the output for a given input should be.
 *
 * 	value		the old output
 *	flip		bit (MSB == 0) at which inputs differ
 *	hdr		the tree we are in
 *
 * note that only hide_addr() sets cur_input (and, that only the "addr"
 * trees set addr_mask).
 *
 * also, addr_mask is munged (by lookup_init()) to have at most
 * opt_class high order bits set as one.
 */

static inline u_long
make_output(u_long value, int flip, nodehdr_p hdr)
{
    if (hdr->flags&NH_FL_RANDOM_PROPAGATE) {
			/*
			 * the output is:
			 * bits 1-(flip-1):	copied from value
			 * bit  flip:		flip bit (XOR with 1) in value
			 * bits (flip+1)-32:	random
			 */
#ifdef WIDE
	int retry = 0;
	while (1) {
	    if (flip == 32) {
		return value^1;
	    } else {		/* get left AND flipped bit */
		value = ((((value>>(32-flip))^1)<<(32-flip)) |
			 ((rand32()&0x7fffffff)>>flip));
		if (retry > 10 || is_normal_ipaddr(value))
		    return (value);
	    }
	    retry++;
	}
#else
	if (flip == 32) {
	    return value^1;
	} else {		/* get left AND flipped bit */
	    return ((((value>>(32-flip))^1)<<(32-flip)) |
			((rand32()&0x7fffffff)>>flip)); /* and get right part */
	}
#endif
    } else if (hdr->flags&NH_FL_COUNTER) {
	hdr->counter += hdr->bump;
	/* now, do we need to copy any bits from head? */
	if (hdr->addr_mask) {
	    int n;
	    u_long m;
	    /*
	     * retain consecutive high order ONE (1) bits from
	     * cur_input.  number of consecutive high order one
	     * bits to retain is constrained by addr_mask.
	     */
	    n = bi_ffs(~hdr->cur_input);	/* n == first ZERO (0) bit */
	    if (n) {
		m = hdr->cur_input>>(32-n);
		return hdr->counter|((m<<(32-n))&hdr->addr_mask);
	    } else {
		/* n == 0 ==> cur_input all ones */
		return hdr->counter&hdr->addr_mask;
	    }
	}
	return hdr->counter;
    } else {
	fprintf(stderr, "unknown flags field %s:%d\n", __FILE__, __LINE__);
	exit(2);
    }
}


/*
 * make a peer that corresponds to input.  return input's node.
 */

static inline node_p
make_peer(u_long input, node_p old, nodehdr_p hdr)
{
    node_p down[2];
    int swivel, bitvalue;

    /*
     * become a peer
     * algo: create two nodes, the two peers.  leave orig node as
     * the parent of the two new ones.
     */

    down[0] = newnode();
    down[1] = newnode();

    swivel = bi_ffs(input^old->input);
    bitvalue = EXTRACT_BIT(input, swivel);

    down[bitvalue]->input = input;
    down[bitvalue]->output = make_output(old->output, swivel, hdr);
    down[bitvalue]->down[0] = down[bitvalue]->down[1] = 0;

    *down[1-bitvalue] = *old;	    /* copy orig node down one level */

    old->input = down[1]->input;    /* NB: 1s to the right (0s to the left) */
    old->output = down[1]->output;
    old->down[0] = down[0];	    /* point to children */
    old->down[1] = down[1];

    return down[bitvalue];
}

#ifdef WIDE

/*
 * 128 bit version of make_output()
 * note: ipv6 addresses are in network byte order.
 */
static void
make_output6(struct in6_addr *old, int flip, node6hdr_p hdr,
	     struct in6_addr *new)
{
    u_int32_t value;
    int i, swivel;
    
    if (hdr->flags&NH_FL_RANDOM_PROPAGATE) {
	int retry = 0;
			/*
			 * the output is:
			 * bits 1-(flip-1):	copied from value
			 * bit  flip:		flip bit (XOR with 1) in value
			 * bits (flip+1)-128:	random
			 */
	while (1) {
	    for (i = 0, swivel = flip; i < 4; i++, swivel -= 32) {
		if (swivel > 32) {
		    /*
		     * flip bit is below this 32 bits.
		     * just copy the original value.
		     */
		    IN6ADDR32(new, i) = IN6ADDR32(old, i);
		}
		else if (swivel < 0) {
		    /*
		     * flip bit is above this 32 bits.
		     * scramble the entire 32 bits.
		     */
		    IN6ADDR32(new, i) = rand32();
		}
		else {
		    /* flip bit is in this 32 bits */
		    value = ntohl(IN6ADDR32(old, i));
		    if (swivel == 32) {
			value = value ^ 1;
		    } else {		/* get left AND flipped bit */
			value = (((value >> (32-swivel)) ^ 1) << (32-swivel)) |
			    ((rand32() & 0x7fffffff) >> swivel);
		    }
		    IN6ADDR32(new, i) = htonl(value);
		}
	    }
	    if (retry > 10 || is_normal_ip6addr(new))
		return;
	    retry++;
	}
    } else if (hdr->flags&NH_FL_COUNTER) {
	/* we support only Level 0 (interger) mapping */
	hdr->counter += hdr->bump;
	IN6ADDR32(new, 0) = IN6ADDR32(new, 1) = IN6ADDR32(new, 2) = 0;
	IN6ADDR32(new, 3) = htonl(hdr->counter);
    } else {
	fprintf(stderr, "unknown flags field %s:%d\n", __FILE__, __LINE__);
	exit(2);
    }
}

static node6_p
make_peer6(struct in6_addr *input, node6_p old, node6hdr_p hdr)
{
    node6_p down[2];
    int swivel, bitvalue, i;
    struct in6_addr tmp;

    /*
     * become a peer
     * algo: create two nodes, the two peers.  leave orig node as
     * the parent of the two new ones.
     */

    down[0] = newnode6();
    down[1] = newnode6();

    for (i = 0; i < 4; i++)
	IN6ADDR32(&tmp, i) = IN6ADDR32(input, i) ^ IN6ADDR32(&old->input, i);
    swivel = bi_ffs6(&tmp);
    bitvalue = extract_bit6(input, swivel);

    down[bitvalue]->input = *input;
    down[bitvalue]->down[0] = down[bitvalue]->down[1] = 0;
    make_output6(&old->output, swivel, hdr, &down[bitvalue]->output);
#if 1
    /*
     * heuristics for ipv6 address readability:
     * 1. copy the high order bits from the original
     *    (16 bits are specified at the initialization of the tree head)
     * 2. when address is randamized, preserve 0x00 and 0xff octet
     *    in the original address
     */
    if (hdr->addr_mask) {
	IN6ADDR32(&down[bitvalue]->output, 0) =
	    (IN6ADDR32(input, 0) & htonl(hdr->addr_mask)) |
	    (IN6ADDR32(&down[bitvalue]->output, 0) & ~htonl(hdr->addr_mask));
    }
    if (hdr->flags & NH_FL_RANDOM_PROPAGATE) {
	int i, bit;

	for (i = 0, bit = swivel; i < 16; i++, bit -= 8)
	    if (bit < 0) {
		/* this octet is below the flip bit and randamized */
		if (input->s6_addr[i] == 0x00)
		    down[bitvalue]->output.s6_addr[i] = 0x00;
		else if (input->s6_addr[i] == 0xff)
		    down[bitvalue]->output.s6_addr[i] = 0xff;
	    }
    }
#endif

    *down[1-bitvalue] = *old;	    /* copy orig node down one level */

    old->input = down[1]->input;    /* NB: 1s to the right (0s to the left) */
    old->output = down[1]->output;
    old->down[0] = down[0];	    /* point to children */
    old->down[1] = down[1];

    return down[bitvalue];
}

#endif /* WIDE */

/*
 *   L   O   O   K   U   P
 */

/*
 * initialize a lookup structure.
 *
 * addr_mask is non-zero if this is a header for IP addresses,
 * in which case it is a mask of the bits covered in the IP
 * address by this header.
 */

static void
lookup_init(nodehdr_p hdr)
{
    node_p node;

    if (hdr->head) {
	freetree(hdr->head);
	hdr->head = 0;
    }

    /*
     * this is all a bit cryptic, so here's the deal
     *
     * if addr_mask is zero, or doesn't cover any of the
     * classness bits preserved by -Cnn, then we create
     * exactly one node whose input value is zero, and whose
     * output value is random.
     *
     * on the other hand, if addr_mask covers some of the
     * classness bits, we create a node which performs the
     * identity map on those bits in addr_mask covered by
     * -Cnn and the rest of which is random.
     */

    hdr->head = newnode();
    node = hdr->head;

    /* if this is high order address byte, prime classness if needed */
    if (hdr->addr_mask) {
	/* compute bump as lsb of addr_mask */
	hdr->bump = 1<<(ffs(hdr->addr_mask)-1);	/* NOTE -- traditional ffs() */
	if (hdr->flags == NH_FL_COUNTER) {
	    node->output = hdr->bump;
	} else {
	    /* whatever we do, don't pick up any bits outside of addr_mask */
		/* zeros for high order opt_class bits */
	    node->output = rand32()>>opt_class;
		/* no bits outside of addr_mask */
	    node->output &= hdr->addr_mask;
	}
	if (opt_class) {
	    /* extract bits in addr_mask covered by opt_class */
	    hdr->addr_mask = hdr->addr_mask>>(32-opt_class);
	    hdr->addr_mask = hdr->addr_mask<<(32-opt_class);
	    node->input = hdr->addr_mask;
	    node->output |= hdr->addr_mask;
	} else {
	    hdr->addr_mask = 0;
	    node->input = 0;
	}
    } else {
	node->input = 0;
	/*
	 * by using rand32(), we get bit 0 (MSB) randomized;
	 * passing 0 wouldn't do at all...
	 */
	node->output = rand32();
	hdr->bump = 1;
    }

    node->down[0] = node->down[1] = 0;
}


/*
 * EVERY NON-LEAF NODE HAS ***2*** CHILDREN!!!
 * (otherwise, the code below dies badly!)
 */

u_long
lookup(u_long input, nodehdr_p hdr)
{
    node_p node;
    int swivel;

    node = hdr->head;	/* non-zero, 'cause lookup_init() already called */
    if (hdr->head == 0) {	/* (but...) */
	fprintf(stderr, "unexpected zero head %s:%d\n", __FILE__, __LINE__);
    }

    while (node) {
	if (input == node->input) {	/* we found our node! */
	    return node->output;
	}
	if (node->down[0] == 0) {	/* need to descend, but can't */
	    node = make_peer(input, node, hdr);		/* create a peer */
	} else {
	    /* swivel is the first bit the left and right children differ in */
	    swivel = bi_ffs(node->down[0]->input^node->down[1]->input);
	    if (bi_ffs(input^node->input) < swivel) {/* input differs earlier */
		node = make_peer(input, node, hdr);  /* make a peer */
	    } else if (input&(1<<(32-swivel))) {
		node = node->down[1];	    /* NB: 1s to the right */
	    } else {
		node = node->down[0];	    /* NB: 0s to the left */
	    }
	}
    }

    /* ??? should not occur! */
    fprintf(stderr, "unexpected loop termination %s:%d\n", __FILE__, __LINE__);
    exit(1);
}

#ifdef	DEBUG
void
dumptable(node_p node, int level)
{
    int i;

    while (node) {
	for (i = 0; i < level; i++) {
	    putchar('.');
	}
	printf("0x%lx 0x%lx\n", node->input, node->output);
	level++;
	if (node->down[0]) {
	    dumptable(node->down[0], level);
	}
	node = node->down[1];
    }
}
#endif	/* DEBUG */

#ifdef WIDE
static void
lookup_init6(node6hdr_p hdr)
{
    node6_p node;

    if (hdr->head) {
	freetree6(hdr->head);
	hdr->head = 0;
    }

    hdr->head = newnode6();
    node = hdr->head;
    memset(&node->input, 0, sizeof(struct in6_addr));

    hdr->bump = 1;	/* level 0 counter only */
    if (hdr->flags == NH_FL_COUNTER) {
	IN6ADDR32(&node->output, 0) = 0;
	IN6ADDR32(&node->output, 1) = 0;
	IN6ADDR32(&node->output, 2) = 0;
	IN6ADDR32(&node->output, 3) = hdr->bump;
    } else {
	IN6ADDR32(&node->output, 0) = rand32();
	IN6ADDR32(&node->output, 1) = rand32();
	IN6ADDR32(&node->output, 2) = rand32();
	IN6ADDR32(&node->output, 3) = rand32();
    }
    if (opt_class) {
	IN6ADDR32(&node->input, 0) = htonl(hdr->addr_mask);
	IN6ADDR32(&node->output, 0) |= htonl(hdr->addr_mask);
    } else {
	hdr->addr_mask = 0;
    }
    node->down[0] = node->down[1] = 0;
}

void
lookup6(struct in6_addr *input, node6hdr_p hdr, struct in6_addr *output)
{
    node6_p node;
    int swivel, i;
    struct in6_addr tmp;

    node = hdr->head;	/* non-zero, 'cause lookup_init() already called */
    if (hdr->head == 0) {	/* (but...) */
	fprintf(stderr, "unexpected zero head %s:%d\n", __FILE__, __LINE__);
    }

    while (node) {
	if (IN6_ARE_ADDR_EQUAL(input, &node->input)) {
	    /* we found our node! */
	    *output = node->output;
	    return;
	}
	if (node->down[0] == 0) {	/* need to descend, but can't */
	    node = make_peer6(input, node, hdr);	/* create a peer */
	} else {
	    /* swivel is the first bit the left and right children differ in */
	    for (i = 0; i < 4; i++)
		IN6ADDR32(&tmp, i) = IN6ADDR32(&node->down[0]->input, i)
		    ^ IN6ADDR32(&node->down[1]->input, i);
	    swivel = bi_ffs6(&tmp);
	    for (i = 0; i < 4; i++)
		IN6ADDR32(&tmp, i) = IN6ADDR32(input, i)
			^ IN6ADDR32(&node->input, i);
	    if (bi_ffs6(&tmp) < swivel) {/* input differs earlier */
		node = make_peer6(input, node, hdr);  /* make a peer */
	    } else if (extract_bit6(input, swivel)) {
		node = node->down[1];	    /* NB: 1s to the right */
	    } else {
		node = node->down[0];	    /* NB: 0s to the left */
	    }
	}
    }

    /* ??? should not occur! */
    fprintf(stderr, "unexpected loop termination %s:%d\n", __FILE__, __LINE__);
    exit(1);
}

#endif /* WIDE */

/*
 *   H   I   D   I   N   G
 */


u_long
hide_addr(u_long addr, u_int ttl)
{
    u_long answer;

    if (IN_CLASSD(addr) && (ttl >= optTOttlLOW(opt_mcastaddr))) {
	return addr;
    }

#ifdef WIDE
    if (!is_normal_ipaddr(addr))
	return (addr);
#endif

    switch (opt_ipaddr) {
    case 0:
	addr_whole.cur_input = addr;
	answer = lookup(addr, &addr_whole);
	break;
    case 1:
	addr_upper.cur_input = addr_lower.cur_input = addr;
	answer = lookup(addr&0xffff0000, &addr_upper) |
			    lookup(addr&0xffff, &addr_lower);
	break;
    case 2:
	addr_byte_0.cur_input =
	    addr_byte_1.cur_input =
	    addr_byte_2.cur_input =
	    addr_byte_3.cur_input = addr;
	/* if i had a hammer... */
	answer =
		lookup(addr&0xff000000, &addr_byte_0) |
		lookup(addr&0x00ff0000, &addr_byte_1) |
		lookup(addr&0x0000ff00, &addr_byte_2) |
		lookup(addr&0x000000ff, &addr_byte_3);
	break;
    case 50:
	addr_propagate.cur_input = addr;
	answer = lookup(addr, &addr_propagate);
	break;
    case 99:
	answer = addr;
	break;
    default:
	fprintf(stderr, "unknown opt_ipaddr %s:%d\n", __FILE__, __LINE__);
	exit(1);
    }

    return answer;
}

u_short
hide_port(u_short port, nodehdr_p whole, nodehdr_p msb, nodehdr_p lsb, int opt)
{
    switch (opt) {
    case 0:
	return lookup(port, whole);
    case 1:
	return (lookup((port>>8)&0xff, msb)<<8) |
			    lookup(port&0xff, lsb);
    case 99:
	return port;
    default:
	fprintf(stderr, "unknown ports %s:%d\n", __FILE__, __LINE__);
	exit(1);
    }
}


u_short
hide_tcpport(u_short tcpport)
{
    return hide_port(tcpport,
	    &tcpport_whole, &tcpport_byte_0, &tcpport_byte_1, opt_tcpports);
}

u_short
hide_udpport(u_short udpport)
{
    return hide_port(udpport,
	    &udpport_whole, &udpport_byte_0, &udpport_byte_1, opt_udpports);
}

#ifdef WIDE
void
hide_addr6(struct in6_addr *inaddr, struct in6_addr *outaddr, u_int hlimit)
{

    if (!is_normal_ip6addr(inaddr)) {
	/* no conversion */
	*outaddr = *inaddr;
#if 1
	/*
	 * special case for solicited-node multicast address that is
	 * formed by taking the low-order 24 bits of the address
	 * (unicast or anycast) and appending those bits to the prefix
	 * FF02:0:0:0:0:1:FF00::/104
	 *
	 * we just clear the low-order 24 bits for now.
	 */
	if (opt_mcastaddr >= 99 && IN6_IS_ADDR_MULTICAST(outaddr) &&
	    outaddr->s6_addr[1] == 0x02 &&
	    outaddr->s6_addr[2] == 0 && outaddr->s6_addr[3] == 0 &&
	    outaddr->s6_addr[4] == 0 && outaddr->s6_addr[5] == 0 &&
	    outaddr->s6_addr[6] == 0 && outaddr->s6_addr[7] == 0 &&
	    outaddr->s6_addr[8] == 0 && outaddr->s6_addr[9] == 0 &&
	    outaddr->s6_addr[10] == 0 && outaddr->s6_addr[11] == 0x01 &&
	    outaddr->s6_addr[12] == 0xff) {
	    outaddr->s6_addr[13] = outaddr->s6_addr[14] =
		outaddr->s6_addr[15] = 0;
	}
#endif
	return;
    }

    switch (opt_ipaddr) {
    case 0:
	addr6_whole.cur_input = *inaddr;
	lookup6(inaddr, &addr6_whole, outaddr);
	break;
    case 1:
    case 2:
	fprintf(stderr, "we don't support -A1 and -A2 for IPv6\n");
	exit(1);
    case 50:
	addr6_propagate.cur_input = *inaddr;
	lookup6(inaddr, &addr6_propagate, outaddr);
	break;
    case 99:
	*outaddr = *inaddr;
	break;
    default:
	fprintf(stderr, "unknown opt_ipaddr %s:%d\n", __FILE__, __LINE__);
	exit(1);
    }

    return;
}

#endif /* WIDE */

/*
 *   T   C   P
 */


/* 
 * deal with TCP options
 */

static u_char *
dumptcpoptions(u_char *p, int caplen, int length, struct tcphdr *tcp)
{
    u_short *usp;
    int optlen;
    u_long sumoff;

    usp = (u_short *)p;
    optlen = (tcp->th_off*4)-sizeof *tcp;
#ifdef WIDE
    if (opt_tcpipopts >= 50) {
	/* preserve tcp options */
	while ((optlen >= 2) && (caplen >= 2)) {
	    usp++; optlen -= 2; caplen -= 2;
	}
	return (u_char *)usp;
    }
#endif
    sumoff = 0;		/* reset this... */
    while ((optlen >= 2) && (caplen >= 2)) {
	sumoff = cksum_subtract(sumoff, *usp);
	*usp = ntohs(0x0101);  /* no ops (doesn't need ntohs(), but...) */
	sumoff = cksum_add(sumoff, *usp);
	usp++; optlen -= 2; caplen -= 2;
    }
    tcp->th_sum = cksum_adjust(tcp->th_sum, sumoff);

    return (u_char *)usp;
}


/*
 * Munge a TCP header.
 *
 * Input:
 *	p		location of first byte of TCP header
 *	caplen		bytes (from p) captured
 *	length		bytes (from p) in current datagram (may not be captured)
 *	phoffset	how much pseudo header checksum changed during
 *			IP munging
 *
 * Output:
 *  	pointer to byte *past* last byte munged (so, first byte of
 *  	    TCP user data)
 */

static u_char *
dumptcp(u_char *p, int caplen, int length, u_long phoffset)
{
    u_short inport, outport;
    struct tcphdr *tcp = (struct tcphdr *)p;

    /* source port */
    if (caplen < 2) {
	return p+caplen;
    }
    inport = ntohs(tcp->th_sport);
    outport = hide_tcpport(inport);
    if (inport != outport) {
	phoffset = cksum_subtract(phoffset, tcp->th_sport);
	tcp->th_sport = htons(outport);
	phoffset = cksum_add(phoffset, tcp->th_sport);
    }
    caplen -= 2; length -= 2; p += 2;

    /* destination port */
    if (caplen < 2) {
	return p+caplen;
    }
    inport = ntohs(tcp->th_dport);
    outport = hide_tcpport(inport);
    if (inport != outport) {
	phoffset = cksum_subtract(phoffset, tcp->th_dport);
	tcp->th_dport = htons(outport);
	phoffset = cksum_add(phoffset, tcp->th_dport);
    }
    caplen -= 2; length -= 2; p += 2;

    /* seq, ack, off, flags, win */
    if (caplen < 12) {
	return p+caplen;
    }
    caplen -= 12; length -= 12; p += 12;

    /* sum */
    if (caplen < 2) {
	return p+caplen;
    }
    tcp->th_sum = cksum_adjust(tcp->th_sum, phoffset);
    caplen -= 2; length -= 2; p += 2;

    /* urgent pointer */
    if (caplen < 2) {
	return p+caplen;
    }
    caplen -= 2; length -= 2; p += 2;

    /* now, deal with options... */
    if ((tcp->th_off*4) > sizeof *tcp) {
	u_char *newp = dumptcpoptions(p, caplen, length, tcp);
	int diff = newp-p;

	p += diff; caplen -= diff; length -= diff;
    }

    return p;
}

/*
 *    U   D   P
 */
#ifdef WIDE
/* dump dns: leave just 12-byte header */
static u_char *
dumpdns(u_char *p, int caplen, int length)
{
    int len = 12;

    if (caplen < len) {
	return p+caplen;
    }

    len = MIN(caplen, len);
    caplen -= len; length -= len; p += len;

    /* don't return any DNS data */
    return p;
}
#endif /* WIDE */

/* 
 * dump a udp packet.  we don't do much, just mask the ports
 * and update the checksum (if necessary).
 *
 * Input:
 *	p		location of first byte of UDP header
 *	caplen		bytes (from p) captured
 *	length		bytes (from p) in current datagram (may not be captured)
 *	phoffset	how much pseudo header checksum changed during
 *			IP munging
 *
 * Output:
 *  	pointer to byte *past* last byte munged (so, first byte of
 *  	    UDP user data)
 */

static u_char *
dumpudp(u_char *p, int caplen, int length, u_long phoffset)
{
    u_short inport, outport;
    struct udphdr *udp = (struct udphdr *)p;
#ifdef WIDE
    u_short sport = 0, dport = 0;
#endif

    /* source port */
    if (caplen < 2) {
	return p+caplen;
    }
    inport = ntohs(udp->uh_sport);
    outport = hide_udpport(inport);
    if (inport != outport) {
	phoffset = cksum_subtract(phoffset, udp->uh_sport);
	udp->uh_sport = htons(outport);
	phoffset = cksum_add(phoffset, udp->uh_sport);
    }
#ifdef WIDE
    else
	sport = inport;
#endif
    caplen -= 2; length -= 2; p += 2;

    /* destination port */
    if (caplen < 2) {
	return p+caplen;
    }
    inport = ntohs(udp->uh_dport);
    outport = hide_udpport(inport);
    if (inport != outport) {
	phoffset = cksum_subtract(phoffset, udp->uh_dport);
	udp->uh_dport = htons(outport);
	phoffset = cksum_add(phoffset, udp->uh_dport);
    }
#ifdef WIDE
    else
	dport = inport;
#endif
    caplen -= 2; length -= 2; p += 2;

    /* length */
    if (caplen < 2) {
	return p+caplen;
    }
    caplen -= 2; length -= 2; p += 2;
    /* nothing to do with length */

    /* checksum */
    if (caplen < 2) {
	return p+caplen;
    }
    /* deal with checksum ... */
    if (udp->uh_sum != 0) {
	udp->uh_sum = cksum_adjust(udp->uh_sum, phoffset);
    }
    caplen -= 2; length -= 2; p += 2;

#ifdef WIDE
    if (caplen < 2)
	return p+caplen;

#define ISPORT(p) (dport == (p) || sport == (p))
    if (ISPORT(NAMESERVER_PORT)) {
	p = dumpdns(p, caplen, length);
    }
#endif /* WIDE */

    /* don't return any UDP data */
    return p;
}

#ifdef WIDE
/*
 *    I   C   M   P
 */


/* 
 * dump an icmp packet.
 *
 * Input:
 *	p		location of first byte of ICMP header
 *	caplen		bytes (from p) captured
 *	length		bytes (from p) in current datagram (may not be captured)
 *	phoffset	how much pseudo header checksum changed during
 *			IP munging
 *
 * Output:
 *  	pointer to byte *past* last byte munged
 */

static u_char *
dumpicmp(u_char *p, int caplen, int length, u_long phoffset)
{
    struct icmp *dp = (struct icmp *)p;
    int len = 4;

    if (caplen < 4) {
	return p+caplen;
    }

    switch (dp->icmp_type) {
    case ICMP_REDIRECT:
    case ICMP_UNREACH:
    case ICMP_TIMXCEED:
	if (dp->icmp_type == ICMP_REDIRECT && caplen >= 8) {
	    /* hide gateway address */
	    u_long inaddr, outaddr;
	    u_short offset = 0;
	    
	    inaddr = ntohl(dp->icmp_gwaddr.s_addr);
	    outaddr = hide_addr(dp->icmp_gwaddr.s_addr, 255);
	    if (inaddr != outaddr) {
		offset = cksum_subtract(offset, dp->icmp_gwaddr.s_addr);
		dp->icmp_gwaddr.s_addr = htonl(outaddr);
		offset = cksum_add(offset, dp->icmp_gwaddr.s_addr);
		if (offset)
		    dp->icmp_cksum = cksum_adjust(dp->icmp_cksum, offset);
	    }
	}
	if (caplen <= 8) {
	    len = caplen;
	    break;
	}
	/*
	 * recursively call dumpip
	 * todo: we have to update icmp checksum.
	 */
	if (caplen >= 8 + sizeof(struct ip)) {
	    u_char *np;
	    np = dumpip(p + 8, caplen - 8, length - 8);
	    len = np - p;
	}
	break;
    case ICMP_ECHO:
    case ICMP_ECHOREPLY:
	len = 8;
	break;
    case ICMP_MASKREPLY:
	len = 12;
	break;
    case ICMP_TSTAMP:
    case ICMP_TSTAMPREPLY:
	len = 20;
    default:
	len = 4;
    }

    len = MIN(caplen, len);
    caplen -= len; length -= len; p += len;

    /* don't return any ICMP data */
    return p;
}

static u_char *
dumpicmp6(u_char *p, int caplen, int length, u_long phoffset)
{
    struct icmp6_hdr *dp = (struct icmp6_hdr *)p;
    int i, len = 4;
    struct in6_addr inaddr, outaddr, *addrp;

    if (caplen < 4) {
	return p+caplen;
    }

    switch (dp->icmp6_type) {
    case ICMP6_DST_UNREACH:
    case ICMP6_PACKET_TOO_BIG:
    case ICMP6_TIME_EXCEEDED:
    case ICMP6_PARAM_PROB:
	if (caplen <= 8) {
	    len = caplen;
	    break;
	}
	/*
	 * recursively call dumpip6
	 * todo: we have to update icmp6 checksum.
	 */
	if (caplen >= 8 + sizeof(struct ip6_hdr)) {
	    u_char *np;
	    np = dumpip6(p + 8, caplen - 8, length - 8);
	    len = np - p;
	}
	break;
    case ICMP6_ECHO_REQUEST:
    case ICMP6_ECHO_REPLY:
	len = 8;
	break;
    case ICMP6_MEMBERSHIP_QUERY:
    case ICMP6_MEMBERSHIP_REPORT:
    case ICMP6_MEMBERSHIP_REDUCTION:
	len = 24;
	break;
    case ND_ROUTER_SOLICIT:
    case ND_ROUTER_ADVERT:
	len = 8;
	break;
    case ND_NEIGHBOR_SOLICIT:
    case ND_NEIGHBOR_ADVERT:
    case ND_REDIRECT:
	/*
	 * neighbor sol and adv has 1 ipv6 address below the icmp6 header.
	 * redirect has 2 addresses.
	 */
	len = 8;
	addrp = (struct in6_addr *)(dp + 1);
    again:
	if (addrp + 1 > (struct in6_addr *)(p + caplen))
	    break;
	inaddr = *addrp;
	hide_addr6(&inaddr, &outaddr, 255);
	if (!IN6_ARE_ADDR_EQUAL(&inaddr, &outaddr)) {
	    /* need to redo checksum */
	    for (i = 0; i < 4; i++)
		phoffset = cksum_subtract(phoffset, IN6ADDR32(&inaddr, i));
	    *addrp = outaddr;
	    for (i = 0; i < 4; i++)
		phoffset = cksum_add(phoffset, IN6ADDR32(&outaddr, i));
	}
	len += sizeof(*addrp);
	if (dp->icmp6_type == ND_REDIRECT && len == 8 + sizeof(*addrp)) {
	    addrp++;
	    goto again;
	}
	break;
    default:
	len = 4;
    }

    /*
     * icm6 checksum includes ipv6 pseudo header (it's a different policy
     * from icmp4).
     */
    if (phoffset)
	dp->icmp6_cksum = cksum_adjust(dp->icmp6_cksum, phoffset);

    len = MIN(caplen, len);
    caplen -= len; length -= len; p += len;

    /* don't return any ICMP6 data */
    return p;
}

static u_char *
dumparp(u_char *p, int caplen, int length)
{
    struct ether_arp *ap = (struct ether_arp *)p;
    u_int32_t addr;
    int len = 8;

    if (caplen < sizeof(*ap)) {
	return p+caplen;
    }

    if (ntohs(ap->arp_pro) != ETHERTYPE_IP) {
	uncoded++;
	return p+8;
    }

    switch (ntohs(ap->arp_op)) {
    case ARPOP_REQUEST:
    case ARPOP_REPLY:
    case ARPOP_REVREQUEST:
    case ARPOP_REVREPLY:
	addr = ntohl(*((u_int32_t *)&ap->arp_spa[0]));
	addr = hide_addr(addr, 255);
	*((u_int32_t *)&ap->arp_spa[0]) = htonl(addr);

	addr = ntohl(*((u_int32_t *)&ap->arp_tpa[0]));
	addr = hide_addr(addr, 255);
	*((u_int32_t *)&ap->arp_tpa[0]) = htonl(addr);
	len = sizeof(*ap);
	break;
    default:
	len = 8;
	break;
    }

    caplen -= len; length -= len; p += len;

    return p;
}


#endif /* WIDE */

/*
 *   I   P
 */


/* 
 * deal with IP options
 */

static u_char *
dumpipoptions(u_char *p, int caplen, int length, struct ip *ip)
{
    u_short *usp;
    int optlen;

    usp = (u_short *)p;
    optlen = (ip->ip_hl*4)-sizeof *ip;
#ifdef WIDE
    if (opt_tcpipopts >= 99) {
	while ((optlen >= 2) && (caplen >= 2)) {
	    usp++; optlen -= 2; caplen -= 2;
	}
	return (u_char *)usp;
    }
#endif
    while ((optlen >= 2) && (caplen >= 2)) {
	ip->ip_sum = cksum_subtract(ip->ip_sum, *usp);
	*usp = ntohs(0x0101);  /* no ops (doesn't need ntohs(), but...) */
	ip->ip_sum = cksum_add(ip->ip_sum, *usp);
	usp++; optlen -= 2; caplen -= 2;
    }
    return (u_char *)usp;
}

/*
 * this is an IP packet --- output it securely.
 *
 * Input:
 *	p		location of first byte of IP header
 *	caplen		bytes (from p) captured
 *	length		bytes (from p) in current datagram (may not be captured)
 *
 * Output:
 *  	pointer to byte *past* last byte munged (so, first byte of
 *  	    IP payload not munged by dumpip() or any of its "children")
 */


static u_char *
dumpip(u_char *p, int caplen, int length)
{
    struct ip *ip;
    u_long inaddr, outaddr;
    u_short phoffset = 0;
    int foff;

    if (caplen < sizeof (struct ip)) {
	tooshort++;
	return p;
    }

    ip = (struct ip *)p;
    caplen -= sizeof *ip;
    length -= sizeof *ip;
    p += sizeof *ip;

    inaddr = ntohl(ip->ip_src.s_addr);
    outaddr = hide_addr(inaddr, ip->ip_ttl);
    if (inaddr != outaddr) {
	/*
	 * need to redo checksum (both for keeping tcpdump
	 * from thinking there is a checksum error, *AND*
	 * to maintain secrecy).  thank you, Vern!
	 */
	phoffset = cksum_subtract(phoffset, ip->ip_src.s_addr);
	ip->ip_src.s_addr = htonl(outaddr);
	phoffset = cksum_add(phoffset, ip->ip_src.s_addr);
    }

    inaddr = ntohl(ip->ip_dst.s_addr);
    outaddr = hide_addr(inaddr, ip->ip_ttl);
    if (inaddr != outaddr) {
	/*
	 * need to redo checksum (both for keeping tcpdump
	 * from thinking there is a checksum error, *AND*
	 * to maintain secrecy).  thank you, Vern!
	 */
	phoffset = cksum_subtract(phoffset, ip->ip_dst.s_addr);
	ip->ip_dst.s_addr = htonl(outaddr);
	phoffset = cksum_add(phoffset, ip->ip_dst.s_addr);
    }

    if (phoffset) {
	ip->ip_sum = cksum_adjust(ip->ip_sum, phoffset);
    }

    if ((ip->ip_hl*4) > sizeof *ip) {		/* options! */
	u_char *newp = dumpipoptions(p, caplen, length, ip);
	int diff = newp-p;

	p += diff; caplen -= diff; length -= diff;
    }

    foff = ntohs(ip->ip_off);

    if ((caplen > 0) && ((foff & IP_OFFMASK) == 0)) {
	switch (ip->ip_p) {
	case IPPROTO_TCP:
	    p = dumptcp(p, caplen, length, phoffset);
	    break;
	case IPPROTO_UDP:
	    p = dumpudp(p, caplen, length, phoffset);
	    break;
#ifndef IPPROTO_IPIP
#define IPPROTO_IPIP 4
#endif	/* ndef IPPROTO_IPIP */
	case IPPROTO_IPIP:
	    if (caplen >= sizeof(struct ip))
		p = dumpip(p, caplen, length);
	    break;
#ifdef WIDE
	case IPPROTO_ICMP:
	    p = dumpicmp(p, caplen, length, phoffset);
	    break;
	case IPPROTO_IPV6:
	    if (caplen >= sizeof(struct ip6_hdr))
		p = dumpip6(p, caplen, length);
	    break;
#endif
	default:
	    break;
	}
    }

    return p;
}

#ifdef WIDE
/*
 * this is an IPv6 packet --- output it securely.
 *
 * Input:
 *	p		location of first byte of IPv6 header
 *	caplen		bytes (from p) captured
 *	length		bytes (from p) in current datagram (may not be captured)
 *
 * Output:
 *  	pointer to byte *past* last byte munged (so, first byte of
 *  	    IPv6 payload not munged by dumpip6() or any of its "children")
 */
static u_char *
dumpip6(u_char *p, int caplen, int length)
{
    struct ip6_hdr *ip6;
    struct in6_addr inaddr, outaddr;
    u_short phoffset = 0;
    u_int8_t nxt;
    int i, len;
    /* structure for ipsec and ipv6 option header template */
    struct _opt6 {
	u_int8_t	opt6_nxt;	/* next header */
	u_int8_t	opt6_hlen;	/* header extension length */
	u_int16_t	_pad;
	u_int32_t	ah_spi;		/* security parameter index
					   for authentication header */
    } *opt6;

    if (caplen < sizeof (struct ip6_hdr)) {
	tooshort++;
	return p;
    }

    ip6 = (struct ip6_hdr *)p;

    inaddr = ip6->ip6_src;
    hide_addr6(&inaddr, &outaddr, ip6->ip6_hlim);
    if (!IN6_ARE_ADDR_EQUAL(&inaddr, &outaddr)) {
	/* need to redo checksum for TCP and UDP */
	for (i = 0; i < 4; i++)
	    phoffset = cksum_subtract(phoffset, IN6ADDR32(&ip6->ip6_src, i));
	ip6->ip6_src = outaddr;
	for (i = 0; i < 4; i++)
	    phoffset = cksum_add(phoffset, IN6ADDR32(&ip6->ip6_src, i));
    }

    inaddr = ip6->ip6_dst;
    hide_addr6(&inaddr, &outaddr, ip6->ip6_hlim);
    if (!IN6_ARE_ADDR_EQUAL(&inaddr, &outaddr)) {
	/* need to redo checksum for TCP and UDP */
	for (i = 0; i < 4; i++)
	    phoffset = cksum_subtract(phoffset, IN6ADDR32(&ip6->ip6_dst, i));
	ip6->ip6_dst = outaddr;
	for (i = 0; i < 4; i++)
	    phoffset = cksum_add(phoffset, IN6ADDR32(&ip6->ip6_dst, i));
    }

    caplen -= sizeof *ip6;
    length -= sizeof *ip6;
    p += sizeof *ip6;

    nxt = ip6->ip6_nxt;
    while (caplen > 0) {
	switch (ip6->ip6_nxt) {
	case IPPROTO_TCP:
	    p = dumptcp(p, caplen, length, phoffset);
	    return (p);
	case IPPROTO_UDP:
	    p = dumpudp(p, caplen, length, phoffset);
	    return (p);
	case IPPROTO_ICMPV6:
	    p = dumpicmp6(p, caplen, length, phoffset);
	    return (p);
	case IPPROTO_IPV4:  /* ipv4 over ipv6 */
	    if (caplen >= sizeof(struct ip))
		p = dumpip(p, caplen, length);
	    return (p);
	case IPPROTO_HOPOPTS:
	case IPPROTO_ROUTING:
	case IPPROTO_DSTOPTS:
	    /* get next header and header length */
	    opt6 = (struct _opt6 *)p;
	    nxt = opt6->opt6_nxt;
	    len = (opt6->opt6_hlen + 1) * 8;
	    /* clear the option contents */
	    memset(opt6 + 1, 0, len - sizeof(*opt6));

	    /* goto the next header */
	    caplen -= len;
	    length -= len;
	    p += len;
	    break;
#ifdef notyet
	case IPPROTO_ESP:
	case IPPROTO_AH:
	case IPPROTO_FRAGMENT:
#endif
	default:
	    /* unknown option header.  stop dumping */
	    return (p);
	}
    }

    return p;
}
#endif /* WIDE */

/*
 *   D   U   M   P   E   R
 */


/*
 * this routine is largely cribbed from various bits and pieces of 
 * tcpdump(1).
 *
 * also, we keep to much of the tcpdump internal conventions, since
 * the form of this looks a lot like that of tcpdump (since the problem
 * is basically the same).
 */

static void
dumper(u_char *user, const struct pcap_pkthdr *inh, const u_char *inp)
{
#if !defined(SLIP_HDRLEN)
#define	SLIP_HDRLEN	16
#endif /* !defined(SLIP_HDRLEN) */

#define	PPP_HDRLEN	4
#define	NULL_HDRLEN	4
#define FDDI_HDRLEN	13		/* nice round number... */

    int caplen = inh->caplen;
    int length = inh->len;
    struct ether_header *ep;
    struct fddi_header *fddip;
    u_short ether_type;
    struct pcap_pkthdr ourh = *inh, *h = &ourh;
    u_char *p;
    static u_char SNAPHDR[] = { LLC_SNAP_LSAP, LLC_SNAP_LSAP, LLC_UI, 0, 0, 0 };

    pktsin++;

    if (caplen > pcap_snap) {
	fprintf(stderr, "packet too large %s:%d\n", __FILE__, __LINE__);
	exit(2);
    }

    memcpy(pktbuffer, inp, caplen);
    p = pktbuffer;

    packetp = p;		/* where packet started */
    snapend = p + caplen;	/* where packet ends */

    switch (pcap_dlt) {
    case DLT_EN10MB:
	if (caplen < sizeof (struct ether_header)) {
	    tooshort++;
	    return;
	}
	ep = (struct ether_header *)p;
	p += sizeof *ep;
	caplen -= sizeof *ep;
	length -= sizeof *ep;
	ether_type = ntohs(ep->ether_type);
	if (ether_type == 0x800) {	/* oh, good! */
	    p = dumpip(p, caplen, length);
#ifdef WIDE
	} else if (ether_type == ETHERTYPE_IPV6) {
	    p = dumpip6(p, caplen, length);
	} else if (ether_type == ETHERTYPE_ARP ||
		   ether_type == ETHERTYPE_REVARP) {
	    p = dumparp(p, caplen, length);
#endif
	} else {
	    uncoded++;
	    return;
	}
	break;
    case DLT_SLIP:
	if (caplen < SLIP_HDRLEN) {
	    tooshort++;
	    return;
	}
	p = dumpip(p+SLIP_HDRLEN, caplen-SLIP_HDRLEN, length-SLIP_HDRLEN);
	break;
    case DLT_PPP:
	if (caplen < PPP_HDRLEN) {
	    tooshort++;
	    return;
	}
	/* XXX -- how do we know it is IP traffic? */
	p = dumpip(p+PPP_HDRLEN, caplen-PPP_HDRLEN, length-PPP_HDRLEN);
	break;
    case DLT_FDDI:
	if (caplen < FDDI_HDRLEN) {
	    tooshort++;
	    return;
	}
	fddip = (struct fddi_header *)p;
	length -= FDDI_HDRLEN;
	p += FDDI_HDRLEN;
	caplen -= FDDI_HDRLEN;
	if ((fddip->fddi_fc&FDDIFC_CLFF) == FDDIFC_LLC_ASYNC) {
	    if (caplen < sizeof SNAPHDR+2) {
		tooshort++;
		return;
	    }
	    if (memcmp(p, SNAPHDR, sizeof SNAPHDR) == 0) {
		ether_type = GETNETSHORT(p+sizeof SNAPHDR);
		if (ether_type == 0x0800) {
		    caplen -= (sizeof SNAPHDR+2);
		    length -= (sizeof SNAPHDR+2);
		    p += (sizeof SNAPHDR+2);
		    p = dumpip(p, caplen, length);
#ifdef WIDE
		} else if (ether_type == ETHERTYPE_IPV6) {
		    caplen -= (sizeof SNAPHDR+2);
		    length -= (sizeof SNAPHDR+2);
		    p += (sizeof SNAPHDR+2);
		    p = dumpip6(p, caplen, length);
#endif
		} else {
		    uncoded++;
		    return;
		}
	    } else {
		uncoded++;
		return;
	    }
	} else {
	    uncoded++;
	    return;
	}
	break;
    case DLT_NULL:
	if (caplen < NULL_HDRLEN) {
	    tooshort++;
	    return;
	}
	length -= NULL_HDRLEN;
	p += NULL_HDRLEN;
	caplen -= NULL_HDRLEN;
	p = dumpip(p, caplen, length);
	break;
#ifdef WIDE
    case DLT_ATM_RFC1483:
	if (caplen < 8) {
	    tooshort++;
	    return;
	}
	if (p[0] != 0xaa || p[1] != 0xaa || p[2] != 0x03) {
	    /* unknown format! */
	    uncoded++;
	    return;
	}
	ether_type = p[6] << 8 | p[7];
	length -= 8;
	caplen -= 8;
	p += 8;
	if (ether_type == ETHERTYPE_IP) {
	    p = dumpip(p, caplen, length);
	} else if (ether_type == ETHERTYPE_IPV6) {
	    p = dumpip6(p, caplen, length);
	} else {
	    uncoded++;
	    return;
	}
	break;
#endif
    default:
	fprintf(stderr, "unknown DLT %d\n", pcap_dlt);
	exit(1);
    }

    /* (now, save [packetp, p) (half-open)] */

    if (p != packetp) {
	h->caplen = p-packetp;
	pcap_dump(user, h, packetp);
	pktsout++;
    }
}

/*
 *    I   N   I   T   I   A   L   I   Z   A   T   I   O   N
 *
 *                         A   N   D
 *
 *                       M   A   I   N
 */

int
dlt_hdrlen(int dlt)
{
    switch (dlt) {
    case DLT_EN10MB:
	return sizeof (struct ether_header);
    case DLT_SLIP:
	return SLIP_HDRLEN;
    case DLT_PPP:
	return PPP_HDRLEN;
#if	defined(FDDI_HDRLEN)
    case DLT_FDDI:
	return FDDI_HDRLEN;
#endif	/* defined(FDDI_HDRLEN) */
    case DLT_NULL:
	return NULL_HDRLEN;
#ifdef WIDE
    case DLT_ATM_RFC1483:
	return 8;
#endif
    default:
	fprintf(stderr, "unknown DLT %d\n", dlt);
	exit(1);
    }
}

static void
verify_and_print_args(char *cmd)
{
    static void usage(char *cmd);

    lookup_init(&addr_propagate);

    lookup_init(&addr_whole);

    lookup_init(&addr_upper);
    lookup_init(&addr_lower);

    lookup_init(&addr_byte_0);
    lookup_init(&addr_byte_1);
    lookup_init(&addr_byte_2);
    lookup_init(&addr_byte_3);

#ifdef WIDE
    lookup_init6(&addr6_propagate);

    lookup_init6(&addr6_whole);
#endif

    tcpport_whole.flags = NH_FL_COUNTER;
    lookup_init(&tcpport_whole);

    tcpport_byte_0.flags = tcpport_byte_1.flags = NH_FL_COUNTER;
    lookup_init(&tcpport_byte_0);
    lookup_init(&tcpport_byte_1);

    udpport_whole.flags = NH_FL_COUNTER;
    lookup_init(&udpport_whole);

    udpport_byte_0.flags = udpport_byte_1.flags = NH_FL_COUNTER;
    lookup_init(&udpport_byte_0);
    lookup_init(&udpport_byte_1);

    switch (opt_ipaddr) {
    case 0:
	if (!qflag) {
	    fprintf(stderr,
		"# map 32-bit addresses into sequential integers\n");
	}
	break;
    case 1:
	if (!qflag) {
	    fprintf(stderr,
		"# map 16-bit address chunks into sequential integers\n");
	}
	break;
    case 2:
	if (!qflag) {
	    fprintf(stderr,
		"# map 8-bit address chunks into sequential integers\n");
	}
	break;
    case 50:
	if (!qflag) {
	    fprintf(stderr,
		"# map address using random numbers, "
		"preserving common prefix attributes\n");
	}
	break;
    case 99:
	if (qflag < 3) {
	    fprintf(stderr,
		"# WARNING WARNING WARNING WARNING WARNING WARNING WARNING\n");
	    fprintf(stderr,
		"# IP addresses being passed through without modification\n");
	    fprintf(stderr,
		"# WARNING WARNING WARNING WARNING WARNING WARNING WARNING\n");
	}
	break;
    default:
	fprintf(stderr, "unknown value %d for -A argument\n", opt_ipaddr);
	usage(cmd);
	/*NOTREACHED*/
    }

    if ((opt_class < 0) || ((opt_class > 32) && (opt_class != 99))) {
	fprintf(stderr, "invalid value %d for -C flag (s/b in range 0-32)\n",
				opt_class);
	usage(cmd);
	/*NOTREACHED*/
    }
    if (opt_class == 99) {
	opt_class = 32;
    }

#if	0
    /* XXX someday */
    switch (opt_linkaddr) {
    case 0:
	fprintf(stderr, "# remove linklayer information\n");
	break;
    case 99:
	fprintf(stderr, "# retain all linklayer information\n");
	break;
    default:
	fprintf(stderr, "unknown value %d for -L argument\n", opt_linkaddr);
	usage(cmd);
	/*NOTREACHED*/
    }
#endif	/* 0 */

    switch (opt_mcastaddr) {
    case 0:
	if (!qflag) {
	    fprintf(stderr,
		    "# multicast addresses cloaked per -A and -C flags\n");
	}
	break;
    case MCAST_OPT_NODE_LOCAL:
	if (!qflag) {
	    fprintf(stderr,
		"# multicast addressses in datagrams scoped node-local\n");
	    fprintf(stderr, "#\t(%d <= ttl <= %d) passed through unchanged\n", 
				    optTOttlLOW(MCAST_OPT_NODE_LOCAL),
				    optTOttlHIGH(MCAST_OPT_NODE_LOCAL));
	}
    case MCAST_OPT_LINK_LOCAL:
	if (!qflag) {
	    fprintf(stderr,
		"# multicast addressses in datagrams scoped link-local\n");
	    fprintf(stderr, "#\t(%d <= ttl <= %d) passed through unchanged\n", 
				    optTOttlLOW(MCAST_OPT_LINK_LOCAL),
				    optTOttlHIGH(MCAST_OPT_LINK_LOCAL));
	}
    case MCAST_OPT_SITE_LOCAL:
	if (!qflag) {
	    fprintf(stderr,
		"# multicast addressses in datagrams scoped site-local\n");
	    fprintf(stderr, "#\t(%d <= ttl <= %d) passed through unchanged\n", 
				    optTOttlLOW(MCAST_OPT_SITE_LOCAL),
				    optTOttlHIGH(MCAST_OPT_SITE_LOCAL));
	}
    case MCAST_OPT_CONTINENT_LOCAL:
	if (!qflag) {
	    fprintf(stderr,
		"# multicast addressses in datagrams scoped continent-local\n");
	    fprintf(stderr, "#\t(%d <= ttl <= %d) passed through unchanged\n", 
				    optTOttlLOW(MCAST_OPT_CONTINENT_LOCAL),
				    optTOttlHIGH(MCAST_OPT_CONTINENT_LOCAL));
	}
    case MCAST_OPT_GLOBAL:
	if (!qflag) {
	    fprintf(stderr,
		"# multicast addressses in datagrams scoped global\n");
	    fprintf(stderr, "#\t(%d <= ttl <= %d) passed through unchanged\n", 
				    optTOttlLOW(MCAST_OPT_GLOBAL),
				    optTOttlHIGH(MCAST_OPT_GLOBAL));
	}
	break;
    case 99:
	if (!qflag) {
	    fprintf(stderr, "# multicast addresses passed through unchanged\n");
	}
	break;
    default:
	fprintf(stderr, "unknown value %d for -M argument\n", opt_mcastaddr);
	usage(cmd);
	/*NOTREACHED*/
    }

    switch (opt_tcpports) {
    case 0:
	if (!qflag) {
	    fprintf(stderr,
		"# map 16-bit TCP port numbers into sequential integer\n");
	}
	break;
    case 1:
	if (!qflag) {
	    fprintf(stderr,
	       "# map 8-bit TCP port number chunks into sequential integers\n");
	}
	break;
    case 99:
	if (!qflag) {
	    fprintf(stderr, "# pass TCP port numbers through unchanged\n");
	}
	break;
    default:
	fprintf(stderr, "unknown value %d for -T or -P argument\n",
								opt_tcpports);
	usage(cmd);
	/*NOTREACHED*/
    }

    switch (opt_udpports) {
    case 0:
	if (!qflag) {
	    fprintf(stderr,
		"# map 16-bit UDP port numbers into sequential integer\n");
	}
	break;
    case 1:
	if (!qflag) {
	    fprintf(stderr,
	       "# map 8-bit UDP port number chunks into sequential integers\n");
	}
	break;
    case 99:
	if (!qflag) {
	    fprintf(stderr, "# pass UDP port numbers through unchanged\n");
	}
	break;
    default:
	fprintf(stderr, "unknown value %d for -U or -P argument\n",
								opt_udpports);
	usage(cmd);
	/*NOTREACHED*/
    }
}



/*
 * print trailer statistics
 */

void
laststats(void)
{
    struct pcap_stat stat;

    if (!qflag) {
	/* Can't print the summary if reading from a savefile */
	if (pc != NULL && pcap_file(pc) == NULL) {
	    (void)fflush(stdout);
	    putc('\n', stderr);
	    if (pcap_stats(pc, &stat) < 0) {
		(void)fprintf(stderr, "pcap_stats: %s\n", pcap_geterr(pc));
	    } else {
		(void)fprintf(stderr, "# %d packets received by filter\n",
		    stat.ps_recv);
		(void)fprintf(stderr, "# %d packets dropped by kernel\n",
		    stat.ps_drop);
	    }
	}
	fprintf(stderr, "# pktsin %d pktsout %d tooshort %d uncoded %d\n",
			    pktsin, pktsout, tooshort, uncoded);
    }
}

/* make a clean exit on interrupts */
void
cleanup(int signo)
{
    laststats();
    exit(0);
}

static void
usage(char *cmd)
{
    fprintf(stderr,
	"usage:\n%s [-Opq] [-a [[hh:]mm:]ss] [-A {0|1|2|50|99}] [-c count]"
	"\n\t\t[-C {0|1|2|3|4|...|32|99}] [-F file] [-i interface]"
	"\n\t\t[-M {0|10|20|70|80|90|99}] [-{P|T|U} {0|1|99}] [-r file]"
	"\n\t\t[-s snaplen] [-w outputfile] [expression]\n", cmd);
    fprintf(stderr, "(one reasonable choice:  %s -P99 -C4 -M20 ...)\n", cmd);
    exit(1);
}

int
main(int argc, char *argv[], char *envp[])
{
    void bpf_dump(FILE *output, struct bpf_program *, int);
    char *copy_argv(register char **argv);
    char *read_infile(char *fname);
    char *rfile, *wfile;
    char *pgmfile, *interface;
    char *cmd = argv[0], *pgmbuf;
    char *alarm = 0;
    extern char *optarg;
    int ch, hdrlen, snaplen = 68;
    int pflag, Oflag, dflag;
    int count = -1;			/* default: all the packets */
    pcap_dumper_t *dc;
    char pcap_errbuf[PCAP_ERRBUF_SIZE];
    struct bpf_program fcode;
    bpf_u_int32 netmask, localnet;

    rand_start();

    /* parse arguments */
    wfile = "-";
    rfile = 0;
    pgmfile = 0;
    interface = 0;
    dflag = 0;
    pflag = 0;
    Oflag = 0;
#ifdef WIDE
    while ((ch = getopt(argc, argv, "a:A:c:C:dF:i:M:OpP:qr:s:T:U:w:X:")) != EOF) {
#else
    while ((ch = getopt(argc, argv, "a:A:c:C:dF:i:M:OpP:qr:s:T:U:w:")) != EOF) {
#endif
	switch (ch) {
	case 'a':
	    alarm = optarg;
	    break;
	case 'A':
	    opt_ipaddr = atoi(optarg);
	    break;
	case 'c':
	    count = atoi(optarg);
	    break;
	case 'C':
	    opt_class = atoi(optarg);
	    break;
	case 'd':
	    dflag++;
	    break;
	case 'F':
	    pgmfile = optarg;
	    break;
	case 'i':
	    interface = optarg;
	    break;
	case 'M':
	    opt_mcastaddr = atoi(optarg);
	    break;
	case 'O':
	    Oflag = 1;
	    break;
	case 'p':
	    pflag = 1;
	    break;
	case 'P':
	    opt_tcpports = opt_udpports = atoi(optarg);
	    break;
	case 'q':
	    qflag++;
	    break;
	case 'r':
	    rfile = optarg;
	    break;
	case 's':
	    snaplen = atoi(optarg);
	    break;
	case 'T':
	    opt_tcpports = atoi(optarg);
	    break;
	case 'U':
	    opt_udpports = atoi(optarg);
	    break;
	case 'w':
	    wfile = optarg;
	    break;
#ifdef WIDE
	case 'X':
	    opt_tcpipopts = atoi(optarg);
	    break;
#endif
	default:
	    usage(cmd);
	    /*NOTREACHED*/
	}
    }

    /* if -r, open offline; else, set up live capture */
    if (rfile != 0) {
	if ((rfile[0] == '-') && (rfile[1] == 0)) {
	    if (isatty(0)) {
		fprintf(stderr, "attempt to read binary dump file from tty\n");
		usage(cmd);
		/*NOTREACHED*/
	    }
	}
	fddipad = FDDIPAD;	/* XXX -- what is this??? */
	pc = pcap_open_offline(rfile, pcap_errbuf);
	if (pc == NULL) {
	    fprintf(stderr, "%s\n", pcap_errbuf);
	    exit(2);
	}
	netmask = 0;
    } else {
	if (interface == 0) {
	    interface = pcap_lookupdev(pcap_errbuf);
	    if (interface == NULL) {
		fprintf(stderr, "%s\n", pcap_errbuf);
		exit(2);
	    }
	}
	pc = pcap_open_live(interface, snaplen, !pflag, 1000, pcap_errbuf);
	if (pc == NULL) {
	    fprintf(stderr, "%s\n", pcap_errbuf);
	    exit(2);
	}
	if (pcap_lookupnet(interface, &localnet, &netmask, pcap_errbuf) < 0) {
	    fprintf(stderr, "%s\n", pcap_errbuf);
	    exit(2);
	}
    }

    /* find filter expression */
    if (pgmfile) {
	pgmbuf = read_infile(pgmfile);
    } else {
	pgmbuf = copy_argv(&argv[optind]);
    }

    /* compile filter */
    if (pcap_compile(pc, &fcode, pgmbuf, Oflag, netmask) < 0) {
	fprintf(stderr, "%s\n", pcap_geterr(pc));
	exit(2);
    }

    /* dump? */
    if (dflag) {
	bpf_dump(stderr, &fcode, dflag);
	exit(0);
    }

    /* install filter */
    if (pcap_setfilter(pc, &fcode) < 0) {
	fprintf(stderr, pcap_geterr(pc));
	exit(2);
    }

    /* protect user's terminal... */
    if ((wfile[0] == '-') && (wfile[1] == 0)) {
	if (isatty(1)) {
	    fprintf(stderr, "attempt to write binary dump file to tty\n");
	    usage(cmd);
	    /*NOTREACHED*/
	}
    }

    /* set up output file */
    dc = pcap_dump_open(pc, wfile);
    if (dc == NULL) {
	fprintf(stderr, "%s\n", pcap_errbuf);
	exit(2);
    }

    /* lots of ways to stop (aside from EOF)...*/
    (void)signal(SIGTERM, cleanup);
    (void)signal(SIGINT, cleanup);
    (void)signal(SIGHUP, cleanup);
    (void)signal(SIGALRM, cleanup);

    pcap_dlt = pcap_datalink(pc);
    pcap_snap = pcap_snapshot(pc);

    hdrlen = dlt_hdrlen(pcap_dlt);
    pktbuffer = malloc(pcap_snap+4);		/* buffer space */
    switch (hdrlen&3) {				/* align IP header */
    case 0:
	break;
    case 1:
	pktbuffer += 3;
	break;
    case 2:
	pktbuffer += 2;
	break;
    case 3:
	pktbuffer += 1;
	break;
    }

    verify_and_print_args(cmd);

    if (alarm) {			/* if a duration ... */
	setalarm(alarm);
    }

    if (pcap_loop(pc, count, dumper, (u_char *)dc) < 0) {
	fprintf(stderr, "%s: pcap_loop: %s\n", cmd, pcap_geterr(pc));
	exit(2);
    }
    pcap_close(pc);
    pc = NULL;
    pcap_dump_close(dc);

    laststats();

    return 0;
}


syntax highlighted by Code2HTML, v. 0.9.1