lvader
December 11, 2022, 12:23pm
1
Good day.
I’m relatively new to mikrotik scripting, so haven’t found myself good answer and asking for pointers to right direction.
I’m trying to parse as part of dhcp-client lease script variable in lease-options array. Specifically the option 212 (6rd dhcp info).
It is present there as type string, length 22 bytes.
First byte is netmask length, e.g. \0E.
Second byte is 6rd prefix length, like \26
Then, in the end of the option, 4 bytes of ipv4 address of tunnel endpoint.
What I’m looking for, is hint on how to do things like getting byte \0E and convert it to numeric 14? Something like ‘ord()’ function?
Or get 4 bytes from string, and convert them to valid IPv4 address to use further in script.
Any examples of similar script problems?
Real example of option 212 value?
on this example netmask length 14, 6rd prefix length 38 and IPv4 192.168.100.1
:global opt "0E26DEADBEEFDEADBEEFDEADBEEFDEADBEEFC0A86401"
:put [[:parse ":return 0x$[:pick $opt 0 2]"]]
:put [[:parse ":return 0x$[:pick $opt 2 4]"]]
:put (0.0.0.0 + [[:parse ":return 0x$[:pick $opt 36 44]"]])
lvader
December 12, 2022, 9:07am
3
Well, unfortunately that is not that easy. DHCP option comes as raw byte array inside string type, not as hex string representation.
Format of option 212 is like this (values are a bit changed, but should be good enough to get the idea):
:local v212 "\0E\26\20\01\22\02\F0\00\00\00\00\00\00\00\00\00\00\00\44\2B\FF\FE"
First byte is v4 mask length, second byte v6 prefix length. Then 16 bytes of ipv6 prefix. Then 4 bytes of 6to4 6rd tunnel endpoint.
The best that I came up with is something below. Ugly, to my taste, but good enough starting point:
:global string2array 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 string $1
:if (([:typeof $string] != "str") or ($string = "")) do={ :return ({}) }
:local lenstr [:len $string]
:local res ({})
:for pos from=0 to=($lenstr - 1) do={
:local ord ([:find $ascii [:pick $string $pos ($pos + 1)] -1])
:set res ($res, $ord)
}
:return $res
}
:local v212 "\0E\26\20\01\22\02\F0\00\00\00\00\00\00\00\00\00\00\00\44\2B\FF\FE"
:put [$string2array $v212]
:global charsString ""
:for x from=0 to=15 step=1 do={ :for y from=0 to=15 step=1 do={
:local tmpHex "$[:pick "0123456789ABCDEF" $x ($x+1)]$[:pick "0123456789ABCDEF" $y ($y+1)]"
:set $charsString "$charsString$[[:parse "(\"\\$tmpHex\")"]]"
} }
:global chr2int do={
:global charsString
:if (($1="") or ([:len $1] > 1) or ([:typeof $1] = "nothing")) do={:return -1}; :return [:find $charsString $1 -1]
}
:global chr2hex do={
:global chr2int
:local number [:tonum [$chr2int $1]]
:local hexadec "0"
:local remainder 0
:if ($number > 0) do={:set hexadec ""}
:while ( $number > 0 ) do={
:set remainder ($number % 16)
:set number (($number-$remainder) / 16)
:set hexadec ([:pick "0123456789ABCDEF" $remainder].$hexadec)
}
:if ([:len $hexadec] = 1) do={:set hexadec "0$hexadec"}
:return "$hexadec"
}
:global str2hex do={
:global chr2hex
: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={
:set constr "$constr$[$chr2hex [:pick $string $pos ($pos + 1)]]"
}
:return $constr
}
:global str2intarr do={
:global chr2int
:local string $1
:if (([:typeof $string] != "str") or ($string = "")) do={ :return "" }
:local lenstr [:len $string]
:local constr [:toarray ""]
:for pos from=0 to=($lenstr - 1) do={
:set ($constr->$pos) [$chr2int [:pick $string $pos ($pos + 1)]]
}
:return $constr
}
:global ipv6raw2format do={
: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={
:if ((($pos % 4) = 0) and ($pos > 0)) do={
:set constr "$constr:$[:pick $string $pos ($pos + 1)]"
} else={
:set constr "$constr$[:pick $string $pos ($pos + 1)]"
}
}
:return $constr
}
{
:local v212 "\0E\26\20\01\22\02\F0\00\00\00\00\00\00\00\00\00\00\00\44\2B\FF\FE"
:local hexopt [$str2hex $v212]
:put $hexopt
:local arrayopt [$str2intarr $v212]
:put $arrayopt
:put "v4 mask length $($arrayopt->0)"
:put "v6 prefix length $($arrayopt->1)"
:put "IPv6 prefix $[$ipv6raw2format [:pick $hexopt 4 36]]"
:put "6to4 6rd tunnel endpoint $(0.0.0.0 + [[:parse ":return 0x$[:pick $hexopt 36 44]"]])"
}
0E2620012202F00000000000000000000000442BFFFE
14;38;32;1;34;2;240;0;0;0;0;0;0;0;0;0;0;0;68;43;255;254
v4 mask length 14
v6 prefix length 38
IPv6 prefix 2001:2202:F000:0000:0000:0000:0000:0000
6to4 6rd tunnel endpoint 68.43.255.254
2001:2202:f000::/38 = from 2001:2202:f000::/38 to 2001:2202:f3ff:ffff:ffff:ffff:ffff:fffff