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