✂ Rextended Fragments of Snippets

Over the years I have published dozens of scripts,
most need to be updated and are hard to find with standard search.
And they also need to be checked if they are still valid for the new stable v7 version.

I’m slowly re-reading all of my 10000 posts
and I’ll add here all the useful Snippets I found,
with a description and the link to the topic where they are present.

In the future, when I’m done, I’ll index everything for better search.

3 Likes

wireless on off with mode button

create directory and subdirectory

mac ping results saved on variable

Put snmp-get value on variable

Convert character to uppercase or lowercase

UNIX time Epoch, day of the week, ISO:8601 date

Import IP or IP prefix from file

How to download only one piece of file at a time with /tool fetch and put it inside a variable

Decode USSD hexstr2chrstr hexadecimal string to char string

hex2chr hexadecimal to char

definitive ip or ip-prefix posix regex

auto add (starlink) classless route

int2hex num2hex integer number to hexadecimal convert function

check host status

dual wan A-B-C failover

dual dhcp wan failover

How to really block invalid ICMP, TCP, UDP packets and others (ver. 2021)
https://forum.mikrotik.com/viewtopic.php?f=9&t=83387&p=867386

comma separated value of interface list

num2month mon2MON mon2num number month

short address list ip address aggregator (to do)

send whatsapp notification

asynchronous script execution

enable x64 mode on RouterOS x86 installed on x64 (virtual or not) machine

random number generator (without OTP frills)

default firewall rules

convert string to ip-prefix

dynamic variables

manage fetch errors

sort array, sort file by date, date2ymd

ip2array ip split octet

DHCPv6 option 39 fqdn2encdns FQDN to DNS encoding DNS encoder

Save and Restore global variables on reboot

Save RouterBOARD full backup of everything [configuration, certificates, host key, router users (no passwords), licence, user-manager, dude, other files]

Online function to obtain the start date and time of the RouterBOARD.
It takes into account the time zone set in the RouterBOARD.

Convert float to Celsius for TG-BT5-OUT

Convert the date string (May|may)/23/2022 to number 20220523
(the "date" type on RouterOS do not exist)

From 2014: I forgot that on print and on find is possible to do some operations insde search string...
like (just an example)

/ip address print where (network + 1) = 192.168.88.1

SMS GSM alphabet

TAG # lastcheck (25 Mar 2014, 13:34)

TAG: ###RCHCK###

Check if the URL is valid and read server response and redirects:

Create a log file and dynamically split the file every 4095 Bytes or less

Obtain some info from one IP, convert subnet mask to prefix, convert prefix to subnet mask, convert IP to num, convert num to IP, convert IP to HEX, convert HEX to IP etc.

If the IP have (do not have) subnet mask different from /32 (255.255.255.255)
other useful info can be obtained from this information.
(MikroTik actually do not support directly /31 addresses)

For example if you have this 10.31.42.56/16

{
:local source    10.31.42.56/16

:local ip        [:toip [:pick $source 0 [:find $source "/"]]]
:local prefix    [:tonum [:pick $source ([:find $source "/"] + 1) [:len $source]]]
:local submask   (255.255.255.255<<(32 - $prefix))
:local addrspace (~$submask)
:local totip     ([:tonum $addrspace] + 1)
:local network   ($ip & $submask)
:local broadcast ($ip | $addrspace)
:local first     (($network     + 1) - ($prefix / 31))
:local last      (($broadcast   - 1) + ($prefix / 31))
:local usable    (($last - $network) + ($prefix / 31))
:put "       Source: $source"
:put "           IP: $ip"
:put "Subnet Prefix: $prefix"
:put "  Subnet Mask: $submask"
:put "Address Space: $addrspace"
:put "    Total IPs: $totip"
:put "  Network* IP: $network"
:put "Broadcast* IP: $broadcast"
:put "    First* IP: $first"
:put "     Last* IP: $last"
:put "  Usable* IPs: $usable"
}
  • = Network / Broadcast / First IP and Last IP are valid only when the IP are distribuited on local LAN,
    instead for routing only, all IP can be used.
    .0 and .255 are perfectly valid IP if are not the network ip or the broadcast address,
    but for compatibility with some end devices that have problems with .0 and .255 outside a /24, is better remove all .0 and all .255 from the IP pools assigned from DHCP Server.

