File Coverage

blib/lib/Net/Pcap/FindDevice.pm
Criterion Covered Total %
statement 4 6 66.6
branch n/a
condition n/a
subroutine 2 2 100.0
pod n/a
total 6 8 75.0


line stmt bran cond sub pod time code
1             package Net::Pcap::FindDevice;
2 1     1   1896 use strict;
  1         4  
  1         86  
3 1     1   639 use Net::Pcap; # just for the convenience function below
  0            
  0            
4             use Carp qw(croak);
5             use Exporter 'import';
6              
7             use vars qw($VERSION @EXPORT);
8             $VERSION = '0.24';
9             @EXPORT = qw(find_device);
10              
11             # TODO: Add diagnosis function to tell the user what the "best" function is
12              
13             =head1 NAME
14              
15             Net::Pcap::FindDevice - find the "best" network device for sniffing
16              
17             =head1 SYNOPSIS
18              
19             use Net::Pcap;
20             use Net::Pcap::FindDevice;
21              
22             my $device = find_device($ARGV[0]);
23              
24             my $pcap = Net::Pcap::open_live($device, 128000, -1, 500, \$err);
25              
26             This module exports only one subroutine, C,
27             which employs a dwimish method to find a network
28             device suitable for sniffing with L.
29              
30             =head2 C<< find_device DEVICE >>
31              
32             Finds a good L device based on some criteria:
33              
34             If the parameter given is a regular expression,
35             is used to scan the names I descriptions of the Net::Pcap
36             device list. The name of the first matching element
37             is returned.
38              
39             If a L device matching the
40             stringified parameter exists, it is returned.
41             If there exists no matching device for the scalar,
42             C is returned.
43              
44             If there is only one network device, the name of
45             that device is returned.
46              
47             If there is only one device left after removing all
48             network devices with IP address 127.0.0.1, the name
49             of that device is returned.
50              
51             The name of the device with the default gateway
52             (if any) is returned.
53              
54             Otherwise it gives up and returns C.
55              
56             =cut
57              
58             sub find_device {
59             my ($device_name) = @_;
60             # Set up Net::Pcap
61             my @devs = Net::Pcap::findalldevs(\my %devinfo,\my $err);
62             $err ||= '';
63             if (! @devs) {
64             croak <
65             Net::Pcap didn't find any device: ($err).
66             This may be because your version of libpcap is too
67             low or you might not have the sufficient
68             privileges. You might also not have any networking
69             installed on this system.
70             NO_DEVICE
71             };
72              
73             my $device = $device_name;
74             if ($device_name) {
75             if (ref $device_name eq 'Regexp') {
76             ($device) = grep {$_ =~ /$device_name/ || $_ =~ $devinfo{$_}} keys %devinfo;
77             } elsif (exists $devinfo{$device_name}) {
78             $device = $device_name;
79             } elsif ( $device_name =~ m!^\d+\.\d+\.\d+\.\d+$! ) {
80             ($device) = interfaces_from_ip( $device_name );
81             } else {
82             croak "Don't know how to handle $device_name as a Net::Pcap device";
83             };
84             } else {
85             # TODO: Remove Data::Dumper dependency
86             #use Data::Dumper;
87             #warn Dumper \%devinfo;
88             # 'any' is disabled as it returns information in a format
89             # I don't understand
90             #if (exists $devinfo{any}) {
91             # $device = 'any';
92             #} elsif
93             if (@devs == 1) {
94             $device = $devs[0];
95             } else {
96             # Now we need to actually look at the devices and select the
97             # one with the default gateway:
98              
99             # First, get the default gateway by using
100             # `netstat -rn` and looking for the interface tied to the gateway
101             my $device_ip;
102             my $re_if = $^O eq 'MSWin32'
103             # route mask gateway interface
104             ? qr/^\s*(?:0.0.0.0)\s+(?:\S+)\s+(\S+)\s+(\S+)/
105             : qr/^(?:0.0.0.0|default)\s+(\S+)\s+.*?(\S+)\s*$/;
106             for (qx{netstat -rn}) {
107             if ( /$re_if/ ) {
108             $device_ip = $2;
109             #warn "Found $2 in $_";
110             last;
111             };
112             };
113              
114             #if (! $device_ip) {
115             # croak "Couldn't find IP address/interface of the default gateway interface. Maybe 'netstat' is unavailable?";
116             #};
117              
118             #for (keys %devinfo) {
119             # warn $_;
120             #};
121             if (exists $devinfo{$device_ip}) {
122             return $device_ip
123             };
124              
125             # Looks like we got an IP and not an interface name.
126              
127             # This should all go into
128             # sub interface_from_ip {}
129              
130             # So scan all interfaces if they have that IP address.
131              
132             my @good_devices = interfaces_from_ip($device_ip);
133              
134             if (@good_devices == 1) {
135             $device = $good_devices[0];
136             } elsif (@good_devices > 1) {
137             croak "Too many device candidates found (@good_devices)";
138             }
139             };
140             };
141              
142             return $device
143             };
144              
145             =head2 C<< interfaces_from_ip IP >>
146              
147             Returns all interfaces that have the ip C.
148             The value of C must be given as a string of
149             four numbers.
150              
151             This method is not exported by default so you
152             need to call it fully specified as
153              
154             Net::Pcap::FindDevice::interfaces_from_ip('127.0.0.1')
155              
156             =cut
157              
158             sub interfaces_from_ip {
159             my ($ip) = @_;
160             my $good_address = unpack "N", pack "C4", (split /\./, $ip);
161              
162             my @devs = Net::Pcap::findalldevs(\my %devinfo,\my $err);
163             my @result;
164             for my $device (@devs) {
165             #warn "$device/$ip";
166             (Net::Pcap::lookupnet($device, \(my $address), \(my $netmask), \$err) == 0) or next;
167              
168             #print "$device / $address / $netmask\n";
169             #for ($address,$netmask) {
170             # print ((join ".", unpack "C4", pack "N", $_),"\n");
171             #};
172              
173             $address != 0 or next;
174              
175             for ($address,$netmask) {
176             $_ = unpack "N", pack "N", $_;
177             };
178             #print "$device / $address / $netmask\n";
179              
180             if ($address == ($good_address & $netmask)) {
181             push @result, $device;
182             };
183             };
184             @result
185             };
186              
187             1;
188              
189             __END__