2010-05-07 01:58:02 +00:00
#!/usr/bin/env perl
# IBM(c) 2010 EPL license http://www.eclipse.org/legal/epl-v10.html
2010-05-07 01:59:49 +00:00
package xCAT::NetworkUtils ;
2010-05-07 01:58:02 +00:00
BEGIN
{
$ ::XCATROOT = $ ENV { 'XCATROOT' } ? $ ENV { 'XCATROOT' } : '/opt/xcat' ;
}
# if AIX - make sure we include perl 5.8.2 in INC path.
# Needed to find perl dependencies shipped in deps tarball.
if ( $^O =~ /^aix/i ) {
use lib "/usr/opt/perl5/lib/5.8.2/aix-thread-multi" ;
use lib "/usr/opt/perl5/lib/5.8.2" ;
use lib "/usr/opt/perl5/lib/site_perl/5.8.2/aix-thread-multi" ;
use lib "/usr/opt/perl5/lib/site_perl/5.8.2" ;
}
use lib "$::XCATROOT/lib/perl" ;
use POSIX qw( ceil ) ;
use File::Path ;
2011-01-17 19:17:38 +00:00
use Math::BigInt ;
2010-05-07 01:58:02 +00:00
use Socket ;
use strict ;
use warnings "all" ;
2010-08-26 15:46:11 +00:00
my $ socket6support = eval { require Socket6 } ;
2010-05-07 01:58:02 +00:00
our @ ISA = qw( Exporter ) ;
2011-01-17 19:17:38 +00:00
our @ EXPORT_OK = qw( getipaddr ) ;
2010-05-07 01:58:02 +00:00
#--------------------------------------------------------------------------------
= head1 xCAT:: NetworkUtils
= head2 Package Description
This program module file , is a set of network utilities used by xCAT commands .
= cut
#-------------------------------------------------------------
#-------------------------------------------------------------------------------
= head3 gethostnameandip
Works for both IPv4 and IPv6 .
Takes either a host name or an IP address string
and performs a lookup on that name ,
returns an array with two elements: the hostname , the ip address
if the host name or ip address can not be resolved ,
the corresponding element in the array will be undef
Arguments:
hostname or ip address
Returns: the hostname and the ip address
Globals:
Error:
none
Example:
2010-05-13 11:59:49 +00:00
my ( $ host , $ ip ) = xCAT::NetworkUtils - > gethostnameandip ( $ iporhost ) ;
2010-05-07 01:58:02 +00:00
Comments:
none
= cut
#-------------------------------------------------------------------------------
sub gethostnameandip ()
{
my ( $ class , $ iporhost ) = @ _ ;
if ( ( $ iporhost =~ /\d+\.\d+\.\d+\.\d+/ ) || ( $ iporhost =~ /:/ ) ) #ip address
{
2010-05-07 02:53:44 +00:00
return ( xCAT::NetworkUtils - > gethostname ( $ iporhost ) , $ iporhost ) ;
2010-05-07 01:58:02 +00:00
}
else #hostname
{
2010-05-07 02:53:44 +00:00
return ( $ iporhost , xCAT::NetworkUtils - > getipaddr ( $ iporhost ) ) ;
2010-05-07 01:58:02 +00:00
}
}
#-------------------------------------------------------------------------------
= head3 gethostname
Works for both IPv4 and IPv6 .
Takes an IP address string and performs a lookup on that name ,
returns the hostname of the ip address
if the ip address can not be resolved , returns undef
Arguments:
ip address
Returns: the hostname
Globals:
cache: % ::iphosthash
Error:
none
Example:
2010-05-07 02:53:44 +00:00
my $ host = xCAT::NetworkUtils - > gethostname ( $ ip ) ;
2010-05-07 01:58:02 +00:00
Comments:
none
= cut
#-------------------------------------------------------------------------------
sub gethostname ()
{
my ( $ class , $ iporhost ) = @ _ ;
2010-09-29 21:12:51 +00:00
if ( ! defined ( $ iporhost ) )
{
return undef ;
}
2010-09-25 03:08:46 +00:00
2010-09-29 21:12:51 +00:00
if ( ref ( $ iporhost ) eq 'ARRAY' )
{
2010-05-11 14:03:58 +00:00
$ iporhost = @ { $ iporhost } [ 0 ] ;
if ( ! $ iporhost )
{
return undef ;
}
2010-09-29 21:12:51 +00:00
}
2010-05-11 14:03:58 +00:00
2010-05-07 01:58:02 +00:00
if ( ( $ iporhost !~ /\d+\.\d+\.\d+\.\d+/ ) && ( $ iporhost !~ /:/ ) )
{
#why you do so? pass in a hostname and only want a hostname??
return $ iporhost ;
}
#cache, do not lookup DNS each time
if ( defined ( $ ::iphosthash { $ iporhost } ) && $ ::iphosthash { $ iporhost } )
{
return $ ::iphosthash { $ iporhost } ;
}
else
{
if ( $ socket6support ) # the getaddrinfo and getnameinfo supports both IPv4 and IPv6
{
2011-01-26 15:43:26 +00:00
my ( $ family , $ socket , $ protocol , $ ip , $ name ) = Socket6:: getaddrinfo ( $ iporhost , 0 , AF_UNSPEC , SOCK_STREAM , 6 ) ; #specifically ask for TCP capable records, maximizing chance of no more than one return per v4/v6
2010-05-07 09:56:01 +00:00
my $ host = ( Socket6:: getnameinfo ( $ ip ) ) [ 0 ] ;
2010-05-07 01:58:02 +00:00
if ( $ host eq $ iporhost ) # can not resolve
{
return undef ;
}
if ( $ host )
{
$ host =~ s/\..*// ; #short hostname
}
return $ host ;
}
else
{
2010-05-14 01:36:40 +00:00
#it is possible that no Socket6 available,
#but passing in IPv6 address, such as ::1 on loopback
if ( $ iporhost =~ /:/ )
{
return undef ;
}
2010-05-07 01:58:02 +00:00
my $ hostname = gethostbyaddr ( inet_aton ( $ iporhost ) , AF_INET ) ;
2010-12-14 13:34:10 +00:00
if ( $ hostname ) {
$ hostname =~ s/\..*// ; #short hostname
}
2010-05-07 01:58:02 +00:00
return $ hostname ;
}
}
}
#-------------------------------------------------------------------------------
= head3 getipaddr
Works for both IPv4 and IPv6 .
Takes a hostname string and performs a lookup on that name ,
returns the the ip address of the hostname
if the hostname can not be resolved , returns undef
Arguments:
hostname
2011-01-17 19:17:38 +00:00
Optional:
GetNumber = > 1 ( return the address as a BigInt instead of readable string )
2010-05-07 01:58:02 +00:00
Returns: ip address
Globals:
cache: % ::hostiphash
Error:
none
Example:
2010-05-07 02:53:44 +00:00
my $ ip = xCAT::NetworkUtils - > getipaddr ( $ hostname ) ;
2010-05-07 01:58:02 +00:00
Comments:
none
= cut
#-------------------------------------------------------------------------------
2011-01-17 19:17:38 +00:00
sub getipaddr
2010-05-07 01:58:02 +00:00
{
2011-01-17 19:17:38 +00:00
my $ iporhost = shift ;
if ( $ iporhost eq 'xCAT::NetworkUtils' ) { #was called with -> syntax
$ iporhost = shift ;
}
my % extraarguments = @ _ ;
2010-05-07 01:58:02 +00:00
2010-09-25 03:08:46 +00:00
if ( ! defined ( $ iporhost ) )
{
return undef ;
}
2010-05-11 14:03:58 +00:00
if ( ref ( $ iporhost ) eq 'ARRAY' )
{
$ iporhost = @ { $ iporhost } [ 0 ] ;
if ( ! $ iporhost )
{
return undef ;
}
}
2010-05-07 01:58:02 +00:00
2011-01-17 19:17:38 +00:00
#go ahead and do the reverse lookup on ip, useful to 'frontend' aton/pton and also to
#spit out a common abbreviation if leading zeroes or using different ipv6 presentation rules
#if ($iporhost and ($iporhost =~ /\d+\.\d+\.\d+\.\d+/) || ($iporhost =~ /:/))
#{
# #pass in an ip and only want an ip??
# return $iporhost;
#}
2010-05-07 01:58:02 +00:00
#cache, do not lookup DNS each time
2010-09-29 21:12:51 +00:00
if ( $ ::hostiphash and defined ( $ ::hostiphash { $ iporhost } ) && $ ::hostiphash { $ iporhost } )
2010-05-07 01:58:02 +00:00
{
return $ ::hostiphash { $ iporhost } ;
}
else
{
if ( $ socket6support ) # the getaddrinfo and getnameinfo supports both IPv4 and IPv6
{
2011-01-26 15:43:26 +00:00
my @ returns ;
2011-01-26 15:44:42 +00:00
my $ reqfamily = AF_UNSPEC ;
2011-01-26 15:43:26 +00:00
if ( $ extraarguments { OnlyV6 } ) {
2011-01-26 15:44:42 +00:00
$ reqfamily = AF_INET6 ;
2011-01-26 15:43:26 +00:00
} elsif ( $ extraarguments { OnlyV4 } ) {
2011-01-26 15:44:42 +00:00
$ reqfamily = AF_INET ;
2011-01-26 15:43:26 +00:00
}
2011-01-26 15:44:42 +00:00
my @ addrinfo = Socket6:: getaddrinfo ( $ iporhost , 0 , $ reqfamily , SOCK_STREAM , 6 ) ;
2011-01-26 15:43:26 +00:00
my ( $ family , $ socket , $ protocol , $ ip , $ name ) = splice ( @ addrinfo , 0 , 5 ) ;
while ( $ ip )
2010-05-07 01:58:02 +00:00
{
2011-01-17 19:17:38 +00:00
if ( $ extraarguments { GetNumber } ) { #return a BigInt for compare, e.g. for comparing ip addresses for determining if they are in a common network or range
my $ ip = ( Socket6:: getnameinfo ( $ ip , Socket6:: NI_NUMERICHOST ( ) ) ) [ 0 ] ;
my $ bignumber = Math::BigInt - > new ( 0 ) ;
foreach ( unpack ( "N*" , Socket6:: inet_pton ( $ family , $ ip ) ) ) { #if ipv4, loop will iterate once, for v6, will go 4 times
$ bignumber - > blsft ( 32 ) ;
$ bignumber - > badd ( $ _ ) ;
}
2011-01-26 15:43:26 +00:00
push ( @ returns , $ bignumber ) ;
} else {
push @ returns , ( Socket6:: getnameinfo ( $ ip , Socket6:: NI_NUMERICHOST ( ) ) ) [ 0 ] ;
}
2011-01-26 15:44:42 +00:00
if ( scalar @ addrinfo and $ extraarguments { GetAllAddresses } ) {
2011-01-26 15:43:26 +00:00
( $ family , $ socket , $ protocol , $ ip , $ name ) = splice ( @ addrinfo , 0 , 5 ) ;
2011-01-17 19:17:38 +00:00
} else {
2011-01-26 15:43:26 +00:00
$ ip = 0 ;
2011-01-17 19:17:38 +00:00
}
2010-05-07 01:58:02 +00:00
}
2011-01-26 16:07:01 +00:00
unless ( $ extraarguments { GetAllAddresses } ) {
return $ returns [ 0 ] ;
}
2011-01-26 15:43:26 +00:00
return @ returns ;
2010-05-07 01:58:02 +00:00
}
else
{
2010-05-11 14:03:58 +00:00
#return inet_ntoa(inet_aton($iporhost))
2010-05-14 01:36:40 +00:00
#TODO, what if no scoket6 support, but passing in a IPv6 hostname?
2011-07-28 17:09:24 +00:00
if ( $ iporhost =~ /:/ ) { #ipv6
die "Attempt to process IPv6 address, but system does not have requisite IPv6 perl support" ;
}
2010-09-29 21:12:51 +00:00
my $ packed_ip ;
$ iporhost and $ packed_ip = inet_aton ( $ iporhost ) ;
2010-05-11 14:03:58 +00:00
if ( ! $ packed_ip )
{
return undef ;
}
2011-01-17 19:17:38 +00:00
if ( $ extraarguments { GetNumber } ) { #only 32 bits, no for loop needed.
return Math::BigInt - > new ( unpack ( "N*" , $ packed_ip ) ) ;
}
2010-05-11 14:03:58 +00:00
return inet_ntoa ( $ packed_ip ) ;
2010-05-07 01:58:02 +00:00
}
}
}
#-------------------------------------------------------------------------------
= head3 linklocaladdr
Only for IPv6 .
Takes a mac address , calculate the IPv6 link local address
Arguments:
mac address
Returns:
ipv6 link local address . returns undef if passed in a invalid mac address
Globals:
Error:
none
Example:
2010-05-07 02:53:44 +00:00
my $ linklocaladdr = xCAT::NetworkUtils - > linklocaladdr ( $ mac ) ;
2010-05-07 01:58:02 +00:00
Comments:
none
= cut
#-------------------------------------------------------------------------------
sub linklocaladdr {
my ( $ class , $ mac ) = @ _ ;
$ mac = lc ( $ mac ) ;
my $ localprefix = "fe80" ;
my ( $ m1 , $ m2 , $ m3 , $ m6 , $ m7 , $ m8 ) ;
# mac address can be 00215EA376B0 or 00:21:5E:A3:76:B0
if ( $ mac =~ /^([0-9A-Fa-f]{2}).*?([0-9A-Fa-f]{2}).*?([0-9A-Fa-f]{2}).*?([0-9A-Fa-f]{2}).*?([0-9A-Fa-f]{2}).*?([0-9A-Fa-f]{2})$/ )
{
( $ m1 , $ m2 , $ m3 , $ m6 , $ m7 , $ m8 ) = ( $ 1 , $ 2 , $ 3 , $ 4 , $ 5 , $ 6 ) ;
}
else
{
#not a valid mac address
return undef ;
}
my ( $ m4 , $ m5 ) = ( "ff" , "fe" ) ;
2010-05-27 07:40:10 +00:00
#my $bit = (int $m1) & 2;
#if ($bit) {
# $m1 = $m1 - 2;
#} else {
# $m1 = $m1 + 2;
#}
$ m1 = hex ( $ m1 ) ;
$ m1 = $ m1 ^ 2 ;
$ m1 = sprintf ( "%x" , $ m1 ) ;
2010-05-07 01:58:02 +00:00
$ m1 = $ m1 . $ m2 ;
$ m3 = $ m3 . $ m4 ;
$ m5 = $ m5 . $ m6 ;
$ m7 = $ m7 . $ m8 ;
my $ laddr = join ":" , $ m1 , $ m3 , $ m5 , $ m7 ;
$ laddr = join "::" , $ localprefix , $ laddr ;
return $ laddr ;
}
2010-05-07 03:49:05 +00:00
2010-05-07 01:58:02 +00:00
#-------------------------------------------------------------------------------
= head3 ishostinsubnet
Works for both IPv4 and IPv6 .
Takes an ip address , the netmask and a subnet ,
chcek if the ip address is in the subnet
Arguments:
ip address , netmask , subnet
Returns:
1 - if the ip address is in the subnet
0 - if the ip address is NOT in the subnet
Globals:
Error:
none
Example:
2010-05-07 02:53:44 +00:00
if ( xCAT::NetworkUtils - > ishostinsubnet ( $ ip , $ netmask , $ subnet ) ;
2010-05-07 01:58:02 +00:00
Comments:
none
= cut
#-------------------------------------------------------------------------------
sub ishostinsubnet {
2010-05-07 03:49:05 +00:00
my ( $ class , $ ip , $ mask , $ subnet ) = @ _ ;
2011-01-17 19:54:25 +00:00
my $ numbits = 32 ;
if ( $ ip =~ /:/ ) { #ipv6
$ numbits = 128 ;
}
if ( $ mask ) {
$ mask = getipaddr ( $ mask , GetNumber = > 1 ) ;
} else { #CIDR notation supported
2011-01-17 20:24:28 +00:00
if ( $ subnet =~ /\// ) {
( $ subnet , $ mask ) = split /\// , $ subnet , 2 ;
2011-01-17 19:54:25 +00:00
$ mask = Math::BigInt - > new ( "0b" . ( "1" x $ mask ) . ( "0" x ( $ numbits - $ mask ) ) ) ;
} else {
die "ishostinsubnet must either be called with a netmask or CIDR /bits notation" ;
}
}
$ ip = getipaddr ( $ ip , GetNumber = > 1 ) ;
2011-01-17 20:24:28 +00:00
$ subnet = getipaddr ( $ subnet , GetNumber = > 1 ) ;
2011-01-17 19:54:25 +00:00
$ ip & = $ mask ;
if ( $ ip == $ subnet ) {
return 1 ;
} else {
return 0 ;
2010-05-07 01:58:02 +00:00
}
}
2010-05-27 15:13:26 +00:00
#-----------------------------------------------------------------------------
= head3 setup_ip_forwarding
Sets up ip forwarding on localhost
= cut
#-----------------------------------------------------------------------------
sub setup_ip_forwarding
{
my ( $ class , $ enable ) = @ _ ;
if ( xCAT::Utils - > isLinux ( ) ) {
my $ conf_file = "/etc/sysctl.conf" ;
`grep "net.ipv4.ip_forward" $conf_file` ;
if ( $? == 0 ) {
`sed -i "s/^net.ipv4.ip_forward = .*/net.ipv4.ip_forward = $enable/" $conf_file` ;
} else {
`echo "net.ipv4.ip_forward = $enable" >> $conf_file` ;
}
`sysctl -p $conf_file` ;
}
else
2010-06-01 14:27:13 +00:00
{
`no -o ipforwarding=$enable` ;
2010-05-27 15:13:26 +00:00
}
return 0 ;
}
2010-06-17 07:18:00 +00:00
#-------------------------------------------------------------------------------
= head3 prefixtomask
Convert the IPv6 prefix length ( e . g . 64 ) to the netmask ( e . g . ffff:ffff:ffff:ffff:0000:0000:0000:0000 ) .
Till now , the netmask format ffff:ffff:ffff:: only works for AIX NIM
Arguments:
prefix length
Returns:
netmask - netmask like ffff:ffff:ffff:ffff:0000:0000:0000:0000
0 - if the prefix length is not correct
Globals:
Error:
none
Example:
my #netmask = xCAT::NetworkUtils->prefixtomask($prefixlength);
Comments:
none
= cut
#-------------------------------------------------------------------------------
sub prefixtomask {
my ( $ class , $ prefixlength ) = @ _ ;
if ( ( $ prefixlength < 1 ) || ( $ prefixlength > 128 ) )
{
return 0 ;
}
2011-01-17 19:54:25 +00:00
my $ number = Math::BigInt - > new ( "0b" . ( "1" x $ prefixlength ) . ( "0" x ( 128 - $ prefixlength ) ) ) ;
my $ mask = $ number - > as_hex ( ) ;
$ mask =~ s/^0x// ;
$ mask =~ s/(....)/$1/g ;
2010-06-17 07:18:00 +00:00
return $ mask ;
}
2011-02-24 03:15:01 +00:00
#-------------------------------------------------------------------------------
= head3 my_ip_in_subnet
Get the facing ip for some specific network
Arguments:
net - subnet , such as 192.168 .0 .01
mask - netmask , such as 255.255 .255 .0
Returns:
facing_ip - The local ip address in the subnet ,
returns undef if no local ip address is in the subnet
Globals:
Error:
none
Example:
my $ facingip = xCAT::NetworkUtils - > my_ip_in_subnet ( $ net , $ mask ) ;
Comments:
none
= cut
#-------------------------------------------------------------------------------
sub my_ip_in_subnet
{
my ( $ class , $ net , $ mask ) = @ _ ;
if ( ! $ net || ! $ mask )
{
return undef ;
}
my $ fmask = xCAT::Utils:: formatNetmask ( $ mask , 0 , 1 ) ;
my $ localnets = xCAT::Utils - > my_nets ( ) ;
return $ localnets - > { "$net\/$fmask" } ;
}
#-------------------------------------------------------------------------------
= head3 ip_forwarding_enabled
Check if the ip_forward enabled on the system
Arguments:
Returns:
1 - The ip_forwarding is eanbled
0 - The ip_forwarding is not eanbled
Globals:
Error:
none
Example:
if ( xCAT::NetworkUtils - > ip_forwarding_enabled ( ) )
Comments:
none
= cut
#-------------------------------------------------------------------------------
sub ip_forwarding_enabled
{
my $ enabled ;
if ( xCAT::Utils - > isLinux ( ) )
{
$ enabled = `sysctl -n net.ipv4.ip_forward` ;
chomp ( $ enabled ) ;
}
else
{
$ enabled = `no -o ipforwarding` ;
chomp ( $ enabled ) ;
$ enabled =~ s/ipforwarding\s+=\s+// ;
}
return $ enabled ;
}
2010-05-07 01:58:02 +00:00
1 ;