xcat-core/xCAT-server/sbin/proxydhcp-xcat
2015-02-05 12:04:42 -05:00

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 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;
}