📜 ⬆️ ⬇️

Mikrotik Router OS, script for dynamic speed division (Version 2)

Mikrotik Router OS, script for dynamic speed division (Version 2)



This is the second version, the first is here: here.

The other day, the following problem arose: to divide the speed equally between all users, so that the speed was not allocated to customers who currently do not use the Internet, but to give to everyone else, so that with a large number of customers and a narrow channel, they get some kind of “buffering” channel.
')
Based on past experience, a completely new script was written that eliminates the flaws of the past and refined for current needs.

The new version has become much easier in terms of the location of users, now it does not matter how the user connects, the main thing is that he has an ip address and that’s enough for us.

So let's go ...

An example for three users.

Addresses of users 192.168.5.100-192.168.5.102

We add three users to the lists, and only those who really exist. You do not need to score a whole range if you do not use it, because each extra entry will greatly affect performance.

/ip firewall address-list add list="users" address=192.168.5.100
/ip firewall address-list add list="users" address=192.168.5.101
/ip firewall address-list add list="users" address=192.168.5.102


In order not to add lists manually, a small loop was thrown, which will speed up the process of adding ranges. Running this script once will do the same thing as we did with you above.

#Settings
######################################################
:local start ("100");
:local stop ("102");
:local net ("192.168.5.");
######################################################

######################################################
:global count ($start);
:for count from=$start to=$stop step=1 do={
/ip firewall address-list add list="users" address=( $net . $count);};
######################################################

######################################################
# (C) Inlarion icq 429-587 mikrotik.axiom-pro.ru Copyright!
######################################################


The second step is to add entries to the / queue and / ip firewall mangle, again to free the administrator from the chore of adding entries manually, which is fraught with errors since the human factor does not sleep, a small script was thrown. This script needs to be run every time you add or delete entries in / ip firewall address-list list = "users", its main task is to add new entries and delete unnecessary old ones. forgot to delete. After the script ends, information about the number of added and deleted records will be recorded in the system log.

#Settings
######################################################
:local DownloadParent ("Download");
:local UploadParent ("Upload");
######################################################

#Internal Var
######################################################
:local i;
:local z;
:local userX;
:local enum (" ");
:local mark;
:local qrd;
:local qru;
:local mrd;
:local mru;
:local qrdadd;
:local qruadd;
:local mrdadd;
:local mruadd;
:set qrd (0);
:set qru (0);
:set mrd (0);
:set mru (0);
:set qrdadd (0);
:set qruadd (0);
:set mrdadd (0);
:set mruadd (0);
######################################################

######################################################
:log warning ("Rules Manager Started!");

:if ([/queue type find name="dshaper_down"] = "") do={ /queue type add name="dshaper_down" kind="pcq" pcq-classifier=dst-address pcq-rate=0 pcq-limit=50 pcq-total-limit=2000;};

:if ([/queue type find name="dshaper_up"] = "") do={ /queue type add name="dshaper_up" kind="pcq" pcq-classifier=src-address pcq-rate=0 pcq-limit=50 pcq-total-limit=2000;};

:if ([/queue tree find name=$DownloadParent] = "") do={ /queue tree add name=$DownloadParent parent="global-out" queue="dshaper_down" priority=8;};
:if ([/queue tree find name=$UploadParent] = "") do={ /queue tree add name=$UploadParent parent="global-out" queue="dshaper_up" priority=8;};

:foreach i in=[/ip firewall address-list find list="users"] do={ :set userX [/ip firewall address-list get $i address];

:if ([/queue tree find name=($userX . "_down")] = "") do={ /queue tree add name=($userX . "_down") parent=$DownloadParent queue="dshaper_down" packet-mark=($userX . "_down") priority=8; :set qrdadd ($qrdadd+1); };
:if ([/queue tree find name=($userX . "_up")] = "") do={ /queue tree add name=($userX . "_up") parent=$UploadParent queue="dshaper_up" packet-mark=($userX . "_up") priority=8; :set qruadd ($qruadd+1);};

:set enum (" ");
:set enum ([/ip firewall mangle find comment=($userX . "_up")]);
:if ($enum = "") do={ /ip firewall mangle add chain=forward src-address=$userX dst-address=0.0.0.0/0 action=mark-packet new-packet-mark=($userX . "_up") comment=($userX . "_up") disabled=no passthrough=yes; :set mruadd ($mruadd+1);
};

:set enum (" ");
:set enum ([/ip firewall mangle find comment=($userX . "_down")]);
:if ($enum = "") do={ /ip firewall mangle add chain=forward src-address=0.0.0.0/0 dst-address=$userX action=mark-packet new-packet-mark=($userX . "_down") comment=($userX . "_down") disabled=no passthrough=yes; :set mrdadd ($mrdadd+1);
};

};

