###############################################################################
# #
# IPFire.org - A linux based firewall #
-# Copyright (C) 2007 Michael Tremer & Christian Schmidt #
+# Copyright (C) 2007-2024 IPFire Team <info@ipfire.org> #
# #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
###############################################################################
use strict;
+use experimental 'smartmatch';
# enable only the following on debugging purpose
#use warnings;
my @nosaved=();
my %color = ();
-#Basic syntax allowed for new Option definition. Not implemented: RECORDS & array of RECORDS
-our $OptionTypes = 'boolean|((un)?signed )?integer (8|16|32)|ip-address|text|string|encapsulate \w+|array of ip-address';
+#Basic syntax allowed for new Option definition. Not implemented: RECORDS & array of RECORDS
+our $OptionTypes = 'boolean|((un)?signed )?integer (8|16|32)|ip-address|text|string|encapsulate \w+|array of (ip-address|integer (8|16|32))';
&Header::showhttpheaders();
our @ITFs=('GREEN');
$dhcpsettings{"NTP2_${itf}"} = '';
$dhcpsettings{"NEXT_${itf}"} = '';
$dhcpsettings{"FILE_${itf}"} = '';
+ $dhcpsettings{"DNS_UPDATE_KEY_NAME_${itf}"} = '';
+ $dhcpsettings{"DNS_UPDATE_KEY_SECRET_${itf}"} = '';
+ $dhcpsettings{"DNS_UPDATE_KEY_ALGO_${itf}"} = '';
+ $dhcpsettings{"DENY_KNOWN_CLIENTS_${itf}"} = 'off';
}
$dhcpsettings{'SORT_FLEASELIST'} = 'FIPADDR';
$dhcpsettings{'SORT_LEASELIST'} = 'IPADDR';
+# DNS Update settings
+$dhcpsettings{'DNS_UPDATE_ENABLED'} = 'off';
+
#Settings2 for editing the multi-line list
#Must not be saved with writehash !
$dhcpsettings{'FIX_MAC'} = '';
&General::readhash("${General::swroot}/ethernet/settings", \%netsettings);
&General::readhash("${General::swroot}/main/settings", \%mainsettings);
&General::readhash("${General::swroot}/time/settings", \%timesettings);
-&General::readhash("/srv/web/ipfire/html/themes/".$mainsettings{'THEME'}."/include/colors.txt", \%color);
+&General::readhash("/srv/web/ipfire/html/themes/ipfire/include/colors.txt", \%color);
#Get GUI values
&Header::getcgihash(\%dhcpsettings);
our @current2 = <FILE>;
close(FILE);
+# Open and read-in file which contains the list of allowed advanced options.
+open(FILE, $filename3) or die "Could not open $filename3. $!\n";
+
+# Grab file content.
+my @advoptions_list = <FILE>;
+
+# Close file handle.
+close(FILE);
+
# Check Settings1 first because they are needed by &buildconf
if ($dhcpsettings{'ACTION'} eq $Lang::tr{'save'}) {
foreach my $itf (@ITFs) {
$errormessage = "DHCP on ${itf}: " . $Lang::tr{'invalid end address'};
goto ERROR;
}
- if (! &General::IpInSubnet ( $dhcpsettings{"START_ADDR_${itf}"},
+ if (! &General::IpInSubnet ( $dhcpsettings{"START_ADDR_${itf}"},
$netsettings{"${itf}_NETADDRESS"},
$netsettings{"${itf}_NETMASK"})) {
$errormessage = "DHCP on ${itf}: " . $Lang::tr{'invalid start address'};
goto ERROR;
}
}
-
+
if ($dhcpsettings{"END_ADDR_${itf}"}) {
if (!(&General::validip($dhcpsettings{"END_ADDR_${itf}"}))) {
$errormessage = "DHCP on ${itf}: " . $Lang::tr{'invalid end address'};
$errormessage = "DHCP on ${itf}: " . $Lang::tr{'invalid start address'};
goto ERROR;
}
- if (! &General::IpInSubnet ( $dhcpsettings{"END_ADDR_${itf}"},
+ if (! &General::IpInSubnet ( $dhcpsettings{"END_ADDR_${itf}"},
$netsettings{"${itf}_NETADDRESS"},
- $netsettings{"${itf}_NETMASK"})) {
+ $netsettings{"${itf}_NETMASK"})) {
$errormessage = "DHCP on ${itf}: " . $Lang::tr{'invalid end address'};
goto ERROR;
}
}
}
+ if ($dhcpsettings{"DENY_KNOWN_CLIENTS_${itf}"} eq 'on') {
+ if (($dhcpsettings{"START_ADDR_${itf}"}) eq '' && ($dhcpsettings{"END_ADDR_${itf}"}) eq '') {
+ $errormessage = "DHCP on ${itf}: " . $Lang::tr{'dhcp valid range required when deny known clients checked'};
+ goto ERROR;
+ }
+ }
+
if (!($dhcpsettings{"DEFAULT_LEASE_TIME_${itf}"} =~ /^\d+$/)) {
$errormessage = "DHCP on ${itf}: " . $Lang::tr{'invalid default lease time'} . $dhcpsettings{'DEFAULT_LEASE_TIME_${itf}'};
goto ERROR;
goto ERROR;
}
if (! $dhcpsettings{"DNS1_${itf}"}) {
- $errormessage = "DHCP on ${itf}: " . $Lang::tr{'cannot specify secondary dns without specifying primary'};
+ $errormessage = "DHCP on ${itf}: " . $Lang::tr{'cannot specify secondary dns without specifying primary'};
goto ERROR;
}
}
if (! $dhcpsettings{"WINS1_${itf}"} ) {
$errormessage = "DHCP on ${itf}: " . $Lang::tr{'cannot specify secondary wins without specifying primary'};
goto ERROR;
- }
+ }
}
if ($dhcpsettings{"NEXT_${itf}"}) {
if (!(&General::validip($dhcpsettings{"NEXT_${itf}"}))) {
} # enabled
}#loop interface verify
- map (delete ($dhcpsettings{$_}) ,@nosaved,'ACTION','KEY1','KEY2'); # Must not be saved
+ map (delete ($dhcpsettings{$_}) ,@nosaved,'ACTION','KEY1','KEY2','q'); # Must not be saved
&General::writehash($setting, \%dhcpsettings); # Save good settings
$dhcpsettings{'ACTION'} = $Lang::tr{'save'}; # create an 'ACTION'
map ($dhcpsettings{$_} = '',@nosaved,'KEY1','KEY2'); # and reinit vars to empty
$newsort.=$Rev;
}
$dhcpsettings{'SORT_FLEASELIST'}=$newsort;
- map (delete ($dhcpsettings{$_}) ,@nosaved,'ACTION','KEY1','KEY2'); # Must never be saved
+ map (delete ($dhcpsettings{$_}) ,@nosaved,'ACTION','KEY1','KEY2', 'q'); # Must never be saved
&General::writehash($setting, \%dhcpsettings);
&sortcurrent2;
$dhcpsettings{'ACTION'} = 'SORT'; # create an 'ACTION'
- map ($dhcpsettings{$_} = '',@nosaved,'KEY1','KEY2');# and reinit vars to empty
+ map ($dhcpsettings{$_} = '',@nosaved,'KEY1','KEY2');# and reinit vars to empty
}
#Sorting of allocated leases
&Header::CheckSortOrder;
-## Now manipulate the two multi-line list with Settings2.
+## Now manipulate the two multi-line list with Settings2.
# '1' suffix is for ADVANCED OPTIONS
# '2' suffix is for FIXED LEASES
open(FILE, ">$filename1") or die 'Unable to open dhcp advanced options file.';
print FILE @current1;
close(FILE);
-
+
#Write changes to dhcpd.conf.
&buildconf;
}
-
+
if ($dhcpsettings{'ACTION'} eq $Lang::tr{'add'}.'1' &&
$dhcpsettings{'SUBMIT'} ne $Lang::tr{'dhcp advopt help'}) {
if ($dhcpsettings{'ADVOPT_DATA'} eq '') {
$errormessage=$Lang::tr{'dhcp advopt blank value'};
}
-
+
# Test for a new option definition string (join field name & data)
if (ExistNewOptionDefinition ($dhcpsettings{'ADVOPT_NAME'} . ' ' . $dhcpsettings{'ADVOPT_DATA'})) {
#only edit permitted if option definition exists
map ($dhcpsettings{"ADVOPT_SCOPE_$_"} = 'off', @ITFs); # force global
} elsif (ValidNewOption ($dhcpsettings{'ADVOPT_NAME'} . ' ' . $dhcpsettings{'ADVOPT_DATA'})) {
#was a new option
- } elsif (! `grep "\$option $dhcpsettings{'ADVOPT_NAME'} " $filename3`) {
+ } elsif (! grep(/option $dhcpsettings{'ADVOPT_NAME'}/, @advoptions_list)) {
$errormessage=$Lang::tr{'dhcp advopt unknown'}.': '.$dhcpsettings{'ADVOPT_NAME'};
}
unless ($errormessage) {
-
+
my $scope = '';
foreach my $itf (@ITFs) { # buils "RED,GREEN,ORANGE,... based on selection
$scope .= $dhcpsettings{"ADVOPT_SCOPE_${itf}"} eq 'on' ? "\t$itf" : "\toff" ;
if ($itf ne 'off') # Only is an interface name is read
{
$dhcpsettings{"ADVOPT_SCOPE_${itf}"} = 'on';
- }
+ }
}
}
open(FILE, ">$filename2") or die 'Unable to open fixed leases file.';
print FILE @current2;
close(FILE);
-
+
#Write changes to dhcpd.conf.
&buildconf;
}
if ($dhcpsettings{'FIX_NEXTADDR'}) {
unless(&General::validip($dhcpsettings{'FIX_NEXTADDR'})) { $errormessage = $Lang::tr{'invalid fixed ip address'}; }
}
-
+
my $key = 0;
CHECK:foreach my $line (@current2) {
my @temp = split(/\,/,$line);
# Also it may be needed to put duplicate fixed lease in their right subnet definition..
foreach my $itf (@ITFs) {
my $scoped = &General::IpInSubnet($dhcpsettings{'FIX_ADDR'},
- $netsettings{"${itf}_NETADDRESS"},
+ $netsettings{"${itf}_NETADDRESS"},
$netsettings{"${itf}_NETMASK"}) &&
$dhcpsettings{"ENABLE_${itf}"} eq 'on';
if ( $scoped &&
(lc($dhcpsettings{'FIX_MAC'}) eq lc($temp[0])) &&
&General::IpInSubnet($temp[1],
- $netsettings{"${itf}_NETADDRESS"},
+ $netsettings{"${itf}_NETADDRESS"},
$netsettings{"${itf}_NETMASK"})) {
$errormessage = "$Lang::tr{'mac address in use'} $dhcpsettings{'FIX_MAC'}";
last CHECK;
$dhcpsettings{'FIX_ROOTPATH'} = &Header::cleanhtml($dhcpsettings{'FIX_ROOTPATH'});
if ($dhcpsettings{'KEY2'} eq '') { #add or edit ?
unshift (@current2, "$dhcpsettings{'FIX_MAC'},$dhcpsettings{'FIX_ADDR'},$dhcpsettings{'FIX_ENABLED'},$dhcpsettings{'FIX_NEXTADDR'},$dhcpsettings{'FIX_FILENAME'},$dhcpsettings{'FIX_ROOTPATH'},$dhcpsettings{'FIX_REMARK'}\n");
+ open(FILE, ">$filename2") or die 'Unable to open fixed lease file.';
+ print FILE @current2;
+ close(FILE);
&General::log($Lang::tr{'fixed ip lease added'});
+
+ # Enter edit mode
+ $dhcpsettings{'KEY2'} = 0;
} else {
@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";
$dhcpsettings{'KEY2'} = ''; # End edit mode
&General::log($Lang::tr{'fixed ip lease modified'});
+
+ # sort newly added/modified entry
+ &sortcurrent2;
}
#Write changes to dhcpd.conf.
- &sortcurrent2; # sort newly added/modified entry
&buildconf; # before calling buildconf which use fixed lease file !
}
}
}
}
$dhcpsettings{'FIX_ENABLED'} = 'on';
-}
+ $dhcpsettings{'ADVOPT_ENABLED'} = 'on';
+ }
-&Header::openpage($Lang::tr{'dhcp configuration'}, 1, '');
+### START PAGE ###
+&Header::openpage($Lang::tr{'dhcp configuration'}, 1, $Header::extraHead);
&Header::openbigbox('100%', 'left', '', $errormessage);
if ($errormessage) {
my %checked=();
$checked{'ENABLE'}{'on'} = ( $dhcpsettings{"ENABLE_${itf}"} ne 'on') ? '' : "checked='checked'";
$checked{'ENABLEBOOTP'}{'on'} = ( $dhcpsettings{"ENABLEBOOTP_${itf}"} ne 'on') ? '' : "checked='checked'";
+ $checked{'DENY_KNOWN_CLIENTS'}{'on'} = ( $dhcpsettings{"DENY_KNOWN_CLIENTS_${itf}"} ne 'on') ? '' : "checked='checked'";
if ($netsettings{"${itf}_DEV"} ne '' ) { # Show only defined interface
my $lc_itf=lc($itf);
<input type='checkbox' name='ENABLE_${itf}' $checked{'ENABLE'}{'on'} /></td>
<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>
</tr><tr>
- <td width='25%' class='base'>$Lang::tr{'start address'}</td>
+ <td width='25%' class='base'>$Lang::tr{'start address'} <img src='/blob.gif' alt='*' /></td>
<td width='25%'><input type='text' name='START_ADDR_${itf}' value='$dhcpsettings{"START_ADDR_${itf}"}' /></td>
- <td width='25%' class='base'>$Lang::tr{'end address'}</td>
+ <td width='25%' class='base'>$Lang::tr{'end address'} <img src='/blob.gif' alt='*' /></td>
<td width='25%'><input type='text' name='END_ADDR_${itf}' value='$dhcpsettings{"END_ADDR_${itf}"}' /></td>
</tr><tr>
- <td class='base'>$Lang::tr{'default lease time'}</td>
+ <td class='base'>$Lang::tr{'dhcp deny known clients:'}</td>
+ <td><input type='checkbox' name='DENY_KNOWN_CLIENTS_${itf}' $checked{'DENY_KNOWN_CLIENTS'}{'on'} /></td>
+</tr><tr>
+ <td class='base'>$Lang::tr{'default lease time'} <img src='/blob.gif' alt='*' /></td>
<td><input type='text' name='DEFAULT_LEASE_TIME_${itf}' value='$dhcpsettings{"DEFAULT_LEASE_TIME_${itf}"}' /></td>
- <td class='base'>$Lang::tr{'max lease time'}</td>
+ <td class='base'>$Lang::tr{'max lease time'} <img src='/blob.gif' alt='*' /></td>
<td><input type='text' name='MAX_LEASE_TIME_${itf}' value='$dhcpsettings{"MAX_LEASE_TIME_${itf}"}' /></td>
</tr><tr>
- <td class='base'>$Lang::tr{'domain name suffix'} <img src='/blob.gif' alt='*' /></td>
+ <td class='base'>$Lang::tr{'domain name suffix'}</td>
<td><input type='text' name='DOMAIN_NAME_${itf}' value='$dhcpsettings{"DOMAIN_NAME_${itf}"}' /></td>
<td>$Lang::tr{'dhcp allow bootp'}:</td>
<td><input type='checkbox' name='ENABLEBOOTP_${itf}' $checked{'ENABLEBOOTP'}{'on'} /></td>
</tr><tr>
- <td class='base'>$Lang::tr{'primary dns'}</td>
+ <td class='base'>$Lang::tr{'primary dns'} <img src='/blob.gif' alt='*' /></td>
<td><input type='text' name='DNS1_${itf}' value='$dhcpsettings{"DNS1_${itf}"}' /></td>
- <td class='base'>$Lang::tr{'secondary dns'} <img src='/blob.gif' alt='*' /></td>
+ <td class='base'>$Lang::tr{'secondary dns'}</td>
<td><input type='text' name='DNS2_${itf}' value='$dhcpsettings{"DNS2_${itf}"}' /></td>
</tr><tr>
- <td class='base'>$Lang::tr{'primary ntp server'}: <img src='/blob.gif' alt='*' /></td>
+ <td class='base'>$Lang::tr{'primary ntp server'}:</td>
<td><input type='text' name='NTP1_${itf}' value='$dhcpsettings{"NTP1_${itf}"}' /></td>
- <td class='base'>$Lang::tr{'secondary ntp server'}: <img src='/blob.gif' alt='*' /></td>
+ <td class='base'>$Lang::tr{'secondary ntp server'}:</td>
<td><input type='text' name='NTP2_${itf}' value='$dhcpsettings{"NTP2_${itf}"}' /></td>
</tr><tr>
- <td class='base'>$Lang::tr{'primary wins server address'}: <img src='/blob.gif' alt='*' /></td>
+ <td class='base'>$Lang::tr{'primary wins server address'}:</td>
<td><input type='text' name='WINS1_${itf}' value='$dhcpsettings{"WINS1_${itf}"}' /></td>
- <td class='base'>$Lang::tr{'secondary wins server address'}: <img src='/blob.gif' alt='*' /></td>
+ <td class='base'>$Lang::tr{'secondary wins server address'}:</td>
<td><input type='text' name='WINS2_${itf}' value='$dhcpsettings{"WINS2_${itf}"}' /></td>
</tr><tr>
- <td class='base'>next-server: <img src='/blob.gif' alt='*' /></td>
+ <td class='base'>next-server:</td>
<td><input type='text' name='NEXT_${itf}' value='$dhcpsettings{"NEXT_${itf}"}' /></td>
- <td class='base'>filename: <img src='/blob.gif' alt='*' /></td>
+ <td class='base'>filename:</td>
<td><input type='text' name='FILE_${itf}' value='$dhcpsettings{"FILE_${itf}"}' /></td>
</tr>
</table>
print <<END
<table width='100%'>
<tr>
- <td class='base' width='25%'><img src='/blob.gif' align='top' alt='*' /> $Lang::tr{'this field may be blank'}</td>
+ <td class='base' width='25%'><img src='/blob.gif' align='top' alt='*' /> $Lang::tr{'required field'}</td>
<td class='base' width='30%'>$warnNTPmessage</td>
<td width='40%' align='right'><input type='submit' name='ACTION' value='$Lang::tr{'save'}' /></td>
</tr>
</table>
+END
+;
+&Header::closebox();
+
+# DHCP DNS update support (RFC2136)
+&Header::openbox('100%', 'left', $Lang::tr{'dhcp dns update'});
+
+my %checked = ();
+$checked{'DNS_UPDATE_ENABLED'}{'on'} = ( $dhcpsettings{'DNS_UPDATE_ENABLED'} ne 'on') ? '' : "checked='checked'";
+
+print <<END
+<table width='100%'>
+ <tr>
+ <td width='30%' class='boldbase'>$Lang::tr{'dhcp dns enable update'}</td>
+ <td class='base'><input type='checkbox' name='DNS_UPDATE_ENABLED' $checked{'DNS_UPDATE_ENABLED'}{'on'}>
+ </td>
+ <tr>
+</table>
+
+<table width='100%'>
+END
+;
+ my @domains = ();
+
+ # Print options for each interface.
+ foreach my $itf (@ITFs) {
+ # Check if DHCP for this interface is enabled.
+ if ($dhcpsettings{"ENABLE_${itf}"} eq 'on') {
+ # Check for same domain name.
+ next if ($dhcpsettings{"DOMAIN_NAME_${itf}"} ~~ @domains);
+ my $lc_itf = lc($itf);
+
+ # Select previously configured update algorithm.
+ my %selected = ();
+ $selected{'DNS_UPDATE_ALGO_${inf}'}{$dhcpsettings{'DNS_UPDATE_ALGO_${inf}'}} = 'selected';
+
+print <<END
+ <tr>
+ <td colspan='6'> </td>
+ </tr>
+ <tr>
+ <td colspan='6' class='boldbase'><b>$dhcpsettings{"DOMAIN_NAME_${itf}"}</b></td>
+ </tr>
+ <tr>
+ <td width='10%' class='boldbase'>$Lang::tr{'dhcp dns key name'}:</td>
+ <td width='20%'><input type='text' name='DNS_UPDATE_KEY_NAME_${itf}' value='$dhcpsettings{"DNS_UPDATE_KEY_NAME_${itf}"}'></td>
+ <td width='10%' class='boldbase' align='right'>$Lang::tr{'dhcp dns update secret'}: </td>
+ <td width='20%'><input type='password' name='DNS_UPDATE_KEY_SECRET_${itf}' value='$dhcpsettings{"DNS_UPDATE_KEY_SECRET_${itf}"}'></td>
+ <td width='10%' class='boldbase' align='right'>$Lang::tr{'dhcp dns update algo'}: </td>
+ <td width='20%'>
+ <select name='DNS_UPDATE_KEY_ALGO_${itf}'>
+ <!-- <option value='hmac-sha1' $selected{'DNS_UPDATE_KEY_ALGO_${itf}'}{'hmac-sha1'}>HMAC-SHA1</option> -->
+ <option value='hmac-md5' $selected{'DNS_UPDATE_KEY_ALGO_${itf}'}{'hmac-md5'}>HMAC-MD5</option>
+ </select>
+ </td>
+ </tr>
+END
+;
+ }
+
+ # Store configured domain based on the interface
+ # in the temporary variable.
+ push(@domains, $dhcpsettings{"DOMAIN_NAME_${itf}"});
+}
+print <<END
+</table>
+<hr>
+<table width='100%'>
+ <tr>
+ <td align='right'><input type='submit' name='ACTION' value='$Lang::tr{'save'}' /></td>
+ </tr>
+</table>
</form>
END
;
}
#search if the 'option' is in the list and print the syntax model
-my $opt = `grep "\$option $dhcpsettings{'ADVOPT_NAME'} " $filename3`;
+my $opt;
+
+# Check if a advanced option name is set.
+if ($dhcpsettings{'ADVOPT_NAME'}) {
+ # Check if the name is part of the list and grab syntax.
+ my @opt = grep(/option $dhcpsettings{'ADVOPT_NAME'}/, @advoptions_list);
+
+ # Assign array element to variable.
+ $opt = @opt[0];
+
+ # Remove newlines.
+ chomp($opt);
+}
+
if ($opt ne '') {
$opt =~ s/option $dhcpsettings{'ADVOPT_NAME'}/Syntax:/; # "option xyz abc" => "syntax: abc"
$opt =~ s/;//;
}
print <<END
<tr>
- <td class='base'>$Lang::tr{'dhcp advopt name'}:</td>
+ <td class='base'>$Lang::tr{'dhcp advopt name'}: <img src='/blob.gif' alt='*' /></td>
<td><input type='text' name='ADVOPT_NAME' value='$dhcpsettings{'ADVOPT_NAME'}' size='18' /></td>
- <td class='base'>$Lang::tr{'dhcp advopt value'}:</td>
+ <td class='base'>$Lang::tr{'dhcp advopt value'}: <img src='/blob.gif' alt='*' /></td>
<td><input type='text' name='ADVOPT_DATA' value='$dhcpsettings{'ADVOPT_DATA'}' size='40' /></td>
</tr>$opt<tr>
<td class='base'>$Lang::tr{'enabled'}</td><td><input type='checkbox' name='ADVOPT_ENABLED' $checked{'ADVOPT_ENABLED'}{'on'} /></td>
- <td class='base'>$Lang::tr{'dhcp advopt scope'}: <img src='/blob.gif' alt='*' /></td>
+ <td class='base'>$Lang::tr{'dhcp advopt scope'}:</td>
<td>
END
;
-# Put a checkbox for each interface. Checkbox visible disabled if interface is disabled
+# Put a checkbox for each interface. Checkbox visible disabled if interface is disabled
foreach my $itf (@ITFs) {
my $lc_itf=lc($itf);
- $checked{'ADVOPT_SCOPE_${itf}'}{'on'} = $dhcpsettings{"ADVOPT_SCOPE_${itf}"} ne 'on' ? '' : "checked='checked'";
+ $checked{'ADVOPT_SCOPE_${itf}'}{'on'} = $dhcpsettings{"ADVOPT_SCOPE_${itf}"} ne 'on' ? '' : "checked='checked'";
print "$Lang::tr{\"${lc_itf}\"} <input type='checkbox' name='ADVOPT_SCOPE_${itf}' $checked{'ADVOPT_SCOPE_${itf}'}{'on'} ";
print $dhcpsettings{"ENABLE_${itf}"} eq 'on' ? "/>" : "disabled='disabled' />";
print " ";
<hr />
<table width='100%'>
<tr>
- <td class='base' width='50%'><img src='/blob.gif' align='top' alt='*' /> $Lang::tr{'dhcp advopt scope help'}</td>
+ <td class='base' width='50%'>$Lang::tr{'dhcp advopt scope help'}</td>
<td width='50%' align='right'>
<input type='hidden' name='ACTION' value='$Lang::tr{'add'}1' />
<input type='submit' name='SUBMIT' value='$buttontext' />
print "<tr><td colspan='2'><hr /></td></tr>\n";
print '<tr><td>string type</td><td>"quoted string" or 00:01:FF...</td></tr>';
print '<tr><td>ip-address type </td><td>10.0.0.1 | www.dot.com</td></tr>';
+ print '<tr><td>domain-list type </td><td>"example.com", "eng.example.com", "sales.example.com"</td></tr>';
print '<tr><td>int,uint types</td><td>numbers</td></tr>';
print '<tr><td>flag type</td><td>on | off</td></tr>';
print '</table>';
print '<tr><td>wpad</td><td>code 252=text</td></tr>';
print '<tr><td>wpad</td><td>"http://www.server.fr/path-to/proxy.pac"</td></tr>';
print '</table>';
-
+
}
print <<END
$gdesc = $Lang::tr{'click to disable'};
} else {
$gif = 'off.gif';
- $gdesc = $Lang::tr{'click to enable'};
+ $gdesc = $Lang::tr{'click to enable'};
}
if ($dhcpsettings{'KEY1'} eq $key) {
- print "<tr bgcolor='${Header::colouryellow}'>";
+ print "<tr class='colouryellow'>";
} elsif ($key % 2) {
- print "<tr bgcolor='$color{'color22'}'>";
+ print "<tr class='color22'>";
} else {
- print "<tr bgcolor='$color{'color20'}'>";
+ print "<tr class='color20'>";
}
print <<END
} else {
$global = $Lang::tr{'dhcp advopt scope global'};
}
-
-
+
+
# Print each checked interface
for (my $key=0; $key<@ITFs; $key++) {
my $itf = $temp[3+$key];
}
print <<END
<tr>
- <td class='base'>$Lang::tr{'mac address'}:</td>
+ <td class='base'>$Lang::tr{'mac address'}: <img src='/blob.gif' alt='*' /></td>
<td><input type='text' name='FIX_MAC' value='$dhcpsettings{'FIX_MAC'}' size='18' /></td>
- <td class='base'>$Lang::tr{'ip address'}:</td>
+ <td class='base'>$Lang::tr{'ip address'}: <img src='/blob.gif' alt='*' /></td>
<td><input type='text' name='FIX_ADDR' value='$dhcpsettings{'FIX_ADDR'}' size='18' /></td>
- <td class='base'>$Lang::tr{'remark'}: <img src='/blob.gif' alt='*' /></td>
+ <td class='base'>$Lang::tr{'remark'}:</td>
<td><input type='text' name='FIX_REMARK' value='$dhcpsettings{'FIX_REMARK'}' size='18' /></td>
</tr><tr>
<td class='base'>$Lang::tr{'enabled'}</td><td><input type='checkbox' name='FIX_ENABLED' $checked{'FIX_ENABLED'}{'on'} /></td>
</tr><tr>
<td colspan = '3'><b>$Lang::tr{'dhcp bootp pxe data'}</b></td>
</tr><tr>
- <td class='base'>next-server: <img src='/blob.gif' alt='*' /></td>
+ <td class='base'>next-server:</td>
<td><input type='text' name='FIX_NEXTADDR' value='$dhcpsettings{'FIX_NEXTADDR'}' size='18' /></td>
- <td class='base'>filename: <img src='/blob.gif' alt='*' /></td>
+ <td class='base'>filename:</td>
<td><input type='text' name='FIX_FILENAME' value='$dhcpsettings{'FIX_FILENAME'}' size='18' /></td>
- <td class='base'>root path: <img src='/blob.gif' alt='*' /></td>
+ <td class='base'>root path:</td>
<td><input type='text' name='FIX_ROOTPATH' value='$dhcpsettings{'FIX_ROOTPATH'}' size='18' /></td>
</tr>
</table>
<hr />
<table width='100%'>
<tr>
- <td class='base' width='50%'><img src='/blob.gif' align='top' alt='*' /> $Lang::tr{'this field may be blank'}</td>
+ <td class='base' width='50%'><img src='/blob.gif' align='top' alt='*' /> $Lang::tr{'required field'}</td>
<td width='50%' align='right'>
<input type='hidden' name='ACTION' value='$Lang::tr{'add'}2' />
<input type='submit' name='SUBMIT' value='$buttontext' />
</tr>
</table>
</form>
-<hr />
END
;
#Edited line number (KEY2) passed until cleared by 'save' or 'remove' or 'new sort order'
my %ipinuse = ();
my %macdupl = (); # Duplicate MACs have to be on different subnets
my %ipoutside = ();
+my %ipinrange = ();
-# mark duplicate ip or duplicate MAC
+# mark duplicate IP, duplicate MAC or IP in dynamic range
foreach my $line (@current2) {
my @temp = split(/\,/,$line);
$macdupl{$temp[0]} += 1;
- if ($macdupl{$temp[0]} > 1) {
+ if ($macdupl{$temp[0]} > 1) {
$ipdup = 1; # Flag up duplicates for use later
}
$ipinuse{$temp[1]} += 1;
- if ($ipinuse{$temp[1]} > 1) {
+ if ($ipinuse{$temp[1]} > 1) {
$ipdup = 1; # Flag up duplicates for use later
}
- # Mark IP addresses outwith known subnets
$ipoutside{$temp[1]} = 1;
+ $ipinrange{$temp[1]} = 0;
foreach my $itf (@ITFs) {
- if ( &General::IpInSubnet($temp[1],
- $netsettings{"${itf}_NETADDRESS"},
- $netsettings{"${itf}_NETMASK"})) {
- $ipoutside{$temp[1]} = 0;
- }
+ # Mark IP addresses outwith known subnets
+ if ( &General::IpInSubnet($temp[1],
+ $netsettings{"${itf}_NETADDRESS"},
+ $netsettings{"${itf}_NETMASK"})) {
+ $ipoutside{$temp[1]} = 0;
+ }
+ # Mark IP addresses that overlap with dynamic range
+ if (&Network::ip_address_in_range($temp[1],
+ $dhcpsettings{"START_ADDR_${itf}"},
+ $dhcpsettings{"END_ADDR_${itf}"})) {
+ $ipinrange{$temp[1]} = 1;
+ }
}
}
$gdesc = $Lang::tr{'click to disable'};
} else {
$gif = 'off.gif';
- $gdesc = $Lang::tr{'click to enable'};
+ $gdesc = $Lang::tr{'click to enable'};
}
# Skip all entries that do not match the search query
if ($search_query ne "") {
- next if (!grep(/$search_query/, @temp));
+ if (!grep(/$search_query/, @temp)) {
+ $key++;
+ next;
+ }
}
if ($dhcpsettings{'KEY2'} eq $key) {
print "<tr>";
- $col="bgcolor='${Header::colouryellow}'";
+ $col="class='colouryellow'";
} elsif ($key % 2) {
print "<tr>";
- $col="bgcolor='$color{'color20'}'";
+ $col="class='color20'";
} else {
print "<tr>";
- $col="bgcolor='$color{'color22'}'";
+ $col="class='color22'";
}
my $TAG0 = '';
my $TAG1 = '';
my $TAG2 = '';
my $TAG3 = '';
my $TAG4 = '';
- if ($ipinuse{$temp[1]} > 1) {
+ if ($ipinuse{$temp[1]} > 1) {
$TAG0 = '<b>';
$TAG1 = '</b>';
}
- if ($macdupl{$temp[0]} > 1) {
+ if ($macdupl{$temp[0]} > 1) {
$TAG2 = '<b>';
$TAG3 = '</b>';
}
- if ($ipoutside{$temp[1]} > 0) {
- $TAG4 = "bgcolor='orange'" if ($dhcpsettings{'KEY2'} ne $key);
+ if ($ipoutside{$temp[1]} > 0) {
+ $TAG4 = "class='orange'" if ($dhcpsettings{'KEY2'} ne $key);
+ }
+ if ($ipinrange{$temp[1]} > 0) {
+ $TAG4 = "class='red'" if ($dhcpsettings{'KEY2'} ne $key);
}
print <<END
<td align='center' $col>$TAG2$temp[0]$TAG3</td>
-<td align='center' $col $TAG4>$TAG0$temp[1]$TAG1</td>
+<td align='center' $TAG4 $col>$TAG0$temp[1]$TAG1</td>
<td align='center' $col>$temp[6] </td>
<td align='center' $col>$temp[3] </td>
<td align='center' $col>$temp[4] </td>
<td class='boldbase'> <b>$Lang::tr{'legend'}: </b></td>
<td><img src='/images/on.gif' alt='$Lang::tr{'click to disable'}' /></td>
<td class='base'>$Lang::tr{'click to disable'}</td>
- <td> </td>
<td><img src='/images/off.gif' alt='$Lang::tr{'click to enable'}' /></td>
<td class='base'>$Lang::tr{'click to enable'}</td>
<td> </td>
</tr>
<tr>
<td> </td>
- <td bgcolor='orange'> </td>
- <td class='base'>$Lang::tr{'ip address outside subnets'}</td>
+ <td class='base orange'> </td>
+ <td class='base'> $Lang::tr{'ip address outside subnets'} </td>
+ <td class='base red'> </td>
+ <td class='base'> $Lang::tr{'dhcp fixed ip address in dynamic range'}</td>
<td> </td>
<td> </td>
$dup
# use combination of ether & IP as key to allow duplicates in either but not both
$entries{$record->{FETHER} . $record->{FIPADDR}} = $record; # add this to a hash of hashes
}
-
+
open(FILE, ">$filename2") or die 'Unable to open fixed lease file.';
foreach my $entry ( sort fixedleasesort keys %entries) {
print FILE "$entries{$entry}->{FETHER},$entries{$entry}->{FIPADDR},$entries{$entry}->{DATA}\n";
close (FILE);
undef (%entries); #This array is reused latter. Clear it.
}
-
+
# Build the configuration file mixing settings, fixed leases and advanced options
sub buildconf {
open(FILE, ">/${General::swroot}/dhcp/dhcpd.conf") or die "Unable to write dhcpd.conf file";
flock(FILE, 2);
# Global settings
- print FILE "ddns-update-style none;\n";
print FILE "deny bootp; #default\n";
print FILE "authoritative;\n";
-
+
+ # DNS Update settings
+ if ($dhcpsettings{'DNS_UPDATE_ENABLED'} eq 'on') {
+ print FILE "ddns-updates on;\n";
+ print FILE "ddns-update-style interim;\n";
+ print FILE "ddns-ttl 60; # 1 min\n";
+ print FILE "ignore client-updates;\n";
+ print FILE "update-static-leases on;\n";
+ } else {
+ print FILE "ddns-update-style none;\n";
+ }
+
# Write first new option definition
foreach my $line (@current1) {
chomp($line); # remove newline
foreach my $line (@current1) {
chomp($line); # remove newline
my @temp = split(/\t/,$line);
-
+
if ($temp[0] eq 'on' && !ExistNewOptionDefinition ($temp[1] . ' ' . $temp[2])){ # active & !definition
my $global=1;
for (my $key=0; $key<@ITFs; $key++) {
if ($global) {
print FILE "option $temp[1] $temp[2];\n";
}
- }# on
+ }# on
}# foreach line
+ print FILE "\n";
#Subnet range definition
foreach my $itf (@ITFs) {
my $lc_itf=lc($itf);
if ($dhcpsettings{"ENABLE_${itf}"} eq 'on' ){
- print FILE "\nsubnet " . $netsettings{"${itf}_NETADDRESS"} . " netmask ". $netsettings{"${itf}_NETMASK"} . " #$itf\n";
+ print FILE "subnet " . $netsettings{"${itf}_NETADDRESS"} . " netmask ". $netsettings{"${itf}_NETMASK"} . " #$itf\n";
print FILE "{\n";
- print FILE "\trange " . $dhcpsettings{"START_ADDR_${itf}"} . ' ' . $dhcpsettings{"END_ADDR_${itf}"}.";\n" if ($dhcpsettings{"START_ADDR_${itf}"});
+ if ($dhcpsettings{"START_ADDR_${itf}"}) {
+ print FILE "pool {\n";
+ print FILE "\trange " . $dhcpsettings{"START_ADDR_${itf}"} . ' ' . $dhcpsettings{"END_ADDR_${itf}"}.";\n";
+ print FILE "\tdeny known-clients;\n" if ($dhcpsettings{"DENY_KNOWN_CLIENTS_${itf}"} eq 'on');
+ print FILE " }\n"; # pool
+ }
print FILE "\toption subnet-mask " . $netsettings{"${itf}_NETMASK"} . ";\n";
print FILE "\toption domain-name \"" . $dhcpsettings{"DOMAIN_NAME_${itf}"} . "\";\n";
print FILE "\toption routers " . $netsettings{"${itf}_ADDRESS"} . ";\n";
print FILE ", " . $dhcpsettings{"WINS2_${itf}"} if ($dhcpsettings{"WINS2_${itf}"});
print FILE ";\n" if ($dhcpsettings{"WINS1_${itf}"});
print FILE "\tnext-server " . $dhcpsettings{"NEXT_${itf}"} . ";\n" if ($dhcpsettings{"NEXT_${itf}"});
- print FILE "\tfilename \"" . $dhcpsettings{"FILE_${itf}"} . "\";\n" if ($dhcpsettings{"FILE_${itf}"});
+ print FILE "\tfilename \"" . &EscapeFilename($dhcpsettings{"FILE_${itf}"}) . "\";\n" if ($dhcpsettings{"FILE_${itf}"});
print FILE "\tdefault-lease-time " . ($dhcpsettings{"DEFAULT_LEASE_TIME_${itf}"} * 60). ";\n";
print FILE "\tmax-lease-time " . ($dhcpsettings{"MAX_LEASE_TIME_${itf}"} * 60) . ";\n";
print FILE "\tallow bootp;\n" if ($dhcpsettings{"ENABLEBOOTP_${itf}"} eq 'on');
foreach my $line (@current1) {
chomp($line); # remove newline
my @temp = split(/\t/,$line); # Use TAB separator !
-
+
if ($temp[0] eq 'on'){
for (my $key=0; $key<@ITFs; $key++) {
if ($itf eq $temp[3+$key]) # Only is an interface name is read
print FILE "\toption $temp[1] $temp[2];\n";
}
}
- }# on
+ }# on
}# foreach line
- print FILE "} #$itf\n";
+ print FILE "} #$itf\n\n";
+
+ if (($dhcpsettings{"DNS_UPDATE_ENABLED"} eq "on") && ($dhcpsettings{"DNS_UPDATE_KEY_NAME_${itf}"} ne "")) {
+ print FILE "key " . $dhcpsettings{"DNS_UPDATE_KEY_NAME_${itf}"} . " {\n";
+ print FILE "\talgorithm " . $dhcpsettings{"DNS_UPDATE_KEY_ALGO_${itf}"} . ";\n";
+ print FILE "\tsecret \"" . $dhcpsettings{"DNS_UPDATE_KEY_SECRET_${itf}"} . "\";\n";
+ print FILE "};\n\n";
+
+ print FILE "zone " . $dhcpsettings{"DOMAIN_NAME_${itf}"} . ". {\n";
+ print FILE "\tkey " . $dhcpsettings{"DNS_UPDATE_KEY_NAME_${itf}"} . ";\n";
+ print FILE "}\n\n";
+ }
- system ('/usr/bin/touch', "${General::swroot}/dhcp/enable_${lc_itf}");
+ &General::system('/usr/bin/touch', "${General::swroot}/dhcp/enable_${lc_itf}");
&General::log("DHCP on ${itf}: " . $Lang::tr{'dhcp server enabled'})
} else {
unlink "${General::swroot}/dhcp/enable_${lc_itf}";
print FILE "\thardware ethernet $temp[0];\n";
print FILE "\tfixed-address $temp[1];\n";
print FILE "\tnext-server $temp[3];\n" if ($temp[3]);
- print FILE "\tfilename \"$temp[4]\";\n" if ($temp[4]);
+ print FILE "\tfilename \"" . &EscapeFilename($temp[4]) . "\";\n" if ($temp[4]);
print FILE "\toption root-path \"$temp[5]\";\n" if ($temp[5]);
print FILE "}\n";
$key++;
}
print FILE "include \"${General::swroot}/dhcp/dhcpd.conf.local\";\n";
close FILE;
- if ( $dhcpsettings{"ENABLE_GREEN"} eq 'on' || $dhcpsettings{"ENABLE_BLUE"} eq 'on' ) {system '/usr/local/bin/dhcpctrl enable >/dev/null 2>&1';}
- else {system '/usr/local/bin/dhcpctrl disable >/dev/null 2>&1';}
- system '/usr/local/bin/dhcpctrl restart >/dev/null 2>&1';
+ if ( $dhcpsettings{"ENABLE_GREEN"} eq 'on' || $dhcpsettings{"ENABLE_BLUE"} eq 'on' ) {&General::system('/usr/local/bin/dhcpctrl', 'enable');}
+ else {&General::system('/usr/local/bin/dhcpctrl', 'disable');}
+ &General::system_background('/usr/local/bin/dhcpctrl', 'restart');
}
#
}
return 0;
}
+
+sub EscapeFilename($) {
+ my $filename = shift;
+
+ # Replace all single / by \/
+ $filename =~ s/\//\\\//g;
+
+ return $filename;
+}