]> git.ipfire.org Git - people/pmueller/ipfire-2.x.git/blame - html/cgi-bin/dhcp.cgi
Run ./make.sh update-contributors
[people/pmueller/ipfire-2.x.git] / html / cgi-bin / dhcp.cgi
CommitLineData
ac1cfefa 1#!/usr/bin/perl
70df8302
MT
2###############################################################################
3# #
4# IPFire.org - A linux based firewall #
74eb1c95 5# Copyright (C) 2007-2023 IPFire Team <info@ipfire.org> #
70df8302
MT
6# #
7# This program is free software: you can redistribute it and/or modify #
8# it under the terms of the GNU General Public License as published by #
9# the Free Software Foundation, either version 3 of the License, or #
10# (at your option) any later version. #
11# #
12# This program is distributed in the hope that it will be useful, #
13# but WITHOUT ANY WARRANTY; without even the implied warranty of #
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
15# GNU General Public License for more details. #
16# #
17# You should have received a copy of the GNU General Public License #
18# along with this program. If not, see <http://www.gnu.org/licenses/>. #
19# #
20###############################################################################
21
ac1cfefa 22use strict;
2de0f49f 23use experimental 'smartmatch';
ee556e82 24use IO::Socket;
ac1cfefa
MT
25
26# enable only the following on debugging purpose
cb5e9c6c
CS
27#use warnings;
28#use CGI::Carp 'fatalsToBrowser';
ac1cfefa 29
986e08d9 30require '/var/ipfire/general-functions.pl';
ac1cfefa
MT
31require "${General::swroot}/lang.pl";
32require "${General::swroot}/header.pl";
33#workaround to suppress a warning when a variable is used only once
34my @dummy = ( ${Header::colouryellow} );
35undef (@dummy);
36
37our %dhcpsettings=();
38our %netsettings=();
39my %mainsettings=();
40my %timesettings=();
41my $setting = "${General::swroot}/dhcp/settings";
42our $filename1 = "${General::swroot}/dhcp/advoptions"; # Field separator is TAB in this file (comma is standart)
d1883e28 43 # because we need commas in the some data
ac1cfefa 44our $filename2 = "${General::swroot}/dhcp/fixleases";
d1883e28 45our $filename3 = "${General::swroot}/dhcp/advoptions-list"; # Describe the allowed syntax for dhcp options
ac1cfefa
MT
46my $errormessage = '';
47my $warnNTPmessage = '';
48my @nosaved=();
fe6cda92 49my %color = ();
ac1cfefa 50
66c36198 51#Basic syntax allowed for new Option definition. Not implemented: RECORDS & array of RECORDS
ac1cfefa
MT
52our $OptionTypes = 'boolean|((un)?signed )?integer (8|16|32)|ip-address|text|string|encapsulate \w+|array of ip-address';
53
54&Header::showhttpheaders();
541d93f0
CS
55our @ITFs=('GREEN');
56if (&Header::blue_used()){push(@ITFs,'BLUE');}
ac1cfefa
MT
57
58#Settings1 for the first screen box
59foreach my $itf (@ITFs) {
60 $dhcpsettings{"ENABLE_${itf}"} = 'off';
61 $dhcpsettings{"ENABLEBOOTP_${itf}"} = 'off';
62 $dhcpsettings{"START_ADDR_${itf}"} = '';
63 $dhcpsettings{"END_ADDR_${itf}"} = '';
64 $dhcpsettings{"DOMAIN_NAME_${itf}"} = '';
65 $dhcpsettings{"DEFAULT_LEASE_TIME_${itf}"} = '';
66 $dhcpsettings{"MAX_LEASE_TIME_${itf}"} = '';
67 $dhcpsettings{"WINS1_${itf}"} = '';
68 $dhcpsettings{"WINS2_${itf}"} = '';
69 $dhcpsettings{"DNS1_${itf}"} = '';
70 $dhcpsettings{"DNS2_${itf}"} = '';
71 $dhcpsettings{"NTP1_${itf}"} = '';
72 $dhcpsettings{"NTP2_${itf}"} = '';
d1883e28
MT
73 $dhcpsettings{"NEXT_${itf}"} = '';
74 $dhcpsettings{"FILE_${itf}"} = '';
f5fb9a04
MT
75 $dhcpsettings{"DNS_UPDATE_KEY_NAME_${itf}"} = '';
76 $dhcpsettings{"DNS_UPDATE_KEY_SECRET_${itf}"} = '';
77 $dhcpsettings{"DNS_UPDATE_KEY_ALGO_${itf}"} = '';
9dbf3c49 78 $dhcpsettings{"DENY_KNOWN_CLIENTS_${itf}"} = 'off';
ac1cfefa
MT
79}
80
81$dhcpsettings{'SORT_FLEASELIST'} = 'FIPADDR';
82$dhcpsettings{'SORT_LEASELIST'} = 'IPADDR';
83
f5fb9a04
MT
84# DNS Update settings
85$dhcpsettings{'DNS_UPDATE_ENABLED'} = 'off';
86
ac1cfefa
MT
87#Settings2 for editing the multi-line list
88#Must not be saved with writehash !
89$dhcpsettings{'FIX_MAC'} = '';
90$dhcpsettings{'FIX_ADDR'} = '';
91$dhcpsettings{'FIX_ENABLED'} = 'off';
92$dhcpsettings{'FIX_NEXTADDR'} = '';
93$dhcpsettings{'FIX_FILENAME'} = '';
94$dhcpsettings{'FIX_ROOTPATH'} = '';
95$dhcpsettings{'FIX_REMARK'} = '';
96$dhcpsettings{'ACTION'} = '';
97$dhcpsettings{'KEY1'} = '';
98$dhcpsettings{'KEY2'} = '';
99@nosaved=('FIX_MAC','FIX_ADDR','FIX_ENABLED','FIX_NEXTADDR',
100 'FIX_FILENAME','FIX_ROOTPATH','FIX_REMARK');
101
102$dhcpsettings{'ADVOPT_ENABLED'} = '';
103$dhcpsettings{'ADVOPT_NAME'} = '';
104$dhcpsettings{'ADVOPT_DATA'} = '';
105unshift (@nosaved,'ADVOPT_ENABLED','ADVOPT_NAME','ADVOPT_DATA');
106foreach my $itf (@ITFs) {
107 $dhcpsettings{"ADVOPT_SCOPE_${itf}"} = 'off';
108 unshift (@nosaved, "ADVOPT_SCOPE_${itf}");
109}
110
111# Read Ipcop settings
112&General::readhash("${General::swroot}/ethernet/settings", \%netsettings);
113&General::readhash("${General::swroot}/main/settings", \%mainsettings);
114&General::readhash("${General::swroot}/time/settings", \%timesettings);
8186b372 115&General::readhash("/srv/web/ipfire/html/themes/ipfire/include/colors.txt", \%color);
ac1cfefa
MT
116
117#Get GUI values
118&Header::getcgihash(\%dhcpsettings);
119
120open(FILE, "$filename1") or die 'Unable to open dhcp advanced options file.';
121our @current1 = <FILE>;
122close(FILE);
123# Extract OptionDefinition
124foreach my $line (@current1) {
125 #chomp($line); # remove newline #don't know why, but this remove newline in @current1 .... !
126 my @temp = split(/\t/,$line);
127 AddNewOptionDefinition ($temp[1] . ' ' . $temp[2]);
128}
129
130open(FILE, "$filename2") or die 'Unable to open fixed leases file.';
131our @current2 = <FILE>;
132close(FILE);
133
2bbf1766 134# Open and read-in file which contains the list of allowed advanced options.
5410fcbc 135open(FILE, $filename3) or die "Could not open $filename3. $!\n";
2bbf1766
SS
136
137# Grab file content.
138my @advoptions_list = <FILE>;
139
140# Close file handle.
141close(FILE);
142
ac1cfefa
MT
143# Check Settings1 first because they are needed by &buildconf
144if ($dhcpsettings{'ACTION'} eq $Lang::tr{'save'}) {
145 foreach my $itf (@ITFs) {
146 if ($dhcpsettings{"ENABLE_${itf}"} eq 'on' ) {
147 # "Start" is defined, need "End" and vice versa
148 if ($dhcpsettings{"START_ADDR_${itf}"}) {
149 if (!(&General::validip($dhcpsettings{"START_ADDR_${itf}"}))) {
150 $errormessage = "DHCP on ${itf}: " . $Lang::tr{'invalid start address'};
151 goto ERROR;
152 }
153 if (!$dhcpsettings{"END_ADDR_${itf}"}) {
154 $errormessage = "DHCP on ${itf}: " . $Lang::tr{'invalid end address'};
155 goto ERROR;
156 }
66c36198 157 if (! &General::IpInSubnet ( $dhcpsettings{"START_ADDR_${itf}"},
ac1cfefa
MT
158 $netsettings{"${itf}_NETADDRESS"},
159 $netsettings{"${itf}_NETMASK"})) {
160 $errormessage = "DHCP on ${itf}: " . $Lang::tr{'invalid start address'};
161 goto ERROR;
162 }
163 }
66c36198 164
ac1cfefa
MT
165 if ($dhcpsettings{"END_ADDR_${itf}"}) {
166 if (!(&General::validip($dhcpsettings{"END_ADDR_${itf}"}))) {
167 $errormessage = "DHCP on ${itf}: " . $Lang::tr{'invalid end address'};
168 goto ERROR;
169 }
170 if (!$dhcpsettings{"START_ADDR_${itf}"}) {
171 $errormessage = "DHCP on ${itf}: " . $Lang::tr{'invalid start address'};
172 goto ERROR;
173 }
66c36198 174 if (! &General::IpInSubnet ( $dhcpsettings{"END_ADDR_${itf}"},
ac1cfefa 175 $netsettings{"${itf}_NETADDRESS"},
66c36198 176 $netsettings{"${itf}_NETMASK"})) {
ac1cfefa
MT
177 $errormessage = "DHCP on ${itf}: " . $Lang::tr{'invalid end address'};
178 goto ERROR;
179 }
180 #swap if necessary! (support 255.255.0.0 range, I doubt we need more) GE
181 my @startoct = split (/\./, $dhcpsettings{"START_ADDR_${itf}"});
182 my @endoct = split (/\./, $dhcpsettings{"END_ADDR_${itf}"});
183 if ( $endoct[2]*256+$endoct[3] < $startoct[2]*256+$startoct[3] ) {
184 ($dhcpsettings{"START_ADDR_${itf}"},$dhcpsettings{"END_ADDR_${itf}"}) =
185 ($dhcpsettings{"END_ADDR_${itf}"},$dhcpsettings{"START_ADDR_${itf}"});
186 }
187 }
188
9dbf3c49
AB
189 if ($dhcpsettings{"DENY_KNOWN_CLIENTS_${itf}"} eq 'on') {
190 if (($dhcpsettings{"START_ADDR_${itf}"}) eq '' && ($dhcpsettings{"END_ADDR_${itf}"}) eq '') {
191 $errormessage = "DHCP on ${itf}: " . $Lang::tr{'dhcp valid range required when deny known clients checked'};
192 goto ERROR;
896fa74d 193 }
9dbf3c49
AB
194 }
195
ac1cfefa
MT
196 if (!($dhcpsettings{"DEFAULT_LEASE_TIME_${itf}"} =~ /^\d+$/)) {
197 $errormessage = "DHCP on ${itf}: " . $Lang::tr{'invalid default lease time'} . $dhcpsettings{'DEFAULT_LEASE_TIME_${itf}'};
198 goto ERROR;
199 }
200
201 if (!($dhcpsettings{"MAX_LEASE_TIME_${itf}"} =~ /^\d+$/)) {
202 $errormessage = "DHCP on ${itf}: " . $Lang::tr{'invalid max lease time'} . $dhcpsettings{'MAX_LEASE_TIME_${itf}'};
203 goto ERROR;
204 }
205
206 if ($dhcpsettings{"DNS1_${itf}"}) {
207 if (!(&General::validip($dhcpsettings{"DNS1_${itf}"}))) {
208 $errormessage = "DHCP on ${itf}: " . $Lang::tr{'invalid primary dns'};
209 goto ERROR;
210 }
211 }
212 if ($dhcpsettings{"DNS2_${itf}"}) {
213 if (!(&General::validip($dhcpsettings{"DNS2_${itf}"}))) {
214 $errormessage = "DHCP on ${itf}: " . $Lang::tr{'invalid secondary dns'};
215 goto ERROR;
216 }
217 if (! $dhcpsettings{"DNS1_${itf}"}) {
66c36198 218 $errormessage = "DHCP on ${itf}: " . $Lang::tr{'cannot specify secondary dns without specifying primary'};
ac1cfefa
MT
219 goto ERROR;
220 }
221 }
222
223 if ($dhcpsettings{"WINS1_${itf}"}) {
224 if (!(&General::validip($dhcpsettings{"WINS1_${itf}"}))) {
225 $errormessage = "DHCP on ${itf}: " . $Lang::tr{'invalid wins address'};
226 goto ERROR;
227 }
228 }
229 if ($dhcpsettings{"WINS2_${itf}"}) {
230 if (!(&General::validip($dhcpsettings{"WINS2_${itf}"}))) {
231 $errormessage = "DHCP on ${itf}: " . $Lang::tr{'invalid wins address'};
232 goto ERROR;
233 }
234 if (! $dhcpsettings{"WINS1_${itf}"} ) {
235 $errormessage = "DHCP on ${itf}: " . $Lang::tr{'cannot specify secondary wins without specifying primary'};
236 goto ERROR;
66c36198 237 }
ac1cfefa 238 }
d1883e28
MT
239 if ($dhcpsettings{"NEXT_${itf}"}) {
240 if (!(&General::validip($dhcpsettings{"NEXT_${itf}"}))) {
241 $errormessage = "next-server on ${itf}: " . $Lang::tr{'invalid ip'};
242 goto ERROR;
243 }
244 }
ac1cfefa
MT
245 if ($dhcpsettings{"NTP1_${itf}"}) {
246 if (!(&General::validip($dhcpsettings{"NTP1_${itf}"}))) {
247 $errormessage = "DHCP on ${itf}: " . $Lang::tr{'invalid primary ntp'};
248 goto ERROR;
249 }
250 if ($dhcpsettings{"NTP1_${itf}"} eq $netsettings{"${itf}_ADDRESS"} && ($timesettings{'ENABLECLNTP'} ne 'on')) {
251 $warnNTPmessage = "DHCP on ${itf}: " . $Lang::tr{'local ntp server specified but not enabled'};
252 #goto ERROR;
253 }
254 }
255 if ($dhcpsettings{"NTP2_${itf}"}) {
256 if (!(&General::validip($dhcpsettings{"NTP2_${itf}"}))) {
257 $errormessage = "DHCP on ${itf}: " . $Lang::tr{'invalid secondary ntp'};
258 goto ERROR;
259 }
260 if ($dhcpsettings{"NTP2_${itf}"} eq $netsettings{"${itf}_ADDRESS"} && ($timesettings{'ENABLECLNTP'} ne 'on')) {
261 $warnNTPmessage = "DHCP on ${itf}: " . $Lang::tr{'local ntp server specified but not enabled'};
262 #goto ERROR;
263 }
264 if (! $dhcpsettings{"NTP1_${itf}"}) {
265 $errormessage = "DHCP on ${itf}: " . $Lang::tr{'cannot specify secondary ntp without specifying primary'};
266 goto ERROR;
267 }
268 }
269 } # enabled
270 }#loop interface verify
271
a1468f66 272 map (delete ($dhcpsettings{$_}) ,@nosaved,'ACTION','KEY1','KEY2','q'); # Must not be saved
ac1cfefa
MT
273 &General::writehash($setting, \%dhcpsettings); # Save good settings
274 $dhcpsettings{'ACTION'} = $Lang::tr{'save'}; # create an 'ACTION'
275 map ($dhcpsettings{$_} = '',@nosaved,'KEY1','KEY2'); # and reinit vars to empty
276 &buildconf;
277 ERROR: # Leave the faulty field untouched
278} else {
279 &General::readhash($setting, \%dhcpsettings); # Get saved settings and reset to good if needed
280}
281
282## Sorting of fixed leases
283if ($ENV{'QUERY_STRING'} =~ /^FETHER|^FIPADDR/ ) {
284 my $newsort=$ENV{'QUERY_STRING'};
285 my $act=$dhcpsettings{'SORT_FLEASELIST'};
286 #Reverse actual sort ?
287 if ($act =~ $newsort) {
288 my $Rev='';
289 if ($act !~ 'Rev') {
290 $Rev='Rev';
291 }
292 $newsort.=$Rev;
293 }
294 $dhcpsettings{'SORT_FLEASELIST'}=$newsort;
a1468f66 295 map (delete ($dhcpsettings{$_}) ,@nosaved,'ACTION','KEY1','KEY2', 'q'); # Must never be saved
ac1cfefa
MT
296 &General::writehash($setting, \%dhcpsettings);
297 &sortcurrent2;
298 $dhcpsettings{'ACTION'} = 'SORT'; # create an 'ACTION'
66c36198 299 map ($dhcpsettings{$_} = '',@nosaved,'KEY1','KEY2');# and reinit vars to empty
ac1cfefa
MT
300}
301
302#Sorting of allocated leases
303&Header::CheckSortOrder;
304
305
66c36198 306## Now manipulate the two multi-line list with Settings2.
ac1cfefa
MT
307# '1' suffix is for ADVANCED OPTIONS
308# '2' suffix is for FIXED LEASES
309
310# Toggle enable/disable field on specified options.
311
312if ($dhcpsettings{'ACTION'} eq $Lang::tr{'toggle enable disable'}.'1') {
313 #move out new line
314 chomp(@current1[$dhcpsettings{'KEY1'}]);
315 my @temp = split(/\t/,@current1[$dhcpsettings{'KEY1'}]); #use TAB separator !
316 $temp[0] = $temp[0] eq 'on' ? '' : 'on'; # Toggle the field
317 @current1[$dhcpsettings{'KEY1'}] = join ("\t",@temp)."\n";
318 $dhcpsettings{'KEY1'} = ''; # End edit mode
319 &General::log($Lang::tr{'dhcp advopt modified'});
320 open(FILE, ">$filename1") or die 'Unable to open dhcp advanced options file.';
321 print FILE @current1;
322 close(FILE);
66c36198 323
ac1cfefa
MT
324 #Write changes to dhcpd.conf.
325 &buildconf;
326}
327
66c36198 328
ac1cfefa
MT
329
330if ($dhcpsettings{'ACTION'} eq $Lang::tr{'add'}.'1' &&
331 $dhcpsettings{'SUBMIT'} ne $Lang::tr{'dhcp advopt help'}) {
332 $dhcpsettings{'ADVOPT_NAME'} =~ s/[^ \w-]//g; # prevent execution of code by removing everything except letters/space
333 $dhcpsettings{'ADVOPT_DATA'} =~ s/`//g; # back tik ` ? not allowed !
334
335 if ($dhcpsettings{'ADVOPT_DATA'} eq '') {
336 $errormessage=$Lang::tr{'dhcp advopt blank value'};
337 }
66c36198 338
ac1cfefa
MT
339 # Test for a new option definition string (join field name & data)
340 if (ExistNewOptionDefinition ($dhcpsettings{'ADVOPT_NAME'} . ' ' . $dhcpsettings{'ADVOPT_DATA'})) {
341 #only edit permitted if option definition exists
342 $errormessage = $Lang::tr{'dhcp advopt definition exists'} if ($dhcpsettings{'KEY1'} eq '');
343 $dhcpsettings{'ADVOPT_ENABLED'} = 'on'; # force active
344 map ($dhcpsettings{"ADVOPT_SCOPE_$_"} = 'off', @ITFs); # force global
345 } elsif (AddNewOptionDefinition ($dhcpsettings{'ADVOPT_NAME'} . ' ' . $dhcpsettings{'ADVOPT_DATA'})) {
346 #was a new option definition
347 $dhcpsettings{'ADVOPT_ENABLED'} = 'on'; # force active
348 map ($dhcpsettings{"ADVOPT_SCOPE_$_"} = 'off', @ITFs); # force global
349 } elsif (ValidNewOption ($dhcpsettings{'ADVOPT_NAME'} . ' ' . $dhcpsettings{'ADVOPT_DATA'})) {
350 #was a new option
2bbf1766 351 } elsif (! grep(/option $dhcpsettings{'ADVOPT_NAME'}/, @advoptions_list)) {
ac1cfefa
MT
352 $errormessage=$Lang::tr{'dhcp advopt unknown'}.': '.$dhcpsettings{'ADVOPT_NAME'};
353 }
354
355 unless ($errormessage) {
66c36198 356
ac1cfefa
MT
357 my $scope = '';
358 foreach my $itf (@ITFs) { # buils "RED,GREEN,ORANGE,... based on selection
359 $scope .= $dhcpsettings{"ADVOPT_SCOPE_${itf}"} eq 'on' ? "\t$itf" : "\toff" ;
360 }
361 if ($dhcpsettings{'KEY1'} eq '') { #add or edit ? TAB separator !
362 unshift (@current1, "$dhcpsettings{'ADVOPT_ENABLED'}\t$dhcpsettings{'ADVOPT_NAME'}\t$dhcpsettings{'ADVOPT_DATA'}$scope\n");
363 &General::log($Lang::tr{'dhcp advopt added'});
364 } else {
365 @current1[$dhcpsettings{'KEY1'}] = "$dhcpsettings{'ADVOPT_ENABLED'}\t$dhcpsettings{'ADVOPT_NAME'}\t$dhcpsettings{'ADVOPT_DATA'}$scope\n";
366 $dhcpsettings{'KEY1'} = ''; # End edit mode
367 &General::log($Lang::tr{'dhcp advopt modified'});
368 }
369
370 #Write changes to dhcpd.conf.
371 &sortcurrent1; # sort newly added/modified entry
372 &buildconf; # before calling buildconf which use fixed lease file !
373 }
374}
375
376if ($dhcpsettings{'ACTION'} eq $Lang::tr{'edit'}.'1') {
377 #move out new line
378 my $line = @current1[$dhcpsettings{'KEY1'}];
379 chomp($line);
380 my @temp = split(/\t/, $line);
381 $dhcpsettings{'ADVOPT_ENABLED'}=$temp[0];
382 $dhcpsettings{'ADVOPT_NAME'}=$temp[1];
383 $dhcpsettings{'ADVOPT_DATA'}=$temp[2];
384
385 # read next fields which are the name (color) of an interface if this interface is scoped
386 for (my $key=0; $key<@ITFs; $key++) {
387 my $itf = $temp[3+$key];
388 if ($itf ne 'off') # Only is an interface name is read
389 {
390 $dhcpsettings{"ADVOPT_SCOPE_${itf}"} = 'on';
66c36198 391 }
ac1cfefa
MT
392 }
393}
394
395if ($dhcpsettings{'ACTION'} eq $Lang::tr{'remove'}.'1') {
396 splice (@current1,$dhcpsettings{'KEY1'},1);
397 open(FILE, ">$filename1") or die 'Unable to open dhcp advanced options file.';
398 print FILE @current1;
399 close(FILE);
400 $dhcpsettings{'KEY1'} = ''; # End remove mode
401 &General::log($Lang::tr{'dhcp advopt removed'});
402 #Write changes to dhcpd.conf.
403 &buildconf;
404}
405#end KEY1
406
407
408# Toggle enable/disable field on specified lease.
409if ($dhcpsettings{'ACTION'} eq $Lang::tr{'toggle enable disable'}.'2') {
410 #move out new line
411 chomp(@current2[$dhcpsettings{'KEY2'}]);
412 my @temp = split(/\,/,@current2[$dhcpsettings{'KEY2'}]);
413 $temp[2] = $temp[2] eq 'on' ? '' : 'on'; # Toggle the field
414 @current2[$dhcpsettings{'KEY2'}] = join (',',@temp)."\n";
415 $dhcpsettings{'KEY2'} = ''; # End edit mode
416 &General::log($Lang::tr{'fixed ip lease modified'});
417 open(FILE, ">$filename2") or die 'Unable to open fixed leases file.';
418 print FILE @current2;
419 close(FILE);
66c36198 420
ac1cfefa
MT
421 #Write changes to dhcpd.conf.
422 &buildconf;
423}
424
425if ($dhcpsettings{'ACTION'} eq $Lang::tr{'add'}.'2') {
426 $dhcpsettings{'FIX_MAC'} =~ tr/-/:/;
427 unless(&General::validip($dhcpsettings{'FIX_ADDR'})) { $errormessage = $Lang::tr{'invalid fixed ip address'}; }
428 unless(&General::validmac($dhcpsettings{'FIX_MAC'})) { $errormessage = $Lang::tr{'invalid fixed mac address'}; }
429 if ($dhcpsettings{'FIX_NEXTADDR'}) {
430 unless(&General::validip($dhcpsettings{'FIX_NEXTADDR'})) { $errormessage = $Lang::tr{'invalid fixed ip address'}; }
431 }
66c36198 432
ac1cfefa
MT
433 my $key = 0;
434 CHECK:foreach my $line (@current2) {
435 my @temp = split(/\,/,$line);
436 if($dhcpsettings{'KEY2'} ne $key) {
437 # same MAC is OK on different subnets. This test is not complete because
438 # if ip are not inside a known subnet, I don't warn.
439 # Also it may be needed to put duplicate fixed lease in their right subnet definition..
440 foreach my $itf (@ITFs) {
441 my $scoped = &General::IpInSubnet($dhcpsettings{'FIX_ADDR'},
66c36198 442 $netsettings{"${itf}_NETADDRESS"},
ac1cfefa
MT
443 $netsettings{"${itf}_NETMASK"}) &&
444 $dhcpsettings{"ENABLE_${itf}"} eq 'on';
445 if ( $scoped &&
446 (lc($dhcpsettings{'FIX_MAC'}) eq lc($temp[0])) &&
447 &General::IpInSubnet($temp[1],
66c36198 448 $netsettings{"${itf}_NETADDRESS"},
ac1cfefa
MT
449 $netsettings{"${itf}_NETMASK"})) {
450 $errormessage = "$Lang::tr{'mac address in use'} $dhcpsettings{'FIX_MAC'}";
451 last CHECK;
452 }
453 }
454 }
455 $key++;
456 }
457
458 unless ($errormessage) {
459 $dhcpsettings{'FIX_REMARK'} = &Header::cleanhtml($dhcpsettings{'FIX_REMARK'});
460 $dhcpsettings{'FIX_NEXTADDR'} = &Header::cleanhtml($dhcpsettings{'FIX_NEXTADDR'});
461 $dhcpsettings{'FIX_FILENAME'} = &Header::cleanhtml($dhcpsettings{'FIX_FILENAME'});
462 $dhcpsettings{'FIX_ROOTPATH'} = &Header::cleanhtml($dhcpsettings{'FIX_ROOTPATH'});
463 if ($dhcpsettings{'KEY2'} eq '') { #add or edit ?
464 unshift (@current2, "$dhcpsettings{'FIX_MAC'},$dhcpsettings{'FIX_ADDR'},$dhcpsettings{'FIX_ENABLED'},$dhcpsettings{'FIX_NEXTADDR'},$dhcpsettings{'FIX_FILENAME'},$dhcpsettings{'FIX_ROOTPATH'},$dhcpsettings{'FIX_REMARK'}\n");
e4f9ea3c
BB
465 open(FILE, ">$filename2") or die 'Unable to open fixed lease file.';
466 print FILE @current2;
467 close(FILE);
ac1cfefa 468 &General::log($Lang::tr{'fixed ip lease added'});
91fc3030
MT
469
470 # Enter edit mode
31672dc8 471 $dhcpsettings{'KEY2'} = 0;
ac1cfefa
MT
472 } else {
473 @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";
474 $dhcpsettings{'KEY2'} = ''; # End edit mode
475 &General::log($Lang::tr{'fixed ip lease modified'});
31672dc8
MT
476
477 # sort newly added/modified entry
478 &sortcurrent2;
ac1cfefa
MT
479 }
480
481 #Write changes to dhcpd.conf.
ac1cfefa
MT
482 &buildconf; # before calling buildconf which use fixed lease file !
483 }
484}
485
486if ($dhcpsettings{'ACTION_ALL'} eq '+') {
487 my $news = 0;
488 foreach (keys %dhcpsettings) {
489 if (/^(\d+\.\d+\.\d+\.\d+)-([0-9a-fA-F:]+)$/) { # checked names are index of the line
490 my $ip=$1;
491 my $mac=$2;
492 if (!grep (/$2/,@current2)) {
493 unshift (@current2, "$mac,$ip,on,,,,imported\n");
494 $news++;
495 }
496 }
497 }
498 if ($news) {
499 #Write changes to dhcpd.conf.
500 $warnNTPmessage = $Lang::tr{'fixed ip lease added'}."($news)";
501 &General::log($warnNTPmessage);
502 &sortcurrent2; # sort newly added/modified entry
503 &buildconf; # before calling buildconf which use fixed lease file !
504 }
505}
506
507if ($dhcpsettings{'ACTION'} eq $Lang::tr{'edit'}.'2') {
508 #move out new line
509 my $line = @current2[$dhcpsettings{'KEY2'}];
510 chomp($line);
511 my @temp = split(/\,/, $line);
512 $dhcpsettings{'FIX_MAC'}=$temp[0];
513 $dhcpsettings{'FIX_ADDR'}=$temp[1];
514 $dhcpsettings{'FIX_ENABLED'}=$temp[2];
515 $dhcpsettings{'FIX_NEXTADDR'}=$temp[3];
516 $dhcpsettings{'FIX_FILENAME'}=$temp[4];
517 $dhcpsettings{'FIX_ROOTPATH'}=$temp[5];
518 $dhcpsettings{'FIX_REMARK'}=$temp[6];
519}
520
521if ($dhcpsettings{'ACTION'} eq $Lang::tr{'remove'}.'2') {
522 splice (@current2,$dhcpsettings{'KEY2'},1);
523 open(FILE, ">$filename2") or die 'Unable to open fixed lease file.';
524 print FILE @current2;
525 close(FILE);
526 $dhcpsettings{'KEY2'} = ''; # End remove mode
527 &General::log($Lang::tr{'fixed ip lease removed'});
528 #Write changes to dhcpd.conf.
529 &buildconf;
530}
531#end KEY2 defined
532
533
534
535
536if ($dhcpsettings{'ACTION'} eq '' ) { # First launch from GUI
537
538 # Set default DHCP values only if blank and disabled
539 foreach my $itf (@ITFs) {
540 if ($dhcpsettings{"ENABLE_${itf}"} ne 'on' ) {
541 $dhcpsettings{"DNS1_${itf}"} = $netsettings{"${itf}_ADDRESS"};
542 $dhcpsettings{"DEFAULT_LEASE_TIME_${itf}"} = '60';
543 $dhcpsettings{"MAX_LEASE_TIME_${itf}"} = '120';
544 $dhcpsettings{"DOMAIN_NAME_${itf}"} = $mainsettings{'DOMAINNAME'};
545 }
546 }
547 $dhcpsettings{'FIX_ENABLED'} = 'on';
6fcd779c
AB
548 $dhcpsettings{'ADVOPT_ENABLED'} = 'on';
549 }
ac1cfefa 550
de4dea96
AB
551### START PAGE ###
552&Header::openpage($Lang::tr{'dhcp configuration'}, 1, $Header::extraHead);
ac1cfefa
MT
553&Header::openbigbox('100%', 'left', '', $errormessage);
554
555if ($errormessage) {
556 &Header::openbox('100%', 'left', $Lang::tr{'error messages'});
557 print "<font class='base'>$errormessage&nbsp;</font>\n";
558 &Header::closebox();
559}
560if ($warnNTPmessage) {
561 $warnNTPmessage = "<font color=${Header::colourred}><b>$Lang::tr{'capswarning'}</b></font>: $warnNTPmessage";
562}
563
564&Header::openbox('100%', 'left', 'DHCP');
565print "<form method='post' action='$ENV{'SCRIPT_NAME'}'>";
566
567foreach my $itf (@ITFs) {
568 my %checked=();
569 $checked{'ENABLE'}{'on'} = ( $dhcpsettings{"ENABLE_${itf}"} ne 'on') ? '' : "checked='checked'";
570 $checked{'ENABLEBOOTP'}{'on'} = ( $dhcpsettings{"ENABLEBOOTP_${itf}"} ne 'on') ? '' : "checked='checked'";
9dbf3c49 571 $checked{'DENY_KNOWN_CLIENTS'}{'on'} = ( $dhcpsettings{"DENY_KNOWN_CLIENTS_${itf}"} ne 'on') ? '' : "checked='checked'";
ac1cfefa
MT
572
573 if ($netsettings{"${itf}_DEV"} ne '' ) { # Show only defined interface
574 my $lc_itf=lc($itf);
575print <<END
576<table width='100%'>
577<tr>
578 <td width='25%' class='boldbase'><b><font color='${lc_itf}'>$Lang::tr{"$lc_itf interface"}</font></b></td>
579 <td class='base'>$Lang::tr{'enabled'}
580 <input type='checkbox' name='ENABLE_${itf}' $checked{'ENABLE'}{'on'} /></td>
9c100957 581 <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>
ac1cfefa 582</tr><tr>
e3edceeb 583 <td width='25%' class='base'>$Lang::tr{'start address'}&nbsp;<img src='/blob.gif' alt='*' /></td>
ac1cfefa 584 <td width='25%'><input type='text' name='START_ADDR_${itf}' value='$dhcpsettings{"START_ADDR_${itf}"}' /></td>
e3edceeb 585 <td width='25%' class='base'>$Lang::tr{'end address'}&nbsp;<img src='/blob.gif' alt='*' /></td>
ac1cfefa 586 <td width='25%'><input type='text' name='END_ADDR_${itf}' value='$dhcpsettings{"END_ADDR_${itf}"}' /></td>
9dbf3c49
AB
587</tr><tr>
588 <td class='base'>$Lang::tr{'dhcp deny known clients:'}</td>
589 <td><input type='checkbox' name='DENY_KNOWN_CLIENTS_${itf}' $checked{'DENY_KNOWN_CLIENTS'}{'on'} /></td>
ac1cfefa 590</tr><tr>
e3edceeb 591 <td class='base'>$Lang::tr{'default lease time'}&nbsp;<img src='/blob.gif' alt='*' /></td>
ac1cfefa 592 <td><input type='text' name='DEFAULT_LEASE_TIME_${itf}' value='$dhcpsettings{"DEFAULT_LEASE_TIME_${itf}"}' /></td>
e3edceeb 593 <td class='base'>$Lang::tr{'max lease time'}&nbsp;<img src='/blob.gif' alt='*' /></td>
ac1cfefa
MT
594 <td><input type='text' name='MAX_LEASE_TIME_${itf}' value='$dhcpsettings{"MAX_LEASE_TIME_${itf}"}' /></td>
595</tr><tr>
e3edceeb 596 <td class='base'>$Lang::tr{'domain name suffix'}</td>
ac1cfefa
MT
597 <td><input type='text' name='DOMAIN_NAME_${itf}' value='$dhcpsettings{"DOMAIN_NAME_${itf}"}' /></td>
598 <td>$Lang::tr{'dhcp allow bootp'}:</td>
599 <td><input type='checkbox' name='ENABLEBOOTP_${itf}' $checked{'ENABLEBOOTP'}{'on'} /></td>
600</tr><tr>
e3edceeb 601 <td class='base'>$Lang::tr{'primary dns'}&nbsp;<img src='/blob.gif' alt='*' /></td>
ac1cfefa 602 <td><input type='text' name='DNS1_${itf}' value='$dhcpsettings{"DNS1_${itf}"}' /></td>
e3edceeb 603 <td class='base'>$Lang::tr{'secondary dns'}</td>
ac1cfefa
MT
604 <td><input type='text' name='DNS2_${itf}' value='$dhcpsettings{"DNS2_${itf}"}' /></td>
605</tr><tr>
e3edceeb 606 <td class='base'>$Lang::tr{'primary ntp server'}:</td>
ac1cfefa 607 <td><input type='text' name='NTP1_${itf}' value='$dhcpsettings{"NTP1_${itf}"}' /></td>
e3edceeb 608 <td class='base'>$Lang::tr{'secondary ntp server'}:</td>
ac1cfefa
MT
609 <td><input type='text' name='NTP2_${itf}' value='$dhcpsettings{"NTP2_${itf}"}' /></td>
610</tr><tr>
e3edceeb 611 <td class='base'>$Lang::tr{'primary wins server address'}:</td>
ac1cfefa 612 <td><input type='text' name='WINS1_${itf}' value='$dhcpsettings{"WINS1_${itf}"}' /></td>
e3edceeb 613 <td class='base'>$Lang::tr{'secondary wins server address'}:</td>
ac1cfefa 614 <td><input type='text' name='WINS2_${itf}' value='$dhcpsettings{"WINS2_${itf}"}' /></td>
d1883e28 615</tr><tr>
e3edceeb 616 <td class='base'>next-server:</td>
d1883e28 617 <td><input type='text' name='NEXT_${itf}' value='$dhcpsettings{"NEXT_${itf}"}' /></td>
e3edceeb 618 <td class='base'>filename:</td>
d1883e28 619 <td><input type='text' name='FILE_${itf}' value='$dhcpsettings{"FILE_${itf}"}' /></td>
ac1cfefa
MT
620</tr>
621</table>
622<hr />
623END
624;
625 }# Show only defined interface
626}#foreach itf
627print <<END
628<table width='100%'>
629<tr>
e3edceeb 630 <td class='base' width='25%'><img src='/blob.gif' align='top' alt='*' />&nbsp;$Lang::tr{'required field'}</td>
ac1cfefa 631 <td class='base' width='30%'>$warnNTPmessage</td>
e084eea0 632 <td width='40%' align='right'><input type='submit' name='ACTION' value='$Lang::tr{'save'}' /></td>
ac1cfefa
MT
633</tr>
634</table>
b510e12a
SS
635END
636;
637&Header::closebox();
638
639# DHCP DNS update support (RFC2136)
640&Header::openbox('100%', 'left', $Lang::tr{'dhcp dns update'});
641
642my %checked = ();
643$checked{'DNS_UPDATE_ENABLED'}{'on'} = ( $dhcpsettings{'DNS_UPDATE_ENABLED'} ne 'on') ? '' : "checked='checked'";
644
645print <<END
646<table width='100%'>
647 <tr>
2dfbb338 648 <td width='30%' class='boldbase'>$Lang::tr{'dhcp dns enable update'}</td>
b510e12a
SS
649 <td class='base'><input type='checkbox' name='DNS_UPDATE_ENABLED' $checked{'DNS_UPDATE_ENABLED'}{'on'}>
650 </td>
651 <tr>
652</table>
653
654<table width='100%'>
655END
656;
657 my @domains = ();
658
659 # Print options for each interface.
660 foreach my $itf (@ITFs) {
661 # Check if DHCP for this interface is enabled.
662 if ($dhcpsettings{"ENABLE_${itf}"} eq 'on') {
663 # Check for same domain name.
664 next if ($dhcpsettings{"DOMAIN_NAME_${itf}"} ~~ @domains);
665 my $lc_itf = lc($itf);
666
667 # Select previously configured update algorithm.
668 my %selected = ();
669 $selected{'DNS_UPDATE_ALGO_${inf}'}{$dhcpsettings{'DNS_UPDATE_ALGO_${inf}'}} = 'selected';
670
671print <<END
672 <tr>
673 <td colspan='6'>&nbsp;</td>
674 </tr>
675 <tr>
676 <td colspan='6' class='boldbase'><b>$dhcpsettings{"DOMAIN_NAME_${itf}"}</b></td>
677 </tr>
678 <tr>
679 <td width='10%' class='boldbase'>$Lang::tr{'dhcp dns key name'}:</td>
680 <td width='20%'><input type='text' name='DNS_UPDATE_KEY_NAME_${itf}' value='$dhcpsettings{"DNS_UPDATE_KEY_NAME_${itf}"}'></td>
681 <td width='10%' class='boldbase' align='right'>$Lang::tr{'dhcp dns update secret'}:&nbsp;&nbsp;</td>
28fee676 682 <td width='20%'><input type='password' name='DNS_UPDATE_KEY_SECRET_${itf}' value='$dhcpsettings{"DNS_UPDATE_KEY_SECRET_${itf}"}'></td>
b510e12a
SS
683 <td width='10%' class='boldbase' align='right'>$Lang::tr{'dhcp dns update algo'}:&nbsp;&nbsp;</td>
684 <td width='20%'>
28fee676 685 <select name='DNS_UPDATE_KEY_ALGO_${itf}'>
a057a976 686 <!-- <option value='hmac-sha1' $selected{'DNS_UPDATE_KEY_ALGO_${itf}'}{'hmac-sha1'}>HMAC-SHA1</option> -->
28fee676 687 <option value='hmac-md5' $selected{'DNS_UPDATE_KEY_ALGO_${itf}'}{'hmac-md5'}>HMAC-MD5</option>
b510e12a
SS
688 </select>
689 </td>
690 </tr>
691END
692;
693 }
694
695 # Store configured domain based on the interface
696 # in the temporary variable.
697 push(@domains, $dhcpsettings{"DOMAIN_NAME_${itf}"});
698}
699print <<END
700</table>
701<hr>
702<table width='100%'>
703 <tr>
704 <td align='right'><input type='submit' name='ACTION' value='$Lang::tr{'save'}' /></td>
705 </tr>
706</table>
ac1cfefa
MT
707</form>
708END
709;
710
711&Header::closebox();
712
713&Header::openbox('100%', 'left', $Lang::tr{'dhcp advopt list'});
714# DHCP Advanced options settings
715my %checked=();
716$checked{'ADVOPT_ENABLED'}{'on'} = ($dhcpsettings{'ADVOPT_ENABLED'} ne 'on') ? '' : "checked='checked'";
717
718print "<form method='post' action='$ENV{'SCRIPT_NAME'}'><table width='100%'>";
719my $buttontext = $Lang::tr{'add'};
720if ($dhcpsettings{'KEY1'} ne '') {
721 $buttontext = $Lang::tr{'update'};
722 print "<tr><td class='boldbase'><b>$Lang::tr{'dhcp advopt edit'}</b></td></tr>";
723} else {
724 print "<tr><td class='boldbase'><b>$Lang::tr{'dhcp advopt add'}</b></td></tr>"
725}
726
727#search if the 'option' is in the list and print the syntax model
5410fcbc 728my $opt;
2bbf1766 729
5410fcbc
SS
730# Check if a advanced option name is set.
731if ($dhcpsettings{'ADVOPT_NAME'}) {
732 # Check if the name is part of the list and grab syntax.
733 my @opt = grep(/option $dhcpsettings{'ADVOPT_NAME'}/, @advoptions_list);
734
735 # Assign array element to variable.
736 $opt = @opt[0];
737
738 # Remove newlines.
739 chomp($opt);
740}
2bbf1766 741
ac1cfefa
MT
742if ($opt ne '') {
743 $opt =~ s/option $dhcpsettings{'ADVOPT_NAME'}/Syntax:/; # "option xyz abc" => "syntax: abc"
744 $opt =~ s/;//;
745 $opt = "<tr><td></td><td></td><td colspan='2'>$opt</td></tr>";
746}
747print <<END
748<tr>
e3edceeb 749 <td class='base'>$Lang::tr{'dhcp advopt name'}:&nbsp;<img src='/blob.gif' alt='*' /></td>
ac1cfefa 750 <td><input type='text' name='ADVOPT_NAME' value='$dhcpsettings{'ADVOPT_NAME'}' size='18' /></td>
e3edceeb 751 <td class='base'>$Lang::tr{'dhcp advopt value'}:&nbsp;<img src='/blob.gif' alt='*' /></td>
ac1cfefa
MT
752 <td><input type='text' name='ADVOPT_DATA' value='$dhcpsettings{'ADVOPT_DATA'}' size='40' /></td>
753</tr>$opt<tr>
754 <td class='base'>$Lang::tr{'enabled'}</td><td><input type='checkbox' name='ADVOPT_ENABLED' $checked{'ADVOPT_ENABLED'}{'on'} /></td>
e3edceeb 755 <td class='base'>$Lang::tr{'dhcp advopt scope'}:</td>
ac1cfefa
MT
756 <td>
757END
758;
759
66c36198 760# Put a checkbox for each interface. Checkbox visible disabled if interface is disabled
ac1cfefa
MT
761foreach my $itf (@ITFs) {
762 my $lc_itf=lc($itf);
66c36198 763 $checked{'ADVOPT_SCOPE_${itf}'}{'on'} = $dhcpsettings{"ADVOPT_SCOPE_${itf}"} ne 'on' ? '' : "checked='checked'";
ac1cfefa
MT
764 print "$Lang::tr{\"${lc_itf}\"} <input type='checkbox' name='ADVOPT_SCOPE_${itf}' $checked{'ADVOPT_SCOPE_${itf}'}{'on'} ";
765 print $dhcpsettings{"ENABLE_${itf}"} eq 'on' ? "/>" : "disabled='disabled' />";
766 print "&nbsp; &nbsp;";
767}
768
769print <<END
770 </td>
771</tr>
772</table>
773<hr />
774<table width='100%'>
775<tr>
e3edceeb 776 <td class='base' width='50%'>$Lang::tr{'dhcp advopt scope help'}</td>
e084eea0 777 <td width='50%' align='right'>
ac1cfefa
MT
778 <input type='hidden' name='ACTION' value='$Lang::tr{'add'}1' />
779 <input type='submit' name='SUBMIT' value='$buttontext' />
780 <input type='submit' name='SUBMIT' value='$Lang::tr{'dhcp advopt help'}' />
781 <input type='hidden' name='KEY1' value='$dhcpsettings{'KEY1'}' />
782 </td>
783</tr>
784</table>
785</form>
786END
787;
788#Edited line number (KEY1) passed until cleared by 'save' or 'remove' or 'new sort order'
789
790# print help taken from the file describing options
791if ($dhcpsettings{'SUBMIT'} eq $Lang::tr{'dhcp advopt help'}) {
792 print "<hr />";
793 print "<table width='100%'>";
794 print "<tr><td width='30%'><b>$Lang::tr{'dhcp advopt name'}</b></td><td width='70%'><b>$Lang::tr{'dhcp advopt value'}</b></td>";
795 open(FILE, "$filename3");
796 my @current3 = <FILE>;
797 close(FILE);
798 foreach my $line (@current3) {
799 $line =~ /option ([a-z0-9-]+) (.*);/;
800 print "<tr><td>$1</td><td>$2</td></tr>\n";
801 }
802 print "<tr><td colspan='2'><hr /></td></tr>\n";
803 print '<tr><td>string type</td><td>"quoted string" or 00:01:FF...</td></tr>';
804 print '<tr><td>ip-address type </td><td>10.0.0.1 | www.dot.com</td></tr>';
31016a38 805 print '<tr><td>domain-list type </td><td>"example.com", "eng.example.com", "sales.example.com"</td></tr>';
ac1cfefa
MT
806 print '<tr><td>int,uint types</td><td>numbers</td></tr>';
807 print '<tr><td>flag type</td><td>on | off</td></tr>';
808 print '</table>';
809 print "<hr />";
810 print "<table width='100%'>";
811 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>";
812 print "<tr><td>any-name </td><td> code NNN=$OptionTypes</td></tr>";
813 print '<tr><td>a-string</td><td>code 100=string</td></tr>';
814 print '<tr><td>a-number</td><td>code 101=signed integer 8</td></tr>';
815 print '<tr><td>wpad</td><td>code 252=text</td></tr>';
816 print '<tr><td>wpad</td><td>"http://www.server.fr/path-to/proxy.pac"</td></tr>';
817 print '</table>';
66c36198 818
ac1cfefa
MT
819}
820
821print <<END
822<hr />
823<table width='100%'>
824<tr>
825 <td width='30%' class='boldbase' align='center'><b>$Lang::tr{'dhcp advopt name'}</b></td>
826 <td width='50%' class='boldbase' align='center'><b>$Lang::tr{'dhcp advopt value'}</b></td>
827 <td width='20%' class='boldbase' align='center'><b>$Lang::tr{'dhcp advopt scope'}</b></td>
828 <td colspan='3' class='boldbase' align='center'><b>$Lang::tr{'action'}</b></td>
829</tr>
830END
831;
832my $key = 0;
833foreach my $line (@current1) {
834 my $gif = '';
835 my $gdesc = '';
836 chomp($line); # remove newline
837 my @temp = split(/\t/,$line);
838
839 if ($temp[0] eq "on") {
840 $gif = 'on.gif';
841 $gdesc = $Lang::tr{'click to disable'};
842 } else {
843 $gif = 'off.gif';
66c36198 844 $gdesc = $Lang::tr{'click to enable'};
ac1cfefa
MT
845 }
846
847 if ($dhcpsettings{'KEY1'} eq $key) {
d3aec718 848 print "<tr class='colouryellow'>";
ac1cfefa 849 } elsif ($key % 2) {
d3aec718 850 print "<tr class='color22'>";
ac1cfefa 851 } else {
d3aec718 852 print "<tr class='color20'>";
ac1cfefa
MT
853 }
854
855 print <<END
856<td align='center'>$temp[1]</td>
857<td align='center'>$temp[2]</td>
858<td align='center'>
859END
860;
861 # Prepare a global flag to make easy reading
862 my $global = '';
863 my $disabledTogle = '';
864 my $disabledEditRemove = '';
865 if ( ExistNewOptionDefinition ($temp[1] . ' ' . $temp[2]) ) {
866 $global = $Lang::tr{'dhcp advopt definition'};
867 $disabledTogle = "disabled='disabled'";
868 # Search if it is a used NewOptionDefinition to also disable edit & delete
869 $disabledEditRemove = "disabled='disabled'" if (IsUsedNewOptionDefinition ($temp[1], $temp[2]));
870 } else {
871 $global = $Lang::tr{'dhcp advopt scope global'};
872 }
66c36198
PM
873
874
ac1cfefa
MT
875 # Print each checked interface
876 for (my $key=0; $key<@ITFs; $key++) {
877 my $itf = $temp[3+$key];
878 if ($itf ne 'off') { # Only if an interface name is read
879 print "$itf";
880 $global=''; # fall to local scope !
881 }
882 }
883 print <<END
884$global</td>
885<td align='center'>
886<form method='post' action='$ENV{'SCRIPT_NAME'}'>
887<input type='hidden' name='ACTION' value='$Lang::tr{'toggle enable disable'}1' />
888<input $disabledTogle type='image' name='$Lang::tr{'toggle enable disable'}' src='/images/$gif' alt='$gdesc' title='$gdesc' />
889<input type='hidden' name='KEY1' value='$key' />
890</form>
891</td>
892
893<td align='center'>
894<form method='post' action='$ENV{'SCRIPT_NAME'}'>
895<input type='hidden' name='ACTION' value='$Lang::tr{'edit'}1' />
896<input $disabledEditRemove type='image' name='$Lang::tr{'edit'}' src='/images/edit.gif' alt='$Lang::tr{'edit'}' title='$Lang::tr{'edit'}' />
897<input type='hidden' name='KEY1' value='$key' />
898</form>
899</td>
900
901<td align='center'>
902<form method='post' action='$ENV{'SCRIPT_NAME'}'>
903<input type='hidden' name='ACTION' value='$Lang::tr{'remove'}1' />
904<input $disabledEditRemove type='image' name='$Lang::tr{'remove'}' src='/images/delete.gif' alt='$Lang::tr{'remove'}' title='$Lang::tr{'remove'}' />
905<input type='hidden' name='KEY1' value='$key' />
906</form>
907</td>
908</tr>
909END
910;
911 $key++;
912}
913
914print "</table>";
915
916# If there are dhcp options, print Key to action icons
917if ($key) {
918print <<END
919<table>
920<tr>
921 <td class='boldbase'>&nbsp;<b>$Lang::tr{'legend'}:&nbsp;</b></td>
922 <td><img src='/images/on.gif' alt='$Lang::tr{'click to disable'}' /></td>
923 <td class='base'>$Lang::tr{'click to disable'}</td>
924 <td>&nbsp;&nbsp;</td>
925 <td><img src='/images/off.gif' alt='$Lang::tr{'click to enable'}' /></td>
926 <td class='base'>$Lang::tr{'click to enable'}</td>
927 <td>&nbsp;&nbsp;</td>
928 <td><img src='/images/edit.gif' alt='$Lang::tr{'edit'}' /></td>
929 <td class='base'>$Lang::tr{'edit'}</td>
930 <td>&nbsp;&nbsp;</td>
931 <td><img src='/images/delete.gif' alt='$Lang::tr{'remove'}' /></td>
932 <td class='base'>$Lang::tr{'remove'}</td>
933</tr>
934</table>
935END
936;
937}
938&Header::closebox();
939
940&Header::openbox('100%', 'left', $Lang::tr{'current fixed leases'});
941# Fixed leases screens
942$checked{'FIX_ENABLED'}{'on'} = ($dhcpsettings{'FIX_ENABLED'} ne 'on') ? '' : "checked='checked'";
943
944$buttontext = $Lang::tr{'add'};
945print "<form method='post' action='$ENV{'SCRIPT_NAME'}'><table width='100%'>";
946
947if ($dhcpsettings{'KEY2'} ne '') {
948 $buttontext = $Lang::tr{'update'};
949 print "<tr><td class='boldbase' colspan='3'><b>$Lang::tr{'edit an existing lease'}</b></td></tr>";
950} else {
951 print "<tr><td class='boldbase' colspan='3'><b>$Lang::tr{'add new lease'}</b></td></tr>"
952}
953print <<END
954<tr>
e3edceeb 955 <td class='base'>$Lang::tr{'mac address'}:&nbsp;<img src='/blob.gif' alt='*' /></td>
ac1cfefa 956 <td><input type='text' name='FIX_MAC' value='$dhcpsettings{'FIX_MAC'}' size='18' /></td>
e3edceeb 957 <td class='base'>$Lang::tr{'ip address'}:&nbsp;<img src='/blob.gif' alt='*' /></td>
ac1cfefa 958 <td><input type='text' name='FIX_ADDR' value='$dhcpsettings{'FIX_ADDR'}' size='18' /></td>
e3edceeb 959 <td class='base'>$Lang::tr{'remark'}:</td>
ac1cfefa
MT
960 <td><input type='text' name='FIX_REMARK' value='$dhcpsettings{'FIX_REMARK'}' size='18' /></td>
961</tr><tr>
962 <td class='base'>$Lang::tr{'enabled'}</td><td><input type='checkbox' name='FIX_ENABLED' $checked{'FIX_ENABLED'}{'on'} /></td>
963</tr><tr>
964 <td colspan = '3'><b>$Lang::tr{'dhcp bootp pxe data'}</b></td>
965</tr><tr>
e3edceeb 966 <td class='base'>next-server:</td>
ac1cfefa 967 <td><input type='text' name='FIX_NEXTADDR' value='$dhcpsettings{'FIX_NEXTADDR'}' size='18' /></td>
e3edceeb 968 <td class='base'>filename:</td>
ac1cfefa 969 <td><input type='text' name='FIX_FILENAME' value='$dhcpsettings{'FIX_FILENAME'}' size='18' /></td>
e3edceeb 970 <td class='base'>root path:</td>
ac1cfefa
MT
971 <td><input type='text' name='FIX_ROOTPATH' value='$dhcpsettings{'FIX_ROOTPATH'}' size='18' /></td>
972</tr>
973</table>
974<hr />
975<table width='100%'>
976<tr>
e3edceeb 977 <td class='base' width='50%'><img src='/blob.gif' align='top' alt='*' />&nbsp;$Lang::tr{'required field'}</td>
e084eea0 978 <td width='50%' align='right'>
ac1cfefa
MT
979 <input type='hidden' name='ACTION' value='$Lang::tr{'add'}2' />
980 <input type='submit' name='SUBMIT' value='$buttontext' />
981 <input type='hidden' name='KEY2' value='$dhcpsettings{'KEY2'}' /></td>
982</tr>
983</table>
984</form>
985END
986;
987#Edited line number (KEY2) passed until cleared by 'save' or 'remove' or 'new sort order'
988
1554e8a3
MT
989# Search for static leases
990my $search_query = $dhcpsettings{'q'};
991
992if (scalar @current2 >= 10) {
993 print <<END;
994 <form method="POST" action="#search">
995 <a name="search"></a>
996 <table width='100%'>
997 <tr>
998 <td>
999 <input type="text" name="q" value="$search_query">
1000 <input type="submit" value="$Lang::tr{'search'}">
1001 </td>
1002 </tr>
1003 </table>
1004 </form>
1005END
1006}
1007
ac1cfefa 1008print <<END
52ca3c80 1009<table width='100%' class='tbl'>
ac1cfefa 1010<tr>
ee556e82
SG
1011 <th width='15%' align='center'><a href='$ENV{'SCRIPT_NAME'}?FETHER'><b>$Lang::tr{'mac address'}</b></a></th>
1012 <th width='10%' align='center'><a href='$ENV{'SCRIPT_NAME'}?FIPADDR'><b>$Lang::tr{'ip address'}</b></a></th>
1013 <th width='15%' align='center'><b>$Lang::tr{'hostname'}</b></th>
52ca3c80
AM
1014 <th width='15%' align='center'><b>$Lang::tr{'remark'}</b></th>
1015 <th width='15%' class='boldbase' align='center'><b>next-server</b></th>
1016 <th width='15%' class='boldbase' align='center'><b>filename</b></th>
1017 <th width='15%' class='boldbase' align='center'><b>root path</b></th>
1018 <th colspan='3' class='boldbase' align='center'><b>$Lang::tr{'action'}</b></th>
ac1cfefa
MT
1019</tr>
1020END
1021;
1022my $ipdup = 0;
1023my %ipinuse = ();
1024my %macdupl = (); # Duplicate MACs have to be on different subnets
1025my %ipoutside = ();
b52a84dd 1026my %ipinrange = ();
ac1cfefa 1027
b52a84dd 1028# mark duplicate IP, duplicate MAC or IP in dynamic range
ac1cfefa
MT
1029foreach my $line (@current2) {
1030 my @temp = split(/\,/,$line);
1031 $macdupl{$temp[0]} += 1;
66c36198 1032 if ($macdupl{$temp[0]} > 1) {
ac1cfefa
MT
1033 $ipdup = 1; # Flag up duplicates for use later
1034 }
1035 $ipinuse{$temp[1]} += 1;
66c36198 1036 if ($ipinuse{$temp[1]} > 1) {
ac1cfefa
MT
1037 $ipdup = 1; # Flag up duplicates for use later
1038 }
ac1cfefa 1039 $ipoutside{$temp[1]} = 1;
b52a84dd 1040 $ipinrange{$temp[1]} = 0;
ac1cfefa 1041 foreach my $itf (@ITFs) {
b52a84dd
AB
1042 # Mark IP addresses outwith known subnets
1043 if ( &General::IpInSubnet($temp[1],
1044 $netsettings{"${itf}_NETADDRESS"},
1045 $netsettings{"${itf}_NETMASK"})) {
1046 $ipoutside{$temp[1]} = 0;
1047 }
1048 # Mark IP addresses that overlap with dynamic range
1049 if (&Network::ip_address_in_range($temp[1],
1050 $dhcpsettings{"START_ADDR_${itf}"},
1051 $dhcpsettings{"END_ADDR_${itf}"})) {
1052 $ipinrange{$temp[1]} = 1;
1053 }
ac1cfefa
MT
1054 }
1055}
1056
1057$key = 0;
52ca3c80 1058my $col="";
ac1cfefa
MT
1059foreach my $line (@current2) {
1060 my $gif = '';
1061 my $gdesc = '';
1062 chomp($line); # remove newline
1063 my @temp = split(/\,/,$line);
1064
1065 if ($temp[2] eq "on") {
1066 $gif = 'on.gif';
1067 $gdesc = $Lang::tr{'click to disable'};
1068 } else {
1069 $gif = 'off.gif';
66c36198 1070 $gdesc = $Lang::tr{'click to enable'};
ac1cfefa
MT
1071 }
1072
1554e8a3
MT
1073 # Skip all entries that do not match the search query
1074 if ($search_query ne "") {
e353470a
MT
1075 if (!grep(/$search_query/, @temp)) {
1076 $key++;
1077 next;
1078 }
1554e8a3
MT
1079 }
1080
ac1cfefa 1081 if ($dhcpsettings{'KEY2'} eq $key) {
52ca3c80 1082 print "<tr>";
d3aec718 1083 $col="class='colouryellow'";
ac1cfefa 1084 } elsif ($key % 2) {
52ca3c80 1085 print "<tr>";
d3aec718 1086 $col="class='color20'";
ac1cfefa 1087 } else {
52ca3c80 1088 print "<tr>";
d3aec718 1089 $col="class='color22'";
ac1cfefa
MT
1090 }
1091 my $TAG0 = '';
1092 my $TAG1 = '';
1093 my $TAG2 = '';
1094 my $TAG3 = '';
1095 my $TAG4 = '';
66c36198 1096 if ($ipinuse{$temp[1]} > 1) {
ac1cfefa
MT
1097 $TAG0 = '<b>';
1098 $TAG1 = '</b>';
1099 }
66c36198 1100 if ($macdupl{$temp[0]} > 1) {
ac1cfefa
MT
1101 $TAG2 = '<b>';
1102 $TAG3 = '</b>';
1103 }
66c36198 1104 if ($ipoutside{$temp[1]} > 0) {
d3aec718 1105 $TAG4 = "class='orange'" if ($dhcpsettings{'KEY2'} ne $key);
ac1cfefa 1106 }
b52a84dd 1107 if ($ipinrange{$temp[1]} > 0) {
d3aec718 1108 $TAG4 = "class='red'" if ($dhcpsettings{'KEY2'} ne $key);
b52a84dd 1109 }
ac1cfefa 1110
ee556e82
SG
1111 # resolved name (if exists)
1112 my $iaddr = inet_aton($temp[1]);
1113 my $rname = gethostbyaddr($iaddr, AF_INET);
1114 if (!$rname || $rname eq "") { $rname = $Lang::tr{'ptr lookup failed'}; }
ac1cfefa 1115 print <<END
52ca3c80 1116<td align='center' $col>$TAG2$temp[0]$TAG3</td>
de4dea96 1117<td align='center' $TAG4 $col>$TAG0$temp[1]$TAG1</td>
ee556e82 1118<td align='center' $col>$rname&nbsp;</td>
52ca3c80
AM
1119<td align='center' $col>$temp[6]&nbsp;</td>
1120<td align='center' $col>$temp[3]&nbsp;</td>
1121<td align='center' $col>$temp[4]&nbsp;</td>
1122<td align='center' $col>$temp[5]&nbsp;</td>
1123
1124<td align='center' $col>
ac1cfefa
MT
1125<form method='post' action='$ENV{'SCRIPT_NAME'}'>
1126<input type='hidden' name='ACTION' value='$Lang::tr{'toggle enable disable'}2' />
1127<input type='image' name='$Lang::tr{'toggle enable disable'}' src='/images/$gif' alt='$gdesc' title='$gdesc' />
1128<input type='hidden' name='KEY2' value='$key' />
1129</form>
1130</td>
1131
52ca3c80 1132<td align='center' $col>
ac1cfefa
MT
1133<form method='post' action='$ENV{'SCRIPT_NAME'}'>
1134<input type='hidden' name='ACTION' value='$Lang::tr{'edit'}2' />
1135<input type='image' name='$Lang::tr{'edit'}' src='/images/edit.gif' alt='$Lang::tr{'edit'}' title='$Lang::tr{'edit'}' />
1136<input type='hidden' name='KEY2' value='$key' />
1137</form>
1138</td>
1139
52ca3c80 1140<td align='center' $col>
ac1cfefa
MT
1141<form method='post' action='$ENV{'SCRIPT_NAME'}'>
1142<input type='hidden' name='ACTION' value='$Lang::tr{'remove'}2' />
1143<input type='image' name='$Lang::tr{'remove'}' src='/images/delete.gif' alt='$Lang::tr{'remove'}' title='$Lang::tr{'remove'}' />
1144<input type='hidden' name='KEY2' value='$key' />
1145</form>
1146</td>
1147</tr>
1148END
1149;
1150 $key++;
1151}
1152print "</table>";
1153
1154# If the fixed lease file contains entries, print Key to action icons
1155if ($key) {
1156my $dup = $ipdup ? "<td class='base'>$Lang::tr{'duplicate ip bold'}</td>" :'';
1157print <<END
1158<table>
1159<tr>
1160 <td class='boldbase'>&nbsp;<b>$Lang::tr{'legend'}:&nbsp;</b></td>
1161 <td><img src='/images/on.gif' alt='$Lang::tr{'click to disable'}' /></td>
1162 <td class='base'>$Lang::tr{'click to disable'}</td>
1163 <td>&nbsp;&nbsp;</td>
1164 <td><img src='/images/off.gif' alt='$Lang::tr{'click to enable'}' /></td>
1165 <td class='base'>$Lang::tr{'click to enable'}</td>
1166 <td>&nbsp;&nbsp;</td>
1167 <td><img src='/images/edit.gif' alt='$Lang::tr{'edit'}' /></td>
1168 <td class='base'>$Lang::tr{'edit'}</td>
1169 <td>&nbsp;&nbsp;</td>
1170 <td><img src='/images/delete.gif' alt='$Lang::tr{'remove'}' /></td>
1171 <td class='base'>$Lang::tr{'remove'}</td>
1172</tr>
1173<tr>
1174 <td>&nbsp;</td>
de4dea96 1175 <td>&nbsp;</td>
d3aec718 1176 <td class='base orange'>$Lang::tr{'ip address outside subnets'}</td>
b52a84dd 1177 <td>&nbsp;&nbsp</td>
d3aec718 1178 <td class='base red'>$Lang::tr{'dhcp fixed ip address in dynamic range'}</td>
ac1cfefa
MT
1179 <td>&nbsp;</td>
1180 <td>&nbsp;</td>
1181 $dup
1182</tr>
1183</table>
1184END
1185;
1186}
1187
1188&Header::closebox();
1189
ac1cfefa
MT
1190foreach my $itf (@ITFs) {
1191 if ($dhcpsettings{"ENABLE_${itf}"} eq 'on') {
1192 # display leases with a list of actions to do with the global select checkbox.
1193 &Header::PrintActualLeases("+"); # "+" => create fixed leases from nodeaddress
1194 last; #Print one time only for all interfaces
1195 };
1196}
1197
1198&Header::closebigbox();
1199&Header::closepage();
1200
1201## Ouf it's the end !
1202
1203sub sortcurrent1 # by now, do not sort, just write
1204{
1205 open(FILE, ">$filename1") or die 'Unable to open dhcp advanced options file.';
1206 print FILE @current1;
1207 close(FILE);
1208}
1209
1210
1211# Sort the "current2" array according to choices
1212sub sortcurrent2
1213{
1214 our %entries = ();
1215
1216 sub fixedleasesort {
1217 my $qs='';
1218 if (rindex ($dhcpsettings{'SORT_FLEASELIST'},'Rev') != -1) {
1219 $qs=substr ($dhcpsettings{'SORT_FLEASELIST'},0,length($dhcpsettings{'SORT_FLEASELIST'})-3);
1220 if ($qs eq 'FIPADDR') {
1221 my @a = split(/\./,$entries{$a}->{$qs});
1222 my @b = split(/\./,$entries{$b}->{$qs});
1223 ($b[0]<=>$a[0]) ||
1224 ($b[1]<=>$a[1]) ||
1225 ($b[2]<=>$a[2]) ||
1226 ($b[3]<=>$a[3]);
1227 } else {
1228 $entries{$b}->{$qs} cmp $entries{$a}->{$qs};
1229 }
1230 } else { #not reverse
1231 $qs=$dhcpsettings{'SORT_FLEASELIST'};
1232 if ($qs eq 'FIPADDR') {
1233 my @a = split(/\./,$entries{$a}->{$qs});
1234 my @b = split(/\./,$entries{$b}->{$qs});
1235 ($a[0]<=>$b[0]) ||
1236 ($a[1]<=>$b[1]) ||
1237 ($a[2]<=>$b[2]) ||
1238 ($a[3]<=>$b[3]);
1239 } else {
1240 $entries{$a}->{$qs} cmp $entries{$b}->{$qs};
1241 }
1242 }
1243 }
1244
1245 #Use an associative array (%entries)
1246 foreach my $line (@current2) {
1247 chomp( $line); #remove newline because can be on field 5 or 6 (addition of REMARK)
1248 my @temp = split (',',$line);
1249 my @record = ('FETHER',$temp[0],'FIPADDR',$temp[1],'DATA',join(',',@temp[2..6]));
1250 my $record = {}; # create a reference to empty hash
1251 %{$record} = @record; # populate that hash with @record
1252 # use combination of ether & IP as key to allow duplicates in either but not both
1253 $entries{$record->{FETHER} . $record->{FIPADDR}} = $record; # add this to a hash of hashes
1254 }
66c36198 1255
ac1cfefa
MT
1256 open(FILE, ">$filename2") or die 'Unable to open fixed lease file.';
1257 foreach my $entry ( sort fixedleasesort keys %entries) {
1258 print FILE "$entries{$entry}->{FETHER},$entries{$entry}->{FIPADDR},$entries{$entry}->{DATA}\n";
1259 }
1260 close(FILE);
1261
1262 # Reload sorted @current2
1263 open (FILE, "$filename2");
1264 @current2 = <FILE>;
1265 close (FILE);
1266 undef (%entries); #This array is reused latter. Clear it.
1267}
66c36198 1268
ac1cfefa
MT
1269# Build the configuration file mixing settings, fixed leases and advanced options
1270sub buildconf {
1271 open(FILE, ">/${General::swroot}/dhcp/dhcpd.conf") or die "Unable to write dhcpd.conf file";
1272 flock(FILE, 2);
1273
1274 # Global settings
ac1cfefa
MT
1275 print FILE "deny bootp; #default\n";
1276 print FILE "authoritative;\n";
f5fb9a04
MT
1277
1278 # DNS Update settings
1279 if ($dhcpsettings{'DNS_UPDATE_ENABLED'} eq 'on') {
1280 print FILE "ddns-updates on;\n";
1281 print FILE "ddns-update-style interim;\n";
5fd7e84c 1282 print FILE "ddns-ttl 60; # 1 min\n";
f5fb9a04
MT
1283 print FILE "ignore client-updates;\n";
1284 print FILE "update-static-leases on;\n";
1285 } else {
1286 print FILE "ddns-update-style none;\n";
1287 }
66c36198 1288
ac1cfefa
MT
1289 # Write first new option definition
1290 foreach my $line (@current1) {
1291 chomp($line); # remove newline
1292 my @temp = split(/\t/,$line);
1293 if (ExistNewOptionDefinition ($temp[1] . ' ' . $temp[2])) {
1294 print FILE "option $temp[1] $temp[2];\n";
1295 }
1296 }
1297 # Write other global options
1298 foreach my $line (@current1) {
1299 chomp($line); # remove newline
1300 my @temp = split(/\t/,$line);
66c36198 1301
ac1cfefa
MT
1302 if ($temp[0] eq 'on' && !ExistNewOptionDefinition ($temp[1] . ' ' . $temp[2])){ # active & !definition
1303 my $global=1;
1304 for (my $key=0; $key<@ITFs; $key++) {
1305 my $itf = $temp[3+$key];
1306 if ($itf ne 'off') # Only if an interface name is read
1307 {
1308 $global=0;
1309 }
1310 }
1311 if ($global) {
1312 print FILE "option $temp[1] $temp[2];\n";
1313 }
66c36198 1314 }# on
ac1cfefa 1315 }# foreach line
28fee676 1316 print FILE "\n";
ac1cfefa
MT
1317
1318 #Subnet range definition
1319 foreach my $itf (@ITFs) {
1320 my $lc_itf=lc($itf);
1321 if ($dhcpsettings{"ENABLE_${itf}"} eq 'on' ){
28fee676 1322 print FILE "subnet " . $netsettings{"${itf}_NETADDRESS"} . " netmask ". $netsettings{"${itf}_NETMASK"} . " #$itf\n";
ac1cfefa 1323 print FILE "{\n";
9dbf3c49
AB
1324 if ($dhcpsettings{"START_ADDR_${itf}"}) {
1325 print FILE "pool {\n";
1326 print FILE "\trange " . $dhcpsettings{"START_ADDR_${itf}"} . ' ' . $dhcpsettings{"END_ADDR_${itf}"}.";\n";
1327 print FILE "\tdeny known-clients;\n" if ($dhcpsettings{"DENY_KNOWN_CLIENTS_${itf}"} eq 'on');
1328 print FILE " }\n"; # pool
1329 }
ac1cfefa
MT
1330 print FILE "\toption subnet-mask " . $netsettings{"${itf}_NETMASK"} . ";\n";
1331 print FILE "\toption domain-name \"" . $dhcpsettings{"DOMAIN_NAME_${itf}"} . "\";\n";
1332 print FILE "\toption routers " . $netsettings{"${itf}_ADDRESS"} . ";\n";
1333 print FILE "\toption domain-name-servers " . $dhcpsettings{"DNS1_${itf}"} if ($dhcpsettings{"DNS1_${itf}"});
1334 print FILE ", " . $dhcpsettings{"DNS2_${itf}"} if ($dhcpsettings{"DNS2_${itf}"});
1335 print FILE ";\n" if ($dhcpsettings{"DNS1_${itf}"});
1336 print FILE "\toption ntp-servers " . $dhcpsettings{"NTP1_${itf}"} if ($dhcpsettings{"NTP1_${itf}"});
1337 print FILE ", " . $dhcpsettings{"NTP2_${itf}"} if ($dhcpsettings{"NTP2_${itf}"});
1338 print FILE ";\n" if ($dhcpsettings{"NTP1_${itf}"});
1339 print FILE "\toption netbios-name-servers " . $dhcpsettings{"WINS1_${itf}"} if ($dhcpsettings{"WINS1_${itf}"});
1340 print FILE ", " . $dhcpsettings{"WINS2_${itf}"} if ($dhcpsettings{"WINS2_${itf}"});
1341 print FILE ";\n" if ($dhcpsettings{"WINS1_${itf}"});
d1883e28 1342 print FILE "\tnext-server " . $dhcpsettings{"NEXT_${itf}"} . ";\n" if ($dhcpsettings{"NEXT_${itf}"});
820ab96c 1343 print FILE "\tfilename \"" . &EscapeFilename($dhcpsettings{"FILE_${itf}"}) . "\";\n" if ($dhcpsettings{"FILE_${itf}"});
ac1cfefa
MT
1344 print FILE "\tdefault-lease-time " . ($dhcpsettings{"DEFAULT_LEASE_TIME_${itf}"} * 60). ";\n";
1345 print FILE "\tmax-lease-time " . ($dhcpsettings{"MAX_LEASE_TIME_${itf}"} * 60) . ";\n";
1346 print FILE "\tallow bootp;\n" if ($dhcpsettings{"ENABLEBOOTP_${itf}"} eq 'on');
1347
1348
1349
1350 # Write scoped options
1351 foreach my $line (@current1) {
1352 chomp($line); # remove newline
1353 my @temp = split(/\t/,$line); # Use TAB separator !
66c36198 1354
ac1cfefa
MT
1355 if ($temp[0] eq 'on'){
1356 for (my $key=0; $key<@ITFs; $key++) {
1357 if ($itf eq $temp[3+$key]) # Only is an interface name is read
1358 {
1359 print FILE "\toption $temp[1] $temp[2];\n";
1360 }
1361 }
66c36198 1362 }# on
ac1cfefa 1363 }# foreach line
28fee676 1364 print FILE "} #$itf\n\n";
ac1cfefa 1365
f5fb9a04 1366 if (($dhcpsettings{"DNS_UPDATE_ENABLED"} eq "on") && ($dhcpsettings{"DNS_UPDATE_KEY_NAME_${itf}"} ne "")) {
28fee676 1367 print FILE "key " . $dhcpsettings{"DNS_UPDATE_KEY_NAME_${itf}"} . " {\n";
f5fb9a04
MT
1368 print FILE "\talgorithm " . $dhcpsettings{"DNS_UPDATE_KEY_ALGO_${itf}"} . ";\n";
1369 print FILE "\tsecret \"" . $dhcpsettings{"DNS_UPDATE_KEY_SECRET_${itf}"} . "\";\n";
1370 print FILE "};\n\n";
1371
1372 print FILE "zone " . $dhcpsettings{"DOMAIN_NAME_${itf}"} . ". {\n";
1373 print FILE "\tkey " . $dhcpsettings{"DNS_UPDATE_KEY_NAME_${itf}"} . ";\n";
1374 print FILE "}\n\n";
1375 }
ac1cfefa 1376
dfdf076d 1377 &General::system('/usr/bin/touch', "${General::swroot}/dhcp/enable_${lc_itf}");
ac1cfefa
MT
1378 &General::log("DHCP on ${itf}: " . $Lang::tr{'dhcp server enabled'})
1379 } else {
1380 unlink "${General::swroot}/dhcp/enable_${lc_itf}";
1381 &General::log("DHCP on ${itf}: " . $Lang::tr{'dhcp server disabled'})
1382 }
1383 }
1384
1385 #write fixed leases if any. Does not handle duplicates to write them elsewhere than the global scope.
1386 my $key = 0;
1387 foreach my $line (@current2) {
1388 chomp($line);
1389 my @temp = split(/\,/,$line);
1390 if ($temp[2] eq "on") {
1391 print FILE "\nhost fix$key # $temp[6]\n";
1392 print FILE "{\n";
1393 print FILE "\thardware ethernet $temp[0];\n";
1394 print FILE "\tfixed-address $temp[1];\n";
1395 print FILE "\tnext-server $temp[3];\n" if ($temp[3]);
820ab96c 1396 print FILE "\tfilename \"" . &EscapeFilename($temp[4]) . "\";\n" if ($temp[4]);
ac1cfefa
MT
1397 print FILE "\toption root-path \"$temp[5]\";\n" if ($temp[5]);
1398 print FILE "}\n";
1399 $key++;
1400 }
1401 }
55b2f2eb 1402 print FILE "include \"${General::swroot}/dhcp/dhcpd.conf.local\";\n";
ac1cfefa 1403 close FILE;
dfdf076d
MT
1404 if ( $dhcpsettings{"ENABLE_GREEN"} eq 'on' || $dhcpsettings{"ENABLE_BLUE"} eq 'on' ) {&General::system('/usr/local/bin/dhcpctrl', 'enable');}
1405 else {&General::system('/usr/local/bin/dhcpctrl', 'disable');}
1406 &General::system_background('/usr/local/bin/dhcpctrl', 'restart');
ac1cfefa
MT
1407}
1408
1409#
1410# Receive a string and if it match model for a new option,
1411# add it to the list %newOptions
1412#
1413my %NewOptions = ();
1414
1415sub AddNewOptionDefinition {
1416 my ($line) = @_;
1417 if ( $line =~ /^([-\w]+)( code \d+=($OptionTypes))/ ) {
1418 $NewOptions{$1} = $2;
1419 #&General::log ("new:<$1><$2>");
1420 return 1;
1421 }
1422 return 0;
1423}
1424
1425#
1426# Check existence of definition for a new option
1427#
1428sub ExistNewOptionDefinition {
1429 my ($line) = @_;
1430
1431 if ( $line =~ /^([-\w]+)( code \d+=($OptionTypes))/ ) {
1432 return defined $NewOptions{$1};
1433 }
1434 return 0;
1435}
1436
1437#
1438# Check if it is a new option (definition must exist)
1439# "code=" test eliminate a false response when definition exists
1440# but this string is a definition with bad $OptionTypes.
1441sub ValidNewOption {
1442 my ($line) = @_;
1443 if ($line =~ /^([-\w]+) (.*)/ ) {
1444 return defined ( $NewOptions{$1} ) && $2 !~ /code=/;
1445 }
1446 return 0;
1447}
1448
1449#
1450# Check if the new option $opt is used, except the definition of itself!
1451#
1452sub IsUsedNewOptionDefinition {
1453 my ($opt,$val) = @_;
1454
1455 foreach my $line (@current1) {
1456 #chomp($line); # remove newline #don't know why, but this remove newline in @current1 .... !
1457 my @temp = split(/\t/,$line);
1458 # if we find something "opt value" & value != "code nnn=" it's ok.
1459 return 1 if ( ($opt eq $temp[1]) && ($temp[2] !~ /code \d+=/) );
1460 }
1461 return 0;
1462}
820ab96c
MT
1463
1464sub EscapeFilename($) {
1465 my $filename = shift;
1466
1467 # Replace all single / by \/
1468 $filename =~ s/\//\\\//g;
1469
1470 return $filename;
1471}