For example if you have this IP 10.31.42.56 and subnet 255.255.0.0

{
:local sourceip    10.31.42.56
:local sourcesub   255.255.0.0

:local ip        [:toip $sourceip]
:local submask   [:toip $sourcesub]
:local addrspace (~$submask)
:local tempsub   [:tonum $addrspace]
:local prefix    32
:while ($tempsub > 0) do={:set tempsub ($tempsub / 2); :set prefix ($prefix - 1)}
:local totip     ([:tonum $addrspace] + 1)
:local network   ($ip & $submask)
:local broadcast ($ip | $addrspace)
:local first     (($network     + 1) - ($prefix / 31))
:local last      (($broadcast   - 1) + ($prefix / 31))
:local usable    (($last - $network) + ($prefix / 31))
:put "    Source IP: $ip"
:put "  Source Mask: $submask"
:put "Subnet Prefix: $prefix"
:put "Address Space: $addrspace"
:put "    Total IPs: $totip"
:put "  Network* IP: $network"
:put "Broadcast* IP: $broadcast"
:put "    First* IP: $first"
:put "     Last* IP: $last"
:put "  Usable* IPs: $usable"
}

Convert IP to decimal number: (127.0.0.1 = 2130706433)

:put [:tonum 127.0.0.1]

Convert decimal number to IP: (2130706433 = 127.0.0.1)

:put (0.0.0.0 + 2130706433)

Convert IP to hexadecimal number
Using this:

The IP can be converted first to decimal, then to hexadecimal.

:put [$num2hex [:tonum 127.0.0.1]]

Convert hexadecimal number to IP

:put (0.0.0.0 + 0x7F000001)

Script for convert codepage or replace characters inside a string

decimal 2 binary (only 1 BYTE)

:global dec2bin do={
    :local number [:tonum $1]
    :local b8 0 ; :if ($number & 128) do={:set b8 1}
    :local b7 0 ; :if ($number &  64) do={:set b7 1}
    :local b6 0 ; :if ($number &  32) do={:set b6 1}
    :local b5 0 ; :if ($number &  16) do={:set b5 1}
    :local b4 0 ; :if ($number &   8) do={:set b4 1}
    :local b3 0 ; :if ($number &   4) do={:set b3 1}
    :local b2 0 ; :if ($number &   2) do={:set b2 1}
    :local b1 0 ; :if ($number &   1) do={:set b1 1}
    :return "$b8$b7$b6$b5$b4$b3$b2$b1"
}

see here for version from 0 to 9.223.372.036.854.775.807 auto BYTE / WORD / DWORD / QWORD version:

hexadecimal 2 binary (only 1 byte) expected as 0x00 ... 0xFF or 00... FF value:

:global hex2bin do={
    :local conv $1
    :if (!($conv~"(^0x|^)[0-9a-fA-F]{2}\$")) do={:return "00000000"}
    :if ([:typeof [:find $conv "0x" -1]] = "nil") do={:set conv "0x$conv"}
    :local number [:tonum $conv]
    :local b8 0 ; :if ($number & 128) do={:set b8 1}
    :local b7 0 ; :if ($number &  64) do={:set b7 1}
    :local b6 0 ; :if ($number &  32) do={:set b6 1}
    :local b5 0 ; :if ($number &  16) do={:set b5 1}
    :local b4 0 ; :if ($number &   8) do={:set b4 1}
    :local b3 0 ; :if ($number &   4) do={:set b3 1}
    :local b2 0 ; :if ($number &   2) do={:set b2 1}
    :local b1 0 ; :if ($number &   1) do={:set b1 1}
    :return "$b8$b7$b6$b5$b4$b3$b2$b1"
}

see here for version from 0 to 9.223.372.036.854.775.807 auto BYTE / WORD / DWORD / QWORD version:

