diff --git a/Shorewall/Perl/Shorewall/Accounting.pm b/Shorewall/Perl/Shorewall/Accounting.pm
index c5b56cd51..cfbf4df56 100644
--- a/Shorewall/Perl/Shorewall/Accounting.pm
+++ b/Shorewall/Perl/Shorewall/Accounting.pm
@@ -141,7 +141,8 @@ sub process_accounting_rule( ) {
$jumpchainref = 0;
- my ($action, $chain, $source, $dest, $proto, $ports, $sports, $user, $mark, $ipsec, $headers ) = split_line1 1, 11, 'Accounting File', $accounting_commands;
+ my ($action, $chain, $source, $dest, $proto, $ports, $sports, $user, $mark, $ipsec, $headers ) =
+ split_line1 1, 11, 'Accounting File', { action => 0, chain => 1, source => 2, dest => 3, proto => 4, dport => 5, sport => 6, user => 7, mark => 8, ipsec => 9, headers => 10 }, $accounting_commands;
if ( $action eq 'COMMENT' ) {
process_comment;
diff --git a/Shorewall/Perl/Shorewall/Config.pm b/Shorewall/Perl/Shorewall/Config.pm
index c3bfabf86..4609258f3 100644
--- a/Shorewall/Perl/Shorewall/Config.pm
+++ b/Shorewall/Perl/Shorewall/Config.pm
@@ -1338,13 +1338,15 @@ sub supplied( $ ) {
# ensure that it has an appropriate number of columns.
# supply '-' in omitted trailing columns.
#
-sub split_line( $$$ ) {
- my ( $mincolumns, $maxcolumns, $description ) = @_;
+sub split_line( $$$$ ) {
+ my ( $mincolumns, $maxcolumns, $description, $columnsref ) = @_;
- fatal_error "Shorewall Configuration file entries may not contain single quotes, double quotes, single back quotes or backslashes" if $currentline =~ /["'`\\]/;
- fatal_error "Non-ASCII gunk in file" if $currentline =~ /[^\s[:print:]]/;
+ my ( $columns, $pairs ) = split( ';', $currentline );
- my @line = split( ' ', $currentline );
+ fatal_error "Shorewall Configuration file entries may not contain single quotes, double quotes, single back quotes or backslashes" if $columns =~ /["'`\\]/;
+ fatal_error "Non-ASCII gunk in file" if $columns =~ /[^\s[:print:]]/;
+
+ my @line = split( ' ', $columns );
my $line = @line;
@@ -1352,31 +1354,44 @@ sub split_line( $$$ ) {
$line-- while $line > 0 && $line[$line-1] eq '-';
- fatal_error "Invalid $description entry (too few columns)" if $line < $mincolumns;
-
push @line, '-' while @line < $maxcolumns;
+ if ( supplied $pairs ) {
+ my @pairs = split( ' ', $pairs );
+
+ for ( @pairs ) {
+ fatal_error "Invalid column/value pair ($_)" unless /^(\w+)=(.*)$/;
+ my ( $column, $value ) = ( lc $1, $2 );
+ fatal_error "Unknown column ($1)" unless exists $columnsref->{$column};
+ $column = $columnsref->{$column};
+ fatal_error "Column $1 already has a value" unless $line[$column] eq '-';
+ $line[$column] = $value =~ /^"([^"]+)"$/ ? $1 : $value;
+ }
+ }
+
@line;
}
#
# Version of 'split_line' used on files with exceptions
#
-sub split_line1( $$$;$ ) {
- my ( $mincolumns, $maxcolumns, $description, $nopad) = @_;
+sub split_line1( $$$$;$ ) {
+ my ( $mincolumns, $maxcolumns, $description, $columnsref, $nopad) = @_;
- fatal_error "Shorewall Configuration file entries may not contain double quotes, single back quotes or backslashes" if $currentline =~ /["`\\]/;
- fatal_error "Non-ASCII gunk in file" if $currentline =~ /[^\s[:print:]]/;
+ my ( $columns, $pairs ) = split( ';', $currentline );
- my @line = split( ' ', $currentline );
+ fatal_error "Shorewall Configuration file entries may not contain double quotes, single back quotes or backslashes" if $columns =~ /["`\\]/;
+ fatal_error "Non-ASCII gunk in file" if $columns =~ /[^\s[:print:]]/;
+
+ my @line = split( ' ', $columns );
$nopad = { COMMENT => 0 } unless $nopad;
- my $first = $line[0];
- my $columns = $nopad->{$first};
+ my $first = $line[0];
+ my $npcolumns = $nopad->{$first};
- if ( defined $columns ) {
- fatal_error "Invalid $first entry" if $columns && @line != $columns;
+ if ( defined $npcolumns ) {
+ fatal_error "Invalid $first entry" if $npcolumns && @line != $npcolumns;
return @line
}
@@ -1392,6 +1407,19 @@ sub split_line1( $$$;$ ) {
push @line, '-' while @line < $maxcolumns;
+ if ( supplied $pairs ) {
+ my @pairs = split( ' ', $pairs );
+
+ for ( @pairs ) {
+ fatal_error "Invalid column/value pair ($_)" unless /^(\w+)=(.*)$/;
+ my ( $column, $value ) = ( lc $1, $2 );
+ fatal_error "Unknown column ($1)" unless exists $columnsref->{$column};
+ $column = $columnsref->{$column};
+ fatal_error "Column $1 already has a value" unless $line[$column] eq '-';
+ $line[$column] = $value =~ /^"([^"]+)"$/ ? $1 : $value;
+ }
+ }
+
@line;
}
diff --git a/Shorewall/Perl/Shorewall/Misc.pm b/Shorewall/Perl/Shorewall/Misc.pm
index 8129049f0..1ea1faf83 100644
--- a/Shorewall/Perl/Shorewall/Misc.pm
+++ b/Shorewall/Perl/Shorewall/Misc.pm
@@ -82,7 +82,7 @@ sub process_tos() {
while ( read_a_line ) {
- my ($src, $dst, $proto, $sports, $ports , $tos, $mark ) = split_line 6, 7, 'tos file entry';
+ my ($src, $dst, $proto, $ports, $sports , $tos, $mark ) = split_line 6, 7, 'tos file entry', { source => 0, dest => 1, proto => 2, dport => 3, sport => 4, tos => 5, mark => 6 } ;
$first_entry = 0;
@@ -159,7 +159,7 @@ sub setup_ecn()
while ( read_a_line ) {
- my ($interface, $hosts ) = split_line 1, 2, 'ecn file entry';
+ my ($interface, $hosts ) = split_line 1, 2, 'ecn file entry', { interface => 0, hosts => 1 };
fatal_error "Unknown interface ($interface)" unless known_interface $interface;
@@ -256,7 +256,7 @@ sub setup_blacklist() {
$first_entry = 0;
}
- my ( $networks, $protocol, $ports, $options ) = split_line 1, 4, 'blacklist file';
+ my ( $networks, $protocol, $ports, $options ) = split_line 1, 4, 'blacklist file', { networks => 0, proto => 1, port => 2, options => 3 };
if ( $options eq '-' ) {
$options = 'src';
@@ -358,7 +358,8 @@ sub process_routestopped() {
while ( read_a_line ) {
- my ($interface, $hosts, $options , $proto, $ports, $sports ) = split_line 1, 6, 'routestopped file';
+ my ($interface, $hosts, $options , $proto, $ports, $sports ) =
+ split_line 1, 6, 'routestopped file', { interface => 1, hosts => 2, options => 3, proto => 4, dport => 5, sport => 6 };
my $interfaceref;
@@ -897,7 +898,7 @@ sub setup_mac_lists( $ ) {
while ( read_a_line ) {
- my ( $original_disposition, $interface, $mac, $addresses ) = split_line1 3, 4, 'maclist file';
+ my ( $original_disposition, $interface, $mac, $addresses ) = split_line1 3, 4, 'maclist file', { origdisposition => 0, interface => 1, mac => 2, addresses => 3 };
if ( $original_disposition eq 'COMMENT' ) {
process_comment;
diff --git a/Shorewall/Perl/Shorewall/Nat.pm b/Shorewall/Perl/Shorewall/Nat.pm
index 8116e3039..830d0214e 100644
--- a/Shorewall/Perl/Shorewall/Nat.pm
+++ b/Shorewall/Perl/Shorewall/Nat.pm
@@ -54,7 +54,8 @@ sub initialize() {
#
sub process_one_masq( )
{
- my ($interfacelist, $networks, $addresses, $proto, $ports, $ipsec, $mark, $user ) = split_line1 2, 8, 'masq file';
+ my ($interfacelist, $networks, $addresses, $proto, $ports, $ipsec, $mark, $user ) =
+ split_line1 2, 8, 'masq file', { interface => 0, source => 1, address => 2, proto => 3, port => 4, ipsec => 5, mark => 6, user => 7 };
if ( $interfacelist eq 'COMMENT' ) {
process_comment;
@@ -374,7 +375,7 @@ sub setup_nat() {
while ( read_a_line ) {
- my ( $external, $interfacelist, $internal, $allints, $localnat ) = split_line1 3, 5, 'nat file';
+ my ( $external, $interfacelist, $internal, $allints, $localnat ) = split_line1 3, 5, 'nat file', { external => 1, interface => 1, internal => 2, allints => 3, localnat => 4 };
if ( $external eq 'COMMENT' ) {
process_comment;
@@ -407,7 +408,7 @@ sub setup_netmap() {
while ( read_a_line ) {
- my ( $type, $net1, $interfacelist, $net2, $net3, $proto, $dport, $sport ) = split_line 4, 8, 'netmap file';
+ my ( $type, $net1, $interfacelist, $net2, $net3, $proto, $dport, $sport ) = split_line 4, 8, 'netmap file', { type => 0, net1 => 1, interface => 2, net2 => 3, net3 => 4, proto => 4, dport => 5, sport => 6 };
$net3 = ALLIP if $net3 eq '-';
diff --git a/Shorewall/Perl/Shorewall/Providers.pm b/Shorewall/Perl/Shorewall/Providers.pm
index 155067cee..3f78a555a 100644
--- a/Shorewall/Perl/Shorewall/Providers.pm
+++ b/Shorewall/Perl/Shorewall/Providers.pm
@@ -267,7 +267,8 @@ sub start_provider( $$$ ) {
#
sub process_a_provider() {
- my ($table, $number, $mark, $duplicate, $interface, $gateway, $options, $copy ) = split_line 6, 8, 'providers file';
+ my ($table, $number, $mark, $duplicate, $interface, $gateway, $options, $copy ) =
+ split_line 6, 8, 'providers file', { table => 0, number => 1, mark => 2, duplicate => 3, interface => 4, gateway => 5, options => 6, copy => 7 };
fatal_error "Duplicate provider ($table)" if $providers{$table};
@@ -729,7 +730,7 @@ sub add_a_provider( $$ ) {
}
sub add_an_rtrule( ) {
- my ( $source, $dest, $provider, $priority ) = split_line 4, 4, 'route_rules file';
+ my ( $source, $dest, $provider, $priority ) = split_line 4, 4, 'route_rules file', { source => 0, dest => 1, provider => 2, priority => 3 };
our $current_if;
@@ -804,7 +805,7 @@ sub add_an_rtrule( ) {
}
sub add_a_route( ) {
- my ( $provider, $dest, $gateway, $device ) = split_line 2, 4, 'routes file';
+ my ( $provider, $dest, $gateway, $device ) = split_line 2, 4, 'routes file', { provider => 0, dest => 1, gateway => 2, device => 3 };
our $current_if;
diff --git a/Shorewall/Perl/Shorewall/Proxyarp.pm b/Shorewall/Perl/Shorewall/Proxyarp.pm
index 9792121b3..6d3357081 100644
--- a/Shorewall/Perl/Shorewall/Proxyarp.pm
+++ b/Shorewall/Perl/Shorewall/Proxyarp.pm
@@ -122,7 +122,8 @@ sub setup_proxy_arp() {
while ( read_a_line ) {
- my ( $address, $interface, $external, $haveroute, $persistent ) = split_line 3, 5, $file_opt;
+ my ( $address, $interface, $external, $haveroute, $persistent ) =
+ split_line 3, 5, { address => 0, interface => 1, external => 2, haveroute => 3, persistent => 4 }, $file_opt;
if ( $first_entry ) {
progress_message2 "$doing $fn...";
diff --git a/Shorewall/Perl/Shorewall/Raw.pm b/Shorewall/Perl/Shorewall/Raw.pm
index a6e4baaa3..12feb1902 100644
--- a/Shorewall/Perl/Shorewall/Raw.pm
+++ b/Shorewall/Perl/Shorewall/Raw.pm
@@ -84,7 +84,7 @@ sub setup_notrack() {
while ( read_a_line ) {
- my ( $source, $dest, $proto, $ports, $sports, $user ) = split_line1 1, 6, 'Notrack File';
+ my ( $source, $dest, $proto, $ports, $sports, $user ) = split_line1 1, 6, 'Notrack File', { source => 0, dest => 1, proto => 2, dport => 3, sport => 4, user => 5 };
if ( $source eq 'COMMENT' ) {
process_comment;
diff --git a/Shorewall/Perl/Shorewall/Rules.pm b/Shorewall/Perl/Shorewall/Rules.pm
index cc4a94b92..2a36e2aa8 100644
--- a/Shorewall/Perl/Shorewall/Rules.pm
+++ b/Shorewall/Perl/Shorewall/Rules.pm
@@ -77,6 +77,21 @@ my $rule_commands = { COMMENT => 0, FORMAT => 2, SECTION => 2 };
my $action_commands = { COMMENT => 0, FORMAT => 2, SECTION => 2, DEFAULTS => 2 };
my $macro_commands = { COMMENT => 0, FORMAT => 2, SECTION => 2, DEFAULT => 2 };
+my %rulecolumns = ( action => 0,
+ source => 1,
+ dest => 2,
+ proto => 3,
+ dport => 4,
+ sport => 5,
+ origdest => 6,
+ rate => 7,
+ user => 8,
+ mark => 9,
+ connlimit => 10,
+ time => 11,
+ headers => 12,
+ switch => 13 );
+
use constant { MAX_MACRO_NEST_LEVEL => 5 };
my $macro_nest_level;
@@ -297,7 +312,8 @@ sub process_a_policy() {
our %validpolicies;
our @zonelist;
- my ( $client, $server, $originalpolicy, $loglevel, $synparams, $connlimit ) = split_line 3, 6, 'policy file';
+ my ( $client, $server, $originalpolicy, $loglevel, $synparams, $connlimit ) =
+ split_line 3, 6, 'policy file', { source => 0, dest => 1, policy => 2, loglevel => 3, limit => 4, connlimit => 5 } ;
$loglevel = '' if $loglevel eq '-';
$synparams = '' if $synparams eq '-';
@@ -1354,7 +1370,7 @@ sub process_actions() {
open_file $file;
while ( read_a_line ) {
- my ( $action ) = split_line 1, 1, 'action file';
+ my ( $action ) = split_line 1, 1, 'action file' , { action => 0 };
if ( $action =~ /:/ ) {
warning_message 'Default Actions are now specified in /etc/shorewall/shorewall.conf';
@@ -1418,11 +1434,11 @@ sub process_action( $) {
my ($target, $source, $dest, $proto, $ports, $sports, $origdest, $rate, $user, $mark, $connlimit, $time, $headers, $condition );
if ( $format == 1 ) {
- ($target, $source, $dest, $proto, $ports, $sports, $rate, $user, $mark ) = split_line1 1, 9, 'action file', $rule_commands;
+ ($target, $source, $dest, $proto, $ports, $sports, $rate, $user, $mark ) = split_line1 1, 9, 'action file', $rule_commands, {};
$origdest = $connlimit = $time = $headers = $condition = '-';
} else {
($target, $source, $dest, $proto, $ports, $sports, $origdest, $rate, $user, $mark, $connlimit, $time, $headers, $condition )
- = split_line1 1, 14, 'action file', $action_commands;
+ = split_line1 1, 14, 'action file', \%rulecolumns, $action_commands;
}
if ( $target eq 'COMMENT' ) {
@@ -1508,10 +1524,10 @@ sub process_macro ( $$$$$$$$$$$$$$$$$$ ) {
my ( $mtarget, $msource, $mdest, $mproto, $mports, $msports, $morigdest, $mrate, $muser, $mmark, $mconnlimit, $mtime, $mheaders, $mcondition );
if ( $format == 1 ) {
- ( $mtarget, $msource, $mdest, $mproto, $mports, $msports, $mrate, $muser ) = split_line1 1, 8, 'macro file', $rule_commands;
+ ( $mtarget, $msource, $mdest, $mproto, $mports, $msports, $mrate, $muser ) = split_line1 1, 8, 'macro file', \%rulecolumns, $rule_commands;
( $morigdest, $mmark, $mconnlimit, $mtime, $mheaders, $mcondition ) = qw/- - - - - -/;
} else {
- ( $mtarget, $msource, $mdest, $mproto, $mports, $msports, $morigdest, $mrate, $muser, $mmark, $mconnlimit, $mtime, $mheaders, $mcondition ) = split_line1 1, 14, 'macro file', $rule_commands;
+ ( $mtarget, $msource, $mdest, $mproto, $mports, $msports, $morigdest, $mrate, $muser, $mmark, $mconnlimit, $mtime, $mheaders, $mcondition ) = split_line1 1, 14, 'macro file', \%rulecolumns, $rule_commands;
}
if ( $mtarget eq 'COMMENT' ) {
@@ -2322,7 +2338,7 @@ sub build_zone_list( $$$\$\$ ) {
#
sub process_rule ( ) {
my ( $target, $source, $dest, $protos, $ports, $sports, $origdest, $ratelimit, $user, $mark, $connlimit, $time, $headers, $condition )
- = split_line1 1, 14, 'rules file', $rule_commands;
+ = split_line1 1, 14, 'rules file', \%rulecolumns, $rule_commands;
process_comment, return 1 if $target eq 'COMMENT';
process_section( $source ), return 1 if $target eq 'SECTION';
diff --git a/Shorewall/Perl/Shorewall/Tc.pm b/Shorewall/Perl/Shorewall/Tc.pm
index 69ae11ba1..4b80299b7 100644
--- a/Shorewall/Perl/Shorewall/Tc.pm
+++ b/Shorewall/Perl/Shorewall/Tc.pm
@@ -191,7 +191,8 @@ sub initialize( $ ) {
}
sub process_tc_rule( ) {
- my ( $originalmark, $source, $dest, $proto, $ports, $sports, $user, $testval, $length, $tos , $connbytes, $helper, $headers ) = split_line1 2, 13, 'tcrules file';
+ my ( $originalmark, $source, $dest, $proto, $ports, $sports, $user, $testval, $length, $tos , $connbytes, $helper, $headers ) =
+ split_line1 2, 13, 'tcrules file', { mark => 0, source => 1, dest => 2, proto => 3, dport => 4, sport => 5, user => 6, test => 7, length => 8, tos => 9, connbytes => 10, helper => 11, headers => 12 };
our @tccmd;
@@ -510,7 +511,7 @@ sub process_flow($) {
}
sub process_simple_device() {
- my ( $device , $type , $in_bandwidth , $out_part ) = split_line 1, 4, 'tcinterfaces';
+ my ( $device , $type , $in_bandwidth , $out_part ) = split_line 1, 4, 'tcinterfaces', { device => 0, type => 1, in_bandwidth => 2, out_bandwidth => 3 };
fatal_error "Duplicate INTERFACE ($device)" if $tcdevices{$device};
fatal_error "Invalid INTERFACE name ($device)" if $device =~ /[:+]/;
@@ -644,7 +645,7 @@ sub process_simple_device() {
}
sub validate_tc_device( ) {
- my ( $device, $inband, $outband , $options , $redirected ) = split_line 3, 5, 'tcdevices';
+ my ( $device, $inband, $outband , $options , $redirected ) = split_line 3, 5, 'tcdevices', { device => 0, in_bandwidth => 1, out_bandwidth => 2, options => 3, redirect => 4 };
fatal_error "Invalid tcdevices entry" if $outband eq '-';
@@ -807,7 +808,8 @@ sub dev_by_number( $ ) {
}
sub validate_tc_class( ) {
- my ( $devclass, $mark, $rate, $ceil, $prio, $options ) = split_line 4, 6, 'tcclasses file';
+ my ( $devclass, $mark, $rate, $ceil, $prio, $options ) =
+ split_line 4, 6, 'tcclasses file', { device => 0, mark => 1, rate => 2, ceil => 3, prio => 4, options => 5 };
my $classnumber = 0;
my $devref;
my $device = $devclass;
@@ -1028,7 +1030,7 @@ my %validlengths = ( 32 => '0xffe0', 64 => '0xffc0', 128 => '0xff80', 256 => '0x
#
sub process_tc_filter() {
- my ( $devclass, $source, $dest , $proto, $portlist , $sportlist, $tos, $length ) = split_line 2, 8, 'tcfilters file';
+ my ( $devclass, $source, $dest , $proto, $portlist , $sportlist, $tos, $length ) = split_line 2, 8, 'tcfilters file', { device => 0, source => 1, dest => 2, proto => 3, dport => 4, sport => 5, tos => 6, length => 7 };
my ($device, $class, $rest ) = split /:/, $devclass, 3;
@@ -1328,7 +1330,7 @@ sub process_tcfilters() {
# Process a tcpri record
#
sub process_tc_priority() {
- my ( $band, $proto, $ports , $address, $interface, $helper ) = split_line1 1, 6, 'tcpri';
+ my ( $band, $proto, $ports , $address, $interface, $helper ) = split_line1 1, 6, 'tcpri', { band => 0, proto => 1, port => 2, address => 3, interface => 4, helper => 5 };
if ( $band eq 'COMMENT' ) {
process_comment;
@@ -1666,7 +1668,8 @@ sub setup_traffic_shaping() {
# Process a record in the secmarks file
#
sub process_secmark_rule() {
- my ( $secmark, $chainin, $source, $dest, $proto, $dport, $sport, $user, $mark ) = split_line1( 2, 9 , 'Secmarks file' );
+ my ( $secmark, $chainin, $source, $dest, $proto, $dport, $sport, $user, $mark ) =
+ split_line1( 2, 9 , 'Secmarks file' , { secmark => 0, chain => 1, source => 2, dest => 3, proto => 4, dport => 5, sport => 6, user => 7, mark => 8 } );
if ( $secmark eq 'COMMENT' ) {
process_comment;
diff --git a/Shorewall/Perl/Shorewall/Tunnels.pm b/Shorewall/Perl/Shorewall/Tunnels.pm
index e0f363bf0..79741a224 100644
--- a/Shorewall/Perl/Shorewall/Tunnels.pm
+++ b/Shorewall/Perl/Shorewall/Tunnels.pm
@@ -284,7 +284,7 @@ sub setup_tunnels() {
while ( read_a_line ) {
- my ( $kind, $zone, $gateway, $gatewayzones ) = split_line1 2, 4, 'tunnels file';
+ my ( $kind, $zone, $gateway, $gatewayzones ) = split_line1 2, 4, 'tunnels file', { kind => 0, zone => 1, gateway => 2, gateway_zone => 3 };
if ( $kind eq 'COMMENT' ) {
process_comment;
diff --git a/Shorewall/Perl/Shorewall/Zones.pm b/Shorewall/Perl/Shorewall/Zones.pm
index 720b5e26a..6d34d7571 100644
--- a/Shorewall/Perl/Shorewall/Zones.pm
+++ b/Shorewall/Perl/Shorewall/Zones.pm
@@ -402,7 +402,8 @@ sub process_zone( \$ ) {
my @parents;
- my ($zone, $type, $options, $in_options, $out_options ) = split_line 1, 5, 'zones file';
+ my ($zone, $type, $options, $in_options, $out_options ) =
+ split_line 1, 5, 'zones file', { zone => 0, type => 1, options => 2, in_options => 3, out_options => 4 };
if ( $zone =~ /(\w+):([\w,]+)/ ) {
$zone = $1;
@@ -871,7 +872,7 @@ sub process_interface( $$ ) {
my ( $nextinum, $export ) = @_;
my $netsref = '';
my $filterref = [];
- my ($zone, $originalinterface, $bcasts, $options ) = split_line 2, 4, 'interfaces file';
+ my ($zone, $originalinterface, $bcasts, $options ) = split_line 2, 4, 'interfaces file', { zone => 0, interface => 1, broadcast => 2, options => 3 };
my $zoneref;
my $bridge = '';
@@ -1727,7 +1728,7 @@ sub compile_updown() {
#
sub process_host( ) {
my $ipsec = 0;
- my ($zone, $hosts, $options ) = split_line 2, 3, 'hosts file';
+ my ($zone, $hosts, $options ) = split_line 2, 3, 'hosts file', { zone => 0, hosts => 1, options => 2 };
my $zoneref = $zones{$zone};
my $type = $zoneref->{type};
diff --git a/docs/configuration_file_basics.xml b/docs/configuration_file_basics.xml
index a524d9a31..d26f1f594 100644
--- a/docs/configuration_file_basics.xml
+++ b/docs/configuration_file_basics.xml
@@ -492,6 +492,269 @@ ACCEPT net:\
+
+ Alternate Specification of Column Values - Shorewall 4.4.24 and
+ Later
+
+ Some of the configuration files now have a large number of columns.
+ That makes it awkward to specify a value for one of the right-most columns
+ as you must have the correct number of intervening '-' columns.
+
+ This problem is addressed by allowing column values to be specified
+ as column-name/value
+ pairs.
+
+ Each file has both required and optional columns. The columns up to
+ the last required column must be specified in the normal way. After that,
+ at any point, you can enter a semicolon (':') followed by one or more
+ specifications of the form:
+
+
+ column-name=value
+
+
+ The value may optionally be enclosed in double quotes.
+
+ The following table shows the right-most required column and the
+ remaining column names for each of the table-oriented configuration
+ files.
+
+
+ Column names are case-insensitive.
+
+
+
+
+
+
+ File
+
+ Right-most required
+ Column
+
+ Remaining column
+ names
+
+
+
+ accounting
+
+ ACTION
+
+ chain, source, dest, proto, dport, sport, user, mark,
+ ipsec, headers
+
+
+
+ blacklist
+
+ ADDRESS/SUBNET
+
+ proto,port,options
+
+
+
+ ecn
+
+ INTERFACE
+
+ hosts
+
+
+
+ hosts
+
+ ZONE
+
+ hosts,options
+
+
+
+ interfaces
+
+ INTERFACE
+
+ broadcast,options
+
+
+
+ maclist
+
+ MAC
+
+ addresses
+
+
+
+ masq
+
+ SOURCE
+
+ address,proto,port,ipsec,mark,user
+
+
+
+ nat
+
+ EXTERNAL
+
+ interface,internal,allints,localnat
+
+
+
+ netmap
+
+ NET2
+
+ net3,proto,dport,sport
+
+
+
+ notrack
+
+ SOURCE
+
+ dest,proto,dport,sport,user
+
+
+
+ policy
+
+ POLICY
+
+ loglevel,limit,connlimit
+
+
+
+ providers
+
+ GATEWAY
+
+ options,copy
+
+
+
+ proxyarp
+
+ EXTERNAL
+
+ haveroute,persistent
+
+
+
+ route_rules
+
+ PRIORITY
+
+ N/A
+
+
+
+ routestopped
+
+ INTERFACE
+
+ hosts,options,proto,dport,sport
+
+
+
+ rules
+
+ ACTION
+
+ source,dest,proto,dport,sport,origdest,rate,user,mark,connlimit,time,headers,switch
+
+
+
+ secmarks
+
+ CHAIN
+
+ source,dest,proto,dport,sport,user,mark
+
+
+
+ tcclasses
+
+ CEIL
+
+ prio,options
+
+
+
+ tcdevices
+
+ OUT_BANDWIDTH
+
+ options,redirect
+
+
+
+ tcfilters
+
+ SOURCE
+
+ dest,proto,dport,sport,tos,length
+
+
+
+ tcinterfaces
+
+ DEVICE
+
+ type,in_bandwidth,out_bandwidth
+
+
+
+ tcpri
+
+ BAND
+
+ proto,port,address,interface,helper
+
+
+
+ tcrules
+
+ SOURCE
+
+ dest,proto,dport,sport,user,test,length,tos,connbytes,helper,headers
+
+
+
+ tos
+
+ TOS
+
+ mark
+
+
+
+ tunnels
+
+ ZONE
+
+ gateway,gateway_zone
+
+
+
+ zones
+
+ ZONE
+
+ type,options,in_options,out_options
+
+
+
+
+
+ Example (rules file):
+
+ #ACTION SOURCE DEST PROTO DEST
+# PORT(S)
+DNAT net loc:10.0.0.1 tcp 80 ; mark="88"
+
+