diff --git a/perl-xCAT/xCAT/SLP.pm b/perl-xCAT/xCAT/SLP.pm new file mode 100644 index 000000000..c40e832a9 --- /dev/null +++ b/perl-xCAT/xCAT/SLP.pm @@ -0,0 +1,150 @@ +package xCAT::SLP; +use strict; +my $ip6support = eval { + require IO::Socket::INET6; + require Socket6; + 1; +}; +use Socket; +unless ($ip6support) { + require IO::Socket::INET; +} + +my $defaultsrvtypes = "service:management-hardware.IBM"; +#TODO: somehow get at system headers to get the value, put in linux's for now +use constant IPV6_MULTICAST_IF => 17; + +sub getmulticasthashes { + my %args = @_; + my $srvtypes; + if ($args{SrvTypes}) { + $srvtypes = $args{SrvTypes}; + } else { + $srvtypes = $defaultsrvtypes; + } + my @types = split /,/,$srvtypes; + my @hashes; + foreach (@types) { + my $hash=0; + my @nums = unpack("C*",$_); + foreach my $num (@nums) { + $hash *= 33; + $hash += $num; + $hash &= 0xffff; + } + $hash &= 0x3ff; + $hash |= 0x1000; + push @hashes,sprintf("%04x",$hash); + } + return @hashes; +} + + +sub dodiscover { + my %args = @_; + my $packet = gendiscover(%args); + my @interfaces = get_interfaces(%args); + my $socket; + if ($args{'socket'}) { + $socket = $args{'socket'}; + } elsif ($ip6support) { + $socket = IO::Socket::INET6->new(Proto => 'udp'); + } else { + die "TODO: SLP without ipv6"; + } + my @v6addrs; + if ($ip6support) { + foreach my $hash (getmulticasthashes(%args)) { + my $target = "ff02::1:$hash"; + my ($fam, $type, $proto, $addr, $name) = + Socket6::getaddrinfo($target,"svrloc",Socket6::AF_INET6(),SOCK_DGRAM,0); + push @v6addrs,$addr; + } + } + foreach my $iface (@interfaces) { + if ($ip6support) { + setsockopt($socket,Socket6::IPPROTO_IPV6(),IPV6_MULTICAST_IF,pack("I",$iface)); + foreach my $v6addr (@v6addrs) { + $socket->send($packet,0,$v6addr); + } + } + #TODO: IPv4 support +# setsockopt($socket,IPPROTO_IP,IP_MULTICAST_IF, + } +} + +sub get_interfaces { + #TODO: AIX tolerance, no subprocess, include/exclude interface(s) + my @ipoutput = `ip link`; + my @ifaceoutput = grep(/MULTICAST/,@ipoutput); + my @interfaces; + foreach (@ifaceoutput) { + chomp; + s/:.*//; + push @interfaces,$_; + } + return @interfaces; +} +# 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 | String \ +# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +# | length of | String \ +# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +# | length of | String \ +# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +# | length of predicate string | Service Request \ +# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +# | length of string | String \ +# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +sub gendiscover { + my %args = @_; + my $srvtypes; + if ($args{SrvTypes}) { + $srvtypes = $args{SrvTypes}; + } else { + $srvtypes = $defaultsrvtypes; + } + my $scope = "DEFAULT"; + if ($args{Scopes}) { $scope = $args{Scopes}; } + my $packet = pack("C*",0,0); #start with PRList, we have no prlist so zero + #TODO: actually accumulate PRList, particularly between IPv4 and IPv6 runs + my $length = length($srvtypes); + $packet .= pack("C*",($length>>8),($length&0xff)); + $packet .= $srvtypes; + $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); + my $header = genslpheader($packet,Multicast=>1,FunctionId=>1); + 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 genslpheader { + my $packet = shift; + my %args = @_; + my $xid = rand(65535); + my $flaghigh=0; + my $flaglow=0; #this will probably never ever ever change + if ($args{Multicast}) { $flaghigh |= 0x20; } + 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,0,0,0,$xid>>8,$xid&0xff,0,2)."en"; +} +