binary to decimal (only 8 bit / 1 byte) from 00000000 to 11111111

:global bin2dec do={
    :local bin $1
    :local dec  0
    :local mol  1
    :if (!($bin~"^[0-1]{8}\$")) do={:return 0}
    :for pos from=1 to=8 do={
        :local temp [:tonum [:pick $bin (8 - $pos) (8 - $pos + 1)]]
        :set dec ($dec + ($temp * $mol))
        :set mol ($mol * 2)
    }
    :return $dec
}

binary to hexadecimal (only 8 bit / 1 byte) from 00000000 to 11111111
the output is on 0x00 … 0xFF format, for remove the 0x simplu remove 0x on last “:return”

:global bin2hex do={
    :local bin $1
    :local dec  0
    :local mol  1
    :if (!($bin~"^[0-1]{8}\$")) do={:return "0x00"}
    :for pos from=1 to=8 do={
        :local temp [:tonum [:pick $bin (8 - $pos) (8 - $pos + 1)]]
        :set dec ($dec + ($temp * $mol))
        :set mol ($mol * 2)
    }
    :local hexadec   "0"
    :local remainder 0
    :local hexChars  "0123456789ABCDEF"
    :if ($dec > 0) do={:set hexadec ""}
    :while ( $dec > 0 ) do={
          :set remainder ($dec % 16)
          :set dec       (($dec-$remainder) / 16)
          :set hexadec   ([:pick $hexChars $remainder].$hexadec)
    } 
    :if ([:len $hexadec] = 1) do={:set hexadec "0$hexadec"}
    :return "0x$hexadec"
}

this is the version of binary to decimal that support binary signed QWORD (64 bit) and is the max supported from RouterOS

:global binQW2dec do={
    :local bin $1
    :local dec  0
    :local mol  1
    :local lgt [:len $bin]
    :if (!($bin~"^[0-1]{$lgt}\$")) do={:return 0}
    :for pos from=1 to=$lgt do={
        :local temp [:tonum [:pick $bin ($lgt - $pos) ($lgt - $pos + 1)]]
        :set dec ($dec + ($temp * $mol))
        :set mol ($mol * 2)
    }
    :return $dec
}

this is the version of binary to hexadecimal that support binary signed QWORD (64 bit) and is the max supported from RouterOS

:global binQW2hex do={
    :local bin $1
    :local dec  0
    :local mol  1
    :local lgt [:len $bin]
    :if (!($bin~"^[0-1]{$lgt}\$")) do={:return "0x00"}
    :for pos from=1 to=$lgt do={
        :local temp [:tonum [:pick $bin ($lgt - $pos) ($lgt - $pos + 1)]]
        :set dec ($dec + ($temp * $mol))
        :set mol ($mol * 2)
    }
    :local firstchar ""
    :if ($dec < 0) do={
        :local chk (($dec & 0x7000000000000000) >> 60)
        :set firstchar [:pick "89ABCDEF" $chk ($chk + 1)]
        :set dec ($dec & 0x0FFFFFFFFFFFFFFF)
    }
    :local hexadec   "0"
    :local remainder 0
    :local hexChars  "0123456789ABCDEF"
    :if ($dec > 0) do={:set hexadec ""}
    :while ( $dec > 0 ) do={
          :set remainder ($dec % 16)
          :set dec       (($dec-$remainder) / 16)
          :set hexadec   ([:pick $hexChars $remainder].$hexadec)
    } 
    :if ($firstchar != "") do={
        :set hexadec "00000000000000$hexadec"
        :set hexadec "$firstchar$[:pick $hexadec ([:len $hexadec] - 15) [:len $hexadec]]"
    }
    :return "0x$hexadec"
}

for convert decimal to hexadecimal (not only one byte) can be used:

for convert hexadecimal to decimal, on RouterOS already exist this two methods (0x must be present):

:put 0xFF85

:put [:tonum "0xFF85"]

but this can be used (not only one byte):

