perl DHCP with mysql

Проблемы с установкой, настройкой и работой системных и сетевых программ.

Модераторы: GRooVE, alexco

Правила форума
Убедительная просьба юзать теги [code] при оформлении листингов.
Сообщения не оформленные должным образом имеют все шансы быть незамеченными.
harmless
лейтенант
Сообщения: 719
Зарегистрирован: 2007-08-23 10:56:51
Откуда: Украина, г. Киев, г. Белая Церковь
Контактная информация:

perl DHCP with mysql

Непрочитанное сообщение harmless » 2011-11-05 17:18:08

Всем привет!
Имеется в наличии сервер dhcp написаный на перле

Код: Выделить всё

#!/usr/bin/perl -w
### Rozhuk Ivan, 2011
### dhcp perl server
### version 1.0


# required:
# /usr/ports/net/p5-Net-DHCP
# /usr/ports/devel/p5-Test-Benchmark
# /usr/ports/databases/p5-DBI
# /usr/ports/databases/p5-Class-DBI-mysql


use strict;
use warnings;
use threads;
use threads::shared;
use Socket;
use Switch;
use DBI;
use Net::DHCP::Packet;
use Net::DHCP::Constants;
use Benchmark ':hireswallclock';
use POSIX qw(setsid setuid strftime :signal_h);
use Getopt::Long;
#require 'sys/syscall.ph';


# settings, via command line or internal defined
my ($BIND_ADDR, $SERVER_PORT, $CLIENT_PORT, $MIRROR, $DHCP_SERVER_ID, $THREADS_COUNT, $DBDATASOURCE, $DBLOGIN, $DBPASS, $PIDFILE, $DEBUG);


# global variables
my ($RUNNING, $ADDR_BCAST, $ADDR_MIRROR, $SOCKET_RCV);

share($RUNNING);
#share($SOCKET_RCV);



&startpoint();


#this keeps the program alive or something after exec'ing perl scripts
END()		{ } 
BEGIN()	{ }
{no warnings; *CORE::GLOBAL::exit = sub { die "fakeexit\nrc=".shift()."\n"; }; };
eval q{exit}; 
if ($@) { exit unless $@ =~ /^fakeexit/; };


# generic signal handler to cause daemon to stop
sub signal_handler {
	$RUNNING = 0;
	close($SOCKET_RCV);
	$_->kill('KILL')->detach() foreach threads->list();
	#$_->join foreach threads->list();
}


sub startpoint {
	if ($#ARGV == -1) {
		usage();
		return;
	}


	# set default settings values
	$BIND_ADDR		= '0.0.0.0';
	$SERVER_PORT		= '67';
	$CLIENT_PORT		= '68';
	$DHCP_SERVER_ID	= ''; # REQUIRED real IP for work!!!
	$MIRROR		= undef;
	$DBDATASOURCE		= 'mysql:DBNAME:IP';
	$DBLOGIN		= '';
	$DBPASS		= '';
	$THREADS_COUNT	= 4;
	$PIDFILE		= '/var/run/perl-dhcpd.pid';
	$DEBUG			= undef;
	my $DAEMON		= undef;

	GetOptions(	'b=s'	=> \$BIND_ADDR,
			'sp:i'	=> \$SERVER_PORT,
			'cp:i'	=> \$CLIENT_PORT,
			'id=s'	=> \$DHCP_SERVER_ID,
			'm=s'	=> \$MIRROR,
			't:i'	=> \$THREADS_COUNT,
			'dbs=s'=> \$DBDATASOURCE,
			'dbl=s'=> \$DBLOGIN,
			'dbp=s'=> \$DBPASS,
			'P:s'	=> \$PIDFILE,
			'v:i'	=> \$DEBUG,
			'd'	=> \$DAEMON,
	);

	# untainte input
	if ($BIND_ADDR =~ /^(.*)$/)		{$BIND_ADDR = $1;}
	if ($DHCP_SERVER_ID =~ /^(.*)$/)	{$DHCP_SERVER_ID = $1;}
	if ($PIDFILE =~ /^(.*)$/)		{$PIDFILE = $1;}

	if (defined($DHCP_SERVER_ID) == 0)	{ usage(); }

	$SIG{INT} = $SIG{TERM} = $SIG{HUP} = \&signal_handler;
	# trap or ignore $SIG{PIPE}

	# Daemon behaviour
	# ignore any PIPE signal: standard behaviour is to quit process
	$SIG{PIPE} = 'IGNORE';


	logger("BIND_ADDR: $BIND_ADDR, THREADS_COUNT: $THREADS_COUNT, PIDFILE: $PIDFILE");

	if (defined($DAEMON)) {
		$DEBUG = undef;
		daemonize();
	}

	main();
}


sub usage {
	print "Usage: dhcpd [options]\n\n";
	print " -b <ip>		ip address to bind (def: 0.0.0.0)\n";
	print " -sp <port>		port bind (def: 67)\n";
	print " -cp <port>		port to send reply directly to client (def: 68)\n";
	print " -id <ip>		ip addr DHCP server ID, REQUIRED!, MUST be real IP of server\n";
	print " -m <ip>		ip address to mirror all packets on 67 port\n";
	print " -t <threads>		number of thread, recomended: CPU cores * 2, (default 4)\n";
	print " -dbs			database data source: DriverName:database=database_name;host=hostname;port=port\n";
	print " -dbl			data base login\n";
	print " -dbp			data base password\n";
	print " -P <path>		name of PID-file for spawned process\n";
	print " -v <level>		print debug info, levels: 1, 2 (def: off)\n";
	print " -d			daemon mode\n";

	exit;
}


