File Coverage

lib/NetworkInfo/Discovery/Sniff.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.0


line stmt bran cond sub pod time code
1             package NetworkInfo::Discovery::Sniff;
2              
3 1     1   497 use vars qw(@ISA);
  1         2  
  1         49  
4 1     1   6 use strict;
  1         1  
  1         27  
5 1     1   4 use warnings;
  1         1  
  1         54  
6              
7 1     1   341 use NetworkInfo::Discovery::Detect;
  1         2  
  1         29  
8             @ISA = ("NetworkInfo::Discovery::Detect");
9              
10 1     1   1334 use Net::Pcap;
  0            
  0            
11             use NetPacket::Ethernet qw(:types);
12             use NetPacket::IP;
13             use NetPacket::TCP;
14             use NetPacket::UDP;
15             use NetPacket::ARP qw(:ALL);
16             use NetPacket::ICMP qw(:ALL);
17              
18              
19             sub new {
20             my $classname = shift;
21             my $self = $classname->SUPER::new(@_);
22             my %args = @_;
23              
24             # set defaults
25             $self->timeout(60);
26             $self->maxcapture(10);
27             $self->snaplen(1500);
28             $self->promisc(1);
29            
30             # use user settings that were passed in.
31             # for all args, see if we can autoload them
32             foreach my $attr (keys %args) {
33             if ($self->can($attr) ) {
34             $self->$attr( $args{$attr} );
35             } else {
36             print "error calling NetworkInfo::Discovery::Sniff-> $attr ( $args{$attr} ) : no method $attr \n";
37             }
38             }
39              
40             return $self;
41             }
42              
43             sub do_it {
44             my $self = shift;
45            
46             $self->capture;
47             $self->process_ip_packets;
48              
49             return $self->get_interfaces;
50             }
51              
52             sub capture {
53             my $self = shift;
54              
55             $self->{'device'} = Net::Pcap::lookupdev(\$self->{'error'});
56             defined $self->{'error'}
57             && die 'Unable to determine network device for monitoring - ', $self->{'error'};
58              
59             Net::Pcap::lookupnet($self->device, \$self->{'address'}, \$self->{'netmask'}, \$self->{'error'})
60             && die 'Unable to look up device information for ', $self->device, ' - ', $self->error;
61              
62             $self->realmask(join('.',unpack("C4",pack("N",$self->netmask))) );
63             $self->realip(join('.',unpack("C4",pack("N",$self->address))) );
64              
65             $self->{'object'} = Net::Pcap::open_live(
66             $self->device,
67             $self->snaplen,
68             $self->promisc,
69             $self->timeout,
70             \$self->{'error'}
71             );
72              
73             defined $self->{'object'}
74             || die 'Unable to create packet capture on device ', $self->device, ' - ', $self->{'error'};
75              
76             Net::Pcap::compile( $self->object, \$self->{'filter'}, '', 0, $self->netmask)
77             && die 'Unable to compile packet capture filter';
78              
79             Net::Pcap::setfilter($self->object, $self->filter)
80             && die 'Unable to set packet capture filter';
81              
82             Net::Pcap::loop($self->object, $self->maxcapture, \&get_packets, \@{$self->{'packetlist'}}) ;
83             # || die 'Unable to perform packet capture';
84            
85             Net::Pcap::close($self->object);
86              
87             }
88              
89             sub get_packets {
90             # print "get_pkt\n" if $DEBUG ;
91             my ( $arg , $hdr, $pkt) = @_ ;
92             push ( @$arg , $pkt ) ;
93             }
94              
95             sub process_ip_packets {
96             my $self = shift;
97              
98             foreach my $packet ( @{$self->{'packetlist'}} ) {
99             my $ether_obj = NetPacket::Ethernet->decode($packet);
100             my $ether_data = $ether_obj->{"data"};
101            
102             if ($ether_obj->{type} == ETH_TYPE_ARP ) {
103             my $arp_data = NetPacket::ARP->decode($ether_data);
104              
105             if ($arp_data->{opcode} == ARP_OPCODE_REQUEST) {
106             # my $shost = new NetworkInfo::Discovery::Host (ipaddress => hex2ip($arp_data->{spa}),
107             # does_ethernet => "yes",
108             # does_arp => "yes",
109             # mac => hex2mac($arp_data->{sha}) );
110             # $self->add_host($shost);
111             $self->add_interface(
112             {
113             ip=> hex2ip($arp_data->{spa}),
114             mac => hex2mac($arp_data->{sha}) ,
115             mask=> $self->realmask,
116             }
117             );
118              
119             } elsif ($arp_data->{opcode} == ARP_OPCODE_REPLY) {
120             # my $shost = new NetworkInfo::Discovery::Host (ipaddress => hex2ip($arp_data->{spa}),
121             # does_ethernet => "yes",
122             # does_arp => "yes",
123             # mac => hex2mac($arp_data->{sha}) );
124             # my $dhost = new NetworkInfo::Discovery::Host (ipaddress => hex2ip($arp_data->{tpa}),
125             # does_ethernet => "yes",
126             # does_arp => "yes",
127             # mac => hex2mac($arp_data->{tha}) );
128             # $self->add_host($shost,$dhost);
129             $self->add_interface(
130             {
131             ip=> hex2ip($arp_data->{spa}),
132             mac=> hex2mac($arp_data->{sha}),
133             mask=> $self->realmask,
134             } ,
135             {
136             ip=> hex2ip($arp_data->{tpa}),
137             mac => hex2mac($arp_data->{tha}),
138             mask=> $self->realmask,
139             }
140             );
141              
142             } elsif ($arp_data->{opcode} == RARP_OPCODE_REQUEST) {
143             print "got RARP_OPCODE_REQUEST\n";
144             } elsif ($arp_data->{opcode} == RARP_OPCODE_REPLY) {
145             print "got RARP_OPCODE_REPLY\n";
146             }
147            
148             } elsif ($ether_obj->{type} == ETH_TYPE_IP ) {
149             ## for IP packets
150            
151             my $ip = NetPacket::IP->decode($ether_data);
152            
153             if ($ip->{"proto"} == 6 ) {
154             # TCP Stuff
155             my ($sports, $dports);
156             my $tcp = NetPacket::TCP->decode($ip->{'data'});
157             push @$sports, $tcp->{'src_port'};
158             push @{$dports}, $tcp->{'dest_port'};
159              
160             # my $shost = new NetworkInfo::Discovery::Host (ipaddress => "$ip->{'src_ip'}",
161             # does_ethernet => "yes",
162             # does_tcp=> "yes");
163             # my $dhost = new NetworkInfo::Discovery::Host (ipaddress => "$ip->{'dest_ip'}",
164             # does_ethernet => "yes",
165             # does_tcp=> "yes");
166             #
167             # $self->add_host($shost,$dhost);
168             #
169              
170              
171             $self->add_interface(
172             {
173             ip=>"$ip->{'src_ip'}",
174             mask=>( ($self->matches_subnet($ip->{'src_ip'})) ? $self->realmask : ""),
175             },
176             {
177             ip=>"$ip->{'dest_ip'}",
178             mask=>( ($self->matches_subnet($ip->{'dest_ip'})) ? $self->realmask : ""),
179             }
180             );
181              
182             } elsif ($ip->{"proto"} == 17 ) {
183             # UDP Stuff
184             my $udp = NetPacket::UDP->decode($ip->{'data'});
185            
186             my ($sports, $dports);
187             push @$sports, $udp->{'src_port'};
188             push @{$dports}, $udp->{'dest_port'};
189              
190             # my $shost = new NetworkInfo::Discovery::Host (ipaddress => "$ip->{'src_ip'}",
191             # does_ethernet => "yes",
192             # does_udp=> "yes");
193             # my $dhost = new NetworkInfo::Discovery::Host (ipaddress => "$ip->{'dest_ip'}",
194             # does_ethernet => "yes",
195             # does_udp=> "yes");
196             #
197             # $self->add_host($shost,$dhost);
198             $self->add_interface(
199             {
200             ip=>$ip->{'src_ip'},
201             mask=>( ($self->matches_subnet($ip->{'src_ip'})) ? $self->realmask : ""),
202             },
203             {
204             ip=>$ip->{'dest_ip'},
205             mask=>( ($self->matches_subnet($ip->{'dest_ip'})) ? $self->realmask : ""),
206             },
207             );
208            
209             } elsif ($ip->{"proto"} == 1 ) {
210             # ICMP stuff here
211             my $icmp = NetPacket::ICMP->decode($ip->{'data'});
212            
213             my $type;
214             if ($icmp->{type} == ICMP_ECHOREPLY ) {
215             $type = "ICMP_ECHOREPLY";
216             } elsif ($icmp->{type} == ICMP_UNREACH ) {
217             $type = "ICMP_UNREACH";
218             } elsif ($icmp->{type} == ICMP_SOURCEQUENCH ) {
219             $type = "ICMP_SOURCEQUENCH";
220             } elsif ($icmp->{type} == ICMP_REDIRECT ) {
221             $type = "ICMP_REDIRECT";
222             } elsif ($icmp->{type} == ICMP_ECHO ) {
223             $type = "ICMP_ECHO";
224             } elsif ($icmp->{type} == ICMP_ROUTERADVERT ) {
225             $type = "ICMP_ROUTERADVERT";
226             } elsif ($icmp->{type} == ICMP_ROUTERSOLICIT ) {
227             $type = "ICMP_ROUTERSOLICIT";
228             } elsif ($icmp->{type} == ICMP_TIMXCEED ) {
229             $type = "ICMP_TIMXCEED";
230             } elsif ($icmp->{type} == ICMP_PARAMPROB ) {
231             $type = "ICMP_PARAMPROB";
232             } elsif ($icmp->{type} == ICMP_TSTAMP ) {
233             $type = "ICMP_TSTAMP";
234             } elsif ($icmp->{type} == ICMP_TSTAMPREPLY ) {
235             $type = "ICMP_TSTAMPREPLY";
236             } elsif ($icmp->{type} == ICMP_IREQ ) {
237             $type = "ICMP_IREQ";
238             } elsif ($icmp->{type} == ICMP_MASREQ ) {
239             $type = "ICMP_MASREQ";
240             } elsif ($icmp->{type} == ICMP_IREQREPLY ) {
241             $type = "ICMP_IREQREPLY";
242             } elsif ($icmp->{type} == ICMP_MASKREPLY ) {
243             $type = "ICMP_MASKREPLY";
244             }
245            
246             # my $shost = new NetworkInfo::Discovery::Host (ipaddress => "$ip->{'src_ip'}",
247             # does_ethernet => "yes",
248             # does_icmp=>"yes");
249             # my $dhost = new NetworkInfo::Discovery::Host (ipaddress => "$ip->{'dest_ip'}",
250             # does_ethernet => "yes",
251             # does_icmp=>"yes");
252             #
253             # $self->add_host($shost,$dhost);
254             $self->add_interface(
255             {
256             ip=>$ip->{'src_ip'},
257             mask=>( ($self->matches_subnet($ip->{'src_ip'})) ? $self->realmask : ""),
258             },
259             {
260             ip=>$ip->{'dest_ip'},
261             mask=>( ($self->matches_subnet($ip->{'dest_ip'})) ? $self->realmask : ""),
262             },
263             );
264             }
265            
266             } else {
267             print("Unknown Ethernet Type: $ether_obj->{src_mac}:$ether_obj->{dest_mac} $ether_obj->{type}\n");
268            
269             }
270             }
271             }
272              
273             sub filter {
274             my $self = shift;
275             $self->{'filter'} = shift if (@_) ;
276             return $self->{'filter'};
277             }
278             sub object {
279             my $self = shift;
280             $self->{'object'} = shift if (@_) ;
281             return $self->{'object'};
282             }
283             sub device {
284             my $self = shift;
285             $self->{'device'} = shift if (@_);
286             return $self->{'device'};
287             }
288             sub address {
289             my $self = shift;
290             $self->{'address'} = shift if (@_);
291             return $self->{'address'};
292             }
293             sub netmask {
294             my $self = shift;
295             $self->{'netmask'} = shift if (@_);
296             return $self->{'netmask'};
297             }
298             sub error {
299             my $self = shift;
300             $self->{'error'} = shift if (@_);
301             return $self->{'error'};
302             }
303             sub snaplen {
304             my $self = shift;
305             $self->{'snaplen'} = shift if (@_);
306             return $self->{'snaplen'};
307             }
308             sub maxcapture {
309             my $self = shift;
310             $self->{'maxcapture'} = shift if (@_);
311             return $self->{'maxcapture'};
312             }
313             sub timeout {
314             my $self = shift;
315             $self->{'timeout'} = shift if (@_);
316             return $self->{'timeout'};
317             }
318             sub promisc {
319             my $self = shift;
320             $self->{'promisc'} = shift if (@_);
321             return $self->{'promisc'};
322             }
323             sub realip {
324             my $self = shift;
325             $self->{'realip'} = shift if (@_);
326             return $self->{'realip'};
327             }
328             sub realmask {
329             my $self = shift;
330             $self->{'realmask'} = shift if (@_);
331             return $self->{'realmask'};
332             }
333              
334              
335             sub matches_subnet {
336             my $self= shift;
337             my $ip = shift;
338              
339             my $bits;
340              
341             # get our ip in machine representation
342             my $mainIP = unpack("N", pack("C4", split(/\./, $ip)));
343              
344             if ($self->realmask =~ m!^\d+\.\d+\.\d+\.\d+!) {
345             my $mask_bits=unpack("B32", pack("C4", split(/\./, $self->realmask)));
346             $bits=length( (split(/0/,$mask_bits,2))[0] );
347             }
348             # what is left over from the mask
349             $bits = 32 - ($bits || 32);
350              
351             # put this acl into machine representation
352             my $otherIP = unpack("N", pack("C4", split(/\./, $self->realip)));
353              
354             # keep only the important parts of the ip address/mask pair
355             my $maskedIP = $otherIP >> $bits;
356              
357             # return true if this one matches
358             return 1 if ($maskedIP == ($mainIP >> $bits));
359              
360             # return false if we didn't match any acl
361             return 0;
362              
363             }
364              
365             sub hex2mac {
366             my $data = shift;
367              
368             my ($a, $b, $c, $d, $e, $f) = ($data =~ m/^(..)(..)(..)(..)(..)(..)$/);
369             return "$a:$b:$c:$d:$e:$f";
370             }
371              
372             sub hex2ip {
373             my $data = shift;
374              
375             my ($a, $b, $c, $d) = ($data =~ m/^(..)(..)(..)(..)$/);
376             $a = hex $a;
377             $b = hex $b;
378             $c = hex $c;
379             $d = hex $d;
380             return "$a.$b.$c.$d";
381             }
382             1;