mirror of
				https://github.com/xcat2/xcat-core.git
				synced 2025-10-31 11:22:27 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			333 lines
		
	
	
		
			7.8 KiB
		
	
	
	
		
			Perl
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			333 lines
		
	
	
		
			7.8 KiB
		
	
	
	
		
			Perl
		
	
	
		
			Executable File
		
	
	
	
	
| #! /usr/bin/perl
 | |
| # IBM(c) 2013 EPL license http://www.eclipse.org/legal/epl-v10.html
 | |
| # This program will start a proxydhcp daemon to handle 4011 request
 | |
| 
 | |
| use Sys::Syslog;
 | |
| use IO::Socket::INET;
 | |
| use IO::Select;
 | |
| use Getopt::Long;
 | |
| 
 | |
| my $quit     = 0;
 | |
| my $doreload = 0;
 | |
| my %nodecfg;
 | |
| my $bootpmagic = pack("C*", 0x63, 0x82, 0x53, 0x63);
 | |
| 
 | |
| # set signal handler to set flag to reload configuration file
 | |
| $SIG{USR1} = sub {
 | |
|     $doreload = 1;
 | |
| };
 | |
| $SIG{TERM} = $SIG{INT} = sub {
 | |
|     $quit = 1;
 | |
| };
 | |
| 
 | |
| my $verbose;
 | |
| my $tobedaemon;
 | |
| Getopt::Long::Configure("bundling");
 | |
| Getopt::Long::Configure("pass_through");
 | |
| GetOptions(
 | |
|     'V' => \$verbose,
 | |
|     'd' => \$tobedaemon,
 | |
| );
 | |
| 
 | |
| if ($tobedaemon) {
 | |
|     daemonize();
 | |
| }
 | |
| 
 | |
| # open syslog
 | |
| openlog("xcat", "nofatal", "local4");
 | |
| 
 | |
| my $socket;
 | |
| my $retry = 5;
 | |
| while ($retry > 0) {
 | |
|     $socket = IO::Socket::INET->new(LocalPort => 4011,
 | |
|         Proto  => 'udp',
 | |
|         Domain => AF_INET);
 | |
|     if ($socket) {
 | |
|         last;
 | |
|     } else {
 | |
|         sleep 1;
 | |
|     }
 | |
|     $retry--;
 | |
| }
 | |
| unless ($socket) {
 | |
|     syslog("info", "Unable to open socket on port 4011.");
 | |
|     closelog();
 | |
|     exit;
 | |
| }
 | |
| 
 | |
| # regist my pid to /var/run/xcat/proxydhcp.pid
 | |
| if (open(PIDF, ">/var/run/xcat/proxydhcp-xcat.pid")) {
 | |
|     print PIDF $$;
 | |
|     close(PIDF);
 | |
| } else {
 | |
|     syslog("info", "Cannot open /var/run/xcat/proxydhcp.pid.");
 | |
|     closelog();
 | |
|     exit;
 | |
| }
 | |
| 
 | |
| 
 | |
| my $select = new IO::Select;
 | |
| $select->add($socket);
 | |
| load_cfg();
 | |
