796 lines
31 KiB
Perl
796 lines
31 KiB
Perl
package xCAT::SLP;
|
|
use Carp;
|
|
use IO::Select;
|
|
use strict;
|
|
use xCAT::Utils;
|
|
my $ip6support = eval {
|
|
require IO::Socket::INET6;
|
|
require Socket6;
|
|
1;
|
|
};
|
|
use Socket;
|
|
unless ($ip6support) {
|
|
require IO::Socket::INET;
|
|
}
|
|
|
|
#TODO: somehow get at system headers to get the value, put in linux's for now
|
|
use constant IPV6_MULTICAST_IF => 17;
|
|
use constant IP_MULTICAST_IF => 32;
|
|
use constant REQ_INTERVAL => 1;
|
|
my %xid_to_srvtype_map;
|
|
my $xid;
|
|
my $gprlist;
|
|
my %searchmacs;
|
|
my %ip4neigh;
|
|
my %ip6neigh;
|
|
my %servicehash;
|
|
my %sendhash;
|
|
my $attrpy = 0;
|
|
my $serrpy = 0;
|
|
sub getmulticasthash {
|
|
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);
|
|
}
|
|
|
|
|
|
sub dodiscover {
|
|
my %args = @_;
|
|
my $unicast = $args{unicast}; #should be used with -s !
|
|
my $ipranges = $args{range};
|
|
my $rspcount = 0;
|
|
my $rspcount1 = 0;
|
|
my $sendcount = 1;
|
|
$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);
|
|
if ($args{Ip}) {
|
|
foreach my $nic (keys %$interfaces) {
|
|
if (${${$interfaces->{$nic}}{ipv4addrs}}[0] =~ /(\d+\.\d+\.\d+\.\d+)/) {
|
|
unless ($args{Ip} =~ $1) {
|
|
delete $interfaces->{$nic};
|
|
}
|
|
}
|
|
}
|
|
}
|
|
my @printip;
|
|
foreach my $iface (keys %{$interfaces}) {
|
|
foreach my $sip (@{$interfaces->{$iface}->{ipv4addrs}}) {
|
|
my $ip = $sip;
|
|
$ip =~ s/\/(.*)//;
|
|
push @printip, $ip;
|
|
}
|
|
}
|
|
my $printinfo = join(",", @printip);
|
|
|
|
if ($unicast) {
|
|
my @servernodes;
|
|
my @iprange = split /,/, $ipranges;
|
|
foreach my $range (@iprange) {
|
|
`/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;
|
|
}
|
|
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;
|
|
my $waittime = ($args{Time}>0)?$args{Time}:60;
|
|
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);
|
|
}
|
|
if ($peername =~ /\./) { #ipv4
|
|
$peername =~ s/::ffff://;
|
|
}
|
|
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;
|
|
}# 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);
|
|
} else {
|
|
($port,$ip4n) = sockaddr_in($pkg);
|
|
$peername = inet_ntoa($ip4n);
|
|
}
|
|
if ($rethash{$peername}) {
|
|
next; #got a dupe, discard
|
|
}
|
|
my $result = process_slp_packet(packet=>$slpkg,sockaddr=>$pkg,'socket'=>$args{'socket'});
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
#############################
|
|
# 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});
|
|
last;
|
|
}
|
|
#########################
|
|
# 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;
|
|
}
|
|
}
|
|
} #end nowait
|
|
} #end of if( unicast )
|
|
|
|
foreach my $entry (keys %rethash) {
|
|
handle_new_slp_entity($rethash{$entry});
|
|
}
|
|
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`;
|
|
}
|
|
}
|
|
}
|
|
return (\%searchmacs, $sendcount, $rspcount);
|
|
}
|
|
|
|
sub process_slp_packet {
|
|
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; }
|
|
if ($parsedpacket->{attributes}) { #service reply had ext
|
|
|
|
return $parsedpacket; #don't bother sending attrrequest, already got it in first packet
|
|
}
|
|
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});
|
|
#delete $parsedpacket->{payload};
|
|
return $parsedpacket;
|
|
} else {
|
|
return undef;
|
|
}
|
|
}
|
|
|
|
sub parse_attribute_reply {
|
|
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);
|
|
}
|
|
sub parse_attribute_list {
|
|
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}=[];
|
|
my $valstring = $1;
|
|
if (defined $valstring) {
|
|
foreach(split /,/,$valstring) {
|
|
push @{$attribs{$attrib}},$_;
|
|
}
|
|
}
|
|
} else {
|
|
$attrstring =~ s/([^,]*),?//;
|
|
$attribs{$1}=[];
|
|
}
|
|
}
|
|
return \%attribs;
|
|
}
|
|
sub generate_attribute_request {
|
|
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});
|
|
}
|
|
|
|
|
|
sub parse_service_reply {
|
|
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;
|
|
}
|
|
|
|
sub parse_extension {
|
|
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);
|
|
}
|
|
}
|
|
|
|
|
|
sub extract_next_url { #section 4.3 url entries
|
|
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);
|
|
}
|
|
|
|
sub send_service_request_single {
|
|
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;
|
|
if (xCAT::Utils->isAIX()) {
|
|
my $runcmd = `route add 239.255.255.253 $ip`;
|
|
}
|
|
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);
|
|
setsockopt($socket,0,IP_MULTICAST_IF,$ipn);
|
|
$socket->send($packet,0,$ipv4sockaddr);
|
|
$socket->send($packet,0,$bcastaddr);
|
|
}
|
|
}
|
|
}
|
|
|
|
sub get_interfaces {
|
|
#TODO: AIX tolerance, no subprocess, include/exclude interface(s)
|
|
my %ifacemap;
|
|
my $payingattention=0;
|
|
my $interface;
|
|
my $keepcurrentiface;
|
|
# 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/;
|
|
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;
|
|
}
|
|
push @{$ifacemap{$nics[$i]}->{ipv4addrs}},$tmpip;
|
|
if( $nics[$i]=~ /\w+(\d+)/){
|
|
$ifacemap{$nics[$i]}->{scopeidx} = $1+2;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
my @ipoutput = `ip addr`;
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
return \%ifacemap;
|
|
}
|
|
# discovery is "service request", 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
|
|
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
# | 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 \
|
|
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
sub generate_service_request {
|
|
my %args = @_;
|
|
my $srvtype = $args{SrvType};
|
|
my $scope = "DEFAULT";
|
|
if ($args{Scopes}) { $scope = $args{Scopes}; }
|
|
my $prlist = $gprlist;
|
|
my $prlength = length($prlist);
|
|
my $packet = pack("C*",($prlength>>8),($prlength&0xff));
|
|
$packet .= $prlist;
|
|
my $length = length($srvtype);
|
|
$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;
|
|
}
|
|
# 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 \
|
|
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
sub removeslpheader {
|
|
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;
|
|
}
|
|
|
|
|
|
|
|
sub genslpheader {
|
|
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";
|
|
}
|
|
|
|
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
|
|
}
|
|
###########################################
|
|
# Parse the slp resulte data
|
|
###########################################
|
|
sub handle_new_slp_entity {
|
|
my $data = shift;
|
|
delete $data->{sockaddr}; #won't need it
|
|
my $mac = get_mac_for_addr($data->{peername});
|
|
unless ($mac) { return; }
|
|
$searchmacs{$mac} = $data;
|
|
}
|
|
###########################################
|
|
# Get mac addresses
|
|
###########################################
|
|
sub get_mac_for_addr {
|
|
my $neigh;
|
|
my $addr = shift;
|
|
if ($addr =~ /:/) {
|
|
get_ipv6_neighbors();
|
|
return $ip6neigh{$addr};
|
|
} else {
|
|
get_ipv4_neighbors();
|
|
return $ip4neigh{$addr};
|
|
}
|
|
}
|
|
|
|
###########################################
|
|
# 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+)/) {
|
|
#$ip4neigh{$ip}=$1.$2.$3.$4.$5.$6;
|
|
$ip4neigh{$ip}=sprintf("%02s%02s%02s%02s%02s%02s",$1,$2,$3,$4,$5,$6);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
#TODO: something less 'hacky'
|
|
my @ipdata = `ip -4 neigh`;
|
|
%ip6neigh=();
|
|
foreach (@ipdata) {
|
|
if (/^(\S*)\s.*lladdr\s*(\S*)\s/) {
|
|
$ip4neigh{$1}=$2;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
###########################################
|
|
# Get ipv6 mac addresses
|
|
###########################################
|
|
sub get_ipv6_neighbors {
|
|
#TODO: something less 'hacky'
|
|
my @ipdata = `ip -6 neigh`;
|
|
%ip6neigh=();
|
|
foreach (@ipdata) {
|
|
if (/^(\S*)\s.*lladdr\s*(\S*)\s/) {
|
|
$ip6neigh{$1}=$2;
|
|
}
|
|
}
|
|
}
|
|
|
|
sub send_message {
|
|
my $callback = shift;
|
|
my $ecode = shift;
|
|
my $msg = shift;
|
|
my %output;
|
|
$output{errorcode} = $ecode;
|
|
$output{data} = $msg;
|
|
$callback->( \%output );
|
|
}
|
|
1;
|
|
|
|
|