# sample logger
sub logger {
	if (defined($DEBUG) == 0) { return; }

	print STDOUT strftime "[%d/%b/%Y %H:%M:%S] ", localtime;
	print STDOUT $_[0]."\n";
}


sub daemonize {
	delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; # Make %ENV safer
	#setuid(65534)		or die "Can't set uid: $!\n"; # nobody

	open(STDIN,  "+>/dev/null")	or die "Can't open STDIN: $!\n";
	open(STDOUT, "+>&STDIN")	or die "Can't open STDOUT: $!\n";
	open(STDERR, "+>&STDIN")	or die "Can't open STDERR: $!\n";
	defined(my $tm = fork)	or die "Can't fork: $!\n";
	exit if $tm;
	setsid				or die "Can't start a new session: $!\n";
	umask 0;
	logger("Daemon mode");
}


sub main {
	if (defined($BIND_ADDR) == 0) { return; }


	# write PID to file
	if (defined($PIDFILE)) {
		open FILE, "> $PIDFILE" || logger("PID file save error: $!");
		print FILE "$$\n";
		close FILE;
	}


	$RUNNING = 1;

	# broadcast address
	$ADDR_BCAST = sockaddr_in($CLIENT_PORT, INADDR_BROADCAST);# sockaddr_in($CLIENT_PORT, inet_aton('255.255.255.255'))

	# set mirror address if defimed
	if (defined($MIRROR)) {# traff mirroring
		$ADDR_MIRROR = sockaddr_in($SERVER_PORT, inet_aton($MIRROR));
	}

	# open listening socket
	socket($SOCKET_RCV, PF_INET, SOCK_DGRAM, getprotobyname('udp')) || die "Socket creation error: $@\n";
	bind($SOCKET_RCV, sockaddr_in($SERVER_PORT, inet_aton($BIND_ADDR))) || die "bind: $!";


	# start threads
	for my $i (1..($THREADS_COUNT - 1)) {
		threads->create({'context' => 'void'}, \&request_loop);
	}

	### Collect the bits and pieces! ...
	#$_->join foreach threads->list();
	#$_->detach foreach threads->list();

	# and handle request with other threads
	request_loop();


	# delete PID file on exit
	if (defined($PIDFILE)) { unlink($PIDFILE); }

	logger("Main: END!");

}





sub request_loop {
	my ($buf, $fromaddr, $dhcpreq); # recv data
	my $dbh; # database connect
	my ($t0, $t1, $td); # perfomance data
	my $tid = threads->tid(); # thread ID


	logger("Thread ($tid): START");


	# each thread make its own connection to DB
	# connect($data_source, $username, $password, \%attr)
	# dbi:DriverName:database=database_name;host=hostname;port=port
	$dbh = DBI->connect("DBI:".$DBDATASOURCE, $DBLOGIN, $DBPASS);
	if (defined($dbh) == 0) {
		logger("Could not connect to database: $DBI::errstr");
		thread_exit(1);
	}
	$dbh->{mysql_auto_reconnect} = 1;


	if ($tid != 0) {
		# disable signals receiving on creted threads and set handler for KILL signal
		my $sigset = POSIX::SigSet->new(SIGINT, SIGTERM, SIGHUP);    # define the signals to block 
		my $old_sigset = POSIX::SigSet->new;        # where the old sigmask will be kept  
		unless (defined sigprocmask(SIG_BLOCK, $sigset, $old_sigset)) { die "Could not unblock SIGINT\n"; }

		$SIG{KILL} = sub { logger("Thread ($tid): END by sig handler"); $dbh->disconnect; thread_exit(0); };
	}



	while( $RUNNING == 1 ) {
		$buf = undef;

		eval { # catch fatal errors
			# receive packet
			$fromaddr = recv($SOCKET_RCV, $buf, 16384, 0) || logger("Thread ($tid) recv err: $!");

			next if ($!); # continue loop if an error occured

			# filter to small packets
			next if (length($buf) < 236); # 300

			if (defined($DEBUG)) { $t0 = Benchmark->new; }

			# parce data to dhcp structes
			$dhcpreq = new Net::DHCP::Packet($buf);

			# filter bad params in head
			next if ($dhcpreq->op() != BOOTREQUEST || $dhcpreq->isDhcp() == 0);
			next if ($dhcpreq->htype() != HTYPE_ETHER || $dhcpreq->hlen() != 6);

			# bad DHCP message!
			next if (defined($dhcpreq->getOptionRaw(DHO_DHCP_MESSAGE_TYPE())) == 0);

			# Is message for us?
			next if (defined($dhcpreq->getOptionRaw(DHO_DHCP_SERVER_IDENTIFIER())) && $dhcpreq->getOptionValue(DHO_DHCP_SERVER_IDENTIFIER()) ne $DHCP_SERVER_ID);

			# RRAS client, ignory them
			next if (defined($dhcpreq->getOptionRaw(DHO_USER_CLASS())) && $dhcpreq->getOptionRaw(DHO_USER_CLASS()) eq "RRAS.Microsoft");


			# send duplicate of received packet to mirror
			if (defined($ADDR_MIRROR)) { send($SOCKET_RCV, $buf, 0, $ADDR_MIRROR) || logger("send mirr error: $!"); }


			# print received packed
			if (defined($DEBUG)) {
				my ($port, $addr) = unpack_sockaddr_in($fromaddr);
				my $ipaddr = inet_ntoa($addr);
			    	logger("Thread $tid: Got a packet src = $ipaddr:$port length = ".length($buf));
				if ($DEBUG > 1) { logger($dhcpreq->toString()); }
			}


			# handle packet
			switch($dhcpreq->getOptionValue(DHO_DHCP_MESSAGE_TYPE())){
			case DHCPDISCOVER { #-> DHCPOFFER
				db_log_detailed($dbh, $dhcpreq);
				handle_discover($dbh, $fromaddr, $dhcpreq);
				}
			case DHCPREQUEST { #-> DHCPACK/DHCPNAK
				db_log_detailed($dbh, $dhcpreq);
				handle_request($dbh, $fromaddr, $dhcpreq);
				}
			case DHCPDECLINE {
				db_log_detailed($dbh, $dhcpreq);
				handle_decline($dbh, $fromaddr, $dhcpreq);
				}
			case DHCPRELEASE {
				db_log_detailed($dbh, $dhcpreq);
				handle_release($dbh, $fromaddr, $dhcpreq);
				}
			case DHCPINFORM { #-> DHCPACK
				db_log_detailed($dbh, $dhcpreq);
				handle_inform($dbh, $fromaddr, $dhcpreq);
				}
			}

			if (defined($DEBUG)) {
				$t1 = Benchmark->new;
				$td = timediff($t1, $t0);
				logger("Thread $tid: the code took: ".timestr($td));
			}
		}; # end of 'eval' blocks
		if ($@) {
			logger("Thread $tid: Caught error in main loop: $@");
		}
	}

	$dbh->disconnect;

	thread_exit(0);
}