| until ($quit) {
 | |
|     until ($select->can_read(5)) {    #Wait for data
 | |
|         if ($doreload) {
 | |
|             load_cfg();
 | |
|             syslog("info", "Reload configuration file in select.");
 | |
|         }
 | |
|         if ($quit) { last; }
 | |
|         yield;
 | |
|     }
 | |
| 
 | |
|     if ($doreload) {
 | |
|         load_cfg();
 | |
|         syslog("info", "Reload configuration file before recv.");
 | |
|     }
 | |
| 
 | |
|     my $data;
 | |
|     my $caddr = $socket->recv($data, 1500);
 | |
| 
 | |
|     my ($cport, $cnip) = sockaddr_in($caddr);
 | |
|     my $snip = my_ip_facing($cnip);
 | |
|     unless ($snip) {
 | |
|         syslog("info", "Cannot find the server ip of proxydhcp daemon");
 | |
|         next;
 | |
|     }
 | |
| 
 | |
|     if (length($data) < 320) {
 | |
|         next;
 | |
|     }
 | |
| 
 | |
|     my @package = unpack("C*", $data);
 | |
|     my @replypkg;
 | |
| 
 | |
|     if (pack("C*", $package[0xec], $package[0xed], $package[0xee], $package[0xef]) != $bootpmagic) {
 | |
|         next;
 | |
|     }
 | |
| 
 | |
|     # get the node name of client
 | |
|     my $nodename = gethostbyaddr($cnip, AF_INET);
 | |
| 
 | |
|     # get the winpepath
 | |
|     my $winpepath = "";
 | |
|     if ($nodename) {
 | |
|         if (defined $nodecfg{$nodename}) {
 | |
|             $winpepath = $nodecfg{$nodename};
 | |
|             if ($verbose) { syslog("info", "Find configure for $nodename= $nodecfg{$nodename} in configuration file") }
 | |
|         } else {
 | |
|             $nodename =~ s/\..*//;
 | |
|             if (defined $nodecfg{$nodename}) {
 | |
|                 $winpepath = $nodecfg{$nodename};
 | |
|                 if ($verbose) { syslog("info", "Find configure for $nodename= $nodecfg{$nodename} in configuration file") }
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     # get the Vendor class identifier
 | |
|     my $strp  = 0xf0;
 | |
|     my $archp = 0;
 | |
|     while ($strp < $#package) {
 | |
|         if ($package[$strp] == 60) {
 | |
|             $archp = $strp + 0x11;
 | |
|             last;
 | |
|         } else {
 | |
|             $strp += $package[ $strp + 1 ] + 2;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     # get the winpe boot loader path
 | |
|     my $winboot = $winpepath . "Boot/pxeboot.0";
 | |
|     if ($archp) {
 | |
|         my $clienttype = substr($data, $archp, 5);
 | |
|         if ($clienttype eq "00000") {
 | |
| 
 | |
|             #if ("$package[$archp]$package[$archp+1]$package[$archp+2]$package[$archp+3]$package[$archp+4]" == "00000") {
 | |
|             $winboot = $winpepath . "Boot/pxeboot.0";
 | |
|         } elsif ($clienttype eq "00007") {
 | |
|             $winboot = $winpepath . "Boot/bootmgfw.efi";
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     syslog("info", "The boot loader path for node $nodename is $winboot");
 | |
| 
 | |
|     # set message type
 | |
|     $replypkg[0] = 2;
 | |
| 
 | |
|     # set the hardware type
 | |
|     $replypkg[1] = $package[1];
 | |
| 
 | |
|     # set the hardware address length
 | |
|     $replypkg[2] = $package[2];
 | |
| 
 | |
|     # set the hops
 | |
|     $replypkg[3] = $package[3];
 | |
| 
 | |
|     # set the transaction ID
 | |
|     @replypkg = (@replypkg, @package[ 4 .. 7 ]);
 | |
| 
 | |
|     # set elapsed time
 | |
|     $replypkg[8] = 0;
 | |
|     $replypkg[9] = 0;
 | |
| 
 | |
|     # set bootp flag
 | |
|     $replypkg[0xa] = 0;
 | |
|     $replypkg[0xb] = 0;
 | |
| 
 | |
|     # set client ip
 | |
|     @replypkg = (@replypkg, @package[ 0xc .. 0xf ]);
 | |
| 
 | |
|     # set Your (client IP)
 | |
|     @replypkg = (@replypkg, 0, 0, 0, 0);
 | |
| 
 | |
|     #set Next server IP (set my IP)
 | |
|     @replypkg = (@replypkg, unpack("C*", $snip));
 | |
| 
 | |
|     # set dhcp relay agent ip
 | |
|     @replypkg = (@replypkg, 0, 0, 0, 0);
 | |
| 
 | |
|     # set client hardware address
 | |
|     @replypkg = (@replypkg, @package[ 0x1c .. 0x2b ]);
 | |
| 
 | |
|     # set server host name
 | |
|     foreach (0x2c .. 0x6b) {
 | |
|         $replypkg[$_] = 0;
 | |
|     }
 | |
| 
 | |
|     # set the boot file name
 | |
|     @replypkg = (@replypkg, unpack("C*", $winboot));
 | |
|     my $lenth = length($winboot);
 | |
|     foreach (0x6c + $lenth .. 0xeb) {
 | |
|         $replypkg[$_] = 0;
 | |
|     }
 | |
| 
 | |
|     # add magic cookie
 | |
|     #my @xx = unpack("C*", $bootpmagic);
 | |
|     #@replypkg = (@replypkg, @xx);
 | |
|     @replypkg = (@replypkg, unpack("C*", $bootpmagic));
 | |
| 
 | |
|     # set dhcp msg type
 | |
|     $replypkg[0xf0] = 0x35;    # option number
 | |
|     $replypkg[0xf1] = 0x1;     # msg length
 | |
|     $replypkg[0xf2] = 0x5;     # dhcp ack
 | |
| 
 | |
|     # set dhcp server identifer
 | |
|     $replypkg[0xf3] = 0x36;    # option number
 | |
|     $replypkg[0xf4] = 0x4;     # msg length
 | |
|     @replypkg = (@replypkg, unpack("C*", $snip));
 | |
| 
 | |
|     # set the bcd path
 | |
|     my $winbcd = $winpepath . "Boot/BCD";
 | |
|     $replypkg[0xf9] = 0xfc;                   # option number
 | |
|     $replypkg[0xfa] = length($winbcd) + 1;    # msg length
 | |
|     @replypkg = (@replypkg, unpack("C*", $winbcd));
 | |
|     $replypkg[ 0xfa + length($winbcd) + 1 ] = 0;
 | |
|     $replypkg[ 0xfa + length($winbcd) + 2 ] = 0xff;
 | |
| 
 | |
|     $socket->send(pack("C*", @replypkg), 0, $caddr);
 | |
| 
 | |
|     syslog("info", "The BCD path for node $nodename is $winbcd");
 | |
| 
 | |
|     # debug package detail
 | |
|     if (0) {
 | |
|         my $msg;
 | |
|         my $firstline = 1;
 | |
|         my $num       = 0;
 | |
|         foreach (@replypkg) {
 | |
|             my $v = sprintf("%2x ", $_);
 | |
|             $msg .= $v;
 | |
|             if (($num - 5) % 8 eq 0) {
 | |
|                 $msg .= "   ";
 | |
|             }
 | |
|             if (($num - 5) % 16 eq 0) {
 | |
|                 syslog("info", $msg);
 | |
|                 print "$msg \n";
 | |
|                 $msg = "";
 | |
|             }
 | |
|             $num++;
 | |
|         }
 | |
|         print "$msg \n";
 | |
|     }
 | |
| }
 | |
| 
 | |
| closelog();
 | |
| 
 | |
| # daemonize the service
 | |
| sub daemonize
 | |
| {
 | |
|     chdir('/');
 | |
|     umask 0022;
 | |
|     my $pid = fork;
 | |
| 
 | |
|     if ($pid) {
 | |
|         exit;
 | |
|     }
 | |
|     open STDOUT, '>/dev/null';
 | |
|     open STDERR, '>/dev/null';
 | |
|     $0        = 'proxydhcp-xcat';
 | |
|     $progname = \$0;
 | |
| }
 | |
| 
 | |
| #  load configuration from /var/lib/xcat/proxydhcp.cfg to %nodecfg
 | |
| sub load_cfg
 | |
| {
 | |
|     $doreload = 0;
 | |
|     if (!-r "/var/lib/xcat/proxydhcp.cfg") {
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     if (!open(CFG, "</var/lib/xcat/proxydhcp.cfg")) {
 | |
|         syslog("info", "Cannot open /var/lib/xcat/proxydhcp.cfg");
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     my $mycfg;
 | |
|     while (<CFG>) {
 | |
|         $mycfg .= $_;
 | |
|     }
 | |
| 
 | |
|     my $p = 0;
 | |
|     while (1) {
 | |
| 
 | |
|         my $name = substr($mycfg, $p, 50);
 | |
|         $p += 50;
 | |
|         my $value = substr($mycfg, $p, 150);
 | |
|         $p += 150;
 | |
| 
 | |
|         $name =~ s/\0//g;
 | |
|         $value =~ s/\0//g;
 | |
|         if ($name) {
 | |
|             $nodecfg{$name} = $value;
 | |
|         } else {
 | |
|             return;
 | |
|         }
 | |
|     }
 | |
| 
 | |
| 
 | |
|     close(CFG);
 | |
| }
 | |
| 
 | |
| # get the ip in xCAT MN/SN which facing target ip
 | |
| sub my_ip_facing
 | |
| {
 | |
|     my $peer = shift;
 | |
|     unless ($peer) { return undef; }
 | |
|     my $noden = unpack("N", $peer);
 | |
|     my @nets = split /\n/, `/sbin/ip addr`;
 | |
|     foreach (@nets)
 | |
|     {
 | |
|         my @elems = split /\s+/;
 | |
|         unless (/^\s*inet\s/)
 | |
|         {
 | |
|             next;
 | |
|         }
 | |
|         (my $curnet, my $maskbits) = split /\//, $elems[2];
 | |
|         my $curmask = 2**$maskbits - 1 << (32 - $maskbits);
 | |
|         my $curn = unpack("N", inet_aton($curnet));
 | |
|         if (($noden & $curmask) eq ($curn & $curmask))
 | |
|         {
 | |
|             return inet_aton($curnet);
 | |
|         }
 | |
|     }
 | |
|     return undef;
 | |
| }
 |