332 lines
7.7 KiB
Perl
Executable File
332 lines
7.7 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("proxydhcp", "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] eq 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 == "00000") {
|
|
#if ("$package[$archp]$package[$archp+1]$package[$archp+2]$package[$archp+3]$package[$archp+4]" == "00000") {
|
|
$winboot = $winpepath."Boot/pxeboot.0";
|
|
} elsif ($clienttype == "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) == ($curn & $curmask))
|
|
{
|
|
return inet_aton($curnet);
|
|
}
|
|
}
|
|
return undef;
|
|
}
|