Этот скрипт для MikroTik RouterOS отправляет перед стартом WireGuard/AmneziaWG handshake набор UDP-пакетов на endpoint peer-а:
- пользовательские signature-пакеты из массива
ipackets; - затем
Jcслучайных junk-пакетов размером отJminдоJmax; - после этого peer включается обратно, и обычный WireGuard handshake уходит уже штатным способом.
Скрипт рассчитан на RouterOS v7 и использует /tool traffic-generator inject.
В начале script.rsc:
:local Jc 4
:local Jmin 40
:local Jmax 70
:local TTL 64
:local UDPChecksumMode "calc"
:local i1 "..."
:local i2 ""
:local i3 ""
:local i4 ""
:local i5 ""
:local ipackets {$i1;$i2;$i3;$i4;$i5}Параметры:
Jc- сколько случайных junk-пакетов отправлять.Jmin,Jmax- минимальный и максимальный размер junk payload в байтах.TTL- TTL в вручную собранном IPv4-пакете.UDPChecksumMode:"calc"- считать UDP checksum. Это основной режим."zero"- писать UDP checksum0000. Для IPv4 это разрешено и может помочь в диагностике raw-inject/NAT/offload проблем.
i1..i5- примеры signature-пакетов. Пустое значение пропускается.ipackets- массив пакетов, которые будут отправлены перед junk. Можно расширить его до любого разумного количества элементов:
:local i6 "<r 64>"
:local i7 "<b 0xdeadbeef><t><rd 8>"
:local ipackets {$i1;$i2;$i3;$i4;$i5;$i6;$i7}Также можно писать пакеты прямо в массив:
:local ipackets {"<b 0xdeadbeef>";"<r 64>";"<b 0xcafe><rc 10>"}Скрипт обрабатывает только WireGuard peers с:
comment=BYPASSТо есть нужному peer-у надо поставить comment BYPASS.
Поддерживаются два варианта.
Готовый HEX:
:local i1 "c3000000..."AmneziaWG-like tags:
:local i1 "<b 0xc3000000><t><r 10>"
:local i2 "<b 0xf6ab3267fa><rc 10>"
:local i3 "<b 0xf6ab3267fa><rd 10>"Теги:
<b 0xHEX>- статические bytes.<r N>-Nслучайных bytes.<rc N>-Nслучайных ASCII letters[a-zA-Z].<rd N>-Nслучайных ASCII digits[0-9].<t>- 4-byte Unix timestamp, big-endian, как вamneziawg-go.
Важно: готовый HEX (строка без тегов <...>) минует парсер тегов и идёт прямо в сборку пакета. Это важно для очень больших пакетов: они не прогоняются через посимвольный разбор, а используются как есть.
Для очень больших статических пакетов предпочтительнее raw HEX вместо одиночного <b 0x...>. Если RouterOS/Winbox обрезает слишком длинную строку, разбейте её на части и соберите переменную конкатенацией:
:local i2a "ce0000000108..."
:local i2b "ed38da58ac29..."
:local i2 ($i2a . $i2b)Скрипт проверяет одиночный <b ...> на закрывающий >; если строка была обрезана, такой пакет не будет отправлен частично.
Для каждого peer-а с comment=BYPASS скрипт:
- Проверяет
last-handshake. - Резолвит endpoint:
- если endpoint уже IPv4, использует его напрямую;
- если это домен, делает
:resolve.
- Меняет
listen-portWireGuard interface на случайный порт. - Пингует endpoint, чтобы определить исходный IPv4 через conntrack.
- Проверяет route до endpoint и out-interface.
- Временно выключает peer.
- Инжектит пакеты из
ipackets, затемJcjunk-пакетов. - Включает peer обратно.
- Скрипт сейчас IPv4-only.
- IPv6 пока не реализован: там другой pseudo-header, обязательный UDP checksum и отдельная логика route/neighbor.
- На
ether,bridge,vlanскрипт собирает L2 Ethernet header. Если ARP-записи для шлюза нет, он сначала пингует шлюз, чтобы её создать; если MAC так и не резолвится, peer пропускается с ошибкойNo ARP entry for gateway. - Сбой на одном peer-е (не срезолвился endpoint, нет conntrack/route/ARP) не прерывает остальных: такой peer логируется как
Skipping peer ...и включается обратно, а цикл идёт к следующему. - На PPPoE у автора сценарий работает с пустым
eth, через inject на out-interface. Если у вас другой тип WAN и пакеты не выходят, проверяйте/tool sniffer. - Не ставьте
Jmaxбольше path MTU. Большие UDP-пакеты могут фрагментироваться и выглядеть подозрительно. - Если endpoint задан доменом 4-го уровня, он должен корректно обрабатываться через
:resolve.
Если не пробивает:
- Проверьте, что peer имеет
comment=BYPASS. - Посмотрите в логах
Endpoint IP,srcip,outInterface. - Убедитесь, что есть строки
fullpacket. - Первый
fullpacketдолжен быть большим, если задан большойi1. - Попробуйте временно:
:local UDPChecksumMode "zero"Если "zero" пробивает, а "calc" нет, значит проблема связана с checksum или изменением пакета на пути raw inject/NAT/offload.
This MikroTik RouterOS script sends a set of UDP packets to a WireGuard/AmneziaWG peer endpoint before the real handshake starts:
- custom signature packets from the
ipacketsarray; - then
Jcrandom junk packets sized betweenJminandJmax; - then the peer is enabled again, so the normal WireGuard handshake is sent by RouterOS.
The script targets RouterOS v7 and uses /tool traffic-generator inject.
At the top of script.rsc:
:local Jc 4
:local Jmin 40
:local Jmax 70
:local TTL 64
:local UDPChecksumMode "calc"
:local i1 "..."
:local i2 ""
:local i3 ""
:local i4 ""
:local i5 ""
:local ipackets {$i1;$i2;$i3;$i4;$i5}Parameters:
Jc- number of random junk packets.Jmin,Jmax- minimum and maximum junk payload size in bytes.TTL- TTL for the manually built IPv4 packet.UDPChecksumMode:"calc"- calculate UDP checksum. This is the default mode."zero"- write UDP checksum as0000. This is valid for IPv4 and useful for diagnosing raw-inject/NAT/offload issues.
i1..i5- example custom signature packets. Empty values are skipped.ipackets- packet array sent before junk packets. It can be extended to any reasonable number of elements:
:local i6 "<r 64>"
:local i7 "<b 0xdeadbeef><t><rd 8>"
:local ipackets {$i1;$i2;$i3;$i4;$i5;$i6;$i7}You can also put packet definitions directly into the array:
:local ipackets {"<b 0xdeadbeef>";"<r 64>";"<b 0xcafe><rc 10>"}The script processes only WireGuard peers with:
comment=BYPASSSo the target peer must have comment BYPASS.
Two formats are supported.
Raw HEX:
:local i1 "c3000000..."AmneziaWG-like tags:
:local i1 "<b 0xc3000000><t><r 10>"
:local i2 "<b 0xf6ab3267fa><rc 10>"
:local i3 "<b 0xf6ab3267fa><rd 10>"Tags:
<b 0xHEX>- static bytes.<r N>-Nrandom bytes.<rc N>-Nrandom ASCII letters[a-zA-Z].<rd N>-Nrandom ASCII digits[0-9].<t>- 4-byte Unix timestamp, big-endian, matchingamneziawg-go.
Important: raw HEX (a string with no <...> tags) bypasses the tag parser and goes straight to packet assembly. This matters for very large packets: they are used as-is instead of being walked character by character.
For very large static packets, prefer raw HEX over a single <b 0x...> tag. If RouterOS/Winbox truncates a very long string literal, split it into chunks and concatenate them:
:local i2a "ce0000000108..."
:local i2b "ed38da58ac29..."
:local i2 ($i2a . $i2b)The script checks a single <b ...> tag for the closing >; if the string was truncated, the partial packet is not sent.
For every peer with comment=BYPASS, the script:
- Checks
last-handshake. - Resolves the endpoint:
- if it is already an IPv4 address, it is used directly;
- if it is a hostname,
:resolveis used.
- Changes the WireGuard interface
listen-portto a random port. - Pings the endpoint to discover the local source IPv4 through conntrack.
- Checks the route and outgoing interface.
- Temporarily disables the peer.
- Injects packets from
ipackets, thenJcjunk packets. - Enables the peer again.
- The current script is IPv4-only.
- IPv6 is not implemented yet: it needs a different pseudo-header, mandatory UDP checksum, and separate route/neighbor logic.
- For
ether,bridge, andvlan, the script builds an Ethernet L2 header. If there is no ARP entry for the gateway, it first pings the gateway to create one; if the MAC still cannot be resolved, the peer is skipped with aNo ARP entry for gatewayerror. - A failure on one peer (endpoint not resolved, missing conntrack/route/ARP) does not abort the others: that peer is logged as
Skipping peer ...and re-enabled, and the loop moves on to the next one. - PPPoE is known to work in the author's setup with an empty
ethprefix and injection on the outgoing interface. For other WAN types, verify actual packet egress with/tool sniffer. - Avoid setting
Jmaxabove path MTU. Large UDP packets may fragment and look suspicious. - Hostnames with multiple labels, including 4th-level domains, should work through
:resolve.
If it does not work:
- Check that the peer has
comment=BYPASS. - Check log values for
Endpoint IP,srcip, andoutInterface. - Make sure
fullpacketlog entries appear. - If
i1is large, the firstfullpacketshould also be large. - Temporarily try:
:local UDPChecksumMode "zero"If "zero" works but "calc" does not, the issue is likely related to checksum calculation or packet mutation along the raw inject/NAT/offload path.
Check whether traffic-gen is enabled / Проверка включен ли traffic-gen
/system/device-mode/print
Enable traffic-gen / Включение traffic-gen
/system/device-mode/update traffic-gen=yes
Peers that should be skipped must be marked with the BYPASS comment / Нужные пиры комментировать BYPASS
:local Jc 4
:local Jmin 40
:local Jmax 70
:local TTL 64
:local UDPChecksumMode "calc"
:local DebugIparser false
:local i1 "c3000000010870ac9c05f49d2bff0341d26000421578ace2b50e80d3a3e8b2c2e2e5f50aebf6f7364c9fbed6be8c14606445db7e5c0f75b825ffc3d872b3c463422f0c6334b45a1d1297ee2abda6150110864de45ade52b8a2e33a7c4db399678ccb0501ce14696ae1de40c350293d31db073976e3eae493500358df59b6e16867d4c39ff670168bf0ab50e43aa0fc0814c0762227ff93f334522d9562142dcdef7241b554bfe2c27a3ab066d516f4d31a47526318c644e15d90e98899e25c0ce8a67e14df149769c3d14833d27a25e25fde8afd68f587cc573e8c88e502793b50626f4c5267a5786b2903172a0ef4eea2fa282a02e3d3385d598baa9cacb9395d6c43c5ccbbdce9845a39ded847779f00c44cf5df34f3ad2a22e63504316b748eabacb3b03a1cc3df9c8d6ab60a0255b7f8d433d6d0a671b5cf30a0af2c04a7138cc1b264382e164ebbbcc290176ac9d6672e57cac55effa9df991a0ec1b4ed63910432ff03b187c3a22206c6a4914e16d59e36f011a08f03f3ac7baed06a884f9fa3ee84ab2d097d4863f84edc87b624ca9aeafcec920339d3addc7b5fae21e59cc47c58147b244300ad857e71b8cb9772c4fed8a7a775744f0d8448c70a491e3a7fa5a98c0997be9319a32495011cafb4c2f9b3ade1ef1a5efbc00dd7374e5ea0226d62934a2847c55c0d524337d4073557e96b9ff177414ef03945503fb7c6149db4c3f4a449e70363fe259360de0df0d194f43a44dd364acadb6683262927e1b3dbcbb8e8a610ab0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
:local i2 "<b 0xce000000010897a2>"
:local i3 "<b 0xf6ab3267fa><t><r 10>"
:local i4 "<b 0xf6ab3267fa><rc 12>"
:local i5 "<b 0xf6ab3267fa><rd 12>"
:local IpacketCount 5
:local tohex do={
:local n $1
:local h ""
:while ($n>0) do={
:local r ($n % 16)
:set h ([:pick "0123456789abcdef" $r ($r+1)] . $h)
:set n ($n / 16)
}
:return $h
}
:local mac2hex do={
:local m $1
:set m ([:pick $m 0 2].[:pick $m 3 5].[:pick $m 6 8].[:pick $m 9 11].[:pick $m 12 14].[:pick $m 15 17])
:return [:convert transform=lc $m]
}
:local hex16 do={
:local n [:tonum $1]
:local hi ($n >> 8)
:local lo ($n & 255)
:local digits "0123456789abcdef"
:local h1 [:pick $digits ($hi >> 4) (($hi >> 4) + 1)]
:local h2 [:pick $digits ($hi & 15) (($hi & 15) + 1)]
:local h3 [:pick $digits ($lo >> 4) (($lo >> 4) + 1)]
:local h4 [:pick $digits ($lo & 15) (($lo & 15) + 1)]
:return ($h1 . $h2 . $h3 . $h4)
}
:local hex8 do={
:local n [:tonum $1]
:local digits "0123456789abcdef"
:local h1 [:pick $digits ($n >> 4) (($n >> 4) + 1)]
:local h2 [:pick $digits ($n & 15) (($n & 15) + 1)]
:return ($h1 . $h2)
}
:local ip2hex do={
:local ip [:tostr $1]
:local digits "0123456789abcdef"
:local out ""
:local p1 [:find $ip "."]
:local o1 [:tonum [:pick $ip 0 $p1]]
:set out ([:pick $digits ($o1 >> 4) (($o1 >> 4)+1)] . [:pick $digits ($o1 & 15) (($o1 & 15)+1)])
:local p2 [:find $ip "." ($p1 + 1)]
:local o2 [:tonum [:pick $ip ($p1 + 1) $p2]]
:set out ($out . [:pick $digits ($o2 >> 4) (($o2 >> 4)+1)] . [:pick $digits ($o2 & 15) (($o2 & 15)+1)])
:local p3 [:find $ip "." ($p2 + 1)]
:local o3 [:tonum [:pick $ip ($p2 + 1) $p3]]
:set out ($out . [:pick $digits ($o3 >> 4) (($o3 >> 4)+1)] . [:pick $digits ($o3 & 15) (($o3 & 15)+1)])
:local o4 [:tonum [:pick $ip ($p3 + 1) [:len $ip]]]
:set out ($out . [:pick $digits ($o4 >> 4) (($o4 >> 4)+1)] . [:pick $digits ($o4 & 15) (($o4 & 15)+1)])
:return $out
}
:local ipchecksum do={
:local header $1
:local sum 0
:for j from=0 to=9 do={
:local offset ($j * 4)
:local word [:pick $header $offset ($offset + 4)]
:local val [:tonum ("0x" . $word)]
:set sum ($sum + $val)
}
:while ($sum > 65535) do={
:set sum (($sum & 65535) + ($sum >> 16))
}
:local checksum (65535 - $sum)
:local hi ($checksum >> 8)
:local lo ($checksum & 255)
:local digits "0123456789abcdef"
:local h1 [:pick $digits ($hi >> 4) (($hi >> 4) + 1)]
:local h2 [:pick $digits ($hi & 15) (($hi & 15) + 1)]
:local h3 [:pick $digits ($lo >> 4) (($lo >> 4) + 1)]
:local h4 [:pick $digits ($lo & 15) (($lo & 15) + 1)]
:return ($h1 . $h2 . $h3 . $h4)
}
:local udpchecksum do={
:local srcHex $1
:local dstHex $2
:local udpLenNum $3
:local udpTmp $4
:local pay $5
:local pseudo ($srcHex . $dstHex . "00" . "11" . [$hex16 $udpLenNum])
:local checkdata ($pseudo . $udpTmp . $pay)
:if (([:len $checkdata] % 4) != 0) do={
:set checkdata ($checkdata . "00")
}
:local sum 0
:local wordCount ([:len $checkdata] / 4)
:for j from=0 to=($wordCount - 1) do={
:local offset ($j * 4)
:local word [:pick $checkdata $offset ($offset + 4)]
:local val [:tonum ("0x" . $word)]
:set sum ($sum + $val)
}
:while ($sum > 65535) do={
:set sum (($sum & 65535) + ($sum >> 16))
}
:local checksum (65535 - $sum)
:if ($checksum = 0) do={ :set checksum 0 }
:local hi ($checksum >> 8)
:local lo ($checksum & 255)
:local digits "0123456789abcdef"
:local h1 [:pick $digits ($hi >> 4) (($hi >> 4) + 1)]
:local h2 [:pick $digits ($hi & 15) (($hi & 15) + 1)]
:local h3 [:pick $digits ($lo >> 4) (($lo >> 4) + 1)]
:local h4 [:pick $digits ($lo & 15) (($lo & 15) + 1)]
:return ($h1 . $h2 . $h3 . $h4)
}
:local randhex do={
:local bytes $1
:local out ""
:local digits "0123456789abcdef"
:for i from=1 to=$bytes do={
:local r [:rndnum from=0 to=255]
:local h1 [:pick $digits ($r >> 4) (($r >> 4) + 1)]
:local h2 [:pick $digits ($r & 15) (($r & 15) + 1)]
:set out ($out . $h1 . $h2)
}
:return $out
}
:local randdigitshex do={
:local bytes $1
:local out ""
:local digits "0123456789abcdef"
:for i from=1 to=$bytes do={
:local n (48 + [:rndnum from=0 to=9])
:set out ($out . [:pick $digits ($n >> 4) (($n >> 4) + 1)] . [:pick $digits ($n & 15) (($n & 15) + 1)])
}
:return $out
}
:local randcharshex do={
:local bytes $1
:local out ""
:local digits "0123456789abcdef"
:for i from=1 to=$bytes do={
:local r [:rndnum from=0 to=51]
:local n 0
:if ($r < 26) do={
:set n (97 + $r)
} else={
:set n (65 + ($r - 26))
}
:set out ($out . [:pick $digits ($n >> 4) (($n >> 4) + 1)] . [:pick $digits ($n & 15) (($n & 15) + 1)])
}
:return $out
}
:local timestamphex do={
:local unix ([:tonsec [:timestamp]] / 1000000000)
:local digits "0123456789abcdef"
:local out ""
:local n (($unix >> 16) & 65535)
:local hi ($n >> 8)
:local lo ($n & 255)
:set out ($out . [:pick $digits ($hi >> 4) (($hi >> 4) + 1)] . [:pick $digits ($hi & 15) (($hi & 15) + 1)] . [:pick $digits ($lo >> 4) (($lo >> 4) + 1)] . [:pick $digits ($lo & 15) (($lo & 15) + 1)])
:set n ($unix & 65535)
:set hi ($n >> 8)
:set lo ($n & 255)
:set out ($out . [:pick $digits ($hi >> 4) (($hi >> 4) + 1)] . [:pick $digits ($hi & 15) (($hi & 15) + 1)] . [:pick $digits ($lo >> 4) (($lo >> 4) + 1)] . [:pick $digits ($lo & 15) (($lo & 15) + 1)])
:return $out
}
:local normalizehex do={
:local value [:tostr $1]
:if ([:pick $value 0 2] = "0x") do={ :set value [:pick $value 2 [:len $value]] }
:if ([:pick $value 0 2] = "0X") do={ :set value [:pick $value 2 [:len $value]] }
:return $value
}
# Собирает IPv4/UDP-пакет из payload-hex и инжектит его.
# do={} не видит внешние :local, поэтому контекст и хелперы передаём аргументами.
:local buildinject do={
:local partLen ([:len $pay] / 2)
:local udpLen ($partLen + 8)
:local ipLen ($udpLen + 20)
:local ipidHex [$fnHex16 [:rndnum from=0 to=65535]]
:local udpHeaderTmp ($sPortHex . $dPortHex . [$fnHex16 $udpLen] . "0000")
:local udpCsum [$fnUdpCsum $sIpHex $dIpHex $udpLen $udpHeaderTmp $pay]
:if ($csMode = "zero") do={ :set udpCsum "0000" }
:local udpHeader ($sPortHex . $dPortHex . [$fnHex16 $udpLen] . $udpCsum)
:local ipHeaderTmp ("4500" . [$fnHex16 $ipLen] . $ipidHex . "0000" . $tHex . "11" . "0000" . $sIpHex . $dIpHex)
:local ipCsum [$fnIpCsum $ipHeaderTmp]
:local ipHeader ("4500" . [$fnHex16 $ipLen] . $ipidHex . "0000" . $tHex . "11" . $ipCsum . $sIpHex . $dIpHex)
:local fullpacket ($ethHdr . $ipHeader . $udpHeader . $pay)
:log warning ("fullpacket: " . $fullpacket)
/tool/traffic-generator/inject interface="$outIfName" data=$fullpacket
}
:global Tx
:global Rx
:if ([:typeof $Tx] != "array") do={ :set Tx ({}) }
:if ([:typeof $Rx] != "array") do={ :set Rx ({}) }
/interface wireguard peers
:foreach i in=[find where comment=BYPASS] do={
:local LocalTx [get $i tx]
:local LocalRx [get $i rx]
:local LastHandshake [get $i last-handshake]
:local isDisabled [get $i disabled]
:if (([:tostr $LastHandshake] = "") or ($LastHandshake > [:totime "3m20s"]) or ($isDisabled = yes)) do={
:local PeerName [get $i name]
# Обёртка: сбой одного пира не должен прерывать обработку остальных.
:do {
:local Interface [get $i interface]
:local EndpointAddress [get $i endpoint-address]
:local EndpointIP ""
:do {
:set EndpointIP [:toip $EndpointAddress]
} on-error={
:set EndpointIP ""
}
:if ([:len [:tostr $EndpointIP]] = 0) do={
:do {
:set EndpointIP [:resolve $EndpointAddress]
} on-error={
:log error ("Could not resolve endpoint: " . $EndpointAddress)
:error "resolve failed"
}
}
:local DstPort [get $i current-endpoint-port]
:if (([:tostr $DstPort] = "") or ($DstPort = 0)) do={
:set DstPort [get $i endpoint-port]
}
:local rndport [:rndnum from=49000 to=59999]
/interface wireguard set $Interface listen-port=$rndport
:local SrcPort [/interface wireguard get $Interface listen-port]
/tool ping address=$EndpointIP count=1 interval=200ms
:delay 1s
:local conn [/ip firewall connection find where dst-address="$EndpointIP" and protocol=icmp]
:if ([:len $conn]=0) do={
:log error "No WG conntrack entry for $EndpointIP:$DstPort"
:error "no conntrack"
}
:local cid [:pick $conn 0]
:local srcip [/ip firewall connection get $cid reply-dst-address]
:local route [/ip route check dst-ip=$EndpointIP once as-value]
:if ([:len $route]=0) do={
:log error "Route check failed"
:error "route check failed"
}
:local outIf ($route->"interface")
:local ifType [/interface get $outIf type]
:local eth ""
:local gw ""
:if ($ifType = "ether" or $ifType = "bridge") do={
:set gw ($route->"nexthop")
:local srcmacRaw [/interface get $outIf mac-address]
:local srcmac [$mac2hex $srcmacRaw]
:local arpId [/ip arp find where address=$gw and interface=$outIf]
:if ([:len $arpId] = 0) do={
/tool ping address=$gw count=1 interval=200ms
:delay 500ms
:set arpId [/ip arp find where address=$gw and interface=$outIf]
}
:if ([:len $arpId] = 0) do={
:log error ("No ARP entry for gateway " . $gw . " on " . $outIf)
:error "no arp"
}
:local dstmacRaw [/ip arp get $arpId mac-address]
:local dstmac [$mac2hex $dstmacRaw]
:set eth ($dstmac.$srcmac."0800")
}
:if ($ifType = "vlan") do={
:set gw ($route->"nexthop")
:local srcmacRaw [/interface get $outIf mac-address]
:local srcmac [$mac2hex $srcmacRaw]
:local arpId [/ip arp find where address=$gw and interface=$outIf]
:if ([:len $arpId] = 0) do={
/tool ping address=$gw count=1 interval=200ms
:delay 500ms
:set arpId [/ip arp find where address=$gw and interface=$outIf]
}
:if ([:len $arpId] = 0) do={
:log error ("No ARP entry for gateway " . $gw . " on " . $outIf)
:error "no arp"
}
:local dstmacRaw [/ip arp get $arpId mac-address]
:local dstmac [$mac2hex $dstmacRaw]
:local vlanId [/interface/vlan/get $outIf vlan-id]
:local vlanIdHex [$hex16 $vlanId]
:set outIf [/interface/vlan/get $outIf interface]
:set eth ($dstmac.$srcmac."8100".$vlanIdHex."0800")
}
:local srcipHex [$ip2hex $srcip]
:local dstipHex [$ip2hex $EndpointIP]
:local ttlHex [$hex8 $TTL]
:local srcPortHex [$hex16 $SrcPort]
:local dstPortHex [$hex16 $DstPort]
#Log peer info
:log warning ("Peer: $PeerName, Interface: $Interface")
:log warning ("Endpoint Address: $EndpointAddress, Endpoint IP: $EndpointIP")
:log warning ("Src Port: $SrcPort, Dst Port: $DstPort, Last Handshake: $LastHandshake")
:log warning ("Last Rx: " . $Rx->[:tostr $i] . ", Current Rx: $LocalRx")
:log warning ("Last Tx: " . $Tx->[:tostr $i] . ", Current Tx: $LocalTx")
#Disable peer
:log warning ("Disable peer: $PeerName")
set $i disabled=yes
:delay 100ms
#Generating spam
:log warning ("Generating spam")
:delay 1
:log warning ("gateway: $gw")
:log warning ("eth: $eth")
:log warning ("srcip: $srcip")
:log warning ("srcipHex: $srcipHex")
:log warning ("dstip: $EndpointIP")
:log warning ("dstipHex: $dstipHex")
:log warning ("outInterface: $outIf")
:log warning ("I-spec lengths: i1=" . [:len $i1] . ", i2=" . [:len $i2] . ", i3=" . [:len $i3] . ", i4=" . [:len $i4] . ", i5=" . [:len $i5])
:for packetNo from=1 to=$IpacketCount do={
:local part ""
:if ($packetNo = 1) do={ :set part $i1 }
:if ($packetNo = 2) do={ :set part $i2 }
:if ($packetNo = 3) do={ :set part $i3 }
:if ($packetNo = 4) do={ :set part $i4 }
:if ($packetNo = 5) do={ :set part $i5 }
:if ($DebugIparser = true) do={
:log warning ("DBG I" . $packetNo . " raw len=" . [:len $part] . " raw=" . $part)
}
:if ([:pick $part 0 2] = "0x") do={ :set part [:pick $part 2 [:len $part]] }
:if ([:pick $part 0 2] = "0X") do={ :set part [:pick $part 2 [:len $part]] }
:local nextTag [:find $part "<" 1]
:if ([:typeof $nextTag] = "nil") do={
:if ([:pick $part 0 5] = "<b 0x") do={
:if ([:pick $part ([:len $part] - 1) [:len $part]] = ">") do={
:set part [:pick $part 5 ([:len $part] - 1)]
} else={
:log error ("I" . $packetNo . " static <b> tag is missing closing >, probably truncated")
:set part ""
}
} else={
:if ([:pick $part 0 3] = "<b ") do={
:if ([:pick $part ([:len $part] - 1) [:len $part]] = ">") do={
:set part [:pick $part 3 ([:len $part] - 1)]
} else={
:log error ("I" . $packetNo . " static <b> tag is missing closing >, probably truncated")
:set part ""
}
}
}
}
:if ([:pick $part 0 1] = "<") do={
:local spec $part
:local out ""
:local pos 0
:if ($DebugIparser = true) do={
:log warning ("DBG I" . $packetNo . " parse spec len=" . [:len $spec] . " spec=" . $spec)
}
:if (([:pick $spec 0 5] = "<b 0x") or ([:pick $spec 0 3] = "<b ")) do={
:local firstEnd [:find $spec ">"]
:if ($DebugIparser = true) do={
:log warning ("DBG I" . $packetNo . " firstEnd=" . $firstEnd . " firstPrefix5=" . [:pick $spec 0 5] . " firstPrefix3=" . [:pick $spec 0 3])
}
:if ([:typeof $firstEnd] = "nil") do={
:log error ("I" . $packetNo . " leading <b> tag is missing >: " . $spec)
:set spec ""
} else={
:local firstTag [:pick $spec 1 $firstEnd]
:local firstSp [:find $firstTag " "]
:if ($DebugIparser = true) do={
:log warning ("DBG I" . $packetNo . " firstTag=" . $firstTag . " firstSp=" . $firstSp)
}
:if ([:typeof $firstSp] != "nil") do={
:local firstVal [:pick $firstTag ($firstSp + 1) [:len $firstTag]]
:if ([:pick $firstVal 0 2] = "0x") do={ :set firstVal [:pick $firstVal 2 [:len $firstVal]] }
:if ([:pick $firstVal 0 2] = "0X") do={ :set firstVal [:pick $firstVal 2 [:len $firstVal]] }
:set out ($out . $firstVal)
:if ($DebugIparser = true) do={
:log warning ("DBG I" . $packetNo . " firstVal len=" . [:len $firstVal] . " outHexLen=" . [:len $out])
}
}
:set pos ($firstEnd + 1)
:if ($DebugIparser = true) do={
:log warning ("DBG I" . $packetNo . " pos after leading b=" . $pos)
}
}
}
:while ($pos < [:len $spec]) do={
:local start $pos
:if ([:pick $spec $pos ($pos + 1)] != "<") do={
:set start [:find $spec "<" $pos]
}
:if ($DebugIparser = true) do={
:log warning ("DBG I" . $packetNo . " loop pos=" . $pos . " start=" . $start . " specLen=" . [:len $spec])
}
:if ([:typeof $start] = "nil") do={
:set pos [:len $spec]
} else={
:local end [:find $spec ">" $start]
:if ($DebugIparser = true) do={
:log warning ("DBG I" . $packetNo . " end=" . $end . " at start=" . $start)
}
:if ([:typeof $end] = "nil") do={
:log error ("I" . $packetNo . " invalid tag, missing >: " . $spec)
:set out ""
:set pos [:len $spec]
} else={
:local tag [:pick $spec ($start + 1) $end]
:local handled false
:if ($DebugIparser = true) do={
:log warning ("DBG I" . $packetNo . " tag=[" . $tag . "] len=" . [:len $tag] . " p2=[" . [:pick $tag 0 2] . "] p3=[" . [:pick $tag 0 3] . "]")
}
:if ([:pick $tag 0 2] = "b ") do={
:local val [:pick $tag 2 [:len $tag]]
:if ([:pick $val 0 2] = "0x") do={ :set val [:pick $val 2 [:len $val]] }
:if ([:pick $val 0 2] = "0X") do={ :set val [:pick $val 2 [:len $val]] }
:set out ($out . $val)
:set handled true
:if ($DebugIparser = true) do={
:log warning ("DBG I" . $packetNo . " handled b valHexLen=" . [:len $val] . " outHexLen=" . [:len $out])
}
}
:if ([:pick $tag 0 2] = "r ") do={
:local count [:tonum [:pick $tag 2 [:len $tag]]]
:local gen [$randhex $count]
:set out ($out . $gen)
:set handled true
:if ($DebugIparser = true) do={
:log warning ("DBG I" . $packetNo . " handled r count=" . $count . " genHexLen=" . [:len $gen] . " outHexLen=" . [:len $out])
}
}
:if ([:pick $tag 0 3] = "rc ") do={
:local count [:tonum [:pick $tag 3 [:len $tag]]]
:local gen [$randcharshex $count]
:set out ($out . $gen)
:set handled true
:if ($DebugIparser = true) do={
:log warning ("DBG I" . $packetNo . " handled rc count=" . $count . " genHexLen=" . [:len $gen] . " outHexLen=" . [:len $out])
}
}
:if ([:pick $tag 0 3] = "rd ") do={
:local count [:tonum [:pick $tag 3 [:len $tag]]]
:local gen [$randdigitshex $count]
:set out ($out . $gen)
:set handled true
:if ($DebugIparser = true) do={
:log warning ("DBG I" . $packetNo . " handled rd count=" . $count . " genHexLen=" . [:len $gen] . " outHexLen=" . [:len $out])
}
}
:if ($tag = "t") do={
:local gen [$timestamphex]
:set out ($out . $gen)
:set handled true
:if ($DebugIparser = true) do={
:log warning ("DBG I" . $packetNo . " handled t genHexLen=" . [:len $gen] . " outHexLen=" . [:len $out])
}
}
:if ($handled = false) do={
:log error ("I" . $packetNo . " unsupported tag: <" . $tag . ">")
:set out ""
:set pos [:len $spec]
}
:set pos ($end + 1)
:if ($DebugIparser = true) do={
:log warning ("DBG I" . $packetNo . " next pos=" . $pos . " handled=" . $handled . " outHexLen=" . [:len $out])
}
}
}
}
:set part $out
:if ($DebugIparser = true) do={
:log warning ("DBG I" . $packetNo . " final partHexLen=" . [:len $part] . " payloadBytes=" . ([:len $part] / 2))
}
}
:if ([:len $part] = 0) do={
:log warning ("I-packet #" . $packetNo . " skipped: empty")
}
:if ([:len $part] > 0) do={
:if (([:len $part] % 2) != 0) do={
:log error ("I" . $packetNo . " UDP payload HEX has odd length: " . [:len $part] . " hex chars")
:if ([:len $part] > 16) do={
:log error ("I" . $packetNo . " head/tail: " . [:pick $part 0 16] . "..." . [:pick $part ([:len $part] - 16) [:len $part]])
} else={
:log error ("I" . $packetNo . " value: " . $part)
}
:set part ""
} else={
:log warning ("I-packet #" . $packetNo . " payload bytes: " . ([:len $part] / 2))
$buildinject pay=$part sPortHex=$srcPortHex dPortHex=$dstPortHex \
sIpHex=$srcipHex dIpHex=$dstipHex tHex=$ttlHex ethHdr=$eth \
csMode=$UDPChecksumMode outIfName=$outIf \
fnHex16=$hex16 fnUdpCsum=$udpchecksum fnIpCsum=$ipchecksum
:delay 5ms
}
}
}
:for j from=1 to=$Jc do={
:local size [:rndnum from=$Jmin to=$Jmax]
:local part [$randhex $size]
:log warning ("Junk packet #" . $j . " payload bytes: " . ([:len $part] / 2))
$buildinject pay=$part sPortHex=$srcPortHex dPortHex=$dstPortHex \
sIpHex=$srcipHex dIpHex=$dstipHex tHex=$ttlHex ethHdr=$eth \
csMode=$UDPChecksumMode outIfName=$outIf \
fnHex16=$hex16 fnUdpCsum=$udpchecksum fnIpCsum=$ipchecksum
:delay 5ms
}
#Enable peer
:log warning ("Enable peer: $PeerName")
set $i disabled=no
} on-error={
:log warning ("Skipping peer " . $PeerName . ", see errors above")
# Страховка: не оставляем пир выключенным, если сбой пришёлся после disable.
set $i disabled=no
}
}
:set ($Tx->[:tostr $i]) $LocalTx
:set ($Rx->[:tostr $i]) $LocalRx
}
Большое спасибо за такой отличный скрипт и проделанную работу. В одной точке отработало точно с первой попытки.
В другой точке не выходит, может связанно с тем что это PPPoE или просто более усиленно блокируют WARP
Из пожеланий, какой-то дополнительный скрипт который бы перегонял из .conf в параметры скрипта.
Генерировал конфиг через https://vadim-khristenko.github.io/AmneziaWG-Architect/
Но вводить их потом вручную проблематично, чтоб подобрать нужную стратегию.