http://qs1969.pair.com?node_id=723734

JPaul has asked for the wisdom of the Perl Monks concerning the following question:

Greetings all;

I have a need to scratch and haven't been able to work out a good way of doing it all together in an elegant, let alone functional, way.
I need to capture and count packets coming from multiple sources, using multiple filters, in real time. Right now I just have three separate perl scripts doing the task, but I'd like to fold it into a single tool. I currently use an open() on the tcpdump tool (under Linux).

Simple psuedo-code to give you an idea what I'm up to:

$SIG{ALRM} = \&every_sec(); alarm(1); open(PROCESS, "tcpdump -ln 'filter'"|); while(<PROCESS>) { next if (!&ParseIPandCheckSomething($_)); $Counter++; } sub every_sec { print "This past second: $Counter\n"; $Counter = 0; alarm(1); }
Here are the approaches I've looked at:
Incorporating Net::Pcap and doing it the "proper" way without reading the external 'tcpdump' output... except Net::Pcap can't handle multiple instances (the filters and like are stored in a single shared variable, according to its documentation). I also require the use of a SIGALRM to keep track of per-second counts, and Net::Pcap::loop must disable this, because the alarm callback certainly isn't being called each second like it's supposed to.

Opening several 'tcpdump' filehandles and looping over reading each one. When I do this the 'real time' aspect of it goes to heck. I was trying to do a bunch of non-blocking read()s and pull in a block of bytes, jump to the next file handle, and so on, and then process completed inputted packets... but it obviously isn't either fast enough, or designed properly, because "real time" it is not.

foreach $filter (qw(filter1 filter2 filter3)) { my $fh = FileHandle->new(); $fh->open("tcpdump -ln $filter|"); $fh->autoflush(1); push(@FHStack, $fh); } while(1) { # Read anything (up to 4096 bytes) from tcpdump foreach my $fh (@FHStack) { my $bytes = read($FH, my $buffer, 4096); $PackStack{$FH} .= $buffer; } # Process what we've gotten, looking for a complete packet foreach my $fh (@FHStack) { if ($PackStack{$FH} =~ /\n/) { # LF means completed packet received from tcpdump $PackStack{$FH} = &ProcessPacketAndRemove($PackStack{$FH +}); } } }
Boy, folks, I know there's a better way to do this, I'd sure love to hear your input.

Thanks!

-- Alexander Widdlemouse undid his bellybutton and his bum dropped off --