sub thread_exit($) {
	my $tid = threads->tid(); # thread ID

	logger("Thread ($tid): END code: ".$_[0]);

	threads->exit($_[0]) if threads->can('exit');
	exit($_[0]);
}


sub send_reply {
	#my $fromaddr = $_[0];
	#my $dhcpreq = $_[1];
	#my $dhcpresp = $_[2];
	my ($dhcpresppkt, $toaddr);


	# add last!!!!
	if (defined($_[1]->getOptionRaw(DHO_DHCP_AGENT_OPTIONS()))) {
		$_[2]->addOptionRaw(DHO_DHCP_AGENT_OPTIONS(), $_[1]->getOptionRaw(DHO_DHCP_AGENT_OPTIONS()));
	}

	$dhcpresppkt = $_[2]->serialize();
	#if (length($dhcpresppkt) < 548) {
	#	$dhcpresppkt .= "\0" x (548 - length($dhcpresppkt));
	#}


	if ($_[1]->giaddr() eq '0.0.0.0') {
		# client local, not relayed
		if ($_[2]->DHO_DHCP_MESSAGE_TYPE() == DHCPNAK) {# allways broadcast DHCPNAK
			$toaddr = $ADDR_BCAST;
		}else{
			if ($_[1]->ciaddr() eq '0.0.0.0') {
				# ALL HERE NON RFC 2131 4.1 COMPLIANT!!!
				# perl can not send to hw addr unicaset with ip 0.0.0.0, and we send broadcast
				if ($_[1]->flags() == 0 || 1) {# send unicast XXXXXXXXX - flags ignored!
					# here we mast send unicast to hw addr, ip 0.0.0.0
					my ($port, $addr) = unpack_sockaddr_in($_[0]);
					my $ipaddr = inet_ntoa($addr);

					if ($ipaddr eq '0.0.0.0') {
						$toaddr = $ADDR_BCAST;
					}else{# giaddr and ciaddr is zero but we know ip addr from received packet
						$toaddr = sockaddr_in($CLIENT_PORT, $addr);
					}
				}else{
					# only this comliant to rfc 2131 4.1
					$toaddr = $ADDR_BCAST;
				}
			}else{# client have IP addr, send unicast
				$toaddr = sockaddr_in($CLIENT_PORT, $_[1]->ciaddrRaw());
			}
		}
	}else{# send to relay
		$toaddr = sockaddr_in($SERVER_PORT, $_[1]->giaddrRaw());
	}
	send($SOCKET_RCV, $dhcpresppkt, 0, $toaddr) || logger("send error: $!");


	if (defined($DEBUG)) {
		my ($port, $addr) = unpack_sockaddr_in($toaddr);
		my $ipaddr = inet_ntoa($addr);
	    	logger("Sending response to = $ipaddr:$port length = ".length($dhcpresppkt));
		if ($DEBUG > 1) { logger($_[2]->toString()); }
	}

	# send copy of packet to mirror, if specified
	if (defined($ADDR_MIRROR)) { send($SOCKET_RCV, $dhcpresppkt, 0, $ADDR_MIRROR) || logger("send mirr error: $!"); }

}




