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