Re-order rules support

This commit is contained in:
Joe Cooper
2026-02-02 18:07:42 -06:00
parent 163dd04175
commit 31d4b6dfd6
6 changed files with 141 additions and 7 deletions

BIN
images/icon.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@@ -87,21 +87,36 @@ if (!@tables) {
my @rules = grep { $_->{'chain'} eq $c } @{$curr->{'rules'}};
my $rules_html_row;
if (@rules) {
my @rows;
my $ri = 0;
$rules_html_row = "<table class='nftables_rules_table' width='100%'>\n";
foreach my $r (@rules) {
my $desc = &describe_rule($r);
push(@rows, &ui_link(
my $rule_link = &ui_link(
"edit_rule.cgi?table=$in{'table'}&chain=".
&urlize($c)."&idx=$r->{'index'}",
$desc));
$desc);
my $move = &ui_up_down_arrows(
"move_rule.cgi?table=$in{'table'}&chain=".
&urlize($c)."&idx=$r->{'index'}&dir=up",
"move_rule.cgi?table=$in{'table'}&chain=".
&urlize($c)."&idx=$r->{'index'}&dir=down",
$ri > 0,
$ri < $#rules);
$rules_html_row .= "<tr><td>$rule_link</td>".
"<td align='right' style='white-space:nowrap'>$move</td></tr>\n";
$ri++;
}
$rules_html_row = join("<br>", @rows);
$rules_html_row .= "<tr><td colspan='2'>".
&ui_link("edit_rule.cgi?table=$in{'table'}&chain=".
&urlize($c)."&new=1", $text{'index_radd'}).
"</td></tr>\n";
$rules_html_row .= "</table>";
} else {
$rules_html_row = "<i>$text{'index_rules_none'}</i>";
$rules_html_row .= "<br>".
&ui_link("edit_rule.cgi?table=$in{'table'}&chain=".
&urlize($c)."&new=1", $text{'index_radd'});
}
$rules_html_row .= "<br>".
&ui_link("edit_rule.cgi?table=$in{'table'}&chain=".
&urlize($c)."&new=1", $text{'index_radd'});
my $actions_html =
&ui_link("edit_chain.cgi?table=$in{'table'}&chain=".

View File

@@ -177,3 +177,8 @@ save_failed=Failed to save rule: <pre>$1</pre>
save_raw_empty=Raw rule cannot be empty.
save_raw_multiline=Raw rule must be a single line.
save_invalid_rule=Raw rule is invalid: $1
move_err=Failed to move rule
move_failed=Failed to move rule: <pre>$1</pre>
move_notable=No such table selected
move_nochain=No such chain selected
move_norule=No such rule selected

40
move_rule.cgi Executable file
View File

@@ -0,0 +1,40 @@
#!/usr/bin/perl
# move_rule.cgi
# Move a rule up or down within a chain
require './nftables-lib.pl';
use strict;
use warnings;
our (%in, %text);
&ReadParse();
&error_setup($text{'move_err'});
my @tables = &get_nftables_save();
my $table = $tables[$in{'table'}];
$table || &error($text{'move_notable'});
my $chain = $in{'chain'};
$chain || &error($text{'move_nochain'});
my $dir = $in{'dir'};
$dir = '' if (!defined($dir));
my $idx = $in{'idx'};
$idx =~ /^\d+$/ || &error($text{'move_norule'});
my $rv = &move_rule_in_chain($table, $chain, $idx, $dir);
if (!defined($rv)) {
&error($text{'move_norule'});
}
if ($rv) {
my $err = &save_configuration(@tables);
&error(&text('move_failed', $err)) if ($err);
&webmin_log("move", "rule", undef,
{ 'table' => $table->{'name'},
'family' => $table->{'family'},
'chain' => $chain,
'dir' => $dir });
}
&redirect("index.cgi?table=$in{'table'}");

View File

@@ -191,6 +191,56 @@ if (defined($type) || defined($hook) || defined($priority) || defined($policy))
return 1;
}
sub move_rule_in_chain
{
my ($table, $chain, $idx, $dir) = @_;
return undef if (!defined($table) || ref($table) ne 'HASH');
return undef if (!defined($idx) || $idx !~ /^\d+$/);
return undef if (!defined($chain) || $chain eq '');
return undef if (!$table->{'rules'} || ref($table->{'rules'}) ne 'ARRAY');
return undef if ($idx > $#{$table->{'rules'}});
my $rule = $table->{'rules'}->[$idx];
return undef if (!$rule || $rule->{'chain'} ne $chain);
my @chain_idxs;
for (my $i = 0; $i < @{$table->{'rules'}}; $i++) {
my $r = $table->{'rules'}->[$i];
next if (!$r || ref($r) ne 'HASH');
push(@chain_idxs, $i) if ($r->{'chain'} && $r->{'chain'} eq $chain);
}
my $pos;
for (my $i = 0; $i <= $#chain_idxs; $i++) {
if ($chain_idxs[$i] == $idx) {
$pos = $i;
last;
}
}
return undef if (!defined($pos));
my $swap;
if ($dir eq 'up') {
return 0 if ($pos == 0);
$swap = $chain_idxs[$pos-1];
}
elsif ($dir eq 'down') {
return 0 if ($pos == $#chain_idxs);
$swap = $chain_idxs[$pos+1];
}
else {
return undef;
}
($table->{'rules'}->[$idx], $table->{'rules'}->[$swap]) =
($table->{'rules'}->[$swap], $table->{'rules'}->[$idx]);
for (my $i = 0; $i < @{$table->{'rules'}}; $i++) {
my $r = $table->{'rules'}->[$i];
$r->{'index'} = $i if ($r && ref($r) eq 'HASH');
}
return 1;
}
sub format_addr_expr
{
my ($dir, $rule) = @_;

View File

@@ -135,4 +135,28 @@ ok(!&validate_chain_base('filter', 'input', undef, 'accept'),
ok(&validate_chain_base(undef, undef, undef, undef),
'chain base none set valid');
my $table_move = {
rules => [
{ chain => 'input', index => 0, text => 'r0' },
{ chain => 'input', index => 1, text => 'r1' },
{ chain => 'forward', index => 2, text => 'r2' },
{ chain => 'input', index => 3, text => 'r3' },
],
};
ok(&move_rule_in_chain($table_move, 'input', 1, 'down'),
'move rule down returns true');
is($table_move->{rules}->[1]->{text}, 'r3', 'rule moved down in array');
is($table_move->{rules}->[3]->{text}, 'r1', 'rule swapped down in array');
is($table_move->{rules}->[1]->{index}, 1, 'moved rule index updated');
is($table_move->{rules}->[3]->{index}, 3, 'swapped rule index updated');
my $table_move2 = {
rules => [
{ chain => 'input', index => 0, text => 'r0' },
{ chain => 'input', index => 1, text => 'r1' },
],
};
is(&move_rule_in_chain($table_move2, 'input', 0, 'up'), 0,
'top rule cannot move up');
done_testing();