/*
* Copyright (c) 1993-1997 JSC Rinet, Novosibirsk, Russia
*
* Redistribution and use in source forms, with and without modification,
* are permitted provided that this entire comment appears intact.
* Redistribution in binary form may occur without any restrictions.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' WITHOUT ANY WARRANTIES OF ANY KIND.
*/
/* trafshow.c -- main code to cooperate with pcap and curses */
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifdef HAVE_SLCURSES
#include <slcurses.h>
#endif
#ifdef HAVE_NCURSES
#include <ncurses.h>
#endif
#ifdef HAVE_CURSES
#include <curses.h>
#endif
#include <sys/types.h>
#ifdef HAVE_SYS_IOCTL_H
#include <sys/ioctl.h>
#endif
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
#include <pcap.h>
#include "trafshow.h"
#ifndef TIOCGWINSZ
#define TIOCGWINSZ 104
#endif
/* command line switches */
int fflag = 0; /* don't translate foreign IP address */
int nflag = 0; /* don't convert addresses to host names */
int Nflag = 0; /* output only host names without domain */
int Oflag = 1; /* don't run filter code optimizer */
int pflag = 0; /* don't put the interface into promiscuous mode */
int kflag = 1; /* disable keyboard input checking */
int eflag = 0; /* show ethernet traffic rather than ip */
struct t_entry t_mask; /* traffic mask */
/* global variables */
char *program_name; /* myself */
char *device_name; /* network interface name */
int alarm_flag = 0; /* flag -- screen refresh requested */
int scr_interval = DEFAULT_SCR; /* screen refresh interval in seconds */
int dns_timeout = DEFAULT_DNS; /* dns query timeout in seconds */
int snaplen = DEFAULT_SNAPLEN; /* length of saved portion of packet */
int curses_initialized = FALSE;
int use_colors = FALSE;
int resize_flag = 0;
int addr_size, proto_size, bytes_size, cps_size, count_size;
void
main(argc, argv)
int argc;
char **argv;
{
int op, cnt;
pcap_t *pd;
bpf_u_int32 localnet, netmask;
char *cp, *infile, *expr, ebuf[PCAP_ERRBUF_SIZE];
struct bpf_program fcode;
extern char *optarg;
extern int optind, opterr;
void usage(), onalarm(), onwinch(), vers();
extern int abort_on_misalignment();
extern pcap_handler lookup_if();
t_mask.src.s_addr = 0xffffffff; /* all bits valid */
t_mask.dst.s_addr = 0xffffffff; /* all bits valid */
t_mask.sport = 0xffff; /* all bits valid */
t_mask.dport = 0xffff; /* all bits valid */
t_mask.proto = 0xffff; /* all bits valid */
cnt = -1;
device_name = NULL;
infile = NULL;
if ((cp = (char *)strrchr(argv[0], '/')) != NULL)
program_name = cp + 1;
else program_name = argv[0];
if (abort_on_misalignment(ebuf) < 0) error(0, ebuf);
while ((op = getopt(argc, argv, "c:CefF:i:kmnNOpr:t:vh?")) != EOF)
switch (op) {
case 'C':
#ifdef HAVE_SLCURSES
use_colors = TRUE;
#else
error(0, "must be compiled with slang curses");
#endif
break;
case 'c':
cnt = atoi(optarg);
if (cnt < 1) usage();
break;
case 'e':
++eflag;
break;
case 'f':
++fflag;
break;
case 'F':
infile = optarg;
break;
case 'i':
device_name = optarg;
break;
case 'k':
kflag = 0;
break;
case 'm':
t_mask.src.s_addr = 0;
t_mask.dst.s_addr = 0;
t_mask.sport = 0;
t_mask.dport = 0;
t_mask.proto = 0;
for (;optind + 1 <= argc;) {
char *s = argv[optind];
u_int32_t arg = 0xffffffff;
int save=optind;
optind++;
if (optind + 1 <= argc &&
isdigit(*(argv[optind])) ) {
arg = strtoul(argv[optind], NULL, 0);
optind++;
}
if (!strcmp(s, "src-ip"))
t_mask.src.s_addr = htonl(arg);
else if (!strcmp(s, "dst-ip"))
t_mask.dst.s_addr = htonl(arg);
else if (!strcmp(s, "src-port"))
t_mask.sport = htons((u_short)(arg));
else if (!strcmp(s, "dst-port"))
t_mask.dport = htons((u_short)(arg));
else if (!strcmp(s, "proto"))
t_mask.proto = arg;
else {
optind = save;
break;
}
}
break;
case 'n':
++nflag;
break;
case 'N':
++Nflag;
break;
case 'O':
Oflag = 0;
break;
case 'p':
++pflag;
break;
case 'r':
scr_interval = atoi(optarg);
if (scr_interval < 1) usage();
break;
case 't':
dns_timeout = atoi(optarg);
if (dns_timeout < 1) usage();
break;
case 'v':
vers();
case 'h':
case '?':
default:
usage();
}
/* Find network interface */
if (device_name == NULL &&
(device_name = pcap_lookupdev(ebuf)) == NULL)
error(0, ebuf);
/* Attach pcap to the network interface */
if ((pd = pcap_open_live(device_name, snaplen, !pflag, 1000, ebuf)) == NULL)
error(0, ebuf);
if ((op = pcap_snapshot(pd)) > snaplen) snaplen = op;
op = pcap_datalink(pd);
if (eflag && op != DLT_EN10MB
#ifdef DLT_IEEE802
&& op != DLT_IEEE802
#endif
) error(0, "interface %s not an Ethernet", device_name);
if (pcap_lookupnet(device_name, &localnet, &netmask, ebuf) < 0)
error(0, ebuf);
/* Get back to user process after socket has been opened */
setuid(getuid());
/* Read out the user filter expression */
if (infile) expr = read_infile(infile);
else expr = copy_argv(&argv[optind]);
/* and compile it */
if (pcap_compile(pd, &fcode, expr, Oflag, netmask) < 0 ||
pcap_setfilter(pd, &fcode) < 0)
error(0, pcap_geterr(pd));
init_addrtoname(localnet, netmask);
init_term(FALSE);
(void) signal(SIGHUP, cleanup);
(void) signal(SIGINT, cleanup);
(void) signal(SIGQUIT, cleanup);
(void) signal(SIGTERM, cleanup);
(void) signal(SIGALRM, onalarm);
(void) signal(SIGWINCH, onwinch);
init_display(FALSE);
if (pcap_loop(pd, cnt, lookup_if(op), NULL) < 0)
error(0, pcap_geterr(pd));
pcap_close(pd);
cleanup(0);
}
/*
* SIGALRM interrupt handler, should be ran each scr_interval seconds.
*/
void
onalarm()
{
if (alarm_flag) {
if (resize_flag) {
resize_flag = 0;
init_term(TRUE);
}
scr_redraw(TRUE);
scr_update();
} else alarm(scr_interval);
alarm_flag++;
(void) signal(SIGALRM, onalarm);
}
void
cleanup(sig)
int sig;
{
#ifdef HAVE_HAS_COLORS
if (use_colors) attrset(A_NORMAL);
#endif
if (sig == SIGINT) {
move(LINES-1, 0);
clrtoeol();
refresh();
}
endwin();
exit(sig < 0 ? 1 : 0);
}
void
init_term(reinit)
int reinit;
{
float i;
if (!reinit) {
if (initscr() == (WINDOW *)ERR)
error(0, "Can't initialize terminal -- unknown terminal type?");
curses_initialized = TRUE;
#ifdef HAVE_HAS_COLORS
if (use_colors == FALSE)
use_colors = has_colors();
#ifdef HAVE_SLCURSES
else SLtt_Use_Ansi_Colors = TRUE;
#endif
if (use_colors == TRUE) {
start_color();
if (init_colormask() == 0) use_colors = FALSE;
}
#endif
cbreak();
noecho();
} else {
#ifdef HAVE_RESIZETERM
struct winsize ws;
if (ioctl(0, TIOCGWINSZ, &ws) == 0)
resizeterm(ws.ws_row, ws.ws_col);
#else /* assume it will work on all curses without resizeterm() */
endwin();
initscr();
cbreak();
noecho();
#endif
}
if (LINES < 10) error(0, "Must more LINES on term; cur %d min %d", LINES, 10);
if (COLS < 40) error(0, "Must more COLS on term; cur %d min %d", COLS, 40);
page_size = LINES - SCR_OFFS - 2;
i = (float)COLS / (float)DEFAULT_COLS;
addr_size = i * (float)ADDR_SIZE;
proto_size = i * (float)PROTO_SIZE;
bytes_size = i * (float)BYTES_SIZE;
cps_size = i * (float)CPS_SIZE;
count_size = i * (float)COUNT_SIZE;
clear();
header_line();
if (reinit) init_display(TRUE);
else scr_update();
}
void
onwinch()
{
alarm_flag++;
resize_flag++;
(void) signal(SIGWINCH, onwinch);
}
void
vers()
{
int hc;
extern char version[], compiled[], target[], libpcap[], copyright[];
#ifdef HAVE_HAS_COLORS
initscr();
hc = has_colors();
endwin();
#endif
fprintf(stderr, "\n%s version %s\ncompiled for %s with\n %s\n",
program_name, version, target, compiled);
fprintf(stderr, "\tpcap library version %s\n", libpcap);
#ifdef HAVE_SLCURSES
fprintf(stderr, "\tslang curses version %d\n", SLang_Version);
#endif
#ifdef HAVE_NCURSES
#ifdef NCURSES_VERSION
fprintf(stderr, "\tncurses version %s\n", NCURSES_VERSION);
#else
fprintf(stderr, "\tncurses version unknown\n");
#endif
#endif
#ifdef HAVE_CURSES
fprintf(stderr, "\tunknown curses library\n");
#endif
#ifdef HAVE_HAS_COLORS
fprintf(stderr, "\tcolors support\n");
if (hc) fprintf(stderr, "your current terminal has color capability\n");
else fprintf(stderr,
"your current terminal has no color capability\n\
\ttry -C option to force ansi color mode\n");
#else
fprintf(stderr, "\tno colors support\n");
#endif
fprintf(stderr, "\n%s\n", copyright);
fprintf(stderr,"For bugs report email to trafshow@rinet.nsk.su (include this page)\n");
exit(1);
}
void
usage()
{
fprintf(stderr,
"Usage: %s [-eCfknNOpv -c num -i name -r sec -t sec] [-F file | expr]\n\
\t-c number\tcount number of packets and exit\n\
\t-C\t\tforce color mode (when color capability is missing)\n\
\t-e\t\tshow Ethernet traffic rather than IP\n\
\t-f\t\tconvert addresses to name only for local hosts\n\
\t-F file\t\tuse file as input for the filter expression\n\
\t-i name\t\tnetwork interface name; ef0, sl0, ppp0, lo0, etc\n\
\t-k\t\tdisable keyboard input checking\n\
\t-n\t\tdon't convert addresses to host names\n\
\t-N\t\toutput only host names without domain\n\
\t-O\t\tdon't run the packet-matching code optimizer\n\
\t-p\t\tdon't put the interface into promiscuous mode\n\
\t-r seconds\tscreen refresh interval, default %d sec\n\
\t-t seconds\tmax timeout in DNS query, default %d sec\n\
\t-v\t\tprint version information\n\
\texpr\t\tfilter expression like tcpdump's\n",
program_name, DEFAULT_SCR, DEFAULT_DNS);
exit(1);
}
syntax highlighted by Code2HTML, v. 0.9.1