2 커밋 fd16f3b33a ... 20622e0b90

작성자 SHA1 메시지 날짜
  root 20622e0b90 Added verification by protocol code, not by name. 1 개월 전
  root fcbda770cd Fixed the comparison of iptables filters. Now the order of the elements is ignored. 1 개월 전
1개의 변경된 파일56개의 추가작업 그리고 3개의 파일을 삭제
  1. 56 3
      scripts/sync_iptables.pl

+ 56 - 3
scripts/sync_iptables.pl

@@ -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;
+}