/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <stdio.h>
#include <unistd.h>
#include <stropts.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <stdarg.h>
#include <setjmp.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <assert.h>
#include <sys/sysmacros.h>
#include <netinet/in_systm.h>
#include <netinet/if_ether.h>
#include <netdb.h>
#include "snoop.h"
static int snaplen;
/* Global error recovery variables */
/* protected interpreter output areas */
static char *detail_line;
static char *line;
static char *encap;
static int audio;
static int sumcount;
#ifdef DEBUG
#endif
static int vlanid = 0;
static void usage(void);
static char *protmalloc(size_t);
static void resetperm(void);
int
{
int c;
int filter = 0;
int Cflg = 0;
char *p, *p2;
void (*proc)();
char *audiodev;
int ret;
char *output_area;
int nbytes;
names[0] = '\0';
/*
* Global error recovery: Prepare for interpreter failures
* with corrupted packets or confused interpreters.
* Allocate protected output and stack areas, with generous
* red-zones.
*/
if (output_area == NULL) {
perror("Warning: mmap");
exit(1);
}
/* Allocate protected output areas */
}
line = output_area;
encap = output_area;
/* Initialize an alternate signal stack to increase robustness */
perror("Warning: malloc");
exit(1);
}
perror("Warning: sigaltstack");
exit(1);
}
/* Initialize a master signal handler */
/* Register master signal handler */
perror("Warning: sigaction");
exit(1);
}
perror("Warning: sigaction");
exit(1);
}
perror("Warning: sigaction");
exit(1);
}
perror("Warning: sigaction");
exit(1);
}
perror("Warning: sigaction");
exit(1);
}
perror("Warning: sigaction");
exit(1);
}
perror("Warning: sigaction");
exit(1);
}
perror("Warning: sigaction");
exit(1);
}
perror("Warning: sigaction");
exit(1);
}
perror("Warning: sigaction");
exit(1);
}
perror("Warning: sigaction");
exit(1);
}
perror("Warning: sigaction");
exit(1);
}
perror("Warning: sigaction");
exit(1);
}
/* Prepare for failure during program initialization/exit */
exit(1);
}
!= EOF) {
switch (c) {
case 'a':
if (audio < 0) {
pr_err("Audio device %s: %m",
audiodev);
exit(1);
}
break;
case 't':
switch (*optarg) {
case 'd': break;
default: usage();
}
break;
case 'I':
usage();
break;
case 'P':
break;
case 'D':
break;
case 'S':
break;
case 'i':
break;
case 'o':
break;
case 'N':
break;
case 'n':
break;
case 's':
break;
case 'd':
if (Iflg)
usage();
break;
case 'v':
break;
case 'V':
break;
case 'p':
p = optarg;
} else {
*p2++ = '\0';
}
break;
case 'f':
if (p) {
*p = '\0';
"Warning: cannot capture packets from %s\n",
self);
*p = ' ';
"Warning: cannot capture packets from %s\n",
self);
break;
case 'x':
p = optarg;
x_length = -1;
} else {
*p2++ = '\0';
}
break;
case 'c':
break;
case 'C':
break;
case 'q':
break;
case 'r':
break;
case 'U':
break;
#ifdef DEBUG
case 'z':
break;
#endif /* DEBUG */
case '?':
default:
usage();
}
}
/*
* Need to know before we decide on filtering method some things
* about the interface. So, go ahead and do part of the initialization
* now so we have that data. Note that if no datalink is specified,
* open_datalink() selects one and returns it. In an ideal world,
* it might be nice if the "correct" interface for the filter
* requested was chosen, but that's too hard.
*/
if (!icapfile) {
} else {
if (!nflg) {
names[0] = '\0';
}
}
if (Uflg)
/* attempt to read .names file if it exists before filtering */
} else if (nflg) {
exit(1);
}
}
if (argstr) {
if (use_kern_pf) {
switch (ret) {
case 0:
filter++;
break;
case 1:
break;
case 2:
filter++;
break;
}
} else {
filter++;
}
if (Cflg)
exit(0);
}
/*
* If the -o flag is set then capture packets
* directly to a file. Don't attempt to
* interpret them on the fly (F_NOW).
* Note: capture to file is much less likely
* to drop packets since we don't spend cpu
* cycles running through the interpreters
* and possibly hanging in address-to-name
* mappings through the name service.
*/
if (ocapfile) {
} else {
proc = process_pkt;
}
/*
* If the -i flag is set then get packets from
* the log file which has been previously captured
* with the -o option.
*/
if (icapfile) {
names[0] = '\0';
if (Nflg) {
exit(1);
}
flags = 0;
"Creating name file %s\n", names);
}
else
resetperm();
if (Nflg)
} else {
/*
* If listening to packets on audio
* then set the buffer timeout down
* to 1/10 sec. A higher value
* makes the audio "bursty".
*/
if (audio) {
} else {
}
show_count();
resetperm();
dlpi_close(dh);
(void) printf("\n");
}
if (ocapfile)
cap_close();
return (0);
}
static int tone[] = {
0x076113, 0x153333, 0x147317, 0x144311, 0x147315, 0x050353, 0x037103, 0x051106,
0x157155, 0x142723, 0x133273, 0x134664, 0x051712, 0x024465, 0x026447, 0x072473,
0x136715, 0x126257, 0x135256, 0x047344, 0x034476, 0x027464, 0x036062, 0x133334,
0x127256, 0x130660, 0x136262, 0x040724, 0x016446, 0x025437, 0x137171, 0x127672,
0x124655, 0x134654, 0x032741, 0x021447, 0x037450, 0x125675, 0x127650, 0x077277,
0x046514, 0x036077, 0x035471, 0x147131, 0x136272, 0x162720, 0x166151, 0x037527,
};
/*
* amount of waveform used is a function of packet length e.g. a series
* of small packets is heard as clicks, whereas a series of NFS packets in
* an 8k read sounds like a "WHAAAARP".
*/
void
int len;
{
len /= 8;
if (audio) {
}
}
/* Display a count of packets */
void
{
return;
}
/*
* Display data that's external to the packet.
* This constitutes the first half of the summary
* line display.
*/
void
{
int i, start;
}
} else {
}
}
if (usec < 0) {
usec += 1000000;
sec -= 1;
}
}
}
}
}
}
}
(void) printf("________________________________\n");
encap);
sumcount = 0;
}
detail_line[0] = '\0';
}
}
/*
* The following three routines are called back
* from the interpreters to display their stuff.
* The theory is that when snoop becomes a window
* based tool we can just supply a new version of
* get_sum_line and get_detail_line and not have
* to touch the interpreters at all.
*/
char *
{
sumcount = 0; /* error recovery */
"get_sum_line: sumline overflow (sumcount=%d, MAXSUM=%d)\n",
}
}
/*ARGSUSED*/
char *
{
if (detail_line[0]) {
detail_line[0] = '\0';
}
return (detail_line);
}
/*
* This function exists to make sure that VLAN information is
* prepended to summary lines displayed. The problem this function
* solves is how to display VLAN information while in summary mode.
* Each interpretor uses the get_sum_line and get_detail_line functions
* to get a character buffer to display information to the user.
* get_sum_line is the important one here. Each call to get_sum_line
* gets a buffer which stores one line of information. In summary mode,
* the last line generated is the line printed. Instead of changing each
* interpreter to add VLAN information to the summary line, the ethernet
* interpreter changes to call this function and set an ID. If the ID is not
* zero and snoop is in default summary mode, snoop displays the
* VLAN information at the beginning of the output line. Otherwise,
* no VLAN information is displayed.
*/
void
{
}
/*
* Print an error.
* Works like printf (fmt string and variable args)
* except that it will substitute an error message
* for a "%m" string (like syslog) and it calls
* long_jump - it doesn't return to where it was
* called from - it goes to the last setjmp().
*/
/* VARARGS1 */
void
{
const char *p1;
/*
* Note that we terminate the buffer with '\n' and '\0'.
*/
const char *errstr;
*p2 = '\0';
}
p1++;
} else {
}
}
*p2++ = '\n';
*p2 = '\0';
/* LINTED: E_SEC_PRINTF_VAR_FMT */
}
/*
* Store a copy of linkname associated with the DLPI handle.
* Save errno before closing the dlpi handle so that the
* correct error value is used if 'err' is a system error.
*/
void
{
dlpi_close(dh);
errno = save_errno;
}
/*
* Ye olde usage proc
* PLEASE keep this up to date!
* Naive users *love* this stuff.
*/
static void
usage(void)
{
"\t[ -a ] # Listen to packets on audio\n");
"\t[ -d link ] # Listen on named link\n");
"\t[ -s snaplen ] # Truncate packets\n");
"\t[ -I IP interface ] # Listen on named IP interface\n");
"\t[ -c count ] # Quit after count packets\n");
"\t[ -P ] # Turn OFF promiscuous mode\n");
"\t[ -D ] # Report dropped packets\n");
"\t[ -S ] # Report packet size\n");
"\t[ -i file ] # Read previously captured packets\n");
"\t[ -o file ] # Capture packets in file\n");
"\t[ -n file ] # Load addr-to-name table from file\n");
"\t[ -N ] # Create addr-to-name table\n");
"\t[ -t r|a|d ] # Time: Relative, Absolute or Delta\n");
"\t[ -v ] # Verbose packet display\n");
"\t[ -V ] # Show all summary lines\n");
"\t[ -p first[,last] ] # Select packet(s) to display\n");
"\t[ -x offset[,length] ] # Hex dump from offset for length\n");
"\t[ -C ] # Print packet filter code\n");
"\t[ -q ] # Suppress printing packet count\n");
"\t[ -r ] # Do not resolve address to name\n");
"\n\t[ filter expression ]\n");
exit(1);
}
/*
* sdefault: default global alarm handler. Causes the current packet
* to be skipped.
*/
static void
sdefault(void)
{
}
/*
* snoop_alarm: register or unregister an alarm handler to be called after
* s_sec seconds. Because snoop wasn't written to tolerate random signal
* delivery, periodic SIGALRM delivery (or SA_RESTART) cannot be used.
*
* s_sec argument of 0 seconds unregisters the handler.
* s_handler argument of NULL registers default handler sdefault(), or
* unregisters all signal handlers (for error recovery).
*
* Variables must be volatile to force the compiler to not optimize
* out the signal blocking.
*/
/*ARGSUSED*/
int
{
if (s_sec < 0)
return (-1);
/* register an alarm handler */
if (s_sec) {
} else {
}
}
return (0);
}
/* unregister an alarm handler */
ret = 0;
else
}
} else {
now + 1;
}
}
/*
* Stop or adjust timer
*/
snoop_nalarm = 0;
(void) alarm(0);
}
return (ret);
}
/*
* snoop_recover: reset snoop's output area, and any internal variables,
* to allow continuation.
* XXX: make this an interface such that each interpreter can
* register a reset routine.
*/
void
snoop_recover(void)
{
int i;
/* Error recovery: reset output_area and associated variables */
for (i = 0; i < MAXSUM; i++)
sumline[i][0] = '\0';
detail_line[0] = '\0';
line[0] = '\0';
encap[0] = '\0';
sumcount = 0;
/* stacking/unstacking cannot be relied upon */
encap_levels = 0;
total_encap_levels = 1;
/* remove any pending timeouts */
(void) snoop_alarm(0, NULL);
}
/*
* snoop_sigrecover: global sigaction routine to manage recovery
* from catastrophic interpreter failures while interpreting
* and user termination are all handled. In the case of a corrupt
* packet or confused interpreter, the packet will be skipped, and
* execution will continue in scan().
*
* Global alarm handling (see snoop_alarm()) is managed here.
*
* Variables must be volatile to force the compiler to not optimize
* out the signal blocking.
*/
/*ARGSUSED*/
static void
{
/*
* Invoke any registered alarms. This involves first calculating
* the time for the next alarm, setting it up, then progressing
* through handler invocations. Note that since handlers may
* use siglongjmp(), in the worst case handlers may be serviced
* at a later time.
*/
/* Calculate next alarm time */
}
}
}
/* Setup next alarm */
if (nalarm) {
} else {
snoop_nalarm = 0;
}
/* Invoke alarm handlers (may not return) */
}
}
}
} else {
}
/*
* Exit if a signal has occurred after snoop has begun the process
* of quitting.
*/
if (quitting)
exit(1);
/*
* If an alarm handler has timed out, and snoop_nrecover has
* reached SNOOP_MAXRECOVER, skip to the next packet.
*
* If any other signal has occurred, and snoop_nrecover has
* reached SNOOP_MAXRECOVER, give up.
*/
/*
* We've stalled on output, which is not a critical
* failure. Reset the recovery counter so we do not
* consider this a persistent failure, and return so
* we do not skip this packet.
*/
snoop_nrecover = 0;
return;
}
if (snoop_nrecover >= SNOOP_MAXRECOVER) {
"snoop: WARNING: skipping from packet %d\n",
count);
snoop_nrecover = 0;
} else {
/* continue trying */
return;
}
} else if (snoop_nrecover >= SNOOP_MAXRECOVER) {
"snoop: ERROR: cannot recover from packet %d\n", count);
exit(1);
}
#ifdef DEBUG
#endif /* DEBUG */
/*
* Prepare to quit. This allows final processing to occur
* after first terminal interruption.
*/
quitting = 1;
return;
/* Inform user that snoop has taken a fault */
"WARNING: received signal %d from packet %d\n",
}
/* Reset interpreter variables */
/* Continue in scan() with the next packet */
/*NOTREACHED*/
}
/*
* Protected malloc for global error recovery: prepare for interpreter
* failures with corrupted packets or confused interpreters. Dynamically
* allocate `nbytes' bytes, and sandwich it between two PROT_NONE pages to
* catch writes outside of the allocated region.
*/
static char *
{
if (start == MAP_FAILED) {
perror("Error: protmalloc: mmap");
return (NULL);
}
perror("Warning: mprotect");
perror("Warning: mprotect");
return (start);
}
/*
* resetperm - reduce security vulnerabilities by resetting
* owner/group/permissions. Always attempt setuid() - if we have
* permission to drop our privilege level, do so.
*/
void
resetperm(void)
{
if (geteuid() == 0) {
(void) setgid(GID_NOBODY);
(void) setuid(UID_NOBODY);
}
}