Interface Data Quota

Hi All,

Was wondering if it possible to set up a quota for an outbound interface?

The situation:

Basically I have two connections set up in failover.. One is hardwired, the other is a 4G connection as backup.
The 4G connection has an 8GB/month ISP quota (additional data is charged per MB), and I really don’t want to exceed it.
I want to set up an outbound interface quota and shaping on the 4G interface, where at 6GB it starts shaping traffic, and at 7.8GB (allowing for usage discrepancies) to deny traffic.
Then I need the quota to reset at the start of each month

Is this possible? Would I do it via queues? Scripting? I’m a bit lost…

Hardwired interface is named “LAN” via ethernet
4G Backup interface is named “BigPondBackup” via USB

let me know if you want any more info and I appreciate the help.. I’m still learning..

I am facing exactly this situation in the implementation of a dual-WAN config for a client of mine.

Since I never ever needed queues I went the scripting way, the outcome of which I like to share with you.
What you see is my initial very basic script, I’m sure there’s much to improve, but I think it’s pretty good for the start.
Some snipplets of this script are taken from other posts here in the forum and altered to my needs.

The first script is scheduled to run every 5 minutes. It collects the traffic of lte1 (sums up tx and rx bytes) and writes this to a file.
I prefer the file method over a global variable because it survives system reboots or interface losses.
As you can see it checks whether the actual data makes sense in context of the data read from the file. If current traffic on the interface is smaller, it gets added to the file content:
:local counter [/interface get lte1 rx-bytes]
:set $counter ($counter + [/interface get lte1 tx-bytes])
:local traffic
:if ([:len [/file find where name=counter.txt]] < 1 ) do={
/file print file=counter.txt where name=counter.txt;
/delay delay-time=2;
/file set counter.txt contents=“0”;
};
:local before value=[/file get counter.txt contents]

:if ($counter > $before) do={
/file set counter.txt contents=$counter
} else= {
:set $traffic ($counter+$before)
/file set counter.txt contents=$traffic
};The second script is run every 15 minutes which checks the traffic usage and issues a warning email at 80% and triggers the stoplte script at 100%:
:local traffic ([/file get traffic.txt contents] / 1000 / 1000 / 1000)
#set the LTE quota in GB
:local limit 30
:local percent ($traffic*100 / $limit)

:if ([:len [/file find where name=warning.txt]] < 1 ) do={
/file print file=warning.txt where name=warning.txt;
/delay delay-time=2;
/file set warning.txt contents=$percent;

:if ($percent < 50) do={
#any action you like - maybe you want to issue daily reports, warnings at 50% or whatsoever
}

} else={
:if ($percent < 79) do={
/file set warning.txt contents=$percent;
:log info “Traffic at $percent%”
} else={
:if (($percent >=80) && ($traffic <$limit)) do={
:if ([/file get warning.txt contents] <> “99”) do={
/tool e-mail send to= subject=“Traffic warning: $percent% reached at $[/system clock get date], $[/system clock get time]” body="The actual traffic amount is $traffic GB \r\nThis is $percent% of the monthly limit\r\n"
/file set warning.txt contents=“99”
:log warning “Warning message sent at $percent%”};
} else={
:if ($percent >= 100) do={
/system script run stoplte
}
}
}
}The stoplte script disables the interface and all related NAT-rules as well as the default route to lte1.
Then automatically the DSL backup kicks in since it still has an active default route (but with a higher distance).

Then I finally have another script, ran daily at midnight which checks if it’s the first of the month and then enables lte1 and all rules/routes, resets the counters and warnings.

Hope that helps a little,
-Chris

OMG wow!

Thanks Chris, you’ve saved my wallet!

Now time to test..

Glad to hear this helps.
In case you modify the script and add new “features” I’d be very interested.

Cheers
-Chris …and thanks for the Karma

Great posting,

I try to build this script on a RB912UAG-2HPnD witth ROS 6.25 and a sierra board as LTE interface.

