NetBSD/usr.sbin/tcpdump/pcap.c

213 lines
5.0 KiB
C

/*
* Copyright (c) 1990 The Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that: (1) source code distributions
* retain the above copyright notice and this paragraph in its entirety, (2)
* distributions including binary code include the above copyright notice and
* this paragraph in its entirety in the documentation or other materials
* provided with the distribution, and (3) all advertising materials mentioning
* features or use of this software display the following acknowledgement:
* ``This product includes software developed by the University of California,
* Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
* the University nor the names of its contributors may be used to endorse
* or promote products derived from this software without specific prior
* written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* $Id: pcap.c,v 1.2 1994/12/23 17:06:07 cgd Exp $
*/
#ifndef lint
static char rcsid[] =
"@(#) Header: pcap-bpf.c,v 1.29 92/06/02 17:57:29 mccanne Exp (LBL)";
#endif
#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
#include <ctype.h>
#include <signal.h>
#include <errno.h>
#include <sys/param.h> /* optionally get BSD define */
#include <sys/time.h>
#include <sys/timeb.h>
#include <sys/socket.h>
#include <sys/file.h>
#include <sys/ioctl.h>
#include <net/bpf.h>
#include <net/if.h>
#include <string.h>
#include "interface.h"
extern int errno;
static void
bpf_stats(fd)
int fd;
{
struct bpf_stat s;
if (ioctl(fd, BIOCGSTATS, &s) < 0)
return;
(void)fflush(stdout);
(void)fprintf(stderr, "%d packets received by filter\n", s.bs_recv);
(void)fprintf(stderr, "%d packets dropped by kernel\n", s.bs_drop);
}
void
readloop(cnt, if_fd, fp, printit)
int cnt;
int if_fd;
struct bpf_program *fp;
void (*printit)();
{
u_char *buf;
u_int bufsize;
int cc;
if (ioctl(if_fd, BIOCGBLEN, (caddr_t)&bufsize) < 0) {
perror("tcpdump: BIOCGBLEN");
exit(1);
}
buf = (u_char *)malloc(bufsize);
if (ioctl(if_fd, BIOCSETF, (caddr_t)fp) < 0) {
perror("tcpdump: BIOCSETF");
exit(1);
}
while (1) {
register u_char *bp, *ep;
if ((cc = read(if_fd, (char *)buf, (int)bufsize)) < 0) {
/* Don't choke when we get ptraced */
if (errno == EINTR)
continue;
#if defined(sun) && !defined(BSD)
/*
* Due to a SunOS bug, after 2^31 bytes, the kernel
* file offset overflows and read fails with EINVAL.
* The lseek() to 0 will fix things.
*/
if (errno == EINVAL &&
(long)(tell(if_fd) + bufsize) < 0) {
(void)lseek(if_fd, 0, 0);
continue;
}
#endif
perror("tcpdump: read");
exit(1);
}
/*
* Loop through each packet.
*/
#define bhp ((struct bpf_hdr *)bp)
bp = buf;
ep = bp + cc;
while (bp < ep) {
register int caplen, hdrlen;
if (cnt >= 0 && --cnt < 0)
goto out;
(*printit)(bp + (hdrlen = bhp->bh_hdrlen),
&bhp->bh_tstamp, bhp->bh_datalen,
caplen = bhp->bh_caplen);
bp += BPF_WORDALIGN(caplen + hdrlen);
}
#undef bhp
}
out:
wrapup(if_fd);
}
wrapup(fd)
int fd;
{
bpf_stats(fd);
close(fd);
}
static inline int
bpf_open()
{
int fd;
int n = 0;
char device[sizeof "/dev/bpf000"];
/*
* Go through all the minors and find one that isn't in use.
*/
do {
(void)sprintf(device, "/dev/bpf%d", n++);
fd = open(device, O_RDONLY);
} while (fd < 0 && errno == EBUSY);
if (fd < 0) {
(void) fprintf(stderr, "tcpdump: ");
perror(device);
exit(-1);
}
return fd;
}
int
initdevice(device, pflag, linktype)
char *device;
int pflag;
int *linktype;
{
struct timeval timeout;
int if_fd;
struct ifreq ifr;
struct bpf_version bv;
if_fd = bpf_open();
if (ioctl(if_fd, BIOCVERSION, (caddr_t)&bv) < 0)
warning("kernel bpf interpreter may be out of date");
else if (bv.bv_major != BPF_MAJOR_VERSION ||
bv.bv_minor < BPF_MINOR_VERSION)
error("requires bpf language %d.%d or higher; kernel is %d.%d",
BPF_MAJOR_VERSION, BPF_MINOR_VERSION,
bv.bv_major, bv.bv_minor);
(void)strncpy(ifr.ifr_name, device, sizeof(ifr.ifr_name));
if (ioctl(if_fd, BIOCSETIF, (caddr_t)&ifr) < 0) {
(void) fprintf(stderr, "tcpdump: BIOCSETIF: ");
perror(device);
exit(-1);
}
/* Get the data link layer type. */
if (ioctl(if_fd, BIOCGDLT, (caddr_t)linktype) < 0) {
perror("tcpdump: BIOCGDLT");
exit(-1);
}
/* set timeout */
timeout.tv_sec = 1;
timeout.tv_usec = 0;
if (ioctl(if_fd, BIOCSRTIMEOUT, (caddr_t)&timeout) < 0) {
perror("tcpdump: BIOCSRTIMEOUT");
exit(-1);
}
/* set promiscuous mode if requested, but only for broadcast nets */
if (pflag == 0) {
switch (*linktype) {
case DLT_SLIP:
case DLT_PPP:
case DLT_NULL:
break;
default:
if (ioctl(if_fd, BIOCPROMISC, (void *)0) < 0) {
perror("tcpdump: BIOCPROMISC");
exit(-1);
}
}
}
return(if_fd);
}