Hello
I have found a potential bug in libpcap on OpenBSD and
likely FreeBSD as
well. If you simultaneously open several programs that open
pcap
connections, you can cause the system to lose track of some
of its
BPF's. When you close all the pcap connections some of the
BPF's may
report that they are still busy.
You can reproduce this problem by doing the following the
instructions
at the end of this message.
This will simultaneously open several copies of bpfmaker and
close them
in a loop and lets you compare the number of bpf's being
used on your
system at the start and end of the program.
I think the problem is that two programs simultaneously
opening a live
pcap connection may cause two bpf device files to point to
the same BPF
in the OS, or two BPF's in the OS to point to the same
device file. So
when one connection is closed, the other one will no longer
properly
clean up when it releases the device file in pcap_close().
To fix the problem I have placed a file lock around the
following code
in pcap_open_live(). Ive also attached the new pcap-bpf.c.
fd = bpf_open(p, ebuf);
if (fd < 0)
goto bad;
p->fd = fd;
p->snapshot = snaplen;
if (ioctl(fd, BIOCVERSION, (caddr_t)&bv) < 0) {
snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCVERSION:
%s",
pcap_strerror(errno));
goto bad;
}
if (bv.bv_major != BPF_MAJOR_VERSION ||
bv.bv_minor < BPF_MINOR_VERSION) {
snprintf(ebuf, PCAP_ERRBUF_SIZE,
"kernel bpf filter out of date");
goto bad;
}
/*
* Try finding a good size for the buffer; 32768 may be
too
* big, so keep cutting it in half until we find a size
* that works, or run out of sizes to try. If the
default
* is larger, don't make it smaller.
*
* XXX - there should be a user-accessible hook to set
the
* initial buffer size.
*/
if ((ioctl(fd, BIOCGBLEN, (caddr_t)&v) < 0) || v
< 32768)
v = 32768;
for ( ; v != 0; v >>= 1) {
/* Ignore the return value - this is because the
call fails
* on BPF systems that don't have kernel malloc.
And if
* the call fails, it's no big deal, we just
continue to
* use the standard buffer size.
*/
(void) ioctl(fd, BIOCSBLEN, (caddr_t)&v);
(void)strncpy(ifr.ifr_name, device,
sizeof(ifr.ifr_name));
if (ioctl(fd, BIOCSETIF, (caddr_t)&ifr) >=
0)
break; /* that size worked; we're done */
if (errno != ENOBUFS) {
snprintf(ebuf, PCAP_ERRBUF_SIZE,
"BIOCSETIF: %s: %s",
device, pcap_strerror(errno));
goto bad;
}
}
If you think that this is an acceptable change, I can clean
up the code
to the standard coding conventions used in the project and
do proper
error reporting and submit that code for approval. I dont
know exactly
how this process works.
Thanks
Jonathan Steel
Reproducing the problem
1. Copy the following into bpfmaker.c
#include <pcap.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <signal.h>
// Opens a pcap connection to the given device and does
nothing with
// the packets. Closes the pcap connection when the program
// is terminated.
char errBuf[PCAP_ERRBUF_SIZE];
pcap_t *sessionHandle;
void receivePacket(u_char *args, const struct pcap_pkthdr
*header,
const u_char *packet);
void terminateOnSignal(int signal);
int main(int argc, char *argv[]) {
int status;
if (argc == 1) {
fprintf(stderr, "Please provide the name of the
ethernet devicen");
return 1;
}
signal(SIGINT, terminateOnSignal);
signal(SIGQUIT, terminateOnSignal);
signal(SIGTRAP, terminateOnSignal);
signal(SIGABRT, terminateOnSignal);
signal(SIGTERM, terminateOnSignal);
signal(SIGTERM, terminateOnSignal);
sessionHandle = pcap_open_live(argv[1], 2000, 1, 1000,
errBuf);
if (sessionHandle == NULL) {
fprintf(stderr, "Error opening pcap
connectionn");
}
status = pcap_loop(sessionHandle, -1, receivePacket,
NULL);
fprintf(stderr, "Abnormal program
terminationn");
}
void receivePacket(u_char *args, const struct pcap_pkthdr
*header,
const u_char *packet) {
return;
}
void terminateOnSignal(int signal) {
if ( sessionHandle )
pcap_close(sessionHandle);
exit(99);
}
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
2. Copy the following into bpfmaker.pl
#!/usr/bin/perl
if ($ARGV[0] eq "") {
print "Please provide the name of the interface
devicen";
exit 1;
}
print "BPF's at startup: ";
system("cat /dev/bpf* 2>&1 | grep busy | wc
-l");
for ($j = 0; $j < 10; $j++) {
for ($i = 0; $i < 8; $i++) {
# creates a program to open a pcap connection
#system("./bpfmaker $ARGV[0] 2> /dev/null
&");
system("./bpfmaker $ARGV[0] &");
}
# kills all the active bpfmaker programs
do {
$pid = `ps ax | grep bpfmaker | grep -v grep | head
-n 1`;
$pid =~ m/(d+).*/;
$pid = $1;
if ($pid) { `kill $pid` };
} while ($pid);
}
print "BPF's upon ending: ";
system("cat /dev/bpf* 2>&1 | grep busy | wc
-l");
///////////////////////////////////////
///////////////////////////////////////
3. run `gcc bpfmaker.c -o bpfmaker -lpcap`
4. run `perl bfpmaker.pl <name of your ethernet card>
-
This is the tcpdump-workers list.
Visit https://cod.sandelman.ca/
a> to unsubscribe.
|