]> git.ipfire.org Git - people/pmueller/ipfire-2.x.git/blob - html/cgi-bin/dhcp.cgi
git-svn-id: http://svn.ipfire.org/svn/ipfire/IPFire/source@16 ea5c0bd1-69bd-2848...
[people/pmueller/ipfire-2.x.git] / html / cgi-bin / dhcp.cgi
1 #!/usr/bin/perl
2 #
3 # SmoothWall CGIs
4 #
5 # This code is distributed under the terms of the GPL
6 #
7 # (c) The SmoothWall Team
8 #
9 # Copyright (C) 01-02-2002 Graham Smith <grhm@grhm.co.uk>
10 # - Fixed DHCP Leases added
11 #
12 # $Id: dhcp.cgi,v 1.14.2.81 2006/01/20 12:05:29 franck78 Exp $
13 #
14 # Franck -rewrite for two or more interface
15 # nov/2004 -check range is in correct subnet
16 # -add NTP option
17 # -add display sorting of actives leases
18 # dec/2004 -add comment field to fixed leases
19 #
20 # to do : choose a correct format for displaying dates
21 #
22 use strict;
23
24 # enable only the following on debugging purpose
25 #use warnings;
26 #use CGI::Carp 'fatalsToBrowser';
27
28 require 'CONFIG_ROOT/general-functions.pl';
29 require "${General::swroot}/lang.pl";
30 require "${General::swroot}/header.pl";
31 #workaround to suppress a warning when a variable is used only once
32 my @dummy = ( ${Header::colouryellow} );
33 undef (@dummy);
34
35 our %dhcpsettings=();
36 our %netsettings=();
37 my %mainsettings=();
38 my %timesettings=();
39 my $setting = "${General::swroot}/dhcp/settings";
40 our $filename1 = "${General::swroot}/dhcp/advoptions"; # Field separator is TAB in this file (comma is standart)
41 # because we need commas in the some data
42 our $filename2 = "${General::swroot}/dhcp/fixleases";
43 our $filename3 = "${General::swroot}/dhcp/advoptions-list"; # Describe the allowed syntax for dhcp options
44 my $errormessage = '';
45 my $warnNTPmessage = '';
46 my @nosaved=();
47
48 #Basic syntax allowed for new Option definition. Not implemented: RECORDS & array of RECORDS
49 our $OptionTypes = 'boolean|((un)?signed )?integer (8|16|32)|ip-address|text|string|encapsulate \w+|array of ip-address';
50
51 &Header::showhttpheaders();
52 our @ITFs=('GREEN','BLUE');
53
54 #Settings1 for the first screen box
55 foreach my $itf (@ITFs) {
56 $dhcpsettings{"ENABLE_${itf}"} = 'off';
57 $dhcpsettings{"ENABLEBOOTP_${itf}"} = 'off';
58 $dhcpsettings{"START_ADDR_${itf}"} = '';
59 $dhcpsettings{"END_ADDR_${itf}"} = '';
60 $dhcpsettings{"DOMAIN_NAME_${itf}"} = '';
61 $dhcpsettings{"DEFAULT_LEASE_TIME_${itf}"} = '';
62 $dhcpsettings{"MAX_LEASE_TIME_${itf}"} = '';
63 $dhcpsettings{"WINS1_${itf}"} = '';
64 $dhcpsettings{"WINS2_${itf}"} = '';
65 $dhcpsettings{"DNS1_${itf}"} = '';
66 $dhcpsettings{"DNS2_${itf}"} = '';
67 $dhcpsettings{"NTP1_${itf}"} = '';
68 $dhcpsettings{"NTP2_${itf}"} = '';
69 }
70
71 $dhcpsettings{'SORT_FLEASELIST'} = 'FIPADDR';
72 $dhcpsettings{'SORT_LEASELIST'} = 'IPADDR';
73
74 #Settings2 for editing the multi-line list
75 #Must not be saved with writehash !
76 $dhcpsettings{'FIX_MAC'} = '';
77 $dhcpsettings{'FIX_ADDR'} = '';
78 $dhcpsettings{'FIX_ENABLED'} = 'off';
79 $dhcpsettings{'FIX_NEXTADDR'} = '';
80 $dhcpsettings{'FIX_FILENAME'} = '';
81 $dhcpsettings{'FIX_ROOTPATH'} = '';
82 $dhcpsettings{'FIX_REMARK'} = '';
83 $dhcpsettings{'ACTION'} = '';
84 $dhcpsettings{'KEY1'} = '';
85 $dhcpsettings{'KEY2'} = '';
86 @nosaved=('FIX_MAC','FIX_ADDR','FIX_ENABLED','FIX_NEXTADDR',
87 'FIX_FILENAME','FIX_ROOTPATH','FIX_REMARK');
88
89 $dhcpsettings{'ADVOPT_ENABLED'} = '';
90 $dhcpsettings{'ADVOPT_NAME'} = '';
91 $dhcpsettings{'ADVOPT_DATA'} = '';
92 unshift (@nosaved,'ADVOPT_ENABLED','ADVOPT_NAME','ADVOPT_DATA');
93 foreach my $itf (@ITFs) {
94 $dhcpsettings{"ADVOPT_SCOPE_${itf}"} = 'off';
95 unshift (@nosaved, "ADVOPT_SCOPE_${itf}");
96 }
97
98 # Read Ipcop settings
99 &General::readhash("${General::swroot}/ethernet/settings", \%netsettings);
100 &General::readhash("${General::swroot}/main/settings", \%mainsettings);
101 &General::readhash("${General::swroot}/time/settings", \%timesettings);
102
103 #Get GUI values
104 &Header::getcgihash(\%dhcpsettings);
105
106 open(FILE, "$filename1") or die 'Unable to open dhcp advanced options file.';
107 our @current1 = <FILE>;
108 close(FILE);
109 # Extract OptionDefinition
110 foreach my $line (@current1) {
111 #chomp($line); # remove newline #don't know why, but this remove newline in @current1 .... !
112 my @temp = split(/\t/,$line);
113 AddNewOptionDefinition ($temp[1] . ' ' . $temp[2]);
114 }
115
116 open(FILE, "$filename2") or die 'Unable to open fixed leases file.';
117 our @current2 = <FILE>;
118 close(FILE);
119
120 # Check Settings1 first because they are needed by &buildconf
121 if ($dhcpsettings{'ACTION'} eq $Lang::tr{'save'}) {
122 foreach my $itf (@ITFs) {
123 if ($dhcpsettings{"ENABLE_${itf}"} eq 'on' ) {
124 # "Start" is defined, need "End" and vice versa
125 if ($dhcpsettings{"START_ADDR_${itf}"}) {
126 if (!(&General::validip($dhcpsettings{"START_ADDR_${itf}"}))) {
127 $errormessage = "DHCP on ${itf}: " . $Lang::tr{'invalid start address'};
128 goto ERROR;
129 }
130 if (!$dhcpsettings{"END_ADDR_${itf}"}) {
131 $errormessage = "DHCP on ${itf}: " . $Lang::tr{'invalid end address'};
132 goto ERROR;
133 }
134 if (! &General::IpInSubnet ( $dhcpsettings{"START_ADDR_${itf}"},
135 $netsettings{"${itf}_NETADDRESS"},
136 $netsettings{"${itf}_NETMASK"})) {
137 $errormessage = "DHCP on ${itf}: " . $Lang::tr{'invalid start address'};
138 goto ERROR;
139 }
140 }
141
142 if ($dhcpsettings{"END_ADDR_${itf}"}) {
143 if (!(&General::validip($dhcpsettings{"END_ADDR_${itf}"}))) {
144 $errormessage = "DHCP on ${itf}: " . $Lang::tr{'invalid end address'};
145 goto ERROR;
146 }
147 if (!$dhcpsettings{"START_ADDR_${itf}"}) {
148 $errormessage = "DHCP on ${itf}: " . $Lang::tr{'invalid start address'};
149 goto ERROR;
150 }
151 if (! &General::IpInSubnet ( $dhcpsettings{"END_ADDR_${itf}"},
152 $netsettings{"${itf}_NETADDRESS"},
153 $netsettings{"${itf}_NETMASK"})) {
154 $errormessage = "DHCP on ${itf}: " . $Lang::tr{'invalid end address'};
155 goto ERROR;
156 }
157 #swap if necessary! (support 255.255.0.0 range, I doubt we need more) GE
158 my @startoct = split (/\./, $dhcpsettings{"START_ADDR_${itf}"});
159 my @endoct = split (/\./, $dhcpsettings{"END_ADDR_${itf}"});
160 if ( $endoct[2]*256+$endoct[3] < $startoct[2]*256+$startoct[3] ) {
161 ($dhcpsettings{"START_ADDR_${itf}"},$dhcpsettings{"END_ADDR_${itf}"}) =
162 ($dhcpsettings{"END_ADDR_${itf}"},$dhcpsettings{"START_ADDR_${itf}"});
163 }
164 }
165
166 if (!($dhcpsettings{"DEFAULT_LEASE_TIME_${itf}"} =~ /^\d+$/)) {
167 $errormessage = "DHCP on ${itf}: " . $Lang::tr{'invalid default lease time'} . $dhcpsettings{'DEFAULT_LEASE_TIME_${itf}'};
168 goto ERROR;
169 }
170
171 if (!($dhcpsettings{"MAX_LEASE_TIME_${itf}"} =~ /^\d+$/)) {
172 $errormessage = "DHCP on ${itf}: " . $Lang::tr{'invalid max lease time'} . $dhcpsettings{'MAX_LEASE_TIME_${itf}'};
173 goto ERROR;
174 }
175
176 if ($dhcpsettings{"DNS1_${itf}"}) {
177 if (!(&General::validip($dhcpsettings{"DNS1_${itf}"}))) {
178 $errormessage = "DHCP on ${itf}: " . $Lang::tr{'invalid primary dns'};
179 goto ERROR;
180 }
181 }
182 if ($dhcpsettings{"DNS2_${itf}"}) {
183 if (!(&General::validip($dhcpsettings{"DNS2_${itf}"}))) {
184 $errormessage = "DHCP on ${itf}: " . $Lang::tr{'invalid secondary dns'};
185 goto ERROR;
186 }
187 if (! $dhcpsettings{"DNS1_${itf}"}) {
188 $errormessage = "DHCP on ${itf}: " . $Lang::tr{'cannot specify secondary dns without specifying primary'};
189 goto ERROR;
190 }
191 }
192
193 if ($dhcpsettings{"WINS1_${itf}"}) {
194 if (!(&General::validip($dhcpsettings{"WINS1_${itf}"}))) {
195 $errormessage = "DHCP on ${itf}: " . $Lang::tr{'invalid wins address'};
196 goto ERROR;
197 }
198 }
199 if ($dhcpsettings{"WINS2_${itf}"}) {
200 if (!(&General::validip($dhcpsettings{"WINS2_${itf}"}))) {
201 $errormessage = "DHCP on ${itf}: " . $Lang::tr{'invalid wins address'};
202 goto ERROR;
203 }
204 if (! $dhcpsettings{"WINS1_${itf}"} ) {
205 $errormessage = "DHCP on ${itf}: " . $Lang::tr{'cannot specify secondary wins without specifying primary'};
206 goto ERROR;
207 }
208 }
209
210 if ($dhcpsettings{"NTP1_${itf}"}) {
211 if (!(&General::validip($dhcpsettings{"NTP1_${itf}"}))) {
212 $errormessage = "DHCP on ${itf}: " . $Lang::tr{'invalid primary ntp'};
213 goto ERROR;
214 }
215 if ($dhcpsettings{"NTP1_${itf}"} eq $netsettings{"${itf}_ADDRESS"} && ($timesettings{'ENABLECLNTP'} ne 'on')) {
216 $warnNTPmessage = "DHCP on ${itf}: " . $Lang::tr{'local ntp server specified but not enabled'};
217 #goto ERROR;
218 }
219 }
220 if ($dhcpsettings{"NTP2_${itf}"}) {
221 if (!(&General::validip($dhcpsettings{"NTP2_${itf}"}))) {
222 $errormessage = "DHCP on ${itf}: " . $Lang::tr{'invalid secondary ntp'};
223 goto ERROR;
224 }
225 if ($dhcpsettings{"NTP2_${itf}"} eq $netsettings{"${itf}_ADDRESS"} && ($timesettings{'ENABLECLNTP'} ne 'on')) {
226 $warnNTPmessage = "DHCP on ${itf}: " . $Lang::tr{'local ntp server specified but not enabled'};
227 #goto ERROR;
228 }
229 if (! $dhcpsettings{"NTP1_${itf}"}) {
230 $errormessage = "DHCP on ${itf}: " . $Lang::tr{'cannot specify secondary ntp without specifying primary'};
231 goto ERROR;
232 }
233 }
234 } # enabled
235 }#loop interface verify
236
237 map (delete ($dhcpsettings{$_}) ,@nosaved,'ACTION','KEY1','KEY2'); # Must not be saved
238 &General::writehash($setting, \%dhcpsettings); # Save good settings
239 $dhcpsettings{'ACTION'} = $Lang::tr{'save'}; # create an 'ACTION'
240 map ($dhcpsettings{$_} = '',@nosaved,'KEY1','KEY2'); # and reinit vars to empty
241 &buildconf;
242 ERROR: # Leave the faulty field untouched
243 } else {
244 &General::readhash($setting, \%dhcpsettings); # Get saved settings and reset to good if needed
245 }
246
247 ## Sorting of fixed leases
248 if ($ENV{'QUERY_STRING'} =~ /^FETHER|^FIPADDR/ ) {
249 my $newsort=$ENV{'QUERY_STRING'};
250 my $act=$dhcpsettings{'SORT_FLEASELIST'};
251 #Reverse actual sort ?
252 if ($act =~ $newsort) {
253 my $Rev='';
254 if ($act !~ 'Rev') {
255 $Rev='Rev';
256 }
257 $newsort.=$Rev;
258 }
259 $dhcpsettings{'SORT_FLEASELIST'}=$newsort;
260 map (delete ($dhcpsettings{$_}) ,@nosaved,'ACTION','KEY1','KEY2'); # Must never be saved
261 &General::writehash($setting, \%dhcpsettings);
262 &sortcurrent2;
263 $dhcpsettings{'ACTION'} = 'SORT'; # create an 'ACTION'
264 map ($dhcpsettings{$_} = '',@nosaved,'KEY1','KEY2');# and reinit vars to empty
265 }
266
267 #Sorting of allocated leases
268 &Header::CheckSortOrder;
269
270
271 ## Now manipulate the two multi-line list with Settings2.
272 # '1' suffix is for ADVANCED OPTIONS
273 # '2' suffix is for FIXED LEASES
274
275 # Toggle enable/disable field on specified options.
276
277 if ($dhcpsettings{'ACTION'} eq $Lang::tr{'toggle enable disable'}.'1') {
278 #move out new line
279 chomp(@current1[$dhcpsettings{'KEY1'}]);
280 my @temp = split(/\t/,@current1[$dhcpsettings{'KEY1'}]); #use TAB separator !
281 $temp[0] = $temp[0] eq 'on' ? '' : 'on'; # Toggle the field
282 @current1[$dhcpsettings{'KEY1'}] = join ("\t",@temp)."\n";
283 $dhcpsettings{'KEY1'} = ''; # End edit mode
284 &General::log($Lang::tr{'dhcp advopt modified'});
285 open(FILE, ">$filename1") or die 'Unable to open dhcp advanced options file.';
286 print FILE @current1;
287 close(FILE);
288
289 #Write changes to dhcpd.conf.
290 &buildconf;
291 }
292
293
294
295 if ($dhcpsettings{'ACTION'} eq $Lang::tr{'add'}.'1' &&
296 $dhcpsettings{'SUBMIT'} ne $Lang::tr{'dhcp advopt help'}) {
297 $dhcpsettings{'ADVOPT_NAME'} =~ s/[^ \w-]//g; # prevent execution of code by removing everything except letters/space
298 $dhcpsettings{'ADVOPT_DATA'} =~ s/`//g; # back tik ` ? not allowed !
299
300 if ($dhcpsettings{'ADVOPT_DATA'} eq '') {
301 $errormessage=$Lang::tr{'dhcp advopt blank value'};
302 }
303
304 # Test for a new option definition string (join field name & data)
305 if (ExistNewOptionDefinition ($dhcpsettings{'ADVOPT_NAME'} . ' ' . $dhcpsettings{'ADVOPT_DATA'})) {
306 #only edit permitted if option definition exists
307 $errormessage = $Lang::tr{'dhcp advopt definition exists'} if ($dhcpsettings{'KEY1'} eq '');
308 $dhcpsettings{'ADVOPT_ENABLED'} = 'on'; # force active
309 map ($dhcpsettings{"ADVOPT_SCOPE_$_"} = 'off', @ITFs); # force global
310 } elsif (AddNewOptionDefinition ($dhcpsettings{'ADVOPT_NAME'} . ' ' . $dhcpsettings{'ADVOPT_DATA'})) {
311 #was a new option definition
312 $dhcpsettings{'ADVOPT_ENABLED'} = 'on'; # force active
313 map ($dhcpsettings{"ADVOPT_SCOPE_$_"} = 'off', @ITFs); # force global
314 } elsif (ValidNewOption ($dhcpsettings{'ADVOPT_NAME'} . ' ' . $dhcpsettings{'ADVOPT_DATA'})) {
315 #was a new option
316 } elsif (! `grep "\$option $dhcpsettings{'ADVOPT_NAME'} " $filename3`) {
317 $errormessage=$Lang::tr{'dhcp advopt unknown'}.': '.$dhcpsettings{'ADVOPT_NAME'};
318 }
319
320 unless ($errormessage) {
321
322 my $scope = '';
323 foreach my $itf (@ITFs) { # buils "RED,GREEN,ORANGE,... based on selection
324 $scope .= $dhcpsettings{"ADVOPT_SCOPE_${itf}"} eq 'on' ? "\t$itf" : "\toff" ;
325 }
326 if ($dhcpsettings{'KEY1'} eq '') { #add or edit ? TAB separator !
327 unshift (@current1, "$dhcpsettings{'ADVOPT_ENABLED'}\t$dhcpsettings{'ADVOPT_NAME'}\t$dhcpsettings{'ADVOPT_DATA'}$scope\n");
328 &General::log($Lang::tr{'dhcp advopt added'});
329 } else {
330 @current1[$dhcpsettings{'KEY1'}] = "$dhcpsettings{'ADVOPT_ENABLED'}\t$dhcpsettings{'ADVOPT_NAME'}\t$dhcpsettings{'ADVOPT_DATA'}$scope\n";
331 $dhcpsettings{'KEY1'} = ''; # End edit mode
332 &General::log($Lang::tr{'dhcp advopt modified'});
333 }
334
335 #Write changes to dhcpd.conf.
336 &sortcurrent1; # sort newly added/modified entry
337 &buildconf; # before calling buildconf which use fixed lease file !
338 }
339 }
340
341 if ($dhcpsettings{'ACTION'} eq $Lang::tr{'edit'}.'1') {
342 #move out new line
343 my $line = @current1[$dhcpsettings{'KEY1'}];
344 chomp($line);
345 my @temp = split(/\t/, $line);
346 $dhcpsettings{'ADVOPT_ENABLED'}=$temp[0];
347 $dhcpsettings{'ADVOPT_NAME'}=$temp[1];
348 $dhcpsettings{'ADVOPT_DATA'}=$temp[2];
349
350 # read next fields which are the name (color) of an interface if this interface is scoped
351 for (my $key=0; $key<@ITFs; $key++) {
352 my $itf = $temp[3+$key];
353 if ($itf ne 'off') # Only is an interface name is read
354 {
355 $dhcpsettings{"ADVOPT_SCOPE_${itf}"} = 'on';
356 }
357 }
358 }
359
360 if ($dhcpsettings{'ACTION'} eq $Lang::tr{'remove'}.'1') {
361 splice (@current1,$dhcpsettings{'KEY1'},1);
362 open(FILE, ">$filename1") or die 'Unable to open dhcp advanced options file.';
363 print FILE @current1;
364 close(FILE);
365 $dhcpsettings{'KEY1'} = ''; # End remove mode
366 &General::log($Lang::tr{'dhcp advopt removed'});
367 #Write changes to dhcpd.conf.
368 &buildconf;
369 }
370 #end KEY1
371
372
373 # Toggle enable/disable field on specified lease.
374 if ($dhcpsettings{'ACTION'} eq $Lang::tr{'toggle enable disable'}.'2') {
375 #move out new line
376 chomp(@current2[$dhcpsettings{'KEY2'}]);
377 my @temp = split(/\,/,@current2[$dhcpsettings{'KEY2'}]);
378 $temp[2] = $temp[2] eq 'on' ? '' : 'on'; # Toggle the field
379 @current2[$dhcpsettings{'KEY2'}] = join (',',@temp)."\n";
380 $dhcpsettings{'KEY2'} = ''; # End edit mode
381 &General::log($Lang::tr{'fixed ip lease modified'});
382 open(FILE, ">$filename2") or die 'Unable to open fixed leases file.';
383 print FILE @current2;
384 close(FILE);
385
386 #Write changes to dhcpd.conf.
387 &buildconf;
388 }
389
390 if ($dhcpsettings{'ACTION'} eq $Lang::tr{'add'}.'2') {
391 $dhcpsettings{'FIX_MAC'} =~ tr/-/:/;
392 unless(&General::validip($dhcpsettings{'FIX_ADDR'})) { $errormessage = $Lang::tr{'invalid fixed ip address'}; }
393 unless(&General::validmac($dhcpsettings{'FIX_MAC'})) { $errormessage = $Lang::tr{'invalid fixed mac address'}; }
394 if ($dhcpsettings{'FIX_NEXTADDR'}) {
395 unless(&General::validip($dhcpsettings{'FIX_NEXTADDR'})) { $errormessage = $Lang::tr{'invalid fixed ip address'}; }
396 }
397
398 my $key = 0;
399 CHECK:foreach my $line (@current2) {
400 my @temp = split(/\,/,$line);
401 if($dhcpsettings{'KEY2'} ne $key) {
402 # same MAC is OK on different subnets. This test is not complete because
403 # if ip are not inside a known subnet, I don't warn.
404 # Also it may be needed to put duplicate fixed lease in their right subnet definition..
405 foreach my $itf (@ITFs) {
406 my $scoped = &General::IpInSubnet($dhcpsettings{'FIX_ADDR'},
407 $netsettings{"${itf}_NETADDRESS"},
408 $netsettings{"${itf}_NETMASK"}) &&
409 $dhcpsettings{"ENABLE_${itf}"} eq 'on';
410 if ( $scoped &&
411 (lc($dhcpsettings{'FIX_MAC'}) eq lc($temp[0])) &&
412 &General::IpInSubnet($temp[1],
413 $netsettings{"${itf}_NETADDRESS"},
414 $netsettings{"${itf}_NETMASK"})) {
415 $errormessage = "$Lang::tr{'mac address in use'} $dhcpsettings{'FIX_MAC'}";
416 last CHECK;
417 }
418 }
419 }
420 $key++;
421 }
422
423 unless ($errormessage) {
424 $dhcpsettings{'FIX_REMARK'} = &Header::cleanhtml($dhcpsettings{'FIX_REMARK'});
425 $dhcpsettings{'FIX_NEXTADDR'} = &Header::cleanhtml($dhcpsettings{'FIX_NEXTADDR'});
426 $dhcpsettings{'FIX_FILENAME'} = &Header::cleanhtml($dhcpsettings{'FIX_FILENAME'});
427 $dhcpsettings{'FIX_ROOTPATH'} = &Header::cleanhtml($dhcpsettings{'FIX_ROOTPATH'});
428 if ($dhcpsettings{'KEY2'} eq '') { #add or edit ?
429 unshift (@current2, "$dhcpsettings{'FIX_MAC'},$dhcpsettings{'FIX_ADDR'},$dhcpsettings{'FIX_ENABLED'},$dhcpsettings{'FIX_NEXTADDR'},$dhcpsettings{'FIX_FILENAME'},$dhcpsettings{'FIX_ROOTPATH'},$dhcpsettings{'FIX_REMARK'}\n");
430 &General::log($Lang::tr{'fixed ip lease added'});
431 } else {
432 @current2[$dhcpsettings{'KEY2'}] = "$dhcpsettings{'FIX_MAC'},$dhcpsettings{'FIX_ADDR'},$dhcpsettings{'FIX_ENABLED'},$dhcpsettings{'FIX_NEXTADDR'},$dhcpsettings{'FIX_FILENAME'},$dhcpsettings{'FIX_ROOTPATH'},$dhcpsettings{'FIX_REMARK'}\n";
433 $dhcpsettings{'KEY2'} = ''; # End edit mode
434 &General::log($Lang::tr{'fixed ip lease modified'});
435 }
436
437 #Write changes to dhcpd.conf.
438 &sortcurrent2; # sort newly added/modified entry
439 &buildconf; # before calling buildconf which use fixed lease file !
440 }
441 }
442
443 if ($dhcpsettings{'ACTION_ALL'} eq '+') {
444 my $news = 0;
445 foreach (keys %dhcpsettings) {
446 if (/^(\d+\.\d+\.\d+\.\d+)-([0-9a-fA-F:]+)$/) { # checked names are index of the line
447 my $ip=$1;
448 my $mac=$2;
449 if (!grep (/$2/,@current2)) {
450 unshift (@current2, "$mac,$ip,on,,,,imported\n");
451 $news++;
452 }
453 }
454 }
455 if ($news) {
456 #Write changes to dhcpd.conf.
457 $warnNTPmessage = $Lang::tr{'fixed ip lease added'}."($news)";
458 &General::log($warnNTPmessage);
459 &sortcurrent2; # sort newly added/modified entry
460 &buildconf; # before calling buildconf which use fixed lease file !
461 }
462 }
463
464 if ($dhcpsettings{'ACTION'} eq $Lang::tr{'edit'}.'2') {
465 #move out new line
466 my $line = @current2[$dhcpsettings{'KEY2'}];
467 chomp($line);
468 my @temp = split(/\,/, $line);
469 $dhcpsettings{'FIX_MAC'}=$temp[0];
470 $dhcpsettings{'FIX_ADDR'}=$temp[1];
471 $dhcpsettings{'FIX_ENABLED'}=$temp[2];
472 $dhcpsettings{'FIX_NEXTADDR'}=$temp[3];
473 $dhcpsettings{'FIX_FILENAME'}=$temp[4];
474 $dhcpsettings{'FIX_ROOTPATH'}=$temp[5];
475 $dhcpsettings{'FIX_REMARK'}=$temp[6];
476 }
477
478 if ($dhcpsettings{'ACTION'} eq $Lang::tr{'remove'}.'2') {
479 splice (@current2,$dhcpsettings{'KEY2'},1);
480 open(FILE, ">$filename2") or die 'Unable to open fixed lease file.';
481 print FILE @current2;
482 close(FILE);
483 $dhcpsettings{'KEY2'} = ''; # End remove mode
484 &General::log($Lang::tr{'fixed ip lease removed'});
485 #Write changes to dhcpd.conf.
486 &buildconf;
487 }
488 #end KEY2 defined
489
490
491
492
493 if ($dhcpsettings{'ACTION'} eq '' ) { # First launch from GUI
494
495 # Set default DHCP values only if blank and disabled
496 foreach my $itf (@ITFs) {
497 if ($dhcpsettings{"ENABLE_${itf}"} ne 'on' ) {
498 $dhcpsettings{"DNS1_${itf}"} = $netsettings{"${itf}_ADDRESS"};
499 $dhcpsettings{"DEFAULT_LEASE_TIME_${itf}"} = '60';
500 $dhcpsettings{"MAX_LEASE_TIME_${itf}"} = '120';
501 $dhcpsettings{"DOMAIN_NAME_${itf}"} = $mainsettings{'DOMAINNAME'};
502 }
503 }
504 $dhcpsettings{'FIX_ENABLED'} = 'on';
505 }
506
507 &Header::openpage($Lang::tr{'dhcp configuration'}, 1, '');
508 &Header::openbigbox('100%', 'left', '', $errormessage);
509
510 if ($errormessage) {
511 &Header::openbox('100%', 'left', $Lang::tr{'error messages'});
512 print "<font class='base'>$errormessage&nbsp;</font>\n";
513 &Header::closebox();
514 }
515 if ($warnNTPmessage) {
516 $warnNTPmessage = "<font color=${Header::colourred}><b>$Lang::tr{'capswarning'}</b></font>: $warnNTPmessage";
517 }
518
519 &Header::openbox('100%', 'left', 'DHCP');
520 print "<form method='post' action='$ENV{'SCRIPT_NAME'}'>";
521
522 foreach my $itf (@ITFs) {
523 my %checked=();
524 $checked{'ENABLE'}{'on'} = ( $dhcpsettings{"ENABLE_${itf}"} ne 'on') ? '' : "checked='checked'";
525 $checked{'ENABLEBOOTP'}{'on'} = ( $dhcpsettings{"ENABLEBOOTP_${itf}"} ne 'on') ? '' : "checked='checked'";
526
527 if ($netsettings{"${itf}_DEV"} ne '' ) { # Show only defined interface
528 my $lc_itf=lc($itf);
529 print <<END
530 <table width='100%'>
531 <tr>
532 <td width='25%' class='boldbase'><b><font color='${lc_itf}'>$Lang::tr{"$lc_itf interface"}</font></b></td>
533 <td class='base'>$Lang::tr{'enabled'}
534 <input type='checkbox' name='ENABLE_${itf}' $checked{'ENABLE'}{'on'} /></td>
535 <td width='25%' class='base'>$Lang::tr{'ip address'}/$Lang::tr{'netmask'}:</td><td><b>$netsettings{"${itf}_ADDRESS"}/$netsettings{"${itf}_NETMASK"}</b></td>
536 </tr><tr>
537 <td width='25%' class='base'>$Lang::tr{'start address'}&nbsp;<img src='/blob.gif' alt='*' /></td>
538 <td width='25%'><input type='text' name='START_ADDR_${itf}' value='$dhcpsettings{"START_ADDR_${itf}"}' /></td>
539 <td width='25%' class='base'>$Lang::tr{'end address'}&nbsp;<img src='/blob.gif' alt='*' /></td>
540 <td width='25%'><input type='text' name='END_ADDR_${itf}' value='$dhcpsettings{"END_ADDR_${itf}"}' /></td>
541 </tr><tr>
542 <td class='base'>$Lang::tr{'default lease time'}</td>
543 <td><input type='text' name='DEFAULT_LEASE_TIME_${itf}' value='$dhcpsettings{"DEFAULT_LEASE_TIME_${itf}"}' /></td>
544 <td class='base'>$Lang::tr{'max lease time'}</td>
545 <td><input type='text' name='MAX_LEASE_TIME_${itf}' value='$dhcpsettings{"MAX_LEASE_TIME_${itf}"}' /></td>
546 </tr><tr>
547 <td class='base'>$Lang::tr{'domain name suffix'}&nbsp;<img src='/blob.gif' alt='*' /></td>
548 <td><input type='text' name='DOMAIN_NAME_${itf}' value='$dhcpsettings{"DOMAIN_NAME_${itf}"}' /></td>
549 <td>$Lang::tr{'dhcp allow bootp'}:</td>
550 <td><input type='checkbox' name='ENABLEBOOTP_${itf}' $checked{'ENABLEBOOTP'}{'on'} /></td>
551 </tr><tr>
552 <td class='base'>$Lang::tr{'primary dns'}</td>
553 <td><input type='text' name='DNS1_${itf}' value='$dhcpsettings{"DNS1_${itf}"}' /></td>
554 <td class='base'>$Lang::tr{'secondary dns'}&nbsp;<img src='/blob.gif' alt='*' /></td>
555 <td><input type='text' name='DNS2_${itf}' value='$dhcpsettings{"DNS2_${itf}"}' /></td>
556 </tr><tr>
557 <td class='base'>$Lang::tr{'primary ntp server'}:&nbsp;<img src='/blob.gif' alt='*' /></td>
558 <td><input type='text' name='NTP1_${itf}' value='$dhcpsettings{"NTP1_${itf}"}' /></td>
559 <td class='base'>$Lang::tr{'secondary ntp server'}:&nbsp;<img src='/blob.gif' alt='*' /></td>
560 <td><input type='text' name='NTP2_${itf}' value='$dhcpsettings{"NTP2_${itf}"}' /></td>
561 </tr><tr>
562 <td class='base'>$Lang::tr{'primary wins server address'}:&nbsp;<img src='/blob.gif' alt='*' /></td>
563 <td><input type='text' name='WINS1_${itf}' value='$dhcpsettings{"WINS1_${itf}"}' /></td>
564 <td class='base'>$Lang::tr{'secondary wins server address'}:&nbsp;<img src='/blob.gif' alt='*' /></td>
565 <td><input type='text' name='WINS2_${itf}' value='$dhcpsettings{"WINS2_${itf}"}' /></td>
566 </tr>
567 </table>
568 <hr />
569 END
570 ;
571 }# Show only defined interface
572 }#foreach itf
573 print <<END
574 <table width='100%'>
575 <tr>
576 <td class='base' width='25%'><img src='/blob.gif' align='top' alt='*' />&nbsp;$Lang::tr{'this field may be blank'}</td>
577 <td class='base' width='30%'>$warnNTPmessage</td>
578 <td width='40%' align='center'><input type='submit' name='ACTION' value='$Lang::tr{'save'}' /></td>
579 <td width='5%' align='right'>
580 <a href='${General::adminmanualurl}/services.html#services_dhcp' target='_blank'>
581 <img src='/images/web-support.png' alt='$Lang::tr{'online help en'}' title='$Lang::tr{'online help en'}' /></a></td>
582 </tr>
583 </table>
584 </form>
585 END
586 ;
587
588 &Header::closebox();
589
590 &Header::openbox('100%', 'left', $Lang::tr{'dhcp advopt list'});
591 # DHCP Advanced options settings
592 my %checked=();
593 $checked{'ADVOPT_ENABLED'}{'on'} = ($dhcpsettings{'ADVOPT_ENABLED'} ne 'on') ? '' : "checked='checked'";
594
595 print "<form method='post' action='$ENV{'SCRIPT_NAME'}'><table width='100%'>";
596 my $buttontext = $Lang::tr{'add'};
597 if ($dhcpsettings{'KEY1'} ne '') {
598 $buttontext = $Lang::tr{'update'};
599 print "<tr><td class='boldbase'><b>$Lang::tr{'dhcp advopt edit'}</b></td></tr>";
600 } else {
601 print "<tr><td class='boldbase'><b>$Lang::tr{'dhcp advopt add'}</b></td></tr>"
602 }
603
604 #search if the 'option' is in the list and print the syntax model
605 my $opt = `grep "\$option $dhcpsettings{'ADVOPT_NAME'} " $filename3`;
606 if ($opt ne '') {
607 $opt =~ s/option $dhcpsettings{'ADVOPT_NAME'}/Syntax:/; # "option xyz abc" => "syntax: abc"
608 $opt =~ s/;//;
609 $opt = "<tr><td></td><td></td><td colspan='2'>$opt</td></tr>";
610 }
611 print <<END
612 <tr>
613 <td class='base'>$Lang::tr{'dhcp advopt name'}:</td>
614 <td><input type='text' name='ADVOPT_NAME' value='$dhcpsettings{'ADVOPT_NAME'}' size='18' /></td>
615 <td class='base'>$Lang::tr{'dhcp advopt value'}:</td>
616 <td><input type='text' name='ADVOPT_DATA' value='$dhcpsettings{'ADVOPT_DATA'}' size='40' /></td>
617 </tr>$opt<tr>
618 <td class='base'>$Lang::tr{'enabled'}</td><td><input type='checkbox' name='ADVOPT_ENABLED' $checked{'ADVOPT_ENABLED'}{'on'} /></td>
619 <td class='base'>$Lang::tr{'dhcp advopt scope'}:&nbsp;<img src='/blob.gif' alt='*' /></td>
620 <td>
621 END
622 ;
623
624 # Put a checkbox for each interface. Checkbox visible disabled if interface is disabled
625 foreach my $itf (@ITFs) {
626 my $lc_itf=lc($itf);
627 $checked{'ADVOPT_SCOPE_${itf}'}{'on'} = $dhcpsettings{"ADVOPT_SCOPE_${itf}"} ne 'on' ? '' : "checked='checked'";
628 print "$Lang::tr{\"${lc_itf}\"} <input type='checkbox' name='ADVOPT_SCOPE_${itf}' $checked{'ADVOPT_SCOPE_${itf}'}{'on'} ";
629 print $dhcpsettings{"ENABLE_${itf}"} eq 'on' ? "/>" : "disabled='disabled' />";
630 print "&nbsp; &nbsp;";
631 }
632
633 print <<END
634 </td>
635 </tr>
636 </table>
637 <hr />
638 <table width='100%'>
639 <tr>
640 <td class='base' width='50%'><img src='/blob.gif' align='top' alt='*' />&nbsp;$Lang::tr{'dhcp advopt scope help'}</td>
641 <td width='50%' align='center'>
642 <input type='hidden' name='ACTION' value='$Lang::tr{'add'}1' />
643 <input type='submit' name='SUBMIT' value='$buttontext' />
644 <input type='submit' name='SUBMIT' value='$Lang::tr{'dhcp advopt help'}' />
645 <input type='hidden' name='KEY1' value='$dhcpsettings{'KEY1'}' />
646 </td>
647 </tr>
648 </table>
649 </form>
650 END
651 ;
652 #Edited line number (KEY1) passed until cleared by 'save' or 'remove' or 'new sort order'
653
654 # print help taken from the file describing options
655 if ($dhcpsettings{'SUBMIT'} eq $Lang::tr{'dhcp advopt help'}) {
656 print "<hr />";
657 print "<table width='100%'>";
658 print "<tr><td width='30%'><b>$Lang::tr{'dhcp advopt name'}</b></td><td width='70%'><b>$Lang::tr{'dhcp advopt value'}</b></td>";
659 open(FILE, "$filename3");
660 my @current3 = <FILE>;
661 close(FILE);
662 foreach my $line (@current3) {
663 $line =~ /option ([a-z0-9-]+) (.*);/;
664 print "<tr><td>$1</td><td>$2</td></tr>\n";
665 }
666 print "<tr><td colspan='2'><hr /></td></tr>\n";
667 print '<tr><td>string type</td><td>"quoted string" or 00:01:FF...</td></tr>';
668 print '<tr><td>ip-address type </td><td>10.0.0.1 | www.dot.com</td></tr>';
669 print '<tr><td>int,uint types</td><td>numbers</td></tr>';
670 print '<tr><td>flag type</td><td>on | off</td></tr>';
671 print '</table>';
672 print "<hr />";
673 print "<table width='100%'>";
674 print "<tr><td width='30%'><b>$Lang::tr{'dhcp advopt custom definition'}</b></td><td width='70%'><b>$Lang::tr{'dhcp advopt value'}</b></td>";
675 print "<tr><td>any-name </td><td> code NNN=$OptionTypes</td></tr>";
676 print '<tr><td>a-string</td><td>code 100=string</td></tr>';
677 print '<tr><td>a-number</td><td>code 101=signed integer 8</td></tr>';
678 print '<tr><td>wpad</td><td>code 252=text</td></tr>';
679 print '<tr><td>wpad</td><td>"http://www.server.fr/path-to/proxy.pac"</td></tr>';
680 print '</table>';
681
682 }
683
684 print <<END
685 <hr />
686 <table width='100%'>
687 <tr>
688 <td width='30%' class='boldbase' align='center'><b>$Lang::tr{'dhcp advopt name'}</b></td>
689 <td width='50%' class='boldbase' align='center'><b>$Lang::tr{'dhcp advopt value'}</b></td>
690 <td width='20%' class='boldbase' align='center'><b>$Lang::tr{'dhcp advopt scope'}</b></td>
691 <td colspan='3' class='boldbase' align='center'><b>$Lang::tr{'action'}</b></td>
692 </tr>
693 END
694 ;
695 my $key = 0;
696 foreach my $line (@current1) {
697 my $gif = '';
698 my $gdesc = '';
699 chomp($line); # remove newline
700 my @temp = split(/\t/,$line);
701
702 if ($temp[0] eq "on") {
703 $gif = 'on.gif';
704 $gdesc = $Lang::tr{'click to disable'};
705 } else {
706 $gif = 'off.gif';
707 $gdesc = $Lang::tr{'click to enable'};
708 }
709
710 if ($dhcpsettings{'KEY1'} eq $key) {
711 print "<tr bgcolor='${Header::colouryellow}'>";
712 } elsif ($key % 2) {
713 print "<tr bgcolor='${Header::table2colour}'>";
714 } else {
715 print "<tr bgcolor='${Header::table1colour}'>";
716 }
717
718 print <<END
719 <td align='center'>$temp[1]</td>
720 <td align='center'>$temp[2]</td>
721 <td align='center'>
722 END
723 ;
724 # Prepare a global flag to make easy reading
725 my $global = '';
726 my $disabledTogle = '';
727 my $disabledEditRemove = '';
728 if ( ExistNewOptionDefinition ($temp[1] . ' ' . $temp[2]) ) {
729 $global = $Lang::tr{'dhcp advopt definition'};
730 $disabledTogle = "disabled='disabled'";
731 # Search if it is a used NewOptionDefinition to also disable edit & delete
732 $disabledEditRemove = "disabled='disabled'" if (IsUsedNewOptionDefinition ($temp[1], $temp[2]));
733 } else {
734 $global = $Lang::tr{'dhcp advopt scope global'};
735 }
736
737
738 # Print each checked interface
739 for (my $key=0; $key<@ITFs; $key++) {
740 my $itf = $temp[3+$key];
741 if ($itf ne 'off') { # Only if an interface name is read
742 print "$itf";
743 $global=''; # fall to local scope !
744 }
745 }
746 print <<END
747 $global</td>
748 <td align='center'>
749 <form method='post' action='$ENV{'SCRIPT_NAME'}'>
750 <input type='hidden' name='ACTION' value='$Lang::tr{'toggle enable disable'}1' />
751 <input $disabledTogle type='image' name='$Lang::tr{'toggle enable disable'}' src='/images/$gif' alt='$gdesc' title='$gdesc' />
752 <input type='hidden' name='KEY1' value='$key' />
753 </form>
754 </td>
755
756 <td align='center'>
757 <form method='post' action='$ENV{'SCRIPT_NAME'}'>
758 <input type='hidden' name='ACTION' value='$Lang::tr{'edit'}1' />
759 <input $disabledEditRemove type='image' name='$Lang::tr{'edit'}' src='/images/edit.gif' alt='$Lang::tr{'edit'}' title='$Lang::tr{'edit'}' />
760 <input type='hidden' name='KEY1' value='$key' />
761 </form>
762 </td>
763
764 <td align='center'>
765 <form method='post' action='$ENV{'SCRIPT_NAME'}'>
766 <input type='hidden' name='ACTION' value='$Lang::tr{'remove'}1' />
767 <input $disabledEditRemove type='image' name='$Lang::tr{'remove'}' src='/images/delete.gif' alt='$Lang::tr{'remove'}' title='$Lang::tr{'remove'}' />
768 <input type='hidden' name='KEY1' value='$key' />
769 </form>
770 </td>
771 </tr>
772 END
773 ;
774 $key++;
775 }
776
777 print "</table>";
778
779 # If there are dhcp options, print Key to action icons
780 if ($key) {
781 print <<END
782 <table>
783 <tr>
784 <td class='boldbase'>&nbsp;<b>$Lang::tr{'legend'}:&nbsp;</b></td>
785 <td><img src='/images/on.gif' alt='$Lang::tr{'click to disable'}' /></td>
786 <td class='base'>$Lang::tr{'click to disable'}</td>
787 <td>&nbsp;&nbsp;</td>
788 <td><img src='/images/off.gif' alt='$Lang::tr{'click to enable'}' /></td>
789 <td class='base'>$Lang::tr{'click to enable'}</td>
790 <td>&nbsp;&nbsp;</td>
791 <td><img src='/images/edit.gif' alt='$Lang::tr{'edit'}' /></td>
792 <td class='base'>$Lang::tr{'edit'}</td>
793 <td>&nbsp;&nbsp;</td>
794 <td><img src='/images/delete.gif' alt='$Lang::tr{'remove'}' /></td>
795 <td class='base'>$Lang::tr{'remove'}</td>
796 </tr>
797 </table>
798 END
799 ;
800 }
801 &Header::closebox();
802
803 &Header::openbox('100%', 'left', $Lang::tr{'current fixed leases'});
804 # Fixed leases screens
805 $checked{'FIX_ENABLED'}{'on'} = ($dhcpsettings{'FIX_ENABLED'} ne 'on') ? '' : "checked='checked'";
806
807 $buttontext = $Lang::tr{'add'};
808 print "<form method='post' action='$ENV{'SCRIPT_NAME'}'><table width='100%'>";
809
810 if ($dhcpsettings{'KEY2'} ne '') {
811 $buttontext = $Lang::tr{'update'};
812 print "<tr><td class='boldbase' colspan='3'><b>$Lang::tr{'edit an existing lease'}</b></td></tr>";
813 } else {
814 print "<tr><td class='boldbase' colspan='3'><b>$Lang::tr{'add new lease'}</b></td></tr>"
815 }
816 print <<END
817 <tr>
818 <td class='base'>$Lang::tr{'mac address'}:</td>
819 <td><input type='text' name='FIX_MAC' value='$dhcpsettings{'FIX_MAC'}' size='18' /></td>
820 <td class='base'>$Lang::tr{'ip address'}:</td>
821 <td><input type='text' name='FIX_ADDR' value='$dhcpsettings{'FIX_ADDR'}' size='18' /></td>
822 <td class='base'>$Lang::tr{'remark'}:&nbsp;<img src='/blob.gif' alt='*' /></td>
823 <td><input type='text' name='FIX_REMARK' value='$dhcpsettings{'FIX_REMARK'}' size='18' /></td>
824 </tr><tr>
825 <td class='base'>$Lang::tr{'enabled'}</td><td><input type='checkbox' name='FIX_ENABLED' $checked{'FIX_ENABLED'}{'on'} /></td>
826 </tr><tr>
827 <td colspan = '3'><b>$Lang::tr{'dhcp bootp pxe data'}</b></td>
828 </tr><tr>
829 <td class='base'>next-server:&nbsp;<img src='/blob.gif' alt='*' /></td>
830 <td><input type='text' name='FIX_NEXTADDR' value='$dhcpsettings{'FIX_NEXTADDR'}' size='18' /></td>
831 <td class='base'>filename:&nbsp;<img src='/blob.gif' alt='*' /></td>
832 <td><input type='text' name='FIX_FILENAME' value='$dhcpsettings{'FIX_FILENAME'}' size='18' /></td>
833 <td class='base'>root path:&nbsp;<img src='/blob.gif' alt='*' /></td>
834 <td><input type='text' name='FIX_ROOTPATH' value='$dhcpsettings{'FIX_ROOTPATH'}' size='18' /></td>
835 </tr>
836 </table>
837 <hr />
838 <table width='100%'>
839 <tr>
840 <td class='base' width='50%'><img src='/blob.gif' align='top' alt='*' />&nbsp;$Lang::tr{'this field may be blank'}</td>
841 <td width='50%' align='center'>
842 <input type='hidden' name='ACTION' value='$Lang::tr{'add'}2' />
843 <input type='submit' name='SUBMIT' value='$buttontext' />
844 <input type='hidden' name='KEY2' value='$dhcpsettings{'KEY2'}' /></td>
845 </tr>
846 </table>
847 </form>
848 END
849 ;
850 #Edited line number (KEY2) passed until cleared by 'save' or 'remove' or 'new sort order'
851
852 print <<END
853 <hr />
854 <table width='100%'>
855 <tr>
856 <td width='20%' align='center'><a href='$ENV{'SCRIPT_NAME'}?FETHER'><b>$Lang::tr{'mac address'}</b></a></td>
857 <td width='20%' align='center'><a href='$ENV{'SCRIPT_NAME'}?FIPADDR'><b>$Lang::tr{'ip address'}</b></a></td>
858 <td width='15%' align='center'><b>$Lang::tr{'remark'}</b></td>
859 <td width='15%' class='boldbase' align='center'><b>next-server</b></td>
860 <td width='15%' class='boldbase' align='center'><b>filename</b></td>
861 <td width='15%' class='boldbase' align='center'><b>root path</b></td>
862 <td colspan='3' class='boldbase' align='center'><b>$Lang::tr{'action'}</b></td>
863 </tr>
864 END
865 ;
866 my $ipdup = 0;
867 my %ipinuse = ();
868 my %macdupl = (); # Duplicate MACs have to be on different subnets
869 my %ipoutside = ();
870
871 # mark duplicate ip or duplicate MAC
872 foreach my $line (@current2) {
873 my @temp = split(/\,/,$line);
874 $macdupl{$temp[0]} += 1;
875 if ($macdupl{$temp[0]} > 1) {
876 $ipdup = 1; # Flag up duplicates for use later
877 }
878 $ipinuse{$temp[1]} += 1;
879 if ($ipinuse{$temp[1]} > 1) {
880 $ipdup = 1; # Flag up duplicates for use later
881 }
882 # Mark IP addresses outwith known subnets
883 $ipoutside{$temp[1]} = 1;
884 foreach my $itf (@ITFs) {
885 if ( &General::IpInSubnet($temp[1],
886 $netsettings{"${itf}_NETADDRESS"},
887 $netsettings{"${itf}_NETMASK"})) {
888 $ipoutside{$temp[1]} = 0;
889 }
890 }
891 }
892
893 $key = 0;
894 foreach my $line (@current2) {
895 my $gif = '';
896 my $gdesc = '';
897 chomp($line); # remove newline
898 my @temp = split(/\,/,$line);
899
900 if ($temp[2] eq "on") {
901 $gif = 'on.gif';
902 $gdesc = $Lang::tr{'click to disable'};
903 } else {
904 $gif = 'off.gif';
905 $gdesc = $Lang::tr{'click to enable'};
906 }
907
908 if ($dhcpsettings{'KEY2'} eq $key) {
909 print "<tr bgcolor='${Header::colouryellow}'>";
910 } elsif ($key % 2) {
911 print "<tr bgcolor='${Header::table2colour}'>";
912 } else {
913 print "<tr bgcolor='${Header::table1colour}'>";
914 }
915 my $TAG0 = '';
916 my $TAG1 = '';
917 my $TAG2 = '';
918 my $TAG3 = '';
919 my $TAG4 = '';
920 if ($ipinuse{$temp[1]} > 1) {
921 $TAG0 = '<b>';
922 $TAG1 = '</b>';
923 }
924 if ($macdupl{$temp[0]} > 1) {
925 $TAG2 = '<b>';
926 $TAG3 = '</b>';
927 }
928 if ($ipoutside{$temp[1]} > 0) {
929 $TAG4 = "bgcolor='orange'" if ($dhcpsettings{'KEY2'} ne $key);
930 }
931
932 print <<END
933 <td align='center'>$TAG2$temp[0]$TAG3</td>
934 <td align='center' $TAG4>$TAG0$temp[1]$TAG1</td>
935 <td align='center'>$temp[6]&nbsp;</td>
936 <td align='center'>$temp[3]&nbsp;</td>
937 <td align='center'>$temp[4]&nbsp;</td>
938 <td align='center'>$temp[5]&nbsp;</td>
939
940 <td align='center'>
941 <form method='post' action='$ENV{'SCRIPT_NAME'}'>
942 <input type='hidden' name='ACTION' value='$Lang::tr{'toggle enable disable'}2' />
943 <input type='image' name='$Lang::tr{'toggle enable disable'}' src='/images/$gif' alt='$gdesc' title='$gdesc' />
944 <input type='hidden' name='KEY2' value='$key' />
945 </form>
946 </td>
947
948 <td align='center'>
949 <form method='post' action='$ENV{'SCRIPT_NAME'}'>
950 <input type='hidden' name='ACTION' value='$Lang::tr{'edit'}2' />
951 <input type='image' name='$Lang::tr{'edit'}' src='/images/edit.gif' alt='$Lang::tr{'edit'}' title='$Lang::tr{'edit'}' />
952 <input type='hidden' name='KEY2' value='$key' />
953 </form>
954 </td>
955
956 <td align='center'>
957 <form method='post' action='$ENV{'SCRIPT_NAME'}'>
958 <input type='hidden' name='ACTION' value='$Lang::tr{'remove'}2' />
959 <input type='image' name='$Lang::tr{'remove'}' src='/images/delete.gif' alt='$Lang::tr{'remove'}' title='$Lang::tr{'remove'}' />
960 <input type='hidden' name='KEY2' value='$key' />
961 </form>
962 </td>
963 </tr>
964 END
965 ;
966 $key++;
967 }
968 print "</table>";
969
970 # If the fixed lease file contains entries, print Key to action icons
971 if ($key) {
972 my $dup = $ipdup ? "<td class='base'>$Lang::tr{'duplicate ip bold'}</td>" :'';
973 print <<END
974 <table>
975 <tr>
976 <td class='boldbase'>&nbsp;<b>$Lang::tr{'legend'}:&nbsp;</b></td>
977 <td><img src='/images/on.gif' alt='$Lang::tr{'click to disable'}' /></td>
978 <td class='base'>$Lang::tr{'click to disable'}</td>
979 <td>&nbsp;&nbsp;</td>
980 <td><img src='/images/off.gif' alt='$Lang::tr{'click to enable'}' /></td>
981 <td class='base'>$Lang::tr{'click to enable'}</td>
982 <td>&nbsp;&nbsp;</td>
983 <td><img src='/images/edit.gif' alt='$Lang::tr{'edit'}' /></td>
984 <td class='base'>$Lang::tr{'edit'}</td>
985 <td>&nbsp;&nbsp;</td>
986 <td><img src='/images/delete.gif' alt='$Lang::tr{'remove'}' /></td>
987 <td class='base'>$Lang::tr{'remove'}</td>
988 </tr>
989 <tr>
990 <td>&nbsp;</td>
991 <td bgcolor='orange'>&nbsp;</td>
992 <td class='base'>$Lang::tr{'ip address outside subnets'}</td>
993 <td>&nbsp;</td>
994 <td>&nbsp;</td>
995 $dup
996 </tr>
997 </table>
998 END
999 ;
1000 }
1001
1002 &Header::closebox();
1003
1004
1005 foreach my $itf (@ITFs) {
1006 if ($dhcpsettings{"ENABLE_${itf}"} eq 'on') {
1007 # display leases with a list of actions to do with the global select checkbox.
1008 &Header::PrintActualLeases("+"); # "+" => create fixed leases from nodeaddress
1009 last; #Print one time only for all interfaces
1010 };
1011 }
1012
1013 &Header::closebigbox();
1014 &Header::closepage();
1015
1016 ## Ouf it's the end !
1017
1018 sub sortcurrent1 # by now, do not sort, just write
1019 {
1020 open(FILE, ">$filename1") or die 'Unable to open dhcp advanced options file.';
1021 print FILE @current1;
1022 close(FILE);
1023 }
1024
1025
1026 # Sort the "current2" array according to choices
1027 sub sortcurrent2
1028 {
1029 our %entries = ();
1030
1031 sub fixedleasesort {
1032 my $qs='';
1033 if (rindex ($dhcpsettings{'SORT_FLEASELIST'},'Rev') != -1) {
1034 $qs=substr ($dhcpsettings{'SORT_FLEASELIST'},0,length($dhcpsettings{'SORT_FLEASELIST'})-3);
1035 if ($qs eq 'FIPADDR') {
1036 my @a = split(/\./,$entries{$a}->{$qs});
1037 my @b = split(/\./,$entries{$b}->{$qs});
1038 ($b[0]<=>$a[0]) ||
1039 ($b[1]<=>$a[1]) ||
1040 ($b[2]<=>$a[2]) ||
1041 ($b[3]<=>$a[3]);
1042 } else {
1043 $entries{$b}->{$qs} cmp $entries{$a}->{$qs};
1044 }
1045 } else { #not reverse
1046 $qs=$dhcpsettings{'SORT_FLEASELIST'};
1047 if ($qs eq 'FIPADDR') {
1048 my @a = split(/\./,$entries{$a}->{$qs});
1049 my @b = split(/\./,$entries{$b}->{$qs});
1050 ($a[0]<=>$b[0]) ||
1051 ($a[1]<=>$b[1]) ||
1052 ($a[2]<=>$b[2]) ||
1053 ($a[3]<=>$b[3]);
1054 } else {
1055 $entries{$a}->{$qs} cmp $entries{$b}->{$qs};
1056 }
1057 }
1058 }
1059
1060 #Use an associative array (%entries)
1061 foreach my $line (@current2) {
1062 chomp( $line); #remove newline because can be on field 5 or 6 (addition of REMARK)
1063 my @temp = split (',',$line);
1064 my @record = ('FETHER',$temp[0],'FIPADDR',$temp[1],'DATA',join(',',@temp[2..6]));
1065 my $record = {}; # create a reference to empty hash
1066 %{$record} = @record; # populate that hash with @record
1067 # use combination of ether & IP as key to allow duplicates in either but not both
1068 $entries{$record->{FETHER} . $record->{FIPADDR}} = $record; # add this to a hash of hashes
1069 }
1070
1071 open(FILE, ">$filename2") or die 'Unable to open fixed lease file.';
1072 foreach my $entry ( sort fixedleasesort keys %entries) {
1073 print FILE "$entries{$entry}->{FETHER},$entries{$entry}->{FIPADDR},$entries{$entry}->{DATA}\n";
1074 }
1075 close(FILE);
1076
1077 # Reload sorted @current2
1078 open (FILE, "$filename2");
1079 @current2 = <FILE>;
1080 close (FILE);
1081 undef (%entries); #This array is reused latter. Clear it.
1082 }
1083
1084 # Build the configuration file mixing settings, fixed leases and advanced options
1085 sub buildconf {
1086 open(FILE, ">/${General::swroot}/dhcp/dhcpd.conf") or die "Unable to write dhcpd.conf file";
1087 flock(FILE, 2);
1088
1089 # Global settings
1090 print FILE "ddns-update-style none;\n";
1091 print FILE "deny bootp; #default\n";
1092 print FILE "authoritative;\n";
1093
1094 # Write first new option definition
1095 foreach my $line (@current1) {
1096 chomp($line); # remove newline
1097 my @temp = split(/\t/,$line);
1098 if (ExistNewOptionDefinition ($temp[1] . ' ' . $temp[2])) {
1099 print FILE "option $temp[1] $temp[2];\n";
1100 }
1101 }
1102 # Write other global options
1103 foreach my $line (@current1) {
1104 chomp($line); # remove newline
1105 my @temp = split(/\t/,$line);
1106
1107 if ($temp[0] eq 'on' && !ExistNewOptionDefinition ($temp[1] . ' ' . $temp[2])){ # active & !definition
1108 my $global=1;
1109 for (my $key=0; $key<@ITFs; $key++) {
1110 my $itf = $temp[3+$key];
1111 if ($itf ne 'off') # Only if an interface name is read
1112 {
1113 $global=0;
1114 }
1115 }
1116 if ($global) {
1117 print FILE "option $temp[1] $temp[2];\n";
1118 }
1119 }# on
1120 }# foreach line
1121
1122 #Subnet range definition
1123 foreach my $itf (@ITFs) {
1124 my $lc_itf=lc($itf);
1125 if ($dhcpsettings{"ENABLE_${itf}"} eq 'on' ){
1126 print FILE "\nsubnet " . $netsettings{"${itf}_NETADDRESS"} . " netmask ". $netsettings{"${itf}_NETMASK"} . " #$itf\n";
1127 print FILE "{\n";
1128 print FILE "\trange " . $dhcpsettings{"START_ADDR_${itf}"} . ' ' . $dhcpsettings{"END_ADDR_${itf}"}.";\n" if ($dhcpsettings{"START_ADDR_${itf}"});
1129 print FILE "\toption subnet-mask " . $netsettings{"${itf}_NETMASK"} . ";\n";
1130 print FILE "\toption domain-name \"" . $dhcpsettings{"DOMAIN_NAME_${itf}"} . "\";\n";
1131 print FILE "\toption routers " . $netsettings{"${itf}_ADDRESS"} . ";\n";
1132 print FILE "\toption domain-name-servers " . $dhcpsettings{"DNS1_${itf}"} if ($dhcpsettings{"DNS1_${itf}"});
1133 print FILE ", " . $dhcpsettings{"DNS2_${itf}"} if ($dhcpsettings{"DNS2_${itf}"});
1134 print FILE ";\n" if ($dhcpsettings{"DNS1_${itf}"});
1135 print FILE "\toption ntp-servers " . $dhcpsettings{"NTP1_${itf}"} if ($dhcpsettings{"NTP1_${itf}"});
1136 print FILE ", " . $dhcpsettings{"NTP2_${itf}"} if ($dhcpsettings{"NTP2_${itf}"});
1137 print FILE ";\n" if ($dhcpsettings{"NTP1_${itf}"});
1138 print FILE "\toption netbios-name-servers " . $dhcpsettings{"WINS1_${itf}"} if ($dhcpsettings{"WINS1_${itf}"});
1139 print FILE ", " . $dhcpsettings{"WINS2_${itf}"} if ($dhcpsettings{"WINS2_${itf}"});
1140 print FILE ";\n" if ($dhcpsettings{"WINS1_${itf}"});
1141 print FILE "\tdefault-lease-time " . ($dhcpsettings{"DEFAULT_LEASE_TIME_${itf}"} * 60). ";\n";
1142 print FILE "\tmax-lease-time " . ($dhcpsettings{"MAX_LEASE_TIME_${itf}"} * 60) . ";\n";
1143 print FILE "\tallow bootp;\n" if ($dhcpsettings{"ENABLEBOOTP_${itf}"} eq 'on');
1144
1145
1146
1147 # Write scoped options
1148 foreach my $line (@current1) {
1149 chomp($line); # remove newline
1150 my @temp = split(/\t/,$line); # Use TAB separator !
1151
1152 if ($temp[0] eq 'on'){
1153 for (my $key=0; $key<@ITFs; $key++) {
1154 if ($itf eq $temp[3+$key]) # Only is an interface name is read
1155 {
1156 print FILE "\toption $temp[1] $temp[2];\n";
1157 }
1158 }
1159 }# on
1160 }# foreach line
1161 print FILE "} #$itf\n";
1162
1163 system ('/bin/touch', "${General::swroot}/dhcp/enable_${lc_itf}");
1164 &General::log("DHCP on ${itf}: " . $Lang::tr{'dhcp server enabled'})
1165 } else {
1166 unlink "${General::swroot}/dhcp/enable_${lc_itf}";
1167 &General::log("DHCP on ${itf}: " . $Lang::tr{'dhcp server disabled'})
1168 }
1169 }
1170
1171 #write fixed leases if any. Does not handle duplicates to write them elsewhere than the global scope.
1172 my $key = 0;
1173 foreach my $line (@current2) {
1174 chomp($line);
1175 my @temp = split(/\,/,$line);
1176 if ($temp[2] eq "on") {
1177 print FILE "\nhost fix$key # $temp[6]\n";
1178 print FILE "{\n";
1179 print FILE "\thardware ethernet $temp[0];\n";
1180 print FILE "\tfixed-address $temp[1];\n";
1181 print FILE "\tnext-server $temp[3];\n" if ($temp[3]);
1182 print FILE "\tfilename \"$temp[4]\";\n" if ($temp[4]);
1183 print FILE "\toption root-path \"$temp[5]\";\n" if ($temp[5]);
1184 print FILE "}\n";
1185 $key++;
1186 }
1187 }
1188 close FILE;
1189 system '/usr/local/bin/restartdhcp';
1190 }
1191
1192 #
1193 # Receive a string and if it match model for a new option,
1194 # add it to the list %newOptions
1195 #
1196 my %NewOptions = ();
1197
1198 sub AddNewOptionDefinition {
1199 my ($line) = @_;
1200 if ( $line =~ /^([-\w]+)( code \d+=($OptionTypes))/ ) {
1201 $NewOptions{$1} = $2;
1202 #&General::log ("new:<$1><$2>");
1203 return 1;
1204 }
1205 return 0;
1206 }
1207
1208 #
1209 # Check existence of definition for a new option
1210 #
1211 sub ExistNewOptionDefinition {
1212 my ($line) = @_;
1213
1214 if ( $line =~ /^([-\w]+)( code \d+=($OptionTypes))/ ) {
1215 return defined $NewOptions{$1};
1216 }
1217 return 0;
1218 }
1219
1220 #
1221 # Check if it is a new option (definition must exist)
1222 # "code=" test eliminate a false response when definition exists
1223 # but this string is a definition with bad $OptionTypes.
1224 sub ValidNewOption {
1225 my ($line) = @_;
1226 if ($line =~ /^([-\w]+) (.*)/ ) {
1227 return defined ( $NewOptions{$1} ) && $2 !~ /code=/;
1228 }
1229 return 0;
1230 }
1231
1232 #
1233 # Check if the new option $opt is used, except the definition of itself!
1234 #
1235 sub IsUsedNewOptionDefinition {
1236 my ($opt,$val) = @_;
1237
1238 foreach my $line (@current1) {
1239 #chomp($line); # remove newline #don't know why, but this remove newline in @current1 .... !
1240 my @temp = split(/\t/,$line);
1241 # if we find something "opt value" & value != "code nnn=" it's ok.
1242 return 1 if ( ($opt eq $temp[1]) && ($temp[2] !~ /code \d+=/) );
1243 }
1244 return 0;
1245 }