/*
* 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