#!/usr/bin/perl -w # # Syntax: mr-net-find # finds on which network device the the server is # $Id$ # # Copyright B. Cornec 2007-2012 # Provided under the GPL v2 # Syntax: see at end use strict 'vars'; use Socket; use Data::Dumper; use Getopt::Long qw(:config auto_abbrev no_ignore_case); use English; #use lib qw (lib); use ProjectBuilder::Version; use ProjectBuilder::Base; =pod =head1 NAME mr-net-find, finds on which network device a machine is reachable =head1 DESCRIPTION mr-net-find, finds on which network device an machine is reachable 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. =head1 SYNOPSIS mr-net-find [-v] NFS_SERVER_NAME =head1 OPTIONS =over 4 =item B<-v|--verbose> Be more verbose =back =head1 WEB SITES The main Web site of the project is available at L. Bug reports should be filled using the trac instance of the project at L. =head1 USER MAILING LIST =head1 AUTHORS The MondoRescue team L lead by Bruno Cornec L. =head1 COPYRIGHT MondoRescue.org is distributed under the GPL v2.0 license described in the file C included with the distribution. =cut # Global variables my ($mrver,$mrrev) = pb_version_init(); my $appname = "mr-net-find"; my %opts; # CLI Options # Initialize the syntax string pb_syntax_init("$appname Version $mrver-$mrrev\n"); GetOptions("help|?|h" => \$opts{'h'}, "man" => \$opts{'man'}, "verbose|v+" => \$opts{'v'}, ) || pb_syntax(-1,0); if (defined $opts{'h'}) { pb_syntax(0,1); } if (defined $opts{'man'}) { pb_syntax(0,2); } if (defined $opts{'v'}) { $pbdebug = $opts{'v'}; } pb_log_init($pbdebug, $pbLOG); # Check to which network an IP balongs sub mr_net_for_ip { my ($ip, $if) = @_; my $dev = undef; # For each device on the system foreach my $d (keys %$if) { # Skip non fully activated interfaces next if ((not defined $if->{$d}->{'nm'}) || (not defined $if->{$d}->{'net'}) || (not defined $if->{$d}->{'bcast'})); # Get the net & bcast assuming the ip is on that net with that nm my ($net,$bcast) = mr_net_nbcast_from_in ($ip,$if->{$d}->{'nm'}); # Is it true ? if (($net eq $if->{$d}->{'net'}) && ($bcast eq $if->{$d}->{'bcast'})) { # found it, return that value $dev = $d; last; } } return($dev); } sub mr_net_find_all { my %if; my $if = \%if; my $curdev = ""; # TODO: Check whther the ip command is available ! # If not consider using ifconfig for compatibility or non Linux availability my $ipcmd = pb_check_req("ip",1); if (defined $ipcmd) { open(IP,"$ipcmd addr |") || die "Unable to read IP config with $ipcmd addr\n"; while () { # Remove duplicate spaces my $line = $_; $line =~ s/\s+/ /g; pb_log(2,"Line: $line\n"); # Check for a new interface my $tmp; if ($line =~ /^[0-9]+:/) { my $dev; my $rank; ($rank,$dev,$tmp) = split(/:/,$line); pb_log(3,"$rank,$dev,$tmp\n"); $dev =~ s/\s*//; $if->{$dev}->{'if'} = $dev; $curdev = $dev; } ($tmp,$tmp,$if->{$curdev}->{'mac'},$tmp) = split(/ /,$line) if (/^\s*link/); ($tmp,$tmp,$if->{$curdev}->{'cidr'},$tmp) = split(/ /,$line) if (/^\s*inet/); } close(IP); } else { my $ipcmd = pb_check_req("ifconfig",1); if (defined $ipcmd) { open(IP,"$ipcmd -a |") || die "Unable to read IP config with $ipcmd -a\n"; while () { # Remove duplicate spaces my $line = $_; $line =~ s/\s+/ /g; pb_log(2,"Line: $line\n"); # Check for a new interface my $tmp; my $tmp2; if ($line =~ /^[^ ]+ /) { my $dev; ($dev,$tmp) = split(/ /,$line,2); pb_log(3,"DEV: $dev,$tmp\n"); $dev =~ s/\s*//; $if->{$dev}->{'if'} = $dev; $curdev = $dev; ($tmp2,$tmp2,$tmp2,$if->{$curdev}->{'mac'}) = split(/ /,$tmp); } if (/^\s*inet/) { ($tmp,$if->{$curdev}->{'ip'},$if->{$curdev}->{'nm'},$tmp) = split(/:/,$line); $if->{$curdev}->{'ip'} =~ s/ .*$// if (defined $if->{$curdev}->{'ip'}); $if->{$curdev}->{'nm'} =~ s/ .*$// if (defined $if->{$curdev}->{'nm'}); pb_log(3,"IP: $if->{$curdev}->{'ip'},$if->{$curdev}->{'nm'}\n"); $if->{$curdev}->{'cidr'} = $if->{$curdev}->{'ip'}."/".mr_net_cvt_mask_bits($if->{$curdev}->{'nm'}) if ((defined $if->{$curdev}->{'ip'}) && (defined $if->{$curdev}->{'nm'})); } } close(IP); } else { die "Neither ip nor ifconfig are available to get IP configuration.\nPlease report upstream how to deal with your platform.\n"; } } # Ideas Taken from http://nixcraft.com/shell-scripting/11398-simple-ipcalc-perl-script.html foreach my $dev (keys %if) { next if (not defined $if->{$dev}->{'cidr'}); ($if->{$dev}->{'ip'},my $cidr) = split(/\//,$if->{$dev}->{'cidr'}) if (not defined $if->{$dev}->{'ip'}); $if->{$dev}->{'nm'} = mr_net_cvt_bits_mask($cidr) if (not defined $if->{$dev}->{'nm'});; ($if->{$dev}->{'net'},$if->{$dev}->{'bcast'}) = mr_net_nbcast_from_in($if->{$dev}->{'ip'},$if->{$dev}->{'nm'}); } pb_log(2,"exit IP: ".Dumper($if)."\n"); return($if); } # Improved from http://icmp.ru/man/cisco/cookbook/ciscockbk-CHP-5-SECT-3.htm sub mr_net_cvt_bits_mask { my ($bits) = @_; my $a = 0; my $b = 0; my $c = 0; my $d = 0; if ($bits <= 8 ) { $a = mr_net_bits_to_dec($bits); } else { $a = 255; if ($bits <= 16 ) { $b = mr_net_bits_to_dec($bits-8); } else { $b=255; if ($bits <= 24 ) { $c = mr_net_bits_to_dec($bits-16); } else { $c=255; if ($bits <= 32 ) { $d = mr_net_bits_to_dec($bits-24); } else { die "invalid bit count\n"; } } } } return ($a.".".$b.".".$c.".".$d); } sub mr_net_cvt_mask_bits { my ($mask) = @_; my ($a,$b,$c,$d) = split(/\./,$mask); my $bits =0; $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); return ($bits); } sub mr_net_bits_to_dec { my ($bits) = @_; if ($bits == 0 ) { return 0; } if ($bits == 1 ) { return 128; } if ($bits == 2 ) { return 192; } if ($bits == 3 ) { return 224; } if ($bits == 4 ) { return 240; } if ($bits == 5 ) { return 248; } if ($bits == 6 ) { return 252; } if ($bits == 7 ) { return 254; } if ($bits == 8 ) { return 255; } } sub mr_net_dec_to_bits { my ($dec) = @_; if ($dec == 0 ) { return 0; } if ($dec == 128 ) { return 1; } if ($dec == 192 ) { return 2; } if ($dec == 224 ) { return 3; } if ($dec == 240 ) { return 4; } if ($dec == 248 ) { return 5; } if ($dec == 252 ) { return 6; } if ($dec == 254 ) { return 7; } if ($dec == 255 ) { return 8; } } # Get Net and Broadcast from IP and Netmask sub mr_net_nbcast_from_in { my ($ip,$nm) = @_; my ($ipaddress) = unpack("N",pack( "C4",split(/\./,$ip))); my ($netmask) = unpack( "N", pack( "C4",split(/\./,$nm))); # Calculate network address by logical AND operation of addr & netmask # and convert network address to IP address format my $net = join(".",unpack("C4",pack("N",($ipaddress & $netmask)))); # Calculate broadcase address by inverting the netmask # and do a logical or with network address my $bcast = join(".",unpack("C4",pack("N",($ipaddress & $netmask)+(~ $netmask)))); return($net,$bcast); } #Main my $nfsip = inet_ntoa(scalar gethostbyname($ARGV[0] || 'localhost')); my $if = mr_net_find_all(); pb_log(1,"IP: ".Dumper($if)."\n"); my $dev = mr_net_for_ip($nfsip,$if); my $strnet = "NONET"; $strnet = $dev if (defined $dev); pb_log(1,"NFS IP ($nfsip) is on net $strnet)\n"); pb_log(0,"$strnet\n");