# Generate responce DHCP packet from request DHCP packet
sub GenDHCPRespPkt {
	#my $dhcpreq = $_[0];

	my $dhcpresp = new Net::DHCP::Packet(Op => BOOTREPLY(),
				Htype => $_[0]->htype(),
				Hlen => $_[0]->hlen(),
				# Hops => $_[0]->hops(), # - not copyed in responce
				Xid => $_[0]->xid(),
				Secs => $_[0]->secs(),
				Flags => $_[0]->flags(),
				Ciaddr => $_[0]->ciaddr(),
				#Yiaddr => '0.0.0.0',
				Siaddr => $_[0]->siaddr(),
				Giaddr => $_[0]->giaddr(),
				Chaddr => $_[0]->chaddr(),
				DHO_DHCP_MESSAGE_TYPE() => DHCPACK, # must be owerwritten
				DHO_DHCP_SERVER_IDENTIFIER() => $DHCP_SERVER_ID
				);
return($dhcpresp);
}


sub BuffToHEX($) {
	my $buf = shift;

	#$buf =~ s/([[:^print:]])/ sprintf q[\x%02X], ord $1 /eg;  # printable text
	$buf =~ s/(.)/sprintf("%02x", ord($1))/eg;

return($buf);
}


# convert RelayAgent options to human readable
sub unpackRelayAgent(%) {
	my @SubOptions = @_;
	my $buf;

	for (my $i = 0; defined($SubOptions[$i]); $i += 2){
		$buf .= "($SubOptions[$i])=".BuffToHEX($SubOptions[($i+1)]).', ';
	}

return($buf);
}



# get relay agent options from dhcp packet
sub GetRelayAgentOptions($$$$$) {
	#my $dhcpreq = $_[0];
	#my $dhcp_opt82_vlan_id = $_[1];
	#my $dhcp_opt82_unit_id = $_[2];
	#my $dhcp_opt82_port_id = $_[3];
	#my $dhcp_opt82_chasis_id = $_[4];
	my @RelayAgent;

	$_[1] = '';
	$_[2] = '';
	$_[3] = '';
	$_[4] = '';

	if (defined($_[0]->getOptionRaw(DHO_DHCP_AGENT_OPTIONS())) == 0) {
		# no options, return
		return(0);
	}


	@RelayAgent = $_[0]->decodeRelayAgent($_[0]->getOptionRaw(DHO_DHCP_AGENT_OPTIONS()));

	for (my $i = 0; defined($RelayAgent[$i]); $i += 2){
		switch($RelayAgent[$i]){
		case 1 { # Circuit ID
			# first bytes must be: 00 04
			$_[1] = unpack('n', substr($RelayAgent[($i+1)], 2, 2)); # may be 's'
			$_[2] = unpack('C', substr($RelayAgent[($i+1)], 4, 1));
			$_[3] = unpack('C', substr($RelayAgent[($i+1)], 5, 1));
			}
		case 2 { # Remote ID
			# first bytes must be: 00 06 or 01 06
			$_[4] = FormatMAC(unpack("H*", substr($RelayAgent[($i+1)], 2, 6)));
			}
		}
	}

return(1);
}


# change mac addr format from "abcdefg" to "a:b:c:d:e:f:g"
sub FormatMAC {
	$_[0] =~ /([0-9a-f]{2})?([0-9a-f]{2})?([0-9a-f]{2})?([0-9a-f]{2})?([0-9a-f]{2})?([0-9a-f]{2})/i;
return(lc(join(':', $1, $2, $3, $4, $5, $6)));
}



# http://cpansearch.perl.org/src/SARENNER/Net-IPAddress-1.10/IPAddress.pm
#sub parseIPtoNumber { 
#return(unpack("N", pack("C4", split(/\./, $_[0])))); # C4 = CCCC
#}


# mask to bits
# http://milanweb.net/uni/old/scripting.html
sub subnetBits {
	my $m = unpack("N", pack("C4", split(/\./, $_[0]))); # parseIPtoNumber

	my $v = pack("L", $m);
	my $bcnt = 0;
	foreach (0..31) {
		if (vec($v, $_,1) == 1) {
			$bcnt++;
		}
	}
return($bcnt);
}


# (121) Classless-Static-Route / (249) MSFT - Classless route
# by Rozhuk Ivan 2011
# RFC 3442
# based on:
# http://www.linuxconfig.net/index.php/linux-manual/network/203-transfer-of-static-routes-to-dhcp.html
# http://www.linux.by/wiki/index.php/FAQ_DHCP_routes
# Syntax: mk_classless_routes_bin_mask($net, $mask, $gw)
# example: mk_classless_routes_bin_mask('192.168.1.0', '255.255.255.0', '192.168.0.254')
sub mk_classless_routes_bin_mask {
	#my $net = $_[0];
	#my $mask = $_[1];
	#my $gw = $_[2];

return(mk_classless_routes_bin_prefixlen($_[0], subnetBits($_[1]), $_[2]));
}

# Syntax: mk_classless_routes_bin_prefixlen($net, $prefixlen, $gw)
# example: mk_classless_routes_bin_prefixlen('192.168.1.0', 24, '192.168.0.254')
sub mk_classless_routes_bin_prefixlen {
	#my $net = $_[0];
	#my $prefixlen = $_[1];
	#my $gw = $_[2];
	my $str;

	$str = pack('C', $_[1]);

	if ($_[1] > 0) {
		my ($s1, $s2, $s3, $s4) = split(/\./, $_[0]);
		$str .= pack('C', $s1);
		$str .= pack('C', $s2) if ($_[1] > 8);
		$str .= pack('C', $s3) if ($_[1] > 16);
		$str .= pack('C', $s4) if ($_[1] > 24);
	}

	$str .= pack('CCCC', split(/\./, $_[2]));

return($str);
}







