/[pxelator]/lib/PXElator/dhcpd.pm
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Contents of /lib/PXElator/dhcpd.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 423 - (show annotations)
Sat Sep 12 22:18:34 2009 UTC (14 years, 7 months ago) by dpavlin
File size: 6133 byte(s)
fix dhcp client address so we don't send nak any more when client allready
knows his ip address

create two element type which include message type from request and response

1 package dhcpd;
2
3 =head1 dhcpd
4
5 start with:
6
7 perl -Ilib/PXElator -Ilib -Mdhcpd -e start
8
9 based on L<http://www.perlmonks.org/index.pl?node_id=325248>
10
11 =cut
12
13 use strict;
14 use warnings;
15
16 use autodie;
17
18 use IO::Socket::INET;
19 use File::Slurp;
20 use Data::Dump qw/dump/;
21
22 use lib '..';
23 use Net::DHCP::Packet;
24 use Net::DHCP::Constants 0.67;
25
26 use CouchDB;
27 use format;
28
29 use server;
30 my $debug = server::debug;
31
32 if ( ! $server::ip ) {
33 my $server_ip = `/sbin/ifconfig`;
34 $server_ip =~ s/^.+?addr:([\d\.]+).*$/$1/gs;
35 $server::ip = $server_ip;
36 }
37
38 warn "server ip $server::ip range: $server::ip_from - $server::ip_to\n";
39
40 use client;
41
42 sub client_mac_ip {
43 my ( $mac, $request_ip ) = @_;
44
45 if ( ! $mac ) {
46 warn "W: no mac in requiest\n";
47 return;
48 }
49
50 my $conf = $server::conf;
51 mkdir $conf unless -e $conf;
52
53 my $ip;
54
55 if ( $ip = client::ip_from_mac( $mac ) ) {
56 print "RENEW $mac $ip\n";
57 client::save_ip_mac( $ip, $mac );
58 return $ip;
59 } elsif ( ip::in_dhcp_range( $request_ip ) || $request_ip eq '0.0.0.0' ) {
60 $ip = client::next_ip( $mac );
61 print "NEW $mac $ip\n";
62 } else {
63 $ip = $request_ip;
64 client::save_ip_mac( $ip, $mac );
65 warn "W: $ip our of server range $server::ip $server::netmask\n";
66 }
67
68 return $ip;
69 }
70
71 use log;
72 use config;
73 use pxelinux;
74 use client;
75
76 our $file;
77 our $transaction = 0; # FIXME predictible transaction numbers
78
79 sub process_packet {
80 my $sock = shift;
81
82 server->refresh;
83
84 my $buf;
85 $sock->recv($buf, 1024);
86 my $size = 'empty';
87 $size = length($buf) . ' bytes' if defined $buf;
88
89 print "packet from ",$sock->peerhost,":",$sock->peerport," $size\n" if $debug;
90 return unless $buf;
91
92 my $dhcp = Net::DHCP::Packet->new($buf);
93
94 warn "recv: ", $dhcp->toString if $debug;
95
96 $dhcp->comment( $transaction++ );
97
98 my $mac = format::mac( substr($dhcp->chaddr(),0,$dhcp->hlen()*2) );
99 my $ip = client_mac_ip($mac, $dhcp->ciaddr);
100
101 my $hostname = $dhcp->getOptionValue(DHO_HOST_NAME);
102 print "$ip ", client::conf( $ip => 'hostname', default => $hostname ), " >> /etc/hosts\n" if $hostname;
103
104 my $audit = { mac => $mac, ip => $ip, hostname => $hostname,
105 options => {
106 map {
107 ( $_ => $dhcp->getOptionValue( $_ ) )
108 } @{ $dhcp->{options_order} }
109 },
110 };
111
112 =for later
113
114 my $user_class = $dhcp->getOptionValue(DHO_USER_CLASS());
115
116 if ( $user_class eq 'gPXE' ) {
117 $file = $gpxe_file;
118 } elsif ( ! $file ) {
119 $file = 'undionly.kpxe';
120 }
121
122 =cut
123
124 config::for_ip( $ip );
125
126 my $packet = {
127 Op => BOOTREPLY(),
128 Hops => $dhcp->hops(),
129 Xid => $dhcp->xid(),
130 Flags => $dhcp->flags(),
131 Ciaddr => $dhcp->ciaddr(),
132 Yiaddr => $ip,
133 Siaddr => $server::ip,
134 Giaddr => $dhcp->giaddr(),
135 Chaddr => $dhcp->chaddr(),
136 File => $file,
137 DHO_DHCP_SERVER_IDENTIFIER() => $server::ip, # busybox/udhcpc needs it but doesn't request
138 };
139
140 my $options = {
141 DHO_SUBNET_MASK() => $server::netmask,
142 DHO_ROUTERS() => $server::ip,
143 DHO_DOMAIN_NAME() => $server::domain,
144 DHO_NAME_SERVERS() => $server::ip,
145 DHO_DOMAIN_NAME_SERVERS() => $server::ip,
146 DHO_HOST_NAME() => client::conf( $ip, 'hostname' ),
147 DHO_BROADCAST_ADDRESS() => $server::bcast,
148 # DHO_NTP_SERVERS() => '',
149 };
150
151 my @requested = split(/\s/, $dhcp->getOptionValue(DHO_DHCP_PARAMETER_REQUEST_LIST));
152 warn "options ",dump( $options ), ' requested: ',dump( @requested ) if $debug;
153
154 my @missing;
155 foreach ( @requested ) {
156 if ( defined $options->{$_} ) {
157 $packet->{$_} = $options->{$_};
158 } else {
159 push @missing, $_;
160 }
161 }
162
163 warn "W: options requested but missing: ",dump( @missing ),$/;
164 $audit->{requested} = [ @requested ];
165 $audit->{missing} = [ @missing ];
166
167 foreach my $opt ( 'magic', 'config_file', 'path_prefix', 'reboot_time' ) {
168 my $DH0 = eval 'DHO_PXELINUX_' . uc $opt;
169 warn "DH0: $@" if $@;
170 my $v = eval "\$pxelinux::$opt";
171 warn "v: $@" if $@;
172 next unless defined $v;
173 warn "pxelinux dhcp option $opt = $DH0 = $v\n" if $debug;
174 $packet->{ $DH0 } = $v;
175 }
176
177 my $messagetype = $dhcp->getOptionValue(DHO_DHCP_MESSAGE_TYPE());
178
179 my @type;
180
181 if ($messagetype eq DHCPDISCOVER()) {
182 $packet->{Comment} = $dhcp->comment();
183 $packet->{DHO_DHCP_MESSAGE_TYPE()} = DHCPOFFER();
184 @type = qw( discover offser );
185 } elsif ($messagetype eq DHCPREQUEST()) {
186 @type = qw( request );
187 my $requested_ip = $dhcp->getOptionValue(DHO_DHCP_REQUESTED_ADDRESS()) || $dhcp->ciaddr();
188 if ( $ip eq $requested_ip ) {
189 $packet->{DHO_DHCP_MESSAGE_TYPE()} = DHCPACK();
190 $packet->{DHO_DHCP_LEASE_TIME()} = 5 * 60; # 5 min
191 # $packet->{DHO_ROOT_PATH()} = '/exports/foobar';
192 $type[1] = 'ack';
193 } else {
194 $packet->{DHO_DHCP_MESSAGE_TYPE()} = DHCPNAK();
195 $packet->{DHO_DHCP_MESSAGE()} = "Bad request, expected $ip got $requested_ip";
196 $type[1] = 'nak';
197 }
198 } elsif ($messagetype eq DHCPINFORM()) {
199 @type = qw( inform ignored );
200 } else {
201 @type = ( $messagetype, 'ignored' );
202 }
203
204 warn "# type ",dump @type;
205 $audit->{type} = [ @type ];
206
207 warn ">> $mac == $ip server: $server::ip", $file ? " file: $file\n" : "\n" if $debug;
208 $audit->{response} = $packet;
209
210 $packet = new Net::DHCP::Packet( %$packet );
211 warn "send ",$packet->toString() if $debug;
212
213 if ( ip::in_dhcp_range( $ip ) ) {
214 my $buff = $packet->serialize();
215
216 my $reply = IO::Socket::INET->new(
217 LocalAddr => $server::ip,
218 LocalPort => 67,
219 Proto => "udp",
220 Broadcast => 1,
221 PeerAddr => '255.255.255.255',
222 PeerPort => 68,
223 Reuse => 1,
224 ) or die "socket: $@";
225
226 $reply->send( $buff, 0 ) or die "Error sending: $!\n";
227 } else {
228 $audit->{error} = "$ip our of our range $server::ip $server::netmask";
229 warn $audit->{error};
230 }
231
232 CouchDB::audit( @type, $audit );
233
234 # system("arp -s $ip $mac"),
235
236 }
237
238 sub start {
239
240 my $sock = IO::Socket::INET->new(
241 LocalPort => 67,
242 # LocalAddr => 'localhost',
243 # LocalAddr => '10.0.0.100',
244 LocalAddr => '0.0.0.0',
245 Proto => 'udp',
246 ReuseAddr => 1,
247 # PeerPort => getservbyname('bootpc', 'udp'),
248 Broadcast => 1,
249 Type => SOCK_DGRAM,
250 ) or die "Failed to bind to socket: $@";
251
252 print "DHCP listen on ",$sock->sockhost,":",$sock->sockport,"\n";
253
254 CouchDB::audit( 'start', { addr => $sock->sockhost, port => $sock->sockport } );
255
256 while (1) {
257 process_packet $sock;
258 }
259 }
260
261 warn "loaded";
262
263 1;

  ViewVC Help
Powered by ViewVC 1.1.26