:foreach z in=[/queue tree find parent=$DownloadParent] do={
:set mark [/queue tree get $z name];
:if ($mark !="") do={
:set mark ([:tostr $mark]);
:set mark ([:pick $mark 0 ([:len $mark]-5)]);
:if ([/ip firewall address-list find address=$mark] = "") do={/queue tree remove [/queue tree find name=($mark . "_down")]; :set qrd ($qrd+1); };};};

:foreach z in=[/queue tree find parent=$UploadParent] do={
:set mark [/queue tree get $z name];
:if ($mark !="") do={
:set mark ([:tostr $mark]);
:set mark ([:pick $mark 0 ([:len $mark]-3)]);
:if ([/ip firewall address-list find address=$mark] = "") do={/queue tree remove [/queue tree find name=($mark . "_up")]; :set qru ($qru+1); };};};

:foreach z in=[/ip firewall mangle find src-address="0.0.0.0/0" action="mark-packet" chain="forward"] do={
:set mark [/ ip firewall mangle get $z comment];
:if ($mark !="") do={
:set mark ([:tostr $mark]);
:set mark ([:pick $mark 0 ([:len $mark]-5)]);
:if ([/ip firewall address-list find address=$mark] = "") do={
:if ([/ip firewall mangle find comment=($mark . "_down")] != "") do={/ip firewall mangle remove [/ip firewall mangle find comment=($mark . "_down")]; :set mrd ($mrd+1); }}}}

:foreach z in=[/ip firewall mangle find dst-address="0.0.0.0/0" action="mark-packet" chain="forward"] do={
:set mark [/ ip firewall mangle get $z comment];
:if ($mark !="") do={
:set mark ([:tostr $mark]);
:set mark ([:pick $mark 0 ([:len $mark]-3)]);
:if ([/ip firewall address-list find address=$mark] = "") do={
:if ([/ip firewall mangle find comment=($mark . "_up")] != "") do={/ip firewall mangle remove [/ ip firewall mangle find comment=($mark . "_up")]; :set mru ($mru+1); }}}}
######################################################

######################################################
:log info ("------------------------------------------");
:log warning ("Rules Manager:");
:log info ("Queue Tree Download Records Added: " . $qrdadd);
:log info ("Queue Tree Upload Records Added: " . $qruadd);
:log info ("Mangle Download Records Added: " . $mrdadd);
:log info ("Mangle Upload Records Added: " . $mruadd);
:log info ("Queue Tree Download Records Deleted: " . $qrd);
:log info ("Queue Tree Upload Records Deleted: " . $qru);
:log info ("Mangle Download Records Deleted: " . $mrd);
:log info ("Mangle Upload Records Deleted: " . $mru);
:log info ("------------------------------------------");
######################################################

######################################################
# (C) Inlarion icq 429-587 mikrotik.axiom-pro.ru Copyright!
######################################################


Now the script is computing and executive part of the scheme


The principle of the script.


The script determines whether night-time support is enabled and checks the current time, selects the channel capacity according to the time range.
The script measures how fast the client is running and, if it exceeds ActiveThresholddown, adds it as an active one to the reception.
also increases the counter active clients to receive.
Next, it checks ActiveThresholdup, if the user has exceeded this mark, then adds it as active for feedback.
also increases the count of active clients on return.

Calculates:
MaxRateDownload divides by the number of active users to receive, displays the speed to the user.
MaxRateUpload divides by the number of active users to return, displays the speed of the user.

Next, it sets a limit to all users in the Queue Tree according to the scheme calculated above.

Next, it calculates the value in kilobits and displays statistics in the log.

Variables in the script at the very beginning:


MaxRateDownload Channel width for all users (reception) Bit / sec.
MaxRateUpload Channel width for all users (return) Bit / sec.
MaxRateDownloadNight Channel width for all users at night (reception) Bit / sec.
MaxRateUploadNight -Width of the channel for all users at night (return) Bit / sec.

ActiveThresholddown - Threshold when exceeded, which the user will be considered active (reception) Bit / sec.
ActiveThresholdup - Threshold when exceeded, which the user will be considered active (return) Bit / sec.

usenighttime can accept the values ​​“yes” and “no”, allows the script to use a different channel width. In accordance with the night rate.
nighttimestart-informs the script of the beginning of the night tariff.
nighttimestop - tells the script the end of the night fare.

#Settings
######################################################
:local MaxRateDownload ("15000000");
:local MaxRateUpload ("15000000");
:local MaxRateDownloadNight ("20000000");
:local MaxRateUploadNight ("20000000");

:local ActiveThresholddown ("15000");
:local ActiveThresholdup ("15000");

:local usenighttime ("yes");
:local nighttimestart ("02:00");
:local nighttimestop ("08:00");
######################################################

