|
|
@@ -32,6 +32,16 @@ use Parallel::ForkManager;
|
|
|
use Net::DNS;
|
|
|
use Getopt::Long;
|
|
|
|
|
|
+# Helper для парсинга вывода Mikrotik
|
|
|
+my $parse_mikrotik_line = sub {
|
|
|
+ my $line = shift;
|
|
|
+ return undef unless defined $line;
|
|
|
+ $line = trim($line);
|
|
|
+ return undef unless $line =~ /^\s*\d/;
|
|
|
+ $line =~ s/^\s*\d+\s+//;
|
|
|
+ return $line;
|
|
|
+};
|
|
|
+
|
|
|
#$debug = 1;
|
|
|
|
|
|
open(SELF,"<",$0) or die "Cannot open $0 - $!";
|
|
|
@@ -243,9 +253,9 @@ foreach my $int (@lan_int) { #interface loop
|
|
|
my @int_addr=netdev_cmd($gate,$t,'/ip address print terse without-paging where interface='.$int,1);
|
|
|
log_debug($gate_ident."Get interfaces: ".Dumper(\@int_addr));
|
|
|
my $found_subnet;
|
|
|
- foreach my $int_str(@int_addr) {
|
|
|
- $int_str=trim($int_str);
|
|
|
- next if (!$int_str);
|
|
|
+ foreach my $row (@int_addr) {
|
|
|
+ my $int_str = $parse_mikrotik_line->($row);
|
|
|
+ next unless $int_str;
|
|
|
if ($int_str=~/\s+address=(\S*)\s+/i) {
|
|
|
my $gate_interface=$1;
|
|
|
if ($gate_interface) {
|
|
|
@@ -841,7 +851,7 @@ my $chain_ok=1;
|
|
|
foreach (my $f_index=0; $f_index<scalar(@get_filter); $f_index++) {
|
|
|
my $filter_str=trim($get_filter[$f_index]);
|
|
|
next if (!$filter_str);
|
|
|
- next if ($filter_str!~/^(\d){1,3}/);
|
|
|
+ next if ($filter_str!~/^(\d{1,3})/);
|
|
|
$filter_str=~s/[^[:ascii:]]//g;
|
|
|
$filter_str=~s/^\d{1,3}\s+//;
|
|
|
$filter_str=trim($filter_str);
|
|
|
@@ -888,286 +898,389 @@ if (!$chain_ok) {
|
|
|
}
|
|
|
}#end access lists config
|
|
|
|
|
|
+SHAPER: {
|
|
|
if ($shaper_enabled) {
|
|
|
+ log_info($gate_ident . "Starting Shaper synchronization...");
|
|
|
|
|
|
-#get userid list
|
|
|
-my $user_auth_sql="SELECT ip, queue_id, id FROM user_auth WHERE user_auth.deleted =0 AND user_auth.ou_id <> ? ORDER BY ip_int";
|
|
|
-my @authlist_ref = get_records_sql($dbh,$user_auth_sql,$default_hotspot_ou_id);
|
|
|
+ # --- 1. Получение списка пользователей (Auth List) ---
|
|
|
+ my %users;
|
|
|
+ eval {
|
|
|
+ my $user_auth_sql = "SELECT ip, queue_id, id FROM user_auth WHERE user_auth.deleted = 0 AND user_auth.ou_id <> ? ORDER BY ip_int";
|
|
|
+ my @authlist_ref = get_records_sql($dbh, $user_auth_sql, $default_hotspot_ou_id);
|
|
|
+
|
|
|
+ foreach my $row (@authlist_ref) {
|
|
|
+ next if (!$row->{ip});
|
|
|
+ if ($connected_users_only) {
|
|
|
+ next if (!$connected_users->match_string($row->{ip}));
|
|
|
+ }
|
|
|
+ next if (!$office_networks->match_string($row->{ip}));
|
|
|
+
|
|
|
+ if ($row->{queue_id}) {
|
|
|
+ $users{'queue_' . $row->{queue_id}}->{$row->{ip}} = 1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ log_debug($gate_ident . "Loaded " . scalar(keys %users) . " user IP groups.");
|
|
|
+ };
|
|
|
+ if ($@) {
|
|
|
+ log_error($gate_ident . "Failed to fetch user auth list: $@");
|
|
|
+ last SHAPER;
|
|
|
+ }
|
|
|
|
|
|
-foreach my $row (@authlist_ref) {
|
|
|
-if ($connected_users_only) { next if (!$connected_users->match_string($row->{ip})); }
|
|
|
-#skip not office ip's
|
|
|
-next if (!$office_networks->match_string($row->{ip}));
|
|
|
-#queue acl's
|
|
|
-if ($row->{queue_id}) { $users{'queue_'.$row->{queue_id}}->{$row->{ip}}=1; }
|
|
|
-}
|
|
|
+ # --- 2. Получение списка очередей из БД (Desired State) ---
|
|
|
+ my %queues;
|
|
|
+ my %lists;
|
|
|
+ eval {
|
|
|
+ my @queuelist_ref = get_records_sql($dbh, "SELECT * FROM queue_list");
|
|
|
+ foreach my $row (@queuelist_ref) {
|
|
|
+ next if (!$row->{id});
|
|
|
+ $lists{'queue_' . $row->{id}} = 1;
|
|
|
+ next if ((!$row->{download}) and !($row->{upload}));
|
|
|
+
|
|
|
+ $queues{'queue_' . $row->{id}}{id} = $row->{id};
|
|
|
+ $queues{'queue_' . $row->{id}}{down} = $row->{download};
|
|
|
+ $queues{'queue_' . $row->{id}}{up} = $row->{upload};
|
|
|
+ }
|
|
|
+ log_debug($gate_ident . "Loaded " . scalar(keys %queues) . " queue definitions from DB.");
|
|
|
+ };
|
|
|
+ if ($@) {
|
|
|
+ log_error($gate_ident . "Failed to fetch queue list from DB: $@");
|
|
|
+ last SHAPER;
|
|
|
+ }
|
|
|
|
|
|
-#get queue list
|
|
|
-my @queuelist_ref = get_records_sql($dbh,"SELECT * FROM queue_list");
|
|
|
+ log_debug($gate_ident . "Queues desired status:" . Dumper(\%queues));
|
|
|
|
|
|
-my %queues;
|
|
|
-foreach my $row (@queuelist_ref) {
|
|
|
-$lists{'queue_'.$row->{id}}=1;
|
|
|
-next if ((!$row->{download}) and !($row->{upload}));
|
|
|
-$queues{'queue_'.$row->{id}}{id}=$row->{id};
|
|
|
-$queues{'queue_'.$row->{id}}{down}=$row->{download};
|
|
|
-$queues{'queue_'.$row->{id}}{up}=$row->{upload};
|
|
|
-}
|
|
|
+ # --- 3. Получение текущего состояния с роутера (Current State) ---
|
|
|
+ my (%get_queue_root, %get_queue_type, %get_queue_tree, %get_filter_mangle);
|
|
|
+
|
|
|
|
|
|
-log_debug($gate_ident."Queues status:".Dumper(\%queues));
|
|
|
-
|
|
|
-#shapers
|
|
|
-my %get_queue_root=();
|
|
|
-my %get_queue_type=();
|
|
|
-my %get_queue_tree=();
|
|
|
-my %get_filter_mangle=();
|
|
|
-
|
|
|
-my @tmp=netdev_cmd($gate,$t,'/queue tree print terse without-paging where name~"(down|up)load_root"',1);
|
|
|
-
|
|
|
-log_debug($gate_ident."Get ROOT queues classes: ".Dumper(\@tmp));
|
|
|
-#5 name=upload_root_sfp-sfpplus1-wan parent=sfp-sfpplus1-wan packet-mark= limit-at=0 queue=pcq-upload-default priority=8 max-limit=10G burst-limit=0 burst-threshold=0 burst-time=0s bucket-size=0.1
|
|
|
-#0 name=download_root_vlan0201 parent=vlan0201 packet-mark= limit-at=0 queue=pcq-download-default priority=8 max-limit=2G burst-limit=0 burst-threshold=0 burst-time=0s bucket-size=0.1
|
|
|
-
|
|
|
-foreach my $row (@tmp) {
|
|
|
-next if (!$row);
|
|
|
-$row = trim($row);
|
|
|
-next if ($row!~/^(\d){1,3}/);
|
|
|
-$row=~s/^\d{1,3}\s+//;
|
|
|
-next if (!$row);
|
|
|
-if ($row=~/name=(down|up)load_root_(\S*)\s+/i) {
|
|
|
- next if (!$2);
|
|
|
- my $parent_device = $2;
|
|
|
- $get_queue_root{$parent_device}{name} = $parent_device;
|
|
|
- if ($row=~/\s+max-limit=(\S*)\s+/) {
|
|
|
- $get_queue_root{$parent_device}{bandwidth} = bitrate_to_kbps($1);
|
|
|
- } else { $get_queue_root{$parent_device}{bandwidth} = 0; }
|
|
|
- }
|
|
|
-}
|
|
|
+ eval {
|
|
|
+ # 3.1 Root Queues
|
|
|
+ my @tmp = netdev_cmd($gate, $t, '/queue tree print terse without-paging where name~"(down|up)load_root"', 1);
|
|
|
+ foreach my $row (@tmp) {
|
|
|
+ my $clean = $parse_mikrotik_line->($row);
|
|
|
+ next unless $clean;
|
|
|
+ if ($clean =~ /name=(down|up)load_root_(\S*)\s+/i) {
|
|
|
+ my $parent_device = $2;
|
|
|
+ $get_queue_root{$parent_device}{name} = $parent_device;
|
|
|
+ if ($clean =~ /\s+max-limit=(\S*)\s+/) {
|
|
|
+ $get_queue_root{$parent_device}{bandwidth} = bitrate_to_kbps($1);
|
|
|
+ } else {
|
|
|
+ $get_queue_root{$parent_device}{bandwidth} = 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
-#log_debug($gate_ident.'GET ROOT:'.Dumper(\%get_queue_root));
|
|
|
-
|
|
|
-@tmp=();
|
|
|
-
|
|
|
-@tmp=netdev_cmd($gate,$t,'/queue type print terse without-paging where name~"pcq_(down|up)load"',1);
|
|
|
-
|
|
|
-log_debug($gate_ident."Get queues: ".Dumper(\@tmp));
|
|
|
-
|
|
|
-# 0 name=pcq_upload_3 kind=pcq pcq-rate=102401k pcq-limit=500KiB pcq-classifier=src-address pcq-total-limit=2000KiB pcq-burst-rate=0 pcq-burst-threshold=0 pcq-burst-time=10s
|
|
|
-#pcq-src-address-mask=32 pcq-dst-address-mask=32 pcq-src-address6-mask=64 pcq-dst-address6-mask=64
|
|
|
-foreach my $row (@tmp) {
|
|
|
-next if (!$row);
|
|
|
-$row = trim($row);
|
|
|
-next if ($row!~/^(\d){1,3}/);
|
|
|
-$row=~s/^\d{1,3}\s+//;
|
|
|
-next if (!$row);
|
|
|
-if ($row=~/name=pcq_(down|up)load_(\d){1,3}\s+/i) {
|
|
|
- next if (!$1);
|
|
|
- next if (!$2);
|
|
|
- my $direct = $1;
|
|
|
- my $index = $2;
|
|
|
- $get_queue_type{$index}{$direct}=$row;
|
|
|
- if ($row=~/pcq-rate=(\S*)\s+\S/i) {
|
|
|
- my $rate = $1;
|
|
|
- if ($rate=~/k$/i) { $rate =~s/k$//i; }
|
|
|
- $get_queue_type{$index}{$direct."-rate"}=$rate;
|
|
|
+ # 3.2 Queue Types (PCQ)
|
|
|
+ @tmp = netdev_cmd($gate, $t, '/queue type print terse without-paging where name~"pcq_(down|up)load"', 1);
|
|
|
+ foreach my $row (@tmp) {
|
|
|
+ my $clean = $parse_mikrotik_line->($row);
|
|
|
+ next unless $clean;
|
|
|
+ if ($clean =~ /name=pcq_(down|up)load_(\d{1,3})\s+/i) {
|
|
|
+ my $direct = $1;
|
|
|
+ my $index = $2;
|
|
|
+ $get_queue_type{$index}{$direct} = $clean;
|
|
|
+
|
|
|
+ if ($clean =~ /pcq-rate=(\S*)\s+/) {
|
|
|
+ my $rate = bitrate_to_kbps($1);
|
|
|
+ $get_queue_type{$index}{$direct . "-rate"} = $rate;
|
|
|
+ }
|
|
|
+ if ($clean =~ /pcq-classifier=(\S*)\s+/) {
|
|
|
+ $get_queue_type{$index}{$direct . "-classifier"} = $1;
|
|
|
+ }
|
|
|
}
|
|
|
- if ($row=~/pcq-classifier=(\S*)\s+\S/i) { $get_queue_type{$index}{$direct."-classifier"}=$1; }
|
|
|
- if ($row=~/pcq-src-address-mask=(\S*)\s+\S/i) { $get_queue_type{$index}{$direct."-src-address-mask"}=$1; }
|
|
|
- if ($row=~/pcq-dst-address-mask=(\S*)\s+\S/i) { $get_queue_type{$index}{$direct."-dst-address-mask"}=$1; }
|
|
|
- }
|
|
|
-}
|
|
|
+ }
|
|
|
|
|
|
-@tmp=();
|
|
|
-@tmp=netdev_cmd($gate,$t,'/queue tree print terse without-paging where parent~"(download|upload)_root"',1);
|
|
|
-#log_debug($gate_ident."Get user queues: ".Dumper(\@tmp));
|
|
|
-
|
|
|
-#print Dumper(\@tmp);
|
|
|
-# 0 I name=queue_3_out parent=upload_root packet-mark=upload_3 limit-at=0 queue=*2A priority=8 max-limit=0 burst-limit=0 burst-threshold=0 burst-time=0s bucket-size=0.1
|
|
|
-# 5 I name=queue_3_vlan2_in parent=download_root_vlan2 packet-mark=download_3_vlan2 limit-at=0 queue=*2B priority=8 max-limit=0 burst-limit=0 burst-threshold=0 burst-time=0s bucket-size=0.1
|
|
|
-foreach my $row (@tmp) {
|
|
|
-next if (!$row);
|
|
|
-$row = trim($row);
|
|
|
-next if ($row!~/^(\d)/);
|
|
|
-$row=~s/^(\d*)\s+//;
|
|
|
-next if (!$row);
|
|
|
-if ($row=~/queue=pcq_(down|up)load_(\d){1,3}/i) {
|
|
|
- if ($row=~/name=queue_(\d){1,3}_(\S*)_out\s+/i) {
|
|
|
- next if (!$1);
|
|
|
- next if (!$2);
|
|
|
- my $index = $1;
|
|
|
- my $int_name = $2;
|
|
|
- $get_queue_tree{$index}{$int_name}{up}=$row;
|
|
|
- if ($row=~/parent=(\S*)\s+\S/i) { $get_queue_tree{$index}{$int_name}{'up-parent'}=$1; }
|
|
|
- if ($row=~/packet-mark=(\S*)\s+\S/i) { $get_queue_tree{$index}{$int_name}{'up-mark'}=$1; }
|
|
|
- if ($row=~/queue=(\S*)\s+\S/i) { $get_queue_tree{$index}{$int_name}{'up-queue'}=$1; }
|
|
|
+ # 3.3 Queue Tree (User queues)
|
|
|
+ @tmp = netdev_cmd($gate, $t, '/queue tree print terse without-paging where parent~"(download|upload)_root"', 1);
|
|
|
+ foreach my $row (@tmp) {
|
|
|
+ my $clean = $parse_mikrotik_line->($row);
|
|
|
+ next unless $clean;
|
|
|
+ # Upload
|
|
|
+ if ($clean =~ /name=queue_(\d{1,3})_(\S*)_out\s+/i) {
|
|
|
+ my $index = $1;
|
|
|
+ my $int_name = $2;
|
|
|
+ $get_queue_tree{$index}{$int_name}{up} = $clean;
|
|
|
+ $get_queue_tree{$index}{$int_name}{'up-parent'} = $1 if $clean =~ /parent=(\S*)\s+/;
|
|
|
+ $get_queue_tree{$index}{$int_name}{'up-mark'} = $1 if $clean =~ /packet-mark=(\S*)\s+/;
|
|
|
+ $get_queue_tree{$index}{$int_name}{'up-queue'} = $1 if $clean =~ /queue=(\S*)\s+/;
|
|
|
+ }
|
|
|
+ # Download
|
|
|
+ if ($clean =~ /name=queue_(\d{1,3})_(\S*)_in\s+/i) {
|
|
|
+ my $index = $1;
|
|
|
+ my $int_name = $2;
|
|
|
+ $get_queue_tree{$index}{$int_name}{down} = $clean;
|
|
|
+ $get_queue_tree{$index}{$int_name}{'down-parent'} = $1 if $clean =~ /parent=(\S*)\s+/;
|
|
|
+ $get_queue_tree{$index}{$int_name}{'down-mark'} = $1 if $clean =~ /packet-mark=(\S*)\s+/;
|
|
|
+ $get_queue_tree{$index}{$int_name}{'down-queue'} = $1 if $clean =~ /queue=(\S*)\s+/;
|
|
|
+ }
|
|
|
}
|
|
|
- if ($row=~/name=queue_(\d){1,3}_(\S*)_in\s+/i) {
|
|
|
- next if (!$1);
|
|
|
- next if (!$2);
|
|
|
- my $index = $1;
|
|
|
- my $int_name = $2;
|
|
|
- $get_queue_tree{$index}{$int_name}{down}=$row;
|
|
|
- if ($row=~/parent=(\S*)\s+\S/i) { $get_queue_tree{$index}{$int_name}{'down-parent'}=$1; }
|
|
|
- if ($row=~/packet-mark=(\S*)\s+\S/i) { $get_queue_tree{$index}{$int_name}{'down-mark'}=$1; }
|
|
|
- if ($row=~/queue=(\S*)\s+\S/i) { $get_queue_tree{$index}{$int_name}{'down-queue'}=$1; }
|
|
|
+
|
|
|
+ # 3.4 Firewall Mangle
|
|
|
+ @tmp = netdev_cmd($gate, $t, '/ip firewall mangle print terse without-paging where action=mark-packet and new-packet-mark~"(upload|download)_[0-9]{1,3}"', 1);
|
|
|
+ foreach my $row (@tmp) {
|
|
|
+ my $clean = $parse_mikrotik_line->($row);
|
|
|
+ next unless $clean;
|
|
|
+
|
|
|
+ if ($clean =~ /new-packet-mark=upload_(\d{1,3})_(\S*)\s+/i) {
|
|
|
+ my $index = $1;
|
|
|
+ my $int_name = $2;
|
|
|
+ $get_filter_mangle{$index}{$int_name}{up} = $clean;
|
|
|
+ $get_filter_mangle{$index}{$int_name}{'up-list'} = $1 if $clean =~ /src-address-list=(\S*)\s+/;
|
|
|
+ $get_filter_mangle{$index}{$int_name}{'up-dev'} = $1 if $clean =~ /out-interface=(\S*)\s+/;
|
|
|
+ $get_filter_mangle{$index}{$int_name}{'up-mark'} = $1 if $clean =~ /new-packet-mark=(\S*)\s+/;
|
|
|
+ }
|
|
|
+ if ($clean =~ /new-packet-mark=download_(\d{1,3})_(\S*)\s+/i) {
|
|
|
+ my $index = $1;
|
|
|
+ my $int_name = $2;
|
|
|
+ $get_filter_mangle{$index}{$int_name}{down} = $clean;
|
|
|
+ $get_filter_mangle{$index}{$int_name}{'down-list'} = $1 if $clean =~ /dst-address-list=(\S*)\s+/;
|
|
|
+ $get_filter_mangle{$index}{$int_name}{'down-dev'} = $1 if $clean =~ /out-interface=(\S*)\s+/;
|
|
|
+ $get_filter_mangle{$index}{$int_name}{'down-mark'} = $1 if $clean =~ /new-packet-mark=(\S*)\s+/;
|
|
|
+ }
|
|
|
}
|
|
|
- }
|
|
|
-}
|
|
|
|
|
|
-@tmp=();
|
|
|
-
|
|
|
-@tmp=netdev_cmd($gate,$t,'/ip firewall mangle print terse without-paging where action=mark-packet and new-packet-mark~"(upload|download)_[0-9]{1,3}"',1);
|
|
|
-log_debug($gate_ident."Get firewall mangle rules for queues:".Dumper(\@tmp));
|
|
|
-
|
|
|
-# 0 chain=forward action=mark-packet new-packet-mark=upload_0 passthrough=yes src-address-list=queue_0 out-interface=sfp-sfpplus1-wan log=no log-prefix=""
|
|
|
-# 0 chain=forward action=mark-packet new-packet-mark=download_3_vlan2 passthrough=yes dst-address-list=queue_3 out-interface=vlan2 in-interface-list=WAN log=no log-prefix=""
|
|
|
-
|
|
|
-foreach my $row (@tmp) {
|
|
|
-next if (!$row);
|
|
|
-$row = trim($row);
|
|
|
-next if ($row!~/^(\d){1,3}/);
|
|
|
-$row=~s/^\d{1,3}\s+//;
|
|
|
-next if (!$row);
|
|
|
-if ($row=~/new-packet-mark=upload_(\d){1,3}_(\S*)\s+/i) {
|
|
|
- next if (!$1);
|
|
|
- next if (!$2);
|
|
|
- my $index = $1;
|
|
|
- my $int_name = $2;
|
|
|
- $get_filter_mangle{$index}{$int_name}{up}=$row;
|
|
|
- if ($row=~/src-address-list=(\S*)\s+\S/i) { $get_filter_mangle{$index}{$int_name}{'up-list'}=$1; }
|
|
|
- if ($row=~/out-interface=(\S*)\s+\S/i) { $get_filter_mangle{$index}{$int_name}{'up-dev'}=$1; }
|
|
|
- if ($row=~/new-packet-mark=(\S*)\s+\S/i) { $get_filter_mangle{$index}{$int_name}{'up-mark'}=$1; }
|
|
|
- }
|
|
|
-if ($row=~/new-packet-mark=download_(\d){1,3}_(\S*)\s+/i) {
|
|
|
- next if (!$1);
|
|
|
- next if (!$2);
|
|
|
- my $index = $1;
|
|
|
- my $int_name = $2;
|
|
|
- $get_filter_mangle{$index}{$int_name}{down}=$row;
|
|
|
- if ($row=~/dst-address-list=(\S*)\s+\S/i) { $get_filter_mangle{$index}{$int_name}{'down-list'}=$1; }
|
|
|
- if ($row=~/new-packet-mark=(\S*)\s+\S/i) { $get_filter_mangle{$index}{$int_name}{'down-mark'}=$1; }
|
|
|
- if ($row=~/out-interface=(\S*)\s+\S/i) { $get_filter_mangle{$index}{$int_name}{'down-dev'}=$1; }
|
|
|
+ log_debug($gate_ident . "Status of the shapers on the router has been received.");
|
|
|
+ };
|
|
|
+ if ($@) {
|
|
|
+ log_error($gate_ident . "Failed to get current router state: $@");
|
|
|
+ last SHAPER;
|
|
|
}
|
|
|
-}
|
|
|
|
|
|
-log_debug($gate_ident."Queues root classes:".Dumper(\%get_queue_root));
|
|
|
-log_debug($gate_ident."Queues type status:".Dumper(\%get_queue_type));
|
|
|
-log_debug($gate_ident."Queues tree status:".Dumper(\%get_queue_tree));
|
|
|
-log_debug($gate_ident."Firewall mangle status:".Dumper(\%get_filter_mangle));
|
|
|
-
|
|
|
-my %queue_type;
|
|
|
-my %queue_tree;
|
|
|
-my %filter_mangle;
|
|
|
-
|
|
|
-#analyze root class
|
|
|
-foreach my $l3_int (keys %l3_interfaces) {
|
|
|
-my $int_type = 'download';
|
|
|
-if ($l3_interfaces{$l3_int}->{type}) { $int_type = 'upload'; }
|
|
|
-#clear unknown root queue
|
|
|
-push(@cmd_list,'/queue tree remove [ find name!~"'.$int_type . '_root_' . $l3_int .'" and parent='.$l3_int.' ]');
|
|
|
-# add root queue if not exists
|
|
|
-if (!exists $get_queue_root{$l3_int}) {
|
|
|
- push(@cmd_list,'/queue tree add max-limit=' . kbps_to_bitrate($l3_interfaces{$l3_int}->{bandwidth}) . ' name=' . $int_type . '_root_' . $l3_int .' parent=' . $l3_int . ' queue=pcq-' . $int_type . '-default');
|
|
|
- next;
|
|
|
- }
|
|
|
-# change bandwidth if differs
|
|
|
-if ($get_queue_root{$l3_int}{bandwidth} ne $l3_interfaces{$l3_int}->{bandwidth}) {
|
|
|
- push(@cmd_list,'/queue tree set max-limit=' . kbps_to_bitrate($l3_interfaces{$l3_int}->{bandwidth}) . ' [ find name='.$int_type . '_root_' . $l3_int . ' ]');
|
|
|
- next;
|
|
|
- }
|
|
|
-}
|
|
|
+ # --- 4. Генерация команд синхронизации ---
|
|
|
+ my @cmd_list;
|
|
|
+
|
|
|
+ eval {
|
|
|
+ # 4.1 Анализ и коррекция Root классов
|
|
|
+ foreach my $l3_int (keys %l3_interfaces) {
|
|
|
+ my $int_type = ($l3_interfaces{$l3_int}->{type}) ? 'upload' : 'download';
|
|
|
+ my $root_name = "${int_type}_root_${l3_int}";
|
|
|
+ my $desired_bw = $l3_interfaces{$l3_int}->{bandwidth};
|
|
|
+
|
|
|
+ # Очистка мусорных рут-очередей на этом интерфейсе
|
|
|
+ push(@cmd_list, "/queue tree remove [ find name!~\"${int_type}_root_${l3_int}\" and parent=${l3_int} ]");
|
|
|
+
|
|
|
+ if (!exists $get_queue_root{$l3_int}) {
|
|
|
+ log_info($gate_ident . "[CHANGE] Root queue '$root_name' missing. Creating with limit ${desired_bw}kbps.");
|
|
|
+ push(@cmd_list, "/queue tree add max-limit=" . kbps_to_bitrate($desired_bw) . " name=$root_name parent=$l3_int queue=pcq-${int_type}-default");
|
|
|
+ next;
|
|
|
+ }
|
|
|
|
|
|
-#generate new config
|
|
|
-foreach my $queue_name (keys %queues) {
|
|
|
-my $q_id=$queues{$queue_name}{id};
|
|
|
-my $q_up=$queues{$queue_name}{up};
|
|
|
-my $q_down=$queues{$queue_name}{down};
|
|
|
+ my $current_bw = $get_queue_root{$l3_int}{bandwidth} || 0;
|
|
|
+ if ($current_bw != $desired_bw) {
|
|
|
+ log_info($gate_ident . "[CHANGE] Root queue '$root_name' bandwidth mismatch. Current: ${current_bw}, Desired: ${desired_bw}. Updating.");
|
|
|
+ push(@cmd_list, "/queue tree set max-limit=" . kbps_to_bitrate($desired_bw) . " [ find name=$root_name ]");
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
-#queue_types
|
|
|
-$queue_type{$q_id}{up}="name=pcq_upload_".$q_id." kind=pcq pcq-rate=".$q_up."k pcq-limit=500KiB pcq-classifier=src-address pcq-total-limit=2000KiB pcq-burst-rate=0 pcq-burst-threshold=0 pcq-burst-time=10s pcq-src-address-mask=32 pcq-dst-address-mask=32 pcq-src-address6-mask=64 pcq-dst-address6-mask=64";
|
|
|
-$queue_type{$q_id}{down}="name=pcq_download_".$q_id." kind=pcq pcq-rate=".$q_down."k pcq-limit=500KiB pcq-classifier=dst-address pcq-total-limit=2000KiB pcq-burst-rate=0 pcq-burst-threshold=0 pcq-burst-time=10s pcq-src-address-mask=32 pcq-dst-address-mask=32 pcq-src-address6-mask=64 pcq-dst-address6-mask=64";
|
|
|
+ # 4.2 Генерация конфигурации очередей (Types, Tree, Mangle)
|
|
|
+ foreach my $queue_name (keys %queues) {
|
|
|
+ my $q_id = $queues{$queue_name}{id};
|
|
|
+ my $q_up = $queues{$queue_name}{up};
|
|
|
+ my $q_down = $queues{$queue_name}{down};
|
|
|
+
|
|
|
+ # --- Queue Types ---
|
|
|
+ my $type_up_str = "name=pcq_upload_${q_id} kind=pcq pcq-rate=${q_up}k pcq-limit=500KiB pcq-classifier=src-address pcq-total-limit=2000KiB pcq-burst-rate=0 pcq-burst-threshold=0 pcq-burst-time=10s pcq-src-address-mask=32 pcq-dst-address-mask=32 pcq-src-address6-mask=64 pcq-dst-address6-mask=64";
|
|
|
+ my $type_down_str = "name=pcq_download_${q_id} kind=pcq pcq-rate=${q_down}k pcq-limit=500KiB pcq-classifier=dst-address pcq-total-limit=2000KiB pcq-burst-rate=0 pcq-burst-threshold=0 pcq-burst-time=10s pcq-src-address-mask=32 pcq-dst-address-mask=32 pcq-src-address6-mask=64 pcq-dst-address6-mask=64";
|
|
|
+
|
|
|
+ # Check UP Type
|
|
|
+ my $need_update_up = 0;
|
|
|
+ if (!$get_queue_type{$q_id}{up}) {
|
|
|
+ $need_update_up = 1;
|
|
|
+ log_info($gate_ident . "[CHANGE] Queue Type 'pcq_upload_${q_id}' missing.");
|
|
|
+ } else {
|
|
|
+ my $curr_rate = $get_queue_type{$q_id}{'up-rate'} || 0;
|
|
|
+ my $curr_class = $get_queue_type{$q_id}{'up-classifier'} || '';
|
|
|
+ if (abs($q_up - $curr_rate) > 50) {
|
|
|
+ $need_update_up = 1;
|
|
|
+ log_info($gate_ident . "[CHANGE] Queue Type 'pcq_upload_${q_id}' rate mismatch: ${curr_rate} vs ${q_up}.");
|
|
|
+ } elsif ($curr_class !~ /src-address/i) {
|
|
|
+ $need_update_up = 1;
|
|
|
+ log_info($gate_ident . "[CHANGE] Queue Type 'pcq_upload_${q_id}' classifier mismatch.");
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
-my $queue_ok=1;
|
|
|
-if (!$get_queue_type{$q_id}{up}) { $queue_ok=0; }
|
|
|
-if ($queue_ok and abs($q_up - bitrate_to_kbps($get_queue_type{$q_id}{'up-rate'}))>10) { $queue_ok=0; }
|
|
|
-if ($queue_ok and $get_queue_type{$q_id}{'up-classifier'}!~/src-address/i) { $queue_ok=0; }
|
|
|
+ if ($need_update_up) {
|
|
|
+ # Формируем понятное сообщение о причине изменения
|
|
|
+ my $action_msg = !$get_queue_type{$q_id}{up}
|
|
|
+ ? "Creating missing queue type"
|
|
|
+ : "Updating queue type parameters (rate/classifier mismatch)";
|
|
|
|
|
|
-if (!$queue_ok) {
|
|
|
- push(@cmd_list,':foreach i in [/queue type find where name~"pcq_upload_'.$q_id.'" ] do={/queue type remove $i};');
|
|
|
- push(@cmd_list,'/queue type add '.$queue_type{$q_id}{up});
|
|
|
- }
|
|
|
+ log_info($gate_ident . "[CHANGE] Queue Type 'pcq_upload_${q_id}': $action_msg.");
|
|
|
+ log_debug($gate_ident . " -> New config: $type_up_str");
|
|
|
|
|
|
-$queue_ok=1;
|
|
|
-if (!$get_queue_type{$q_id}{down}) { $queue_ok=0; }
|
|
|
-if ($queue_ok and abs($q_up - bitrate_to_kbps($get_queue_type{$q_id}{'down-rate'}))>10) { $queue_ok=0; }
|
|
|
-if ($queue_ok and $get_queue_type{$q_id}{'down-classifier'}!~/dst-address/i) { $queue_ok=0; }
|
|
|
+ # Команда удаления старых экземпляров (если есть мусорные дубликаты)
|
|
|
+ push(@cmd_list, ":foreach i in [/queue type find where name=\"pcq_upload_${q_id}\"] do={/queue type remove \$i};");
|
|
|
+
|
|
|
+ # Команда добавления новой конфигурации
|
|
|
+ push(@cmd_list, "/queue type add $type_up_str");
|
|
|
+ }
|
|
|
|
|
|
-if (!$queue_ok) {
|
|
|
- push(@cmd_list,':foreach i in [/queue type find where name~"pcq_download_'.$q_id.'" ] do={/queue type remove $i};');
|
|
|
- push(@cmd_list,'/queue type add '.$queue_type{$q_id}{down});
|
|
|
- }
|
|
|
+ # Check DOWN Type
|
|
|
+ my $need_update_down = 0;
|
|
|
+ if (!$get_queue_type{$q_id}{down}) {
|
|
|
+ $need_update_down = 1;
|
|
|
+ log_info($gate_ident . "[CHANGE] Queue Type 'pcq_download_${q_id}' missing.");
|
|
|
+ } else {
|
|
|
+ my $curr_rate = $get_queue_type{$q_id}{'down-rate'} || 0;
|
|
|
+ my $curr_class = $get_queue_type{$q_id}{'down-classifier'} || '';
|
|
|
+ if (abs($q_down - $curr_rate) > 50) {
|
|
|
+ $need_update_down = 1;
|
|
|
+ log_info($gate_ident . "[CHANGE] Queue Type 'pcq_download_${q_id}' rate mismatch: ${curr_rate} vs ${q_down}.");
|
|
|
+ } elsif ($curr_class !~ /dst-address/i) {
|
|
|
+ $need_update_down = 1;
|
|
|
+ log_info($gate_ident . "[CHANGE] Queue Type 'pcq_download_${q_id}' classifier mismatch.");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if ($need_update_down) {
|
|
|
+ # Определяем причину изменения для лога
|
|
|
+ my $action_msg = !$get_queue_type{$q_id}{down}
|
|
|
+ ? "Creating missing queue type"
|
|
|
+ : "Updating queue type parameters (rate/classifier mismatch)";
|
|
|
+
|
|
|
+ log_info($gate_ident . "[CHANGE] Queue Type 'pcq_download_${q_id}': $action_msg.");
|
|
|
+ log_debug($gate_ident . " -> New config: $type_down_str");
|
|
|
+
|
|
|
+ # Удаляем старые/некорректные записи
|
|
|
+ push(@cmd_list, ":foreach i in [/queue type find where name=\"pcq_download_${q_id}\"] do={/queue type remove \$i};");
|
|
|
+
|
|
|
+ # Добавляем новую конфигурацию
|
|
|
+ push(@cmd_list, "/queue type add $type_down_str");
|
|
|
+ }
|
|
|
|
|
|
-#upload queue
|
|
|
-foreach my $int (@wan_int) {
|
|
|
-$queue_tree{$q_id}{$int}{up}="name=queue_".$q_id."_".$int."_out parent=upload_root_".$int." packet-mark=upload_".$q_id."_".$int." limit-at=0 queue=pcq_upload_".$q_id." priority=8 max-limit=0 burst-limit=0 burst-threshold=0 burst-time=0s bucket-size=0.1";
|
|
|
-$filter_mangle{$q_id}{$int}{up}="chain=forward action=mark-packet new-packet-mark=upload_".$q_id."_".$int." passthrough=yes src-address-list=queue_".$q_id." out-interface=".$int." log=no log-prefix=\"\"";
|
|
|
+ # --- Upload Queue Tree & Mangle (per WAN interface) ---
|
|
|
+ foreach my $int (@wan_int) {
|
|
|
+ next unless $int;
|
|
|
+ my $q_name = "queue_${q_id}_${int}_out";
|
|
|
+ my $p_mark = "upload_${q_id}_${int}";
|
|
|
+ my $p_list = "queue_${q_id}";
|
|
|
+
|
|
|
+ my $tree_up_str = "name=${q_name} parent=upload_root_${int} packet-mark=${p_mark} limit-at=0 queue=pcq_upload_${q_id} priority=8 max-limit=0 burst-limit=0 burst-threshold=0 burst-time=0s bucket-size=0.1";
|
|
|
+ my $mangle_up_str = "chain=forward action=mark-packet new-packet-mark=${p_mark} passthrough=yes src-address-list=${p_list} out-interface=${int} log=no log-prefix=\"\"";
|
|
|
+
|
|
|
+ # Check Tree UP
|
|
|
+ my $fix_tree_up = 0;
|
|
|
+ if (!$get_queue_tree{$q_id}{$int}{up}) {
|
|
|
+ $fix_tree_up = 1;
|
|
|
+ log_info($gate_ident . "[CHANGE] Queue Tree '$q_name' missing.");
|
|
|
+ } else {
|
|
|
+ $fix_tree_up = 1 if ($get_queue_tree{$q_id}{$int}{'up-parent'} ne "upload_root_${int}");
|
|
|
+ $fix_tree_up = 1 if ($get_queue_tree{$q_id}{$int}{'up-mark'} ne $p_mark);
|
|
|
+ $fix_tree_up = 1 if ($get_queue_tree{$q_id}{$int}{'up-queue'} ne "pcq_upload_${q_id}");
|
|
|
+ log_info($gate_ident . "[CHANGE] Queue Tree '$q_name' parameters mismatch.") if $fix_tree_up;
|
|
|
+ }
|
|
|
|
|
|
-$queue_ok=1;
|
|
|
-if (!$get_queue_tree{$q_id}{$int}{up}) { $queue_ok=0; }
|
|
|
-if ($queue_ok and ($get_queue_tree{$q_id}{$int}{'up-parent'} ne "upload_root_".$int)) { $queue_ok=0;}
|
|
|
-if ($queue_ok and ($get_queue_tree{$q_id}{$int}{'up-mark'} ne "upload_".$q_id."_".$int)) { $queue_ok=0; }
|
|
|
-if ($queue_ok and ($get_queue_tree{$q_id}{$int}{'up-queue'} ne "pcq_upload_".$q_id)) { $queue_ok=0; }
|
|
|
+ if ($fix_tree_up) {
|
|
|
+ # Определяем причину изменения для лога
|
|
|
+ my $action_msg = !$get_queue_tree{$q_id}{$int}{up}
|
|
|
+ ? "Creating missing queue tree rule"
|
|
|
+ : "Updating queue tree parameters (parent/mark/queue mismatch)";
|
|
|
+
|
|
|
+ log_info($gate_ident . "[CHANGE] Queue Tree '${q_name}': $action_msg.");
|
|
|
+ log_debug($gate_ident . " -> New config: $tree_up_str");
|
|
|
+
|
|
|
+ # Удаляем старые/некорректные правила
|
|
|
+ push(@cmd_list, ":foreach i in [/queue tree find where name=\"${q_name}\"] do={/queue tree remove \$i};");
|
|
|
+
|
|
|
+ # Добавляем новое правило
|
|
|
+ push(@cmd_list, "/queue tree add $tree_up_str");
|
|
|
+ }
|
|
|
|
|
|
-if (!$queue_ok) {
|
|
|
- push(@cmd_list,':foreach i in [/queue tree find where name~"queue_'.$q_id."_".$int."_out".'" ] do={/queue tree remove $i};');
|
|
|
- push(@cmd_list,'/queue tree add '.$queue_tree{$q_id}{$int}{up});
|
|
|
- }
|
|
|
+ # Check Mangle UP
|
|
|
+ my $fix_mangle_up = 0;
|
|
|
+ if (!$get_filter_mangle{$q_id}{$int}{up}) {
|
|
|
+ $fix_mangle_up = 1;
|
|
|
+ log_info($gate_ident . "[CHANGE] Mangle rule for '$p_mark' missing.");
|
|
|
+ } else {
|
|
|
+ $fix_mangle_up = 1 if ($get_filter_mangle{$q_id}{$int}{'up-mark'} ne $p_mark);
|
|
|
+ $fix_mangle_up = 1 if ($get_filter_mangle{$q_id}{$int}{'up-list'} ne $p_list);
|
|
|
+ $fix_mangle_up = 1 if ($get_filter_mangle{$q_id}{$int}{'up-dev'} ne $int);
|
|
|
+ log_info($gate_ident . "[CHANGE] Mangle rule for '$p_mark' parameters mismatch.") if $fix_mangle_up;
|
|
|
+ }
|
|
|
|
|
|
-$queue_ok=1;
|
|
|
-if (!$get_filter_mangle{$q_id}{$int}{up}) { $queue_ok=0; }
|
|
|
-if ($queue_ok and ($get_filter_mangle{$q_id}{$int}{'up-mark'} ne "upload_".$q_id."_".$int)) { $queue_ok=0; }
|
|
|
-if ($queue_ok and ($get_filter_mangle{$q_id}{$int}{'up-list'} ne "queue_".$q_id)) { $queue_ok=0; }
|
|
|
-if ($queue_ok and ($get_filter_mangle{$q_id}{$int}{'up-dev'} ne $int)) { $queue_ok=0; }
|
|
|
+ if ($fix_mangle_up) {
|
|
|
+ # Определяем причину изменения для лога
|
|
|
+ my $action_msg = !$get_filter_mangle{$q_id}{$int}{up}
|
|
|
+ ? "Creating missing mangle rule"
|
|
|
+ : "Updating mangle rule parameters (mark/list/interface mismatch)";
|
|
|
+
|
|
|
+ log_info($gate_ident . "[CHANGE] Mangle Rule '${p_mark}': $action_msg.");
|
|
|
+ log_debug($gate_ident . " -> New config: $mangle_up_str");
|
|
|
+
|
|
|
+ # Удаляем старые/некорректные правила
|
|
|
+ # Примечание: используем точное совпадение new-packet-mark=, так как имя метки уникально
|
|
|
+ push(@cmd_list, ":foreach i in [/ip firewall mangle find where action=mark-packet and new-packet-mark=\"${p_mark}\"] do={/ip firewall mangle remove \$i};");
|
|
|
+
|
|
|
+ # Добавляем новое правило
|
|
|
+ push(@cmd_list, "/ip firewall mangle add $mangle_up_str");
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
-if (!$queue_ok) {
|
|
|
- push(@cmd_list,':foreach i in [/ip firewall mangle find where action=mark-packet and new-packet-mark~"upload_'.$q_id."_".$int.'" ] do={/ip firewall mangle remove $i};');
|
|
|
- push(@cmd_list,'/ip firewall mangle add '.$filter_mangle{$q_id}{$int}{up});
|
|
|
- }
|
|
|
-}
|
|
|
+ # --- Download Queue Tree & Mangle (per LAN interface) ---
|
|
|
+ foreach my $int (@lan_int) {
|
|
|
+ next unless $int;
|
|
|
+ my $q_name = "queue_${q_id}_${int}_in";
|
|
|
+ my $p_mark = "download_${q_id}_${int}";
|
|
|
+ my $p_list = "queue_${q_id}";
|
|
|
+
|
|
|
+ my $tree_down_str = "name=${q_name} parent=download_root_${int} packet-mark=${p_mark} limit-at=0 queue=pcq_download_${q_id} priority=8 max-limit=0 burst-limit=0 burst-threshold=0 burst-time=0s bucket-size=0.1";
|
|
|
+ my $mangle_down_str = "chain=forward action=mark-packet new-packet-mark=${p_mark} passthrough=yes dst-address-list=${p_list} out-interface=${int} in-interface-list=WAN log=no log-prefix=\"\"";
|
|
|
+
|
|
|
+ # Check Tree DOWN
|
|
|
+ my $fix_tree_down = 0;
|
|
|
+ if (!$get_queue_tree{$q_id}{$int}{down}) {
|
|
|
+ $fix_tree_down = 1;
|
|
|
+ log_info($gate_ident . "[CHANGE] Queue Tree '$q_name' missing.");
|
|
|
+ } else {
|
|
|
+ $fix_tree_down = 1 if ($get_queue_tree{$q_id}{$int}{'down-parent'} ne "download_root_${int}");
|
|
|
+ $fix_tree_down = 1 if ($get_queue_tree{$q_id}{$int}{'down-mark'} ne $p_mark);
|
|
|
+ $fix_tree_down = 1 if ($get_queue_tree{$q_id}{$int}{'down-queue'} ne "pcq_download_${q_id}");
|
|
|
+ log_info($gate_ident . "[CHANGE] Queue Tree '$q_name' parameters mismatch.") if $fix_tree_down;
|
|
|
+ }
|
|
|
|
|
|
-#download
|
|
|
-foreach my $int (@lan_int) {
|
|
|
-next if (!$int);
|
|
|
-$queue_tree{$q_id}{$int}{down}="name=queue_".$q_id."_".$int."_in parent=download_root_".$int." packet-mark=download_".$q_id."_".$int." limit-at=0 queue=pcq_download_".$q_id." priority=8 max-limit=0 burst-limit=0 burst-threshold=0 burst-time=0s bucket-size=0.1";
|
|
|
-$filter_mangle{$q_id}{$int}{down}="chain=forward action=mark-packet new-packet-mark=download_".$q_id."_".$int." passthrough=yes dst-address-list=queue_".$q_id." out-interface=".$int." in-interface-list=WAN log=no log-prefix=\"\"";
|
|
|
-
|
|
|
-$queue_ok=1;
|
|
|
-if (!$get_queue_tree{$q_id}{$int}{down}) { $queue_ok=0; }
|
|
|
-if ($queue_ok and ($get_queue_tree{$q_id}{$int}{'down-parent'} ne "download_root_".$int)) { $queue_ok=0; }
|
|
|
-if ($queue_ok and ($get_queue_tree{$q_id}{$int}{'down-mark'} ne "download_".$q_id."_".$int)) { $queue_ok=0; }
|
|
|
-if ($queue_ok and ($get_queue_tree{$q_id}{$int}{'down-queue'} ne "pcq_download_".$q_id)) { $queue_ok=0; }
|
|
|
-
|
|
|
-if (!$queue_ok) {
|
|
|
- push(@cmd_list,':foreach i in [/queue tree find where name~"queue_'.$q_id."_".$int."_in".'" ] do={/queue tree remove $i};');
|
|
|
- push(@cmd_list,'/queue tree add '.$queue_tree{$q_id}{$int}{down});
|
|
|
- }
|
|
|
+ if ($fix_tree_down) {
|
|
|
+ push(@cmd_list, ":foreach i in [/queue tree find where name=\"${q_name}\"] do={/queue tree remove \$i};");
|
|
|
+ push(@cmd_list, "/queue tree add $tree_down_str");
|
|
|
+ }
|
|
|
|
|
|
-$queue_ok=1;
|
|
|
-if (!$get_filter_mangle{$q_id}{$int}{down}) { $queue_ok=0; }
|
|
|
-if ($queue_ok and ($get_filter_mangle{$q_id}{$int}{'down-mark'} ne "download_".$q_id."_".$int)) { $queue_ok=0; }
|
|
|
-if ($queue_ok and ($get_filter_mangle{$q_id}{$int}{'down-list'} ne "queue_".$q_id)) { $queue_ok=0; }
|
|
|
-if ($queue_ok and ($get_filter_mangle{$q_id}{$int}{'down-dev'} ne $int)) { $queue_ok=0; }
|
|
|
+ # Check Mangle DOWN
|
|
|
+ my $fix_mangle_down = 0;
|
|
|
+ if (!$get_filter_mangle{$q_id}{$int}{down}) {
|
|
|
+ $fix_mangle_down = 1;
|
|
|
+ log_info($gate_ident . "[CHANGE] Mangle rule for '$p_mark' missing.");
|
|
|
+ } else {
|
|
|
+ $fix_mangle_down = 1 if ($get_filter_mangle{$q_id}{$int}{'down-mark'} ne $p_mark);
|
|
|
+ $fix_mangle_down = 1 if ($get_filter_mangle{$q_id}{$int}{'down-list'} ne $p_list);
|
|
|
+ $fix_mangle_down = 1 if ($get_filter_mangle{$q_id}{$int}{'down-dev'} ne $int);
|
|
|
+ log_info($gate_ident . "[CHANGE] Mangle rule for '$p_mark' parameters mismatch.") if $fix_mangle_down;
|
|
|
+ }
|
|
|
|
|
|
-if (!$queue_ok) {
|
|
|
- push(@cmd_list,':foreach i in [/ip firewall mangle find where action=mark-packet and new-packet-mark~"download_'.$q_id."_".$int.'" ] do={/ip firewall mangle remove $i};');
|
|
|
- push(@cmd_list,'/ip firewall mangle add '.$filter_mangle{$q_id}{$int}{down});
|
|
|
- }
|
|
|
-}
|
|
|
+ if ($fix_mangle_down) {
|
|
|
+ # Определяем причину изменения для лога
|
|
|
+ my $action_msg = !$get_filter_mangle{$q_id}{$int}{down}
|
|
|
+ ? "Creating missing mangle rule"
|
|
|
+ : "Updating mangle rule parameters (mark/list/interface mismatch)";
|
|
|
+
|
|
|
+ log_info($gate_ident . "[CHANGE] Mangle Rule '${p_mark}': $action_msg.");
|
|
|
+ log_debug($gate_ident . " -> New config: $mangle_down_str");
|
|
|
+
|
|
|
+ # Удаляем старые/некорректные правила
|
|
|
+ # Используем точное совпадение new-packet-mark, так как имя метки уникально
|
|
|
+ push(@cmd_list, ":foreach i in [/ip firewall mangle find where action=mark-packet and new-packet-mark=\"${p_mark}\"] do={/ip firewall mangle remove \$i};");
|
|
|
+
|
|
|
+ # Добавляем новое правило
|
|
|
+ push(@cmd_list, "/ip firewall mangle add $mangle_down_str");
|
|
|
+ }
|
|
|
|
|
|
+ }
|
|
|
+ }
|
|
|
+ };
|
|
|
+ if ($@) {
|
|
|
+ log_error($gate_ident . "Error during command generation logic: $@");
|
|
|
+ }
|
|
|
+} # end shaper_enabled
|
|
|
}
|
|
|
|
|
|
-}#end shaper
|
|
|
-
|
|
|
# Analyze actual ACL
|
|
|
|
|
|
my %cur_users;
|