Triggering script from a firewall rule

I want to trigger a script from a firewall rule. I understand the risk of causing complete CPU saturation by sending a bazillion script triggers caused by the data..

What i want to do is simple. If I get a DDoS I want to:


add chain=input protocol=tcp connection-limit=10000,32 action=trigger script

The script would change my WAN MAC address and reboot my cable modem. This would give me a new external WAN IP assigned by the cable co and terminate the DDoS by simply changing to a new IP.

I have the script for changing my mac and rebooting the cable modem by using the LED output to control a relay. This all works great.

BUT I need to be able to trigger a script from a firewall rule. Trigger once only too.

Not that I have EVER had a DoS attack, but its fun to know if I did my router would automatically deal with it.

Its also a kinda fun idea that should I detect a port scan or other nefarious activity I could change external WAN IP’s as a precaution.

If you happen to wonder how im changing my MAC on the WAN side pseudo randomly... The scripts are below. These I did NOT write, I got tthem from various places here and elsewhere..


:global prngResult;
:global prngParams;

define char table

:global chArray 0abcdef0123456789abcdef012345abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef012345
:local strPass "";

#Get Random
:set prngParams "1 99 true";
{/system script run Random} ;
:local ch1num 0;

#Get Random
:set prngParams "1 99 false";
{/system script run Random} ;
:local ch2num 0;

#Get Random
:set prngParams "1 99 false";
{/system script run Random} ;
:local ch3num $prngResult;

#Get Random
:set prngParams "1 99 false";
{/system script run Random} ;
:local ch4num $prngResult;

#Get Random
:set prngParams "1 99 false";
{/system script run Random} ;
:local ch5num $prngResult;

#Get Random
:set prngParams "1 99 false";
{/system script run Random} ;
:local ch6num $prngResult;

#Get Random
:set prngParams "1 99 false";
{/system script run Random} ;
:local ch7num $prngResult;

#Get Random
:set prngParams "1 99 false";
{/system script run Random} ;
:local ch8num $prngResult;

#Get Random
:set prngParams "1 99 false";
{/system script run Random} ;
:local ch9num $prngResult;

#Get Random
:set prngParams "1 99 false";
{/system script run Random} ;
:local ch10num $prngResult;

#Get Random
:set prngParams "1 99 false";
{/system script run Random} ;
:local ch11num $prngResult;

#Get Random
:set prngParams "1 99 false";
{/system script run Random} ;
:local ch12num $prngResult;

#Get Random
:set prngParams "1 09 true";
{/system script run Random} ;

generate MAC address

:set strPass ( [:tostr [ :pick $chArray $ch1num ]] . [:tostr [:pick $chArray $ch2num ]] . [:tostr [:pick $chArray ($ch3num+$prngResult) ]] . [:tostr [:pick $chArray ($ch4num*2) ]] . [:tostr [:pick $chArray ($ch5num+16) ]] . [:tostr [:pick $chArray ($ch6num+4) ]] . [:tostr [:pick $chArray ($ch7num+12) ]] . [:tostr [:pick $chArray $ch8num ]] . [:tostr [:pick $chArray $ch9num ]] . [:tostr [:pick $chArray $ch10num ]] . [:tostr [:pick $chArray $ch11num ]] . [:tostr [:pick $chArray $ch12num ]] );
:log info $strPass;
/interface ethernet set ether1 mac-address=$strPass;
led user=yes;
:delay 500ms;
led user=no;

\


The random generator script


Binary Galois LFSR 1/63 bits, periods 0-1..(2^63)-1

based on:

http://en.wikipedia.org/wiki/Linear_feedback_shift_register

http://stackoverflow.com/questions/693880/create-random-number-sequence-with-no-repeats/3181296#3181296

usage: call first time after setting prngParams={low,high,true} for initializing

0 <= Low <= High <= ((2^63)-1)

then keep calling ((high - low) + 1) times with prngParams={low,high,false}

to get the permutated interval one item at a time, in prngResult

when no more numbers are inside the LFSR, prngResult starts to be -1

GLOBALS ---------------------------------------------------------------

debug settings

:global dbgMisc;

params / result prng

:global prngParams;
:global prngResult;

register state

:global prngSeed;
:global prngLFSR;
:global prngSeq;
:global prngMask;

START SCRIPT ----------------------------------------------------

#:set prngParams "1 15 true";

copy params to local scope

:local Low [:tonum [:pick $prngParams 0]];
:local High [:tonum [:pick $prngParams 2 4]];
:local Initialize (!![:pick $prngParams 5 12]);

adjust interval to 0..prngMax

:local prngMax ($High - $Low + 1);

initialization

