/[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

Annotation of /lib/PXElator/dhcpd.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 423 - (hide 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 dpavlin 48 package dhcpd;
2 dpavlin 1
3 dpavlin 44 =head1 dhcpd
4 dpavlin 1
5 dpavlin 44 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 dpavlin 1 use strict;
14     use warnings;
15    
16 dpavlin 22 use autodie;
17    
18 dpavlin 1 use IO::Socket::INET;
19 dpavlin 17 use File::Slurp;
20 dpavlin 1 use Data::Dump qw/dump/;
21    
22 dpavlin 44 use lib '..';
23 dpavlin 27 use Net::DHCP::Packet;
24     use Net::DHCP::Constants 0.67;
25 dpavlin 1
26 dpavlin 207 use CouchDB;
27 dpavlin 208 use format;
28 dpavlin 207
29 dpavlin 44 use server;
30 dpavlin 67 my $debug = server::debug;
31 dpavlin 1
32 dpavlin 44 if ( ! $server::ip ) {
33     my $server_ip = `/sbin/ifconfig`;
34 dpavlin 22 $server_ip =~ s/^.+?addr:([\d\.]+).*$/$1/gs;
35 dpavlin 44 $server::ip = $server_ip;
36 dpavlin 22 }
37    
38 dpavlin 110 warn "server ip $server::ip range: $server::ip_from - $server::ip_to\n";
39 dpavlin 1
40 dpavlin 168 use client;
41 dpavlin 17
42 dpavlin 244 sub client_mac_ip {
43     my ( $mac, $request_ip ) = @_;
44 dpavlin 1
45 dpavlin 267 if ( ! $mac ) {
46     warn "W: no mac in requiest\n";
47     return;
48     }
49    
50 dpavlin 156 my $conf = $server::conf;
51 dpavlin 17 mkdir $conf unless -e $conf;
52 dpavlin 1
53 dpavlin 177 my $ip;
54    
55 dpavlin 194 if ( $ip = client::ip_from_mac( $mac ) ) {
56 dpavlin 94 print "RENEW $mac $ip\n";
57 dpavlin 324 client::save_ip_mac( $ip, $mac );
58 dpavlin 17 return $ip;
59 dpavlin 413 } elsif ( ip::in_dhcp_range( $request_ip ) || $request_ip eq '0.0.0.0' ) {
60 dpavlin 200 $ip = client::next_ip( $mac );
61 dpavlin 177 print "NEW $mac $ip\n";
62 dpavlin 244 } 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 dpavlin 17 }
67 dpavlin 1
68     return $ip;
69     }
70    
71 dpavlin 110 use log;
72     use config;
73 dpavlin 129 use pxelinux;
74 dpavlin 160 use client;
75 dpavlin 110
76     our $file;
77 dpavlin 44 our $transaction = 0; # FIXME predictible transaction numbers
78 dpavlin 27
79 dpavlin 44 sub process_packet {
80     my $sock = shift;
81 dpavlin 1
82 dpavlin 128 server->refresh;
83    
84 dpavlin 1 my $buf;
85     $sock->recv($buf, 1024);
86 dpavlin 44 my $size = 'empty';
87     $size = length($buf) . ' bytes' if defined $buf;
88 dpavlin 1
89 dpavlin 94 print "packet from ",$sock->peerhost,":",$sock->peerport," $size\n" if $debug;
90 dpavlin 44 return unless $buf;
91 dpavlin 1
92 dpavlin 44 my $dhcp = Net::DHCP::Packet->new($buf);
93 dpavlin 1
94 dpavlin 67 warn "recv: ", $dhcp->toString if $debug;
95 dpavlin 1
96 dpavlin 260 $dhcp->comment( $transaction++ );
97    
98 dpavlin 313 my $mac = format::mac( substr($dhcp->chaddr(),0,$dhcp->hlen()*2) );
99 dpavlin 244 my $ip = client_mac_ip($mac, $dhcp->ciaddr);
100 dpavlin 1
101 dpavlin 201 my $hostname = $dhcp->getOptionValue(DHO_HOST_NAME);
102 dpavlin 313 print "$ip ", client::conf( $ip => 'hostname', default => $hostname ), " >> /etc/hosts\n" if $hostname;
103 dpavlin 201
104 dpavlin 313 my $audit = { mac => $mac, ip => $ip, hostname => $hostname,
105 dpavlin 260 options => {
106     map {
107     ( $_ => $dhcp->getOptionValue( $_ ) )
108     } @{ $dhcp->{options_order} }
109     },
110     };
111 dpavlin 207
112 dpavlin 44 =for later
113 dpavlin 6
114 dpavlin 44 my $user_class = $dhcp->getOptionValue(DHO_USER_CLASS());
115 dpavlin 1
116 dpavlin 44 if ( $user_class eq 'gPXE' ) {
117     $file = $gpxe_file;
118     } elsif ( ! $file ) {
119     $file = 'undionly.kpxe';
120     }
121 dpavlin 22
122 dpavlin 44 =cut
123    
124 dpavlin 110 config::for_ip( $ip );
125    
126 dpavlin 44 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 dpavlin 110 File => $file,
137 dpavlin 160 DHO_DHCP_SERVER_IDENTIFIER() => $server::ip, # busybox/udhcpc needs it but doesn't request
138 dpavlin 44 };
139    
140 dpavlin 160 my $options = {
141     DHO_SUBNET_MASK() => $server::netmask,
142     DHO_ROUTERS() => $server::ip,
143 dpavlin 393 DHO_DOMAIN_NAME() => $server::domain,
144 dpavlin 160 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 dpavlin 167 warn "options ",dump( $options ), ' requested: ',dump( @requested ) if $debug;
153    
154     my @missing;
155 dpavlin 160 foreach ( @requested ) {
156 dpavlin 167 if ( defined $options->{$_} ) {
157     $packet->{$_} = $options->{$_};
158     } else {
159     push @missing, $_;
160     }
161 dpavlin 160 }
162    
163 dpavlin 168 warn "W: options requested but missing: ",dump( @missing ),$/;
164 dpavlin 207 $audit->{requested} = [ @requested ];
165     $audit->{missing} = [ @missing ];
166 dpavlin 167
167 dpavlin 129 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 dpavlin 167 warn "pxelinux dhcp option $opt = $DH0 = $v\n" if $debug;
174 dpavlin 129 $packet->{ $DH0 } = $v;
175     }
176    
177 dpavlin 44 my $messagetype = $dhcp->getOptionValue(DHO_DHCP_MESSAGE_TYPE());
178    
179 dpavlin 423 my @type;
180    
181 dpavlin 44 if ($messagetype eq DHCPDISCOVER()) {
182     $packet->{Comment} = $dhcp->comment();
183     $packet->{DHO_DHCP_MESSAGE_TYPE()} = DHCPOFFER();
184 dpavlin 423 @type = qw( discover offser );
185 dpavlin 44 } elsif ($messagetype eq DHCPREQUEST()) {
186 dpavlin 423 @type = qw( request );
187     my $requested_ip = $dhcp->getOptionValue(DHO_DHCP_REQUESTED_ADDRESS()) || $dhcp->ciaddr();
188 dpavlin 44 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 dpavlin 423 $type[1] = 'ack';
193 dpavlin 27 } else {
194 dpavlin 44 $packet->{DHO_DHCP_MESSAGE_TYPE()} = DHCPNAK();
195 dpavlin 423 $packet->{DHO_DHCP_MESSAGE()} = "Bad request, expected $ip got $requested_ip";
196     $type[1] = 'nak';
197 dpavlin 27 }
198 dpavlin 44 } elsif ($messagetype eq DHCPINFORM()) {
199 dpavlin 423 @type = qw( inform ignored );
200 dpavlin 44 } else {
201 dpavlin 423 @type = ( $messagetype, 'ignored' );
202 dpavlin 44 }
203 dpavlin 27
204 dpavlin 423 warn "# type ",dump @type;
205     $audit->{type} = [ @type ];
206    
207 dpavlin 110 warn ">> $mac == $ip server: $server::ip", $file ? " file: $file\n" : "\n" if $debug;
208 dpavlin 207 $audit->{response} = $packet;
209 dpavlin 27
210 dpavlin 44 $packet = new Net::DHCP::Packet( %$packet );
211 dpavlin 67 warn "send ",$packet->toString() if $debug;
212 dpavlin 27
213 dpavlin 413 if ( ip::in_dhcp_range( $ip ) ) {
214 dpavlin 244 my $buff = $packet->serialize();
215 dpavlin 207
216 dpavlin 244 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 dpavlin 1
226 dpavlin 244 $reply->send( $buff, 0 ) or die "Error sending: $!\n";
227     } else {
228     $audit->{error} = "$ip our of our range $server::ip $server::netmask";
229 dpavlin 413 warn $audit->{error};
230 dpavlin 244 }
231 dpavlin 1
232 dpavlin 423 CouchDB::audit( @type, $audit );
233 dpavlin 244
234 dpavlin 44 # system("arp -s $ip $mac"),
235 dpavlin 1
236 dpavlin 44 }
237 dpavlin 1
238 dpavlin 44 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 dpavlin 232 CouchDB::audit( 'start', { addr => $sock->sockhost, port => $sock->sockport } );
255 dpavlin 207
256 dpavlin 44 while (1) {
257     process_packet $sock;
258 dpavlin 1 }
259 dpavlin 44 }
260 dpavlin 1
261 dpavlin 45 warn "loaded";
262    
263 dpavlin 44 1;

  ViewVC Help
Powered by ViewVC 1.1.26