:global hex2dec do={
    :local conv $1
    :if (!($conv~"^[0-9a-fA-F]+\$")) do={:return 0}
    :if ([:typeof [:find $conv "0x" -1]] = "nil") do={:set conv "0x$conv"}
    :return [:tonum $conv]
}

WARNING: RouterOS have a bug that do not recognize negative hex values, on future I mod the previous script to support QWORD hex conversion

Convert string from ASCII 8-bit CP1252 to UNICODE entry points

:global ASCIItoCP1252toUNICODE do={
    :local ascii "\00\01\02\03\04\05\06\07\08\09\0A\0B\0C\0D\0E\0F\
                  \10\11\12\13\14\15\16\17\18\19\1A\1B\1C\1D\1E\1F\
                  \20\21\22\23\24\25\26\27\28\29\2A\2B\2C\2D\2E\2F\
                  \30\31\32\33\34\35\36\37\38\39\3A\3B\3C\3D\3E\3F\
                  \40\41\42\43\44\45\46\47\48\49\4A\4B\4C\4D\4E\4F\
                  \50\51\52\53\54\55\56\57\58\59\5A\5B\5C\5D\5E\5F\
                  \60\61\62\63\64\65\66\67\68\69\6A\6B\6C\6D\6E\6F\
                  \70\71\72\73\74\75\76\77\78\79\7A\7B\7C\7D\7E\7F\
                  \80\81\82\83\84\85\86\87\88\89\8A\8B\8C\8D\8E\8F\
                  \90\91\92\93\94\95\96\97\98\99\9A\9B\9C\9D\9E\9F\
                  \A0\A1\A2\A3\A4\A5\A6\A7\A8\A9\AA\AB\AC\AD\AE\AF\
                  \B0\B1\B2\B3\B4\B5\B6\B7\B8\B9\BA\BB\BC\BD\BE\BF\
                  \C0\C1\C2\C3\C4\C5\C6\C7\C8\C9\CA\CB\CC\CD\CE\CF\
                  \D0\D1\D2\D3\D4\D5\D6\D7\D8\D9\DA\DB\DC\DD\DE\DF\
                  \E0\E1\E2\E3\E4\E5\E6\E7\E8\E9\EA\EB\EC\ED\EE\EF\
                  \F0\F1\F2\F3\F4\F5\F6\F7\F8\F9\FA\FB\FC\FD\FE\FF"
    :local CP1252toUNICODE {"0000";"0001";"0002";"0003";"0004";"0005";"0006";"0007";"0008";"0009";"000A";"000B";"000C";"000D";"000E";"000F";
                         "0010";"0011";"0012";"0013";"0014";"0015";"0016";"0017";"0018";"0019";"001A";"001B";"001C";"001D";"001E";"001F";
                         "0020";"0021";"0022";"0023";"0024";"0025";"0026";"0027";"0028";"0029";"002A";"002B";"002C";"002D";"002E";"002F";
                         "0030";"0031";"0032";"0033";"0034";"0035";"0036";"0037";"0038";"0039";"003A";"003B";"003C";"003D";"003E";"003F";
                         "0040";"0041";"0042";"0043";"0044";"0045";"0046";"0047";"0048";"0049";"004A";"004B";"004C";"004D";"004E";"004F";
                         "0050";"0051";"0052";"0053";"0054";"0055";"0056";"0057";"0058";"0059";"005A";"005B";"005C";"005D";"005E";"005F";
                         "0060";"0061";"0062";"0063";"0064";"0065";"0066";"0067";"0068";"0069";"006A";"006B";"006C";"006D";"006E";"006F";
                         "0070";"0071";"0072";"0073";"0074";"0075";"0076";"0077";"0078";"0079";"007A";"007B";"007C";"007D";"007E";"007F";
                         "20AC";"FFFD";"201A";"0192";"201E";"2026";"2020";"2021";"02C6";"2030";"0160";"2039";"0152";"FFFD";"017D";"FFFD";
                         "FFFD";"2018";"2019";"201C";"201D";"2022";"2013";"2014";"02DC";"2122";"0161";"203A";"0153";"FFFD";"017E";"0178";
                         "00A0";"00A1";"00A2";"00A3";"00A4";"00A5";"00A6";"00A7";"00A8";"00A9";"00AA";"00AB";"00AC";"00AD";"00AE";"00AF";
                         "00B0";"00B1";"00B2";"00B3";"00B4";"00B5";"00B6";"00B7";"00B8";"00B9";"00BA";"00BB";"00BC";"00BD";"00BE";"00BF";
                         "00C0";"00C1";"00C2";"00C3";"00C4";"00C5";"00C6";"00C7";"00C8";"00C9";"00CA";"00CB";"00CC";"00CD";"00CE";"00CF";
                         "00D0";"00D1";"00D2";"00D3";"00D4";"00D5";"00D6";"00D7";"00D8";"00D9";"00DA";"00DB";"00DC";"00DD";"00DE";"00DF";
                         "00E0";"00E1";"00E2";"00E3";"00E4";"00E5";"00E6";"00E7";"00E8";"00E9";"00EA";"00EB";"00EC";"00ED";"00EE";"00EF";
                         "00F0";"00F1";"00F2";"00F3";"00F4";"00F5";"00F6";"00F7";"00F8";"00F9";"00FA";"00FB";"00FC";"00FD";"00FE";"00FF"
                        }
    :local string $1
    :if (([:typeof $string] != "str") or ($string = "")) do={ :return "" }
    :local lenstr [:len $string]
    :local constr ""
    :for pos from=0 to=($lenstr - 1) do={
        :local unicode "0x$($CP1252toUNICODE->[:find $ascii [:pick $string $pos ($pos + 1)] -1])"
        :set constr "$constr$unicode"
    }
    :return $constr
}