sub handle_discover {
	#my $dbh = $_[0];
	#my $fromaddr  = $_[1];
	#my $dhcpreq = $_[2];
	my ($dhcpresp);


	$dhcpresp = GenDHCPRespPkt($_[2]);
	$dhcpresp->{options}->{DHO_DHCP_MESSAGE_TYPE()} = pack('C', DHCPOFFER);

	if (db_get_requested_data($_[0], $_[2], $dhcpresp) == 1) {
		send_reply($_[1], $_[2], $dhcpresp);

		db_lease_offered($_[0], $_[2]);
	}else{# if AUTO_CONFIGURE (116) supported - send disable generate link local addr
	if (defined($_[2]->getOptionRaw(DHO_AUTO_CONFIGURE)) && $_[2]->getOptionValue(DHO_AUTO_CONFIGURE()) != 0) {
		$dhcpresp->addOptionValue(DHO_AUTO_CONFIGURE(), 0);

		send_reply($_[1], $_[2], $dhcpresp);
	}}
}



sub handle_request {
	#my $dbh = $_[0];
	#my $fromaddr  = $_[1];
	#my $dhcpreq = $_[2];
	my ($dhcpresp);


	$dhcpresp = GenDHCPRespPkt($_[2]);

	if (db_get_requested_data($_[0], $_[2], $dhcpresp) == 1) {
		if ( (defined($_[2]->getOptionRaw(DHO_DHCP_REQUESTED_ADDRESS())) && $_[2]->getOptionValue(DHO_DHCP_REQUESTED_ADDRESS()) ne $dhcpresp->yiaddr())
			|| (defined($_[2]->getOptionRaw(DHO_DHCP_REQUESTED_ADDRESS())) == 0 && $_[2]->ciaddr() ne $dhcpresp->yiaddr())
			) {
			# NAK if requested addr not equal IP addr in DB
			$dhcpresp->ciaddr('0.0.0.0');
			$dhcpresp->yiaddr('0.0.0.0');
			$dhcpresp->{options}->{DHO_DHCP_MESSAGE_TYPE()} = pack('C', DHCPNAK);

			db_lease_nak($_[0], $_[2]);
		}else{
			$dhcpresp->{options}->{DHO_DHCP_MESSAGE_TYPE()} = pack('C', DHCPACK);

			db_lease_success($_[0], $_[2]);
		}

		send_reply($_[1], $_[2], $dhcpresp);
	}
}




sub handle_decline {
	#my $dbh = $_[0];
	#my $fromaddr  = $_[1];
	#my $dhcpreq = $_[2];

	db_lease_decline($_[0], $_[2]);
}





sub handle_release {
	#my $dbh = $_[0];
	#my $fromaddr  = $_[1];
	#my $dhcpreq = $_[2];

	db_lease_release($_[0], $_[2]);
}





sub handle_inform {
	#my $dbh = $_[0];
	#my $fromaddr  = $_[1];
	#my $dhcpreq = $_[2];
	my ($dhcpreqparams, $dhcpresp);

	$dhcpresp = GenDHCPRespPkt($_[2]);
	$dhcpresp->{options}->{DHO_DHCP_MESSAGE_TYPE()} = pack('C', DHCPACK);

	if (db_get_requested_data($_[0], $_[2], $dhcpresp) == 0) {
		$dhcpreqparams = $_[2]->getOptionValue(DHO_DHCP_PARAMETER_REQUEST_LIST());
		static_data_to_reply($dhcpreqparams, $dhcpresp);
	}

	send_reply($_[1], $_[2], $dhcpresp);
}




sub static_data_to_reply {
	#my $dhcpreqparams = $_[0];
	#my $dhcpresp = $_[1];


	# do not add params if not requested
	if (defined($_[0]) == 0) {
		return();
	}


	if (index($_[0], DHO_ROUTER_DISCOVERY()) != -1) {
		$_[1]->addOptionValue(DHO_ROUTER_DISCOVERY(), 0);
	}

	if (index($_[0], DHO_NTP_SERVERS()) != -1) {
		$_[1]->addOptionValue(DHO_NTP_SERVERS(), '8.8.8.8 8.8.8.8');
	}

	if (index($_[0], DHO_VENDOR_ENCAPSULATED_OPTIONS()) != -1) {
		# 001 - NetBIOS over TCP/IP (NetBT): 00000002 (2) - disabled
		# 002 - Release DHCP Lease on Shutdown: 00000001 (1) - enabled
		# 255 - END
		$_[1]->addOptionRaw(DHO_VENDOR_ENCAPSULATED_OPTIONS(), "\x01\x04\x00\x00\x00\x02\x02\x04\x00\x00\x00\x01\xff");
	}

	if (index($_[0], DHO_NETBIOS_NODE_TYPE()) != -1) {
		$_[1]->addOptionValue(DHO_NETBIOS_NODE_TYPE(), 8); # H-Node
	}

}




sub db_get_requested_data {
	#my $dbh = $_[0];
	#my $dhcpreq = $_[1];
	#my $dhcpresp = $_[2];
	my ($mac, $sth, $dhcpreqparams, $result);
	my ($dhcp_opt82_vlan_id, $dhcp_opt82_unit_id, $dhcp_opt82_port_id, $dhcp_opt82_chasis_id);


	# change hw addr format
	$mac = FormatMAC(substr($_[1]->chaddr(), 0, (2 * $_[1]->hlen())));
	$dhcpreqparams = $_[1]->getOptionValue(DHO_DHCP_PARAMETER_REQUEST_LIST());

	$sth = $_[0]->prepare("");
	$sth->execute();
	if ($sth->rows()) {
		$result = $sth->fetchrow_hashref();

		$_[2]->yiaddr($result->{ip});

		db_data_to_reply($result, $dhcpreqparams, $_[2]);

		static_data_to_reply($dhcpreqparams, $_[2]);

		db_get_routing($_[0], $dhcpreqparams, $result->{subnet_id}, $_[2]);

		$sth->finish();
		return(1);
	}


	if (GetRelayAgentOptions($_[1], $dhcp_opt82_vlan_id, $dhcp_opt82_unit_id, $dhcp_opt82_port_id, $dhcp_opt82_chasis_id)) {
		# try work as traditional DHCP: find scope by opt82 info, then give some free addr

		if ($dhcp_opt82_chasis_id ne '') {
			$sth = $_[0]->prepare("SELECT
              	  `dhcp_lease_time`,
	                `dhcp_renewal`,
       	         `dhcp_rebind_time`,
              	  `mask`,
	                `gateway`,
       	         `dns1`,
              	  `dns2`,
	                `subnet_id`
	                `domain`
       		 FROM `subnets` as s
	       	 WHERE `vlan_id` = '$dhcp_opt82_vlan_id',
				AND `type` = 'guest'
			 LIMIT 1"
       	 	);
			$sth->execute();
			if ($sth->rows()) {
				$result = $sth->fetchrow_hashref();

				db_data_to_reply($result, $dhcpreqparams, $_[2]);

				static_data_to_reply($dhcpreqparams, $_[2]);

				db_get_routing($_[0], $dhcpreqparams, $result->{subnet_id}, $_[2]);

				#######################$_[2]->yiaddr($result->{ip});

				$sth->finish();
				return(1);
			}
			# subnet_id + dhcp client id = client lease
			# From: dhcp_guest_leases
		}
	}

	$sth->finish();

return(0);
}



sub db_data_to_reply {
	#my $result = $_[0];
	#my $dhcpreqparams = $_[1];
	#my $dhcpresp = $_[2];


	# http://www.tcpipguide.com/free/t_DHCPLeaseLifeCycleOverviewAllocationReallocationRe.htm
	# DHCPDiscover / DHCPRequest / DHCPOffer
	#$_[0]->{dhcp_lease_time} = 180;
	if (defined($_[0]->{dhcp_lease_time})) {
		$_[2]->addOptionValue(DHO_DHCP_LEASE_TIME(), $_[0]->{dhcp_lease_time});

		# function (typically 50%) of the full configured duration (or lease time) for a client's lease
		if (defined($_[0]->{dhcp_renewal})) {
			$_[2]->addOptionValue(DHO_DHCP_RENEWAL_TIME(), $_[0]->{dhcp_renewal});
		#}else{
		#	$_[2]->addOptionValue(DHO_DHCP_RENEWAL_TIME(), ($_[0]->{dhcp_lease_time}/2));
		}

		# function (typically 87.5%) of the full configured duration (or lease time) for a client's lease
		if (defined($_[0]->{dhcp_rebind_time})) {
			$_[2]->addOptionValue(DHO_DHCP_REBINDING_TIME(), $_[0]->{dhcp_rebind_time});
		#}else{
		#	$_[2]->addOptionValue(DHO_DHCP_REBINDING_TIME(), ($_[0]->{dhcp_lease_time}*7/8));
		}
	}


	# do not add params if not requested
	if (defined($_[1]) == 0) {
		return();
	}


	if (index($_[1], DHO_SUBNET_MASK()) != -1 && defined($_[0]->{mask})) {
		$_[2]->addOptionValue(DHO_SUBNET_MASK(), $_[0]->{mask});
	}

	if (index($_[1], DHO_ROUTERS()) != -1 && defined($_[0]->{gateway})) {
		$_[2]->addOptionValue(DHO_ROUTERS(), $_[0]->{gateway});
	}

	if (index($_[1], DHO_DOMAIN_NAME_SERVERS()) != -1 && defined($_[0]->{dns1})) {
		$_[2]->addOptionValue(DHO_DOMAIN_NAME_SERVERS(), "$_[0]->{dns1} $_[0]->{dns2}");
	}

	if (index($_[1], DHO_HOST_NAME()) != -1 && defined($_[0]->{hostname})) {
		$_[2]->addOptionValue(DHO_HOST_NAME(), $_[0]->{hostname});
	}

	if (index($_[1], DHO_DOMAIN_NAME()) != -1 && defined($_[0]->{domain})) {
		$_[2]->addOptionValue(DHO_DOMAIN_NAME(), $_[0]->{domain});
	}
}



sub db_get_routing {
	#my $dbh = $_[0];
	#my $dhcpreqparams = $_[1];
	#my $subnet_id = $_[2];
	#my $dhcpresp = $_[3];
	my $sth;
	my $opt33Enbled;
	my $optClasslessRoutesCode;


	# do not add routes if not requested
	if (defined($_[1]) == 0) {
		return();
	}

	$opt33Enbled = index($_[1], DHO_STATIC_ROUTES());
	if ($opt33Enbled == -1) {
		$opt33Enbled = undef;
	}

	$optClasslessRoutesCode = index($_[1], 121);
	if ($optClasslessRoutesCode == -1) {
		$optClasslessRoutesCode = index($_[1], 249);
		if ($optClasslessRoutesCode == -1) {
			$optClasslessRoutesCode = undef;
		}else{
			$optClasslessRoutesCode = 249;
		}
	}else{	
		$optClasslessRoutesCode = 121;
	}

	if (defined($opt33Enbled) == 0 && defined($optClasslessRoutesCode) == 0) {
		# nothink to do, return
		return();
	}

	$sth = $_[0]->prepare("
     SELECT
      `destination`,
      `mask`,
      `gateway`
      FROM `subnets_routes`
      WHERE `subnet_id` = '$_[2]'
     LIMIT 30"
    );
 
	$sth->execute();
	if ($sth->rows()) {
		my $ref;
		my $row;
		my $opt33_data = undef;# routes to single hosts
		my $opt_classless_routes_data = undef;# routes to nets

		$ref = $sth->fetchall_arrayref;
		foreach $row ( @{$ref} ) {
			if (defined($opt33Enbled) && @$row[1] eq '255.255.255.255') {
				# pack dst
				$opt33_data .= pack('CCCC', split(/\./, @$row[0]));

				# pack gw
				$opt33_data .= pack('CCCC', split(/\./, @$row[2]));
			}
			if (defined($optClasslessRoutesCode)) {
				$opt_classless_routes_data .= mk_classless_routes_bin_mask(@$row[0], @$row[1], @$row[2]);
			}
		}

		if (defined($opt33_data)) {# add option
			$_[3]->addOptionRaw(DHO_STATIC_ROUTES(), $opt33_data);
		}

		if (defined($opt_classless_routes_data)) {# add option
			$_[3]->addOptionRaw($optClasslessRoutesCode, $opt_classless_routes_data);
		}
	}
	$sth->finish();

}


sub db_lease_offered {
	#my $dbh = $_[0];
	#my $dhcpreq = $_[1];
	my ($mac, $sth);

	# change hw addr format
	$mac = FormatMAC(substr($_[1]->chaddr(), 0, (2 * $_[1]->hlen())));

       $sth = $_[0]->prepare("");

	$sth->execute();
	$sth->finish();

return(0);
}




sub db_lease_nak {
	#my $dbh = $_[0];
	#my $dhcpreq = $_[1];
	my ($mac, $sth);

	# change hw addr format
	$mac = FormatMAC(substr($_[1]->chaddr(), 0, (2 * $_[1]->hlen())));

	$sth = $_[0]->prepare("");

	$sth->execute();
	$sth->finish();

return(0);
}




sub db_lease_decline {
	#my $dbh = $_[0];
	#my $dhcpreq = $_[1];
	my ($mac, $sth);

	# change hw addr format
	$mac = FormatMAC(substr($_[1]->chaddr(), 0, (2 * $_[1]->hlen())));

	$sth = $_[0]->prepare("");
 
	$sth->execute();
	$sth->finish();

return(0);
}




sub db_lease_release {
	#my $dbh = $_[0];
	#my $dhcpreq = $_[1];
	my ($mac, $sth);

	# change hw addr format
	$mac = FormatMAC(substr($_[1]->chaddr(), 0, (2 * $_[1]->hlen())));

	$sth = $_[0]->prepare("");
 
	$sth->execute();
	$sth->finish();

return(0);
}




sub db_lease_success {
	#my $dbh = $_[0];
	#my $dhcpreq = $_[1];
	my ($mac, $sth, $result);
	my ($dhcp_opt82_vlan_id, $dhcp_opt82_unit_id, $dhcp_opt82_port_id, $dhcp_opt82_chasis_id);
	my ($dhcp_vendor_class, $dhcp_user_class);


	# change hw addr format
	$mac = FormatMAC(substr($_[1]->chaddr(), 0, (2 * $_[1]->hlen())));

	GetRelayAgentOptions($_[1], $dhcp_opt82_vlan_id, $dhcp_opt82_unit_id, $dhcp_opt82_port_id, $dhcp_opt82_chasis_id);


	if (defined($_[1]->getOptionRaw(DHO_VENDOR_CLASS_IDENTIFIER()))) {
		$dhcp_vendor_class = $_[1]->getOptionValue(DHO_VENDOR_CLASS_IDENTIFIER());
	}else{
		$dhcp_vendor_class = '';
	}
	if (defined($_[1]->getOptionRaw(DHO_USER_CLASS()))) {
		$dhcp_user_class = $_[1]->getOptionRaw(DHO_USER_CLASS());
	}else{
		$dhcp_user_class = '';
	}


	$sth = $_[0]->prepare("");

 
	$sth->execute();
	$sth->finish();
}




