use Net::DHCP::Packet;
use Net::DHCP::Constants 0.67;
+use store;
+use format;
+
use server;
my $debug = server::debug;
use client;
-sub client_ip {
- my ( $mac ) = @_;
+sub client_mac_ip {
+ my ( $mac, $request_ip ) = @_;
+
+ if ( ! $mac ) {
+ warn "W: no mac in requiest\n";
+ return;
+ }
my $conf = $server::conf;
mkdir $conf unless -e $conf;
if ( $ip = client::ip_from_mac( $mac ) ) {
print "RENEW $mac $ip\n";
+ client::save_ip_mac( $ip, $mac );
return $ip;
- } else {
+ } elsif ( ip::in_dhcp_range( $request_ip ) || $request_ip eq '0.0.0.0' ) {
$ip = client::next_ip( $mac );
print "NEW $mac $ip\n";
+ } else {
+ $ip = $request_ip;
+ client::save_ip_mac( $ip, $mac );
+ warn "W: $ip out of server range $server::ip/$server::netmask\n";
}
return $ip;
sub process_packet {
my $sock = shift;
- server->refresh;
-
my $buf;
$sock->recv($buf, 1024);
my $size = 'empty';
return unless $buf;
my $dhcp = Net::DHCP::Packet->new($buf);
- $dhcp->comment( $transaction++ );
warn "recv: ", $dhcp->toString if $debug;
- my $mac = substr($dhcp->chaddr(),0,$dhcp->hlen()*2);
- my $ip = client_ip($mac);
+ $dhcp->comment( $transaction++ );
+
+ my $mac = format::mac( substr($dhcp->chaddr(),0,$dhcp->hlen()*2) );
+ my $ip = client_mac_ip($mac, $dhcp->ciaddr);
+
+ my $hostname = $dhcp->getOptionValue(DHO_HOST_NAME);
+ print "$ip ", client::conf( $ip => 'hostname', default => $hostname ), " >> /etc/hosts\n" if $hostname;
+
+ my $audit = { mac => $mac, ip => $ip, hostname => $hostname,
+ options => {
+ map {
+ ( $_ => $dhcp->getOptionValue( $_ ) )
+ } @{ $dhcp->{options_order} }
+ },
+ };
=for later
config::for_ip( $ip );
+ my $server = server::as_hash_for $ip;
+
my $packet = {
Op => BOOTREPLY(),
Hops => $dhcp->hops(),
Flags => $dhcp->flags(),
Ciaddr => $dhcp->ciaddr(),
Yiaddr => $ip,
- Siaddr => $server::ip,
+ Siaddr => $server->{ip},
Giaddr => $dhcp->giaddr(),
Chaddr => $dhcp->chaddr(),
File => $file,
- DHO_DHCP_SERVER_IDENTIFIER() => $server::ip, # busybox/udhcpc needs it but doesn't request
+ DHO_DHCP_SERVER_IDENTIFIER() => $server->{ip}, # busybox/udhcpc needs it but doesn't request
};
my $options = {
- DHO_SUBNET_MASK() => $server::netmask,
- DHO_ROUTERS() => $server::ip,
- DHO_DOMAIN_NAME() => $server::domain_name,
- DHO_NAME_SERVERS() => $server::ip,
- DHO_DOMAIN_NAME_SERVERS() => $server::ip,
+ DHO_SUBNET_MASK() => $server->{netmask},
+ DHO_ROUTERS() => ( $server->{gw} || $server->{ip} ),
+ DHO_DOMAIN_NAME() => $server->{domain},
+ DHO_NAME_SERVERS() => $server->{ip},
+ DHO_DOMAIN_NAME_SERVERS() => ( $server->{dns} || $server->{ip} ),
DHO_HOST_NAME() => client::conf( $ip, 'hostname' ),
- DHO_BROADCAST_ADDRESS() => $server::bcast,
+ DHO_BROADCAST_ADDRESS() => $server->{bcast},
# DHO_NTP_SERVERS() => '',
};
}
warn "W: options requested but missing: ",dump( @missing ),$/;
+ $audit->{requested} = [ @requested ];
+ $audit->{missing} = [ @missing ];
foreach my $opt ( 'magic', 'config_file', 'path_prefix', 'reboot_time' ) {
my $DH0 = eval 'DHO_PXELINUX_' . uc $opt;
my $messagetype = $dhcp->getOptionValue(DHO_DHCP_MESSAGE_TYPE());
+ my @type;
+
if ($messagetype eq DHCPDISCOVER()) {
- log::mac $mac, "DHCP DISCOVER";
$packet->{Comment} = $dhcp->comment();
$packet->{DHO_DHCP_MESSAGE_TYPE()} = DHCPOFFER();
+ @type = qw( discover offer );
} elsif ($messagetype eq DHCPREQUEST()) {
- my $requested_ip = $dhcp->getOptionValue(DHO_DHCP_REQUESTED_ADDRESS());
- log::mac $mac, "DHCP REQUEST $requested_ip $ip $file";
+ @type = qw( request );
+ my $requested_ip = $dhcp->getOptionValue(DHO_DHCP_REQUESTED_ADDRESS()) || $dhcp->ciaddr();
if ( $ip eq $requested_ip ) {
$packet->{DHO_DHCP_MESSAGE_TYPE()} = DHCPACK();
$packet->{DHO_DHCP_LEASE_TIME()} = 5 * 60; # 5 min
# $packet->{DHO_ROOT_PATH()} = '/exports/foobar';
+ $type[1] = 'ack';
} else {
$packet->{DHO_DHCP_MESSAGE_TYPE()} = DHCPNAK();
- $packet->{DHO_DHCP_MESSAGE()} = "Bad request, expected $ip";
+ $packet->{DHO_DHCP_MESSAGE()} = "Bad request, expected $ip got $requested_ip";
+ $type[1] = 'nak';
}
} elsif ($messagetype eq DHCPINFORM()) {
- log::mac $mac, "DHCP INFORM ignored";
+ @type = qw( inform ignored );
} else {
- log::mac $mac, "$messagetype igored (bootp?)";
+ @type = ( $messagetype, 'ignored' );
}
- warn ">> $mac == $ip server: $server::ip", $file ? " file: $file\n" : "\n" if $debug;
+ warn "# type ",dump @type;
+ $audit->{type} = [ @type ];
+
+ $audit->{response} = $packet;
$packet = new Net::DHCP::Packet( %$packet );
warn "send ",$packet->toString() if $debug;
- my $reply = IO::Socket::INET->new(
- LocalAddr => $server::ip,
- LocalPort => 67,
- Proto => "udp",
- Broadcast => 1,
- PeerAddr => '255.255.255.255',
- PeerPort => 68,
- Reuse => 1,
- ) or die "socket: $@";
+ if ( ip::in_dhcp_range( $ip ) ) {
+ my $buff = $packet->serialize();
+
+ my $reply = IO::Socket::INET->new(
+ LocalAddr => $server->{ip},
+ LocalPort => 67,
+ Proto => "udp",
+ Broadcast => 1,
+# PeerAddr => '255.255.255.255',
+ PeerAddr => $server->{bcast},
+ PeerPort => 68,
+ Reuse => 1,
+ ) or die "socket: $@";
+
+ $reply->send( $buff, 0 ) or die "Error sending: $!\n";
+ warn ">> $mac == $ip server: $server->{ip}", $file ? " file: $file\n" : "\n";
+ } else {
+ $audit->{error} = "$ip not in server range $server::ip $server::netmask - no packet sent";
+ warn $audit->{error};
+ }
- my $buff = $packet->serialize();
- $reply->send( $buff, 0 ) or die "Error sending: $!\n";
+ store::audit( @type, $audit );
# system("arp -s $ip $mac"),
print "DHCP listen on ",$sock->sockhost,":",$sock->sockport,"\n";
+ store::audit( 'start', { addr => $sock->sockhost, port => $sock->sockport } );
+
while (1) {
+ server->refresh;
process_packet $sock;
}
}