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