:if ($Initialize = true) do={

time seed

:local seed1 2;
{
:local time [:tostr [/system clock get time]];
:for i from=0 to=([:len $time] -1) do {
:local char [:pick $time $i ($i+1)];
:if ($char!=":") do={:set seed1 ($seed1 * ([:tonum $char] + 1))};
};
}

firewall counter seed

:local seed2 0
:foreach item in=[/ip firewall filter find bytes>0] do={
:set seed2 ($seed2+[/ip firewall filter get $item bytes]);
};
:foreach item in=[/ip firewall nat find bytes>0] do={
:set seed2 ($seed2+[/ip firewall nat get $item bytes]);
};
:foreach item in=[/ip firewall mangle find bytes>0] do={
:set seed2 ($seed2+[/ip firewall mangle get $item bytes]);
};

uptime seed

:local seed3 2;
{
:local uptime [:tostr [/system resource get uptime]];
:for i from=0 to=([:len $uptime] - 1) do {
:local char [:pick $uptime $i ($i+1)];
:if ($char!=":") do={:set seed3 ($seed3 * ([:tonum $char] + 1))};
};
}

cpu load seed

:local seed4 ([/system resource get cpu-load] * 16);

conntrack seed

:local seed5 "";
{
:local t "";
:local s "";
:local d "";
:foreach connection in=[/ip firewall connection find] do={
:local timeout [:tostr [/ip firewall connection get $connection timeout ]];
:for i from=0 to=([:len $timeout] - 1) do {
:local char [:pick $timeout $i ($i+1)];
:if ($char!=":") do={:set t "$t$char"};
};
:local src [:tostr [/ip firewall connection get $connection src-address ]];
:for i from=0 to=([:len $src] - 1) do {
:local char [:pick $src $i ($i+1)];
:if ([:typeof [:find ":." $char]]="nil") do={:set s "$s$char"};
};
:local dst [:tostr [/ip firewall connection get $connection dst-address ]];
:for i from=0 to=([:len $dst] - 1) do {
:local char [:pick $dst $i ($i+1)];
:if ([:typeof [:find ":." $char]]="nil") do={:set d "$d$char"};
};
};
:set seed5 "$t$s$d";
}

checksum seeds

:local prngPreSeed (($seed1 ^ $seed2 ^ $seed3 ^ $seed4) | 1);
:set prngSeed 0;
:for i from=[:len $seed5] to=0 do={
:local charnum [:tonum [:pick $seed5 $i]];
:set prngSeed ($prngSeed ^ ($prngPreSeed / ($charnum + 1)));
}

:put "prngPreSeed=$prngPreSeed, seed1=$seed1, seed2=$seed2, seed3=$seed3, seed4=$seed4, seed5=$seed5";

:put "prngSeed=$prngSeed";


\

array of arrays with taps for maximal period, indexed by bit width (1..63)

:local tapsfor {
"";
"";
"";
"3,2";
"4,3";
"5,3";
"6,5";
"7,6";
"8,6,5,4";
"9,5";
"10,7";
"11,9";
"12,6,4,1";
"13,4,3,1";
"14,5,3,1";
"15,14";
"16,15,13,4";
"17,14";
"18,11";
"19,6,2,1";
"20,17";
"21,19";
"22,21";
"23,18";
"24,23,22,17";
"25,22";
"26,6,2,1";
"27,5,2,1";
"28,25";
"29,27";
"30,6,4,1";
"31,28";
"32,22,2,1";
"33,20";
"34,27,2,1";
"35,33";
"36,25";
"37,5,4,3,2,1";
"38,6,5,1";
"39,35";
"40,38,21,19";
"41,38";
"42,41,20,19";
"43,42,38,37";
"44,43,18,17";
"45,44,42,41";
"46,45,26,25";
"47,42";
"48,47,21,20";
"49,40";
"50,49,24,23";
"51,50,36,35";
"52,49";
"53,52,38,37";
"54,53,18,17";
"55,31";
"56,55,35,34";
"57,50";
"58,39";
"59,58,38,37";
"60,59";
"61,60,46,45";
"62,61,6,5";
"63,62"
};

determine $prngMax bit width

:local highB $prngMax;
:local bits 0;
:while ($highB > 0) do={
:set highB ($highB >> 1);
:set bits ($bits + 1);
};
:if ($bits < 3) do={ :set bits 3 };

get taps for a $bits wide register

:local taps [:toarray [:tostr [:pick $tapsfor $bits]]];

get feedback function

:set prngMask 0;
:for i from=0 to=([:len $taps]-1) do={
:set prngMask ($prngMask | (1 << ([:tonum [:pick $taps $i]]-1)));
};

init register with the seed

:set prngLFSR $prngSeed;

init seq counter

:set prngSeq 0;
};

if we haven't finalized the sequence

:if ($prngSeq < $prngMax) do={
:do {

shift the register until it's high bit is 1 and then apply the mask

we end up with next seq number

:do {
:set prngLFSR (($prngLFSR >> 1) ^ (-($prngLFSR & 1) & $prngMask));
} while ($prngLFSR > $prngMax);

if our first result is 0 we cheat

:set prngResult ($prngLFSR-1);

:if (($prngSeq=0) && ($prngLFSR=0)) do={
:set prngLFSR ($prngLFSR+1);
};
} while (($prngSeq=0) && ($prngResult=-1));

keep the seq count

:set prngSeq ($prngSeq+1);

move result to the original range

:set prngResult ($prngResult+$Low);
} else={

if we completed the sequence then exit

:set prngResult -1;
};

:put $prngResult;

Make the firewall rule that add to an address list, schedule a script to run ever so often and check if something has been added to that list, if that’s the case, then remove entry (entries) and do your thing.

first add

ip fire filt
add action=add-src-to-address-list address-list=block address-list-timeout=10m chain=input connection-limit=10000,32 disabled=no protocol=tcp

and then add a script

{
local ip [[len [ip firewall address-list fin]]]
if ($ip>0) do={/sys scrit run NameOfScipt}
}

OooOo… Good idea…

THANX !

I will play with this in the next few days :slight_smile: