|
|
@@ -61,6 +61,11 @@ my $gate = get_record_sql($dbh, 'SELECT * FROM devices WHERE id=?', $router_id )
|
|
|
|
|
|
exit 100 if (!$gate);
|
|
|
|
|
|
+our %PROTO_NUMS;
|
|
|
+my $proto_loaded = 0;
|
|
|
+
|
|
|
+_load_protocols();
|
|
|
+
|
|
|
my $gate_ident = $gate->{device_name}." [$gate->{ip}]:: ";
|
|
|
|
|
|
my @cmd_list=();
|
|
|
@@ -531,14 +536,16 @@ if ($gate->{user_acl}) {
|
|
|
# Проверка текущих правил
|
|
|
for (my $f_index=0; $f_index<scalar(@cur_filter); $f_index++) {
|
|
|
my $filter_str = trim($cur_filter[$f_index]);
|
|
|
- if (!$chain_rules{$group_name}[$f_index] or $filter_str !~ /$chain_rules{$group_name}[$f_index]/i) {
|
|
|
+ my $expected = $chain_rules{$group_name}[$f_index];
|
|
|
+ my $rules_ok = compare_iptables_rules($expected, $filter_str);
|
|
|
+ if (!$rules_ok) {
|
|
|
log_error($gate_ident."Check chain $chain_name error! Rule mismatch at position $f_index");
|
|
|
- log_verbose($gate_ident."Expected: $chain_rules{$group_name}[$f_index]");
|
|
|
+ log_verbose($gate_ident."Expected: $expected");
|
|
|
log_verbose($gate_ident."Current: $filter_str");
|
|
|
$chain_ok = 0;
|
|
|
last;
|
|
|
+ }
|
|
|
}
|
|
|
- }
|
|
|
}
|
|
|
|
|
|
if (!$chain_ok) {
|
|
|
@@ -697,3 +704,49 @@ sub save_ipsets_to_files {
|
|
|
}
|
|
|
log_verbose($gate_ident."Saved $files_saved ipset configuration files");
|
|
|
}
|
|
|
+
|
|
|
+sub compare_iptables_rules {
|
|
|
+ my ($r1, $r2) = @_;
|
|
|
+ return 1 if $r1 eq $r2;
|
|
|
+ return 0 if !defined $r1;
|
|
|
+ return 0 if !defined $r2;
|
|
|
+
|
|
|
+ my $normalize = sub {
|
|
|
+ my ($rule) = @_;
|
|
|
+ my @parts;
|
|
|
+ # Режем строго перед флагами (-x или --xx). Аргументы остаются в том же блоке.
|
|
|
+ for my $block (grep { $_ } split(/\s+(?=-(?:-)?[a-z])/i, $rule)) {
|
|
|
+ $block =~ s/^\s+|\s+$//g;
|
|
|
+ $block = lc($block);
|
|
|
+ if ($block =~ /^(!?\s*-[\w-]+)\s*(.*)/) {
|
|
|
+ my ($flag, $val) = ($1, $2);
|
|
|
+ $flag =~ s/\s+//g; # ! -p -> !-p
|
|
|
+ $val =~ s/\s+/ /g; # схлопываем внутренние пробелы
|
|
|
+ # Обработка -p: имя -> число, если найдено в /etc/protocols
|
|
|
+ if ($flag eq '-p' || $flag eq '!-p') {
|
|
|
+ $val = $PROTO_NUMS{$val} if exists $PROTO_NUMS{$val};
|
|
|
+ }
|
|
|
+ push @parts, "$flag=$val";
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return sort @parts;
|
|
|
+ };
|
|
|
+
|
|
|
+ my @n1 = $normalize->($r1);
|
|
|
+ my @n2 = $normalize->($r2);
|
|
|
+
|
|
|
+ # Длина массивов + посимвольное равенство отсортированных списков
|
|
|
+ return @n1 == @n2 && join(' ', @n1) eq join(' ', @n2);
|
|
|
+}
|
|
|
+
|
|
|
+sub _load_protocols {
|
|
|
+ return if $proto_loaded;
|
|
|
+ open my $fh, '<', '/etc/protocols' or return;
|
|
|
+ while (<$fh>) {
|
|
|
+ chomp; s/#.*//; s/^\s+|\s+$//g; next unless length;
|
|
|
+ my @p = split(/\s+/); next if @p < 2;
|
|
|
+ # Мапим официальное имя и все алиасы (регистронезависимо) на число
|
|
|
+ $PROTO_NUMS{lc($_)} = $p[1] for @p;
|
|
|
+ }
|
|
|
+ close $fh; $proto_loaded = 1;
|
|
|
+}
|