Unfortunatly i’m not getting any usage data with ; [/interface get lte1 rx-bytes
This works on a normal ether1 interface not on an LTE

Any suggestion how to get rx-bytes tx-bytes in a script from the LTE interface ??

Thanks !

Dear Chris,

First of all I would like to thank you, your script is the first introduction for me with RouterOS (6.38.5) and the language C
Your script was not directly usable at first because of many updates of ROS but helped me tremendously, thanks again for that.
I would like to share the script that I build the last couple of days, I tested it on my router (hEX routerboard);
<as this is firstly commercially sold I removed the information, I will add it later>

Dear Maikel,


Can you already share your script. I would highly appreciate it!

Thanks & best regards,
Peter

Hi maikel,

I would also be very interested in your new and improved script if you’re willing!

Thanks,

Chris



Dear Peter and Chris,
In the end we never really implemented this scripts, but if I’m remembering correctly it should be working. If you would like to know which specifik routerBoard I was programming this for, please ask me.
The scripts are a buch of different ones, one main scripts is executing the other ones;
mainscript freedata-mikrotik.c reads data from interfaces and writes them to files

#This first script is scheduled to run every 5 minutes. It collects the traffic of lte1 (sums up tx and rx bytes)
#and writes different data to multiple files. Kudos to Christopher Diedrich: http://forum.mikrotik.com/t/interface-data-quota/79514/1
#Creator: Maikel Egberink Robacomm.nl
#The following crontab rule is essential for the correct execution of this script:
#
#Please crontab as follow:   */5 * * * *   which lets cron starts this script every 5 min, e.g. 21:40,21:45,21:50,21:55
#
#Get newest tx- and rx-data from interface and put in var $newdata
:local interf "ether2"
:global newdata
:global tx [/interface get $interf tx-byte] 
:put "tx: $tx"
:global rx [/interface get $interf rx-byte] 
:put "rx: $rx"
:set $newdata ( $rx + $tx )
:put "Newdata value: $newdata"
#
#Check if freedata is applicable
:local curtime [/system clock get time]
:put "Current time value: $curtime"
#Example: freedata is from 0:00:00 until 6:00:00 (when changing times please change if-then's too
:local startfreedata 0:00:00
:local beforestartfreedata 23:59:59
:local stopfreedata 6:00:00
:local freedata
#Between freedata-times count freedata
:if ( $curtime >= $startfreedata && $curtime <= $stopfreedata ) do={
	:set $freedata true
	:put "Freedata applicable"
};
#Later than (latest) freedata-times count no freedata. Cannot look before 0:00:00, so always later than 0:00:00
:if ( $curtime > $stopfreedata && $curtime < $beforestartfreedata ) do={
	:set $freedata false
	:put "Freedata NOT applicable"
};
:put "Freedata is set to: $freedata"
#
#Check if files are already created;
:local fileref "refcount.txt"
:local filename "counter.txt"
:local filefree "freecount.txt"
#Check if file refcount.txt not exists, then do (1=exist, 0=not exist)
:if ([:len [/file find where name=$fileref]] < 1 ) do={
	#Create new file
	/file print file=$fileref where name=$fileref;
	#Wait 2 sec
	/delay delay-time=2;
	#Put value 0 in file contents
	/file set $fileref contents=0;
	:put "New file $fileref is created with content 0"
};
#Check if file counter.txt not exists, then do (1=exist, 0=not exist)
:if ([:len [/file find where name=$filename]] < 1 ) do={
	#Create new file
	/file print file=$filename where name=$filename;
	#Wait 2 sec
	/delay delay-time=2;
	#Put value 0 in file contents
	/file set $filename contents=0;
	:put "New file $filename is created with content 0"
}

#Check if file freecount.txt not exists, then do (1=exist, 0=not exist)
:if ([:len [/file find where name=$filefree]] < 1 ) do={
	#Create new file
	/file print file=$filefree where name=$filefree;
	#Wait 2 sec
	/delay delay-time=2;
	#Put value 0 in file contents
	/file set $filefree contents=0;
	:put "New file $filefree is created with content 0"
};
#
:local ref
#Reference file stores the $newdata value on 1st of the month at 0:00 after script 'resetfiles-mikrotik-x.x' ran 
#If there is no value (0) in refcount.txt then put contents to file from var newdata 
#If there is a value in refcount.txt then read it and put it in var ref
:set $ref [/file get $fileref contents]
:put "Reference value from $fileref: $ref"
:if ( $ref = 0 ) do={
	#Write var $newdata to file
	/file set $fileref contents=$newdata
	#Wait 2 sec
	/delay delay-time=2;
	:put "Reference data $newdata put in $fileref"
	:log info "Freedatascripts: Reference data $newdata put in $fileref"
};
#
#When freedata is applicable then count bytes during freedata (not total interface bytes) and write it to freecount.txt.
:local before
:local correct
:local free
#
#Freedata is applicable; write bytes to freecount.txt
#Check if freedata; >0:00 and < 6:00 do
:if ( $freedata = true ) do={
	#:put "if freecount true do"	
	#Read freecount.txt (last freedata)
	:set $free [/file get $filefree contents]
	:put "Previous Free value from $filefree: $free"
	#When newdata is lower than before (e.g. when interface reset) do
	:if ( $newdata < $free ) do={
		#:put "freedata true if newdata < before do"  
		#Correct reference file when counters did reset (to prevent reset value, this way we remember 'old' bytes)
		/file set $fileref contents=$newdata	
		#correct value written to freecount.txt (we suppose that 5 min before also was freedata)
		:set $correct ( $free + $newdata )
		#Write corrected value to freecount.txt
		/file set $filefree contents=$correct
		:put "Writen $correct to $filefree"
	} else={
		#:put "freedata true else newdata < before  do"
	:if ( $newdata >= $free ) do={
		#:put "freedata true if newdata >= before do"  
		#Read reference data and set freedata
		:set $ref [/file get $fileref contents]
		#The var $newdata is now only the actual bytes used from 01-mm-yyyy 0:00:00
		:set $newdata ( $newdata - $ref )
		:put "Variable newdata is now set to new value: $newdata"
		#Newest data from interface - content of counter.txt + contents of freecount.txt
		:set $free ( $newdata - $free )
		#Write updated data to freecount.txt
		/file set $filefree contents=$free
		:put "Written $free to $filefree"
		};
	};
};  
#
#Freedata is not applicable; write bytes to counter.txt
#Check if not freedata; > 6:00 and <23:59 do
:if ( $freedata = false ) do={
	#:put "if freedata false" 	
	#Read contents from file counter.txt (no freetime) and puts it in var $before
	:set $before [/file get $filename contents]
	:put "Previous Before value from $filename: $before"
	#When newdata is lower than before (e.g. when interface reset) do  
	:if ( $newdata < $before ) do={
		#:put "freedata false if newdata < before do"	  
		#Correct reference file
		/file set $fileref contents=$newdata
		#correct value written to counter.txt (to prevent reset value, this way we remember 'old' bytes)
		:set $correct ( $before + $newdata )
		#Write corrected value to counter.txt
		/file set $filename contents=$correct
		:put "Written $correct to $filename"
	} else={
	:if ( $newdata >= $before ) do={ 
		#:put "freedata false if newdata >= before do"
		#Read reference data and set counterdata
		:set $ref [/file get $fileref contents]
		#The var $newdata is now only the actual bytes used from 01-mm-yyyy 0:00:00
		:set $newdata ( $newdata - $ref )
		:put "Variable newdata is now set to new value: $newdata"
		#Write newdata value to counter.txt
		:set $correct ( $newdata - $free )
		/file set $filename contents=$correct
		:put "Written $newdata to $filename"
		}; 
	}; 
};

warning-mikrotik.c checks traffic usage

#This second script is scheduled to run every 15 minutes. It checks the traffic usage and issues a warning email at 80% 
#and triggers the stoplte script at 100%. Kudos to Christopher Diedrich: http://forum.mikrotik.com/t/interface-data-quota/79514/1
#Creator: Maikel Egberink Robacomm.nl
#The following crontab rule is essential for the correct execution of this script:
#
#Please crontab as follow:   */15 * * * *   which lets cron starts this script every 15 min, e.g. 21:30,21:45,22:00,22:15
#
#Wait for script one; freedata-mikrotik0.2  for 5 seconds
:local interf "ether2"
:put "Wait for 5 seconds to finish first script"
/delay delay-time=5
:local filecount "counter.txt"
:local filewarn "warning.txt"
:local traffic ([/file get $filecount contents] / 1024 / 1024 / 1024)
:put "Registered amount of data is now: $traffic GB"
#Set the interface quota in GB;
:local limit 100
:put "Quota is set to: $limit GB"
#Calculate percents
:local percent ($traffic*100 / $limit)
:put "$percent % is now reached until set quota of $limit GB"
#
#Check if file is already created. Check if file warning.txt not exists, then do (1=exist, 0=not exist)
:if ([:len [/file find where name=$filewarn]] < 1 ) do={
	/file print file=$filewarn where name=$filewarn
	/delay delay-time=2
	/file set $filewarn contents=$percent
};
#Write precentage to file
/file set $filewarn contents=$percent
#
#Select action according to percentage reached until quota
:if ($percent < 50) do={
	#any action you like - maybe you want to issue daily reports, warnings at 50% or whatsoever
	:log info "Traffic on $interf at $percent %"
	:log info "Freedatascripts: Traffic on $interf at $percent%"
	/tool e-mail send to=maikel@robacomm.nl subject="Traffic warning: $percent% reached at $[/system clock get date], $[/system clock get time]" body="The actual traffic amount is $traffic GB \r\nThis is $percent% of the monthly limit of $limit GB\r\nThis email is automatically generated by Freedatascript on mikrotik router at CarpeDiem, please do not reply\r\n\"
} else={
     :if ($percent < 79) do={
		#Write percentage to file
        :set $percent [/file get $filewarn contents]
		:put "Traffic on $interf at $percent % (less than 79%)"
        #:log info "Freedatascripts: Traffic on $interf at $percent%"
     } else={
        :if (($percent >=80) && ($traffic < $limit)) do={
			#Send email one time when percentage is over 80% and traffic is not reached quota yet
			:if ([/file get $filewarn contents] <> 99) do={
				#/tool e-mail send to=<youremailaddress> subject="Traffic warning: $percent% reached at $[/system clock get date], $[/system clock get time]" body="The actual traffic amount is $traffic GB \r\nThis is $percent% of the monthly limit\r\n\"
				/file set $filewarn contents=99
				:put "Traffic on $interf at $percent % (80% and over) -99 in file and email sent-"
				:log warning "Freedatascripts: Warning message sent for $interf at $percent%"
				}
		} else={
			:if ($percent >= 100) do={
				/file set $filewarn contents=$percent
				:put "Traffic at $percent % (100% or over) -stop interface-"
				/system script run stoplte
				};
		};
	};
};

reset-files-mikrotik.c resets all counter files every first of the month

#This script ran daily at midnight which checks if it's the first of the month and then enables lte1 (and all rules/routes)
#and resets the counters and warnings. Kudos to Christopher Diedrich: http://forum.mikrotik.com/t/interface-data-quota/79514/1
#Creator: Maikel Egberink Robacomm.nl
#The following crontab rule is essential for the correct execution of this script:
#
#Please crontab as follow:   0 0 * 1 *   which lets cron starts this script every 1st of every month
#
:local interf "ether2"
:local curdate [/system clock get date]
:put "Current date value: $curdate"
:local fileref "refcount.txt"
:local filename "counter.txt"
:local filefree "freecount.txt"
:local filewarn "warning.txt"
:local day [ :pick $curdate 4 6 ];
:put $day
:if ( $day = 1 ) do={
	:put "Today is 1th of the month, resetting all counters..."
	#RAM
	:put "Resetting counters on RAM..."
	:put "Resetting counter for regular data $filename ..."
	/file set $filename contents=0
	:put "Resetting counter for free data $filefree ..."
	/file set $filefree contents=0
	:put "Resetting counter for warnings $filewarn ..."
	/file set $filewarn contents=0
	:put "Resetting counter for reference data $fileref ..."
	/file set $fileref contents=0
	#enable interface, which might be disabled after reaching 100% data-usage
	:put "Enabling interface LTE..."
	#/interface ethernet set $interf disabled=no
	:put "The interface is enabled"
	:log info "Freedatascripts: All counters are reset to 0, interface $interf is enabled"
};
:if ( $day = 13 ) do={
    :log info "Freedatascripts: test 00:00:01 13th day"
};

stoplte.c stops the interface lte

#This script stops interface when the quota is reached (100%)
#Change the interfacename here;
:local interf "ether2"
#Uncomment the following rules for running scripts in production;
#/interface ethernet set $interf disabled=yes
#:put "The interface is disabled"
#:log warning "Interface $interf is DISABLED due to reached datalimit"

readfiles.c helpscript to run manually so you can read all files at once

:local filename counter.txt
:local fileref refcount.txt
:local filefree freecount.txt
:local filewarn warning.txt
#:put "-- Reference point made when the freedatascripts started running (1st of the month) --"
:put "-- Referentie/Ijkpunt gemaakt wanneer de freedatascripts zijn gestart (1ste vd maand) --"
:set $ref [/file get $fileref contents]
#:put "Reference value from $fileref: $ref in bytes"
:put "Referentie waarde vanuit $fileref: $ref in bytes"
#:put "-- Registered amount of data during NON-free data times --"
:put "-- Geregistreerde hoeveelheid data tijdens NIET-vrije data tijden --"
:set $before [/file get $filename contents]
#:put "Counted bytes value from $filename: $before"
:put "Getelde bytes waarde vanuit $filename: $before"
:local beforemb ( $before / 1024 / 1024 )
#:put "Counted Megabytes value from $filename: $beforemb"
:put "Getelde Megabytes waarde vanuit $filename: $beforemb"
:local beforegb ( $before / 1024 / 1024 / 1024 )
:put "Getelde Gigabytes waarde vanuit $filename: $beforegb"
#:put "-- Registered amount of data during FREE data times --"
:put "-- Geregistreerde hoeveelheid data tijdens VRIJE data tijden --"
:set $free [/file get $filefree contents]
#:put "Counted bytes value from $filefree: $free"
:put "Getelde bytes waarde vanuit $filefree: $free"
:local freemb ( $free / 1024 / 1024 )
#:put "Counted Megabytes value from $filename: $freemb"
:put "Getelde Megabytes waarde vanuit $filename: $freemb"
:local freegb ( $free / 1024 / 1024 / 1024 )
#:put "Counted Gigabytes value from $filename: $freegb"
:put "Getelde Gigabytes waarde vanuit $filename: $freegb"
#:put "-- Amount of GB when interface has to be switched off --"
:put "-- Waarde in GB wanneer de interface uitgeschakeld moet worden --"
#:put "The limit is set to: $limit GB"
:put "De limiet is ingesteld op: $limit GB"
:set $percent [/file get $filewarn contents]
#:put "Counted traffic data is now at $percent %"
:put "Getelde traffic data is nu op $percent %"

Hope it helps, it was a lot of fun program this, so thanks again Chris. If you have any questions please let me know
Maikel

Hi Maikel,

thanks so much for sharing! :slight_smile:
I will try to implement it in the wAP LTE kit counting LTE traffic.

Best regards,
Peter

Very welcome

Hello

For LTE:


#   /$$    /$$$$$$$$/$$$$$$$$ /$$       /$$               /$$   /$$                        
#  | $$   |__  $$__/ $$_____/| $$      |__/              |__/  | $$                        
#  | $$      | $$  | $$      | $$       /$$ /$$$$$$/$$$$  /$$ /$$$$$$    /$$$$$$   /$$$$$$ 
#  | $$      | $$  | $$$$$   | $$      | $$| $$_  $$_  $$| $$|_  $$_/   /$$__  $$ /$$__  $$
#  | $$      | $$  | $$__/   | $$      | $$| $$ \ $$ \ $$| $$  | $$    | $$$$$$$$| $$  \__/
#  | $$      | $$  | $$      | $$      | $$| $$ | $$ | $$| $$  | $$ /$$| $$_____/| $$      
#  | $$$$$$$$| $$  | $$$$$$$$| $$$$$$$$| $$| $$ | $$ | $$| $$  |  $$$$/|  $$$$$$$| $$      
#  |________/|__/  |________/|________/|__/|__/ |__/ |__/|__/   \___/   \_______/|__/      
#  
#  Using: Set all var in User config section. Add scripts to schedule with onstart and interval 30 sek       
:do {              
#######################################################################################################
#                                              User config
#Set limit in MB                                                                                                                                                     
:local limit 12288 
#Set day to reset limit
:local dayresetlimit 1
#Set interface
:local interf "lte1"
#Set auto bring up interface 
:local autointon true
#######################################################################################################
:local filename "counter.txt"
:local filewarn "warning.txt"
:local fileref "refcount.txt"
:local reblock "reblock.txt"
:local status
:if ([:len [/file find where name=$filewarn]] < 1 ) do={
	/file print file=$filewarn where name=$filewarn
	/delay delay-time=2
	/file set $filewarn contents=$percent
};
:if ([:len [/file find where name=$filename]] < 1 ) do={
	/file print file=$filename where name=$filename
	/delay delay-time=2;
	/file set $filename contents=0
}
:if ([:len [/file find where name=$reblock]] < 1 ) do={
	/file print file=$reblock where name=$reblock
	/delay delay-time=2;
	/file set $reblock contents=1
}
:if ([:len [/file find where name=$fileref]] < 1 ) do={
	/file print file=$fileref where name=$fileref
	/delay delay-time=2;
	/file set $fileref contents=0
}
:local reblocker ([/file get $reblock contents])
:local curdate [/system clock get date]
:local day [ :pick $curdate 4 6 ]
:if ( $day = $dayresetlimit and $reblocker = 0) do={
	/file set $filename contents=0
	/file set $filewarn contents=0
	/file set $fileref contents=0
	/interface lte set $interf disabled=no
	/file set $reblock contents=1
	:log info "Limit reset, int $interf bring up" 
	/delay delay-time=5;
	:log info "Clear int counters - reboot"
	#TODO find another method for interf clear counters 
        /system reboot
} 
:if ( $day != $dayresetlimit ) do={
        /file set $reblock contents=0
}
:local before
:local correct
:local ref
:local newdata
:local tx [/interface get $interf tx-byte] 
:local rx [/interface get $interf rx-byte] 
:set $newdata (( $rx + $tx ) / 1024 / 1024 )
:set $before [/file get $filename contents]
:if ( $newdata < $before ) do={
		:set $ref [/file get $fileref contents]
		:set $correct ( $before + $newdata - $ref ) 
		/file set $fileref contents=$newdata
		/file set $filename contents=$correct
	} 
:if ( $newdata >= $before ) do={
		/file set $filename contents=$newdata
}
:local traffic ([/file get $filename contents])
:log warning "Limit: $limit MB"
:local percent ($traffic*100 / $limit)
:log warning "Use: $percent%"
/file set $filewarn contents=$percent
:if ($percent >= 100) do={
	/file set $filewarn contents=$percent
	/interface lte set $interf disabled=yes
 }
:if ($percent < 100 and $autointon = true) do={
             :set $status [/interface get $interf value-name=disabled] 
             :if ($status = true) do={
             /interface lte set $interf disabled=no
              }
}
} on-error={ :log error "Error LTELimiter "};



for PPP-Client


#   /$$    /$$$$$$$$/$$$$$$$$ /$$       /$$               /$$   /$$                        
#  | $$   |__  $$__/ $$_____/| $$      |__/              |__/  | $$                        
#  | $$      | $$  | $$      | $$       /$$ /$$$$$$/$$$$  /$$ /$$$$$$    /$$$$$$   /$$$$$$ 
#  | $$      | $$  | $$$$$   | $$      | $$| $$_  $$_  $$| $$|_  $$_/   /$$__  $$ /$$__  $$
#  | $$      | $$  | $$__/   | $$      | $$| $$ \ $$ \ $$| $$  | $$    | $$$$$$$$| $$  \__/
#  | $$      | $$  | $$      | $$      | $$| $$ | $$ | $$| $$  | $$ /$$| $$_____/| $$      
#  | $$$$$$$$| $$  | $$$$$$$$| $$$$$$$$| $$| $$ | $$ | $$| $$  |  $$$$/|  $$$$$$$| $$      
#  |________/|__/  |________/|________/|__/|__/ |__/ |__/|__/   \___/   \_______/|__/      
#  
#  Using: Set all var in User config section. Add scripts to schedule with onstart and interval 30 sek       
:do {              
#######################################################################################################
#                                              User config
#Set limit in MB                                                                                                                                                     
:local limit 12288 
#Set day to reset limit
:local dayresetlimit 1
#Set interface
:local interf "ppp-out1"
#Set auto bring up interface 
:local autointon true
#######################################################################################################
:local filename "counter.txt"
:local filewarn "warning.txt"
:local fileref "refcount.txt"
:local reblock "reblock.txt"
:local status
:if ([:len [/file find where name=$filewarn]] < 1 ) do={
	/file print file=$filewarn where name=$filewarn
	/delay delay-time=2
	/file set $filewarn contents=$percent
};
:if ([:len [/file find where name=$filename]] < 1 ) do={
	/file print file=$filename where name=$filename
	/delay delay-time=2;
	/file set $filename contents=0
}
:if ([:len [/file find where name=$reblock]] < 1 ) do={
	/file print file=$reblock where name=$reblock
	/delay delay-time=2;
	/file set $reblock contents=1
}
:if ([:len [/file find where name=$fileref]] < 1 ) do={
	/file print file=$fileref where name=$fileref
	/delay delay-time=2;
	/file set $fileref contents=0
}
:local reblocker ([/file get $reblock contents])
:local curdate [/system clock get date]
:local day [ :pick $curdate 4 6 ]
:if ( $day = $dayresetlimit and $reblocker = 0) do={
	/file set $filename contents=0
	/file set $filewarn contents=0
	/file set $fileref contents=0
	/interface ppp-client set $interf disabled=no
	/file set $reblock contents=1
	:log info "Limit reset, int $interf bring up" 
	/delay delay-time=5;
	:log info "Clear int counters - reboot"
	#TODO find another method for interf clear counters 
        /system reboot
} 
:if ( $day != $dayresetlimit ) do={
        /file set $reblock contents=0
}
:local before
:local correct
:local ref
:local newdata
:local tx [/interface get $interf tx-byte] 
:local rx [/interface get $interf rx-byte] 
:set $newdata (( $rx + $tx ) / 1024 / 1024 )
:set $before [/file get $filename contents]
:if ( $newdata < $before ) do={
		:set $ref [/file get $fileref contents]
		:set $correct ( $before + $newdata - $ref ) 
		/file set $fileref contents=$newdata
		/file set $filename contents=$correct
	} 
:if ( $newdata >= $before ) do={
		/file set $filename contents=$newdata
}
:local traffic ([/file get $filename contents])
:log warning "Limit: $limit MB"
:local percent ($traffic*100 / $limit)
:log warning "Use: $percent%"
/file set $filewarn contents=$percent
:if ($percent >= 100) do={
	/file set $filewarn contents=$percent
	/interface ppp-client set $interf disabled=yes
 }
:if ($percent < 100 and $autointon = true) do={
             :set $status [/interface get $interf value-name=disabled] 
             :if ($status = true) do={
             /interface ppp-client set $interf disabled=no
              }
}
} on-error={ :log error "Error ppp-clientLimiter "};

Hi, I used this excellent LTELimiter script for several weeks on 2 SXT’s. Keeping an eye on the data volumes. It works excellent. Thanks for sharing!

I made it only run every 10 minutes not to damage the flash with too many write operations. The reset of the interface counters in an SXT LTE kit is also done when you disable/enable the interface (or switch between the SIM cards). Yesterday we had a power failure, and the counters that have been incremented over a long time, were reduced to some value of an earlier interface reset. The calculation of the “correct” counter value is mistakenly subtracting the “ref” value in the case of a counter reset.. Anything accumulated in this period is subtracted. So I modified the script to avoid this substraction of the accumulated value.

The script reads in that lower part now as:

:log info “Clear int counters - reboot”
#TODO find another method for interf clear counters
#/system reboot
#resetting interface will clear counters
/interface lte set $interf disabled=yes
/delay delay-time=2;
/interface lte set $interf disabled=no
/delay delay-time=2;

}
:if ( $day != $dayresetlimit ) do={
/file set $reblock contents=0
}
:local before
:local correct
:local ref
:local newdata
:local tx [/interface get $interf tx-byte]
:local rx [/interface get $interf rx-byte]
:set $newdata (( $rx + $tx ) / 1024 / 1024 )
:set $before [/file get $filename contents]

