5 # This code is distributed under the terms of the GPL
7 # (c) The SmoothWall Team
9 # Copyright (C) 01-02-2002 Graham Smith <grhm@grhm.co.uk>
10 # - Fixed DHCP Leases added
12 # $Id: dhcp.cgi,v 1.14.2.81 2006/01/20 12:05:29 franck78 Exp $
14 # Franck -rewrite for two or more interface
15 # nov/2004 -check range is in correct subnet
17 # -add display sorting of actives leases
18 # dec/2004 -add comment field to fixed leases
20 # to do : choose a correct format for displaying dates
24 # enable only the following on debugging purpose
26 use CGI
::Carp
'fatalsToBrowser';
28 require '/var/ipfire/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
} );
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 = '';
49 #Basic syntax allowed for new Option definition. Not implemented: RECORDS & array of RECORDS
50 our $OptionTypes = 'boolean|((un)?signed )?integer (8|16|32)|ip-address|text|string|encapsulate \w+|array of ip-address';
52 &Header
::showhttpheaders
();
54 if (&Header
::blue_used
()){push(@ITFs,'BLUE');}
56 #Settings1 for the first screen box
57 foreach my $itf (@ITFs) {
58 $dhcpsettings{"ENABLE_${itf}"} = 'off';
59 $dhcpsettings{"ENABLEBOOTP_${itf}"} = 'off';
60 $dhcpsettings{"START_ADDR_${itf}"} = '';
61 $dhcpsettings{"END_ADDR_${itf}"} = '';
62 $dhcpsettings{"DOMAIN_NAME_${itf}"} = '';
63 $dhcpsettings{"DEFAULT_LEASE_TIME_${itf}"} = '';
64 $dhcpsettings{"MAX_LEASE_TIME_${itf}"} = '';
65 $dhcpsettings{"WINS1_${itf}"} = '';
66 $dhcpsettings{"WINS2_${itf}"} = '';
67 $dhcpsettings{"DNS1_${itf}"} = '';
68 $dhcpsettings{"DNS2_${itf}"} = '';
69 $dhcpsettings{"NTP1_${itf}"} = '';
70 $dhcpsettings{"NTP2_${itf}"} = '';
71 $dhcpsettings{"NEXT_${itf}"} = '';
72 $dhcpsettings{"FILE_${itf}"} = '';
75 $dhcpsettings{'SORT_FLEASELIST'} = 'FIPADDR';
76 $dhcpsettings{'SORT_LEASELIST'} = 'IPADDR';
78 #Settings2 for editing the multi-line list
79 #Must not be saved with writehash !
80 $dhcpsettings{'FIX_MAC'} = '';
81 $dhcpsettings{'FIX_ADDR'} = '';
82 $dhcpsettings{'FIX_ENABLED'} = 'off';
83 $dhcpsettings{'FIX_NEXTADDR'} = '';
84 $dhcpsettings{'FIX_FILENAME'} = '';
85 $dhcpsettings{'FIX_ROOTPATH'} = '';
86 $dhcpsettings{'FIX_REMARK'} = '';
87 $dhcpsettings{'ACTION'} = '';
88 $dhcpsettings{'KEY1'} = '';
89 $dhcpsettings{'KEY2'} = '';
90 @nosaved=('FIX_MAC','FIX_ADDR','FIX_ENABLED','FIX_NEXTADDR',
91 'FIX_FILENAME','FIX_ROOTPATH','FIX_REMARK');
93 $dhcpsettings{'ADVOPT_ENABLED'} = '';
94 $dhcpsettings{'ADVOPT_NAME'} = '';
95 $dhcpsettings{'ADVOPT_DATA'} = '';
96 unshift (@nosaved,'ADVOPT_ENABLED','ADVOPT_NAME','ADVOPT_DATA');
97 foreach my $itf (@ITFs) {
98 $dhcpsettings{"ADVOPT_SCOPE_${itf}"} = 'off';
99 unshift (@nosaved, "ADVOPT_SCOPE_${itf}");
102 # Read Ipcop settings
103 &General
::readhash
("${General::swroot}/ethernet/settings", \
%netsettings);
104 &General
::readhash
("${General::swroot}/main/settings", \
%mainsettings);
105 &General
::readhash
("${General::swroot}/time/settings", \
%timesettings);
106 &General
::readhash
("/srv/web/ipfire/html/themes/".$mainsettings{'THEME'}."/include/colors.txt", \
%color);
109 &Header
::getcgihash
(\
%dhcpsettings);
111 open(FILE
, "$filename1") or die 'Unable to open dhcp advanced options file.';
112 our @current1 = <FILE
>;
114 # Extract OptionDefinition
115 foreach my $line (@current1) {
116 #chomp($line); # remove newline #don't know why, but this remove newline in @current1 .... !
117 my @temp = split(/\t/,$line);
118 AddNewOptionDefinition
($temp[1] . ' ' . $temp[2]);
121 open(FILE
, "$filename2") or die 'Unable to open fixed leases file.';
122 our @current2 = <FILE
>;
125 # Check Settings1 first because they are needed by &buildconf
126 if ($dhcpsettings{'ACTION'} eq $Lang::tr
{'save'}) {
127 foreach my $itf (@ITFs) {
128 if ($dhcpsettings{"ENABLE_${itf}"} eq 'on' ) {
129 # "Start" is defined, need "End" and vice versa
130 if ($dhcpsettings{"START_ADDR_${itf}"}) {
131 if (!(&General
::validip
($dhcpsettings{"START_ADDR_${itf}"}))) {
132 $errormessage = "DHCP on ${itf}: " . $Lang::tr
{'invalid start address'};
135 if (!$dhcpsettings{"END_ADDR_${itf}"}) {
136 $errormessage = "DHCP on ${itf}: " . $Lang::tr
{'invalid end address'};
139 if (! &General
::IpInSubnet
( $dhcpsettings{"START_ADDR_${itf}"},
140 $netsettings{"${itf}_NETADDRESS"},
141 $netsettings{"${itf}_NETMASK"})) {
142 $errormessage = "DHCP on ${itf}: " . $Lang::tr
{'invalid start address'};
147 if ($dhcpsettings{"END_ADDR_${itf}"}) {
148 if (!(&General
::validip
($dhcpsettings{"END_ADDR_${itf}"}))) {
149 $errormessage = "DHCP on ${itf}: " . $Lang::tr
{'invalid end address'};
152 if (!$dhcpsettings{"START_ADDR_${itf}"}) {
153 $errormessage = "DHCP on ${itf}: " . $Lang::tr
{'invalid start address'};
156 if (! &General
::IpInSubnet
( $dhcpsettings{"END_ADDR_${itf}"},
157 $netsettings{"${itf}_NETADDRESS"},
158 $netsettings{"${itf}_NETMASK"})) {
159 $errormessage = "DHCP on ${itf}: " . $Lang::tr
{'invalid end address'};
162 #swap if necessary! (support 255.255.0.0 range, I doubt we need more) GE
163 my @startoct = split (/\./, $dhcpsettings{"START_ADDR_${itf}"});
164 my @endoct = split (/\./, $dhcpsettings{"END_ADDR_${itf}"});
165 if ( $endoct[2]*256+$endoct[3] < $startoct[2]*256+$startoct[3] ) {
166 ($dhcpsettings{"START_ADDR_${itf}"},$dhcpsettings{"END_ADDR_${itf}"}) =
167 ($dhcpsettings{"END_ADDR_${itf}"},$dhcpsettings{"START_ADDR_${itf}"});
171 if (!($dhcpsettings{"DEFAULT_LEASE_TIME_${itf}"} =~ /^\d+$/)) {
172 $errormessage = "DHCP on ${itf}: " . $Lang::tr
{'invalid default lease time'} . $dhcpsettings{'DEFAULT_LEASE_TIME_${itf}'};
176 if (!($dhcpsettings{"MAX_LEASE_TIME_${itf}"} =~ /^\d+$/)) {
177 $errormessage = "DHCP on ${itf}: " . $Lang::tr
{'invalid max lease time'} . $dhcpsettings{'MAX_LEASE_TIME_${itf}'};
181 if ($dhcpsettings{"DNS1_${itf}"}) {
182 if (!(&General
::validip
($dhcpsettings{"DNS1_${itf}"}))) {
183 $errormessage = "DHCP on ${itf}: " . $Lang::tr
{'invalid primary dns'};
187 if ($dhcpsettings{"DNS2_${itf}"}) {
188 if (!(&General
::validip
($dhcpsettings{"DNS2_${itf}"}))) {
189 $errormessage = "DHCP on ${itf}: " . $Lang::tr
{'invalid secondary dns'};
192 if (! $dhcpsettings{"DNS1_${itf}"}) {
193 $errormessage = "DHCP on ${itf}: " . $Lang::tr
{'cannot specify secondary dns without specifying primary'};
198 if ($dhcpsettings{"WINS1_${itf}"}) {
199 if (!(&General
::validip
($dhcpsettings{"WINS1_${itf}"}))) {
200 $errormessage = "DHCP on ${itf}: " . $Lang::tr
{'invalid wins address'};
204 if ($dhcpsettings{"WINS2_${itf}"}) {
205 if (!(&General
::validip
($dhcpsettings{"WINS2_${itf}"}))) {
206 $errormessage = "DHCP on ${itf}: " . $Lang::tr
{'invalid wins address'};
209 if (! $dhcpsettings{"WINS1_${itf}"} ) {
210 $errormessage = "DHCP on ${itf}: " . $Lang::tr
{'cannot specify secondary wins without specifying primary'};
214 if ($dhcpsettings{"NEXT_${itf}"}) {
215 if (!(&General
::validip
($dhcpsettings{"NEXT_${itf}"}))) {
216 $errormessage = "next-server on ${itf}: " . $Lang::tr
{'invalid ip'};
220 if ($dhcpsettings{"NTP1_${itf}"}) {
221 if (!(&General
::validip
($dhcpsettings{"NTP1_${itf}"}))) {
222 $errormessage = "DHCP on ${itf}: " . $Lang::tr
{'invalid primary ntp'};
225 if ($dhcpsettings{"NTP1_${itf}"} eq $netsettings{"${itf}_ADDRESS"} && ($timesettings{'ENABLECLNTP'} ne 'on')) {
226 $warnNTPmessage = "DHCP on ${itf}: " . $Lang::tr
{'local ntp server specified but not enabled'};
230 if ($dhcpsettings{"NTP2_${itf}"}) {
231 if (!(&General
::validip
($dhcpsettings{"NTP2_${itf}"}))) {
232 $errormessage = "DHCP on ${itf}: " . $Lang::tr
{'invalid secondary ntp'};
235 if ($dhcpsettings{"NTP2_${itf}"} eq $netsettings{"${itf}_ADDRESS"} && ($timesettings{'ENABLECLNTP'} ne 'on')) {
236 $warnNTPmessage = "DHCP on ${itf}: " . $Lang::tr
{'local ntp server specified but not enabled'};
239 if (! $dhcpsettings{"NTP1_${itf}"}) {
240 $errormessage = "DHCP on ${itf}: " . $Lang::tr
{'cannot specify secondary ntp without specifying primary'};
245 }#loop interface verify
247 map (delete ($dhcpsettings{$_}) ,@nosaved,'ACTION','KEY1','KEY2'); # Must not be saved
248 &General
::writehash
($setting, \
%dhcpsettings); # Save good settings
249 $dhcpsettings{'ACTION'} = $Lang::tr
{'save'}; # create an 'ACTION'
250 map ($dhcpsettings{$_} = '',@nosaved,'KEY1','KEY2'); # and reinit vars to empty
252 ERROR
: # Leave the faulty field untouched
254 &General
::readhash
($setting, \
%dhcpsettings); # Get saved settings and reset to good if needed
257 ## Sorting of fixed leases
258 if ($ENV{'QUERY_STRING'} =~ /^FETHER|^FIPADDR/ ) {
259 my $newsort=$ENV{'QUERY_STRING'};
260 my $act=$dhcpsettings{'SORT_FLEASELIST'};
261 #Reverse actual sort ?
262 if ($act =~ $newsort) {
269 $dhcpsettings{'SORT_FLEASELIST'}=$newsort;
270 map (delete ($dhcpsettings{$_}) ,@nosaved,'ACTION','KEY1','KEY2'); # Must never be saved
271 &General
::writehash
($setting, \
%dhcpsettings);
273 $dhcpsettings{'ACTION'} = 'SORT'; # create an 'ACTION'
274 map ($dhcpsettings{$_} = '',@nosaved,'KEY1','KEY2');# and reinit vars to empty
277 #Sorting of allocated leases
278 &Header
::CheckSortOrder
;
281 ## Now manipulate the two multi-line list with Settings2.
282 # '1' suffix is for ADVANCED OPTIONS
283 # '2' suffix is for FIXED LEASES
285 # Toggle enable/disable field on specified options.
287 if ($dhcpsettings{'ACTION'} eq $Lang::tr
{'toggle enable disable'}.'1') {
289 chomp(@current1[$dhcpsettings{'KEY1'}]);
290 my @temp = split(/\t/,@current1[$dhcpsettings{'KEY1'}]); #use TAB separator !
291 $temp[0] = $temp[0] eq 'on' ?
'' : 'on'; # Toggle the field
292 @current1[$dhcpsettings{'KEY1'}] = join ("\t",@temp)."\n";
293 $dhcpsettings{'KEY1'} = ''; # End edit mode
294 &General
::log($Lang::tr
{'dhcp advopt modified'});
295 open(FILE
, ">$filename1") or die 'Unable to open dhcp advanced options file.';
296 print FILE
@current1;
299 #Write changes to dhcpd.conf.
305 if ($dhcpsettings{'ACTION'} eq $Lang::tr
{'add'}.'1' &&
306 $dhcpsettings{'SUBMIT'} ne $Lang::tr
{'dhcp advopt help'}) {
307 $dhcpsettings{'ADVOPT_NAME'} =~ s/[^ \w-]//g; # prevent execution of code by removing everything except letters/space
308 $dhcpsettings{'ADVOPT_DATA'} =~ s/`//g; # back tik ` ? not allowed !
310 if ($dhcpsettings{'ADVOPT_DATA'} eq '') {
311 $errormessage=$Lang::tr
{'dhcp advopt blank value'};
314 # Test for a new option definition string (join field name & data)
315 if (ExistNewOptionDefinition
($dhcpsettings{'ADVOPT_NAME'} . ' ' . $dhcpsettings{'ADVOPT_DATA'})) {
316 #only edit permitted if option definition exists
317 $errormessage = $Lang::tr
{'dhcp advopt definition exists'} if ($dhcpsettings{'KEY1'} eq '');
318 $dhcpsettings{'ADVOPT_ENABLED'} = 'on'; # force active
319 map ($dhcpsettings{"ADVOPT_SCOPE_$_"} = 'off', @ITFs); # force global
320 } elsif (AddNewOptionDefinition
($dhcpsettings{'ADVOPT_NAME'} . ' ' . $dhcpsettings{'ADVOPT_DATA'})) {
321 #was a new option definition
322 $dhcpsettings{'ADVOPT_ENABLED'} = 'on'; # force active
323 map ($dhcpsettings{"ADVOPT_SCOPE_$_"} = 'off', @ITFs); # force global
324 } elsif (ValidNewOption
($dhcpsettings{'ADVOPT_NAME'} . ' ' . $dhcpsettings{'ADVOPT_DATA'})) {
326 } elsif (! `grep "\$option $dhcpsettings{'ADVOPT_NAME'} " $filename3`) {
327 $errormessage=$Lang::tr
{'dhcp advopt unknown'}.': '.$dhcpsettings{'ADVOPT_NAME'};
330 unless ($errormessage) {
333 foreach my $itf (@ITFs) { # buils "RED,GREEN,ORANGE,... based on selection
334 $scope .= $dhcpsettings{"ADVOPT_SCOPE_${itf}"} eq 'on' ?
"\t$itf" : "\toff" ;
336 if ($dhcpsettings{'KEY1'} eq '') { #add or edit ? TAB separator !
337 unshift (@current1, "$dhcpsettings{'ADVOPT_ENABLED'}\t$dhcpsettings{'ADVOPT_NAME'}\t$dhcpsettings{'ADVOPT_DATA'}$scope\n");
338 &General
::log($Lang::tr
{'dhcp advopt added'});
340 @current1[$dhcpsettings{'KEY1'}] = "$dhcpsettings{'ADVOPT_ENABLED'}\t$dhcpsettings{'ADVOPT_NAME'}\t$dhcpsettings{'ADVOPT_DATA'}$scope\n";
341 $dhcpsettings{'KEY1'} = ''; # End edit mode
342 &General
::log($Lang::tr
{'dhcp advopt modified'});
345 #Write changes to dhcpd.conf.
346 &sortcurrent1
; # sort newly added/modified entry
347 &buildconf
; # before calling buildconf which use fixed lease file !
351 if ($dhcpsettings{'ACTION'} eq $Lang::tr
{'edit'}.'1') {
353 my $line = @current1[$dhcpsettings{'KEY1'}];
355 my @temp = split(/\t/, $line);
356 $dhcpsettings{'ADVOPT_ENABLED'}=$temp[0];
357 $dhcpsettings{'ADVOPT_NAME'}=$temp[1];
358 $dhcpsettings{'ADVOPT_DATA'}=$temp[2];
360 # read next fields which are the name (color) of an interface if this interface is scoped
361 for (my $key=0; $key<@ITFs; $key++) {
362 my $itf = $temp[3+$key];
363 if ($itf ne 'off') # Only is an interface name is read
365 $dhcpsettings{"ADVOPT_SCOPE_${itf}"} = 'on';
370 if ($dhcpsettings{'ACTION'} eq $Lang::tr
{'remove'}.'1') {
371 splice (@current1,$dhcpsettings{'KEY1'},1);
372 open(FILE
, ">$filename1") or die 'Unable to open dhcp advanced options file.';
373 print FILE
@current1;
375 $dhcpsettings{'KEY1'} = ''; # End remove mode
376 &General
::log($Lang::tr
{'dhcp advopt removed'});
377 #Write changes to dhcpd.conf.
383 # Toggle enable/disable field on specified lease.
384 if ($dhcpsettings{'ACTION'} eq $Lang::tr
{'toggle enable disable'}.'2') {
386 chomp(@current2[$dhcpsettings{'KEY2'}]);
387 my @temp = split(/\,/,@current2[$dhcpsettings{'KEY2'}]);
388 $temp[2] = $temp[2] eq 'on' ?
'' : 'on'; # Toggle the field
389 @current2[$dhcpsettings{'KEY2'}] = join (',',@temp)."\n";
390 $dhcpsettings{'KEY2'} = ''; # End edit mode
391 &General
::log($Lang::tr
{'fixed ip lease modified'});
392 open(FILE
, ">$filename2") or die 'Unable to open fixed leases file.';
393 print FILE
@current2;
396 #Write changes to dhcpd.conf.
400 if ($dhcpsettings{'ACTION'} eq $Lang::tr
{'add'}.'2') {
401 $dhcpsettings{'FIX_MAC'} =~ tr/-/:/;
402 unless(&General
::validip
($dhcpsettings{'FIX_ADDR'})) { $errormessage = $Lang::tr
{'invalid fixed ip address'}; }
403 unless(&General
::validmac
($dhcpsettings{'FIX_MAC'})) { $errormessage = $Lang::tr
{'invalid fixed mac address'}; }
404 if ($dhcpsettings{'FIX_NEXTADDR'}) {
405 unless(&General
::validip
($dhcpsettings{'FIX_NEXTADDR'})) { $errormessage = $Lang::tr
{'invalid fixed ip address'}; }
409 CHECK:foreach my $line (@current2) {
410 my @temp = split(/\,/,$line);
411 if($dhcpsettings{'KEY2'} ne $key) {
412 # same MAC is OK on different subnets. This test is not complete because
413 # if ip are not inside a known subnet, I don't warn.
414 # Also it may be needed to put duplicate fixed lease in their right subnet definition..
415 foreach my $itf (@ITFs) {
416 my $scoped = &General
::IpInSubnet
($dhcpsettings{'FIX_ADDR'},
417 $netsettings{"${itf}_NETADDRESS"},
418 $netsettings{"${itf}_NETMASK"}) &&
419 $dhcpsettings{"ENABLE_${itf}"} eq 'on';
421 (lc($dhcpsettings{'FIX_MAC'}) eq lc($temp[0])) &&
422 &General
::IpInSubnet
($temp[1],
423 $netsettings{"${itf}_NETADDRESS"},
424 $netsettings{"${itf}_NETMASK"})) {
425 $errormessage = "$Lang::tr{'mac address in use'} $dhcpsettings{'FIX_MAC'}";
433 unless ($errormessage) {
434 $dhcpsettings{'FIX_REMARK'} = &Header
::cleanhtml
($dhcpsettings{'FIX_REMARK'});
435 $dhcpsettings{'FIX_NEXTADDR'} = &Header
::cleanhtml
($dhcpsettings{'FIX_NEXTADDR'});
436 $dhcpsettings{'FIX_FILENAME'} = &Header
::cleanhtml
($dhcpsettings{'FIX_FILENAME'});
437 $dhcpsettings{'FIX_ROOTPATH'} = &Header
::cleanhtml
($dhcpsettings{'FIX_ROOTPATH'});
438 if ($dhcpsettings{'KEY2'} eq '') { #add or edit ?
439 unshift (@current2, "$dhcpsettings{'FIX_MAC'},$dhcpsettings{'FIX_ADDR'},$dhcpsettings{'FIX_ENABLED'},$dhcpsettings{'FIX_NEXTADDR'},$dhcpsettings{'FIX_FILENAME'},$dhcpsettings{'FIX_ROOTPATH'},$dhcpsettings{'FIX_REMARK'}\n");
440 &General
::log($Lang::tr
{'fixed ip lease added'});
442 @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";
443 $dhcpsettings{'KEY2'} = ''; # End edit mode
444 &General
::log($Lang::tr
{'fixed ip lease modified'});
447 #Write changes to dhcpd.conf.
448 &sortcurrent2
; # sort newly added/modified entry
449 &buildconf
; # before calling buildconf which use fixed lease file !
453 if ($dhcpsettings{'ACTION_ALL'} eq '+') {
455 foreach (keys %dhcpsettings) {
456 if (/^(\d+\.\d+\.\d+\.\d+)-([0-9a-fA-F:]+)$/) { # checked names are index of the line
459 if (!grep (/$2/,@current2)) {
460 unshift (@current2, "$mac,$ip,on,,,,imported\n");
466 #Write changes to dhcpd.conf.
467 $warnNTPmessage = $Lang::tr
{'fixed ip lease added'}."($news)";
468 &General
::log($warnNTPmessage);
469 &sortcurrent2
; # sort newly added/modified entry
470 &buildconf
; # before calling buildconf which use fixed lease file !
474 if ($dhcpsettings{'ACTION'} eq $Lang::tr
{'edit'}.'2') {
476 my $line = @current2[$dhcpsettings{'KEY2'}];
478 my @temp = split(/\,/, $line);
479 $dhcpsettings{'FIX_MAC'}=$temp[0];
480 $dhcpsettings{'FIX_ADDR'}=$temp[1];
481 $dhcpsettings{'FIX_ENABLED'}=$temp[2];
482 $dhcpsettings{'FIX_NEXTADDR'}=$temp[3];
483 $dhcpsettings{'FIX_FILENAME'}=$temp[4];
484 $dhcpsettings{'FIX_ROOTPATH'}=$temp[5];
485 $dhcpsettings{'FIX_REMARK'}=$temp[6];
488 if ($dhcpsettings{'ACTION'} eq $Lang::tr
{'remove'}.'2') {
489 splice (@current2,$dhcpsettings{'KEY2'},1);
490 open(FILE
, ">$filename2") or die 'Unable to open fixed lease file.';
491 print FILE
@current2;
493 $dhcpsettings{'KEY2'} = ''; # End remove mode
494 &General
::log($Lang::tr
{'fixed ip lease removed'});
495 #Write changes to dhcpd.conf.
503 if ($dhcpsettings{'ACTION'} eq '' ) { # First launch from GUI
505 # Set default DHCP values only if blank and disabled
506 foreach my $itf (@ITFs) {
507 if ($dhcpsettings{"ENABLE_${itf}"} ne 'on' ) {
508 $dhcpsettings{"DNS1_${itf}"} = $netsettings{"${itf}_ADDRESS"};
509 $dhcpsettings{"DEFAULT_LEASE_TIME_${itf}"} = '60';
510 $dhcpsettings{"MAX_LEASE_TIME_${itf}"} = '120';
511 $dhcpsettings{"DOMAIN_NAME_${itf}"} = $mainsettings{'DOMAINNAME'};
514 $dhcpsettings{'FIX_ENABLED'} = 'on';
517 &Header
::openpage
($Lang::tr
{'dhcp configuration'}, 1, '');
518 &Header
::openbigbox
('100%', 'left', '', $errormessage);
521 &Header
::openbox
('100%', 'left', $Lang::tr
{'error messages'});
522 print "<font class='base'>$errormessage </font>\n";
525 if ($warnNTPmessage) {
526 $warnNTPmessage = "<font color=${Header::colourred}><b>$Lang::tr{'capswarning'}</b></font>: $warnNTPmessage";
529 &Header
::openbox
('100%', 'left', 'DHCP');
530 print "<form method='post' action='$ENV{'SCRIPT_NAME'}'>";
532 foreach my $itf (@ITFs) {
534 $checked{'ENABLE'}{'on'} = ( $dhcpsettings{"ENABLE_${itf}"} ne 'on') ?
'' : "checked='checked'";
535 $checked{'ENABLEBOOTP'}{'on'} = ( $dhcpsettings{"ENABLEBOOTP_${itf}"} ne 'on') ?
'' : "checked='checked'";
537 if ($netsettings{"${itf}_DEV"} ne '' ) { # Show only defined interface
542 <td width='25%' class='boldbase'><b><font color='${lc_itf}'>$Lang::tr{"$lc_itf interface"}</font></b></td>
543 <td class='base'>$Lang::tr{'enabled'}
544 <input type='checkbox' name='ENABLE_${itf}' $checked{'ENABLE'}{'on'} /></td>
545 <td width='25%' class='base'>$Lang::tr{'ip address'}<br />$Lang::tr{'netmask'}:</td><td><b>$netsettings{"${itf}_ADDRESS"}<br />$netsettings{"${itf}_NETMASK"}</b></td>
547 <td width='25%' class='base'>$Lang::tr{'start address'} <img src='/blob.gif' alt='*' /></td>
548 <td width='25%'><input type='text' name='START_ADDR_${itf}' value='$dhcpsettings{"START_ADDR_${itf}"}' /></td>
549 <td width='25%' class='base'>$Lang::tr{'end address'} <img src='/blob.gif' alt='*' /></td>
550 <td width='25%'><input type='text' name='END_ADDR_${itf}' value='$dhcpsettings{"END_ADDR_${itf}"}' /></td>
552 <td class='base'>$Lang::tr{'default lease time'}</td>
553 <td><input type='text' name='DEFAULT_LEASE_TIME_${itf}' value='$dhcpsettings{"DEFAULT_LEASE_TIME_${itf}"}' /></td>
554 <td class='base'>$Lang::tr{'max lease time'}</td>
555 <td><input type='text' name='MAX_LEASE_TIME_${itf}' value='$dhcpsettings{"MAX_LEASE_TIME_${itf}"}' /></td>
557 <td class='base'>$Lang::tr{'domain name suffix'} <img src='/blob.gif' alt='*' /></td>
558 <td><input type='text' name='DOMAIN_NAME_${itf}' value='$dhcpsettings{"DOMAIN_NAME_${itf}"}' /></td>
559 <td>$Lang::tr{'dhcp allow bootp'}:</td>
560 <td><input type='checkbox' name='ENABLEBOOTP_${itf}' $checked{'ENABLEBOOTP'}{'on'} /></td>
562 <td class='base'>$Lang::tr{'primary dns'}</td>
563 <td><input type='text' name='DNS1_${itf}' value='$dhcpsettings{"DNS1_${itf}"}' /></td>
564 <td class='base'>$Lang::tr{'secondary dns'} <img src='/blob.gif' alt='*' /></td>
565 <td><input type='text' name='DNS2_${itf}' value='$dhcpsettings{"DNS2_${itf}"}' /></td>
567 <td class='base'>$Lang::tr{'primary ntp server'}: <img src='/blob.gif' alt='*' /></td>
568 <td><input type='text' name='NTP1_${itf}' value='$dhcpsettings{"NTP1_${itf}"}' /></td>
569 <td class='base'>$Lang::tr{'secondary ntp server'}: <img src='/blob.gif' alt='*' /></td>
570 <td><input type='text' name='NTP2_${itf}' value='$dhcpsettings{"NTP2_${itf}"}' /></td>
572 <td class='base'>$Lang::tr{'primary wins server address'}: <img src='/blob.gif' alt='*' /></td>
573 <td><input type='text' name='WINS1_${itf}' value='$dhcpsettings{"WINS1_${itf}"}' /></td>
574 <td class='base'>$Lang::tr{'secondary wins server address'}: <img src='/blob.gif' alt='*' /></td>
575 <td><input type='text' name='WINS2_${itf}' value='$dhcpsettings{"WINS2_${itf}"}' /></td>
577 <td class='base'>next-server: <img src='/blob.gif' alt='*' /></td>
578 <td><input type='text' name='NEXT_${itf}' value='$dhcpsettings{"NEXT_${itf}"}' /></td>
579 <td class='base'>filename: <img src='/blob.gif' alt='*' /></td>
580 <td><input type='text' name='FILE_${itf}' value='$dhcpsettings{"FILE_${itf}"}' /></td>
586 }# Show only defined interface
591 <td class='base' width='25%'><img src='/blob.gif' align='top' alt='*' /> $Lang::tr{'this field may be blank'}</td>
592 <td class='base' width='30%'>$warnNTPmessage</td>
593 <td width='40%' align='center'><input type='submit' name='ACTION' value='$Lang::tr{'save'}' /></td>
594 <td width='5%' align='right'> </td>
603 &Header
::openbox
('100%', 'left', $Lang::tr
{'dhcp advopt list'});
604 # DHCP Advanced options settings
606 $checked{'ADVOPT_ENABLED'}{'on'} = ($dhcpsettings{'ADVOPT_ENABLED'} ne 'on') ?
'' : "checked='checked'";
608 print "<form method='post' action='$ENV{'SCRIPT_NAME'}'><table width='100%'>";
609 my $buttontext = $Lang::tr
{'add'};
610 if ($dhcpsettings{'KEY1'} ne '') {
611 $buttontext = $Lang::tr
{'update'};
612 print "<tr><td class='boldbase'><b>$Lang::tr{'dhcp advopt edit'}</b></td></tr>";
614 print "<tr><td class='boldbase'><b>$Lang::tr{'dhcp advopt add'}</b></td></tr>"
617 #search if the 'option' is in the list and print the syntax model
618 my $opt = `grep "\$option $dhcpsettings{'ADVOPT_NAME'} " $filename3`;
620 $opt =~ s/option $dhcpsettings{'ADVOPT_NAME'}/Syntax:/; # "option xyz abc" => "syntax: abc"
622 $opt = "<tr><td></td><td></td><td colspan='2'>$opt</td></tr>";
626 <td class='base'>$Lang::tr{'dhcp advopt name'}:</td>
627 <td><input type='text' name='ADVOPT_NAME' value='$dhcpsettings{'ADVOPT_NAME'}' size='18' /></td>
628 <td class='base'>$Lang::tr{'dhcp advopt value'}:</td>
629 <td><input type='text' name='ADVOPT_DATA' value='$dhcpsettings{'ADVOPT_DATA'}' size='40' /></td>
631 <td class='base'>$Lang::tr{'enabled'}</td><td><input type='checkbox' name='ADVOPT_ENABLED' $checked{'ADVOPT_ENABLED'}{'on'} /></td>
632 <td class='base'>$Lang::tr{'dhcp advopt scope'}: <img src='/blob.gif' alt='*' /></td>
637 # Put a checkbox for each interface. Checkbox visible disabled if interface is disabled
638 foreach my $itf (@ITFs) {
640 $checked{'ADVOPT_SCOPE_${itf}'}{'on'} = $dhcpsettings{"ADVOPT_SCOPE_${itf}"} ne 'on' ?
'' : "checked='checked'";
641 print "$Lang::tr{\"${lc_itf}\"} <input type='checkbox' name='ADVOPT_SCOPE_${itf}' $checked{'ADVOPT_SCOPE_${itf}'}{'on'} ";
642 print $dhcpsettings{"ENABLE_${itf}"} eq 'on' ?
"/>" : "disabled='disabled' />";
643 print " ";
653 <td class='base' width='50%'><img src='/blob.gif' align='top' alt='*' /> $Lang::tr{'dhcp advopt scope help'}</td>
654 <td width='50%' align='center'>
655 <input type='hidden' name='ACTION' value='$Lang::tr{'add'}1' />
656 <input type='submit' name='SUBMIT' value='$buttontext' />
657 <input type='submit' name='SUBMIT' value='$Lang::tr{'dhcp advopt help'}' />
658 <input type='hidden' name='KEY1' value='$dhcpsettings{'KEY1'}' />
665 #Edited line number (KEY1) passed until cleared by 'save' or 'remove' or 'new sort order'
667 # print help taken from the file describing options
668 if ($dhcpsettings{'SUBMIT'} eq $Lang::tr
{'dhcp advopt help'}) {
670 print "<table width='100%'>";
671 print "<tr><td width='30%'><b>$Lang::tr{'dhcp advopt name'}</b></td><td width='70%'><b>$Lang::tr{'dhcp advopt value'}</b></td>";
672 open(FILE
, "$filename3");
673 my @current3 = <FILE
>;
675 foreach my $line (@current3) {
676 $line =~ /option ([a-z0-9-]+) (.*);/;
677 print "<tr><td>$1</td><td>$2</td></tr>\n";
679 print "<tr><td colspan='2'><hr /></td></tr>\n";
680 print '<tr><td>string type</td><td>"quoted string" or 00:01:FF...</td></tr>';
681 print '<tr><td>ip-address type </td><td>10.0.0.1 | www.dot.com</td></tr>';
682 print '<tr><td>int,uint types</td><td>numbers</td></tr>';
683 print '<tr><td>flag type</td><td>on | off</td></tr>';
686 print "<table width='100%'>";
687 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>";
688 print "<tr><td>any-name </td><td> code NNN=$OptionTypes</td></tr>";
689 print '<tr><td>a-string</td><td>code 100=string</td></tr>';
690 print '<tr><td>a-number</td><td>code 101=signed integer 8</td></tr>';
691 print '<tr><td>wpad</td><td>code 252=text</td></tr>';
692 print '<tr><td>wpad</td><td>"http://www.server.fr/path-to/proxy.pac"</td></tr>';
701 <td width='30%' class='boldbase' align='center'><b>$Lang::tr{'dhcp advopt name'}</b></td>
702 <td width='50%' class='boldbase' align='center'><b>$Lang::tr{'dhcp advopt value'}</b></td>
703 <td width='20%' class='boldbase' align='center'><b>$Lang::tr{'dhcp advopt scope'}</b></td>
704 <td colspan='3' class='boldbase' align='center'><b>$Lang::tr{'action'}</b></td>
709 foreach my $line (@current1) {
712 chomp($line); # remove newline
713 my @temp = split(/\t/,$line);
715 if ($temp[0] eq "on") {
717 $gdesc = $Lang::tr
{'click to disable'};
720 $gdesc = $Lang::tr
{'click to enable'};
723 if ($dhcpsettings{'KEY1'} eq $key) {
724 print "<tr bgcolor='${Header::colouryellow}'>";
726 print "<tr bgcolor='$color{'color22'}'>";
728 print "<tr bgcolor='$color{'color20'}'>";
732 <td align='center'>$temp[1]</td>
733 <td align='center'>$temp[2]</td>
737 # Prepare a global flag to make easy reading
739 my $disabledTogle = '';
740 my $disabledEditRemove = '';
741 if ( ExistNewOptionDefinition
($temp[1] . ' ' . $temp[2]) ) {
742 $global = $Lang::tr
{'dhcp advopt definition'};
743 $disabledTogle = "disabled='disabled'";
744 # Search if it is a used NewOptionDefinition to also disable edit & delete
745 $disabledEditRemove = "disabled='disabled'" if (IsUsedNewOptionDefinition
($temp[1], $temp[2]));
747 $global = $Lang::tr
{'dhcp advopt scope global'};
751 # Print each checked interface
752 for (my $key=0; $key<@ITFs; $key++) {
753 my $itf = $temp[3+$key];
754 if ($itf ne 'off') { # Only if an interface name is read
756 $global=''; # fall to local scope !
762 <form method='post' action='$ENV{'SCRIPT_NAME'}'>
763 <input type='hidden' name='ACTION' value='$Lang::tr{'toggle enable disable'}1' />
764 <input $disabledTogle type='image' name='$Lang::tr{'toggle enable disable'}' src='/images/$gif' alt='$gdesc' title='$gdesc' />
765 <input type='hidden' name='KEY1' value='$key' />
770 <form method='post' action='$ENV{'SCRIPT_NAME'}'>
771 <input type='hidden' name='ACTION' value='$Lang::tr{'edit'}1' />
772 <input $disabledEditRemove type='image' name='$Lang::tr{'edit'}' src='/images/edit.gif' alt='$Lang::tr{'edit'}' title='$Lang::tr{'edit'}' />
773 <input type='hidden' name='KEY1' value='$key' />
778 <form method='post' action='$ENV{'SCRIPT_NAME'}'>
779 <input type='hidden' name='ACTION' value='$Lang::tr{'remove'}1' />
780 <input $disabledEditRemove type='image' name='$Lang::tr{'remove'}' src='/images/delete.gif' alt='$Lang::tr{'remove'}' title='$Lang::tr{'remove'}' />
781 <input type='hidden' name='KEY1' value='$key' />
792 # If there are dhcp options, print Key to action icons
797 <td class='boldbase'> <b>$Lang::tr{'legend'}: </b></td>
798 <td><img src='/images/on.gif' alt='$Lang::tr{'click to disable'}' /></td>
799 <td class='base'>$Lang::tr{'click to disable'}</td>
800 <td> </td>
801 <td><img src='/images/off.gif' alt='$Lang::tr{'click to enable'}' /></td>
802 <td class='base'>$Lang::tr{'click to enable'}</td>
803 <td> </td>
804 <td><img src='/images/edit.gif' alt='$Lang::tr{'edit'}' /></td>
805 <td class='base'>$Lang::tr{'edit'}</td>
806 <td> </td>
807 <td><img src='/images/delete.gif' alt='$Lang::tr{'remove'}' /></td>
808 <td class='base'>$Lang::tr{'remove'}</td>
816 &Header
::openbox
('100%', 'left', $Lang::tr
{'current fixed leases'});
817 # Fixed leases screens
818 $checked{'FIX_ENABLED'}{'on'} = ($dhcpsettings{'FIX_ENABLED'} ne 'on') ?
'' : "checked='checked'";
820 $buttontext = $Lang::tr
{'add'};
821 print "<form method='post' action='$ENV{'SCRIPT_NAME'}'><table width='100%'>";
823 if ($dhcpsettings{'KEY2'} ne '') {
824 $buttontext = $Lang::tr
{'update'};
825 print "<tr><td class='boldbase' colspan='3'><b>$Lang::tr{'edit an existing lease'}</b></td></tr>";
827 print "<tr><td class='boldbase' colspan='3'><b>$Lang::tr{'add new lease'}</b></td></tr>"
831 <td class='base'>$Lang::tr{'mac address'}:</td>
832 <td><input type='text' name='FIX_MAC' value='$dhcpsettings{'FIX_MAC'}' size='18' /></td>
833 <td class='base'>$Lang::tr{'ip address'}:</td>
834 <td><input type='text' name='FIX_ADDR' value='$dhcpsettings{'FIX_ADDR'}' size='18' /></td>
835 <td class='base'>$Lang::tr{'remark'}: <img src='/blob.gif' alt='*' /></td>
836 <td><input type='text' name='FIX_REMARK' value='$dhcpsettings{'FIX_REMARK'}' size='18' /></td>
838 <td class='base'>$Lang::tr{'enabled'}</td><td><input type='checkbox' name='FIX_ENABLED' $checked{'FIX_ENABLED'}{'on'} /></td>
840 <td colspan = '3'><b>$Lang::tr{'dhcp bootp pxe data'}</b></td>
842 <td class='base'>next-server: <img src='/blob.gif' alt='*' /></td>
843 <td><input type='text' name='FIX_NEXTADDR' value='$dhcpsettings{'FIX_NEXTADDR'}' size='18' /></td>
844 <td class='base'>filename: <img src='/blob.gif' alt='*' /></td>
845 <td><input type='text' name='FIX_FILENAME' value='$dhcpsettings{'FIX_FILENAME'}' size='18' /></td>
846 <td class='base'>root path: <img src='/blob.gif' alt='*' /></td>
847 <td><input type='text' name='FIX_ROOTPATH' value='$dhcpsettings{'FIX_ROOTPATH'}' size='18' /></td>
853 <td class='base' width='50%'><img src='/blob.gif' align='top' alt='*' /> $Lang::tr{'this field may be blank'}</td>
854 <td width='50%' align='center'>
855 <input type='hidden' name='ACTION' value='$Lang::tr{'add'}2' />
856 <input type='submit' name='SUBMIT' value='$buttontext' />
857 <input type='hidden' name='KEY2' value='$dhcpsettings{'KEY2'}' /></td>
863 #Edited line number (KEY2) passed until cleared by 'save' or 'remove' or 'new sort order'
869 <td width='20%' align='center'><a href='$ENV{'SCRIPT_NAME'}?FETHER'><b>$Lang::tr{'mac address'}</b></a></td>
870 <td width='20%' align='center'><a href='$ENV{'SCRIPT_NAME'}?FIPADDR'><b>$Lang::tr{'ip address'}</b></a></td>
871 <td width='15%' align='center'><b>$Lang::tr{'remark'}</b></td>
872 <td width='15%' class='boldbase' align='center'><b>next-server</b></td>
873 <td width='15%' class='boldbase' align='center'><b>filename</b></td>
874 <td width='15%' class='boldbase' align='center'><b>root path</b></td>
875 <td colspan='3' class='boldbase' align='center'><b>$Lang::tr{'action'}</b></td>
881 my %macdupl = (); # Duplicate MACs have to be on different subnets
884 # mark duplicate ip or duplicate MAC
885 foreach my $line (@current2) {
886 my @temp = split(/\,/,$line);
887 $macdupl{$temp[0]} += 1;
888 if ($macdupl{$temp[0]} > 1) {
889 $ipdup = 1; # Flag up duplicates for use later
891 $ipinuse{$temp[1]} += 1;
892 if ($ipinuse{$temp[1]} > 1) {
893 $ipdup = 1; # Flag up duplicates for use later
895 # Mark IP addresses outwith known subnets
896 $ipoutside{$temp[1]} = 1;
897 foreach my $itf (@ITFs) {
898 if ( &General
::IpInSubnet
($temp[1],
899 $netsettings{"${itf}_NETADDRESS"},
900 $netsettings{"${itf}_NETMASK"})) {
901 $ipoutside{$temp[1]} = 0;
907 foreach my $line (@current2) {
910 chomp($line); # remove newline
911 my @temp = split(/\,/,$line);
913 if ($temp[2] eq "on") {
915 $gdesc = $Lang::tr
{'click to disable'};
918 $gdesc = $Lang::tr
{'click to enable'};
921 if ($dhcpsettings{'KEY2'} eq $key) {
922 print "<tr bgcolor='${Header::colouryellow}'>";
924 print "<tr bgcolor='$color{'color22'}'>";
926 print "<tr bgcolor='$color{'color20'}'>";
933 if ($ipinuse{$temp[1]} > 1) {
937 if ($macdupl{$temp[0]} > 1) {
941 if ($ipoutside{$temp[1]} > 0) {
942 $TAG4 = "bgcolor='orange'" if ($dhcpsettings{'KEY2'} ne $key);
946 <td align='center'>$TAG2$temp[0]$TAG3</td>
947 <td align='center' $TAG4>$TAG0$temp[1]$TAG1</td>
948 <td align='center'>$temp[6] </td>
949 <td align='center'>$temp[3] </td>
950 <td align='center'>$temp[4] </td>
951 <td align='center'>$temp[5] </td>
954 <form method='post' action='$ENV{'SCRIPT_NAME'}'>
955 <input type='hidden' name='ACTION' value='$Lang::tr{'toggle enable disable'}2' />
956 <input type='image' name='$Lang::tr{'toggle enable disable'}' src='/images/$gif' alt='$gdesc' title='$gdesc' />
957 <input type='hidden' name='KEY2' value='$key' />
962 <form method='post' action='$ENV{'SCRIPT_NAME'}'>
963 <input type='hidden' name='ACTION' value='$Lang::tr{'edit'}2' />
964 <input type='image' name='$Lang::tr{'edit'}' src='/images/edit.gif' alt='$Lang::tr{'edit'}' title='$Lang::tr{'edit'}' />
965 <input type='hidden' name='KEY2' value='$key' />
970 <form method='post' action='$ENV{'SCRIPT_NAME'}'>
971 <input type='hidden' name='ACTION' value='$Lang::tr{'remove'}2' />
972 <input type='image' name='$Lang::tr{'remove'}' src='/images/delete.gif' alt='$Lang::tr{'remove'}' title='$Lang::tr{'remove'}' />
973 <input type='hidden' name='KEY2' value='$key' />
983 # If the fixed lease file contains entries, print Key to action icons
985 my $dup = $ipdup ?
"<td class='base'>$Lang::tr{'duplicate ip bold'}</td>" :'';
989 <td class='boldbase'> <b>$Lang::tr{'legend'}: </b></td>
990 <td><img src='/images/on.gif' alt='$Lang::tr{'click to disable'}' /></td>
991 <td class='base'>$Lang::tr{'click to disable'}</td>
992 <td> </td>
993 <td><img src='/images/off.gif' alt='$Lang::tr{'click to enable'}' /></td>
994 <td class='base'>$Lang::tr{'click to enable'}</td>
995 <td> </td>
996 <td><img src='/images/edit.gif' alt='$Lang::tr{'edit'}' /></td>
997 <td class='base'>$Lang::tr{'edit'}</td>
998 <td> </td>
999 <td><img src='/images/delete.gif' alt='$Lang::tr{'remove'}' /></td>
1000 <td class='base'>$Lang::tr{'remove'}</td>
1004 <td bgcolor='orange'> </td>
1005 <td class='base'>$Lang::tr{'ip address outside subnets'}</td>
1015 &Header
::closebox
();
1017 &Header
::openbox
('100%', 'left', $Lang::tr
{'arp table entries'});
1018 my @ip = qx(/sbin/ip neigh show
| awk
'{print \$1}');
1019 my @mac = qx(/sbin/ip neigh show
| awk
'{print \$5}');
1023 <table width='100%'>
1025 <td width='40%'><b>$Lang::tr{'ip address'}</b></td><td width='40%'><b>$Lang::tr{'mac address'}</b></td><td width='20%'>Optionen</td>
1033 print"<tr><form method='post' action='$ENV{'SCRIPT_NAME'}'><td><input type='hidden' name='FIX_ADDR' value='$ip' />$ip</td>";
1034 print"<td><input type='hidden' name='FIX_MAC' value='@mac[$i]' />@mac[$i]</td>";
1035 print"<td><input type='hidden' name='ACTION' value='$Lang::tr{'add'}2' /><input type='submit' name='SUBMIT' value='$buttontext' /></td></form></tr>";
1041 &Header
::closebox
();
1044 foreach my $itf (@ITFs) {
1045 if ($dhcpsettings{"ENABLE_${itf}"} eq 'on') {
1046 # display leases with a list of actions to do with the global select checkbox.
1047 &Header
::PrintActualLeases
("+"); # "+" => create fixed leases from nodeaddress
1048 last; #Print one time only for all interfaces
1052 &Header
::closebigbox
();
1053 &Header
::closepage
();
1055 ## Ouf it's the end !
1057 sub sortcurrent1
# by now, do not sort, just write
1059 open(FILE
, ">$filename1") or die 'Unable to open dhcp advanced options file.';
1060 print FILE
@current1;
1065 # Sort the "current2" array according to choices
1070 sub fixedleasesort
{
1072 if (rindex ($dhcpsettings{'SORT_FLEASELIST'},'Rev') != -1) {
1073 $qs=substr ($dhcpsettings{'SORT_FLEASELIST'},0,length($dhcpsettings{'SORT_FLEASELIST'})-3);
1074 if ($qs eq 'FIPADDR') {
1075 my @a = split(/\./,$entries{$a}->{$qs});
1076 my @b = split(/\./,$entries{$b}->{$qs});
1082 $entries{$b}->{$qs} cmp $entries{$a}->{$qs};
1084 } else { #not reverse
1085 $qs=$dhcpsettings{'SORT_FLEASELIST'};
1086 if ($qs eq 'FIPADDR') {
1087 my @a = split(/\./,$entries{$a}->{$qs});
1088 my @b = split(/\./,$entries{$b}->{$qs});
1094 $entries{$a}->{$qs} cmp $entries{$b}->{$qs};
1099 #Use an associative array (%entries)
1100 foreach my $line (@current2) {
1101 chomp( $line); #remove newline because can be on field 5 or 6 (addition of REMARK)
1102 my @temp = split (',',$line);
1103 my @record = ('FETHER',$temp[0],'FIPADDR',$temp[1],'DATA',join(',',@temp[2..6]));
1104 my $record = {}; # create a reference to empty hash
1105 %{$record} = @record; # populate that hash with @record
1106 # use combination of ether & IP as key to allow duplicates in either but not both
1107 $entries{$record->{FETHER
} . $record->{FIPADDR
}} = $record; # add this to a hash of hashes
1110 open(FILE
, ">$filename2") or die 'Unable to open fixed lease file.';
1111 foreach my $entry ( sort fixedleasesort
keys %entries) {
1112 print FILE
"$entries{$entry}->{FETHER},$entries{$entry}->{FIPADDR},$entries{$entry}->{DATA}\n";
1116 # Reload sorted @current2
1117 open (FILE
, "$filename2");
1120 undef (%entries); #This array is reused latter. Clear it.
1123 # Build the configuration file mixing settings, fixed leases and advanced options
1125 open(FILE
, ">/${General::swroot}/dhcp/dhcpd.conf") or die "Unable to write dhcpd.conf file";
1129 print FILE
"ddns-update-style none;\n";
1130 print FILE
"deny bootp; #default\n";
1131 print FILE
"authoritative;\n";
1133 # Write first new option definition
1134 foreach my $line (@current1) {
1135 chomp($line); # remove newline
1136 my @temp = split(/\t/,$line);
1137 if (ExistNewOptionDefinition
($temp[1] . ' ' . $temp[2])) {
1138 print FILE
"option $temp[1] $temp[2];\n";
1141 # Write other global options
1142 foreach my $line (@current1) {
1143 chomp($line); # remove newline
1144 my @temp = split(/\t/,$line);
1146 if ($temp[0] eq 'on' && !ExistNewOptionDefinition
($temp[1] . ' ' . $temp[2])){ # active & !definition
1148 for (my $key=0; $key<@ITFs; $key++) {
1149 my $itf = $temp[3+$key];
1150 if ($itf ne 'off') # Only if an interface name is read
1156 print FILE
"option $temp[1] $temp[2];\n";
1161 #Subnet range definition
1162 foreach my $itf (@ITFs) {
1163 my $lc_itf=lc($itf);
1164 if ($dhcpsettings{"ENABLE_${itf}"} eq 'on' ){
1165 print FILE
"\nsubnet " . $netsettings{"${itf}_NETADDRESS"} . " netmask ". $netsettings{"${itf}_NETMASK"} . " #$itf\n";
1167 print FILE
"\trange " . $dhcpsettings{"START_ADDR_${itf}"} . ' ' . $dhcpsettings{"END_ADDR_${itf}"}.";\n" if ($dhcpsettings{"START_ADDR_${itf}"});
1168 print FILE
"\toption subnet-mask " . $netsettings{"${itf}_NETMASK"} . ";\n";
1169 print FILE
"\toption domain-name \"" . $dhcpsettings{"DOMAIN_NAME_${itf}"} . "\";\n";
1170 print FILE
"\toption routers " . $netsettings{"${itf}_ADDRESS"} . ";\n";
1171 print FILE
"\toption domain-name-servers " . $dhcpsettings{"DNS1_${itf}"} if ($dhcpsettings{"DNS1_${itf}"});
1172 print FILE
", " . $dhcpsettings{"DNS2_${itf}"} if ($dhcpsettings{"DNS2_${itf}"});
1173 print FILE
";\n" if ($dhcpsettings{"DNS1_${itf}"});
1174 print FILE
"\toption ntp-servers " . $dhcpsettings{"NTP1_${itf}"} if ($dhcpsettings{"NTP1_${itf}"});
1175 print FILE
", " . $dhcpsettings{"NTP2_${itf}"} if ($dhcpsettings{"NTP2_${itf}"});
1176 print FILE
";\n" if ($dhcpsettings{"NTP1_${itf}"});
1177 print FILE
"\toption netbios-name-servers " . $dhcpsettings{"WINS1_${itf}"} if ($dhcpsettings{"WINS1_${itf}"});
1178 print FILE
", " . $dhcpsettings{"WINS2_${itf}"} if ($dhcpsettings{"WINS2_${itf}"});
1179 print FILE
";\n" if ($dhcpsettings{"WINS1_${itf}"});
1180 print FILE
"\tnext-server " . $dhcpsettings{"NEXT_${itf}"} . ";\n" if ($dhcpsettings{"NEXT_${itf}"});
1181 print FILE
"\tfilename \"" . $dhcpsettings{"FILE_${itf}"} . "\";\n" if ($dhcpsettings{"FILE_${itf}"});
1182 print FILE
"\tdefault-lease-time " . ($dhcpsettings{"DEFAULT_LEASE_TIME_${itf}"} * 60). ";\n";
1183 print FILE
"\tmax-lease-time " . ($dhcpsettings{"MAX_LEASE_TIME_${itf}"} * 60) . ";\n";
1184 print FILE
"\tallow bootp;\n" if ($dhcpsettings{"ENABLEBOOTP_${itf}"} eq 'on');
1188 # Write scoped options
1189 foreach my $line (@current1) {
1190 chomp($line); # remove newline
1191 my @temp = split(/\t/,$line); # Use TAB separator !
1193 if ($temp[0] eq 'on'){
1194 for (my $key=0; $key<@ITFs; $key++) {
1195 if ($itf eq $temp[3+$key]) # Only is an interface name is read
1197 print FILE
"\toption $temp[1] $temp[2];\n";
1202 print FILE
"} #$itf\n";
1204 system ('/usr/bin/touch', "${General::swroot}/dhcp/enable_${lc_itf}");
1205 &General
::log("DHCP on ${itf}: " . $Lang::tr
{'dhcp server enabled'})
1207 unlink "${General::swroot}/dhcp/enable_${lc_itf}";
1208 &General
::log("DHCP on ${itf}: " . $Lang::tr
{'dhcp server disabled'})
1212 #write fixed leases if any. Does not handle duplicates to write them elsewhere than the global scope.
1214 foreach my $line (@current2) {
1216 my @temp = split(/\,/,$line);
1217 if ($temp[2] eq "on") {
1218 print FILE
"\nhost fix$key # $temp[6]\n";
1220 print FILE
"\thardware ethernet $temp[0];\n";
1221 print FILE
"\tfixed-address $temp[1];\n";
1222 print FILE
"\tnext-server $temp[3];\n" if ($temp[3]);
1223 print FILE
"\tfilename \"$temp[4]\";\n" if ($temp[4]);
1224 print FILE
"\toption root-path \"$temp[5]\";\n" if ($temp[5]);
1230 if ( $dhcpsettings{"ENABLE_GREEN"} eq 'on' || $dhcpsettings{"ENABLE_BLUE"} eq 'on' ) {system '/usr/local/bin/dhcpctrl enable >/dev/null 2>&1';}
1231 else {system '/usr/local/bin/dhcpctrl disable >/dev/null 2>&1';}
1232 system '/usr/local/bin/dhcpctrl restart >/dev/null 2>&1';
1236 # Receive a string and if it match model for a new option,
1237 # add it to the list %newOptions
1239 my %NewOptions = ();
1241 sub AddNewOptionDefinition
{
1243 if ( $line =~ /^([-\w]+)( code \d+=($OptionTypes))/ ) {
1244 $NewOptions{$1} = $2;
1245 #&General::log ("new:<$1><$2>");
1252 # Check existence of definition for a new option
1254 sub ExistNewOptionDefinition
{
1257 if ( $line =~ /^([-\w]+)( code \d+=($OptionTypes))/ ) {
1258 return defined $NewOptions{$1};
1264 # Check if it is a new option (definition must exist)
1265 # "code=" test eliminate a false response when definition exists
1266 # but this string is a definition with bad $OptionTypes.
1267 sub ValidNewOption
{
1269 if ($line =~ /^([-\w]+) (.*)/ ) {
1270 return defined ( $NewOptions{$1} ) && $2 !~ /code=/;
1276 # Check if the new option $opt is used, except the definition of itself!
1278 sub IsUsedNewOptionDefinition
{
1279 my ($opt,$val) = @_;
1281 foreach my $line (@current1) {
1282 #chomp($line); # remove newline #don't know why, but this remove newline in @current1 .... !
1283 my @temp = split(/\t/,$line);
1284 # if we find something "opt value" & value != "code nnn=" it's ok.
1285 return 1 if ( ($opt eq $temp[1]) && ($temp[2] !~ /code \d+=/) );