:put [$ASCIItoCP1252toUNICODE "test"]

0x81, 0x8F, 0x8D, 0x90, 0x9D (not assigned on CP1252) => 0xFFFD REPLACEMENT CHARACTER �




Convert string from ASCII 8-bit CP1252 to UTF-8 string with each byte escaped with %

:global ASCIItoCP1252toUTF8 do={
    :local ascii "\00\01\02\03\04\05\06\07\08\09\0A\0B\0C\0D\0E\0F\
                  \10\11\12\13\14\15\16\17\18\19\1A\1B\1C\1D\1E\1F\
                  \20\21\22\23\24\25\26\27\28\29\2A\2B\2C\2D\2E\2F\
                  \30\31\32\33\34\35\36\37\38\39\3A\3B\3C\3D\3E\3F\
                  \40\41\42\43\44\45\46\47\48\49\4A\4B\4C\4D\4E\4F\
                  \50\51\52\53\54\55\56\57\58\59\5A\5B\5C\5D\5E\5F\
                  \60\61\62\63\64\65\66\67\68\69\6A\6B\6C\6D\6E\6F\
                  \70\71\72\73\74\75\76\77\78\79\7A\7B\7C\7D\7E\7F\
                  \80\81\82\83\84\85\86\87\88\89\8A\8B\8C\8D\8E\8F\
                  \90\91\92\93\94\95\96\97\98\99\9A\9B\9C\9D\9E\9F\
                  \A0\A1\A2\A3\A4\A5\A6\A7\A8\A9\AA\AB\AC\AD\AE\AF\
                  \B0\B1\B2\B3\B4\B5\B6\B7\B8\B9\BA\BB\BC\BD\BE\BF\
                  \C0\C1\C2\C3\C4\C5\C6\C7\C8\C9\CA\CB\CC\CD\CE\CF\
                  \D0\D1\D2\D3\D4\D5\D6\D7\D8\D9\DA\DB\DC\DD\DE\DF\
                  \E0\E1\E2\E3\E4\E5\E6\E7\E8\E9\EA\EB\EC\ED\EE\EF\
                  \F0\F1\F2\F3\F4\F5\F6\F7\F8\F9\FA\FB\FC\FD\FE\FF"
    :local CP1252toUTF8 {"00";"01";"02";"03";"04";"05";"06";"07";"08";"09";"0A";"0B";"0C";"0D";"0E";"0F";
                         "10";"11";"12";"13";"14";"15";"16";"17";"18";"19";"1A";"1B";"1C";"1D";"1E";"1F";
                         "20";"21";"22";"23";"24";"25";"26";"27";"28";"29";"2A";"2B";"2C";"2D";"2E";"2F";
                         "30";"31";"32";"33";"34";"35";"36";"37";"38";"39";"3A";"3B";"3C";"3D";"3E";"3F";
                         "40";"41";"42";"43";"44";"45";"46";"47";"48";"49";"4A";"4B";"4C";"4D";"4E";"4F";
                         "50";"51";"52";"53";"54";"55";"56";"57";"58";"59";"5A";"5B";"5C";"5D";"5E";"5F";
                         "60";"61";"62";"63";"64";"65";"66";"67";"68";"69";"6A";"6B";"6C";"6D";"6E";"6F";
                         "70";"71";"72";"73";"74";"75";"76";"77";"78";"79";"7A";"7B";"7C";"7D";"7E";"7F";
      "E282AC";"EFBFBD";"E2809A";"C692";"E2809E";"E280A6";"E280A0";"E280A1";"CB86";"E280B0";"C5A0";"E280B9";"C592";"EFBFBD";"C5BD";"EFBFBD";
      "EFBFBD";"E28098";"E28099";"E2809C";"E2809D";"E280A2";"E28093";"E28094";"CB9C";"E284A2";"C5A1";"E280BA";"C593";"EFBFBD";"C5BE";"C5B8";
                         "C2A0";"C2A1";"C2A2";"C2A3";"C2A4";"C2A5";"C2A6";"C2A7";"C2A8";"C2A9";"C2AA";"C2AB";"C2AC";"C2AD";"C2AE";"C2AF";
                         "C2B0";"C2B1";"C2B2";"C2B3";"C2B4";"C2B5";"C2B6";"C2B7";"C2B8";"C2B9";"C2BA";"C2BB";"C2BC";"C2BD";"C2BE";"C2BF";
                         "C380";"C381";"C382";"C383";"C384";"C385";"C386";"C387";"C388";"C389";"C38A";"C38B";"C38C";"C38D";"C38E";"C38F";
                         "C390";"C391";"C392";"C393";"C394";"C395";"C396";"C397";"C398";"C399";"C39A";"C39B";"C39C";"C39D";"C39E";"C39F";
                         "C3A0";"C3A1";"C3A2";"C3A3";"C3A4";"C3A5";"C3A6";"C3A7";"C3A8";"C3A9";"C3AA";"C3AB";"C3AC";"C3AD";"C3AE";"C3AF";
                         "C3B0";"C3B1";"C3B2";"C3B3";"C3B4";"C3B5";"C3B6";"C3B7";"C3B8";"C3B9";"C3BA";"C3BB";"C3BC";"C3BD";"C3BE";"C3BF"
                        }
    :local string $1
    :if (([:typeof $string] != "str") or ($string = "")) do={ :return "" }
    :local lenstr [:len $string]
    :local constr ""
    :for pos from=0 to=($lenstr - 1) do={
        :local utf ($CP1252toUTF8->[:find $ascii [:pick $string $pos ($pos + 1)] -1])
        :local sym ""
        :if ([:len $utf] = 2) do={:set sym "%$[:pick $utf 0 2]" }
        :if ([:len $utf] = 4) do={:set sym "%$[:pick $utf 0 2]%$[:pick $utf 2 4]" }
        :if ([:len $utf] = 6) do={:set sym "%$[:pick $utf 0 2]%$[:pick $utf 2 4]%$[:pick $utf 4 6]" }
        :set constr "$constr$sym"
    }
    :return $constr
}

:put [$ASCIItoCP1252toUTF8 "test"]

0x81, 0x8F, 0x8D, 0x90, 0x9D (not assigned on CP1252) => %EF%BF%BD REPLACEMENT CHARACTER �

The BOM, Byte Order Mark for UTF-8 is “%EF%BB%BF”