2012-03-17 19:59:46 +00:00
package xCAT::SLP ;
2012-03-17 19:59:52 +00:00
use Carp ;
2012-03-17 19:59:58 +00:00
use IO::Select ;
2012-03-17 19:59:46 +00:00
use strict ;
2012-05-03 06:47:36 +00:00
use xCAT::Utils ;
2012-03-17 19:59:46 +00:00
my $ ip6support = eval {
2013-12-13 09:13:44 +00:00
require IO::Socket::INET6 ;
require Socket6 ;
1 ;
2012-03-17 19:59:46 +00:00
} ;
use Socket ;
unless ( $ ip6support ) {
2013-12-13 09:13:44 +00:00
require IO::Socket::INET ;
2012-03-17 19:59:46 +00:00
}
#TODO: somehow get at system headers to get the value, put in linux's for now
use constant IPV6_MULTICAST_IF = > 17 ;
2012-03-19 21:06:17 +00:00
use constant IP_MULTICAST_IF = > 32 ;
2012-07-27 14:17:37 +00:00
use constant REQ_INTERVAL = > 1 ;
2012-03-18 21:46:07 +00:00
my % xid_to_srvtype_map ;
my $ xid ;
2013-12-13 09:13:44 +00:00
my $ gprlist ;
2012-07-23 07:33:51 +00:00
my % searchmacs ;
my % ip4neigh ;
my % ip6neigh ;
2013-12-13 09:13:44 +00:00
my % servicehash ;
my % sendhash ;
my $ attrpy = 0 ;
my $ serrpy = 0 ;
2012-03-17 19:59:52 +00:00
sub getmulticasthash {
2013-12-13 09:13:44 +00:00
my $ hash = 0 ;
my @ nums = unpack ( "C*" , shift ) ;
foreach my $ num ( @ nums ) {
$ hash *= 33 ;
$ hash += $ num ;
$ hash & = 0xffff ;
}
$ hash & = 0x3ff ;
$ hash |= 0x1000 ;
return sprintf ( "%04x" , $ hash ) ;
2012-03-17 19:59:46 +00:00
}
2013-12-13 09:13:44 +00:00
2012-03-17 19:59:46 +00:00
sub dodiscover {
2013-12-13 09:13:44 +00:00
my % args = @ _ ;
my $ unicast = $ args { unicast } ; #should be used with -s !
my $ ipranges = $ args { range } ;
my $ rspcount = 0 ;
2012-08-11 07:05:28 +00:00
my $ rspcount1 = 0 ;
my $ sendcount = 1 ;
2013-12-13 09:13:44 +00:00
$ xid = int ( rand ( 16384 ) ) + 1 ;
my % rethash ;
unless ( $ args { 'socket' } ) {
if ( $ ip6support ) {
$ args { 'socket' } = IO::Socket::INET6 - > new ( Proto = > 'udp' ) ;
} else {
$ args { 'socket' } = IO::Socket::INET - > new ( Proto = > 'udp' ) ;
}
#make an extra effort to request biggest receive buffer OS is willing to give us
if ( - r "/proc/sys/net/core/rmem_max" ) { # we can detect the maximum allowed socket, read it.
my $ sysctl ;
open ( $ sysctl , "<" , "/proc/sys/net/core/rmem_max" ) ;
my $ maxrcvbuf = <$sysctl> ;
my $ rcvbuf = $ args { 'socket' } - > sockopt ( SO_RCVBUF ) ;
if ( $ maxrcvbuf > $ rcvbuf ) {
$ args { 'socket' } - > sockopt ( SO_RCVBUF , $ maxrcvbuf / 2 ) ;
}
}
} #end of unless socket
unless ( $ args { SrvTypes } ) { croak "SrvTypes argument is required for xCAT::SLP::Dodiscover" ; }
unless ( xCAT::Utils - > isAIX ( ) ) { # AIX bug, can't set socket with SO_BROADCAST, otherwise multicast can't work.
setsockopt ( $ args { 'socket' } , SOL_SOCKET , SO_BROADCAST , 1 ) ; #allow for broadcasts to be sent, we know what we are doing
}
my @ srvtypes ;
if ( ref $ args { SrvTypes } ) {
@ srvtypes = @ { $ args { SrvTypes } } ;
} else {
@ srvtypes = split /,/ , $ args { SrvTypes } ;
}
my $ interfaces = get_interfaces ( % args ) ;
2012-04-26 15:32:21 +00:00
if ( $ args { Ip } ) {
2012-05-17 01:46:08 +00:00
foreach my $ nic ( keys %$ interfaces ) {
if ( $ { $ { $ interfaces - > { $ nic } } { ipv4addrs } } [ 0 ] =~ /(\d+\.\d+\.\d+\.\d+)/ ) {
unless ( $ args { Ip } =~ $ 1 ) {
2012-05-10 02:49:07 +00:00
delete $ interfaces - > { $ nic } ;
}
2012-04-26 15:32:21 +00:00
}
2012-05-10 02:49:07 +00:00
}
2012-04-26 15:32:21 +00:00
}
2012-08-11 07:05:28 +00:00
my @ printip ;
foreach my $ iface ( keys % { $ interfaces } ) {
foreach my $ sip ( @ { $ interfaces - > { $ iface } - > { ipv4addrs } } ) {
my $ ip = $ sip ;
$ ip =~ s/\/(.*)// ;
push @ printip , $ ip ;
}
}
2013-12-13 09:13:44 +00:00
my $ printinfo = join ( "," , @ printip ) ;
if ( $ unicast ) {
2014-01-08 15:32:40 +00:00
if ( xCAT::Utils - > isAIX ( ) ) {
send_message ( $ args { reqcallback } , 1 , "lsslp unicast is not supported on AIX" ) ;
exit 1 ;
}
if ( ! - f "/usr/bin/nmap" ) {
send_message ( $ args { reqcallback } , 1 , "nmap does not exist, lsslp unicast is not possible" ) ;
exit 1 ;
}
2013-12-13 09:13:44 +00:00
my @ servernodes ;
my @ iprange = split /,/ , $ ipranges ;
foreach my $ range ( @ iprange ) {
2014-01-28 21:32:46 +00:00
send_message ( $ args { reqcallback } , 0 , "Processing range $range..." ) ;
if ( $ range =~ /\/(\d+)/ ) {
2014-01-28 21:49:26 +00:00
if ( $ 1 < 16 ) {
2014-01-28 21:32:46 +00:00
send_message ( $ args { reqcallback } , 0 , "The rarge is too large and may be time consuming. Broadcast is recommended." ) ;
}
}
2014-10-22 22:36:59 +00:00
#no need to check site.nmapoptions because it specifilly
# uses T5 for certain performance requirement.
2013-12-13 09:13:44 +00:00
`/usr/bin/nmap $range -sn -PE -n --send-ip -T5 ` ;
my $ nmapres = `/usr/bin/nmap $range -PE -p 427 -n --send-ip -T5 ` ;
foreach my $ line ( split ( /\n\n/ , $ nmapres ) ) {
my $ server ;
foreach my $ sline ( split ( /\n/ , $ line ) ) {
if ( $ sline =~ /Nmap scan report for (\d+\.\d+\.\d+\.\d+)/ ) {
$ server = $ 1 ;
}
if ( $ sline =~ /427/ and ( $ sline =~ /open/ or $ sline =~ /filtered/ ) ) {
push @ servernodes , $ server ;
}
} # end of foreach line
} # end of foreach line
} # end of foreach pi-range
unless ( @ servernodes ) {
send_message ( $ args { reqcallback } , 0 , "Nmap returns nothing" ) ;
return undef ;
}
2013-12-22 13:11:50 +00:00
my $ number = scalar ( @ servernodes ) ;
send_message ( $ args { reqcallback } , 0 , "Begin to do unicast to $number nodes..." ) ;
2013-12-13 09:13:44 +00:00
my % rechash ;
pipe CREAD , PWRITE ;
my $ pid = xCAT::Utils - > xfork ( ) ;
if ( ! defined ( $ pid ) ) {
send_message ( $ args { reqcallback } , 1 , "Fork error: $!" ) ;
return undef ;
} elsif ( $ pid == 0 ) {
close PWRITE ;
foreach my $ srvtype ( @ srvtypes ) {
my $ packet = generate_attribute_request ( % args , SrvType = > $ srvtype ) ;
foreach my $ destserver ( @ servernodes ) {
my $ destip = inet_aton ( $ destserver ) ;
my $ destaddr = sockaddr_in ( 427 , $ destip ) ;
my $ res = $ args { 'socket' } - > send ( $ packet , 0 , $ destaddr ) ;
} # end of foreach destserver
} # end of foreach services
while ( <CREAD> ) {
chomp ;
my $ destserver = $ _ ;
if ( $ destserver =~ /NowYouNeedToDie/ ) {
close CREAD ;
exit 0 ;
}
foreach my $ srvtype ( @ srvtypes ) {
my $ packet = generate_attribute_request ( % args , SrvType = > $ srvtype ) ;
my $ destip = inet_aton ( $ destserver ) ;
my $ destaddr = sockaddr_in ( 427 , $ destip ) ;
for ( my $ j = 0 ; $ j < 1 ; $ j + + ) {
my $ res = $ args { 'socket' } - > send ( $ packet , 0 , $ destaddr ) ;
} # end of foreach j++
} # end of foreach services
} # end of while (cread)
} else {
close CREAD ;
$ rspcount = 0 ;
2013-12-22 13:11:50 +00:00
my $ waittime = ( $ args { Time } > 0 ) ? $ args { Time } : 300 ;
2013-12-13 09:13:44 +00:00
my $ deadline = time ( ) + $ waittime ;
my $ waitforsocket = IO::Select - > new ( ) ;
$ waitforsocket - > add ( $ args { 'socket' } ) ;
my $ rectime = time ( ) + 5 ;
my $ recvzero = 0 ;
while ( $ deadline > time ( ) ) {
$ rspcount1 = 0 ;
while ( $ rectime > time ( ) ) {
while ( $ waitforsocket - > can_read ( 0 ) ) {
my $ slppacket ;
my $ peer = $ args { 'socket' } - > recv ( $ slppacket , 3000 , 0 ) ;
$ rechash { $ peer } = $ slppacket ;
} #end of can_read
} # end of receiving
# now begin to parse the packets
for my $ tp ( keys % rechash ) {
my @ restserver ;
my $ pkg = $ tp ;
my $ slpkg = $ rechash { $ tp } ;
my ( $ port , $ flow , $ ip6n , $ ip4n , $ scope ) ;
my $ peername ;
if ( $ ip6support ) {
( $ port , $ flow , $ ip6n , $ scope ) = Socket6:: unpack_sockaddr_in6_all ( $ pkg ) ;
$ peername = Socket6:: inet_ntop ( Socket6:: AF_INET6 ( ) , $ ip6n ) ;
} else {
( $ port , $ ip4n ) = sockaddr_in ( $ pkg ) ;
$ peername = inet_ntoa ( $ ip4n ) ;
}
2012-08-11 07:05:28 +00:00
if ( $ peername =~ /\./ ) { #ipv4
$ peername =~ s/::ffff:// ;
}
2013-12-13 09:13:44 +00:00
if ( $ rethash { $ peername } ) {
next ; #got a dupe, discard
}
my $ result = process_slp_packet ( packet = > $ slpkg , sockaddr = > $ pkg , 'socket' = > $ args { 'socket' } , peername = > $ peername , callback = > $ args { reqcallback } ) ;
if ( $ result ) {
$ rspcount + + ;
$ rspcount1 + + ;
$ result - > { peername } = $ peername ;
$ result - > { scopeid } = $ scope ;
$ result - > { sockaddr } = $ pkg ;
my $ hashkey ;
if ( $ peername =~ /fe80/ ) {
$ peername . = '%' . $ scope ;
}
$ rethash { $ peername } = $ result ;
if ( $ args { Callback } ) {
$ args { Callback } - > ( $ result ) ;
}
foreach my $ mynode ( @ servernodes ) {
unless ( $ mynode =~ $ peername ) {
push @ restserver , $ mynode ;
} #end of mynode=~peername
} # end of foreach
@ servernodes = @ restserver ;
} # end of if result
} # end of foreach processing
foreach my $ node ( @ servernodes ) {
syswrite PWRITE , "$node\n" ;
} # end of foreach servernodes
$ recvzero + + unless ( $ rspcount1 ) ;
last if ( $ recvzero > 2 ) ;
} # end of while(deadline)
syswrite PWRITE , "NowYouNeedToDie\n" ;
close PWRITE ;
2013-12-22 13:11:50 +00:00
if ( @ servernodes ) {
my $ miss = join ( "," , @ servernodes ) ;
2014-01-28 21:33:57 +00:00
send_message ( $ args { reqcallback } , 0 , "Warning: can't get attributes from these nodes' replies: $miss. Please re-send unicast to these nodes." ) if ( $ args { reqcallback } ) ;
2013-12-22 13:11:50 +00:00
}
2013-12-13 09:13:44 +00:00
} # end of parent process
} else {
send_message ( $ args { reqcallback } , 0 , "Sending SLP request on interfaces: $printinfo ..." ) if ( $ args { reqcallback } and ! $ args { nomsg } ) ;
foreach my $ srvtype ( @ srvtypes ) {
send_service_request_single ( % args , ifacemap = > $ interfaces , SrvType = > $ srvtype ) ;
}
unless ( $ args { NoWait } ) { #in nowait, caller owns the responsibility..
#by default, report all respondants within 3 seconds:
my $ waitforsocket = IO::Select - > new ( ) ;
$ waitforsocket - > add ( $ args { 'socket' } ) ;
my $ retrytime = ( $ args { Retry } > 0 ) ? $ args { Retry } + 1 : 3 ;
my $ retryinterval = ( $ args { Retry } > 0 ) ? $ args { Retry } : REQ_INTERVAL ;
my $ waittime = ( $ args { Time } > 0 ) ? $ args { Time } : 20 ;
my @ peerarray ;
my @ pkgarray ;
my $ startinterval = time ( ) ;
my $ interval ;
my $ deadline = time ( ) + $ waittime ;
my ( $ port , $ flow , $ ip6n , $ ip4n , $ scope ) ;
my $ slppacket ;
my $ peername ;
while ( $ deadline > time ( ) ) {
########################################
# receive untill there is none
########################################
while ( $ waitforsocket - > can_read ( 0 ) ) {
my $ peer = $ args { 'socket' } - > recv ( $ slppacket , 3000 , 0 ) ;
push @ peerarray , $ peer ;
push @ pkgarray , $ slppacket ;
}
#######################################
# process the packets
#######################################
for ( my $ j = 0 ; $ j < scalar ( @ peerarray ) ; $ j + + ) {
my $ pkg = $ peerarray [ $ j ] ;
my $ slpkg = $ pkgarray [ $ j ] ;
if ( $ ip6support ) {
( $ port , $ flow , $ ip6n , $ scope ) = Socket6:: unpack_sockaddr_in6_all ( $ pkg ) ;
$ peername = Socket6:: inet_ntop ( Socket6:: AF_INET6 ( ) , $ ip6n ) ;
2012-08-11 07:05:28 +00:00
} else {
2013-12-13 09:13:44 +00:00
( $ port , $ ip4n ) = sockaddr_in ( $ pkg ) ;
$ peername = inet_ntoa ( $ ip4n ) ;
2012-08-11 07:05:28 +00:00
}
2013-12-13 09:13:44 +00:00
if ( $ rethash { $ peername } ) {
next ; #got a dupe, discard
2012-08-11 07:05:28 +00:00
}
2014-07-23 14:18:11 +00:00
my $ result = process_slp_packet ( packet = > $ slpkg , sockaddr = > $ pkg , 'socket' = > $ args { 'socket' } , peername = > $ peername , callback = > $ args { reqcallback } ) ;
2013-12-13 09:13:44 +00:00
if ( $ result ) {
if ( $ peername =~ /\./ ) { #ipv4
$ peername =~ s/::ffff:// ;
}
$ result - > { peername } = $ peername ;
if ( $ gprlist ) {
$ gprlist . = ',' . $ peername if ( length ( $ gprlist ) < 1250 ) ;
} else {
$ gprlist = $ peername ;
}
$ result - > { scopeid } = $ scope ;
$ result - > { sockaddr } = $ pkg ;
my $ hashkey ;
if ( $ peername =~ /fe80/ ) {
$ peername . = '%' . $ scope ;
}
$ rspcount + + ;
$ rspcount1 + + ;
$ rethash { $ peername } = $ result ;
if ( $ args { Callback } ) {
$ args { Callback } - > ( $ result ) ;
}
2012-08-11 07:05:28 +00:00
}
}
2013-12-13 09:13:44 +00:00
#############################
# check if need to return
#############################
@ peerarray = ( ) ;
@ pkgarray = ( ) ;
$ interval = time ( ) - $ startinterval ;
if ( $ args { Time } and $ args { Count } ) {
if ( $ rspcount >= $ args { Count } or $ interval >= $ args { Time } ) {
send_message ( $ args { reqcallback } , 0 , "Received $rspcount1 responses." ) if ( $ args { reqcallback } and ! $ args { nomsg } ) ;
last ;
}
}
if ( $ sendcount > $ retrytime and $ rspcount1 == 0 ) {
send_message ( $ args { reqcallback } , 0 , "Received $rspcount1 responses." ) if ( $ args { reqcallback } and ! $ args { nomsg } ) ;
2012-08-11 07:05:28 +00:00
last ;
}
2013-12-13 09:13:44 +00:00
#########################
# send request again
#########################
if ( $ interval > $ retryinterval ) { #* (2**$sendcount))) { #double time
$ sendcount + + ;
$ startinterval = time ( ) ;
send_message ( $ args { reqcallback } , 0 , "Received $rspcount1 responses." ) if ( $ args { reqcallback } and ! $ args { nomsg } ) ;
send_message ( $ args { reqcallback } , 0 , "Sending SLP request on interfaces: $printinfo ..." ) if ( $ args { reqcallback } and ! $ args { nomsg } ) ;
foreach my $ srvtype ( @ srvtypes ) {
send_service_request_single ( % args , ifacemap = > $ interfaces , SrvType = > $ srvtype ) ;
}
$ rspcount1 = 0 ;
}
2012-08-11 07:05:28 +00:00
}
2013-12-13 09:13:44 +00:00
} #end nowait
} #end of if( unicast )
2012-07-27 14:17:37 +00:00
foreach my $ entry ( keys % rethash ) {
2013-12-13 09:13:44 +00:00
handle_new_slp_entity ( $ rethash { $ entry } ) ;
2012-08-11 07:05:28 +00:00
}
if ( xCAT::Utils - > isAIX ( ) ) {
foreach my $ iface ( keys % { $ interfaces } ) {
foreach my $ sip ( @ { $ interfaces - > { $ iface } - > { ipv4addrs } } ) {
my $ ip = $ sip ;
$ ip =~ s/\/(.*)// ;
my $ maskbits = $ 1 ;
my $ runcmd = `route delete 239.255.255.253 $ip` ;
}
2013-12-13 09:13:44 +00:00
}
2012-08-11 07:05:28 +00:00
}
return ( \ % searchmacs , $ sendcount , $ rspcount ) ;
2012-03-17 19:59:52 +00:00
}
2012-03-17 19:59:58 +00:00
sub process_slp_packet {
2013-12-13 09:13:44 +00:00
my % args = @ _ ;
my $ sockaddy = $ args { sockaddr } ;
my $ socket = $ args { 'socket' } ;
my $ packet = $ args { packet } ;
my $ parsedpacket = removeslpheader ( $ packet ) ;
if ( $ parsedpacket - > { FunctionId } == 2 ) { #Service Reply
parse_service_reply ( $ parsedpacket - > { payload } , $ parsedpacket ) ;
unless ( ref $ parsedpacket - > { service_urls } and scalar @ { $ parsedpacket - > { service_urls } } ) { return undef ; }
2014-07-23 14:18:11 +00:00
if ( $ parsedpacket - > { attributes } && get_mac_for_addr ( $ args { peername } ) ) {
#service reply had ext. Stop here if has gotten attributes and got mac.
#continue the unicast request for service attributes if cannot find mac for peernode
return $ parsedpacket ; #don't bother sending attrrequest, already got it in first packet
2013-12-13 09:13:44 +00:00
}
my $ srvtype = $ xid_to_srvtype_map { $ parsedpacket - > { Xid } } ;
my $ packet = generate_attribute_request ( % args , SrvType = > $ srvtype ) ;
$ sendhash { $ args { peername } } - > { package } = $ packet ;
$ sendhash { $ args { peername } } - > { sockaddy } = $ sockaddy ;
$ serrpy + + ;
$ socket - > send ( $ packet , 0 , $ sockaddy ) ;
return undef ;
} elsif ( $ parsedpacket - > { FunctionId } == 7 ) { #attribute reply
$ attrpy + + ;
$ parsedpacket - > { SrvType } = $ xid_to_srvtype_map { $ parsedpacket - > { Xid } } ;
$ parsedpacket - > { attributes } = parse_attribute_reply ( $ parsedpacket - > { payload } ) ;
2013-12-22 13:11:50 +00:00
my $ attributes = $ parsedpacket - > { attributes } ;
my $ type = $ { $ attributes - > { 'type' } } [ 0 ] ;
return undef unless ( $ type ) ;
2013-12-13 09:13:44 +00:00
#delete $parsedpacket->{payload};
return $ parsedpacket ;
} else {
return undef ;
}
2012-03-17 19:59:58 +00:00
}
2012-03-17 20:00:11 +00:00
sub parse_attribute_reply {
2013-12-13 09:13:44 +00:00
my $ contents = shift ;
my @ payload = unpack ( "C*" , $ contents ) ;
if ( $ payload [ 0 ] != 0 or $ payload [ 1 ] != 0 ) {
return { } ;
}
splice ( @ payload , 0 , 2 ) ;
return parse_attribute_list ( \ @ payload ) ;
2012-03-20 14:21:48 +00:00
}
sub parse_attribute_list {
2013-12-13 09:13:44 +00:00
my $ payload = shift ;
my $ attrlength = ( $ payload - > [ 0 ] <<8)+$payload-> [ 1 ] ;
splice ( @$ payload , 0 , 2 ) ;
my @ attributes = splice ( @$ payload , 0 , $ attrlength ) ;
my $ attrstring = pack ( "C*" , @ attributes ) ;
my % attribs ;
#now we have a string...
my $ lastattrstring ;
while ( $ attrstring ) {
if ( $ lastattrstring eq $ attrstring ) { #infinite loop
$ attribs { unparsed_attribdata } = $ attrstring ;
last ;
}
$ lastattrstring = $ attrstring ;
if ( $ attrstring =~ /^\(/ ) {
$ attrstring =~ s/([^)]*\)),?// ;
my $ attrib = $ 1 ;
$ attrib =~ s/^\(// ;
$ attrib =~ s/\),?$// ;
$ attrib =~ s/=(.*)$// ;
$ attribs { $ attrib } = [] ;
2012-04-26 08:56:24 +00:00
my $ valstring = $ 1 ;
if ( defined $ valstring ) {
2013-12-13 09:13:44 +00:00
foreach ( split /,/ , $ valstring ) {
push @ { $ attribs { $ attrib } } , $ _ ;
}
}
} else {
$ attrstring =~ s/([^,]*),?// ;
$ attribs { $ 1 } = [] ;
}
}
return \ % attribs ;
2012-03-17 20:00:11 +00:00
}
2012-03-18 21:46:07 +00:00
sub generate_attribute_request {
2013-12-13 09:13:44 +00:00
my % args = @ _ ;
my $ srvtype = $ args { SrvType } ;
my $ scope = "DEFAULT" ;
if ( $ args { Scopes } ) { $ scope = $ args { Scopes } ; }
my $ packet = pack ( "C*" , 0 , 0 ) ; #no prlist
my $ service = $ srvtype ;
$ service =~ s!://.*!! ;
my $ length = length ( $ service ) ;
$ packet . = pack ( "C*" , ( $ length >> 8 ) , ( $ length & 0xff ) ) ;
$ length = length ( $ scope ) ;
$ packet . = $ service . pack ( "C*" , ( $ length >> 8 ) , ( $ length & 0xff ) ) . $ scope ;
$ packet . = pack ( "C*" , 0 , 0 , 0 , 0 ) ;
my $ header = genslpheader ( $ packet , FunctionId = > 6 ) ;
$ xid_to_srvtype_map { $ xid + + } = $ srvtype ;
return $ header . $ packet ;
#$args{'socket'}->send($header.$packet,0,$args{sockaddry});
2012-03-17 20:00:05 +00:00
}
2013-12-13 09:13:44 +00:00
2012-03-17 19:59:58 +00:00
2012-03-19 21:06:17 +00:00
sub parse_service_reply {
2013-12-13 09:13:44 +00:00
my $ packet = shift ;
my $ parsedpacket = shift ;
my @ reply = unpack ( "C*" , $ packet ) ;
if ( $ reply [ 0 ] != 0 or $ reply [ 1 ] != 0 ) {
return ( ) ;
}
if ( $ parsedpacket - > { extoffset } ) {
my @ extdata = splice ( @ reply , $ parsedpacket - > { extoffset } - $ parsedpacket - > { currentoffset } ) ;
$ parsedpacket - > { currentoffset } = $ parsedpacket - > { extoffset } ;
parse_extension ( \ @ extdata , $ parsedpacket ) ;
}
my $ numurls = ( $ reply [ 2 ] << 8 ) + $ reply [ 3 ] ;
splice ( @ reply , 0 , 4 ) ;
while ( $ numurls - - ) {
push @ { $ parsedpacket - > { service_urls } } , extract_next_url ( \ @ reply ) ;
}
return ;
2012-03-19 21:06:17 +00:00
}
2012-03-17 19:59:58 +00:00
2012-03-20 14:21:48 +00:00
sub parse_extension {
2013-12-13 09:13:44 +00:00
my $ extdata = shift ;
my $ parsedpacket = shift ;
my $ extid = ( $ extdata - > [ 0 ] <<8)+$extdata-> [ 1 ] ;
my $ nextext = ( ( $ extdata - > [ 2 ] ) <<16)+(($extdata-> [ 3 ] ) <<8)+$extdata-> [ 4 ] ;
if ( $ nextext ) {
my @ nextext = splice ( @$ extdata , $ nextext - $ parsedpacket - > { currentoffset } ) ;
$ parsedpacket - > { currentoffset } = $ nextext ;
parse_extension ( \ @ nextext , $ parsedpacket ) ;
}
splice ( @$ extdata , 0 , 5 ) ;
if ( $ extid == 2 ) {
#this is defined in RFC 3059, attribute list extension
#employed by AMM for one...
my $ urllen = ( ( shift @$ extdata ) << 8 ) + ( shift @$ extdata ) ;
splice @$ extdata , 0 , $ urllen ; #throw this out for now..
$ parsedpacket - > { attributes } = parse_attribute_list ( $ extdata ) ;
}
2012-03-20 14:21:48 +00:00
}
2013-12-13 09:13:44 +00:00
2012-03-20 14:21:48 +00:00
2012-03-19 21:06:17 +00:00
sub extract_next_url { #section 4.3 url entries
2013-12-13 09:13:44 +00:00
my $ payload = shift ;
splice ( @$ payload , 0 , 3 ) ; # discard reserved and lifetime which we will not bother using
my $ urllength = ( ( shift @$ payload ) << 8 ) + ( shift @$ payload ) ;
my @ url = splice ( @$ payload , 0 , $ urllength ) ;
my $ authblocks = shift @$ payload ;
unless ( $ authblocks == 0 ) {
$ payload = [] ; #TODO: skip/use auth blocks if needed to get at more URLs
}
return pack ( "C*" , @ url ) ;
2012-03-19 21:06:17 +00:00
}
2013-12-13 09:13:44 +00:00
2012-03-19 21:06:17 +00:00
sub send_service_request_single {
2013-12-13 09:13:44 +00:00
my % args = @ _ ;
my $ packet = generate_service_request ( % args ) ;
my $ interfaces = $ args { ifacemap } ; #get_interfaces(%args);
my $ socket = $ args { 'socket' } ;
my @ v6addrs ;
my $ v6addr ;
if ( $ ip6support ) {
my $ hash = getmulticasthash ( $ args { SrvType } ) ;
my $ target = "ff02::1:$hash" ;
my ( $ fam , $ type , $ proto , $ name ) ;
( $ fam , $ type , $ proto , $ v6addr , $ name ) =
Socket6:: getaddrinfo ( $ target , "svrloc" , Socket6:: AF_INET6 ( ) , SOCK_DGRAM , 0 ) ;
push @ v6addrs , $ v6addr ;
( $ fam , $ type , $ proto , $ v6addr , $ name ) =
Socket6:: getaddrinfo ( "ff01::1:$hash" , "svrloc" , Socket6:: AF_INET6 ( ) , SOCK_DGRAM , 0 ) ;
push @ v6addrs , $ v6addr ;
}
my $ ipv4mcastaddr = inet_aton ( "239.255.255.253" ) ; #per rfc 2608
my $ ipv4sockaddr = sockaddr_in ( 427 , $ ipv4mcastaddr ) ;
foreach my $ iface ( keys % { $ interfaces } ) {
if ( $ ip6support ) {
setsockopt ( $ socket , Socket6:: IPPROTO_IPV6 ( ) , IPV6_MULTICAST_IF , pack ( "I" , $ interfaces - > { $ iface } - > { scopeidx } ) ) ;
foreach $ v6addr ( @ v6addrs ) {
$ socket - > send ( $ packet , 0 , $ v6addr ) ;
}
}
foreach my $ sip ( @ { $ interfaces - > { $ iface } - > { ipv4addrs } } ) {
my $ ip = $ sip ;
$ ip =~ s/\/(.*)// ;
my $ maskbits = $ 1 ;
2012-08-11 07:05:28 +00:00
if ( xCAT::Utils - > isAIX ( ) ) {
my $ runcmd = `route add 239.255.255.253 $ip` ;
}
2013-12-13 09:13:44 +00:00
my $ ipn = inet_aton ( $ ip ) ; #we are ipv4 only, this is ok
my $ ipnum = unpack ( "N" , $ ipn ) ;
$ ipnum = $ ipnum | ( 2 ** ( 32 - $ maskbits ) ) - 1 ;
my $ bcastn = pack ( "N" , $ ipnum ) ;
my $ bcastaddr = sockaddr_in ( 427 , $ bcastn ) ;
2013-12-22 13:11:50 +00:00
setsockopt ( $ socket , 0 , IP_MULTICAST_IF , $ ipn ) ;
$ socket - > send ( $ packet , 0 , $ ipv4sockaddr ) ;
$ socket - > send ( $ packet , 0 , $ bcastaddr ) ;
2013-12-13 09:13:44 +00:00
}
}
2012-03-17 19:59:46 +00:00
}
sub get_interfaces {
2013-12-13 09:13:44 +00:00
#TODO: AIX tolerance, no subprocess, include/exclude interface(s)
my % ifacemap ;
my $ payingattention = 0 ;
my $ interface ;
my $ keepcurrentiface ;
2012-05-03 06:47:36 +00:00
# AIX part
if ( xCAT::Utils - > isAIX ( ) ) {
$ ip6support = 0 ;
my $ result = `ifconfig -a` ;
my @ nics = $ result =~ /(\w+\d+)\: flags=/g ;
my @ adapter = split /\w+\d+:\s+flags=/ , $ result ;
for ( my $ i = 0 ; $ i < scalar ( @ adapter ) ; $ i + + ) {
$ _ = $ adapter [ $ i + 1 ] ;
if ( ! ( $ _ =~ /LOOPBACK/ ) and
$ _ =~ /UP(,|>)/ and
$ _ =~ /BROADCAST/ ) {
my @ ip = split /\n/ ;
2012-05-05 02:06:19 +00:00
for my $ entry ( @ ip ) {
if ( $ entry =~ /broadcast\s+/ and $ entry =~ /^\s*inet\s+(\d+\.\d+\.\d+\.\d+)/ ) {
my $ tmpip = $ 1 ;
if ( $ entry =~ /netmask\s+(0x\w+)/ ) {
my $ mask = hex ( $ 1 ) ;
my $ co = 31 ;
my $ count = 0 ;
while ( $ co + 1 ) {
if ( ( ( $ mask & ( 2 ** $ co ) ) >> $ co ) == 1 ) {
$ count + + ;
}
$ co - - ;
}
$ tmpip = $ tmpip . '/' . $ count ;
2013-12-13 09:13:44 +00:00
}
2012-05-05 02:06:19 +00:00
push @ { $ ifacemap { $ nics [ $ i ] } - > { ipv4addrs } } , $ tmpip ;
2012-05-03 06:47:36 +00:00
if ( $ nics [ $ i ] =~ /\w+(\d+)/ ) {
$ ifacemap { $ nics [ $ i ] } - > { scopeidx } = $ 1 + 2 ;
}
}
}
}
}
} else {
2012-05-05 02:06:19 +00:00
my @ ipoutput = `ip addr` ;
2013-12-13 09:13:44 +00:00
foreach my $ line ( @ ipoutput ) {
if ( $ line =~ /^\d/ ) { # new interface, new context..
if ( $ interface and not $ keepcurrentiface ) {
#don't bother reporting unusable nics
delete $ ifacemap { $ interface } ;
}
$ keepcurrentiface = 0 ;
unless ( $ line =~ /MULTICAST/ ) { #don't care if it isn't multicast capable
$ payingattention = 0 ;
next ;
}
$ payingattention = 1 ;
$ line =~ /^([^:]*): ([^:]*):/ ;
$ interface = $ 2 ;
$ ifacemap { $ interface } - > { scopeidx } = $ 1 ;
}
unless ( $ payingattention ) { next ; } #don't think about lines unless in context of paying attention.
if ( $ line =~ /inet/ ) {
$ keepcurrentiface = 1 ;
}
if ( $ line =~ /\s+inet\s+(\S+)\s/ ) { #got an ipv4 address, store it
push @ { $ ifacemap { $ interface } - > { ipv4addrs } } , $ 1 ;
}
}
2012-05-03 06:47:36 +00:00
}
2013-12-13 09:13:44 +00:00
return \ % ifacemap ;
2012-03-17 19:59:46 +00:00
}
2013-12-13 09:13:44 +00:00
# discovery is "service request", rfc 2608
2012-03-17 19:59:46 +00:00
# 0 1 2 3
# 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# | Service Location header (function = SrvRqst = 1) |
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# | length of <PRList> | <PRList> String \
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# | length of <service-type> | <service-type> String \
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# | length of <scope-list> | <scope-list> String \
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# | length of predicate string | Service Request <predicate> \
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# | length of <SLP SPI> string | <SLP SPI> String \
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2012-03-19 21:06:17 +00:00
sub generate_service_request {
2013-12-13 09:13:44 +00:00
my % args = @ _ ;
my $ srvtype = $ args { SrvType } ;
my $ scope = "DEFAULT" ;
if ( $ args { Scopes } ) { $ scope = $ args { Scopes } ; }
my $ prlist = $ gprlist ;
2012-05-07 09:40:57 +00:00
my $ prlength = length ( $ prlist ) ;
2013-12-13 09:13:44 +00:00
my $ packet = pack ( "C*" , ( $ prlength >> 8 ) , ( $ prlength & 0xff ) ) ;
2012-05-07 09:40:57 +00:00
$ packet . = $ prlist ;
my $ length = length ( $ srvtype ) ;
2013-12-13 09:13:44 +00:00
$ packet . = pack ( "C*" , ( $ length >> 8 ) , ( $ length & 0xff ) ) ;
$ packet . = $ srvtype ;
$ length = length ( $ scope ) ;
$ packet . = pack ( "C*" , ( $ length >> 8 ) , ( $ length & 0xff ) ) ;
$ packet . = $ scope ;
#no ldap predicates, and no auth, so zeroes..
$ packet . = pack ( "C*" , 0 , 0 , 0 , 0 ) ;
$ packet . = pack ( "C*" , 0 , 2 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ) ;
my $ extoffset = length ( $ srvtype ) + length ( $ scope ) + length ( $ prlist ) + 10 ;
my $ header = genslpheader ( $ packet , Multicast = > 1 , FunctionId = > 1 , ExtOffset = > $ extoffset ) ;
$ xid_to_srvtype_map { $ xid + + } = $ srvtype ;
return $ packet = $ header . $ packet ;
2012-03-19 21:06:17 +00:00
}
2012-03-17 19:59:46 +00:00
# SLP header from RFC 2608
# 0 1 2 3
# 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# | Version | Function-ID | Length |
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# | Length, contd.|O|F|R| reserved |Next Ext Offset|
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# | Next Extension Offset, contd.| XID |
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# | Language Tag Length | Language Tag \
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2012-03-17 19:59:58 +00:00
sub removeslpheader {
2013-12-13 09:13:44 +00:00
my $ packet = shift ;
my % parsedheader ;
my @ payload = unpack ( "C*" , $ packet ) ;
$ parsedheader { Version } = shift @ payload ;
$ parsedheader { FunctionId } = shift @ payload ;
splice ( @ payload , 0 , 3 ) ; #remove length
splice ( @ payload , 0 , 2 ) ; #TODO: parse flags
my $ nextoffset = ( ( shift @ payload ) << 16 ) + ( ( shift @ payload ) << 8 ) + ( shift @ payload ) ;
$ parsedheader { Xid } = ( ( shift @ payload ) << 8 ) + ( shift @ payload ) ;
my $ langlen = ( ( shift @ payload ) << 8 ) + ( shift @ payload ) ;
$ parsedheader { lang } = pack ( "C*" , splice ( @ payload , 0 , $ langlen ) ) ;
$ parsedheader { payload } = pack ( "C*" , @ payload ) ;
if ( $ nextoffset != 0 ) {
#correct offset since header will be removed
$ parsedheader { currentoffset } = 14 + $ langlen ;
$ parsedheader { extoffset } = $ nextoffset ;
}
return \ % parsedheader ;
2012-03-17 19:59:58 +00:00
}
2013-12-13 09:13:44 +00:00
2012-03-17 19:59:46 +00:00
sub genslpheader {
2013-12-13 09:13:44 +00:00
my $ packet = shift ;
my % args = @ _ ;
my $ flaghigh = 0 ;
my $ flaglow = 0 ; #this will probably never ever ever change
if ( $ args { Multicast } ) { $ flaghigh |= 0x20 ; }
my $ extoffset = 0 ;
if ( $ args { ExtOffset } ) {
$ extoffset = $ args { ExtOffset } + 16 ;
}
my @ extoffset = ( ( $ extoffset >> 16 ) , ( ( $ extoffset >> 8 ) & 0xff ) , ( $ extoffset & 0xff ) ) ;
my $ length = length ( $ packet ) + 16 ; #our header is 16 bytes due to lang tag invariance
if ( $ length > 1400 ) { die "Overflow not supported in xCAT SLP" ; }
return pack ( "C*" , 2 , $ args { FunctionId } , ( $ length >> 16 ) , ( $ length >> 8 ) & 0xff , $ length & 0xff , $ flaghigh , $ flaglow , @ extoffset , $ xid >> 8 , $ xid & 0xff , 0 , 2 ) . "en" ;
2012-03-17 19:59:46 +00:00
}
2013-12-13 09:13:44 +00:00
unless ( caller ) {
#time to provide unit testing/example usage
#somewhat fancy invocation with multiple services and callback for
#results on-the-fly
require Data::Dumper ;
Data::Dumper - > import ( ) ;
my $ srvtypes = [ "service:management-hardware.IBM:chassis-management-module" , "service:management-hardware.IBM:integrated-management-module2" , "service:management-hardware.IBM:management-module" , "service:management-hardware.IBM:cec-service-processor" ] ;
xCAT::SLP:: dodiscover ( SrvTypes = > $ srvtypes , Callback = > sub { print Dumper ( @ _ ) } ) ;
#example 2: simple invocation of a single service type
$ srvtypes = "service:management-hardware.IBM:chassis-management-module" ;
print Dumper ( xCAT::SLP:: dodiscover ( SrvTypes = > $ srvtypes ) ) ;
#TODO: pass-in socket and not wait inside SLP.pm example
2012-03-17 20:00:11 +00:00
}
2012-07-23 07:33:51 +00:00
###########################################
# Parse the slp resulte data
###########################################
sub handle_new_slp_entity {
2013-12-13 09:13:44 +00:00
my $ data = shift ;
delete $ data - > { sockaddr } ; #won't need it
my $ mac = get_mac_for_addr ( $ data - > { peername } ) ;
unless ( $ mac ) { return ; }
$ searchmacs { $ mac } = $ data ;
2012-07-23 07:33:51 +00:00
}
###########################################
# Get mac addresses
###########################################
sub get_mac_for_addr {
2013-12-13 09:13:44 +00:00
my $ neigh ;
my $ addr = shift ;
if ( $ addr =~ /:/ ) {
get_ipv6_neighbors ( ) ;
return $ ip6neigh { $ addr } ;
} else {
get_ipv4_neighbors ( ) ;
return $ ip4neigh { $ addr } ;
}
2012-07-23 07:33:51 +00:00
}
###########################################
# Get ipv4 mac addresses
###########################################
sub get_ipv4_neighbors {
if ( xCAT::Utils - > isAIX ( ) ) {
my @ ipdata = `arp -a` ;
% ip6neigh = ( ) ;
for my $ entry ( @ ipdata ) {
if ( $ entry =~ /(\d+\.\d+\.\d+\.\d+)/ ) {
my $ ip = $ 1 ;
#if ($entry =~ /at (\w+\:\w+\:\w+\:\w+\:\w+\:\w+)/) {
# $ip4neigh{$ip}=$1;
if ( $ entry =~ /at (\w+)\:(\w+)\:(\w+)\:(\w+)\:(\w+)\:(\w+)/ ) {
2012-08-07 02:55:25 +00:00
#$ip4neigh{$ip}=$1.$2.$3.$4.$5.$6;
$ ip4neigh { $ ip } = sprintf ( "%02s%02s%02s%02s%02s%02s" , $ 1 , $ 2 , $ 3 , $ 4 , $ 5 , $ 6 ) ;
2012-07-23 07:33:51 +00:00
}
}
}
} else {
#TODO: something less 'hacky'
my @ ipdata = `ip -4 neigh` ;
% ip6neigh = ( ) ;
foreach ( @ ipdata ) {
if ( /^(\S*)\s.*lladdr\s*(\S*)\s/ ) {
2013-12-13 09:13:44 +00:00
$ ip4neigh { $ 1 } = $ 2 ;
2012-07-23 07:33:51 +00:00
}
}
}
}
###########################################
# Get ipv6 mac addresses
###########################################
sub get_ipv6_neighbors {
2013-12-13 09:13:44 +00:00
#TODO: something less 'hacky'
my @ ipdata = `ip -6 neigh` ;
% ip6neigh = ( ) ;
foreach ( @ ipdata ) {
if ( /^(\S*)\s.*lladdr\s*(\S*)\s/ ) {
$ ip6neigh { $ 1 } = $ 2 ;
2012-07-23 07:33:51 +00:00
}
2013-12-13 09:13:44 +00:00
}
2012-07-23 07:33:51 +00:00
}
2012-08-11 07:05:28 +00:00
sub send_message {
my $ callback = shift ;
my $ ecode = shift ;
my $ msg = shift ;
my % output ;
$ output { errorcode } = $ ecode ;
$ output { data } = $ msg ;
$ callback - > ( \ % output ) ;
}
2012-03-17 20:00:11 +00:00
1 ;
2013-12-13 09:13:44 +00:00