#Internal Var
######################################################
:local z;
:local i;
:local ii;
:local userX;
:local timedelay (0);
:local startmin;
:local startsec;
:local stopmin;
:local stopsec;
:local scripttimedelay (0);
:local scriptstartmin;
:local scriptstartsec;
:local scriptstopmin;
:local scriptstopsec;
:local userscount ("0");
:local userstmp ("");
:local firstdowntmp ("");
:local firstuptmp ("");
:local twodowntmp ("");
:local twouptmp ("");
:local activedownuserstmp ("");
:local activeupuserstmp ("");
:local activedowncount ("0");
:local activeupcount ("0");
######################################################

######################################################
:set scriptstartmin ([: pick [/system clock get time] 3 5]);
:set scriptstartsec ([: pick [/system clock get time] 6 8]);

:if ($usenighttime = "yes") do={
:set nighttimestart ([: pick $nighttimestart 0 2] . [: pick $nighttimestart 3 5]);
:set nighttimestop ([: pick $nighttimestop 0 2] . [: pick $nighttimestop 3 5]);
:local currenthours ([: pick [/system clock get time] 0 2]);
:local currenttime ([: pick [/system clock get time] 0 2] . [: pick [/system clock get time] 3 5] );
:local acttime ("day");
:local starttime ("day");
:if ($currenthours < 10) do={ :set acttime ("night"); };
:if ( [: pick $nighttimestart 0 2] < 10) do={ :set starttime ("night"); };
:local night ("no");

:if ($starttime = "night") do={
:if ( $currenttime > $nighttimestart && $currenttime < $nighttimestop) do={ :set night ("yes"); };
};
:if ($starttime = "day") do={
:if ( $acttime = "day") do={
:if ( $currenttime >= $nighttimestart) do={ :set night ("yes"); };
};
:if ( $acttime = "night") do={
:if ( $currenttime < $nighttimestop ) do={ :set night ("yes"); };
};};

:if ($night = "yes") do={
:set MaxRateDownload ($MaxRateDownloadNight);
:set MaxRateUpload ($MaxRateUploadNight);
};
};

:set ActiveThresholddown ($ActiveThresholddown / 8);
:set ActiveThresholdup ($ActiveThresholdup / 8);

:foreach i in=[/ip firewall address-list find list="users"] do={ :set userX [/ip firewall address-list get $i address];
:set userscount ($userscount+1);
:set userstmp ($userstmp . $userX . ",");
};

:local users [:toarray $userstmp];

:set startmin ([: pick [/system clock get time] 3 5]);
:set startsec ([: pick [/system clock get time] 6 8]);

:global dcount ("1");
:for dcount from=1 to=$userscount step=1 do={
:set firstdowntmp ($firstdowntmp . [/ip firewall mangle get [/ip firewall mangle find comment=[:pick $users ($dcount-1)] . "_down"] bytes] . ",");
:set firstuptmp ($firstuptmp . [/ip firewall mangle get [/ip firewall mangle find comment=[:pick $users ($dcount-1)] . "_up"] bytes] . ",");
};

:set stopmin ([: pick [/system clock get time] 3 5]);
:set stopsec ([: pick [/system clock get time] 6 8]);

:global dcount ("1");
:for dcount from=1 to=$userscount step=1 do={
:set twodowntmp ($twodowntmp . [/ip firewall mangle get [/ip firewall mangle find comment=[:pick $users ($dcount-1)] . "_down"] bytes] . ",");
:set twouptmp ($twouptmp . [/ip firewall mangle get [/ip firewall mangle find comment=[:pick $users ($dcount-1)] . "_up"] bytes] . ",");
};

:if ( $stopmin > $startmin) do={
:set timedelay (($stopmin-$startmin) * 60);
};
:set timedelay (($timedelay+$stopsec)-$startsec);

:local firstdown [:toarray $firstdowntmp];
:local firstup [:toarray $firstuptmp];
:local twodown [:toarray $twodowntmp];
:local twoup [:toarray $twouptmp];

:global dcount ("1");
:for dcount from=1 to=$userscount step=1 do={

:if ( ($ActiveThresholddown * $timedelay) < ([:pick $twodown ($dcount-1)] - [:pick $firstdown ($dcount-1)]) ) do={
:set activedownuserstmp ($activedownuserstmp . [:pick $users ($dcount-1)] . ",");
:set activedowncount ($activedowncount+1);
};

:if ( ($ActiveThresholdup * $timedelay) < ([:pick $twoup ($dcount-1)] - [:pick $firstup ($dcount-1)]) ) do={
:set activeupuserstmp ($activeupuserstmp . [:pick $users ($dcount-1)] . ",");
:set activeupcount ($activeupcount+1);
};

};

:local activedownusers [:toarray $activedownuserstmp];
:local activeupusers [:toarray $activeupuserstmp];

