Scriptlet: Bulk VPN connections on MikroTik with connection rate limiting

During my day job we use some MikroTik CHR deployments for (among other things) VPN session termination. The CHR’s are easy to spin up, offer a wide variety of VPN types, and for low traffic sessions can support upwards of 10,000 sessions on a single device.

It’s over 9000!

In the event of an outage though, you would run into a problem – those 10,000 sessions all want to re-establish at once.. and the CPU on the MikroTik quickly bottlenecks until it becomes unable to cope and begins to drop connections quickly becoming a vicious cycle.

We initially dealt with this by defining a hard limit on the number of new sessions per second, using 2 simple firewall rules and the connection limit classifier to keep these under 10 per second – however this meant that after an outage it would take at absolute minimum, over 15 minutes for all the sessions to come back online! So we came up with a better solution.

The following script was designed to be run as a scheduler entry every 10 seconds however the frequency and rates can be changed as desired; this version was made for OVPN connections on port 1194.

The firewall filter rules

/ip firewall filter
add action=accept chain=input comment="AUTO RATE LIMIT" connection-state=new dst-port=1194 limit=20,0:packet protocol=tcp
add action=drop chain=input comment="Drop new ovpn connections where rate exceeds the limit" connection-state=new dst-port=1194 protocol=tcp

The script:

#Auto Adjust VPN Rate Limit - recommend running every 10 seconds
:do {
:local minconnlimit "4";
:local maxconnlimit "20";
:local cpulimit "70";

:local ratelimit [/ip firewall filter get [find comment="AUTO RATE LIMIT"] limit]
:set $ratelimit [:pick $ratelimit 0 [:find $ratelimit ","]];
:local cpuload [/system resource get cpu-load];
:put "Ratelimit: $ratelimit CPU: $cpuload";
:if ($cpuload >$cpulimit && $ratelimit > $minconnlimit) do={
/ip firewall filter set [find comment="AUTO RATE LIMIT"] limit=(($ratelimit -1) . ",0:packet");
:log warning "New Rate limit: $ratelimit";
:if ($cpuload <$cpulimit && $ratelimit < $maxconnlimit) do={
/ip firewall filter set [find comment="AUTO RATE LIMIT"] limit=(($ratelimit +1) . ",0:packet");
:log warning ("New Rate limit: " . ($ratelimit +1));

The min and max connection limit define what your minimum allowed connections per second are – there may be times where you expect to see the CPU spike but don’t want to lower your connections per second to (for example) 1! While the Max limit ensures your box has some time to start processing connections at a known viable rate before it needs to ramp back.

This allows your VPN box the ability to handle many clients without concern of overloading or connection loss after session transfer/outage/upgrade or any other such event, while keeping the VM cost low.

Leave a Reply