Subject: | select() with pcap_dispatch() |
Date: | Tue, 18 Jan 2011 11:54:14 +0000 |
To: | bug-Net-Pcap [...] rt.cpan.org |
From: | Alexander Clouter <alex [...] digriz.org.uk> |
Hi,
I have been working on some code to process some particular IPv6
traffic[1], and want a nice responsive while() loop. To do this I use a
combination of select()/pcap_dispatch(). The following is conceptually
what I use (stripped of error checking, check the footnote linky for
the full version):
----
# not seen a packet larger than 86 bytes
# FIXME when set to 86 bytes I get truncated packets of 78 bytes
my $SNAPLEN = 96;
my $pcap = pcap_open_live($interface, $SNAPLEN, 1, 0, $err);
my $pcap_fd = pcap_get_selectable_fd($pcap);
# select()/poll() manpage recommends on Linux to use O_NONBLOCK
pcap_setnonblock($pcap, 0, \$err);
my $sel = '';
vec($sel, $pcap_fd, 1) = 1;
my $running = 1;
$SIG{'TERM'} = $SIG{'INT'} = sub {
print STDERR "received SIG$_[0], closing down\n";
$running = 0;
pcap_breakloop($pcap);
$SIG{'TERM'} = $SIG{'INT'} = 'DEFAULT';
};
while ($running) {
my $rc = select $sel, undef, $sel, undef;
if ($rc == -1) {
next if ($! == EINTR);
print STDERR "select(): $?\n";
exit EX_OSERR;
}
$rc = pcap_dispatch($pcap, -1, \&pcap_cb, $dpcap);
if ($rc < 0) {
if ($rc == -1) {
print STDERR "pcap_dispatch() failed: " . pcap_geterr($pcap)) . "\n";
exit EX_SOFTWARE;
}
}
}
exit EX_OK;
sub pcap_cb {
my ($rec, $hdr, $pkt) = @_;
pcap_dump($rec, $hdr, $pkt)
if (defined($rec));
if ($hdr->{'len'} > $hdr->{'caplen'}) {
if ($hdr->{'len'} > $SNAPLEN) {
print STDERR "phat packet ($hdr->{'len'}), please record and contact author\n";
$SNAPLEN = $hdr->{'len'};
return;
}
print STDERR "dropping phat packet ($hdr->{'len'})\n";
return;
}
[do stuff]
}
----
For the *first* select() call, when a packet is picked up by Net::Pcap,
select() notices the fd can be read from, and so calls pcap_dispatch()
with no problems. Once pcap_dispatch() clears out it's buffer, calls to
select() pretty much just block forever (even though a simultaneous run
of tcpdump alongside shows plenty of additional traffic should be
processed). strace'ing the process just shows it hanging on select() as
if nothing is appearing on the fd.
If I move to using poll() instead, the problem disappears and
everything works as expected:
----
my $p = IO::Poll->new;
$p->mask($pcap_fd => POLLIN);
[snipped]
while ($running) {
my $rc = $p->poll();
----
Any ideas what is going wrong? I'm happy to just use poll(), but
thought at least a documentation amendment might be called for. I
vaguely recall when writing catnip[2], I chose to use poll() instead of
select() for a similar reason.
I have tried making the socket blockable, no change. Moving to
pcap_loop() works, but I notice from an strace internally it uses poll()
also; but then pcap_loop() is not suitable for my needs anyway.
My guess is an underlying problem in the pcap library (I am using
1.1.1-2 from Debian 'unstable').
Let me know if you need more information.
Cheers
[1] http://stuff.digriz.org.uk/slaacer
[2] http://www.digriz.org.uk/catnip
--
Alexander Clouter
.sigmonster says: BOFH excuse #189:
SCSI's too wide.