:local maxlimitdown ("0");
:local maxlimitup ("0");

:if ( $activedowncount > 0 ) do={
:set maxlimitdown ($MaxRateDownload/$activedowncount);
:global dcount ("1");
:for dcount from=1 to=$activedowncount step=1 do={
:if ([/queue tree get [find name=[:pick $activedownusers ($dcount-1)] . "_down"] max-limit] != $maxlimitdown) do={
/queue tree set [/queue tree find name=[:pick $activedownusers ($dcount-1)] . "_down"] max-limit=$maxlimitdown; };
};
};

:if ( $activeupcount > 0 ) do={
:set maxlimitup ($MaxRateUpload/$activeupcount);
:global dcount ("1");
:for dcount from=1 to=$activeupcount step=1 do={
:if ([/queue tree get [find name=[:pick $activeupusers ($dcount-1)] . "_up"] max-limit] != $maxlimitup) do={
/queue tree set [/queue tree find name=[:pick $activeupusers ($dcount-1)] . "_up"] max-limit=$maxlimitup; };
};
};

:local kbsmaxdown ($MaxRateDownload/1000);
:local kbsmaxup ($MaxRateUpload /1000);

:if ( $maxlimitdown = 0 ) do={ :set maxlimitdown ($MaxRateDownload); };
:if ( $maxlimitup = 0 ) do={ :set maxlimitup ($MaxRateUpload); };
:local kbsmaxlimitdown ($maxlimitdown/1024);
:local kbsmaxlimitup ($maxlimitup/1024);

:set scriptstopmin ([: pick [/system clock get time] 3 5]);
:set scriptstopsec ([: pick [/system clock get time] 6 8]);

:if ( $scriptstopmin > $scriptstartmin) do={
:set scripttimedelay (($scriptstopmin-$scriptstartmin) * 60);
};
:set scripttimedelay (($scripttimedelay+$scriptstopsec)-$scriptstartsec);
######################################################

######################################################
:log info ("------------------------------------------");
:log warning ("Shaper:");
:log info ("MaxRate Download : " . $MaxRateDownload . " bps /" . $kbsmaxdown . " kbs / Upload : " . $MaxRateUpload . " bps /" . $kbsmaxup . " kbs");
:log info ("Active Users : Download : " . $activedowncount . " / Upload : " . $activeupcount );
:log info ("User Speed Download : " . $maxlimitdown . " bps /" . $kbsmaxlimitdown . " kbs / Upload : " . $maxlimitdown . " bps /" . $kbsmaxlimitup . " kbs" );
:log warning ("Performance Time: " . $scripttimedelay . " seconds.");
:log info ("------------------------------------------");
######################################################

######################################################
# (C) Inlarion icq 429-587 mikrotik.axiom-pro.ru Copyright!
######################################################


Regarding the last wishes


In this version, you can notice the addition of a new line in the "Performance Time" system log, this line allows you to correctly set the interval for executing the script in the scheduler. "Performance Time" reflects the runtime of the script in seconds with an accuracy of + -1sec. This time increases dramatically with the number of clients added to the / ip firewall address-list list = “users”, the load on your microtic and the incorrectly set script execution interval, so this parameter must be constantly monitored.

Install the script execution interval based on the formula: Performance Time + 10-15 seconds. When you first run the script, set the interval to 1-2 minutes, in the ideal case, if the system will be maximally loaded at this moment.

Script Parameters:

:local MaxRateDownload ("15000000");
:local MaxRateUpload ("15000000");
:local MaxRateDownloadNight ("20000000");
:local MaxRateUploadNight ("20000000");

It should be set slightly less than the width of your channel in order to avoid brakes when another user is active, who has not consumed the Internet before.

:local ActiveThresholddown ("15000");
:local ActiveThresholdup ("15000");


Set these values ​​according to your needs, but based on the calculation: the number of users multiplied by Thresholddown = should not exceed MaxRateDownload otherwise your users will always be inactive! Naturally users * ActiveThresholdup = should not exceed MaxRateUpload. Always leave some stock.

In addition to everything, I want to note that the question of the inexpediency of using the script in versions 5.x is closed, because After testing version 5.0rc7, I really made sure that / queue in microtic began to work much better compared to earlier versions, so in versions 5.x the script is not needed. But the issue of transition to the fifth version is very doubtful, in view of those bugs that are allowed by the developers. For example, the ping utility works clumsily in rc3 and does not return parameters; in rc7, routing does not work when using names in a table, although in earlier versions everything works with a bang. But this is only RC, although it is already the seventh in a row and this is very worrisome.

Even if you do not need the main part, but you use / queue tree, you can simply use the script to create rules, a very handy thing when adding / deleting users.

Source: https://habr.com/ru/post/111745/


All Articles