| 1 | #!/usr/bin/perl -w
|
|---|
| 2 | #
|
|---|
| 3 | # Syntax: mr-net-find <IP Addr>
|
|---|
| 4 | # finds on which network device the the server is
|
|---|
| 5 | # $Id$
|
|---|
| 6 | #
|
|---|
| 7 | # Copyright B. Cornec 2007-2012
|
|---|
| 8 | # Provided under the GPL v2
|
|---|
| 9 |
|
|---|
| 10 | # Syntax: see at end
|
|---|
| 11 |
|
|---|
| 12 | use strict 'vars';
|
|---|
| 13 | use Socket;
|
|---|
| 14 | use Data::Dumper;
|
|---|
| 15 | use Getopt::Long qw(:config auto_abbrev no_ignore_case);
|
|---|
| 16 | use English;
|
|---|
| 17 | #use lib qw (lib);
|
|---|
| 18 | use ProjectBuilder::Version;
|
|---|
| 19 | use ProjectBuilder::Base;
|
|---|
| 20 |
|
|---|
| 21 | =pod
|
|---|
| 22 |
|
|---|
| 23 | =head1 NAME
|
|---|
| 24 |
|
|---|
| 25 | mr-net-find, finds on which network device a machine is reachable
|
|---|
| 26 |
|
|---|
| 27 | =head1 DESCRIPTION
|
|---|
| 28 |
|
|---|
| 29 | mr-net-find, finds on which network device an machine is reachable
|
|---|
| 30 | It takes the IP or named looked at as single parameter and diagnose on which device it's reachable or gives NONET if it's not.
|
|---|
| 31 |
|
|---|
| 32 | =head1 SYNOPSIS
|
|---|
| 33 |
|
|---|
| 34 | mr-net-find [-v] NFS_SERVER_NAME
|
|---|
| 35 |
|
|---|
| 36 | =head1 OPTIONS
|
|---|
| 37 |
|
|---|
| 38 | =over 4
|
|---|
| 39 |
|
|---|
| 40 | =item B<-v|--verbose>
|
|---|
| 41 |
|
|---|
| 42 | Be more verbose
|
|---|
| 43 |
|
|---|
| 44 | =back
|
|---|
| 45 |
|
|---|
| 46 | =head1 WEB SITES
|
|---|
| 47 |
|
|---|
| 48 | The main Web site of the project is available at L<http://www.mondorescue.org/>. Bug reports should be filled using the trac instance of the project at L<http://trac.mondorescue.org/>.
|
|---|
| 49 |
|
|---|
| 50 | =head1 USER MAILING LIST
|
|---|
| 51 |
|
|---|
| 52 |
|
|---|
| 53 | =head1 AUTHORS
|
|---|
| 54 |
|
|---|
| 55 | The MondoRescue team L<http://www.mondorescue.org/> lead by Bruno Cornec L<mailto:bruno@mondorescue.org>.
|
|---|
| 56 |
|
|---|
| 57 | =head1 COPYRIGHT
|
|---|
| 58 |
|
|---|
| 59 | MondoRescue.org is distributed under the GPL v2.0 license
|
|---|
| 60 | described in the file C<COPYING> included with the distribution.
|
|---|
| 61 |
|
|---|
| 62 | =cut
|
|---|
| 63 |
|
|---|
| 64 | # Global variables
|
|---|
| 65 | my ($mrver,$mrrev) = pb_version_init();
|
|---|
| 66 | my $appname = "mr-net-find";
|
|---|
| 67 | my %opts; # CLI Options
|
|---|
| 68 |
|
|---|
| 69 | # Initialize the syntax string
|
|---|
| 70 |
|
|---|
| 71 | pb_syntax_init("$appname Version $mrver-$mrrev\n");
|
|---|
| 72 |
|
|---|
| 73 | GetOptions("help|?|h" => \$opts{'h'},
|
|---|
| 74 | "man" => \$opts{'man'},
|
|---|
| 75 | "verbose|v+" => \$opts{'v'},
|
|---|
| 76 | ) || pb_syntax(-1,0);
|
|---|
| 77 |
|
|---|
| 78 | if (defined $opts{'h'}) {
|
|---|
| 79 | pb_syntax(0,1);
|
|---|
| 80 | }
|
|---|
| 81 | if (defined $opts{'man'}) {
|
|---|
| 82 | pb_syntax(0,2);
|
|---|
| 83 | }
|
|---|
| 84 | if (defined $opts{'v'}) {
|
|---|
| 85 | $pbdebug = $opts{'v'};
|
|---|
| 86 | }
|
|---|
| 87 | pb_log_init($pbdebug, $pbLOG);
|
|---|
| 88 |
|
|---|
| 89 |
|
|---|
| 90 | # Check to which network an IP balongs
|
|---|
| 91 | sub mr_net_for_ip {
|
|---|
| 92 |
|
|---|
| 93 | my ($ip, $if) = @_;
|
|---|
| 94 | my $dev = undef;
|
|---|
| 95 |
|
|---|
| 96 | # For each device on the system
|
|---|
| 97 | foreach my $d (keys %$if) {
|
|---|
| 98 | # Skip non fully activated interfaces
|
|---|
| 99 | next if ((not defined $if->{$d}->{'nm'}) || (not defined $if->{$d}->{'net'}) || (not defined $if->{$d}->{'bcast'}));
|
|---|
| 100 | # Get the net & bcast assuming the ip is on that net with that nm
|
|---|
| 101 | my ($net,$bcast) = mr_net_nbcast_from_in ($ip,$if->{$d}->{'nm'});
|
|---|
| 102 | # Is it true ?
|
|---|
| 103 | if (($net eq $if->{$d}->{'net'}) && ($bcast eq $if->{$d}->{'bcast'})) {
|
|---|
| 104 | # found it, return that value
|
|---|
| 105 | $dev = $d;
|
|---|
| 106 | last;
|
|---|
| 107 | }
|
|---|
| 108 | }
|
|---|
| 109 | return($dev);
|
|---|
| 110 | }
|
|---|
| 111 |
|
|---|
| 112 | sub mr_net_find_all {
|
|---|
| 113 |
|
|---|
| 114 | my %if;
|
|---|
| 115 | my $if = \%if;
|
|---|
| 116 |
|
|---|
| 117 | my $curdev = "";
|
|---|
| 118 | # TODO: Check whther the ip command is available !
|
|---|
| 119 | # If not consider using ifconfig for compatibility or non Linux availability
|
|---|
| 120 | my $ipcmd = pb_check_req("ip",1);
|
|---|
| 121 | if (defined $ipcmd) {
|
|---|
| 122 | open(IP,"$ipcmd addr |") || die "Unable to read IP config with $ipcmd addr\n";
|
|---|
| 123 | while (<IP>) {
|
|---|
| 124 | # Remove duplicate spaces
|
|---|
| 125 | my $line = $_;
|
|---|
| 126 | $line =~ s/\s+/ /g;
|
|---|
| 127 | pb_log(2,"Line: $line\n");
|
|---|
| 128 | # Check for a new interface
|
|---|
| 129 | my $tmp;
|
|---|
| 130 | if ($line =~ /^[0-9]+:/) {
|
|---|
| 131 | my $dev;
|
|---|
| 132 | my $rank;
|
|---|
| 133 | ($rank,$dev,$tmp) = split(/:/,$line);
|
|---|
| 134 | pb_log(3,"$rank,$dev,$tmp\n");
|
|---|
| 135 | $dev =~ s/\s*//;
|
|---|
| 136 | $if->{$dev}->{'if'} = $dev;
|
|---|
| 137 | $curdev = $dev;
|
|---|
| 138 | }
|
|---|
| 139 | ($tmp,$tmp,$if->{$curdev}->{'mac'},$tmp) = split(/ /,$line) if (/^\s*link/);
|
|---|
| 140 | ($tmp,$tmp,$if->{$curdev}->{'cidr'},$tmp) = split(/ /,$line) if (/^\s*inet/);
|
|---|
| 141 | }
|
|---|
| 142 | close(IP);
|
|---|
| 143 | } else {
|
|---|
| 144 | my $ipcmd = pb_check_req("ifconfig",1);
|
|---|
| 145 | if (defined $ipcmd) {
|
|---|
| 146 | open(IP,"$ipcmd -a |") || die "Unable to read IP config with $ipcmd -a\n";
|
|---|
| 147 | while (<IP>) {
|
|---|
| 148 | # Remove duplicate spaces
|
|---|
| 149 | my $line = $_;
|
|---|
| 150 | $line =~ s/\s+/ /g;
|
|---|
| 151 | pb_log(2,"Line: $line\n");
|
|---|
| 152 | # Check for a new interface
|
|---|
| 153 | my $tmp;
|
|---|
| 154 | my $tmp2;
|
|---|
| 155 | if ($line =~ /^[^ ]+ /) {
|
|---|
| 156 | my $dev;
|
|---|
| 157 | ($dev,$tmp) = split(/ /,$line,2);
|
|---|
| 158 | pb_log(3,"DEV: $dev,$tmp\n");
|
|---|
| 159 | $dev =~ s/\s*//;
|
|---|
| 160 | $if->{$dev}->{'if'} = $dev;
|
|---|
| 161 | $curdev = $dev;
|
|---|
| 162 | ($tmp2,$tmp2,$tmp2,$if->{$curdev}->{'mac'}) = split(/ /,$tmp);
|
|---|
| 163 | }
|
|---|
| 164 | if (/^\s*inet/) {
|
|---|
| 165 | ($tmp,$if->{$curdev}->{'ip'},$if->{$curdev}->{'nm'},$tmp) = split(/:/,$line);
|
|---|
| 166 | $if->{$curdev}->{'ip'} =~ s/ .*$// if (defined $if->{$curdev}->{'ip'});
|
|---|
| 167 | $if->{$curdev}->{'nm'} =~ s/ .*$// if (defined $if->{$curdev}->{'nm'});
|
|---|
| 168 | pb_log(3,"IP: $if->{$curdev}->{'ip'},$if->{$curdev}->{'nm'}\n");
|
|---|
| 169 | $if->{$curdev}->{'cidr'} = $if->{$curdev}->{'ip'}."/".mr_net_cvt_mask_bits($if->{$curdev}->{'nm'}) if ((defined $if->{$curdev}->{'ip'}) && (defined $if->{$curdev}->{'nm'}));
|
|---|
| 170 | }
|
|---|
| 171 | }
|
|---|
| 172 | close(IP);
|
|---|
| 173 | } else {
|
|---|
| 174 | die "Neither ip nor ifconfig are available to get IP configuration.\nPlease report upstream how to deal with your platform.\n";
|
|---|
| 175 | }
|
|---|
| 176 | }
|
|---|
| 177 |
|
|---|
| 178 | # Ideas Taken from http://nixcraft.com/shell-scripting/11398-simple-ipcalc-perl-script.html
|
|---|
| 179 | foreach my $dev (keys %if) {
|
|---|
| 180 | next if (not defined $if->{$dev}->{'cidr'});
|
|---|
| 181 | ($if->{$dev}->{'ip'},my $cidr) = split(/\//,$if->{$dev}->{'cidr'}) if (not defined $if->{$dev}->{'ip'});
|
|---|
| 182 |
|
|---|
| 183 | $if->{$dev}->{'nm'} = mr_net_cvt_bits_mask($cidr) if (not defined $if->{$dev}->{'nm'});;
|
|---|
| 184 |
|
|---|
| 185 | ($if->{$dev}->{'net'},$if->{$dev}->{'bcast'}) = mr_net_nbcast_from_in($if->{$dev}->{'ip'},$if->{$dev}->{'nm'});
|
|---|
| 186 | }
|
|---|
| 187 | pb_log(2,"exit IP: ".Dumper($if)."\n");
|
|---|
| 188 |
|
|---|
| 189 | return($if);
|
|---|
| 190 | }
|
|---|
| 191 |
|
|---|
| 192 |
|
|---|
| 193 | # Improved from http://icmp.ru/man/cisco/cookbook/ciscockbk-CHP-5-SECT-3.htm
|
|---|
| 194 | sub mr_net_cvt_bits_mask {
|
|---|
| 195 |
|
|---|
| 196 | my ($bits) = @_;
|
|---|
| 197 | my $a = 0;
|
|---|
| 198 | my $b = 0;
|
|---|
| 199 | my $c = 0;
|
|---|
| 200 | my $d = 0;
|
|---|
| 201 |
|
|---|
| 202 | if ($bits <= 8 ) {
|
|---|
| 203 | $a = mr_net_bits_to_dec($bits);
|
|---|
| 204 | } else {
|
|---|
| 205 | $a = 255;
|
|---|
| 206 | if ($bits <= 16 ) {
|
|---|
| 207 | $b = mr_net_bits_to_dec($bits-8);
|
|---|
| 208 | } else {
|
|---|
| 209 | $b=255;
|
|---|
| 210 | if ($bits <= 24 ) {
|
|---|
| 211 | $c = mr_net_bits_to_dec($bits-16);
|
|---|
| 212 | } else {
|
|---|
| 213 | $c=255;
|
|---|
| 214 | if ($bits <= 32 ) {
|
|---|
| 215 | $d = mr_net_bits_to_dec($bits-24);
|
|---|
| 216 | } else {
|
|---|
| 217 | die "invalid bit count\n";
|
|---|
| 218 | }
|
|---|
| 219 | }
|
|---|
| 220 | }
|
|---|
| 221 | }
|
|---|
| 222 | return ($a.".".$b.".".$c.".".$d);
|
|---|
| 223 | }
|
|---|
| 224 |
|
|---|
| 225 | sub mr_net_cvt_mask_bits {
|
|---|
| 226 |
|
|---|
| 227 | my ($mask) = @_;
|
|---|
| 228 | my ($a,$b,$c,$d) = split(/\./,$mask);
|
|---|
| 229 | my $bits =0;
|
|---|
| 230 |
|
|---|
| 231 | $bits= mr_net_dec_to_bits($a) + mr_net_dec_to_bits($b) + mr_net_dec_to_bits($c) + mr_net_dec_to_bits($d);
|
|---|
| 232 | return ($bits);
|
|---|
| 233 | }
|
|---|
| 234 |
|
|---|
| 235 | sub mr_net_bits_to_dec {
|
|---|
| 236 | my ($bits) = @_;
|
|---|
| 237 |
|
|---|
| 238 | if ($bits == 0 ) { return 0; }
|
|---|
| 239 | if ($bits == 1 ) { return 128; }
|
|---|
| 240 | if ($bits == 2 ) { return 192; }
|
|---|
| 241 | if ($bits == 3 ) { return 224; }
|
|---|
| 242 | if ($bits == 4 ) { return 240; }
|
|---|
| 243 | if ($bits == 5 ) { return 248; }
|
|---|
| 244 | if ($bits == 6 ) { return 252; }
|
|---|
| 245 | if ($bits == 7 ) { return 254; }
|
|---|
| 246 | if ($bits == 8 ) { return 255; }
|
|---|
| 247 | }
|
|---|
| 248 |
|
|---|
| 249 | sub mr_net_dec_to_bits {
|
|---|
| 250 | my ($dec) = @_;
|
|---|
| 251 |
|
|---|
| 252 | if ($dec == 0 ) { return 0; }
|
|---|
| 253 | if ($dec == 128 ) { return 1; }
|
|---|
| 254 | if ($dec == 192 ) { return 2; }
|
|---|
| 255 | if ($dec == 224 ) { return 3; }
|
|---|
| 256 | if ($dec == 240 ) { return 4; }
|
|---|
| 257 | if ($dec == 248 ) { return 5; }
|
|---|
| 258 | if ($dec == 252 ) { return 6; }
|
|---|
| 259 | if ($dec == 254 ) { return 7; }
|
|---|
| 260 | if ($dec == 255 ) { return 8; }
|
|---|
| 261 | }
|
|---|
| 262 |
|
|---|
| 263 | # Get Net and Broadcast from IP and Netmask
|
|---|
| 264 | sub mr_net_nbcast_from_in {
|
|---|
| 265 |
|
|---|
| 266 | my ($ip,$nm) = @_;
|
|---|
| 267 | my ($ipaddress) = unpack("N",pack( "C4",split(/\./,$ip)));
|
|---|
| 268 | my ($netmask) = unpack( "N", pack( "C4",split(/\./,$nm)));
|
|---|
| 269 |
|
|---|
| 270 | # Calculate network address by logical AND operation of addr & netmask
|
|---|
| 271 | # and convert network address to IP address format
|
|---|
| 272 | my $net = join(".",unpack("C4",pack("N",($ipaddress & $netmask))));
|
|---|
| 273 |
|
|---|
| 274 | # Calculate broadcase address by inverting the netmask
|
|---|
| 275 | # and do a logical or with network address
|
|---|
| 276 | my $bcast = join(".",unpack("C4",pack("N",($ipaddress & $netmask)+(~ $netmask))));
|
|---|
| 277 | return($net,$bcast);
|
|---|
| 278 | }
|
|---|
| 279 |
|
|---|
| 280 | #Main
|
|---|
| 281 |
|
|---|
| 282 | my $nfsip = inet_ntoa(scalar gethostbyname($ARGV[0] || 'localhost'));
|
|---|
| 283 |
|
|---|
| 284 | my $if = mr_net_find_all();
|
|---|
| 285 | pb_log(1,"IP: ".Dumper($if)."\n");
|
|---|
| 286 | my $dev = mr_net_for_ip($nfsip,$if);
|
|---|
| 287 | my $strnet = "NONET";
|
|---|
| 288 | $strnet = $dev if (defined $dev);
|
|---|
| 289 | pb_log(1,"NFS IP ($nfsip) is on net $strnet)\n");
|
|---|
| 290 | pb_log(0,"$strnet\n");
|
|---|