sub db_log_detailed {
	#my $dbh = $_[0];
	#my $dhcpreq = $_[1];
	my ($mac, $sth);
	my ($dhcp_opt82_vlan_id, $dhcp_opt82_unit_id, $dhcp_opt82_port_id, $dhcp_opt82_chasis_id);
	my ($client_ip, $gateway_ip, $client_ident, $requested_ip, $hostname, $dhcp_vendor_class, $dhcp_user_class);


	# change hw addr format
	$mac = FormatMAC(substr($_[1]->chaddr(), 0, (2 * $_[1]->hlen())));

	GetRelayAgentOptions($_[1], $dhcp_opt82_vlan_id, $dhcp_opt82_unit_id, $dhcp_opt82_port_id, $dhcp_opt82_chasis_id);


	$client_ip = $_[1]->ciaddr;
	$gateway_ip = $_[1]->giaddr;
	if (defined($_[1]->getOptionRaw(DHO_DHCP_CLIENT_IDENTIFIER()))) {
		$client_ident = BuffToHEX($_[1]->getOptionRaw(DHO_DHCP_CLIENT_IDENTIFIER()));
	}else{
		$client_ident = '';
	}
	if (defined($_[1]->getOptionRaw(DHO_DHCP_REQUESTED_ADDRESS()))) {
		$requested_ip = $_[1]->getOptionValue(DHO_DHCP_REQUESTED_ADDRESS());
	}else{
		$requested_ip = '';
	}
	if (defined($_[1]->getOptionRaw(DHO_HOST_NAME()))) {
		$hostname = $_[1]->getOptionValue(DHO_HOST_NAME());
	}else{
		$hostname = '';
	}
	if (defined($_[1]->getOptionRaw(DHO_VENDOR_CLASS_IDENTIFIER()))) {
		$dhcp_vendor_class = $_[1]->getOptionValue(DHO_VENDOR_CLASS_IDENTIFIER());
	}else{
		$dhcp_vendor_class = '';
	}
	if (defined($_[1]->getOptionRaw(DHO_USER_CLASS()))) {
		$dhcp_user_class = $_[1]->getOptionRaw(DHO_USER_CLASS());
	}else{
		$dhcp_user_class = '';
	}



	$sth = $_[0]->prepare("INSERT INTO `dhcp_log`
             (`created`,`client_mac`,`client_ip`,`gateway_ip`,`client_ident`,`requested_ip`,`hostname`,
              `dhcp_vendor_class`,`dhcp_user_class`,`dhcp_opt82_chasis_id`,`dhcp_opt82_unit_id`,
              `dhcp_opt82_port_id`, `dhcp_opt82_vlan_id`)
      VALUES(NOW(),'$mac','$client_ip','$gateway_ip','$client_ident','$requested_ip','$hostname',
              '$dhcp_vendor_class','$dhcp_user_class','$dhcp_opt82_chasis_id','$dhcp_opt82_unit_id',
              '$dhcp_opt82_port_id','$dhcp_opt82_vlan_id')
      ON DUPLICATE KEY UPDATE
              `client_ip` = '$client_ip',
              `gateway_ip` = 
		if('$gateway_ip' = '0.0.0.0',
		    `gateway_ip`,
		    '$gateway_ip'
		),
              `client_ident` = '$client_ident',
              `requested_ip` = '$requested_ip',
              `hostname` = '$hostname',
              `dhcp_vendor_class` = '$dhcp_vendor_class',
              `dhcp_user_class` = '$dhcp_user_class',
              `dhcp_opt82_chasis_id` =
		if('$dhcp_opt82_chasis_id' = '',
		    `dhcp_opt82_chasis_id`,
		    '$dhcp_opt82_chasis_id'
		),
              `dhcp_opt82_unit_id` =
		if('$dhcp_opt82_unit_id' = '',
		    `dhcp_opt82_unit_id`,
		    '$dhcp_opt82_unit_id'
		),
              `dhcp_opt82_port_id` =
		if('$dhcp_opt82_port_id' = '',
		    `dhcp_opt82_port_id`,
		    '$dhcp_opt82_port_id'
		),
              `dhcp_opt82_vlan_id` =
		if('$dhcp_opt82_vlan_id' = '',
		    `dhcp_opt82_vlan_id`,
		    '$dhcp_opt82_vlan_id'
		)
	");


	$sth->execute();
	$sth->finish();
}



помогите состряпать к нему базу!
пробовал так но не пашет!
http://forum.nag.ru/forum/index.php?sho ... t&p=653191

Хостинговая компания Host-Food.ru
Хостинг HostFood.ru
 

Услуги хостинговой компании Host-Food.ru

Хостинг HostFood.ru

Тарифы на хостинг в России, от 12 рублей: https://www.host-food.ru/tariffs/hosting/
Тарифы на виртуальные сервера (VPS/VDS/KVM) в РФ, от 189 руб.: https://www.host-food.ru/tariffs/virtualny-server-vps/
Выделенные сервера, Россия, Москва, от 2000 рублей (HP Proliant G5, Intel Xeon E5430 (2.66GHz, Quad-Core, 12Mb), 8Gb RAM, 2x300Gb SAS HDD, P400i, 512Mb, BBU):
https://www.host-food.ru/tariffs/vydelennyi-server-ds/
Недорогие домены в популярных зонах: https://www.host-food.ru/domains/

harmless
лейтенант
Сообщения: 719
Зарегистрирован: 2007-08-23 10:56:51
Откуда: Украина, г. Киев, г. Белая Церковь
Контактная информация:

Re: perl DHCP with mysql

Непрочитанное сообщение harmless » 2011-11-05 17:34:13

Как я понял в строках вида

Код: Выделить всё

$sth = $_[0]->prepare("");
должен присутствовать запрос к базе.