From 6ad6f9aa08d558ff2e8ff3010fe5daec3c600c4a Mon Sep 17 00:00:00 2001 From: Christian Hesse Date: Thu, 16 Oct 2025 10:30:20 +0200 Subject: [PATCH 1/4] global-functions: introduce $NetMask6 RouterOS does not support bit shifting on IPv6 data types, so we have to split the problem: * each 16 bit block is calculated separately, as number * the complete netmask is assembled in a loop, as string * the final string is casted to correct data type --- global-functions.rsc | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/global-functions.rsc b/global-functions.rsc index 5c98a20..ffa5277 100644 --- a/global-functions.rsc +++ b/global-functions.rsc @@ -62,6 +62,7 @@ :global MIN; :global MkDir; :global NetMask4; +:global NetMask6; :global NotificationFunctions; :global ParseDate; :global ParseKeyValueStore; @@ -998,6 +999,24 @@ :return ((255.255.255.255 << (32 - $CIDR)) & 255.255.255.255); } +# return an IPv6 netmask for CIDR +:set NetMask6 do={ + :local FuncName $0; + :local CIDR [ :tostr $1 ]; + + :global IfThenElse; + :global MAX; + :global MIN; + + :local Mask ""; + :for I from=0 to=7 do={ + :set Mask ($Mask . \ + [ :convert from=num to=hex (0xffff - (0xffff >> [ :tonum [ $MIN [ $MAX ($CIDR - (16 * $I)) 0 ] 16 ] ])) ] . \ + [ $IfThenElse ($I < 7) ":" ]); + } + :return [ :toip6 $Mask ]; +} + # prepare NotificationFunctions array :if ([ :typeof $NotificationFunctions ] != "array") do={ :set NotificationFunctions ({}); From d7a6eb1d0083c1d788e8febedaea67464361d271 Mon Sep 17 00:00:00 2001 From: Christian Hesse Date: Thu, 16 Oct 2025 15:13:14 +0200 Subject: [PATCH 2/4] global-functions: $NetMask6: implement simple caching The calculation is quite complex for something that needs to be done frequently, for example by `fw-addr-lists`. The number of possible netmasks is limited, so let's cache the results that were calculated already. --- global-functions.rsc | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/global-functions.rsc b/global-functions.rsc index ffa5277..5ede654 100644 --- a/global-functions.rsc +++ b/global-functions.rsc @@ -1008,13 +1008,25 @@ :global MAX; :global MIN; + :global NetMask6Cache; + + :if ([ :typeof ($NetMask6Cache->$CIDR) ] = "ip6") do={ + :return ($NetMask6Cache->$CIDR); + } + + :if ([ :typeof $NetMask6Cache ] = "nothing") do={ + :set NetMask6Cache ({}); + } + :local Mask ""; :for I from=0 to=7 do={ :set Mask ($Mask . \ [ :convert from=num to=hex (0xffff - (0xffff >> [ :tonum [ $MIN [ $MAX ($CIDR - (16 * $I)) 0 ] 16 ] ])) ] . \ [ $IfThenElse ($I < 7) ":" ]); } - :return [ :toip6 $Mask ]; + :set Mask [ :toip6 $Mask ]; + :set ($NetMask6Cache->$CIDR) $Mask; + :return $Mask; } # prepare NotificationFunctions array From ea05b69f7cfb1506e24117020a115af2d1b19c4a Mon Sep 17 00:00:00 2001 From: Christian Hesse Date: Thu, 16 Oct 2025 13:02:14 +0200 Subject: [PATCH 3/4] fw-addr-lists: use $NetMask6 --- fw-addr-lists.rsc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fw-addr-lists.rsc b/fw-addr-lists.rsc index 26e041a..c85cc8b 100644 --- a/fw-addr-lists.rsc +++ b/fw-addr-lists.rsc @@ -26,8 +26,8 @@ :global LogPrint; :global LogPrintOnce; :global LogPrintVerbose; - :global MIN; :global NetMask4; + :global NetMask6; :global ScriptLock; :global WaitFullyConnected; @@ -130,13 +130,13 @@ } :if ($Address ~ "^[0-9a-zA-Z]*:[0-9a-zA-Z:\\.]+(/[0-9]{1,3})?\$") do={ :local Net $Address; - :local Cidr 64; + :local CIDR 128; :local Slash [ :find $Address "/" ]; :if ([ :typeof $Slash ] = "num") do={ :set Net [ :toip6 [ :pick $Address 0 $Slash ] ] - :set Cidr [ $MIN [ :pick $Address ($Slash + 1) [ :len $Address ] ] 64 ]; + :set CIDR [ :pick $Address ($Slash + 1) [ :len $Address ] ]; } - :set Address (([ :toip6 $Net ] & ffff:ffff:ffff:ffff::) . "/" . $Cidr); + :set Address (([ :toip6 $Net ] & [ $NetMask6 $CIDR ]) . "/" . $CIDR); :set Branch [ $GetBranch $Address ]; :set ($IPv6Addresses->$Branch->$Address) $TimeOut; :error true; From b80b872e557e2513c7e9ee4c6f119e3ad56d4116 Mon Sep 17 00:00:00 2001 From: Christian Hesse Date: Thu, 16 Oct 2025 17:31:15 +0200 Subject: [PATCH 4/4] mod/ipcalc: support IPv6 Well, some of these values do not make a lot of sense for IPv6... Something to be cleaned up later. --- mod/ipcalc.rsc | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/mod/ipcalc.rsc b/mod/ipcalc.rsc index fecf6f2..d65d472 100644 --- a/mod/ipcalc.rsc +++ b/mod/ipcalc.rsc @@ -36,21 +36,32 @@ :local Input [ :tostr $1 ]; :global NetMask4; + :global NetMask6; - :local Address [ :toip [ :pick $Input 0 [ :find $Input "/" ] ] ]; + :local Address [ :pick $Input 0 [ :find $Input "/" ] ]; :local Bits [ :tonum [ :pick $Input ([ :find $Input "/" ] + 1) [ :len $Input ] ] ]; - :local Mask [ $NetMask4 $Bits ]; + :local Mask; + :local One; + :if ([ :typeof [ :toip $Address ] ] = "ip") do={ + :set Address [ :toip $Address ]; + :set Mask [ $NetMask4 $Bits ]; + :set One 0.0.0.1; + } else={ + :set Address [ :toip6 $Address ]; + :set Mask [ $NetMask6 $Bits ]; + :set One ::1; + } - :local Return { + :local Return ({ "address"=$Address; "netmask"=$Mask; "networkaddress"=($Address & $Mask); "networkbits"=$Bits; "network"=(($Address & $Mask) . "/" . $Bits); - "hostmin"=(($Address & $Mask) | 0.0.0.1); - "hostmax"=(($Address | ~$Mask) ^ 0.0.0.1); + "hostmin"=(($Address & $Mask) | $One); + "hostmax"=(($Address | ~$Mask) ^ $One); "broadcast"=($Address | ~$Mask); - } + }); :return $Return; }