delta count ?

:if ( $newdata < $before ) do={
:set $ref [/file get $fileref contents]
# BPWL by counter reset then zero out old ref value
:if ( $newdata < $ref ) do={
set $ref (0)
}

:set $correct ( $before + $newdata - $ref )
/file set $fileref contents=$newdata
/file set $filename contents=$correct
}

regular count

:if ( $newdata >= $before ) do={
/file set $filename contents=$newdata
}
:local traffic ([/file get $filename contents])
:local percent ($traffic*100 / $limit)
:log warning “Limit: $limit MB Traffic: $traffic MB Used: $percent%”
/file set $filewarn contents=$percent
:if ($percent >= 100) do={
/file set $filewarn contents=$percent
/interface lte set $interf disabled=yes
}
:if ($percent < 100 and $autointon = true) do={
:set $status [/interface get $interf value-name=disabled]
:if ($status = true) do={
/interface lte set $interf disabled=no
}
}
} on-error={ :log error "Error LTELimiter "};

Very good this script but it has a problem, I think.
If the router is powered off in the day of dayresetlimit the counter reset never happens. Is it right?

Yes leon84, it has a problem with power failure or reboot.
See my version above with the correction: zero out old ref value

Otherwise: “The calculation of the “correct” counter value is mistakenly subtracting the “ref” value in the case of a counter reset or reboot. Anything accumulated in this period is subtracted. So I modified the script to avoid this substraction of the accumulated value.”

After trying one of the final version of this script, I made some modification to avoid couple of things from the original idea: resetting the router to reset the interface’s counters and writing to file to keep variable values. When using ethernet interface in order to reset the traffic counter you have to reboot the router, as disable/enable does not reset those counters.
In this version I’m using someone’s else idea to keep all variables in the firewall Layer7 protocol section; those values are retained also after router reboot (original post http://forum.mikrotik.com/t/to-retain-variables-values-after-reboot/5848/1).
Then I decided to add a new variable for the offset for when the new month kicks in and I don’t reboot the router and therefore the ethernet counters are still with values.
I have tested it with the latest ROS 6.46 and it seem working fine, even trying to simulate router reboot, new month and so on.
If someone is interested here is the final code, starting from the one on this post; this version is used on ethernet interface, so if you want to use it for LTE then you need to change the /interface command to reflect that.

Cheers,
Armando

:global persistVar do={
	:local varName $1;
	:local varValue $2;
	:local varID [/ip firewall layer7-protocol find name="$varName"];

	:if ([:typeof $varValue] = "nothing") do={
		:if ($varID != "") do={
			:set $varValue [/ip firewall layer7-protocol get $varID value-name=regexp];
		}
	} else={
		:if ($varID = "") do={
			/ip firewall layer7-protocol add name="$varName" regexp="$varValue"
		} else={
			/ip firewall layer7-protocol set $varID regexp="$varValue"
		}
	}

return $varValue
}

:do {              
####################################################################################################
# User config
#Set limit in MB                                                                                                                                                     
:local limit 350000;
#Set day to reset limit
:local dayresetlimit "01";
#Set interface
:local interf "ether1-WAN1";
#Set auto bring up interface 
:local autointon true;
####################################################################################################
:local day ([:pick [/system clock get date] 4 6]);
:local newdata;
:local result;
:local ref;
:local status;
:local tx [/interface get $interf tx-byte];
:local rx [/interface get $interf rx-byte];

:set $newdata (($rx + $tx) / 1024 / 1024);

:if ([:len [$persistVar wan_counter]] < 1 ) do={
	$persistVar wan_counter 0;
}

:if ([:len [$persistVar wan_rebootblock]] < 1 ) do={
	$persistVar wan_rebootblock 1;
}

:if ([:len [$persistVar wan_offset]] < 1 ) do={
	$persistVar wan_offset 0;
}

:if ([:len [$persistVar wan_refdata]] < 1 ) do={
	$persistVar wan_refdata 0;
}

:local rebootblocker ([$persistVar wan_rebootblock]);

:if ( $day = $dayresetlimit and $rebootblocker = 0) do={
	$persistVar wan_counter 0;
	$persistVar wan_offset $newdata;
	$persistVar wan_refdata 0;
	/interface ethernet set $interf disabled=no
	$persistVar wan_rebootblock 1;
	:log info "Limit reset, int $interf bring up";
}

:if ( $day != $dayresetlimit ) do={
	$persistVar wan_rebootblock 0;
}

:local before [$persistVar wan_counter];
:local offset [$persistVar wan_offset];

:if ( $newdata < $before ) do={
	$persistVar wan_offset 0;
	:set $ref [$persistVar wan_refdata];
	:if ( $newdata < $ref ) do={
		set $ref (0)
	}
	:set $result ($before + $newdata - $ref); 
	$persistVar wan_refdata $newdata;
	$persistVar wan_counter $result;
} else={
	:set $result ($newdata - $offset);
	$persistVar wan_counter $result;
}

:local traffic ([$persistVar wan_counter]);
:local percent ($traffic*100 / $limit);
:log warning "Limit: $limit MB Traffic: $traffic MB Used: $percent%";

:if ($percent >= 100) do={
	/interface ethernet set $interf disabled=yes
 }

:if ($percent < 100 and $autointon = true) do={
	:set $status [/interface get $interf value-name=disabled];
	:if ($status = true) do={
		/interface ethernet set $interf disabled=no
	}
}
} on-error={ :log error "Error WAN1_Limiter "};

Hi Armando,

This is an excellent script and thanks for posting it. I would like to know if it is possible to modify the script to reset the limit every day? I’ve tried a few regex but I’m not getting intended results.

Cheers!

Disregard, I’ve modified Armando script to reset daily at 1am by changing line 33 from:

:local day ([:pick [/system clock get date] 4 6]);

to:

:local day ([:pick [/system clock get time] 0 2]);

Hope this helps!

Hi Armando,

thank you, the script works perfect for my LTE interface.

Would you be able/willing to explain what the 3 other L7 variables do?
I tried to understand from your script, but can’t get their meaning…:

  • wan-offset ?
  • wan-rebootlock ?
  • wan-refdata ?

Again, thank you very much for your script :slight_smile:

Dear all,

I’m new to Mikrotik, this forum was of great help. Thanks to the community.
Happy to contribute by sharing below my implementation to avoid getting overpriced LTE invoices.

How this works?
1- the counting of the LTE traffic is made via a firewall filter. Main reason here is to avoid file read/write, hence SSD wear + even if firewall counter doesnt survive a reboot. This firewall filter is expected to be the first in the chain, so it really counts all matching traffic. Observe that this firewall filter purposefully excludes the traffic that only goes to the internal IP of the LTE router (or its subnet): the counter hence only includes the traffic that goes through the LTE router and to the internet
2- when the volume is exceeded, the interface is not shut down, instead it’s the NAT which gets disabled. This enables the router to still have service
3- the script runs every minute (via scheduler) and reset the firewall counter at midnight, on the 1st of each month. The script logs the NAT disable/enable action, and logs the counter-resetting
The script uniquely identifies the firewall filter and the nat via their comment


Below is the content


Firewall filter (to count traffic)

add action=passthrough chain=forward comment="Count traffic going to internet through WAN2 (LTE)" dst-address=!172.16.5.0/24 log-prefix="FW fwd passthrough WAN2" out-interface="WAN2"
add action=passthrough chain=forward comment="Count traffic coming from internet through WAN2 (LTE)" dst-address=!172.16.5.0/24 log-prefix="FW fwd passthrough WAN2" in-interface="WAN2"

NAT (this is what gets enabled/disabled on volume excess)

add action=masquerade chain=srcnat comment="NAT to WAN2" ipsec-policy=out,none out-interface="WAN2"

Script wan2DisableNatOnVolumeExcess

# /system script run wan2DisableNatOnVolumeExcess

## Script config
:local firewallFilterOUTComment "Count traffic going to internet through WAN2 (LTE)";
:local firewallFilterINComment "Count traffic coming from internet through WAN2 (LTE)";
:local wan2NatComment "NAT to WAN2";
:local maxVolumeMB 2000

## Script program below

## Function: flip wan2 nat disable state
:local flipWan2NatDisableStatus do={
	
	# get WAN2 nat status
	:local wan2NatDisabledStatus value=[:tostr [/ip firewall nat get value-name=disabled [find comment="$wan2NatComment"]]];
	:local outputLogMessage
	
	# invert the disabled state
	:local wan2NatDisabledSwitch;
	:local wan2NatDisabledSwitchRequired false;
	:if (($wan2NatDisabledStatus=true) && ($maxVolumeThresholdExceeded=false)) do={
		:set wan2NatDisabledSwitchRequired true;
		:set wan2NatDisabledSwitch "no";
		:set outputLogMessage "WAN2 interface volume threshold not exceeded (<$maxVolumeMB MB), enabling NAT";
	}
	:if (($wan2NatDisabledStatus=false) && ($maxVolumeThresholdExceeded=true)) do={
		:set wan2NatDisabledSwitchRequired true;
		:set wan2NatDisabledSwitch "yes";
		:set outputLogMessage "WAN2 interface volume threshold exceeded (>$maxVolumeMB MB), disabling NAT";
		
	}
	if ($wan2NatDisabledSwitchRequired=true) do={
		/ip firewall nat set disabled=[$wan2NatDisabledSwitch] [find comment="$wan2NatComment"];
		:log warning $outputLogMessage
	}
}


# reset filter counter, on the 1st of each month at 00:00
:if (([/system clock get date]~"^[a-zA-Z][a-zA-Z][a-zA-Z]/01/[0-9][0-9][0-9][0-9]") && ([/system clock get time]~"^00:00:[0-9][0-9]")) do={
	/ip firewall filter reset-counters [find comment="$firewallFilterOUTComment"];
	/ip firewall filter reset-counters [find comment="$firewallFilterINComment"];
	:log info "WAN2 firewall filter counter has been reset"
};


# get the counter of traffic gone through WAN2 to internet
:local maxVolumeB value=[:tonum ($maxVolumeMB*1024*1024)]
:local maxVolumeThresholdExceeded
:local firewallFilterOUTVolume value=[:tostr [/ip firewall filter get value-name=bytes [find comment="$firewallFilterOUTComment"]]];
:local firewallFilterINVolume value=[:tostr [/ip firewall filter get value-name=bytes [find comment="$firewallFilterINComment"]]];
:local filrewallFilterTotalVolume ($firewallFilterOUTVolume + $firewallFilterINVolume);
:if ($filrewallFilterTotalVolume > $maxVolumeB) do={
	:set maxVolumeThresholdExceeded true
} else={
	:set maxVolumeThresholdExceeded false
}

## enable/disable nat of WAN2 when traffic below/exceeds threshold
$flipWan2NatDisableStatus wan2NatComment=$wan2NatComment maxVolumeThresholdExceeded=$maxVolumeThresholdExceeded maxVolumeMB=$maxVolumeMB