From 5ea36ac18a3d21690a07997132946e58ec7186ff Mon Sep 17 00:00:00 2001 From: vados-dev Date: Sat, 11 Jul 2026 21:01:50 +0300 Subject: [PATCH] Backup for MikroGig created successfully at Jul 11 21:01:50. --- .../20260711/20260711210146-Binary.backup | Bin 0 -> 194200 bytes MikroGig/20260711/20260711210146-Config.rsc | 3337 ++++++++++++++ .../20260711/20260711210146-Diff-Config.rsc | 4 + .../20260711/20260711200003-Config.rsc | 3841 ++++++++++++++++ .../20260711/20260711202002-Config.rsc | 3843 +++++++++++++++++ .../20260711/20260711204002-Config.rsc | 3841 ++++++++++++++++ MikroMaster/tmp/Diff-Config.rsc | 3 - 7 files changed, 14866 insertions(+), 3 deletions(-) create mode 100644 MikroGig/20260711/20260711210146-Binary.backup create mode 100644 MikroGig/20260711/20260711210146-Config.rsc create mode 100644 MikroGig/history/20260711/20260711210146-Diff-Config.rsc create mode 100644 MikroMaster/20260711/20260711200003-Config.rsc create mode 100644 MikroMaster/20260711/20260711202002-Config.rsc create mode 100644 MikroMaster/20260711/20260711204002-Config.rsc delete mode 100644 MikroMaster/tmp/Diff-Config.rsc diff --git a/MikroGig/20260711/20260711210146-Binary.backup b/MikroGig/20260711/20260711210146-Binary.backup new file mode 100644 index 0000000000000000000000000000000000000000..7c2e2888b844c51320d92a7421cf5477b5d09f45 GIT binary patch literal 194200 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

?ajkecMkmL%bvC^YK{x1nzqAQXi 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

    LlP-ExC#g3C0rFv=H4Lw5vf;3`TQ(m(> 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\"data\");\r\ + \n#:global NewScript (a);\r\ + \n#:global ScriptInstallUpdate; \$ScriptInstallUpdate;\r\ + \n#\r\ + \n# For run from terminal:\r\ + \n#/system/script/add name=AM-setup owner=AM-Setup source=([/tool/fetch ch\ + eck-certificate=yes-without-crl \"https://ros.vados.ru/AM-setup.rsc\" outp\ + ut=user as-value ]->\"data\");\r\ + \n#\r\ + \n\r\ + \n:global ScriptUpdatesCRLF \"LF\";\r\ + \n#:if (\$ScriptUpdatesCRLF != false) do={\r\ + \n# :foreach Script in=[ /system/script/find where source~\"^#!rsc by Vad\ + os\" ] do={\r\ + \n# if (\$ScriptUpdatesCRLF = true) do={\r\ + \n# /system/script/set source=[ :tocrlf [ get \$Script source ] ] \$S\ + cript;\r\ + \n# } else={\r\ + \n# /system/script/set source=[ :tolf [ get \$Script source ] ] \$Scr\ + ipt;\r\ + \n# }\r\ + \n# }\r\ + \n#}\r\ + \n\r\ + \n# /import verbose=no \$ScriptName\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global SetupScript [ :jobname ];\r\ + \n:global RemoveMe true;\r\ + \n:global RunOnce (a);\r\ + \n:global InitSetup true;\r\ + \n:global tmpfs false;\r\ + \n\r\ + \n{\r\ + \n :local RmMe [:tobool \$1];\r\ + \n\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global SetupScript;\r\ + \n :global RemoveMe;\r\ + \n :global InitSetup;\r\ + \n :global tmpfs;\r\ + \n\r\ + \n :local UrlSuffix \$Identity;\r\ + \n :local UrlDir \"certs/\";\r\ + \n :local CertUrl (\$GlobalScriptsUrl . \$UrlDir . \$UrlSuffix .\"/\"); \ + \r\ + \n :local CertCommonName \"Root YE\";\r\ + \n :local CertFileName \"Root-YE.pem\";\r\ + \n :local CertFingerprint \"e14ffcad5b0025731006caa43a121a22d8e9700f4fb9c\ + f852f02a708aa5d5666\";\r\ + \n\r\ + \n :if (\$SetupScript = false) do={\r\ + \n :global SetupScript \"AM-setup\";\r\ + \n }\r\ + \n :if (\$RemoveMe != false) do={\r\ + \n :set RemoveMe true;\r\ + \n }\r\ + \n\r\ + \n :log warning (\"\\n\\\$GlobalScriptsUrl = \" . \$GlobalScriptsUrl);\r\ + \n \r\ + \n\r\ + \n :if (!(((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" ||\ + \_\\\r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \"fetch\" || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\r\ + \n [ :len [ /certificate/builtin/find where common-name=\$CertComm\ + onName ] ] > 0)) do={\r\ + \n :put \"Importing certificate...\";\r\ + \n /tool/fetch (\$CertUrl . \$CertFileName) dst-path=\$CertFileName\ + \_as-value;\r\ + \n :delay 1s;\r\ + \n /certificate/import file-name=\$CertFileName passphrase=\"\";\r\ + \n /certificate/set name=\"Root-YE\" [ find where common-name=\"Roo\ + t YE\" ];\r\ + \n :delay 1s;\r\ + \n };\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if ([ :len [ /certificate/find where fingerprint=\$CertFingerprint ] \ + ] != 1) do={\r\ + \n :error \"Something is wrong with your certificates!\";\r\ + \n };\r\ + \n\r\ + \n:local MkTmpfs do={\r\ + \n :local TmpFs [ /disk/find where slot=tmpfs type=tmpfs ];\r\ + \n :if ([ :len \$TmpFs ] = 1) do={\r\ + \n :if ([ /disk/get \$TmpFs disabled ] = true) do={\r\ + \n :log info (\"The tmpfs is disabled, enabling.\");\r\ + \n /disk/enable \$TmpFs;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :log info (\"Try to removing directory 'tmpfs'.\");\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/remove tmpfs;\r\ + \n :return true;\r\ + \n } delay=100ms max=10;\r\ + \n } on-error={ }\r\ + \n# \$RmDir \"tmpfs\";\r\ + \n :onerror Err {\r\ + \n /disk/add slot=tmpfs type=tmpfs tmpfs-max-size=([ /system/resource/g\ + et total-memory ] / 3);\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/get tmpfs;\r\ + \n :return true;\r\ + \n } delay=222ms max=10;\r\ + \n } on-error={ }\r\ + \n } do={\r\ + \n :log warning (\"Creating disk of type tmpfs failed: \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n# :return true;\r\ + \n}\r\ + \n:if (\$tmpfs = true) do={\r\ + \n :put \"Tmpfs variable is true. Checking for tmpfs/scripts directory...\ + \";\r\ + \n :if ([/file find name=\"tmpfs/scripts\"] != \"\") do={\r\ + \n :log info (\$0 . \"Directory tmpfs/scripts exsist.\");\r\ + \n :return \$true;\r\ + \n } else={\r\ + \n :log warning (\$0 . \"Directory tmpfs/scripts does't exsist.\");\r\ + \n :if ([ \$MkTmpfs ] = false) do={\r\ + \n :log error (\$0 . \"Directory tmpfs is not created!\");\r\ + \n :return false;\r\ + \n } else={\r\ + \n :onerror Err {\r\ + \n /file/add type=\"directory\" name=/tmpfs/scripts;\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/get /tmpfs/scripts;\r\ + \n :return true;\r\ + \n } delay=100ms max=10;\r\ + \n } on-error={ }\r\ + \n } do={\r\ + \n :log warning (\$0 . \"Making directory 'scripts' failed: \" . \$\ + Err);\r\ + \n :return false;\r\ + \n }\r\ + \n# :return true;\r\ + \n# [ /file/get \$FileName ]\r\ + \n }\r\ + \n# :log error (\$0 . \"Directory tmpfs/scripts does not exsist. Remove\ + \_global variable tmpfs!\");\r\ + \n# :global tmpfs (a);\r\ + \n# return false;\r\ + \n }\r\ + \n}\r\ + \n :put \"Renaming AM-GlobalConfig, if exists...\";\r\ + \n :local ConfOldName (\"AM-GlobalConfig-\" . [ /system/clock/get date ] \ + . [ /system/clock/get time ] . \".rsc\");\r\ + \n /system/script/set name=\$ConfOldName [ find where name=\"AM-GlobalCon\ + fig\" ];\r\ + \n :foreach Script in={ \"AM-GlobalConfig\"; \"AM-GlobalFunc\" } do={\r\ + \n :put \"Installing \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n /system/script/add name=\$Script owner=\$Script source=([ /tool/fetc\ + h check-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \ + \".rsc\") output=user as-value ]->\"data\");\r\ + \n \r\ + \n /system/script run \$Script;\r\ + \n :delay 2s;\r\ + \n };\r\ + \n\r\ + \n :local Script \"mod/AM-Certs-By-Ident\";\r\ + \n \r\ + \n :put \"Installing \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n /system/script/add name=\$Script owner=\$Script source=([ /tool/fetch \ + check-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".\ + rsc\") output=user as-value ]->\"data\");\r\ + \n /system/script run \$Script;\r\ + \n :global tmpAddCerts;\r\ + \n :while ([ \$tmpAddCerts ] = true) do={\r\ + \n :delay 1s;\r\ + \n }\r\ + \n :delay 1s;\r\ + \n :put \"Removing \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n\r\ + \n :if ([ :len [ /certificate/find where fingerprint=\$CertFingerprint ] \ + ] > 0) do={\r\ + \n :put \"Renaming certificate by its common-name...\";\r\ + \n :global CertificateNameByCN;\r\ + \n \$CertificateNameByCN \$CertFingerprint;\r\ + \n };\r\ + \n\r\ + \n :if (\$RemoveMe = true) do={\r\ + \n :put \"Add Scheduler for remove this script...\";\r\ + \n :local OnEvent (\":delay 30s;\\n\" . \"/system/script/remove [ find \ + where name=\\\"\$SetupScript\\\" ];\\n\" . \\\r\ + \n \":delay 2s;\\n\" . \"/system/scheduler/remove [ find where name=\\\ + \"_RemoveSetup\\\" ];\\n\" . \\\r\ + \n \":delay 2s;\\n\" . \":global RemoveMe (a);\\n\");\r\ + \n :local Comment \"_RemoveSetup tmp scheduler for remove setup script.\ + \";\r\ + \n /system/scheduler/add name=\"_RemoveSetup\" comment=\$Comment start-\ + time=startup interval=30s on-event=\$OnEvent;\r\ + \n :log warning (\$0 . \"\\nI've done everything, and now I'm tired..\ + . I'm leaving.\\nThere will be an error below, this is normal.\");\r\ + \n }\r\ + \n :delay 1;\r\ + \n :put \"Loading configuration and functions...\";\r\ + \n :do { \r\ + \n :global ScriptInstallUpdate; \$ScriptInstallUpdate;\r\ + \n :put \"Normal removing temporary global environment variables.\";\ + \r\ + \n :global SetupScript (a);\r\ + \n :global InitSetup (a);\r\ + \n :global RemoveMe (a);\r\ + \n } on-error={\r\ + \n :local ErrorMsg \"Loading configuration and functions error!\";\r\ + \n :log error (\$0 . \$ErrorMsg . :error);\r\ + \n :error \$ErrorMsg;\r\ + \n }\r\ + \n}\r\ + \n" +add comment="Global Config Script" dont-require-permissions=no name=\ + AM-GlobalConfig owner=AM-GlobalConfig policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GlobalConfig\r\ + \n# Script comment: Global Config Script\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# Warning: Dont touch this line!\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global GlobalConfReady false;\r\ + \n#\r\ + \n#\r\ + \n# Global Url For Amster scripts to fetch\r\ + \n# most be defined in AM-Init-Script or AM-setup scripts\r\ + \n:global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n:global ScriptsUrlSuffix (a);\r\ + \n:global CheckSumsVerify true;\r\ + \n\r\ + \n:global ScriptUpdatesCRLF true;\r\ + \n:global CommentsInScripts true;\r\ + \n\r\ + \n# Debug output:\r\ + \n:global PrintDebug true; #false;\r\ + \n\r\ + \n# Debug output for specific script:\r\ + \n#:set (\$PrintDebugOverride->\"AM-GlobalFunc\") true;\r\ + \n\r\ + \n# Debug logs\r\ + \n# Other actions (disk, email, remote or support) can be used as well.\r\ + \n# I do not recommend using echo - use debug output instead (https://rsc.\ + eworm.de/main/DEBUG.html#debug-output).\r\ + \n#/system/logging/add topics=script,debug action=memory;\r\ + \n\r\ + \n:global MikrotikUpgradeUrl \"https://upgrade.mikrotik.com/routeros\";\r\ + \n:global Domain \"WORKGROUP\";\r\ + \n\r\ + \n:global EmailGeneralTo \"vados@vados.ru\";\r\ + \n#:global EmailGeneralCc \"\";\r\ + \n\r\ + \n# \$ScriptInstallUpdate mod/notification-telegram\r\ + \n:global TelegramTokenId \"8390868324:AAG3_d1hfVR2M6pGAgDK6ZvhOe4J97nyopw\ + \";\r\ + \n:global TelegramChatId \"-1003631309063\"; #\"454403445\";\r\ + \n# Use this to send notifications to a specific topic in group.\r\ + \n#:global TelegramThreadId \"30\";\r\ + \n# Using telegram-chat you have to define trusted chat ids (not group ids\ + !)\r\ + \n# or user names. Groups allow to chat with devices simultaneously.\r\ + \n:global TelegramChatIdsTrusted {\r\ + \n \"454403445\"\r\ + \n \"-1003631309063\"\r\ + \n \"-5124482807\"\r\ + \n \"1087968824\";\r\ + \n};\r\ + \n# \"-5294757340\";\r\ + \n\r\ + \n:global TelegramChatGroups \"(all)\";\r\ + \n#:global TelegramChatGroups \"(all|home|office)\";\r\ + \n\r\ + \n:global NotificationFunctions {\r\ + \n \"email\"\r\ + \n \"telegram\";\r\ + \n};\r\ + \n# Toggle this to disable symbols in notifications.\r\ + \n:global NotificationsWithSymbols true;\r\ + \n:global TerminalColorOutput true;\r\ + \n\r\ + \n:global DetectIpAddrDefault \"https://ipv4.mikrotik.ovh/\";\r\ + \n:global DetectIpAddrFallback \"https://api.ipify.org/\";\r\ + \n\r\ + \n## Update channel. Possible values: stable, long-term, testing, developm\ + ent\r\ + \n:global updateChannel \"stable\";\r\ + \n\r\ + \n:global CertRenewUrl (\$GlobalScriptsUrl . \"certs/\" . \$Identity . \"/\ + \");\r\ + \n:global CertRenewTime 2w;\r\ + \n:global CertRenewPass (a); #{\r\ + \n# \"\";\r\ + \n# \"v3ry-s3cr3t\";\r\ + \n# \"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n:global CertWarnTime 1w;\r\ + \n:global CertIssuedExportPass (a); # {\r\ + \n# \"\";\r\ + \n# \"cert1-cn\"=\"v3ry-s3cr3t\";\r\ + \n# \"cert2-cn\"=\"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n\r\ + \n# Script BackupAndUpdate mode, possible values: backup,\r\ + \n# updateOnce - Update once as \"osnotify\" and back \"osnotify\" mode (i\ + n terminal run :set OSUpdateMode updateOnce; \$OSUpdateMode;). \r\ + \n# osupdate - Update if available and creates backups before/after update\ + \_(ignores `forceBackup`)\r\ + \n# Set `forceBackup` to true to always create backups, even\ + \_without updates\r\ + \n# osnotify - Set `forceBackup` to always create backups on every run\r\ + \n:global DevInfoIncludeIP true;\r\ + \n:global BackupAndUpdateMode \"osnotify\"\r\ + \n\r\ + \n:global forceBackup true;\r\ + \n:global BackupPassword \"Peskar55\";\r\ + \n\r\ + \n# SFTP Backup Variables\r\ + \n:global BackupRandomDelay 0;\r\ + \n:global BackupUploadUrl \"sftp://amster.vados.ru/upload/\";\r\ + \n:global BackupUploadUser \$Identity;\r\ + \n:global BackupUploadPass \"root.mnuc.backuper\";\r\ + \n\r\ + \n:global BackupSendBinary true;\r\ + \n:global BackupSendExport true;\r\ + \n:global BackupSendGlobalConfig true;\r\ + \n:global BackupSendScripts true;\r\ + \n\r\ + \n# Remove local file after uploading if no errors\r\ + \n:global BackupRmLocal true;\r\ + \n\r\ + \n# Local Backups Root Directory \r\ + \n:global BackupLocalRoot \"tmpfs\";\r\ + \n\r\ + \n# Encrypt Backup\r\ + \n:global BackupEncrypt true;\r\ + \n\r\ + \n# Sensitive information in Backups\r\ + \n:global BackupSens true;\r\ + \n\r\ + \n# User Export\r\ + \n:global BackupUser true;\r\ + \n\r\ + \n# License Export (not for CHR, will silently skip)\r\ + \n:global BackupLicense false;\r\ + \n\r\ + \n# SSH Keys\r\ + \n:global BackupSshKeys true;\r\ + \n\r\ + \n# Certificate Export\r\ + \n:global BackupCerts true;\r\ + \n\r\ + \n# Certificate Password\r\ + \n:global BackupCertsPasswd \"\";\r\ + \n\r\ + \n# User-Manager Export\r\ + \n:global BackupUserMan false;\r\ + \n\r\ + \n# The Dude Export\r\ + \n:global BackupDude false;\r\ + \n\r\ + \n# User Files to export, comma-separated string or array of strings\r\ + \n# User Files are not removed on backup\r\ + \n# Any directory paths will be removed (/ -> _) on remote file\r\ + \n# Nonpresent files are silently skipped\r\ + \n:global BackupUserList \"autosupout.rif,autosupout.old.rif\";\r\ + \n\r\ + \n# load functions and any custom scripts\r\ + \n# [ /system/script/find where name=\"global-config-ov\ + erlay\" ], \\\r\ + \n# [ /system/script/find where name~\"^global-config-o\ + verlay\\\\.d/.\" ]\r\ + \n#\r\ + \n#:foreach Script in=([ /system/script/find where name=\"Amster-TGglobalV\ + ars\" ], [ /system/script/find where name=\"Amster-TGglobalFunc\" ]) do={\ + \r\ + \n# :onerror Err {\r\ + \n# /system/script/run \$Script;\r\ + \n# } do={\r\ + \n# :log error (\"Loading configuration from configs and functions\"\ + \_. [ /system/script/get \$Script name ] . \" failed: \" . \$Err);\r\ + \n# }\r\ + \n#}\r\ + \n#:log info \"\\ndevMode = \$devMode\";\r\ + \n#:log info \"\\ndevRunning = \$devRunning\";\r\ + \n:foreach Script in=[ /system/script/find where source~\"^#!rsc by Vados \ + GlobalRUN\\r\?\\n\" ] do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n :log error (\"Run script \" . [ /system/script/get \$Script name \ + ] . \" width marker \\\" GlobalRUN\\\" failed: \" . \$Err);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# signal we are ready\r\ + \n:set GlobalConfReady true;\r\ + \n:while (\$GlobalConfReady != true) do={:delay 500ms};\r\ + \n" +add comment="Global functions" dont-require-permissions=no name=AM-GlobalFunc \ + owner=AM-GlobalFunc policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GglobalFunc\r\ + \n# Script comment: Global functions\r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch, scheduler\r\ + \n:local ExitOK false;\r\ + \n:local ScriptName [ :jobname ];\r\ + \n#:global checkRunJob \$SriptName;\r\ + \n#:global fixCRLF;\r\ + \n#:if (\$fixCRLF != false) do={\r\ + \n#:foreach fixScript in=([ /system/script/find where name~\"*\" ]) do={/s\ + ystem/script/set source=[ :tocrlf [ get \$fixScript source ] ] \$fixScript\ + }\r\ + \n#:if (\$fixCRLF != \"LF\") do={ /system/script/set source=[ :tocrlf [ g\ + et \$fixScript source ] ] \$fixScript};};\r\ + \n#}\r\ + \n:global GlobalFuncReady false;\r\ + \n\r\ + \n# Git commit id & info, expected configuration version\r\ + \n:global CommitId \"main/728/bebacfc7\";\r\ + \n:global CommitTAG \"unknown\";\r\ + \n:global CommitInfo \"Autocommit in repo: ros.vados.ru, generated by owne\ + r: root, in 11:00:05 30.06.2026.\";\r\ + \n\r\ + \n# The variables are received right here.\r\ + \n:global GlobalConfVersion 140;\r\ + \n\r\ + \n:global SumsDirAndFileName \"tmp/checksums.json\";\r\ + \n\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global IdentityShort [:pick \$Identity 0 18];\r\ + \n\r\ + \n:global devBoardName [/system/resource/get board-name];\r\ + \n:global devMode [/system/device-mode/get mode];\r\ + \n:global devCurArch [/system/resource/get architecture-name];\r\ + \n:global devCurVersion [/system/resource/get version];\r\ + \n\r\ + \n# Variables defined in AM-GlobalConfig.rsc\r\ + \n:global TerminalColorOutput;\r\ + \n:global CommentsInScripts;\r\ + \n:global DetectIpAddrDefault;\r\ + \n:global DetectIpAddrFallback;\r\ + \n:global updateChannel;\r\ + \n:global Domain;\r\ + \n:global EmailGeneralTo;\r\ + \n:global BackupAndUpdateMode;\r\ + \n\r\ + \n# Single functions, without nested functions.\r\ + \n:global MiniDateTimeStamp;\r\ + \n:global MicroDateTimeStamp;\r\ + \n\r\ + \n# Include globals \$PrintDebug and \$PrintDebugOverride\r\ + \n:global LogPrint;\r\ + \n\r\ + \n# DeviceInfo and Updates\r\ + \n:global IsCheckUpdates;\r\ + \n:global WaitCheckUpdates;\r\ + \n:global GetPubIp;\r\ + \n\r\ + \n:global GetArrDeviceInfo;\r\ + \n:global WaitArrDeviceInfo;\r\ + \n\r\ + \n:global devModel;\r\ + \n:global devRunningMode;\r\ + \n\r\ + \n:global devRbRevision;\r\ + \n:global devRbSerialNumber;\r\ + \n:global devRbVerAvail;\r\ + \n\r\ + \n:global runningChannelVersion;\r\ + \n:global runningChannel;\r\ + \n:global runningVersion;\r\ + \n\r\ + \n:global devBuildTime;\r\ + \n:global ROsVerAvail;\r\ + \n:global PkgUpdStatus;\r\ + \n:global currentUpdateChannel;\r\ + \n\r\ + \n:global ScriptInstallUpdate;\r\ + \n\r\ + \n:global DeviceInfo;\r\ + \n\r\ + \n:global OldMode;\r\ + \n\r\ + \n:global ParseKeyValueStore;\r\ + \n:global RandomDelay;\r\ + \n:global GetRandomNumber;\r\ + \n:global EscapeForRegEx;\r\ + \n:global CharacterMultiply;\r\ + \n:global CharacterReplace;\r\ + \n:global CleanFilePath;\r\ + \n:global CleanName;\r\ + \n\r\ + \n:global CommitBrief;\r\ + \n\r\ + \n:global EitherOr;\r\ + \n:global FormatLine;\r\ + \n:global GetRandom20CharAlNum;\r\ + \n:global GetRandom20CharHex;\r\ + \n:global GetRandomNumber;\r\ + \n:global Grep;\r\ + \n:global IfThenElse;\r\ + \n:global MAX;\r\ + \n:global MIN;\r\ + \n:global HumanReadableNum;\r\ + \n\r\ + \n:global IsFullyConnected;\r\ + \n:global IsDNSResolving;\r\ + \n:global IsTimeSync;\r\ + \n:global IsTimeSyncCached;\r\ + \n:global IsTimeSyncResetNtp;\r\ + \n\r\ + \n:global MkDir;\r\ + \n:global RmDir;\r\ + \n:global RmFile;\r\ + \n:global FileGet;\r\ + \n:global Unix2Dos;\r\ + \n:global UrlEncode;\r\ + \n:global WaitForFile;\r\ + \n:global ScriptLock;\r\ + \n:global SendTelegram;\r\ + \n:global NotificationFunctions;\r\ + \n:global SendNotification;\r\ + \n:global SendNotification2;\r\ + \n:global SymbolForNotification;\r\ + \n:global SymbolByUnicodeName;\r\ + \n:global ValidateSyntax;\r\ + \n:global CheckSumsVerify;\r\ + \n:global GlobalConfChanges;\r\ + \n:global GlobalConfMigration;\r\ + \n:global VersionToNum;\r\ + \n:global RequiredRouterOS;\r\ + \n:global ExitError;\r\ + \n:global ExitOnError;\r\ + \n\r\ + \n:global FetchHugeFiles;\r\ + \n:global FetchUserAgentStr;\r\ + \n:global FileExists;\r\ + \n:global CertificateAvailable;\r\ + \n:global CertificateDownload;\r\ + \n:global CertificateNameByCN;\r\ + \n:global CharacterMultiply;\r\ + \n\r\ + \n:global InspectVar;\r\ + \n:global InspectVarRet;\r\ + \n:global ParseCustom;\r\ + \n\r\ + \n:set IsCheckUpdates do={\r\ + \n :local isInfo [:tobool \$1];\r\ + \n :global updateChannel;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :log info (\$0 . \" \\\$isInfo = \" . \$isInfo); \r\ + \n :local CurrentChannel [ /system/package/update/get channel ];\r\ + \n :if ([ :len \$updateChannel ] > 0 && \$CurrentChannel != \$updateChann\ + el) do={\r\ + \n :retry command={/system/package/update set channel=\$updateChannel} \ + \_delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" Set updates channel to: \$updateChannel\");\r\ + \n }\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local Channel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set currentUpdateChannel \$Channel;\r\ + \n :log info (\$0 . \" Update Channel: \" . \$Channel . \".\");\r\ + \n :local StatusPkg \"undefined\";\r\ + \n :log info (\$0 . \" Checking for updates...\");\r\ + \n :retry command={:set \$StatusPkg ([/system/package/update/check-for-up\ + dates without-paging as-value]->\"status\")} \\\r\ + \n delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" \\\$StatusPkg = \" . \$StatusPkg);\r\ + \n :if (\$StatusPkg = \"New version is available\") do={ :set PkgUpdStatu\ + s true; } else={ :set PkgUpdStatus false; }\r\ + \n :set ROsVerAvail [ :tostr [ /system/package/update/get latest-version \ + ] ];\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if (\$isInfo != false || \$PkgUpdStatus = true) do={\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :retry command={:set (\$arrDeviceInfo->\"devUpdates\") [ /system/pac\ + kage/update/get ]} delay=500ms max=5 on-error={:log error (\"Error\")};\r\ + \n :return true;\r\ + \n } else={\r\ + \n :return \$PkgUpdStatus;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set WaitCheckUpdates do={\r\ + \n :global IsCheckUpdates;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :local Status \"\";\r\ + \n :local Update;\r\ + \n :local WaitCount 0;\r\ + \n :if ([ \$IsCheckUpdates true ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n :while ((\$Status = \"\" || \$Status = \"finding out latest version...\ + \") && \$WaitCount < 60) do={\r\ + \n :delay 1s;\r\ + \n :set WaitCount (\$WaitCount + 1);\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n }\r\ + \n :set Update [ /system/package/update/get ];\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n :if ((\$Update->\"status\") = \"New version is available\") do={\r\ + \n :set PkgUpdStatus true;\r\ + \n } else={\r\ + \n :set PkgUpdStatus false;\r\ + \n }\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :set (\$arrDeviceInfo->\"devUpdates\") \$Update;\r\ + \n :if (\$Status = \"\" || \$Status = \"finding out latest version...\") \ + do={\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set WaitArrDeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :while ([ \$GetArrDeviceInfo ] = false) do={\r\ + \n :delay 1s;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set GetArrDeviceInfo do={\r\ + \n :global LogPrint;\r\ + \n :global arrDeviceInfo;\r\ + \n :global devBoardName;\r\ + \n :global devModel;\r\ + \n :global devRunning;\r\ + \n :global devCurArch;\r\ + \n :global devRbRevision;\r\ + \n :global devRbSerialNumber;\r\ + \n :global devBuildTime;\r\ + \n :global runningChannelVersion;\r\ + \n :global runningChannel;\r\ + \n :global runningVersion;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n\r\ + \n :local DeviceInfo;\r\ + \n :local Resource [/system/resource/get];\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local VersionString [ :tostr (\$Resource->\"version\") ];\r\ + \n :local ChannelStart [ :find \$VersionString \" (\" ];\r\ + \n :local RouterBoard;\r\ + \n :local IsRouterBoard false;\r\ + \n :local RunningMode \"\";\r\ + \n\r\ + \n :set devCurArch [ :tostr (\$Resource->\"architecture-name\") ];\r\ + \n :set devBuildTime [ :tostr (\$Resource->\"build-time\") ];\r\ + \n :set runningChannelVersion \$VersionString;\r\ + \n :set runningVersion \$VersionString;\r\ + \n :set runningChannel \"\";\r\ + \n\r\ + \n :if ([ :typeof \$ChannelStart ] = \"num\" && \$ChannelStart >= 0) do=\ + {\r\ + \n :set runningVersion [ :pick \$VersionString 0 \$ChannelStart ];\r\ + \n :local ChannelRest [ :pick \$VersionString (\$ChannelStart + 2) [ :\ + len \$VersionString ] ];\r\ + \n :local ChannelEnd [ :find \$ChannelRest \")\" ];\r\ + \n :if ([ :typeof \$ChannelEnd ] = \"num\" && \$ChannelEnd >= 0) do={\ + \r\ + \n :set runningChannel [ :pick \$ChannelRest 0 \$ChannelEnd ];\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([:pick \$devBoardName 0 3] != \"CHR\" or [:pick \$devBoardName 0\ + \_3] != \"x86\") do={\r\ + \n :do {\r\ + \n :set RouterBoard [[ :parse \"/system/routerboard/get\" ]];\r\ + \n# :set RouterBoard [ /system/routerboard/get ];\r\ + \n :if ((\$RouterBoard->\"routerboard\") = true) do={\r\ + \n :set IsRouterBoard true;\r\ + \n }\r\ + \n } on-error={}\r\ + \n }\r\ + \n\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get mode ];\r\ + \n } on-error={\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get running ];\r\ + \n } on-error={\r\ + \n :set RunningMode \"\";\r\ + \n }\r\ + \n }\r\ + \n :set devRunning \$RunningMode;\r\ + \n\r\ + \n :if (\$IsRouterBoard = true) do={\r\ + \n :set devModel [ :tostr (\$RouterBoard->\"model\") ];\r\ + \n :set devRbRevision [ :tostr (\$RouterBoard->\"revision\") ];\r\ + \n :set devRbSerialNumber [ :tostr (\$RouterBoard->\"serial-number\") \ + ];\r\ + \n } else={\r\ + \n :set devModel \$devBoardName;\r\ + \n :set devRbRevision \"\";\r\ + \n :set devRbSerialNumber \"\";\r\ + \n }\r\ + \n\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n\r\ + \n :set (\$DeviceInfo->\"board-name\") \$devBoardName;\r\ + \n :set (\$DeviceInfo->\"architecture-name\") \$devCurArch;\r\ + \n :set (\$DeviceInfo->\"model\") \$devModel;\r\ + \n :set (\$DeviceInfo->\"revision\") \$devRbRevision;\r\ + \n :set (\$DeviceInfo->\"serial-number\") \$devRbSerialNumber;\r\ + \n :set (\$DeviceInfo->\"running-mode\") \$devRunning;\r\ + \n :set (\$DeviceInfo->\"build-time\") \$devBuildTime;\r\ + \n :set (\$DeviceInfo->\"running-channel-version\") \$runningChannelVers\ + ion;\r\ + \n :set (\$DeviceInfo->\"running-version\") \$runningVersion;\r\ + \n :set (\$DeviceInfo->\"running-channel\") \$runningChannel;\r\ + \n :set (\$DeviceInfo->\"routerboard\") \$IsRouterBoard;\r\ + \n :set (\$DeviceInfo->\"current-firmware\") [ :tostr (\$RouterBoard->\"\ + current-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"upgrade-firmware\") [ :tostr (\$RouterBoard->\"\ + upgrade-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"package-channel\") \$currentUpdateChannel;\r\ + \n :set (\$DeviceInfo->\"installed-version\") [ :tostr (\$Update->\"inst\ + alled-version\") ];\r\ + \n :set (\$DeviceInfo->\"latest-version\") \$ROsVerAvail;\r\ + \n :set (\$DeviceInfo->\"update-status\") [ :tostr (\$Update->\"status\"\ + ) ];\r\ + \n\r\ + \n :set arrDeviceInfo \$DeviceInfo;\r\ + \n :return \$DeviceInfo;\r\ + \n}\r\ + \n\r\ + \n:set DeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global IfThenElse;\r\ + \n :global FormatLine;\r\ + \n :global CommitBrief;\r\ + \n\r\ + \n :local License [/system/license/get];\r\ + \n :local Snmp [ /snmp/get ];\r\ + \n :local Info [ \$GetArrDeviceInfo ];\r\ + \n\r\ + \n :if ([ :typeof \$Info ] != \"array\") do={\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :return ( \\\r\ + \n [ \$FormatLine \"Hostname\" \$Identity ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"location\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Location\" (\$Snmp->\"location\") ] . \"\\n\") ]\ + \_. \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"contact\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Contact\" (\$Snmp->\"contact\") ] . \"\\n\") ] .\ + \_\\\r\ + \n \"Hardware:\\n\" . \\\r\ + \n [ \$FormatLine \" Board\" (\$Info->\"board-name\") ] . \"\\n\" . \ + \\\r\ + \n [ \$FormatLine \" Arch\" (\$Info->\"architecture-name\") ] . \"\\\ + n\" . \\\r\ + \n [ \$FormatLine \" Model\" (\$Info->\"model\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"revision\") ] > 0) \\\r\ + \n (\" \" . (\$Info->\"revision\")) ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"serial-number\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Serial\" (\$Info->\"serial-number\") ] . \"\ + \\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"nlevel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"nlevel\ + \")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS:\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"level\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"level\ + \")) ] . \"\\n\") ] . \\\r\ + \n [ \$FormatLine \" Channel\" (\$Info->\"package-channel\") ] . \"\ + \\n\" . \\\r\ + \n [ \$FormatLine \" Installed\" (\$Info->\"installed-version\") ] .\ + \_\"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"running-channel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Running\" ((\$Info->\"running-version\") . \ + \" (\" . (\$Info->\"running-channel\") . \")\") ] . \"\\n\") \\\r\ + \n ([ \$FormatLine \" Running\" (\$Info->\"running-version\") ] . \ + \"\\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"latest-version\") ] > 0 && \\\r\ + \n (\$Info->\"installed-version\") != (\$Info->\"latest-version\"))\ + \_\\\r\ + \n ([ \$FormatLine \" Available\" (\$Info->\"latest-version\") ] .\ + \_\"\\n\") ] . \\\r\ + \n [ \$IfThenElse ((\$Info->\"routerboard\") = true && \\\r\ + \n (\$Info->\"current-firmware\") != (\$Info->\"upgrade-firmware\")\ + ) \\\r\ + \n ([ \$FormatLine \" Firmware\" ((\$Info->\"current-firmware\") .\ + \_\" -> \" . (\$Info->\"upgrade-firmware\")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS-Scripts:\\n\" . \\\r\ + \n [ \$FormatLine \" Commit\" [ \$CommitBrief ] ] . \"\\n\" . \\\r\ + \n [ \$FormatLine \" Version\" \$GlobalConfVersion ]);\r\ + \n}\r\ + \n\r\ + \n:if ([ :typeof \$NotificationFunctions ] != \"array\") do={:set Notifica\ + tionFunctions ({})}\r\ + \n\r\ + \n:set GetRandom20CharAlNum do={\r\ + \n :global EitherOr;\r\ + \n :return [ :rndstr length=[ \$EitherOr [ :tonum \$1 ] 20 ] from=\"ABCDE\ + FGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\" ];\r\ + \n}\r\ + \n\r\ + \n:set GetRandom20CharHex do={:global EitherOr; :return [ :rndstr length=[\ + \_\$EitherOr [ :tonum \$1 ] 20 ] from=\"0123456789abcdef\" ]; }\r\ + \n:set GetRandomNumber do={:global EitherOr; :return [ :rndnum from=0 to=[\ + \_\$EitherOr [ :tonum \$1 ] 4294967295 ] ]; }\r\ + \n\r\ + \n:global checkRunJob;\r\ + \n:set checkRunJob do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLock; \r\ + \n :if ([ \$ScriptLock \$Script ] = false) do={:return false}\r\ + \n :foreach Job in=[ /system/script/job/find where script=\$Script ] do={\ + :set Job [ /system/script/job/get \$Job ];\r\ + \n :while ([ :typeof (\$Job->\"parent\") ] = \"id\") do={:set Job [ /sy\ + stem/script/job/get [ find where .id=(\$Job->\"parent\") ] ]}\r\ + \n :if ((\$Job->\"type\") = \"login\") do={:return true};}; :return fa\ + lse; \r\ + \n}\r\ + \n\r\ + \n:set InspectVar do={\r\ + \n :global InspectVarRet;\r\ + \n :put [ :tocrlf [ \$InspectVarRet \$1 ] ];\r\ + \n}\r\ + \n\r\ + \n:set CommitBrief do={\r\ + \n :global CommitId;\r\ + \n :global CommitInfo;\r\ + \n :if (\$CommitId = \"unknown\") do={\r\ + \n :return \"unknown\";\r\ + \n }\r\ + \n :return (\$CommitInfo . \"/\" . [ :pick \$CommitId 0 8 ]);\r\ + \n}\r\ + \n\r\ + \n:set InspectVarRet do={\r\ + \n :local Input \$1;\r\ + \n :local Level (0 + [ :tonum \$2 ]);\r\ + \n :global CharacterReplace;\r\ + \n :global IfThenElse;\r\ + \n :global InspectVarRet;\r\ + \n :local IndentReturn do={\r\ + \n :local Prefix [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Level [ :tonum \$3 ];\r\ + \n :global CharacterMultiply;\r\ + \n :return ([ \$CharacterMultiply \" \" (\$Level * 2) ] . \"-\" . \$Pre\ + fix . \"-> \" . \$Value);\r\ + \n }\r\ + \n :local TypeOf [ :typeof \$Input ];\r\ + \n :local Len [ :len \$Input ];\r\ + \n :local Return [ \$IndentReturn \"type\" \$TypeOf \$Level ];\r\ + \n :if (\$TypeOf = \"array\") do={\r\ + \n :foreach Key,Value in=\$Input do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"key\" \$Key (\$Level + 1) ] . \"\\n\" . \\\r\ + \n [ \$InspectVarRet \$Value (\$Level + 2) ]);\r\ + \n }\r\ + \n } else={\r\ + \n :if (\$TypeOf = \"str\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"len\" \$Len \$Level ]);\r\ + \n :if ([ :typeof [ :find \$Input (\"\\r\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\r\") \"\" ];\r\ + \n }\r\ + \n :if ([ :typeof [ :find \$Input (\"\\n\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\n\") \" \" ];\r\ + \n }\r\ + \n }\r\ + \n :if (\$TypeOf != \"nothing\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"value\" [ \$IfThenElse ([ :len \$Input ] > 80)\ + \_\\\r\ + \n ([ :pick \$Input 0 77 ] . \"...\") \$Input ] \$Level ]);\r\ + \n }\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set GetPubIp do={\r\ + \n :global DetectIpAddrDefault;\r\ + \n :global DetectIpAddrFallback;\r\ + \n\r\ + \n :local PubIpAddr false;\r\ + \n :local Url \$DetectIpAddrDefault;\r\ + \n :local JobName [ :jobname ];\r\ + \n\r\ + \n :do {\r\ + \n :log debug (\$0 . \" Trying to detect public IP using default serv\ + ice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=50ms max=5 on-error={:log error (\$JobName . \" Failed to \ + detect public IP using default service: \" . \$Url . \".\")}\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed to detect public IP using default serv\ + ice: \" . \$Url . \".\");\r\ + \n :set Url \$DetectIpAddrFallBack;\r\ + \n :log debug (\$0 . \" Trying to detect public IP using fallback ser\ + vice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=200ms max=5 on-error={:log error (\$JobName . \" Failed to\ + \_detect public IP using fallback service: \" . \$Url . \".\")}\r\ + \n };\r\ + \n :local IpLen ([ :len \$PubIpAddr ] -1);\r\ + \n :if (\$PubIpAddr = false) do={:set PubIpAddr \"not-detected\"} else=\ + {:set PubIpAddr [:pick \$PubIpAddr 0 \$IpLen]}\r\ + \n :return \$PubIpAddr;\r\ + \n}\r\ + \n\r\ + \n:set FormatLine do={\r\ + \n :local Key [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Indent [ :tonum \$3 ];\r\ + \n :local Spaces;\r\ + \n :local Return \"\";\r\ + \n :global CharacterMultiply;\r\ + \n :global EitherOr;\r\ + \n :set Indent [ \$EitherOr \$Indent 16 ];\r\ + \n :local Spaces [ \$CharacterMultiply \" \" \$Indent ];\r\ + \n :if ([ :len \$Key ] > 0) do={ :set Return (\$Key . \":\")}\r\ + \n :if ([ :len \$Key ] > (\$Indent - 2)) do={:set Return (\$Return . \"\\\ + n\" . [ :pick \$Spaces 0 \$Indent ] . \$Value);\r\ + \n } else={:set Return (\$Return . [ :pick \$Spaces 0 (\$Indent - [ :len \ + \$Return ]) ] . \$Value)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ParseCustom do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Start [:find [ \$Str ] [:tostr \$2]];\r\ + \n :local End [ :tostr \$3 ];\r\ + \n :local Num [ :tonum \$4 ];\r\ + \n :local Return;\r\ + \n :if ([:len \$Num] = 0) do={:set Num \"0\"}\r\ + \n :local rest [:pick \$Str (\$Start+\$Num) [:len \$Str]]; \r\ + \n :if ([:len \$End ] = 0) do={:set Return [:pick \$Str \$Num \$Start]} e\ + lse={\r\ + \n :set End [:find \$rest \$End];\r\ + \n :set Return [:pick \$rest 0 \$End];\r\ + \n }\r\ + \n:return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set LogPrint do={\r\ + \n :local Severity [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Message [ :tostr \$3 ];\r\ + \n :global PrintDebug;\r\ + \n :global PrintDebugOverride;\r\ + \n :global EitherOr;\r\ + \n :local Debug [ \$EitherOr (\$PrintDebugOverride->\$Name) \$PrintDebug \ + ];\r\ + \n :local PrintSeverity do={\r\ + \n :global TerminalColorOutput;\r\ + \n :if (\$TerminalColorOutput != true) do={:return \$1}\r\ + \n :local Color { debug=96; info=97; warning=93; error=91 };\r\ + \n :return (\"\\1B[\" . \$Color->\$1 . \"m\" . \$1 . \"\\1B[0m\");\r\ + \n}\r\ + \n :local Log ([ \$EitherOr \$Name \"\" ] . \": \" . \$Message);\ + \r\ + \n :if (\$Severity ~ (\"^(custom|debug|error|info)\\\$\")) do={\r\ + \n :if (\$Severity = \"debug\") do={:log debug \$Log}\r\ + \n :if (\$Severity = \"error\") do={:log error \$Log}\r\ + \n :if (\$Severity = \"info\" ) do={:log info \$Log}\r\ + \n } else={:log warning \$Log; :set Severity \"warning\"}\r\ + \n :if (\$Severity != \"debug\" || \$Debug = true) do={:put ([ \$PrintSev\ + erity \$Severity ] . \": \" . \$Message)}\r\ + \n}\r\ + \n\r\ + \n:set CertificateAvailable do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :local UseFor [ :tostr \$2 ];\r\ + \n\r\ + \n :global CertificateDownload;\r\ + \n :global ParseKeyValueStore;\r\ + \n\r\ + \n :if ([ :len \$UseFor ] = 0) do={\r\ + \n :log warning (\$0 . \"The intended use is undefined!\");\r\ + \n :set UseFor \"undefined\";\r\ + \n }\r\ + \n :if ([ /system/resource/get free-hdd-space ] < 8388608 && \\\r\ + \n [ /certificate/settings/get crl-download ] = true && \\\r\ + \n [ /certificate/settings/get crl-store ] = \"system\") do={\r\ + \n :log warning (\$0 . \"This system has low free flash space but \" . \ + \\\r\ + \n \"is configured to download certificate CRLs to system!\");\r\ + \n }\r\ + \n :if ([ :len \$CommonName ] = 0) do={\r\ + \n :log warning (\$0 . \"No CommonName given!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if (((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" || \\\ + \r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \$UseFor || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\r\ + \n [ :len [ /certificate/builtin/find where common-name=\$CommonName\ + \_] ] > 0) do={\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n :log info (\$0 . \"Certificate with CommonName '\" . \$CommonName . \ + \"' not available.\");\r\ + \n :if ([ \$CertificateDownload \$CommonName ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] > 1\ + ) do={\r\ + \n :log info (\$0 . \"There are \" . \$CertCount . \" Certificates with\ + \_CommonName '\" . \$CommonName . \"'. Should be ok.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :local CertVal [ /certificate/get [ find where common-name=\$CommonNam\ + e ] ];\r\ + \n :while ((\$CertVal->\"akid\") != \"\" && (\$CertVal->\"akid\") != (\$C\ + ertVal->\"skid\")) do={\r\ + \n :if ([ :len [ /certificate/find where skid=(\$CertVal->\"akid\") ] ]\ + \_= 0) do={\r\ + \n :local IssuerCN ([ \$ParseKeyValueStore (\$CertVal->\"issuer\") ]-\ + >\"CN\");\r\ + \n :log info (\$0 . \"Certificate chain for '\" . \$CommonName . \"' \ + is incomplete, missing '\" . \$IssuerCN . \"'.\");\r\ + \n :if ([ \$CertificateDownload \$IssuerCN ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :set CertVal [ /certificate/get [ find where skid=(\$CertVal->\"akid\ + \") ] ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateDownload do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :global ScriptUpdatesBaseUrl;\r\ + \n :global ScriptUpdatesUrlSuffix;\r\ + \n :global CertificateNameByCN;\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :log info (\$0 . \"Downloading and importing certificate with \" . \\\ + \r\ + \n \"CommonName '\" . \$CommonName . \"'.\");\r\ + \n :local FileName ([ \$CleanName \$CommonName ] . \".pem\");\r\ + \n :do {\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-header\ + -field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n (\$ScriptUpdatesBaseUrl . \"certs/\" . \$FileName . \$ScriptUpdate\ + sUrlSuffix) \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n } on-error={\r\ + \n :log warning (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \\\r\ + \n \"' from repository! Trying fallback to mkcert.org...\");\r\ + \n :do {\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if ([ :len [ /certificate/find where common-name=\"ISRG Root X1\"\ + \_] ] = 0 && \\\r\ + \n !(((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" \ + || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \"fetch\" || \ + \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\ + \r\ + \n [ :len [ /certificate/builtin/find where common-name=\"ISRG\ + \_Root X1\" ] ] > 0)) do={\r\ + \n :log error (\$0 . \" Required certificate is not available.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-head\ + er-field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n url=\"https://mkcert.org/generate/\" http-data=[ :serialize to=jso\ + n ({ \$CommonName }) ] \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n :if ([ /file/get \$FileName size ] = 0) do={\r\ + \n \$RmFile \$FileName;\r\ + \n :error false;\r\ + \n }\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \"'!\");\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n /certificate/import file-name=\$FileName passphrase=\"\" as-value;\r\ + \n :delay 1s;\r\ + \n \$RmFile \$FileName;\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n /certificate/remove [ find where name~(\"^\" . \$FileName . \"_[0-9]\ + +\\\$\") ];\r\ + \n :log warning (\$0 . \" Certificate with CommonName '\" . \$CommonNam\ + e . \"' still unavailable!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :foreach Cert in=[ /certificate/find where name~(\"^\" . \$FileName . \ + \"_[0-9]+\\\$\") ] do={\r\ + \n \$CertificateNameByCN [ /certificate/get \$Cert common-name ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateNameByCN do={\r\ + \n :local Match [ :tostr \$1 ];\r\ + \n :global CleanName;\r\ + \n :local Cert ([ /certificate/find where (common-name=\$Match or fingerp\ + rint=\$Match or name=\$Match) ]->0);\r\ + \n :if ([ :len \$Cert ] = 0) do={\r\ + \n :log warning (\$0 .\" No matching certificate found.\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CommonName [ /certificate/get \$Cert common-name ];\r\ + \n /certificate/set \$Cert name=[ \$CleanName \$CommonName ];\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FileExists do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local Type [ :tostr \$2 ];\r\ + \n :global FileGet;\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={ :return false; }\r\ + \n :if ([ :len (\$FileVal->\"size\") ] = 0) do={ :return false; }\r\ + \n :if ([ :len \$Type ] = 0 || \$FileVal->\"type\" = \$Type) do={ :return\ + \_true; }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set Grep do={\r\ + \n :local Input ([ :tostr \$1 ] . \"\\n\");\r\ + \n :local Pattern [ :tostr \$2 ];\r\ + \n :if ([ :typeof [ :find \$Input \$Pattern ] ] = \"nil\") do={:return []\ + }\r\ + \n :do {\r\ + \n :local Line [ :pick \$Input 0 [ :find \$Input \"\\n\" ] ];\r\ + \n :if ([ :typeof [ :find \$Line \$Pattern ] ] = \"num\") do={:return \ + \$Line}\r\ + \n :set Input [ :pick \$Input ([ :find \$Input \"\\n\" ] + 1) [ :len \$\ + Input ] ];\r\ + \n } while=([ :len \$Input ] > 0);\r\ + \n :return [];\r\ + \n}\r\ + \n\r\ + \n:set HumanReadableNum do={\r\ + \n :local Input [ :tonum \$1 ];\r\ + \n :local Base [ :tonum \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global IfThenElse;\r\ + \n :local Prefix \"kMGTPE\";\r\ + \n :local Pow 1;\r\ + \n :set Base [ \$EitherOr \$Base 1024 ];\r\ + \n :local Bin [ \$IfThenElse (\$Base = 1024) \"i\" \"\" ];\r\ + \n :if (\$Input < \$Base) do={:return \$Input}\r\ + \n :for I from=0 to=[ :len \$Prefix ] do={\r\ + \n :set Pow (\$Pow * \$Base);\r\ + \n :if (\$Input / \$Base < \$Pow) do={\r\ + \n :set Prefix [ :pick \$Prefix \$I ];\r\ + \n :local Tmp1 (\$Input * 100 / \$Pow);\r\ + \n :local Tmp2 (\$Tmp1 / 100);\r\ + \n :if (\$Tmp2 >= 100) do={:return (\$Tmp2 . \$Prefix . \$Bin)}\r\ + \n :return (\$Tmp2 . \".\" . \\\r\ + \n [ :pick \$Tmp1 [ :len \$Tmp2 ] ([ :len \$Tmp1 ] - [ :len \$Tmp\ + 2 ] + 1) ] . \\\r\ + \n \$Prefix . \$Bin);\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set CleanFilePath do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CharacterReplace;\r\ + \n :while (\$Path ~ \"//\") do={ :set \$Path [ \$CharacterReplace \$Path \ + \"//\" \"/\" ]; }\r\ + \n :if ([ :pick \$Path 0 ] = \"/\") do={ :set Path [ :pick \$Path 1 [ :le\ + n \$Path ] ]; }\r\ + \n :if ([ :pick \$Path ([ :len \$Path ] - 1) ] = \"/\") do={ :set Path [ \ + :pick \$Path 0 ([ :len \$Path ] - 1) ]; }\r\ + \n :return \$Path;\r\ + \n}\r\ + \n\r\ + \n:set CleanName do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Return \"\";\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :typeof [ find \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr\ + stuvwxyz0123456789\" \$Char ] ] = \"nil\") do={\r\ + \n :do {:if ([ :len \$Return ] = 0) do={:error true};\r\ + \n :if ([ :pick \$Return ([ :len \$Return ] - 1) ] = \"-\") do={:er\ + ror true}; :set Char \"-\"} on-error={:set Char \"\"}; \r\ + \n };\r\ + \n :set Return (\$Return . \$Char)};\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set RandomDelay do={\r\ + \n :local Time [ :tonum \$1 ];\r\ + \n :local Unit [ :tostr \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global GetRandomNumber;\r\ + \n :global MAX;\r\ + \n :if (\$Time = 0) do={:return false}\r\ + \n :delay ([ \$MAX 10 [ \$GetRandomNumber ([ :tonsec [ :totime (\$Time . \ + [ \$EitherOr \$Unit \"s\" ]) ] ] / 1000000) ] ] . \"ms\");\r\ + \n}\r\ + \n\r\ + \n:set EscapeForRegEx do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"^.[]\\\$()|*+\?{}\\\\\");\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :find \$Chars \$Char ]) do={:set Char (\"\\\\\" . \$Char)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ExitError do={\r\ + \n :local ExitOK [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Error [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :if (\$ExitOK = \"false\") do={\r\ + \n :log error (\$Name [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\\\$\") \ + \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with er\ + ror\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \ + \$Error) \".\" ]);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set ExitOnError do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :local Error [ :tostr \$2 ];\r\ + \n :global IfThenElse;\r\ + \n :log error (\$Name . \" \" . [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\ + \\\$\") \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with erro\ + r\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \$E\ + rror) \".\" ]);\r\ + \n}\r\ + \n\r\ + \n:set MiniDateTimeStamp do={\r\ + \n :local mikro [ :tobool \$1 ];\r\ + \n /system/clock;\r\ + \n :local vdate [get date];\r\ + \n :local vtime [get time];\r\ + \n :local vdoff [:toarray \"0,4,5,7,8,10\"];\r\ + \n :local MM [:pick \$vdate (\$vdoff->2) (\$vdoff->3)];\r\ + \n :local M [:tonum \$MM];\r\ + \n :if (\$vdate ~ \".../../....\") do={\r\ + \n :set vdoff [:toarray \"7,11,1,3,4,6\"];\r\ + \n :set M ([:find \"xxanebarprayunulugepctovecANEBARPRAYUNULUGE\ + PCTOVEC\" [:pick \$vdate (\$vdoff->2) (\$vdoff->3)] -1] / 2);\r\ + \n :if (\$M>12) do={:set M (\$M - 12)}\r\ + \n :set MM [:pick (100 + \$M) 1 3];\r\ + \n }\r\ + \n :local yyyy [:pick \$vdate (\$vdoff->0) (\$vdoff->1)];\r\ + \n :local dd [:pick \$vdate (\$vdoff->4) (\$vdoff->5)];\r\ + \n :local HH [:pick \$vtime 0 2];\r\ + \n :local mm [:pick \$vtime 3 5];\r\ + \n :local ss [:pick \$vtime 6 8];\r\ + \n :local Return;\r\ + \n :if (\$mikro = true) do={\r\ + \n :set Return ([:pick \$yyyy 2 4] . [:pick \$dd 0 1] . [:pick \$HH 0\ + \_1] . [:pick \$mm 3 4] . [:pick \$ss 6 7]);\r\ + \n } else={\r\ + \n :set Return ([\$yyyy] . [\$MM] . [\$dd] . [\$HH] . [\$mm] . [\$ss]);\ + \r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set WaitForFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local WaitTime [ :totime \$2 ];\r\ + \n :global CleanFilePath;\r\ + \n :global EitherOr;\r\ + \n :global MAX;\r\ + \n :set FileName [ \$CleanFilePath \$FileName ];\r\ + \n :local Delay ([ \$MAX [ \$EitherOr \$WaitTime 2s ] 100ms ] / 9);\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } delay=\$Delay max=10;\r\ + \n } on-error={ }\r\ + \n :while ([ :len [ /file/find where name=\$FileName ] ] > 0) do={\r\ + \n :do {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } on-error={ }\r\ + \n :delay \$Delay;\r\ + \n :set Delay (\$Delay * 3 / 2);\r\ + \n }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set FileGet do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global WaitForFile;\r\ + \n :if ([ \$WaitForFile \$FileName 0s ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :local FileVal false;\r\ + \n :do {\r\ + \n :set FileVal [ /file/get \$FileName ];\r\ + \n } on-error={ }\r\ + \n :return \$FileVal;\r\ + \n}\r\ + \n\r\ + \n:set RmDir do={\r\ + \n :local DirName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing directory: \". \$DirName);\r\ + \n :local DirVal [ \$FileGet \$DirName ];\r\ + \n :if (\$DirVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n };\r\ + \n :if (\$DirVal->\"type\" != \"directory\") do={\r\ + \n :log error (\$0 . \" Directory '\" . \$DirName . \"' is not a direct\ + ory.\");\r\ + \n :return false;\r\ + \n };\r\ + \n :onerror Err {\r\ + \n /file/remove \$DirName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing directory '\" . \$DirName . \"' failed\ + : \" . \$Err);\r\ + \n :return false;\r\ + \n };\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set RmFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing file: \". \$FileName);\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if (\$FileVal->\"type\" = \"directory\" || \$FileVal->\"type\" = \"di\ + sk\") do={\r\ + \n :log error (\$0 . \" File '\" . \$FileName . \"' is not a file.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/remove \$FileName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing file '\" . \$FileName . \"' failed: \"\ + \_. \$Err); :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MkDir do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CleanFilePath;\r\ + \n :global FileGet;\r\ + \n :global RmDir;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :local MkTmpfs do={\r\ + \n :global WaitForFile;\r\ + \n :local TmpFs [ /disk/find where slot=tmpfs type=tmpfs ];\r\ + \n :if ([ :len \$TmpFs ] = 1) do={\r\ + \n :if ([ /disk/get \$TmpFs disabled ] = true) do={\r\ + \n :log info (\$0 . \" The tmpfs is disabled, enabling.\");\r\ + \n /disk/enable \$TmpFs;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :log info (\$0 . \" Creating disk of type tmpfs.\");\r\ + \n \$RmDir \"tmpfs\";\r\ + \n :onerror Err {\r\ + \n /disk/add slot=tmpfs type=tmpfs tmpfs-max-size=([ /system/resource\ + /get total-memory ] / 3);\r\ + \n \$WaitForFile \"tmpfs\";\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Creating disk of type tmpfs failed: \" . \$\ + Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :set Path [ \$CleanFilePath \$Path ];\r\ + \n :if (\$Path = \"\") do={\r\ + \n :return true;\r\ + \n }\r\ + \n :log debug (\$0 . \" Making directory: \" . \$Path);\r\ + \n :local PathVal [ \$FileGet \$Path ];\r\ + \n :if (\$PathVal->\"type\" = \"directory\") do={\r\ + \n :log debug (\$0 . \" ... which already exists.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :pick \$Path 0 5 ] = \"tmpfs\") do={\r\ + \n :if ([ \$MkTmpfs ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/add type=\"directory\" name=\$Path;\r\ + \n \$WaitForFile \$Path;\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Making directory '\" . \$Path . \"' failed: \ + \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MAX do={:if (\$1 > \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n:set MIN do={:if (\$1 < \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set CharacterReplace do={\r\ + \n :local String [ :tostr \$1 ];\r\ + \n :local ReplaceFrom [ :tostr \$2 ];\r\ + \n :local ReplaceWith [ :tostr \$3 ];\r\ + \n :local Return \"\";\r\ + \n :if (\$ReplaceFrom = \"\") do={:return \$String}\r\ + \n :while ([ :typeof [ :find \$String \$ReplaceFrom ] ] != \"nil\") do={\ + \r\ + \n :local Pos [ :find \$String \$ReplaceFrom ];\r\ + \n :set Return (\$Return . [ :pick \$String 0 \$Pos ] . \$ReplaceWith);\ + \r\ + \n :set String [ :pick \$String (\$Pos + [ :len \$ReplaceFrom ]) [ :len\ + \_\$String ] ];\r\ + \n }\r\ + \n :return (\$Return . \$String);\r\ + \n}\r\ + \n\r\ + \n:set IfThenElse do={\r\ + \n :if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1 ] = true) do={:return \ + \$2};\r\ + \n :return \$3;\r\ + \n}\r\ + \n\r\ + \n:set EitherOr do={\r\ + \n :global IfThenElse;\r\ + \n :if ([ :typeof \$1 ] = \"num\") do={:return [ \$IfThenElse (\$1 != 0) \ + \$1 \$2 ]}\r\ + \n :if ([ :typeof \$1 ] = \"time\") do={:return [ \$IfThenElse (\$1 > 0s)\ + \_\$1 \$2 ]}\r\ + \n# this works for boolean values, literal ones with parentheses\r\ + \n :return [ \$IfThenElse ([ :len [ :tostr \$1 ] ] > 0) \$1 \$2 ];\r\ + \n}\r\ + \n\r\ + \n:set ParseKeyValueStore do={\r\ + \n :local Source \$1;\r\ + \n :if ([ :pick \$Source 0 1 ] = \"{\") do={\r\ + \n :do {\r\ + \n :return [ :deserialize from=json \$Source ];\r\ + \n } on-error={ }\r\ + \n } \r\ + \n :if ([ :typeof \$Source ] != \"array\") do={:set Source [ :tostr \$1 ]\ + }\r\ + \n :local Result ({});\r\ + \n :foreach KeyValue in=[ :toarray \$Source ] do={\r\ + \n :if ([ :find \$KeyValue \"=\" ]) do={\r\ + \n :local Key [ :pick \$KeyValue 0 [ :find \$KeyValue \"=\" ] ];\r\ + \n :local Value [ :pick \$KeyValue ([ :find \$KeyValue \"=\" ] + 1) [\ + \_:len \$KeyValue ] ];\r\ + \n :if (\$Value=\"true\") do={:set Value true}\r\ + \n :if (\$Value=\"false\") do={:set Value false}\r\ + \n :set (\$Result->\$Key) \$Value;\r\ + \n } else={:set (\$Result->\$KeyValue) true}\r\ + \n }\r\ + \n :return \$Result;\r\ + \n}\r\ + \n:set RequiredRouterOS do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :local Required [ :tostr \$2 ];\r\ + \n :local Warn [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :global VersionToNum;\r\ + \n :if (!(\$Required ~ \"^\\\\d+\\\\.\\\\d+((alpha|beta|rc|\\\\.)\\\\d+|)\ + \\\$\")) do={\r\ + \n :log error (\$0 . \" No valid RouterOS version: \" . \$Required);\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ \$VersionToNum \$Required ] > [ \$VersionToNum [ /system/packag\ + e/update/get installed-version ] ]) do={\r\ + \n :if (\$Warn = \"true\") do={\r\ + \n :log warning (\$0 . \" This \" . [ \$IfThenElse ([ :pick \$Caller \ + 0 ] = (\"\\\$\")) \"function\" \"script\" ] . \\\r\ + \n \" '\" . \$Caller . \"' (at least specific functionality) requir\ + es RouterOS \" . \$Required . \". Please update!\");\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set ValidateSyntax do={\r\ + \n :local Code [ :tostr \$1 ];\r\ + \n :global LogPrint;\r\ + \n :onerror Err {\r\ + \n [ :parse (\":local Validate do={\\n\" . \$Code . \"\\n}\") ];\r\ + \n } do={\r\ + \n \$LogPrint debug \$0 (\"Valdation failed: \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set VersionToNum do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Multi 0x1000000;\r\ + \n :local Return 0;\r\ + \n :global CharacterReplace;\r\ + \n :set Input [ \$CharacterReplace \$Input \".\" \",\" ];\r\ + \n :foreach I in={ \"zero\"; \"alpha\"; \"beta\"; \"rc\" } do={\r\ + \n :set Input [ \$CharacterReplace \$Input \$I (\",\" . \$I . \",\") ];\ + \r\ + \n }\r\ + \n :foreach Value in=([ :toarray \$Input ], 0) do={\r\ + \n :local Num [ :tonum \$Value ];\r\ + \n :if (\$Multi = 0x100) do={\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={\r\ + \n :set Return (\$Return + 0xff00);\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n } else={\r\ + \n :if (\$Value = \"zero\") do={ }\r\ + \n :if (\$Value = \"alpha\") do={ :set Return (\$Return + 0x3f00); \ + }\r\ + \n :if (\$Value = \"beta\") do={ :set Return (\$Return + 0x5f00); }\ + \r\ + \n :if (\$Value = \"rc\") do={ :set Return (\$Return + 0x7f00); }\r\ + \n }\r\ + \n }\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={ :set Return (\$Return + (\$Va\ + lue * \$Multi)); }\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ScriptLock do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local WaitMax [ :totime \$2 ];\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global ScriptLockOrder;\r\ + \n :if ([ :typeof \$ScriptLockOrder ] = \"nothing\") do={\r\ + \n :set ScriptLockOrder ({});\r\ + \n }\r\ + \n :if ([ :typeof (\$ScriptLockOrder->\$Script) ] = \"nothing\") do={\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :local JobCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :return [ :len [ /system/script/job/find where script=\$Script ] ];\ + \r\ + \n }\r\ + \n :local TicketCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :local Count 0;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:set Count (\$Count \ + + 1)}\r\ + \n }\r\ + \n :return \$Count;\r\ + \n }\r\ + \n :local IsFirstTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Check [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if (\$Ticket = \$Check) do={:return true}\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\" && \$Ticket != \$Check) d\ + o={:return false}\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :local AddTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Add [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :while (true) do={\r\ + \n :local Pos [ :len (\$ScriptLockOrder->\$Script) ];\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Pos) \$Add;\r\ + \n :delay 10ms;\r\ + \n :if ((\$ScriptLockOrder->\$Script->\$Pos) = \$Add) do={:return tru\ + e}\r\ + \n }\r\ + \n }\r\ + \n :local RemoveTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Remove [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Id,Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :while ((\$ScriptLockOrder->\$Script->\$Id) = \$Remove) do={\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Id);\r\ + \n :delay 10ms;\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n :local CleanupTickets do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:return false}\r\ + \n }\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :if ([ :typeof \$WaitMax ] = \"nil\" ) do={:set WaitMax 0s}\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\r\ + \n :log error (\$0 . \" A script named '\" . \$Script . \"' does not ex\ + ist!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$JobCount \$Script ] = 0) do={\r\ + \n \$LogPrint error \$0 (\"No script '\" . \$Script . \"' is running!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$TicketCount \$Script ] >= [ \$JobCount \$Script ]) do={\r\ + \n :log error (\$0 . \" More tickets than running scripts '\" . \$Scrip\ + t . \"', resetting!\");\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n /system/script/job/remove [ find where script=\$Script ];\r\ + \n }\r\ + \n :local MyTicket [ \$GetRandom20CharAlNum 6 ];\r\ + \n \$AddTicket \$Script \$MyTicket;\r\ + \n :local WaitInterval (\$WaitMax / 20);\r\ + \n :local WaitTime \$WaitMax;\r\ + \n :while (\$WaitTime > 0 && \\\r\ + \n ([ \$IsFirstTicket \$Script \$MyTicket ] = false || \\\r\ + \n [ \$TicketCount \$Script ] < [ \$JobCount \$Script ])) do={\r\ + \n :set WaitTime (\$WaitTime - \$WaitInterval);\r\ + \n :delay \$WaitInterval;\r\ + \n }\r\ + \n :if ([ \$IsFirstTicket \$Script \$MyTicket ] = true && \\\r\ + \n [ \$TicketCount \$Script ] = [ \$JobCount \$Script ]) do={\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$CleanupTickets \$Script;\r\ + \n :return true;\r\ + \n }\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$LogPrint debug \$0 (\"Script '\" . \$Script . \"' started more than \ + once\" . \\\r\ + \n [ \$IfThenElse (\$WaitTime < \$WaitMax) \" and timed out waiting for\ + \_lock\" \"\" ] . \"...\");\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set SendNotification do={ :onerror Err {\r\ + \n :global SendNotification2;\r\ + \n \$SendNotification2 ({ origin=\$0; subject=\$1; message=\$2; link=\$3;\ + \_silent=\$4 });\r\ + \n} do={:global ExitOnError; \$ExitOnError \$0 \$Err}\r\ + \n}\r\ + \n\r\ + \n:set SendNotification2 do={\r\ + \n :local Notification \$1;\r\ + \n :global NotificationFunctions;\r\ + \n :foreach FunctionName,Discard in=\$NotificationFunctions do={\r\ + \n (\$NotificationFunctions->\$FunctionName) (\"\\\$NotificationFunctio\ + ns->\\\"\" . \$FunctionName . \"\\\"\") \$Notification;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set SymbolByUnicodeName do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :global EitherOr;\r\ + \n :global SymbolsExtra;\r\ + \n :local Symbols ({\r\ + \n \"abacus\"=\"\\F0\\9F\\A7\\AE\";\r\ + \n \"alarm-clock\"=\"\\E2\\8F\\B0\";\r\ + \n \"arrow-down\"=\"\\E2\\AC\\87\";\r\ + \n \"arrow-up\"=\"\\E2\\AC\\86\";\r\ + \n \"calendar\"=\"\\F0\\9F\\93\\85\";\r\ + \n \"card-file-box\"=\"\\F0\\9F\\97\\83\";\r\ + \n \"chart-decreasing\"=\"\\F0\\9F\\93\\89\";\r\ + \n \"chart-increasing\"=\"\\F0\\9F\\93\\88\";\r\ + \n \"cloud\"=\"\\E2\\98\\81\";\r\ + \n \"cross-mark\"=\"\\E2\\9D\\8C\";\r\ + \n \"earth\"=\"\\F0\\9F\\8C\\8D\";\r\ + \n \"fire\"=\"\\F0\\9F\\94\\A5\";\r\ + \n \"floppy-disk\"=\"\\F0\\9F\\92\\BE\";\r\ + \n \"gear\"=\"\\E2\\9A\\99\";\r\ + \n \"heart\"=\"\\E2\\99\\A5\";\r\ + \n \"high-voltage-sign\"=\"\\E2\\9A\\A1\";\r\ + \n \"incoming-envelope\"=\"\\F0\\9F\\93\\A8\";\r\ + \n \"information\"=\"\\E2\\84\\B9\";\r\ + \n \"large-orange-circle\"=\"\\F0\\9F\\9F\\A0\";\r\ + \n \"large-red-circle\"=\"\\F0\\9F\\94\\B4\";\r\ + \n \"link\"=\"\\F0\\9F\\94\\97\";\r\ + \n \"lock-with-ink-pen\"=\"\\F0\\9F\\94\\8F\";\r\ + \n \"memo\"=\"\\F0\\9F\\93\\9D\";\r\ + \n \"mobile-phone\"=\"\\F0\\9F\\93\\B1\";\r\ + \n \"pushpin\"=\"\\F0\\9F\\93\\8C\";\r\ + \n \"scissors\"=\"\\E2\\9C\\82\";\r\ + \n \"scroll\"=\"\\F0\\9F\\93\\9C\";\r\ + \n \"smiley-partying-face\"=\"\\F0\\9F\\A5\\B3\";\r\ + \n \"smiley-smiling-face\"=\"\\E2\\98\\BA\";\r\ + \n \"smiley-winking-face-with-tongue\"=\"\\F0\\9F\\98\\9C\";\r\ + \n \"sparkles\"=\"\\E2\\9C\\A8\";\r\ + \n \"speech-balloon\"=\"\\F0\\9F\\92\\AC\";\r\ + \n \"star\"=\"\\E2\\AD\\90\";\r\ + \n \"warning-sign\"=\"\\E2\\9A\\A0\";\r\ + \n \"white-heavy-check-mark\"=\"\\E2\\9C\\85\"\r\ + \n }, \$SymbolsExtra);\r\ + \n :local Magic [ :pick [ /system/clock/get date ] 4 10 ];\r\ + \n :local Special {\r\ + \n \"information-04-01\"=\"\\F0\\9F\\9A\\BB\";\r\ + \n \"large-orange-circle-04-01\"=\"\\F0\\9F\\8D\\8A\";\r\ + \n \"large-orange-circle-10-31\"=\"\\F0\\9F\\8E\\83\";\r\ + \n \"large-red-circle-04-01\"=\"\\F0\\9F\\8D\\92\" };\r\ + \n :if ([ :len (\$Symbols->\$Name) ] = 0) do={\r\ + \n :log warning (\$0 . \"No symbol available for name '\" . \$Name . \"\ + '!\");\r\ + \n :return \"\";\r\ + \n }\r\ + \n :return ([ \$EitherOr (\$Special->(\$Name . \$Magic)) (\$Symbols->\$Na\ + me) ] . \"\\EF\\B8\\8F\");\r\ + \n}\r\ + \n\r\ + \n:set SymbolForNotification do={\r\ + \n :global NotificationsWithSymbols;\r\ + \n :global SymbolByUnicodeName;\r\ + \n :global IfThenElse;\r\ + \n :if (\$NotificationsWithSymbols != true) do={:return [ \$IfThenElse ([\ + \_:len \$2 ] > 0) ([ :tostr \$2 ] . \" \") \"\" ]}\r\ + \n :local Return \"\";\r\ + \n :foreach Symbol in=[ :toarray \$1 ] do={:set Return (\$Return . [ \$Sy\ + mbolByUnicodeName \$Symbol ])}\r\ + \n :return (\$Return . \" \");\r\ + \n}\r\ + \n\r\ + \n:set Unix2Dos do={:return [ :tocrlf [ :tostr \$1 ] ]}\r\ + \n:set UrlEncode do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"\\n\\r !\\\"#\\\$%&'()*+,:;<=>\?@[\\\\]^`{|}~\");\r\ + \n :local Subs { \"%0A\"; \"%0D\"; \"%20\"; \"%21\"; \"%22\"; \"%23\"; \"\ + %24\"; \"%25\"; \"%26\"; \"%27\";\r\ + \n \"%28\"; \"%29\"; \"%2A\"; \"%2B\"; \"%2C\"; \"%3A\"; \"%3B\"; \ + \"%3C\"; \"%3D\"; \"%3E\"; \"%3F\";\r\ + \n \"%40\"; \"%5B\"; \"%5C\"; \"%5D\"; \"%5E\"; \"%60\"; \"%7B\"; \ + \"%7C\"; \"%7D\"; \"%7E\" };\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :local Replace [ :find \$Chars \$Char ];\r\ + \n :if ([ :typeof \$Replace ] = \"num\") do={:set Char (\$Subs->\$Repla\ + ce)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set IsDNSResolving do={\r\ + \n :do {:resolve \"api.telegram.org\"} on-error={:return false}\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsFullyConnected do={\r\ + \n :global IsDNSResolving;\r\ + \n :global IsTimeSync;\r\ + \n :if ([ \$IsDNSResolving ] = false) do={:return false};\r\ + \n :if ([ \$IsTimeSync ] = false) do={:return false};\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsTimeSync do={\r\ + \n :global IsTimeSyncCached;\r\ + \n :global IsTimeSyncResetNtp;\r\ + \n :if (\$IsTimeSyncCached = true) do={:return true}\r\ + \n :if ([ /system/ntp/client/get enabled ] = true) do={\r\ + \n :if ([ /system/ntp/client/get status ] = \"synchronized\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :local Uptime [ /system/resource/get uptime ];\r\ + \n :if ([ :typeof \$IsTimeSyncResetNtp ] = \"nothing\") do={\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n }\r\ + \n :if (\$Uptime - \$IsTimeSyncResetNtp < 3m) do={:return false}\r\ + \n :log warning \"\$0 The ntp client is configured, but did not sync.\"\ + ;\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n /system/ntp/client/set enabled=no;\r\ + \n :delay 20ms;\r\ + \n /system/ntp/client/set enabled=yes;\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ /system/license/get ]->\"level\" = \"free\" || \\\r\ + \n [ /system/resource/get ]->\"board-name\" = \"x86\") do={\r\ + \n :log debug \"\$0 No ntp client configured, relying on RTC for CHR fr\ + ee license and x86.\";\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ /ip/cloud/get update-time ] = true) do={\r\ + \n :if ([ :typeof [ /ip/cloud/get public-address ] ] = \"ip\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :log debug \"\$0 No time source configured! Returning gracefully...\";\ + \r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set FetchHugeFiles do={\r\ + \n :local ScriptName [ :tostr \$1 ];\r\ + \n :local Url [ :tostr \$2 ];\r\ + \n :local CheckCert [ :tostr \$3 ];\r\ + \n\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global MkDir;\r\ + \n :global RmDir;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :set CheckCert [ \$IfThenElse (\$CheckCert = \"false\") \"no\" \"yes-w\ + ithout-crl\" ];\r\ + \n\r\ + \n :local DirName (\"tmpfs/\" . [ \$CleanName \$ScriptName ]);\r\ + \n :if ([ \$MkDir \$DirName ] = false) do={\r\ + \n \$LogPrint error \$0 (\"Failed creating directory!\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileName (\$DirName . \"/\" . [ \$CleanName \$0 ] . \"-\" . [ \ + \$GetRandom20CharAlNum ]);\r\ + \n :onerror Err {\r\ + \n /tool/fetch check-certificate=\$CheckCert \$Url dst-path=\$FileName \ + \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$ScriptName ] }) as-va\ + lue;\r\ + \n } do={\r\ + \n :if ([ \$WaitForFile \$FileName 500ms ] = true) do={\r\ + \n \$RmFile \$FileName;\r\ + \n }\r\ + \n \$LogPrint debug \$0 (\"Failed downloading from \" . \$Url . \" - \"\ + \_. \$Err);\r\ + \n \$RmDir \$DirName;\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$WaitForFile \$FileName 5s ] = false) do={\r\ + \n \$LogPrint debug \$0 (\"The file downloaded from \" . \$Url . \" did\ + \_not show up.\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileSize [ /file/get \$FileName size ];\r\ + \n :local Return \"\";\r\ + \n :local VarSize 0;\r\ + \n :while (\$VarSize != \$FileSize) do={\r\ + \n :set Return (\$Return . ([ /file/read offset=\$VarSize chunk-size=32\ + 768 file=\$FileName as-value ]->\"data\"));\r\ + \n :set FileSize [ /file/get \$FileName size ];\r\ + \n :set VarSize [ :len \$Return ];\r\ + \n :if (\$VarSize > \$FileSize) do={\r\ + \n :delay 100ms;\r\ + \n }\r\ + \n }\r\ + \n \$RmDir \$DirName;\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FetchUserAgentStr do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global CommitBrief;\r\ + \n :global IfThenElse;\r\ + \n :local Resource [ /system/resource/get ];\r\ + \n :return (\"User-Agent: Mikrotik/\" . \$Resource->\"version\" . \" \" .\ + \_\$Resource->\"architecture-name\" . \\\r\ + \n \" \" . \$Caller . \"/Fetch (\" . \$GlobalScriptsUrl . \"; \" . [ \$\ + CommitBrief ] . \")\");\r\ + \n}\r\ + \n\r\ + \n:set ScriptInstallUpdate do={ :onerror Err {\r\ + \n :local Scripts [ :toarray \$1 ];\r\ + \n :local NewComment [ :tostr \$2 ];\r\ + \n :global InitSetup;\r\ + \n\r\ + \n :global CommitId;\r\ + \n :global ScriptUpdatesCRLF;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global CertificateAvailable;\r\ + \n :global CheckSumsVerify;\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global SumsDirAndFileName;\r\ + \n :global GlobalScriptsUrlSuffix;\r\ + \n :global EitherOr;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global Grep;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global ParseKeyValueStore;\r\ + \n :global RequiredRouterOS;\r\ + \n :global SendNotification2;\r\ + \n :global SymbolForNotification;\r\ + \n :global ValidateSyntax;\r\ + \n :global CommentsInScripts;\r\ + \n\r\ + \n :if ([ \$CertificateAvailable \"Root YE\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, trying wit\ + hout.\");\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=\$Scripts do={\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\ + \r\ + \n \$LogPrint info \$0 (\"Adding new script: \" . \$Script);\r\ + \n /system/script/add name=\$Script owner=\$Script source=\"#!rsc by \ + Vados\\n\" comment=\$NewComment;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :local CommitIdBefore \$CommitId;\r\ + \n :local GlobalConfVersionBefore \$GlobalConfVersion;\r\ + \n :local ReloadGlobal false;\r\ + \n :local DeviceMode [ /system/device-mode/get ];\r\ + \n\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSums ({});\r\ + \n :do {\r\ + \n :local Url (\$GlobalScriptsUrl . \$SumsDirAndFileName . \$GlobalScr\ + iptsUrlSuffix);\r\ + \n \$LogPrint debug \$0 (\"Fetching checksums from url: \" . \$Url);\ + \r\ + \n :set CheckSums [ :deserialize from=json ([ /tool/fetch check-certi\ + ficate=yes-without-crl \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url output\ + =user as-value ]->\"data\") ];\r\ + \n } on-error={ \$LogPrint warning \$0 (\"Err = \" . \$Err); }\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=[ /system/script/find where source~\"^#!rsc by Vado\ + s\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :local ScriptInfo [ \$ParseKeyValueStore (\$ScriptVal->\"comment\") \ + ];\r\ + \n :local SourceNew;\r\ + \n\r\ + \n :foreach Scheduler in=[ /system/scheduler/find where on-event~(\"\\\ + \\b\" . \$ScriptVal->\"name\" . \"\\\\b\") ] do={\r\ + \n :local SchedulerVal [ /system/scheduler/get \$Scheduler ];\r\ + \n :if (\$ScriptVal->\"policy\" != \$SchedulerVal->\"policy\") do={\r\ + \n \$LogPrint warning \$0 (\"Policies differ for script '\" . \$Scr\ + iptVal->\"name\" . \"' and its scheduler '\" . \$SchedulerVal->\"name\" . \ + \"'!\");\r\ + \n }\r\ + \n }\r\ + \n :do {\r\ + \n :if (\$ScriptInfo->\"ignore\" = true) do={\r\ + \n \$LogPrint debug \$0 (\"Ignoring script '\" . \$ScriptVal->\"nam\ + e\" . \"', as requested.\");\r\ + \n :error true;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSum (\$CheckSums->(\$ScriptVal->\"name\"));\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Scri\ + ptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :convert transform=md5 to=hex [ :tolf (\$ScriptVal->\"sou\ + rce\") ] ] = \$CheckSum) do={\r\ + \n \$LogPrint debug \$0 (\"Checksum for script '\" . \$ScriptVal-\ + >\"name\" . \"' matches, ignoring.\");\r\ + \n :error true;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len (\$ScriptInfo->\"certificate\") ] > 0) do={\r\ + \n :if ([ \$CertificateAvailable (\$ScriptInfo->\"certificate\") \"\ + fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, tryi\ + ng without.\");\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n :local BaseUrl [ \$EitherOr (\$ScriptInfo->\"base-url\") \$Globa\ + lScriptsUrl ];\r\ + \n :local UrlSuffix [ \$EitherOr (\$ScriptInfo->\"url-suffix\") \$G\ + lobalScriptsUrlSuffix ];\r\ + \n :local Url (\$BaseUrl . \$ScriptVal->\"name\" . \".rsc\" . \$Url\ + Suffix);\r\ + \n \$LogPrint warning \$0 (\"Fetching script '\" . (\$ScriptVal->\"\ + name\") . \"' from url: \" . \$Url);\r\ + \n :local Result [ /tool/fetch check-certificate=yes-without-crl \\\ + \r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url outp\ + ut=user as-value ];\r\ + \n :if (\$Result->\"status\" = \"finished\") do={\r\ + \n :set SourceNew [ :tolf (\$Result->\"data\") ];\r\ + \n } \r\ + \n } do={\r\ + \n \$LogPrint warning \$0 (\"Failed fetching script '\" . \$ScriptV\ + al->\"name\" . \"': \" . \$Err);\r\ + \n :if (\$Err != \"Fetch failed with status 404\") do={\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$ScriptVal->\"source\" = \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Removing dummy. Typo on installation\ + \?\");\r\ + \n /system/script/remove \$Script;\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Sc\ + riptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :len \$CheckSum ] = 0) do={\r\ + \n \$LogPrint warning \$0 (\"Added the script manually\? Skip u\ + pdates with 'ignore=true' in comment.\");\r\ + \n }\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$SourceNew ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"No update for script '\" . (\$ScriptVal-\ + >\"name\") . \"'.\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :local SourceCRLF [ :tocrlf \$SourceNew ];\r\ + \n :if (\$InitSetup != true && ((\$ScriptVal->\"name\") != \"AM-Globa\ + lConfig\" || (\$ScriptVal->\"name\") != \"AM-GlobalFunc\")) do={\r\ + \n :if (\$SourceNew = (\$ScriptVal->\"source\") || \$SourceCRLF = (\ + \$ScriptVal->\"source\")) do={\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\") \ + . \"' source did not change. End of lines set to CRLF.\");\r\ + \n if (\$ScriptUpdatesCRLF = false) do={\r\ + \n :put [ :tolf \$SourceNew ];\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\"\ + ) . \"' source update end of lines to LF.\");\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([ :pick \$SourceNew 0 14 ] != \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Looks like new script '\" . \$ScriptVa\ + l->\"name\" . \"' is not valid (missing shebang = '\$shebang') Ignoring!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if (\$CommentsInScripts = true) do={\r\ + \n :local CommentLine [ \$Grep \$SourceNew (\"\\23 Script comment:\ + \_\") ];\r\ + \n :if ([ :len \$CommentLine ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"The search in new script '\" . (\$Scr\ + iptVal->\"name\") . \"' for comment in script body, is not found result Ig\ + noring!\");\r\ + \n :error false;\r\ + \n } else={\r\ + \n :if ([ :len (\$ScriptVal->\"comment\")] = 0 || [ :len (\$Scri\ + ptVal->\"comment\")] != [ :pick \$CommentLine ([ :find \$CommentLine \":\"\ + ] + 2) [ :len \$CommentLine] ]) do={\r\ + \n \$LogPrint debug \$0 (\"Exsiting comment for script '\" . (\ + \$ScriptVal->\"name\") . \"' is '\" . (\$ScriptVal->\"comment\") . \"'.\")\ + ;\r\ + \n :set (\$ScriptVal->\"comment\") [ :pick \$CommentLine ([ :f\ + ind \$CommentLine \":\"] + 2) [ :len \$CommentLine] ];\r\ + \n \$LogPrint debug \$0 (\"New comment (\" . (\$ScriptVal->\"c\ + omment\") . \") for script '\" . (\$ScriptVal->\"name\") . \"' has been ad\ + ded!\");\r\ + \n } else={\r\ + \n \$LogPrint debug \$0 (\"Comment for script '\" . (\$ScriptV\ + al->\"name\") . \"' did not change.\");\r\ + \n :error false;\r\ + \n }\r\ + \n } \r\ + \n }\r\ + \n\r\ + \n :local RequiredROS ([ \$ParseKeyValueStore [ \$Grep \$SourceNew (\ + \"\\23 requires RouterOS, \") ] ]->version);\r\ + \n :if ([ \$RequiredRouterOS \$0 [ \$EitherOr \$RequiredROS \"0.0\" ]\ + \_false ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires RouterOS \" . \\\r\ + \n \$RequiredROS . \", which is not met by your installation. I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :local RequiredDM [ \$ParseKeyValueStore [ \$Grep \$SourceNew (\"\ + \\23 requires device-mode, \") ] ];\r\ + \n :local MissingDM ({});\r\ + \n :foreach Feature,Value in=\$RequiredDM do={\r\ + \n :if ([ :typeof (\$DeviceMode->\$Feature) ] = \"bool\" && (\$Devi\ + ceMode->\$Feature) = false) do={\r\ + \n :set MissingDM (\$MissingDM, \$Feature);\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len \$MissingDM ] > 0) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires disabled \" . \\\r\ + \n \"device-mode features (\" . [ :tostr \$MissingDM ] . \"). I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$ValidateSyntax \$SourceNew ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Syntax validation for script '\" . (\$\ + ScriptVal->\"name\") . \"' failed! Ignoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n \r\ + \n :log info (\"Updating script: \" . (\$ScriptVal->\"name\") . \" Sc\ + ript comment: \" . (\$ScriptVal->\"comment\"));\r\ + \n\r\ + \n /system/script/set owner=(\$ScriptVal->\"name\") comment=(\$Script\ + Val->\"comment\") \\\r\ + \n source=[ \$IfThenElse (\$ScriptUpdatesCRLF = true) \$SourceCRL\ + F \$SourceNew ] \$Script;\r\ + \n :if (\$ScriptVal->\"name\" = \"AM-GlobalConfig\" || \\\r\ + \n \$ScriptVal->\"name\" = \"AM-GlobalFunc\" || \\\r\ + \n \$ScriptVal->\"name\" ~ (\"^(AM-GlobalFunc\\\\.d|mod)/.\")) d\ + o={\r\ + \n :set ReloadGlobal true;\r\ + \n }\r\ + \n } on-error={ }\r\ + \n }\r\ + \n :if (\$ReloadGlobal = true) do={\r\ + \n \$LogPrint info \$0 (\"Reloading global configuration and functions.\ + \");\r\ + \n :onerror Err {\r\ + \n :global InitSetup (a);\r\ + \n /system/script {run AM-GlobalConfig; run AM-GlobalFunc;};\r\ + \n } do={\$LogPrint error \$0 (\"Reloading global configuration and fun\ + ctions failed! \" . \$Err)}\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore > \$GlobalConfVersion) do={\r\ + \n \$LogPrint warning \$0 (\"The configuration version decreased from \ + \" . \\\r\ + \n \$GlobalConfVersionBefore . \" to \" . \$GlobalConfVersion . \\\r\ + \n \". Installed an older version\?\");\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore < \$GlobalConfVersion) do={\r\ + \n :global GlobalConfChanges;\r\ + \n :global GlobalConfMigration;\r\ + \n :local ChangeLogCode;\r\ + \n :if ([ :len \$ChangeLogCode ] > 0) do={\r\ + \n :if ([ \$ValidateSyntax \$ChangeLogCode ] = true) do={\r\ + \n :onerror Err {\r\ + \n [ :parse \$ChangeLogCode ];\r\ + \n } do={\$LogPrint warning \$0 (\"The changelog failed to run: \" \ + . \$Err)}\r\ + \n } else={\$LogPrint warning \$0 (\"The changelog failed syntax vali\ + dation!\")}\r\ + \n }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Migration (\$GlobalConfMigration->[ :tostr \$I ]);\r\ + \n :do {\r\ + \n :if ([ :typeof \$Migration ] != \"str\") do={\r\ + \n \$LogPrint debug \$0 (\"Migration code for change \" . \$I .\ + \_\" is not available.\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$ValidateSyntax \$Migration ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Migration code for change \" . \$I\ + \_. \" failed syntax validation!\");\r\ + \n :error false}\r\ + \n \$LogPrint info \$0 (\"Applying migration for change \" . \$I \ + . \": \" . \$Migration);\r\ + \n :onerror Err {\r\ + \n [ :parse \$Migration ];\r\ + \n } do={\$LogPrint warning \$0 (\"Migration code for change \" .\ + \_\$I . \" failed to run: \" . \$Err)}\r\ + \n } on-error={ }\r\ + \n }\r\ + \n }\r\ + \n :local NotificationMessage (\"The configuration version on \" . \$Id\ + entity . \" increased \" . \\\r\ + \n \"to \" . \$GlobalConfVersion . \", current configuration may nee\ + d modification. \" . \\\r\ + \n \"Please review and update global-config-overlay, then re-run glo\ + bal-config.\");\r\ + \n \$LogPrint info \$0 (\$NotificationMessage);\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nChanges:\ + \");\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Change (\$GlobalConfChanges->[ :tostr \$I ]);\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n \" . \\\ + \r\ + \n [ \$SymbolForNotification \"pushpin\" \"*\" ] . \$Change);\r\ + \n \$LogPrint info \$0 (\"Change \" . \$I . \": \" . \$Change);\r\ + \n }\r\ + \n } else={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nNews and\ + \_changes are not available.\");\r\ + \n\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={ :set GlobalConfChanges; \ + } else={ :global GlobalConfChanges (a); }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={ :set GlobalConfMigrati\ + on; } else={ :global GlobalConfMigration (a); }\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n\r\ + \n# load modules\r\ + \n:foreach Script in=[ /system/script/find where name ~ \"^(global-functio\ + ns\\\\.d|mod)/.\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :if ([ \$ValidateSyntax (\$ScriptVal->\"source\") ] = true) do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' f\ + ailed to run: \" . \$Err);\r\ + \n }\r\ + \n } else={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' fai\ + led syntax validation, skipping.\");\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# Add and fix global scheduler\r\ + \n:put \"Add or/and fix Global scheduler...\";\r\ + \n:local OnEvent \"/system/script { run AM-GlobalConfig; run AM-GlobalFunc\ + ; }\";\r\ + \n:local Comment \"Every Day Set Global Environment Variables\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-SetGlobalEnv\" ] ] \ + = 0) do={\r\ + \n /system/scheduler/add name=\"AM-SetGlobalEnv\" comment=\$Comment on-ev\ + ent=\$OnEvent start-time=startup;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" comment ] != \$Comment)\ + \_do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" comment=\$Comment;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" on-event ] != \$OnEvent\ + ) do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" on-event=\$OnEvent;\r\ + \n}\r\ + \n# Add Update Script scheduler\r\ + \n:put \"Add or/and fix Update Script scheduler\";\r\ + \n:set OnEvent \":global ScriptInstallUpdate; \\\$ScriptInstallUpdate;\";\ + \r\ + \n:set Comment \"Every Day Check For Update Scripts\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-ScriptInstallUpdate\ + \" ] ] = 0) do={\r\ + \n/system/scheduler/add name=\"AM-ScriptInstallUpdate\" comment=\$Comment \ + start-time=startup interval=1d on-event=\$OnEvent;\r\ + \n}\r\ + \n\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady;\r\ + \n :retry { :if (\$GlobalConfReady != true) do={ :error (\"Global configs\ + \_not ready.\"); }; } delay=100ms max=5;\r\ + \n\r\ + \n:log warning (\"This script \$ScriptName Loaded \" . \\\r\ + \n [ \$IfThenElse (\$CommitId != \"unknown\") ([ \$CommitBrief ] . \" \ + \") ] . \\\r\ + \n \"on \" . \$devBoardName . \"\\nRouterOS: \" . \$devCurVersion . \"\ + \\nArchitecture: \" . \$devCurArch . \"\\nIdentity: \" . \$Identity);\r\ + \n # signal we are ready\r\ + \n:set GlobalFuncReady true;\r\ + \n:set ExitOK true;\r\ + \n\r\ + \n:log info (\$0 . \"Global configs ready!\");\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n}\r\ + \n" +add comment="Download custom Certificates By Routers Identity " \ + dont-require-permissions=no name=mod/AM-Certs-By-Ident owner=\ + mod/AM-Certs-By-Ident policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-Certs-By-Ident\r\ + \n# Script comment: Download custom Certificates By Routers Identity \r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n#:local ScriptName [ :jobname ];\r\ + \n#\r\ + \n# CloudFlare\r\ + \n#/tool/fetch mode=https url=https://cacerts.digicert.com/DigiCertGlobalR\ + ootCA.crt.pem;\r\ + \n#/certificate/import file-name=DigiCertGlobalRootCA.crt.pem passphrase=\ + \"\";\r\ + \n#/tool/ fetch mode=https url=https://cacerts.digicert.com/DigiCertGlobal\ + G2TLSRSASHA2562020CA1-1.crt.pem;\r\ + \n#/certificate/import file-name=DigiCertGlobalG2TLSRSASHA2562020CA1-1.crt\ + .pem passphrase=\"\";\r\ + \n# Quad9\r\ + \n#/tool/fetch mode=https url=\"https://cacerts.digicert.com/DigiCertGloba\ + lG3TLSECCSHA3842020CA1-1.crt.pem\"\r\ + \n#/certificate/import file-name=DigiCertGlobalG3TLSECCSHA3842020CA1-1.crt\ + .pem passphrase=\"\";\r\ + \n:global tmpAddCerts true; \r\ + \n\r\ + \n:global GlobalScriptsUrl;\r\ + \n:local UrlSuffix [/system/identity/get name];\r\ + \n:local UrlDir \"certs/\";\r\ + \n:local CertUrl (\$GlobalScriptsUrl . \$UrlDir . \$UrlSuffix .\"/\");\r\ + \n\r\ + \n:log error \$CertUrl;\r\ + \n\r\ + \n:local CertName \"DigiCert-Global-Root-CA.pem\";\r\ + \n/tool/fetch mode=https url=(\$CertUrl . \$CertName);\r\ + \n/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n/certificate/set name=\"DigiCert-Global-Root-CA\" [ find where common-na\ + me=\"DigiCert Global Root CA\" ];\r\ + \n\r\ + \n:set CertName \"DigiCert-Assured-ID-Root-CA.pem\";\r\ + \n/tool/fetch mode=https url=(\$CertUrl . \$CertName);\r\ + \n/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n/certificate/set name=\"DigiCert-Assured-ID-Root-CA\" [ find where commo\ + n-name=\"DigiCert Assured ID Root CA\" ];\r\ + \n\r\ + \n:set CertName \"DigiCert-Assured-ID-Root-G2.pem\";\r\ + \n/tool/fetch mode=https url=(\$CertUrl . \$CertName);\r\ + \n/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n/certificate/set name=\"DigiCert-Assured-ID-Root-G2\" [ find where commo\ + n-name=\"DigiCert Assured ID Root G3\" ];\r\ + \n\r\ + \n:set CertName \"DigiCert-Assured-ID-Root-G3.pem\";\r\ + \n/tool/fetch mode=https url=(\$CertUrl . \$CertName);\r\ + \n/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n/certificate/set name=\"DigiCert-Assured-ID-Root-G3\" [ find where commo\ + n-name=\"DigiCert Assured ID Root G3\" ];\r\ + \n\r\ + \n:set CertName \"DigiCert-Global-G2-TLS-RSA-SHA256-2020-CA1.pem\";\r\ + \n/tool/fetch mode=https url=(\$CertUrl . \$CertName);\r\ + \n/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n/certificate/set name=\"DigiCert-Global-G2-TLS-RSA-SHA256-2020-CA1\" [ f\ + ind where common-name=\"DigiCert Global G2 TLS RSA SHA256 2020 CA1\" ];\r\ + \n\r\ + \n:set CertName \"DigiCert-Global-G3-TLS-ECC-SHA384-2020-CA1.pem\";\r\ + \n/tool/fetch mode=https url=(\$CertUrl . \$CertName);\r\ + \n/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n/certificate/set name=\"DigiCert-Global-G3-TLS-ECC-SHA384-2020-CA1\" [ f\ + ind where common-name=\"DigiCert Global G3 TLS ECC SHA384 2020 CA1\" ];\r\ + \n\r\ + \n:set CertName \"DigiCert-Global-Root-G2.pem\";\r\ + \n/tool/fetch mode=https url=(\$CertUrl . \$CertName);\r\ + \n/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n/certificate/set name=\"DigiCert-Global-Root-G2\" [ find where common-na\ + me=\"DigiCert Global Root G2\" ];\r\ + \n\r\ + \n:set CertName \"DigiCert-Global-Root-G3.pem\";\r\ + \n/tool/fetch mode=https url=(\$CertUrl . \$CertName);\r\ + \n/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n/certificate/set name=\"DigiCert-Global-Root-G3\" [ find where common-na\ + me=\"DigiCert Global Root G3\" ];\r\ + \n\r\ + \n:set CertName \"DigiCert-TLS-ECC-P384-Root-G5.pem\";\r\ + \n/tool/fetch mode=https url=(\$CertUrl . \$CertName);\r\ + \n/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n/certificate/set name=\"DigiCert-TLS-ECC-P384-Root-G5\" [ find where com\ + mon-name=\"DigiCert TLS ECC P384 Root G5\" ];\r\ + \n\r\ + \n:set CertName \"DigiCert-TLS-RSA4096-Root-G5.pem\";\r\ + \n/tool/fetch mode=https url=(\$CertUrl . \$CertName);\r\ + \n/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n/certificate/set name=\"DigiCert-TLS-RSA4096-Root-G5\" [ find where comm\ + on-name=\"DigiCert TLS RSA4096 Root G5\" ];\r\ + \n\r\ + \n:set CertName \"DigiCert-Trusted-Root-G4.pem\";\r\ + \n/tool/fetch mode=https url=(\$CertUrl . \$CertName);\r\ + \n/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n/certificate/set name=\"DigiCert-Trusted-Root-G4\" [ find where common-n\ + ame=\"DigiCert Trusted Root G4\" ];\r\ + \n\r\ + \n:set CertName \"GTS-Root-R4.pem\";\r\ + \n/tool/fetch mode=https url=(\$CertUrl . \$CertName);\r\ + \n/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n/certificate/set name=\"GTS-Root-R4\" [ find where common-name=\"GTS Roo\ + t R4\" ];\r\ + \n\r\ + \n:set CertName \"Go-Daddy-Root-Certificate-Authority-G2.pem\";\r\ + \n/tool/fetch mode=https url=(\$CertUrl . \$CertName);\r\ + \n/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n/certificate/set name=\"Go-Daddy-Root-Certificate-Authority-G2\" [ find \ + where common-name=\"Go Daddy Root Certificate Authority - G2\" ];\r\ + \n\r\ + \n:set CertName \"ISRG-Root-X1.pem\";\r\ + \n/tool/fetch mode=https url=(\$CertUrl . \$CertName);\r\ + \n/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n/certificate/set name=\"ISRG-Root-X1\" [ find where common-name=\"ISRG R\ + oot X1\" ];\r\ + \n\r\ + \n:set CertName \"Root-YE.pem\";\r\ + \n/tool/fetch mode=https url=(\$CertUrl . \$CertName);\r\ + \n/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n/certificate/set name=\"Root-YE\" [ find where common-name=\"Root YE\" ]\ + ;\r\ + \n\r\ + \n:set CertName \"Starfield-Root-Certificate-Authority-G2.pem\";\r\ + \n/tool/fetch mode=https url=(\$CertUrl . \$CertName);\r\ + \n/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n/certificate/set name=\"Starfield-Root-Certificate-Authority-G2\" [ find\ + \_where common-name=\"Starfield Root Certificate Authority - G2\" ];\r\ + \n\r\ + \n:set CertName \"USERTrust-RSA-Certification-Authority.pem\";\r\ + \n/tool/fetch mode=https url=(\$CertUrl . \$CertName);\r\ + \n/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n/certificate/set name=\"USERTrust-RSA-Certification-Authority\" [ find w\ + here common-name=\"USERTrust RSA Certification Authority\" ];\r\ + \n\r\ + \n:global tmpAddCerts (a);\r\ + \n\r\ + \n#/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n#/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n#:set CertName \"GTS-Root-RX.pem\";\r\ + \n#/tool/fetch mode=https url=(\$AmScriptsUrl . \"certs/\" . \$CertName);\ + \r\ + \n#/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n#internet\r\ + \n#/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n#:set CertName \"ISRG-Root-X2.pem\";\r\ + \n#/tool/fetch mode=https url=(\$AmScriptsUrl . \"certs/\" . \$CertName);\ + \r\ + \n#/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n\r\ + \n\r\ + \n#/ip/dns/set allow-remote-requests=yes doh-max-concurrent-queries=100 do\ + h-max-server-connections=20 use-doh-server=https://security.cloudflare-dns\ + .com/dns-query verify-doh-cert=yes;\r\ + \n#/ip/dns/static remove [find where address=1.1.1.1];\r\ + \n#/ip/dns/static add address=1.1.1.1 name=security.cloudflare-dns.com com\ + ment=\"cloudflare-dns IPv4 1\";\r\ + \n#/ip/dns/ static remove [find where address=1.0.0.1];\r\ + \n#/ip/dns/static add address=1.0.0.1 name=security.cloudflare-dns.com com\ + ment=\"cloudflare-dns IPv4 2\"\r\ + \n#/ip dns static add address=2606:4700:4700::1111 name=security.cloudflar\ + e-dns.com type=AAAA\r\ + \n#/ip dns static add address=2606:4700:4700::1001 name=security.cloudflar\ + e-dns.com type=AAAA\r\ + \n#/ip/dns set servers=\"1.0.0.1, 8.8.4.4\";\r\ + \n#/ip/dns set servers=\"\";" +add dont-require-permissions=no name=fetchCustom owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="\ + \r\ + \n:global GlobalScriptsUrl;\r\ + \n/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@10\ + .30.30.86.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.86.pub\";\ + \n" +add dont-require-permissions=no name=awg2-webui owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + :put \"Download image, and save him to usb1/tmp\"\ + \n#/tool/fetch url=\"https://ros.vados.ru/docker/awg2-webui-arm.tar\" dst-\ + path=usb1/tmp/awg2-webui.tar\ + \n\ + \n:put \"1. Create veth interface\"\ + \n/interface veth add name=veth-awg address=10.9.9.2/24 gateway=10.9.9.1\ + \n\ + \n:put \"2. Create bridge and add veth\"\ + \n/interface bridge add name=br-awg\ + \n/interface bridge port add bridge=br-awg interface=veth-awg\ + \n\ + \n:put \"3. Assign IP to bridge\"\ + \n/ip address add address=10.9.9.1/24 interface=br-awg\ + \n\ + \n:put \"4. NAT: masquerade container traffic\"\ + \n/ip firewall nat add chain=srcnat src-address=10.9.9.0/24 action=masquer\ + ade\ + \n\ + \n:put \"5. Port forward: UDP 443 \? container (AmneziaWG QUIC mimicry)\"\ + \n/ip firewall nat add chain=dstnat protocol=udp dst-port=443 \\\ + \n action=dst-nat to-addresses=10.9.9.2 to-ports=55055 in-interface=eth\ + er1\ + \n\ + \n:put \"6. Port forward: HTTPS 9443 \? container Web UI\"\ + \n/ip firewall nat add chain=dstnat protocol=tcp dst-port=9443 \\\ + \n action=dst-nat to-addresses=10.9.9.2 to-ports=443 in-interface=ether\ + 1\ + \n\ + \n:put \"Create environment list..\"\ + \n/container envs add list=awgui-env key=WEB_USER value=vados\ + \n/container envs add list=awgui-env key=WEB_PASS value=zoomzoomrulezz\ + \n/container envs add list=awgui-env key=SECRET_KEY value=RanDomSecret32Sy\ + mboLs2325272931q\ + \n/container envs add list=awgui-env key=AWG_ENDPOINT value=10.30.30.10:44\ + 3\ + \n\ + \n:put \"Load and start container...\"\ + \n/container add file=usb1/tmp/awg2-webui.tar interface=veth-awg hostname=\ + awg2-webui \\\ + \nroot-dir=usb1/awg2-webui envlist=awgui-env start-on-boot=yes logging=yes\ + \_shm-size=4M\ + \n\ + \n:put \"Start...\"\ + \n/container start 0\ + \n\ + \n:put \"Verify...\"\ + \n/container print\ + \n\ + \n:put \"Should show: status=running\"\ + \n\ + \n/log print where topics~\"container\"\ + \n\ + \n:put \"Should show: AWG started, nginx running\"\ + \n" +add comment=Generator dont-require-permissions=no name=AWG-VDSina owner=vados \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="# ============================================================\r\ + \n# AWG Proxy -- MikroTik RouterOS (protocol: v2)\r\ + \n# 2026-06-30 22:08:12\r\ + \n# ============================================================\r\ + \n\r\ + \n# 0. Check prerequisites\r\ + \n:if ([:len [/system/package/find where name=\"container\" disabled=no]] \ + = 0) do={\r\ + \n :put \"Container package is not installed or disabled. Install it and \ + reboot.\"\r\ + \n :error \"Usage: /system/device-mode/update container=yes\"\r\ + \n}\r\ + \n\r\ + \n# Auto-detect storage\r\ + \n:global awgDisk \"disk1\"\r\ + \n{\r\ + \n :local usbFree 0\r\ + \n :do { :set usbFree [/disk/get [find where mount-point=\"usb1\"] free] \ + } on-error={}\r\ + \n :if (\$usbFree > 0) do={\r\ + \n :set awgDisk \"usb1\"\r\ + \n :put (\"Using USB: usb1 (\" . (\$usbFree / 1048576) . \" MB free)\")\ + \r\ + \n } else={\r\ + \n :if ([:len [/disk/find where slot=\"usb1\"]] > 0) do={\r\ + \n :put \"USB found but not mounted. Format as ext4\?\"\r\ + \n :put \"Press 'y' to confirm:\"\r\ + \n :local key [/terminal/inkey timeout=30]\r\ + \n :if (\$key = 121 || \$key = 89) do={\r\ + \n /disk/format-drive usb1 file-system=ext4 label=usb1\r\ + \n :delay 3s\r\ + \n :do { :set usbFree [/disk/get [find where mount-point=\"usb1\"] \ + free] } on-error={}\r\ + \n :if (\$usbFree > 0) do={\r\ + \n :set awgDisk \"usb1\"\r\ + \n } else={ :put \"Format failed, using disk1\" }\r\ + \n } else={ :put \"Using disk1\" }\r\ + \n } else={ :put \"No USB, using disk1\" }\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# 1. Uninstall script\r\ + \n/system/script/add name=awg-VDSina-uninstall comment=awg-VDSina source={\ + \r\ + \n :put \"Uninstallimg AmneziaWG...\"\r\ + \n :log info \"Uninstallimg AmneziaWG...\"\r\ + \n :local prevDNS \"\"\r\ + \n :do { :set prevDNS [/container/envs/get [find where list=\"awg-VDSina-\ + env\" key=\"AWG_PREV_DNS\"] value] } on-error={}\r\ + \n :local storageDisk \"disk1\"\r\ + \n :do { :set storageDisk [/container/envs/get [find where list=\"awg-VDS\ + ina-env\" key=\"AWG_STORAGE\"] value] } on-error={}\r\ + \n :local prevTmpdir \"\"\r\ + \n :if (\$storageDisk != \"disk1\") do={\r\ + \n :do { :set prevTmpdir [/container/envs/get [find where list=\"awg-VD\ + Sina-env\" key=\"AWG_PREV_TMPDIR\"] value] } on-error={}\r\ + \n }\r\ + \n /ip/route/remove [find where comment=awg-VDSina-tunnel]\r\ + \n /ip/route/remove [find where comment=awg-VDSina-dns]\r\ + \n /container/stop [find where interface=veth-awg-VDSina]\r\ + \n :delay 7s\r\ + \n /container/remove [find where interface=veth-awg-VDSina]\r\ + \n /container/envs/remove [find where list=\"awg-VDSina-env\"]\r\ + \n :if (\$prevDNS != \"\") do={\r\ + \n :do { /ip/dns/set servers=\"\$prevDNS\" } on-error={}\r\ + \n :put (\"DNS restored to: \$prevDNS\")\r\ + \n }\r\ + \n :if (\$storageDisk != \"disk1\") do={\r\ + \n /container/config set tmpdir=\$prevTmpdir\r\ + \n :put (\"Container tmpdir restored to: \$prevTmpdir\")\r\ + \n }\r\ + \n /ip/firewall/nat/remove [find where out-interface=\"wg-awg-VDSina\"]\r\ + \n /ip/firewall/nat/remove [find where src-address=\"172.19.75.208/30\"]\ + \r\ + \n /ip/address/remove [find where address=\"172.19.75.209/30\"]\r\ + \n /interface/veth/remove [find where name=\"veth-awg-VDSina\"]\r\ + \n /interface/wireguard/peers/remove [find where interface=\"wg-awg-VDSin\ + a\"]\r\ + \n /ip/address/remove [find where interface=\"wg-awg-VDSina\"]\r\ + \n /interface/wireguard/remove [find where name=\"wg-awg-VDSina\"]\r\ + \n :do { /file/remove [find where name~\"awg-VDSina.+tar\"] } on-error={}\ + \r\ + \n :do { /file/remove [find where name=(\$storageDisk . \"/awg-VDSina\")]\ + \_} on-error={}\r\ + \n :do { /file/remove [find where name=(\$storageDisk . \"/ru_ranges_time\ + out.rsc\")] } on-error={}\r\ + \n :if (\$storageDisk != \"disk1\") do={\r\ + \n :do { /file/remove [find where name~(\$storageDisk . \"/pull\")] } o\ + n-error={}\r\ + \n }\r\ + \n :put \"Uninstall AmneziaWG Proxy complete!\"\r\ + \n :log info \"Uninstall AmneziaWG Proxy complete!\"\r\ + \n /system/script/remove [find where name=awg-VDSina-uninstall]\r\ + \n}\r\ + \n\r\ + \n# 2. Network infrastructure\r\ + \n/interface/veth/add name=veth-awg-VDSina address=172.19.75.210/30 gatewa\ + y=172.19.75.209\r\ + \n/ip/address/add address=172.19.75.209/30 interface=veth-awg-VDSina\r\ + \n/ip/firewall/nat/add chain=srcnat action=masquerade src-address=172.19.7\ + 5.208/30\r\ + \n\r\ + \n# 3. WireGuard interface (MikroTik derives the public key automatically)\ + \r\ + \n/interface/wireguard/add name=wg-awg-VDSina private-key=\"4CHyNhbOs923/E\ + yNDguMXJXdghL2I3UyzXPwrANVgHs=\" listen-port=14710 disabled=yes\r\ + \n/interface/wireguard/peers/add interface=wg-awg-VDSina public-key=\"o5Ig\ + Cl1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+v0b9jU=\" preshared-key=\"RNvF7T1cg5tyXcY\ + QnD9N5BEp6e20vVJQTv0GMjyvHU0=\" endpoint-address=172.19.75.210 endpoint-po\ + rt=51820 allowed-address=0.0.0.0/0 persistent-keepalive=10\r\ + \n/ip/address/add address=10.9.0.3/32 interface=wg-awg-VDSina\r\ + \n\r\ + \n# 4. Container environment variables\r\ + \n/container/envs/add list=awg-VDSina-env key=AWG_LISTEN value=\":44044\"\ + \r\ + \n/container/envs/add list=awg-VDSina-env key=AWG_REMOTE value=\"109.107.1\ + 77.79:55055\"\r\ + \n/container/envs/add list=awg-VDSina-env key=AWG_JC value=\"9\"\r\ + \n/container/envs/add list=awg-VDSina-env key=AWG_JMIN value=\"105\"\r\ + \n/container/envs/add list=awg-VDSina-env key=AWG_JMAX value=\"920\"\r\ + \n/container/envs/add list=awg-VDSina-env key=AWG_S1 value=\"90\"\r\ + \n/container/envs/add list=awg-VDSina-env key=AWG_S2 value=\"136\"\r\ + \n/container/envs/add list=awg-VDSina-env key=AWG_H1 value=\"472437748-472\ + 438637\"\r\ + \n/container/envs/add list=awg-VDSina-env key=AWG_H2 value=\"707808381-707\ + 809250\"\r\ + \n/container/envs/add list=awg-VDSina-env key=AWG_H3 value=\"1609078552-16\ + 09079495\"\r\ + \n/container/envs/add list=awg-VDSina-env key=AWG_H4 value=\"1012329484-10\ + 12329741\"\r\ + \n/container/envs/add list=awg-VDSina-env key=AWG_SERVER_PUB value=\"o5IgC\ + l1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+v0b9jU=\"\r\ + \n/container/envs/add list=awg-VDSina-env key=AWG_CLIENT_PUB value=[/inter\ + face/wireguard/get [find name=wg-awg-VDSina] public-key]\r\ + \n/container/envs/add list=awg-VDSina-env key=AWG_S3 value=\"45\"\r\ + \n/container/envs/add list=awg-VDSina-env key=AWG_S4 value=\"27\"\r\ + \n/container/envs/add list=awg-VDSina-env key=AWG_I1 value=\"\"\r\ + \n/container/envs/add list=awg-VDSina-env key=AWG_I2 value=\"\"\r\ + \n/container/envs/add list=awg-VDSina-env key=AWG_I3 value=\"\ + \"\r\ + \n/container/envs/add list=awg-VDSina-env key=AWG_I4 value=\"\"\ + \r\ + \n/container/envs/add list=awg-VDSina-env key=AWG_I5 value=\"\"\r\ + \n\r\ + \n# 5. DNS\r\ + \n{ :local rawDNS [/ip/dns/get servers]; :local prevDNS \"\"; :foreach s i\ + n=[:toarray \$rawDNS] do={ :if ([:len \$prevDNS] > 0) do={ :set prevDNS \"\ + \$prevDNS,\$s\" } else={ :set prevDNS \$s } }; /container/envs/add list=aw\ + g-VDSina-env key=AWG_PREV_DNS value=\"\$prevDNS\" }\r\ + \n# NOTE: replaces existing DNS configuration\r\ + \n/ip/dns/set servers=8.8.8.8\r\ + \n#/ip/route/add dst-address=109.107.177.79/32 gateway=wg-awg-VDSina dista\ + nce=1 comment=awg-VDSina-dns\r\ + \n\r\ + \n# 6. Download, create and start container\r\ + \n/container/envs/add list=awg-VDSina-env key=AWG_STORAGE value=\$awgDisk\ + \r\ + \n:if (\$awgDisk != \"disk1\") do={\r\ + \n :local tmpPath (\$awgDisk . \"/pull\")\r\ + \n /container/envs/add list=awg-VDSina-env key=AWG_PREV_TMPDIR value=[/co\ + ntainer/config/get tmpdir]\r\ + \n /container/config set tmpdir=\$tmpPath memory-high=200M\r\ + \n}\r\ + \n{\r\ + \n :local arch [/system/resource/get architecture-name]\r\ + \n :local ver [/system/resource/get version]\r\ + \n :local dotPos [:find \$ver \".\"]\r\ + \n :local rest [:pick \$ver (\$dotPos + 1) [:len \$ver]]\r\ + \n :local endPos [:find \$rest \".\"]\r\ + \n :if ([:typeof \$endPos] = \"nil\") do={ :set endPos [:find \$rest \" \ + \"] }\r\ + \n :local minor [:tonum [:pick \$rest 0 \$endPos]]\r\ + \n :local suffix \"\"\r\ + \n :if (\$minor <= 20) do={ :set suffix \"-7.20-Docker\" }\r\ + \n :local file \"\"\r\ + \n :if (\$arch = \"arm64\") do={ :set file (\"awg-proxy-arm64\" . \$suffi\ + x . \".tar.gz\") }\r\ + \n :if (\$arch = \"arm\") do={ :set file (\"awg-proxy-arm\" . \$suffix . \ + \".tar.gz\") }\r\ + \n :if (\$arch ~ \"x86\") do={ :set file (\"awg-proxy-amd64\" . \$suffix \ + . \".tar.gz\") }\r\ + \n :if (\$file = \"\") do={ :error \"Unsupported architecture: \$arch\" }\ + \r\ + \n :local url \"https://github.com/timbrs/amneziawg-mikrotik-c/releases/l\ + atest/download/\$file\"\r\ + \n :local filePath \$file\r\ + \n :if (\$awgDisk != \"disk1\") do={\r\ + \n :set filePath (\$awgDisk . \"/pull/\" . \$file)\r\ + \n :do { /file/make-directory (\$awgDisk . \"/pull\") } on-error={}\r\ + \n }\r\ + \n :if ([:len [/file/find where name=\$filePath]] = 0) do={\r\ + \n :local freeStorage 0\r\ + \n :if (\$awgDisk = \"disk1\") do={\r\ + \n :set freeStorage [/system/resource/get free-hdd-space]\r\ + \n } else={\r\ + \n :do { :set freeStorage [/disk/get [find where mount-point=\$awgDis\ + k] free] } on-error={}\r\ + \n }\r\ + \n :if (\$freeStorage < 262144) do={\r\ + \n :put (\"WARNING: Low disk space (\" . (\$freeStorage / 1024) . \"K\ + B free). Need at least 256KB.\")\r\ + \n :put \"See: https://github.com/timbrs/amneziawg-mikrotik-c#insuffi\ + cient-disk-space\"\r\ + \n :error \"Insufficient disk space\"\r\ + \n }\r\ + \n :put (\"Fetching: \$url -> \" . \$filePath)\r\ + \n /tool/fetch url=\$url dst-path=\$filePath http-max-redirect-count=10\ + \r\ + \n } else={\r\ + \n :put (\"File already exists: \" . \$filePath)\r\ + \n }\r\ + \n :local rootDir (\$awgDisk . \"/awg-VDSina\")\r\ + \n /container/add file=\$filePath interface=veth-awg-VDSina envlist=awg-V\ + DSina-env hostname=awg-VDSina root-dir=\$rootDir logging=no start-on-boot=\ + yes comment=awg-VDSina\r\ + \n :if (\$minor > 20) do={ [:parse \"/container/set [find where interface\ + =veth-awg-VDSina] shm-size=4M\"] }\r\ + \n /file/remove \$filePath\r\ + \n :local freeMem [/system/resource/get free-memory]\r\ + \n :if (\$freeMem < 16777216) do={\r\ + \n :put (\"WARNING: Low memory (\" . (\$freeMem / 1048576) . \"MB free)\ + . 16MB+ recommended.\")\r\ + \n }\r\ + \n /container/start [find where interface=veth-awg-VDSina]\r\ + \n :do { /file/remove [find where name=\"console-dump.txt\"] } on-error={\ + }\r\ + \n :put \"Waiting for container to start...\"\r\ + \n :delay 5s\r\ + \n /interface/wireguard/enable wg-awg-VDSina\r\ + \n :put \"WireGuard interface enabled\"\r\ + \n :put \"Installation complete!\"\r\ + \n :put \"Ping test: 109.107.177.79 via AmneziaWG tunnel. repeat 10 times\ + \"\r\ + \n /ping 109.107.177.79 count=10 interval=1\r\ + \n :put \"\"\r\ + \n :put \"======= NEXT STEPS =======\"\r\ + \n :put \"\"\r\ + \n :put \"Route specific traffic through the AmneziaWG tunnel:\"\r\ + \n :put \"\"\r\ + \n :put \" Route a single host:\"\r\ + \n :put \" /ip/route/add dst-address=8.8.8.8/32 gateway=wg-awg-VDSina \ + comment=awg-VDSina-tunnel\"\r\ + \n :put \"\"\r\ + \n :put \" Route a subnet:\"\r\ + \n :put \" /ip/route/add dst-address=100.0.0.0/8 gateway=wg-awg-VDSina\ + \_comment=awg-VDSina-tunnel\"\r\ + \n :put \"\"\r\ + \n :put \" Remove a route:\"\r\ + \n :put \" /ip/route/remove [find where comment=awg-VDSina-tunnel dst-\ + address=8.8.8.8/32]\"\r\ + \n :put \"\"\r\ + \n :put \" Show all tunnel routes:\"\r\ + \n :put \" /ip/route/print where comment=awg-VDSina-tunnel\"\r\ + \n :put \"\"\r\ + \n :put \" Uninstall:\"\r\ + \n :put \" /system/script/run awg-VDSina-uninstall\"\r\ + \n :put \"=========================\"\r\ + \n}\r\ + \n\r\ + \n# 7. Verification\r\ + \n# /container/print\r\ + \n# /interface/wireguard/print\r\ + \n# /interface/wireguard/peers/print\r\ + \n" +add comment=awgVDSina dont-require-permissions=no name=awgVDSina-uninstall \ + owner=Manager policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="\ + \r\ + \n :put \"Uninstalling AmneziaWG...\"\r\ + \n :log info \"Uninstalling AmneziaWG...\"\r\ + \n /ip/route/remove [find where comment=awgVDSina-tunnel]\r\ + \n :do { /tool/netwatch/remove [find where comment=Monitor-AWGVDSINA] } o\ + n-error={}\r\ + \n :do { /ip/route/remove [find where comment=Monitor-AWGVDSINA] } on-err\ + or={}\r\ + \n /container/stop [find where interface=veth-awgVDSina]\r\ + \n :delay 7s\r\ + \n /interface/list/member/remove [find where interface=wg-awgVDSina]\r\ + \n /container/remove [find where interface=veth-awgVDSina]\r\ + \n /container/envs/remove [find where list=\"awgVDSina-env\"]\r\ + \n /ip/firewall/nat/remove [find where out-interface=\"wg-awgVDSina\"]\r\ + \n /ip/address/remove [find where address=\"172.20.0.1/30\"]\r\ + \n /interface/veth/remove [find where name=\"veth-awgVDSina\"]\r\ + \n /interface/wireguard/peers/remove [find where interface=\"wg-awgVDSina\ + \"]\r\ + \n /ip/address/remove [find where interface=\"wg-awgVDSina\"]\r\ + \n /interface/wireguard/remove [find where name=\"wg-awgVDSina\"]\r\ + \n :do { /file/remove [find where name~\"awgVDSina.+tar\"] } on-error={}\ + \r\ + \n :do { /file/remove [find where name=\"disk1/awgVDSina\"] } on-error={}\ + \r\ + \n :put \"Uninstall AmneziaWG Proxy complete!\"\r\ + \n :log info \"Uninstall AmneziaWG Proxy complete!\"\r\ + \n /system/script/remove [find where name=awgVDSina-uninstall]\r\ + \n" +/user group +add name=backup policy="telnet,ssh,ftp,read,write,policy,test,sniff,sensitive,\ + !local,!reboot,!winbox,!password,!web,!api,!romon,!rest-api" +/container config +set layer-dir=/usb1/docker memory-high=200.0MiB memory-max=2048.0KiB \ + registry-url=https://registry-1.docker.io tmpdir=/usb1/pull username=\ + amsternl +/disk settings +set auto-media-interface=lo +/interface bridge port +add bridge=Bridge-LAN interface=ether2 +add bridge=Bridge-LAN interface=ether3 +add bridge=Bridge-LAN interface=ether4 +add bridge=Bridge-LAN interface=ether5 +add bridge=Bridge-LAN interface=wlan2 +add bridge=Bridge-LAN interface=wlan1 +add bridge=Bridge-LAN interface=ether1 +/ip neighbor discovery-settings +set discover-interface-list=Internet +/ipv6 settings +set allow-fast-path=no disable-ipv6=yes disable-link-local-address=yes \ + forward=no +/interface detect-internet +set detect-interface-list=WAN internet-interface-list=Internet \ + lan-interface-list=LAN wan-interface-list=WAN +/interface list member +add interface=ether2 list=LAN +add interface=ether3 list=LAN +add interface=ether4 list=LAN +add interface=ether5 list=LAN +add interface=wlan2 list=LAN +add interface=wlan1 list=LAN +add interface=ether1 list=Internet +add interface=Bridge-LAN list=LAN +/interface wireguard peers +add allowed-address=0.0.0.0/0,::/0 disabled=yes endpoint-address=172.20.0.2 \ + endpoint-port=51820 interface=wg-awgVDSina name=awgVDSina \ + persistent-keepalive=27s public-key=\ + "o5IgCl1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+v0b9jU=" +/interface wireless cap +# +set bridge=Bridge-LAN caps-man-addresses=10.30.30.10 \ + caps-man-certificate-common-names=CAPsMAN-51B25E507D07 certificate=\ + CAP-18FD746E5814 discovery-interfaces=Bridge-LAN enabled=yes interfaces=\ + wlan1,wlan2 lock-to-caps-man=yes +/ip address +add address=10.30.30.86/24 interface=Bridge-LAN network=10.30.30.0 +add address=172.20.0.1/30 interface=veth-awgVDSina network=172.20.0.0 +add address=10.9.0.2 interface=wg-awgVDSina network=10.9.0.2 +/ip dns +set allow-remote-requests=yes servers=8.8.8.8 +/ip firewall nat +add action=masquerade chain=srcnat src-address=10.9.9.0/24 +add action=dst-nat chain=dstnat disabled=yes dst-port=443 in-interface=ether1 \ + protocol=udp to-addresses=10.9.9.2 to-ports=55055 +add action=dst-nat chain=dstnat disabled=yes dst-port=9443 in-interface=\ + ether1 protocol=tcp to-addresses=10.9.9.2 to-ports=443 +/ip firewall service-port +set ftp disabled=yes +set tftp disabled=yes +set irc disabled=no +set sip disabled=yes +set pptp disabled=yes +set rtsp disabled=no +/ip hotspot walled-garden +add server=*1 +add server=*1 +/ip ipsec settings +set accounting=no +/ip ipsec policy +set 0 disabled=yes dst-address=0.0.0.0/0 src-address=0.0.0.0/0 +/ip ipsec profile +set [ find default=yes ] dpd-interval=2m dpd-maximum-failures=5 +/ip proxy +set cache-administrator="" max-cache-size=none port=80,443 src-address=\ + 0.0.0.0 +/ip route +add check-gateway=ping disabled=no distance=2 dst-address=0.0.0.0/0 gateway=\ + 10.30.30.81 routing-table=main scope=30 target-scope=10 +add comment=Monitor-AWGVDSINA distance=1 dst-address=9.9.9.9/32 gateway=\ + wg-awgVDSina +/ip service +set ftp disabled=yes +set telnet disabled=yes +set ssh address=10.30.30.0/24 port=2222 +set winbox address=10.30.30.0/24 +set api address=10.30.30.0/24 disabled=yes +set api-ssl address=10.30.30.0/24 disabled=yes +set www-ssl port=18443 +set www address=10.30.30.0/24 disabled=yes port=18880 +/ip service webserver +set graphs-plain=no rest-plain=no webfig-plain=no +/ip ssh +set forwarding-enabled=both publickey-authentication-options=verify-required \ + strong-crypto=yes +/ip upnp +set enabled=yes +/ip upnp interfaces +add interface=Bridge-LAN type=internal +add forced-ip=109.107.177.79 interface=veth-awgVDSina type=external +add interface=lo type=internal +add interface=wlan2 type=internal +add interface=wlan1 type=internal +/ipv6 nd +set [ find default=yes ] advertise-dns=yes +/system clock +set time-zone-name=Europe/Moscow +/system clock manual +set time-zone=+03:00 +/system identity +set name=MikroGig +/system logging +add action=email disabled=yes regex="* added|changed|removed *" topics=\ + system,info +/system ntp client +set enabled=yes +/system ntp server +set enabled=yes manycast=yes multicast=yes use-local-clock=yes +/system ntp client servers +add address=10.30.30.81 +add address=10.30.30.10 +add address=0.ru.pool.ntp.org +add address=1.ru.pool.ntp.org +add address=2.ru.pool.ntp.org +add address=3.ru.pool.ntp.org +add address=time.google.com disabled=yes +/system scheduler +add comment="Every Day Set Global Environment Variables" name=AM-SetGlobalEnv \ + on-event="/system/script { run AM-GlobalConfig; run AM-GlobalFunc; }" \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Every Day Check For Update Scripts" interval=1d name=\ + AM-ScriptInstallUpdate on-event=\ + ":global ScriptInstallUpdate; \$ScriptInstallUpdate;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment=Generator name=awg-VDSina on-event="# ============================\ + ================================\r\ + \n# AWG Proxy -- MikroTik RouterOS (protocol: v2)\r\ + \n# 2026-07-01 00:03:43\r\ + \n# ============================================================\r\ + \n\r\ + \n# 0. Check prerequisites\r\ + \n:if ([:len [/system/package/find where name=\"container\" disabled=no]] \ + = 0) do={\r\ + \n :put \"Container package is not installed or disabled. Install it and \ + reboot.\"\r\ + \n :error \"Usage: /system/device-mode/update container=yes\"\r\ + \n}\r\ + \n\r\ + \n# Auto-detect storage\r\ + \n:global awgDisk \"disk1\"\r\ + \n{\r\ + \n :local usbFree 0\r\ + \n :do { :set usbFree [/disk/get [find where mount-point=\"usb1\"] free] \ + } on-error={}\r\ + \n :if (\$usbFree > 0) do={\r\ + \n :set awgDisk \"usb1\"\r\ + \n :put (\"Using USB: usb1 (\" . (\$usbFree / 1048576) . \" MB free)\")\ + \r\ + \n } else={\r\ + \n :if ([:len [/disk/find where slot=\"usb1\"]] > 0) do={\r\ + \n :put \"USB found but not mounted. Format as ext4\?\"\r\ + \n :put \"Press 'y' to confirm:\"\r\ + \n :local key [/terminal/inkey timeout=30]\r\ + \n :if (\$key = 121 || \$key = 89) do={\r\ + \n /disk/format-drive usb1 file-system=ext4 label=usb1\r\ + \n :delay 3s\r\ + \n :do { :set usbFree [/disk/get [find where mount-point=\"usb1\"] \ + free] } on-error={}\r\ + \n :if (\$usbFree > 0) do={\r\ + \n :set awgDisk \"usb1\"\r\ + \n } else={ :put \"Format failed, using disk1\" }\r\ + \n } else={ :put \"Using disk1\" }\r\ + \n } else={ :put \"No USB, using disk1\" }\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# 1. Uninstall script\r\ + \n/system/script/add name=awg-VDSina-uninstall comment=awg-VDSina source={\ + \r\ + \n :put \"Uninstallimg AmneziaWG...\"\r\ + \n :log info \"Uninstallimg AmneziaWG...\"\r\ + \n :local prevDNS \"\"\r\ + \n :do { :set prevDNS [/container/envs/get [find where list=\"awg-VDSina-\ + env\" key=\"AWG_PREV_DNS\"] value] } on-error={}\r\ + \n :local storageDisk \"disk1\"\r\ + \n :do { :set storageDisk [/container/envs/get [find where list=\"awg-VDS\ + ina-env\" key=\"AWG_STORAGE\"] value] } on-error={}\r\ + \n :local prevTmpdir \"\"\r\ + \n :if (\$storageDisk != \"disk1\") do={\r\ + \n :do { :set prevTmpdir [/container/envs/get [find where list=\"awg-VD\ + Sina-env\" key=\"AWG_PREV_TMPDIR\"] value] } on-error={}\r\ + \n }\r\ + \n /ip/route/remove [find where comment=awg-VDSina-tunnel]\r\ + \n /ip/route/remove [find where comment=awg-VDSina-dns]\r\ + \n /container/stop [find where interface=veth-awg-VDSina]\r\ + \n :delay 7s\r\ + \n /container/remove [find where interface=veth-awg-VDSina]\r\ + \n /container/envs/remove [find where list=\"awg-VDSina-env\"]\r\ + \n :if (\$prevDNS != \"\") do={\r\ + \n :do { /ip/dns/set servers=\"\$prevDNS\" } on-error={}\r\ + \n :put (\"DNS restored to: \$prevDNS\")\r\ + \n }\r\ + \n :if (\$storageDisk != \"disk1\") do={\r\ + \n /container/config set tmpdir=\$prevTmpdir\r\ + \n :put (\"Container tmpdir restored to: \$prevTmpdir\")\r\ + \n }\r\ + \n /ip/firewall/nat/remove [find where out-interface=\"wg-awg-VDSina\"]\r\ + \n /ip/firewall/nat/remove [find where src-address=\"172.19.75.208/30\"]\ + \r\ + \n /ip/address/remove [find where address=\"172.19.75.209/30\"]\r\ + \n /interface/veth/remove [find where name=\"veth-awg-VDSina\"]\r\ + \n /interface/wireguard/peers/remove [find where interface=\"wg-awg-VDSin\ + a\"]\r\ + \n /ip/address/remove [find where interface=\"wg-awg-VDSina\"]\r\ + \n /interface/wireguard/remove [find where name=\"wg-awg-VDSina\"]\r\ + \n :do { /file/remove [find where name~\"awg-VDSina.+tar\"] } on-error={}\ + \r\ + \n :do { /file/remove [find where name=(\$storageDisk . \"/awg-VDSina\")]\ + \_} on-error={}\r\ + \n :do { /file/remove [find where name=(\$storageDisk . \"/ru_ranges_time\ + out.rsc\")] } on-error={}\r\ + \n :if (\$storageDisk != \"disk1\") do={\r\ + \n :do { /file/remove [find where name~(\$storageDisk . \"/pull\")] } o\ + n-error={}\r\ + \n }\r\ + \n :put \"Uninstall AmneziaWG Proxy complete!\"\r\ + \n :log info \"Uninstall AmneziaWG Proxy complete!\"\r\ + \n /system/script/remove [find where name=awg-VDSina-uninstall]\r\ + \n}\r\ + \n\r\ + \n# 2. Network infrastructure\r\ + \n/interface/veth/add name=veth-awg-VDSina address=172.19.75.210/30 gatewa\ + y=172.19.75.209\r\ + \n/ip/address/add address=172.19.75.209/30 interface=veth-awg-VDSina\r\ + \n/ip/firewall/nat/add chain=srcnat action=masquerade src-address=172.19.7\ + 5.208/30\r\ + \n\r\ + \n# 3. WireGuard interface (MikroTik derives the public key automatically)\ + \r\ + \n/interface/wireguard/add name=wg-awg-VDSina private-key=\"QCMsDU98Gm+nhn\ + kYRcAN/dyczDYHOLsX3PfBRFjWBF4=\" listen-port=14710 disabled=yes\r\ + \n/interface/wireguard/peers/add interface=wg-awg-VDSina public-key=\"o5Ig\ + Cl1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+v0b9jU=\" preshared-key=\"b39T9K2/LR8vNtY\ + AZw+sLRze6yGlS3EaZhAa7wiFB20=\" endpoint-address=172.19.75.210 endpoint-po\ + rt=51820 allowed-address=0.0.0.0/0 persistent-keepalive=10\r\ + \n/ip/address/add address=10.9.0.2/32 interface=wg-awg-VDSina\r\ + \n\r\ + \n# 4. Container environment variables\r\ + \n/container/envs/add list=awg-VDSina-env key=AWG_LISTEN value=\":51820\"\ + \r\ + \n/container/envs/add list=awg-VDSina-env key=AWG_REMOTE value=\"109.107.1\ + 77.79:55055\"\r\ + \n/container/envs/add list=awg-VDSina-env key=AWG_JC value=\"9\"\r\ + \n/container/envs/add list=awg-VDSina-env key=AWG_JMIN value=\"105\"\r\ + \n/container/envs/add list=awg-VDSina-env key=AWG_JMAX value=\"920\"\r\ + \n/container/envs/add list=awg-VDSina-env key=AWG_S1 value=\"90\"\r\ + \n/container/envs/add list=awg-VDSina-env key=AWG_S2 value=\"136\"\r\ + \n/container/envs/add list=awg-VDSina-env key=AWG_H1 value=\"472437748-472\ + 438637\"\r\ + \n/container/envs/add list=awg-VDSina-env key=AWG_H2 value=\"707808381-707\ + 809250\"\r\ + \n/container/envs/add list=awg-VDSina-env key=AWG_H3 value=\"1609078552-16\ + 09079495\"\r\ + \n/container/envs/add list=awg-VDSina-env key=AWG_H4 value=\"1012329484-10\ + 12329741\"\r\ + \n/container/envs/add list=awg-VDSina-env key=AWG_SERVER_PUB value=\"o5IgC\ + l1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+v0b9jU=\"\r\ + \n/container/envs/add list=awg-VDSina-env key=AWG_CLIENT_PUB value=[/inter\ + face/wireguard/get [find name=wg-awg-VDSina] public-key]\r\ + \n/container/envs/add list=awg-VDSina-env key=AWG_S3 value=\"45\"\r\ + \n/container/envs/add list=awg-VDSina-env key=AWG_S4 value=\"27\"\r\ + \n/container/envs/add list=awg-VDSina-env key=AWG_I1 value=\"\"\r\ + \n/container/envs/add list=awg-VDSina-env key=AWG_I2 value=\"\"\r\ + \n/container/envs/add list=awg-VDSina-env key=AWG_I3 value=\"\ + \"\r\ + \n/container/envs/add list=awg-VDSina-env key=AWG_I4 value=\"\"\ + \r\ + \n/container/envs/add list=awg-VDSina-env key=AWG_I5 value=\"\"\r\ + \n\r\ + \n# 5. DNS\r\ + \n{ :local rawDNS [/ip/dns/get servers]; :local prevDNS \"\"; :foreach s i\ + n=[:toarray \$rawDNS] do={ :if ([:len \$prevDNS] > 0) do={ :set prevDNS \"\ + \$prevDNS,\$s\" } else={ :set prevDNS \$s } }; /container/envs/add list=aw\ + g-VDSina-env key=AWG_PREV_DNS value=\"\$prevDNS\" }\r\ + \n# NOTE: replaces existing DNS configuration\r\ + \n/ip/dns/set servers=109.107.177.79\r\ + \n/ip/route/add dst-address=109.107.177.79/32 gateway=wg-awg-VDSina distan\ + ce=1 comment=awg-VDSina-dns\r\ + \n\r\ + \n# 6. Download, create and start container\r\ + \n/container/envs/add list=awg-VDSina-env key=AWG_STORAGE value=\$awgDisk\ + \r\ + \n:if (\$awgDisk != \"disk1\") do={\r\ + \n :local tmpPath (\$awgDisk . \"/pull\")\r\ + \n /container/envs/add list=awg-VDSina-env key=AWG_PREV_TMPDIR value=[/co\ + ntainer/config/get tmpdir]\r\ + \n /container/config set tmpdir=\$tmpPath memory-high=200M\r\ + \n}\r\ + \n{\r\ + \n :local arch [/system/resource/get architecture-name]\r\ + \n :local ver [/system/resource/get version]\r\ + \n :local dotPos [:find \$ver \".\"]\r\ + \n :local rest [:pick \$ver (\$dotPos + 1) [:len \$ver]]\r\ + \n :local endPos [:find \$rest \".\"]\r\ + \n :if ([:typeof \$endPos] = \"nil\") do={ :set endPos [:find \$rest \" \ + \"] }\r\ + \n :local minor [:tonum [:pick \$rest 0 \$endPos]]\r\ + \n :local suffix \"\"\r\ + \n :if (\$minor <= 20) do={ :set suffix \"-7.20-Docker\" }\r\ + \n :local file \"\"\r\ + \n :if (\$arch = \"arm64\") do={ :set file (\"awg-proxy-arm64\" . \$suffi\ + x . \".tar.gz\") }\r\ + \n :if (\$arch = \"arm\") do={ :set file (\"awg-proxy-arm\" . \$suffix . \ + \".tar.gz\") }\r\ + \n :if (\$arch ~ \"x86\") do={ :set file (\"awg-proxy-amd64\" . \$suffix \ + . \".tar.gz\") }\r\ + \n :if (\$file = \"\") do={ :error \"Unsupported architecture: \$arch\" }\ + \r\ + \n :local url \"https://github.com/timbrs/amneziawg-mikrotik-c/releases/l\ + atest/download/\$file\"\r\ + \n :local filePath \$file\r\ + \n :if (\$awgDisk != \"disk1\") do={\r\ + \n :set filePath (\$awgDisk . \"/pull/\" . \$file)\r\ + \n :do { /file/make-directory (\$awgDisk . \"/pull\") } on-error={}\r\ + \n }\r\ + \n :if ([:len [/file/find where name=\$filePath]] = 0) do={\r\ + \n :local freeStorage 0\r\ + \n :if (\$awgDisk = \"disk1\") do={\r\ + \n :set freeStorage [/system/resource/get free-hdd-space]\r\ + \n } else={\r\ + \n :do { :set freeStorage [/disk/get [find where mount-point=\$awgDis\ + k] free] } on-error={}\r\ + \n }\r\ + \n :if (\$freeStorage < 262144) do={\r\ + \n :put (\"WARNING: Low disk space (\" . (\$freeStorage / 1024) . \"K\ + B free). Need at least 256KB.\")\r\ + \n :put \"See: https://github.com/timbrs/amneziawg-mikrotik-c#insuffi\ + cient-disk-space\"\r\ + \n :error \"Insufficient disk space\"\r\ + \n }\r\ + \n :put (\"Fetching: \$url -> \" . \$filePath)\r\ + \n /tool/fetch url=\$url dst-path=\$filePath http-max-redirect-count=10\ + \r\ + \n } else={\r\ + \n :put (\"File already exists: \" . \$filePath)\r\ + \n }\r\ + \n :local rootDir (\$awgDisk . \"/awg-VDSina\")\r\ + \n /container/add file=\$filePath interface=veth-awg-VDSina envlist=awg-V\ + DSina-env hostname=awg-VDSina root-dir=\$rootDir logging=no start-on-boot=\ + yes comment=awg-VDSina\r\ + \n :if (\$minor > 20) do={ [:parse \"/container/set [find where interface\ + =veth-awg-VDSina] shm-size=4M\"] }\r\ + \n /file/remove \$filePath\r\ + \n :local freeMem [/system/resource/get free-memory]\r\ + \n :if (\$freeMem < 16777216) do={\r\ + \n :put (\"WARNING: Low memory (\" . (\$freeMem / 1048576) . \"MB free)\ + . 16MB+ recommended.\")\r\ + \n }\r\ + \n /container/start [find where interface=veth-awg-VDSina]\r\ + \n :do { /file/remove [find where name=\"console-dump.txt\"] } on-error={\ + }\r\ + \n :put \"Waiting for container to start...\"\r\ + \n :delay 5s\r\ + \n /interface/wireguard/enable wg-awg-VDSina\r\ + \n :put \"WireGuard interface enabled\"\r\ + \n :put \"Installation complete!\"\r\ + \n :put \"Ping test: 109.107.177.79 via AmneziaWG tunnel. repeat 10 times\ + \"\r\ + \n /ping 109.107.177.79 count=10 interval=1\r\ + \n :put \"\"\r\ + \n :put \"======= NEXT STEPS =======\"\r\ + \n :put \"\"\r\ + \n :put \"Route specific traffic through the AmneziaWG tunnel:\"\r\ + \n :put \"\"\r\ + \n :put \" Route a single host:\"\r\ + \n :put \" /ip/route/add dst-address=8.8.8.8/32 gateway=wg-awg-VDSina \ + comment=awg-VDSina-tunnel\"\r\ + \n :put \"\"\r\ + \n :put \" Route a subnet:\"\r\ + \n :put \" /ip/route/add dst-address=100.0.0.0/8 gateway=wg-awg-VDSina\ + \_comment=awg-VDSina-tunnel\"\r\ + \n :put \"\"\r\ + \n :put \" Remove a route:\"\r\ + \n :put \" /ip/route/remove [find where comment=awg-VDSina-tunnel dst-\ + address=8.8.8.8/32]\"\r\ + \n :put \"\"\r\ + \n :put \" Show all tunnel routes:\"\r\ + \n :put \" /ip/route/print where comment=awg-VDSina-tunnel\"\r\ + \n :put \"\"\r\ + \n :put \" Uninstall:\"\r\ + \n :put \" /system/script/run awg-VDSina-uninstall\"\r\ + \n :put \"=========================\"\r\ + \n}\r\ + \n\r\ + \n# 7. Verification\r\ + \n# /container/print\r\ + \n# /interface/wireguard/print\r\ + \n# /interface/wireguard/peers/print\r\ + \n" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-date=2026-07-01 start-time=02:03:59 +/tool e-mail +set certificate-verification=no from=MikroVados port=587 \ + server=smtp.mail.me.com tls=starttls user=vadossio@icloud.com +/tool netwatch +add comment=Monitor-AWGVDSINA down-script=\ + "/ip route disable [find comment=\"Route-AWGVDSINA\"]" host=9.9.9.9 \ + interval=10s packet-count=5 thr-avg=0ms thr-loss-count=4 timeout=5s type=\ + icmp up-script="/ip route enable [find comment=\"Route-AWGVDSINA\"]" diff --git a/MikroGig/history/20260711/20260711210146-Diff-Config.rsc b/MikroGig/history/20260711/20260711210146-Diff-Config.rsc new file mode 100644 index 0000000..7700481 --- /dev/null +++ b/MikroGig/history/20260711/20260711210146-Diff-Config.rsc @@ -0,0 +1,4 @@ +11c11 +< # channel: 2442/20-Ce/gn(17dBm), SSID: vados, local forwarding +--- +> # channel: 2422/20-Ce/gn(17dBm), SSID: vados, local forwarding diff --git a/MikroMaster/20260711/20260711200003-Config.rsc b/MikroMaster/20260711/20260711200003-Config.rsc new file mode 100644 index 0000000..809894c --- /dev/null +++ b/MikroMaster/20260711/20260711200003-Config.rsc @@ -0,0 +1,3841 @@ +# 2026-07-11 20:00:08 by RouterOS 7.23.1 +# software id = A8L5-07YF +# +# model = RB952Ui-5ac2nD +# serial number = D3D50CEDCD38 +/interface bridge +add admin-mac=48:8F:5A:8E:1A:BF auto-mac=no dhcpv6-agent-circuit-id=\ + "\$(INTERFACE):\$(VID)" dhcpv6-agent-remote-id="\$(BRIDGEMAC)" name=\ + Bridge-LAN port-cost-mode=short +/interface ethernet +set [ find default-name=ether1 ] comment=Unet-cable loop-protect=off name=\ + Unet-ether1 rx-flow-control=auto tx-flow-control=auto +set [ find default-name=ether2 ] comment=LAN-2 +set [ find default-name=ether3 ] advertise="10M-baseT-half,10M-baseT-full,100M\ + -baseT-half,100M-baseT-full,1G-baseT-full,1G-baseX" comment=LAN-3 +set [ find default-name=ether4 ] comment=LAN-4 +set [ find default-name=ether5 ] advertise="10M-baseT-half,10M-baseT-full,100M\ + -baseT-half,100M-baseT-full,1G-baseT-full,1G-baseX" comment=LAN-5 \ + loop-protect=on poe-out=off rx-flow-control=auto tx-flow-control=auto +/interface wireless +# managed by CAPsMAN +# channel: 2422/20-Ce/gn(18dBm), SSID: vados, local forwarding +set [ find default-name=wlan1 ] country=belarus disabled=no mode=ap-bridge \ + ssid=vados wireless-protocol=802.11 +# managed by CAPsMAN +# channel: 5200/20-eCee/ac/P(21dBm), SSID: vados5g, local forwarding +set [ find default-name=wlan2 ] country=belarus disabled=no mode=ap-bridge \ + ssid=vados5g wireless-protocol=802.11 +/interface wireguard +add comment=WG listen-port=13231 mtu=1320 name=WG +add comment="WG interface for Amnezia route" disabled=yes listen-port=33033 \ + mtu=1360 name=wg-nuc +/interface list +add include=static name=LAN +add name=Internet +add include=Internet name=WAN +/interface wireless security-profiles +set [ find default=yes ] authentication-types=wpa-psk,wpa2-psk mode=\ + dynamic-keys supplicant-identity=MikroTik +/iot wiliot servers +set *1 address=mqtt.us-east-2.prod.wiliot.cloud name="Wiliot US East" +/ip dhcp-server option +add code=12 name=HostName +add code=119 force=yes name=domain-search-list value="0x03'WORKGROUP'0x00" +/ip dhcp-server option sets +add name=HP1505n options=HostName +add name=StartLife1 options=HostName +add name=amsterDev options=HostName +add name=MiTV options=HostName +add name=miller options=HostName +add name=amster options=HostName +add name=garage options=HostName +add name=domain-search-set options=domain-search-list +add name=VADOS-LEN options=HostName +add name=VADOS-NUC options=HostName +add name=MikroNUC options=HostName +add name=master options=HostName +add name=MikroYellow options=HostName +add name="=\"Client ID\"" options=HostName +add name=iVados options=HostName +/ip dns forwarders +add disabled=yes dns-servers=1.0.0.1,1.1.1.1 name=blsp.at verify-doh-cert=no +add comment=Kra disabled=yes dns-servers=1.0.0.1,1.1.1.1 name=slon2.to \ + verify-doh-cert=no +add disabled=yes dns-servers=1.1.1.1,1.0.0.1 name=slon3.at verify-doh-cert=no +add disabled=yes dns-servers=1.0.0.1,1.1.1.1 name=slon3.cc verify-doh-cert=no +add disabled=yes dns-servers=1.1.1.1,1.0.0.1 name=bs2web.at verify-doh-cert=\ + no +add disabled=yes dns-servers=1.1.1.1,1.0.0.1 name=bs2best.at verify-doh-cert=\ + no +add disabled=yes dns-servers=1.0.0.1,1.1.1.1 name=psrms.is verify-doh-cert=no +/ip ipsec proposal +set [ find default=yes ] disabled=yes +/ip pool +add name=dhcp ranges=10.30.30.83-10.30.30.254 +/ip dhcp-server +add add-arp=yes address-pool=dhcp always-broadcast=yes bootp-support=none \ + dhcp-option-set=domain-search-set interface=Bridge-LAN lease-time=10m \ + name=masterDHCP server-address=10.30.30.81 +/ip smb users +set [ find default=yes ] disabled=yes +add disabled=yes name=vados +/ipv6 dhcp-client option +add code=119 name=domain-search-list value="0x03'WORKGROUP'0x00" +add code=12 name=HostName value="\$(HOSTNAME)" +add code=61 name=DUID value=0x00030001488f5a8e1abc +add code=61 name=clientid +/ipv6 dhcp-server option +add code=119 name=domain-search-list value="0x03'WORKGROUP'0x00" +add code=12 name=HostName value="\$(HOSTNAME)" +add code=61 name=CLIENT_DUID +add code=61 name=CLIENT_MAC +/ppp profile +set *FFFFFFFE use-upnp=no +/routing bgp template +set default disabled=no output.network=bgp-networks +/routing table +add disabled=no fib name=To-VPN +add disabled=no fib name=r_to_vpn +/system logging action +add email-start-tls=yes email-to=vados@vados.ru name=email target=email +/system script +add dont-require-permissions=no name=fetchCustom owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n\r\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.86.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.86.pub\";\ + \n\r\ + \n#:local Script AM-setup\ + \n;\r\ + \n#/system/script/add name=\$Script owner=\$Script source=([ /tool/fetch c\ + heck-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".r\ + sc\") output=user as-value ]->\"data\");\r\ + \n\ + \n#/usb1/mikro_bak_rsa.pub\ + \n\ + \n\r\ + \n#\ + \n\r\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.10.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.10.pub\";\ + \n\r\ + \n#\r\ + \n\ + \n#root.mnuc.backuper\ + \n\ + \n\r\ + \n/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@10\ + .30.30.81.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.81.pub\";\r\ + \n\ + \n#:local Script AM-GlobalConfig;\ + \n\r\ + \n#/system/script/add name=\$Script owner=\$Script source=([ /tool/fetch c\ + heck-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".r\ + sc\") output=user as-value ]->\"data\");\r\ + \n\ + \n#/usb1/mikro_bak_rsa.pub\ + \n#:local FetchFile AM-DOHCert.rsc\ + \n\ + \n\r\ + \n\r\ + \n#\ + \n:global MkDir;\ + \n\r\ + \n\ + \n#:local FetchFile Binary.backup\ + \n:local localDir \"\";\ + \n\ + \n\r\ + \n#\$MkDir \$localDir;\r\ + \n\ + \n#\ + \n/tool/fetch mode=https url=(\$GlobalScriptsUrl . \$FetchFile) dst-path=(\ + \$localDir . \$FetchFile);\r\ + \n" +add comment="Global Config Script" dont-require-permissions=no name=\ + AM-GlobalConfig owner=AM-GlobalConfig policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GlobalConfig\r\ + \n# Script comment: Global Config Script\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# Warning: Dont touch this line!\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global GlobalConfReady false;\r\ + \n#\r\ + \n#\r\ + \n# Global Url For Amster scripts to fetch\r\ + \n# most be defined in AM-Init-Script or AM-setup scripts\r\ + \n:global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n:global ScriptsUrlSuffix (a);\r\ + \n:global CheckSumsVerify true;\r\ + \n\r\ + \n:global ScriptUpdatesCRLF true;\r\ + \n:global CommentsInScripts true;\r\ + \n\r\ + \n# Debug output:\r\ + \n:global PrintDebug true; #false;\r\ + \n\r\ + \n# Debug output for specific script:\r\ + \n#:set (\$PrintDebugOverride->\"AM-GlobalFunc\") true;\r\ + \n\r\ + \n# Debug logs\r\ + \n# Other actions (disk, email, remote or support) can be used as well.\r\ + \n# I do not recommend using echo - use debug output instead (https://rsc.\ + eworm.de/main/DEBUG.html#debug-output).\r\ + \n#/system/logging/add topics=script,debug action=memory;\r\ + \n\r\ + \n:global MikrotikUpgradeUrl \"https://upgrade.mikrotik.com/routeros\";\r\ + \n:global Domain \"WORKGROUP\";\r\ + \n\r\ + \n:global EmailGeneralTo \"vados@vados.ru\";\r\ + \n#:global EmailGeneralCc \"\";\r\ + \n\r\ + \n# \$ScriptInstallUpdate mod/notification-telegram\r\ + \n:global TelegramTokenId \"8390868324:AAG3_d1hfVR2M6pGAgDK6ZvhOe4J97nyopw\ + \";\r\ + \n:global TelegramChatId \"-1003631309063\"; #\"454403445\";\r\ + \n# Use this to send notifications to a specific topic in group.\r\ + \n#:global TelegramThreadId \"30\";\r\ + \n# Using telegram-chat you have to define trusted chat ids (not group ids\ + !)\r\ + \n# or user names. Groups allow to chat with devices simultaneously.\r\ + \n:global TelegramChatIdsTrusted {\r\ + \n \"454403445\"\r\ + \n \"-1003631309063\"\r\ + \n \"-5124482807\"\r\ + \n \"1087968824\";\r\ + \n};\r\ + \n# \"-5294757340\";\r\ + \n\r\ + \n:global TelegramChatGroups \"(all)\";\r\ + \n#:global TelegramChatGroups \"(all|home|office)\";\r\ + \n\r\ + \n:global NotificationFunctions {\r\ + \n \"email\"\r\ + \n \"telegram\";\r\ + \n};\r\ + \n# Toggle this to disable symbols in notifications.\r\ + \n:global NotificationsWithSymbols true;\r\ + \n:global TerminalColorOutput true;\r\ + \n\r\ + \n:global DetectIpAddrDefault \"https://ipv4.mikrotik.ovh/\";\r\ + \n:global DetectIpAddrFallback \"https://api.ipify.org/\";\r\ + \n\r\ + \n## Update channel. Possible values: stable, long-term, testing, developm\ + ent\r\ + \n:global updateChannel \"stable\";\r\ + \n\r\ + \n:global CertRenewUrl (\$GlobalScriptsUrl . \"certs/\" . \$Identity . \"/\ + \");\r\ + \n:global CertRenewTime 2w;\r\ + \n:global CertRenewPass (a); #{\r\ + \n# \"\";\r\ + \n# \"v3ry-s3cr3t\";\r\ + \n# \"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n:global CertWarnTime 1w;\r\ + \n:global CertIssuedExportPass (a); # {\r\ + \n# \"\";\r\ + \n# \"cert1-cn\"=\"v3ry-s3cr3t\";\r\ + \n# \"cert2-cn\"=\"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n\r\ + \n# Script BackupAndUpdate mode, possible values: backup,\r\ + \n# updateOnce - Update once as \"osnotify\" and back \"osnotify\" mode (i\ + n terminal run :set OSUpdateMode updateOnce; \$OSUpdateMode;). \r\ + \n# osupdate - Update if available and creates backups before/after update\ + \_(ignores `forceBackup`)\r\ + \n# Set `forceBackup` to true to always create backups, even\ + \_without updates\r\ + \n# osnotify - Set `forceBackup` to always create backups on every run\r\ + \n:global DevInfoIncludeIP true;\r\ + \n:global BackupAndUpdateMode \"osnotify\"\r\ + \n\r\ + \n:global forceBackup true;\r\ + \n:global BackupPassword \"Peskar55\";\r\ + \n\r\ + \n# SFTP Backup Variables\r\ + \n:global BackupRandomDelay 0;\r\ + \n:global BackupUploadUrl \"sftp://amster.vados.ru/upload/\";\r\ + \n:global BackupUploadUser \$Identity;\r\ + \n:global BackupUploadPass \"root.mnuc.backuper\";\r\ + \n\r\ + \n:global BackupSendBinary true;\r\ + \n:global BackupSendExport true;\r\ + \n:global BackupSendGlobalConfig true;\r\ + \n:global BackupSendScripts true;\r\ + \n\r\ + \n# Remove local file after uploading if no errors\r\ + \n:global BackupRmLocal true;\r\ + \n\r\ + \n# Local Backups Root Directory \r\ + \n:global BackupLocalRoot \"tmpfs\";\r\ + \n\r\ + \n# Encrypt Backup\r\ + \n:global BackupEncrypt true;\r\ + \n\r\ + \n# Sensitive information in Backups\r\ + \n:global BackupSens true;\r\ + \n\r\ + \n# User Export\r\ + \n:global BackupUser true;\r\ + \n\r\ + \n# License Export (not for CHR, will silently skip)\r\ + \n:global BackupLicense false;\r\ + \n\r\ + \n# SSH Keys\r\ + \n:global BackupSshKeys true;\r\ + \n\r\ + \n# Certificate Export\r\ + \n:global BackupCerts true;\r\ + \n\r\ + \n# Certificate Password\r\ + \n:global BackupCertsPasswd \"\";\r\ + \n\r\ + \n# User-Manager Export\r\ + \n:global BackupUserMan false;\r\ + \n\r\ + \n# The Dude Export\r\ + \n:global BackupDude false;\r\ + \n\r\ + \n# User Files to export, comma-separated string or array of strings\r\ + \n# User Files are not removed on backup\r\ + \n# Any directory paths will be removed (/ -> _) on remote file\r\ + \n# Nonpresent files are silently skipped\r\ + \n:global BackupUserList \"autosupout.rif,autosupout.old.rif\";\r\ + \n\r\ + \n# load functions and any custom scripts\r\ + \n# [ /system/script/find where name=\"global-config-ov\ + erlay\" ], \\\r\ + \n# [ /system/script/find where name~\"^global-config-o\ + verlay\\\\.d/.\" ]\r\ + \n#\r\ + \n#:foreach Script in=([ /system/script/find where name=\"Amster-TGglobalV\ + ars\" ], [ /system/script/find where name=\"Amster-TGglobalFunc\" ]) do={\ + \r\ + \n# :onerror Err {\r\ + \n# /system/script/run \$Script;\r\ + \n# } do={\r\ + \n# :log error (\"Loading configuration from configs and functions\"\ + \_. [ /system/script/get \$Script name ] . \" failed: \" . \$Err);\r\ + \n# }\r\ + \n#}\r\ + \n#:log info \"\\ndevMode = \$devMode\";\r\ + \n#:log info \"\\ndevRunning = \$devRunning\";\r\ + \n:foreach Script in=[ /system/script/find where source~\"^#!rsc by Vados \ + GlobalRUN\\r\?\\n\" ] do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n :log error (\"Run script \" . [ /system/script/get \$Script name \ + ] . \" width marker \\\" GlobalRUN\\\" failed: \" . \$Err);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# signal we are ready\r\ + \n:set GlobalConfReady true;\r\ + \n:while (\$GlobalConfReady != true) do={:delay 500ms};\r\ + \n" +add comment="Global functions" dont-require-permissions=no name=AM-GlobalFunc \ + owner=AM-GlobalFunc policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GglobalFunc\r\ + \n# Script comment: Global functions\r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch, scheduler\r\ + \n:local ExitOK false;\r\ + \n:local ScriptName [ :jobname ];\r\ + \n#:global checkRunJob \$SriptName;\r\ + \n#:global fixCRLF;\r\ + \n#:if (\$fixCRLF != false) do={\r\ + \n#:foreach fixScript in=([ /system/script/find where name~\"*\" ]) do={/s\ + ystem/script/set source=[ :tocrlf [ get \$fixScript source ] ] \$fixScript\ + }\r\ + \n#:if (\$fixCRLF != \"LF\") do={ /system/script/set source=[ :tocrlf [ g\ + et \$fixScript source ] ] \$fixScript};};\r\ + \n#}\r\ + \n:global GlobalFuncReady false;\r\ + \n\r\ + \n# Git commit id & info, expected configuration version\r\ + \n:global CommitId \"main/729/f42d43ae\";\r\ + \n:global CommitTAG \"unknown\";\r\ + \n:global CommitInfo \"Autocommit in repo: ros.vados.ru, generated by owne\ + r: root, in 00:05:03 01.07.2026.\";\r\ + \n\r\ + \n# The variables are received right here.\r\ + \n:global GlobalConfVersion 140;\r\ + \n\r\ + \n:global SumsDirAndFileName \"tmp/checksums.json\";\r\ + \n\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global IdentityShort [:pick \$Identity 0 18];\r\ + \n\r\ + \n:global devBoardName [/system/resource/get board-name];\r\ + \n:global devMode [/system/device-mode/get mode];\r\ + \n:global devCurArch [/system/resource/get architecture-name];\r\ + \n:global devCurVersion [/system/resource/get version];\r\ + \n\r\ + \n# Variables defined in AM-GlobalConfig.rsc\r\ + \n:global TerminalColorOutput;\r\ + \n:global CommentsInScripts;\r\ + \n:global DetectIpAddrDefault;\r\ + \n:global DetectIpAddrFallback;\r\ + \n:global updateChannel;\r\ + \n:global Domain;\r\ + \n:global EmailGeneralTo;\r\ + \n:global BackupAndUpdateMode;\r\ + \n\r\ + \n# Single functions, without nested functions.\r\ + \n:global MiniDateTimeStamp;\r\ + \n:global MicroDateTimeStamp;\r\ + \n\r\ + \n# Include globals \$PrintDebug and \$PrintDebugOverride\r\ + \n:global LogPrint;\r\ + \n\r\ + \n# DeviceInfo and Updates\r\ + \n:global IsCheckUpdates;\r\ + \n:global WaitCheckUpdates;\r\ + \n:global GetPubIp;\r\ + \n\r\ + \n:global GetArrDeviceInfo;\r\ + \n:global WaitArrDeviceInfo;\r\ + \n\r\ + \n:global devModel;\r\ + \n:global devRunningMode;\r\ + \n\r\ + \n:global devRbRevision;\r\ + \n:global devRbSerialNumber;\r\ + \n:global devRbVerAvail;\r\ + \n\r\ + \n:global runningChannelVersion;\r\ + \n:global runningChannel;\r\ + \n:global runningVersion;\r\ + \n\r\ + \n:global devBuildTime;\r\ + \n:global ROsVerAvail;\r\ + \n:global PkgUpdStatus;\r\ + \n:global currentUpdateChannel;\r\ + \n\r\ + \n:global ScriptInstallUpdate;\r\ + \n\r\ + \n:global DeviceInfo;\r\ + \n\r\ + \n:global OldMode;\r\ + \n\r\ + \n:global ParseKeyValueStore;\r\ + \n:global RandomDelay;\r\ + \n:global GetRandomNumber;\r\ + \n:global EscapeForRegEx;\r\ + \n:global CharacterMultiply;\r\ + \n:global CharacterReplace;\r\ + \n:global CleanFilePath;\r\ + \n:global CleanName;\r\ + \n\r\ + \n:global CommitBrief;\r\ + \n\r\ + \n:global EitherOr;\r\ + \n:global FormatLine;\r\ + \n:global GetRandom20CharAlNum;\r\ + \n:global GetRandom20CharHex;\r\ + \n:global GetRandomNumber;\r\ + \n:global Grep;\r\ + \n:global IfThenElse;\r\ + \n:global MAX;\r\ + \n:global MIN;\r\ + \n:global HumanReadableNum;\r\ + \n\r\ + \n:global IsFullyConnected;\r\ + \n:global IsDNSResolving;\r\ + \n:global IsTimeSync;\r\ + \n:global IsTimeSyncCached;\r\ + \n:global IsTimeSyncResetNtp;\r\ + \n\r\ + \n:global MkDir;\r\ + \n:global RmDir;\r\ + \n:global RmFile;\r\ + \n:global FileGet;\r\ + \n:global Unix2Dos;\r\ + \n:global UrlEncode;\r\ + \n:global WaitForFile;\r\ + \n:global ScriptLock;\r\ + \n:global SendTelegram;\r\ + \n:global NotificationFunctions;\r\ + \n:global SendNotification;\r\ + \n:global SendNotification2;\r\ + \n:global SymbolForNotification;\r\ + \n:global SymbolByUnicodeName;\r\ + \n:global ValidateSyntax;\r\ + \n:global CheckSumsVerify;\r\ + \n:global GlobalConfChanges;\r\ + \n:global GlobalConfMigration;\r\ + \n:global VersionToNum;\r\ + \n:global RequiredRouterOS;\r\ + \n:global ExitError;\r\ + \n:global ExitOnError;\r\ + \n\r\ + \n:global FetchHugeFiles;\r\ + \n:global FetchUserAgentStr;\r\ + \n:global FileExists;\r\ + \n:global CertificateAvailable;\r\ + \n:global CertificateDownload;\r\ + \n:global CertificateNameByCN;\r\ + \n:global CharacterMultiply;\r\ + \n\r\ + \n:global InspectVar;\r\ + \n:global InspectVarRet;\r\ + \n:global ParseCustom;\r\ + \n\r\ + \n:set IsCheckUpdates do={\r\ + \n :local isInfo [:tobool \$1];\r\ + \n :global updateChannel;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :log info (\$0 . \" \\\$isInfo = \" . \$isInfo); \r\ + \n :local CurrentChannel [ /system/package/update/get channel ];\r\ + \n :if ([ :len \$updateChannel ] > 0 && \$CurrentChannel != \$updateChann\ + el) do={\r\ + \n :retry command={/system/package/update set channel=\$updateChannel} \ + \_delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" Set updates channel to: \$updateChannel\");\r\ + \n }\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local Channel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set currentUpdateChannel \$Channel;\r\ + \n :log info (\$0 . \" Update Channel: \" . \$Channel . \".\");\r\ + \n :local StatusPkg \"undefined\";\r\ + \n :log info (\$0 . \" Checking for updates...\");\r\ + \n :retry command={:set \$StatusPkg ([/system/package/update/check-for-up\ + dates without-paging as-value]->\"status\")} \\\r\ + \n delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" \\\$StatusPkg = \" . \$StatusPkg);\r\ + \n :if (\$StatusPkg = \"New version is available\") do={ :set PkgUpdStatu\ + s true; } else={ :set PkgUpdStatus false; }\r\ + \n :set ROsVerAvail [ :tostr [ /system/package/update/get latest-version \ + ] ];\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if (\$isInfo != false || \$PkgUpdStatus = true) do={\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :retry command={:set (\$arrDeviceInfo->\"devUpdates\") [ /system/pac\ + kage/update/get ]} delay=500ms max=5 on-error={:log error (\"Error\")};\r\ + \n :return true;\r\ + \n } else={\r\ + \n :return \$PkgUpdStatus;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set WaitCheckUpdates do={\r\ + \n :global IsCheckUpdates;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :local Status \"\";\r\ + \n :local Update;\r\ + \n :local WaitCount 0;\r\ + \n :if ([ \$IsCheckUpdates true ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n :while ((\$Status = \"\" || \$Status = \"finding out latest version...\ + \") && \$WaitCount < 60) do={\r\ + \n :delay 1s;\r\ + \n :set WaitCount (\$WaitCount + 1);\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n }\r\ + \n :set Update [ /system/package/update/get ];\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n :if ((\$Update->\"status\") = \"New version is available\") do={\r\ + \n :set PkgUpdStatus true;\r\ + \n } else={\r\ + \n :set PkgUpdStatus false;\r\ + \n }\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :set (\$arrDeviceInfo->\"devUpdates\") \$Update;\r\ + \n :if (\$Status = \"\" || \$Status = \"finding out latest version...\") \ + do={\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set WaitArrDeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :while ([ \$GetArrDeviceInfo ] = false) do={\r\ + \n :delay 1s;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set GetArrDeviceInfo do={\r\ + \n :global LogPrint;\r\ + \n :global arrDeviceInfo;\r\ + \n :global devBoardName;\r\ + \n :global devModel;\r\ + \n :global devRunning;\r\ + \n :global devCurArch;\r\ + \n :global devRbRevision;\r\ + \n :global devRbSerialNumber;\r\ + \n :global devBuildTime;\r\ + \n :global runningChannelVersion;\r\ + \n :global runningChannel;\r\ + \n :global runningVersion;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n\r\ + \n :local DeviceInfo;\r\ + \n :local Resource [/system/resource/get];\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local VersionString [ :tostr (\$Resource->\"version\") ];\r\ + \n :local ChannelStart [ :find \$VersionString \" (\" ];\r\ + \n :local RouterBoard;\r\ + \n :local IsRouterBoard false;\r\ + \n :local RunningMode \"\";\r\ + \n\r\ + \n :set devCurArch [ :tostr (\$Resource->\"architecture-name\") ];\r\ + \n :set devBuildTime [ :tostr (\$Resource->\"build-time\") ];\r\ + \n :set runningChannelVersion \$VersionString;\r\ + \n :set runningVersion \$VersionString;\r\ + \n :set runningChannel \"\";\r\ + \n\r\ + \n :if ([ :typeof \$ChannelStart ] = \"num\" && \$ChannelStart >= 0) do=\ + {\r\ + \n :set runningVersion [ :pick \$VersionString 0 \$ChannelStart ];\r\ + \n :local ChannelRest [ :pick \$VersionString (\$ChannelStart + 2) [ :\ + len \$VersionString ] ];\r\ + \n :local ChannelEnd [ :find \$ChannelRest \")\" ];\r\ + \n :if ([ :typeof \$ChannelEnd ] = \"num\" && \$ChannelEnd >= 0) do={\ + \r\ + \n :set runningChannel [ :pick \$ChannelRest 0 \$ChannelEnd ];\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([:pick \$devBoardName 0 3] != \"CHR\" or [:pick \$devBoardName 0\ + \_3] != \"x86\") do={\r\ + \n :do {\r\ + \n :set RouterBoard [[ :parse \"/system/routerboard/get\" ]];\r\ + \n# :set RouterBoard [ /system/routerboard/get ];\r\ + \n :if ((\$RouterBoard->\"routerboard\") = true) do={\r\ + \n :set IsRouterBoard true;\r\ + \n }\r\ + \n } on-error={}\r\ + \n }\r\ + \n\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get mode ];\r\ + \n } on-error={\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get running ];\r\ + \n } on-error={\r\ + \n :set RunningMode \"\";\r\ + \n }\r\ + \n }\r\ + \n :set devRunning \$RunningMode;\r\ + \n\r\ + \n :if (\$IsRouterBoard = true) do={\r\ + \n :set devModel [ :tostr (\$RouterBoard->\"model\") ];\r\ + \n :set devRbRevision [ :tostr (\$RouterBoard->\"revision\") ];\r\ + \n :set devRbSerialNumber [ :tostr (\$RouterBoard->\"serial-number\") \ + ];\r\ + \n } else={\r\ + \n :set devModel \$devBoardName;\r\ + \n :set devRbRevision \"\";\r\ + \n :set devRbSerialNumber \"\";\r\ + \n }\r\ + \n\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n\r\ + \n :set (\$DeviceInfo->\"board-name\") \$devBoardName;\r\ + \n :set (\$DeviceInfo->\"architecture-name\") \$devCurArch;\r\ + \n :set (\$DeviceInfo->\"model\") \$devModel;\r\ + \n :set (\$DeviceInfo->\"revision\") \$devRbRevision;\r\ + \n :set (\$DeviceInfo->\"serial-number\") \$devRbSerialNumber;\r\ + \n :set (\$DeviceInfo->\"running-mode\") \$devRunning;\r\ + \n :set (\$DeviceInfo->\"build-time\") \$devBuildTime;\r\ + \n :set (\$DeviceInfo->\"running-channel-version\") \$runningChannelVers\ + ion;\r\ + \n :set (\$DeviceInfo->\"running-version\") \$runningVersion;\r\ + \n :set (\$DeviceInfo->\"running-channel\") \$runningChannel;\r\ + \n :set (\$DeviceInfo->\"routerboard\") \$IsRouterBoard;\r\ + \n :set (\$DeviceInfo->\"current-firmware\") [ :tostr (\$RouterBoard->\"\ + current-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"upgrade-firmware\") [ :tostr (\$RouterBoard->\"\ + upgrade-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"package-channel\") \$currentUpdateChannel;\r\ + \n :set (\$DeviceInfo->\"installed-version\") [ :tostr (\$Update->\"inst\ + alled-version\") ];\r\ + \n :set (\$DeviceInfo->\"latest-version\") \$ROsVerAvail;\r\ + \n :set (\$DeviceInfo->\"update-status\") [ :tostr (\$Update->\"status\"\ + ) ];\r\ + \n\r\ + \n :set arrDeviceInfo \$DeviceInfo;\r\ + \n :return \$DeviceInfo;\r\ + \n}\r\ + \n\r\ + \n:set DeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global IfThenElse;\r\ + \n :global FormatLine;\r\ + \n :global CommitBrief;\r\ + \n\r\ + \n :local License [/system/license/get];\r\ + \n :local Snmp [ /snmp/get ];\r\ + \n :local Info [ \$GetArrDeviceInfo ];\r\ + \n\r\ + \n :if ([ :typeof \$Info ] != \"array\") do={\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :return ( \\\r\ + \n [ \$FormatLine \"Hostname\" \$Identity ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"location\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Location\" (\$Snmp->\"location\") ] . \"\\n\") ]\ + \_. \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"contact\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Contact\" (\$Snmp->\"contact\") ] . \"\\n\") ] .\ + \_\\\r\ + \n \"Hardware:\\n\" . \\\r\ + \n [ \$FormatLine \" Board\" (\$Info->\"board-name\") ] . \"\\n\" . \ + \\\r\ + \n [ \$FormatLine \" Arch\" (\$Info->\"architecture-name\") ] . \"\\\ + n\" . \\\r\ + \n [ \$FormatLine \" Model\" (\$Info->\"model\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"revision\") ] > 0) \\\r\ + \n (\" \" . (\$Info->\"revision\")) ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"serial-number\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Serial\" (\$Info->\"serial-number\") ] . \"\ + \\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"nlevel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"nlevel\ + \")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS:\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"level\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"level\ + \")) ] . \"\\n\") ] . \\\r\ + \n [ \$FormatLine \" Channel\" (\$Info->\"package-channel\") ] . \"\ + \\n\" . \\\r\ + \n [ \$FormatLine \" Installed\" (\$Info->\"installed-version\") ] .\ + \_\"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"running-channel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Running\" ((\$Info->\"running-version\") . \ + \" (\" . (\$Info->\"running-channel\") . \")\") ] . \"\\n\") \\\r\ + \n ([ \$FormatLine \" Running\" (\$Info->\"running-version\") ] . \ + \"\\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"latest-version\") ] > 0 && \\\r\ + \n (\$Info->\"installed-version\") != (\$Info->\"latest-version\"))\ + \_\\\r\ + \n ([ \$FormatLine \" Available\" (\$Info->\"latest-version\") ] .\ + \_\"\\n\") ] . \\\r\ + \n [ \$IfThenElse ((\$Info->\"routerboard\") = true && \\\r\ + \n (\$Info->\"current-firmware\") != (\$Info->\"upgrade-firmware\")\ + ) \\\r\ + \n ([ \$FormatLine \" Firmware\" ((\$Info->\"current-firmware\") .\ + \_\" -> \" . (\$Info->\"upgrade-firmware\")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS-Scripts:\\n\" . \\\r\ + \n [ \$FormatLine \" Commit\" [ \$CommitBrief ] ] . \"\\n\" . \\\r\ + \n [ \$FormatLine \" Version\" \$GlobalConfVersion ]);\r\ + \n}\r\ + \n\r\ + \n:if ([ :typeof \$NotificationFunctions ] != \"array\") do={:set Notifica\ + tionFunctions ({})}\r\ + \n\r\ + \n:set GetRandom20CharAlNum do={\r\ + \n :global EitherOr;\r\ + \n :return [ :rndstr length=[ \$EitherOr [ :tonum \$1 ] 20 ] from=\"ABCDE\ + FGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\" ];\r\ + \n}\r\ + \n\r\ + \n:set GetRandom20CharHex do={:global EitherOr; :return [ :rndstr length=[\ + \_\$EitherOr [ :tonum \$1 ] 20 ] from=\"0123456789abcdef\" ]; }\r\ + \n:set GetRandomNumber do={:global EitherOr; :return [ :rndnum from=0 to=[\ + \_\$EitherOr [ :tonum \$1 ] 4294967295 ] ]; }\r\ + \n\r\ + \n:global checkRunJob;\r\ + \n:set checkRunJob do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLock; \r\ + \n :if ([ \$ScriptLock \$Script ] = false) do={:return false}\r\ + \n :foreach Job in=[ /system/script/job/find where script=\$Script ] do={\ + :set Job [ /system/script/job/get \$Job ];\r\ + \n :while ([ :typeof (\$Job->\"parent\") ] = \"id\") do={:set Job [ /sy\ + stem/script/job/get [ find where .id=(\$Job->\"parent\") ] ]}\r\ + \n :if ((\$Job->\"type\") = \"login\") do={:return true};}; :return fa\ + lse; \r\ + \n}\r\ + \n\r\ + \n:set InspectVar do={\r\ + \n :global InspectVarRet;\r\ + \n :put [ :tocrlf [ \$InspectVarRet \$1 ] ];\r\ + \n}\r\ + \n\r\ + \n:set CommitBrief do={\r\ + \n :global CommitId;\r\ + \n :global CommitInfo;\r\ + \n :if (\$CommitId = \"unknown\") do={\r\ + \n :return \"unknown\";\r\ + \n }\r\ + \n :return (\$CommitInfo . \"/\" . [ :pick \$CommitId 0 8 ]);\r\ + \n}\r\ + \n\r\ + \n:set InspectVarRet do={\r\ + \n :local Input \$1;\r\ + \n :local Level (0 + [ :tonum \$2 ]);\r\ + \n :global CharacterReplace;\r\ + \n :global IfThenElse;\r\ + \n :global InspectVarRet;\r\ + \n :local IndentReturn do={\r\ + \n :local Prefix [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Level [ :tonum \$3 ];\r\ + \n :global CharacterMultiply;\r\ + \n :return ([ \$CharacterMultiply \" \" (\$Level * 2) ] . \"-\" . \$Pre\ + fix . \"-> \" . \$Value);\r\ + \n }\r\ + \n :local TypeOf [ :typeof \$Input ];\r\ + \n :local Len [ :len \$Input ];\r\ + \n :local Return [ \$IndentReturn \"type\" \$TypeOf \$Level ];\r\ + \n :if (\$TypeOf = \"array\") do={\r\ + \n :foreach Key,Value in=\$Input do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"key\" \$Key (\$Level + 1) ] . \"\\n\" . \\\r\ + \n [ \$InspectVarRet \$Value (\$Level + 2) ]);\r\ + \n }\r\ + \n } else={\r\ + \n :if (\$TypeOf = \"str\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"len\" \$Len \$Level ]);\r\ + \n :if ([ :typeof [ :find \$Input (\"\\r\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\r\") \"\" ];\r\ + \n }\r\ + \n :if ([ :typeof [ :find \$Input (\"\\n\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\n\") \" \" ];\r\ + \n }\r\ + \n }\r\ + \n :if (\$TypeOf != \"nothing\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"value\" [ \$IfThenElse ([ :len \$Input ] > 80)\ + \_\\\r\ + \n ([ :pick \$Input 0 77 ] . \"...\") \$Input ] \$Level ]);\r\ + \n }\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set GetPubIp do={\r\ + \n :global DetectIpAddrDefault;\r\ + \n :global DetectIpAddrFallback;\r\ + \n\r\ + \n :local PubIpAddr false;\r\ + \n :local Url \$DetectIpAddrDefault;\r\ + \n :local JobName [ :jobname ];\r\ + \n\r\ + \n :do {\r\ + \n :log debug (\$0 . \" Trying to detect public IP using default serv\ + ice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=50ms max=5 on-error={:log error (\$JobName . \" Failed to \ + detect public IP using default service: \" . \$Url . \".\")}\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed to detect public IP using default serv\ + ice: \" . \$Url . \".\");\r\ + \n :set Url \$DetectIpAddrFallBack;\r\ + \n :log debug (\$0 . \" Trying to detect public IP using fallback ser\ + vice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=200ms max=5 on-error={:log error (\$JobName . \" Failed to\ + \_detect public IP using fallback service: \" . \$Url . \".\")}\r\ + \n };\r\ + \n :local IpLen ([ :len \$PubIpAddr ] -1);\r\ + \n :if (\$PubIpAddr = false) do={:set PubIpAddr \"not-detected\"} else=\ + {:set PubIpAddr [:pick \$PubIpAddr 0 \$IpLen]}\r\ + \n :return \$PubIpAddr;\r\ + \n}\r\ + \n\r\ + \n:set FormatLine do={\r\ + \n :local Key [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Indent [ :tonum \$3 ];\r\ + \n :local Spaces;\r\ + \n :local Return \"\";\r\ + \n :global CharacterMultiply;\r\ + \n :global EitherOr;\r\ + \n :set Indent [ \$EitherOr \$Indent 16 ];\r\ + \n :local Spaces [ \$CharacterMultiply \" \" \$Indent ];\r\ + \n :if ([ :len \$Key ] > 0) do={ :set Return (\$Key . \":\")}\r\ + \n :if ([ :len \$Key ] > (\$Indent - 2)) do={:set Return (\$Return . \"\\\ + n\" . [ :pick \$Spaces 0 \$Indent ] . \$Value);\r\ + \n } else={:set Return (\$Return . [ :pick \$Spaces 0 (\$Indent - [ :len \ + \$Return ]) ] . \$Value)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ParseCustom do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Start [:find [ \$Str ] [:tostr \$2]];\r\ + \n :local End [ :tostr \$3 ];\r\ + \n :local Num [ :tonum \$4 ];\r\ + \n :local Return;\r\ + \n :if ([:len \$Num] = 0) do={:set Num \"0\"}\r\ + \n :local rest [:pick \$Str (\$Start+\$Num) [:len \$Str]]; \r\ + \n :if ([:len \$End ] = 0) do={:set Return [:pick \$Str \$Num \$Start]} e\ + lse={\r\ + \n :set End [:find \$rest \$End];\r\ + \n :set Return [:pick \$rest 0 \$End];\r\ + \n }\r\ + \n:return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set LogPrint do={\r\ + \n :local Severity [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Message [ :tostr \$3 ];\r\ + \n :global PrintDebug;\r\ + \n :global PrintDebugOverride;\r\ + \n :global EitherOr;\r\ + \n :local Debug [ \$EitherOr (\$PrintDebugOverride->\$Name) \$PrintDebug \ + ];\r\ + \n :local PrintSeverity do={\r\ + \n :global TerminalColorOutput;\r\ + \n :if (\$TerminalColorOutput != true) do={:return \$1}\r\ + \n :local Color { debug=96; info=97; warning=93; error=91 };\r\ + \n :return (\"\\1B[\" . \$Color->\$1 . \"m\" . \$1 . \"\\1B[0m\");\r\ + \n}\r\ + \n :local Log ([ \$EitherOr \$Name \"\" ] . \": \" . \$Message);\ + \r\ + \n :if (\$Severity ~ (\"^(custom|debug|error|info)\\\$\")) do={\r\ + \n :if (\$Severity = \"debug\") do={:log debug \$Log}\r\ + \n :if (\$Severity = \"error\") do={:log error \$Log}\r\ + \n :if (\$Severity = \"info\" ) do={:log info \$Log}\r\ + \n } else={:log warning \$Log; :set Severity \"warning\"}\r\ + \n :if (\$Severity != \"debug\" || \$Debug = true) do={:put ([ \$PrintSev\ + erity \$Severity ] . \": \" . \$Message)}\r\ + \n}\r\ + \n\r\ + \n:set CertificateAvailable do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :local UseFor [ :tostr \$2 ];\r\ + \n\r\ + \n :global CertificateDownload;\r\ + \n :global ParseKeyValueStore;\r\ + \n\r\ + \n :if ([ :len \$UseFor ] = 0) do={\r\ + \n :log warning (\$0 . \"The intended use is undefined!\");\r\ + \n :set UseFor \"undefined\";\r\ + \n }\r\ + \n :if ([ /system/resource/get free-hdd-space ] < 8388608 && \\\r\ + \n [ /certificate/settings/get crl-download ] = true && \\\r\ + \n [ /certificate/settings/get crl-store ] = \"system\") do={\r\ + \n :log warning (\$0 . \"This system has low free flash space but \" . \ + \\\r\ + \n \"is configured to download certificate CRLs to system!\");\r\ + \n }\r\ + \n :if ([ :len \$CommonName ] = 0) do={\r\ + \n :log warning (\$0 . \"No CommonName given!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if (((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" || \\\ + \r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \$UseFor || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\r\ + \n [ :len [ /certificate/builtin/find where common-name=\$CommonName\ + \_] ] > 0) do={\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n :log info (\$0 . \"Certificate with CommonName '\" . \$CommonName . \ + \"' not available.\");\r\ + \n :if ([ \$CertificateDownload \$CommonName ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] > 1\ + ) do={\r\ + \n :log info (\$0 . \"There are \" . \$CertCount . \" Certificates with\ + \_CommonName '\" . \$CommonName . \"'. Should be ok.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :local CertVal [ /certificate/get [ find where common-name=\$CommonNam\ + e ] ];\r\ + \n :while ((\$CertVal->\"akid\") != \"\" && (\$CertVal->\"akid\") != (\$C\ + ertVal->\"skid\")) do={\r\ + \n :if ([ :len [ /certificate/find where skid=(\$CertVal->\"akid\") ] ]\ + \_= 0) do={\r\ + \n :local IssuerCN ([ \$ParseKeyValueStore (\$CertVal->\"issuer\") ]-\ + >\"CN\");\r\ + \n :log info (\$0 . \"Certificate chain for '\" . \$CommonName . \"' \ + is incomplete, missing '\" . \$IssuerCN . \"'.\");\r\ + \n :if ([ \$CertificateDownload \$IssuerCN ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :set CertVal [ /certificate/get [ find where skid=(\$CertVal->\"akid\ + \") ] ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateDownload do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :global ScriptUpdatesBaseUrl;\r\ + \n :global ScriptUpdatesUrlSuffix;\r\ + \n :global CertificateNameByCN;\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :log info (\$0 . \"Downloading and importing certificate with \" . \\\ + \r\ + \n \"CommonName '\" . \$CommonName . \"'.\");\r\ + \n :local FileName ([ \$CleanName \$CommonName ] . \".pem\");\r\ + \n :do {\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-header\ + -field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n (\$ScriptUpdatesBaseUrl . \"certs/\" . \$FileName . \$ScriptUpdate\ + sUrlSuffix) \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n } on-error={\r\ + \n :log warning (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \\\r\ + \n \"' from repository! Trying fallback to mkcert.org...\");\r\ + \n :do {\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if ([ :len [ /certificate/find where common-name=\"ISRG Root X1\"\ + \_] ] = 0 && \\\r\ + \n !(((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" \ + || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \"fetch\" || \ + \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\ + \r\ + \n [ :len [ /certificate/builtin/find where common-name=\"ISRG\ + \_Root X1\" ] ] > 0)) do={\r\ + \n :log error (\$0 . \" Required certificate is not available.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-head\ + er-field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n url=\"https://mkcert.org/generate/\" http-data=[ :serialize to=jso\ + n ({ \$CommonName }) ] \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n :if ([ /file/get \$FileName size ] = 0) do={\r\ + \n \$RmFile \$FileName;\r\ + \n :error false;\r\ + \n }\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \"'!\");\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n /certificate/import file-name=\$FileName passphrase=\"\" as-value;\r\ + \n :delay 1s;\r\ + \n \$RmFile \$FileName;\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n /certificate/remove [ find where name~(\"^\" . \$FileName . \"_[0-9]\ + +\\\$\") ];\r\ + \n :log warning (\$0 . \" Certificate with CommonName '\" . \$CommonNam\ + e . \"' still unavailable!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :foreach Cert in=[ /certificate/find where name~(\"^\" . \$FileName . \ + \"_[0-9]+\\\$\") ] do={\r\ + \n \$CertificateNameByCN [ /certificate/get \$Cert common-name ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateNameByCN do={\r\ + \n :local Match [ :tostr \$1 ];\r\ + \n :global CleanName;\r\ + \n :local Cert ([ /certificate/find where (common-name=\$Match or fingerp\ + rint=\$Match or name=\$Match) ]->0);\r\ + \n :if ([ :len \$Cert ] = 0) do={\r\ + \n :log warning (\$0 .\" No matching certificate found.\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CommonName [ /certificate/get \$Cert common-name ];\r\ + \n /certificate/set \$Cert name=[ \$CleanName \$CommonName ];\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FileExists do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local Type [ :tostr \$2 ];\r\ + \n :global FileGet;\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={ :return false; }\r\ + \n :if ([ :len (\$FileVal->\"size\") ] = 0) do={ :return false; }\r\ + \n :if ([ :len \$Type ] = 0 || \$FileVal->\"type\" = \$Type) do={ :return\ + \_true; }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set Grep do={\r\ + \n :local Input ([ :tostr \$1 ] . \"\\n\");\r\ + \n :local Pattern [ :tostr \$2 ];\r\ + \n :if ([ :typeof [ :find \$Input \$Pattern ] ] = \"nil\") do={:return []\ + }\r\ + \n :do {\r\ + \n :local Line [ :pick \$Input 0 [ :find \$Input \"\\n\" ] ];\r\ + \n :if ([ :typeof [ :find \$Line \$Pattern ] ] = \"num\") do={:return \ + \$Line}\r\ + \n :set Input [ :pick \$Input ([ :find \$Input \"\\n\" ] + 1) [ :len \$\ + Input ] ];\r\ + \n } while=([ :len \$Input ] > 0);\r\ + \n :return [];\r\ + \n}\r\ + \n\r\ + \n:set HumanReadableNum do={\r\ + \n :local Input [ :tonum \$1 ];\r\ + \n :local Base [ :tonum \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global IfThenElse;\r\ + \n :local Prefix \"kMGTPE\";\r\ + \n :local Pow 1;\r\ + \n :set Base [ \$EitherOr \$Base 1024 ];\r\ + \n :local Bin [ \$IfThenElse (\$Base = 1024) \"i\" \"\" ];\r\ + \n :if (\$Input < \$Base) do={:return \$Input}\r\ + \n :for I from=0 to=[ :len \$Prefix ] do={\r\ + \n :set Pow (\$Pow * \$Base);\r\ + \n :if (\$Input / \$Base < \$Pow) do={\r\ + \n :set Prefix [ :pick \$Prefix \$I ];\r\ + \n :local Tmp1 (\$Input * 100 / \$Pow);\r\ + \n :local Tmp2 (\$Tmp1 / 100);\r\ + \n :if (\$Tmp2 >= 100) do={:return (\$Tmp2 . \$Prefix . \$Bin)}\r\ + \n :return (\$Tmp2 . \".\" . \\\r\ + \n [ :pick \$Tmp1 [ :len \$Tmp2 ] ([ :len \$Tmp1 ] - [ :len \$Tmp\ + 2 ] + 1) ] . \\\r\ + \n \$Prefix . \$Bin);\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set CleanFilePath do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CharacterReplace;\r\ + \n :while (\$Path ~ \"//\") do={ :set \$Path [ \$CharacterReplace \$Path \ + \"//\" \"/\" ]; }\r\ + \n :if ([ :pick \$Path 0 ] = \"/\") do={ :set Path [ :pick \$Path 1 [ :le\ + n \$Path ] ]; }\r\ + \n :if ([ :pick \$Path ([ :len \$Path ] - 1) ] = \"/\") do={ :set Path [ \ + :pick \$Path 0 ([ :len \$Path ] - 1) ]; }\r\ + \n :return \$Path;\r\ + \n}\r\ + \n\r\ + \n:set CleanName do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Return \"\";\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :typeof [ find \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr\ + stuvwxyz0123456789\" \$Char ] ] = \"nil\") do={\r\ + \n :do {:if ([ :len \$Return ] = 0) do={:error true};\r\ + \n :if ([ :pick \$Return ([ :len \$Return ] - 1) ] = \"-\") do={:er\ + ror true}; :set Char \"-\"} on-error={:set Char \"\"}; \r\ + \n };\r\ + \n :set Return (\$Return . \$Char)};\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set RandomDelay do={\r\ + \n :local Time [ :tonum \$1 ];\r\ + \n :local Unit [ :tostr \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global GetRandomNumber;\r\ + \n :global MAX;\r\ + \n :if (\$Time = 0) do={:return false}\r\ + \n :delay ([ \$MAX 10 [ \$GetRandomNumber ([ :tonsec [ :totime (\$Time . \ + [ \$EitherOr \$Unit \"s\" ]) ] ] / 1000000) ] ] . \"ms\");\r\ + \n}\r\ + \n\r\ + \n:set EscapeForRegEx do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"^.[]\\\$()|*+\?{}\\\\\");\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :find \$Chars \$Char ]) do={:set Char (\"\\\\\" . \$Char)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ExitError do={\r\ + \n :local ExitOK [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Error [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :if (\$ExitOK = \"false\") do={\r\ + \n :log error (\$Name [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\\\$\") \ + \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with er\ + ror\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \ + \$Error) \".\" ]);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set ExitOnError do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :local Error [ :tostr \$2 ];\r\ + \n :global IfThenElse;\r\ + \n :log error (\$Name . \" \" . [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\ + \\\$\") \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with erro\ + r\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \$E\ + rror) \".\" ]);\r\ + \n}\r\ + \n\r\ + \n:set MiniDateTimeStamp do={\r\ + \n :local mikro [ :tobool \$1 ];\r\ + \n /system/clock;\r\ + \n :local vdate [get date];\r\ + \n :local vtime [get time];\r\ + \n :local vdoff [:toarray \"0,4,5,7,8,10\"];\r\ + \n :local MM [:pick \$vdate (\$vdoff->2) (\$vdoff->3)];\r\ + \n :local M [:tonum \$MM];\r\ + \n :if (\$vdate ~ \".../../....\") do={\r\ + \n :set vdoff [:toarray \"7,11,1,3,4,6\"];\r\ + \n :set M ([:find \"xxanebarprayunulugepctovecANEBARPRAYUNULUGE\ + PCTOVEC\" [:pick \$vdate (\$vdoff->2) (\$vdoff->3)] -1] / 2);\r\ + \n :if (\$M>12) do={:set M (\$M - 12)}\r\ + \n :set MM [:pick (100 + \$M) 1 3];\r\ + \n }\r\ + \n :local yyyy [:pick \$vdate (\$vdoff->0) (\$vdoff->1)];\r\ + \n :local dd [:pick \$vdate (\$vdoff->4) (\$vdoff->5)];\r\ + \n :local HH [:pick \$vtime 0 2];\r\ + \n :local mm [:pick \$vtime 3 5];\r\ + \n :local ss [:pick \$vtime 6 8];\r\ + \n :local Return;\r\ + \n :if (\$mikro = true) do={\r\ + \n :set Return ([:pick \$yyyy 2 4] . [:pick \$dd 0 1] . [:pick \$HH 0\ + \_1] . [:pick \$mm 3 4] . [:pick \$ss 6 7]);\r\ + \n } else={\r\ + \n :set Return ([\$yyyy] . [\$MM] . [\$dd] . [\$HH] . [\$mm] . [\$ss]);\ + \r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set WaitForFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local WaitTime [ :totime \$2 ];\r\ + \n :global CleanFilePath;\r\ + \n :global EitherOr;\r\ + \n :global MAX;\r\ + \n :set FileName [ \$CleanFilePath \$FileName ];\r\ + \n :local Delay ([ \$MAX [ \$EitherOr \$WaitTime 2s ] 100ms ] / 9);\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } delay=\$Delay max=10;\r\ + \n } on-error={ }\r\ + \n :while ([ :len [ /file/find where name=\$FileName ] ] > 0) do={\r\ + \n :do {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } on-error={ }\r\ + \n :delay \$Delay;\r\ + \n :set Delay (\$Delay * 3 / 2);\r\ + \n }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set FileGet do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global WaitForFile;\r\ + \n :if ([ \$WaitForFile \$FileName 0s ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :local FileVal false;\r\ + \n :do {\r\ + \n :set FileVal [ /file/get \$FileName ];\r\ + \n } on-error={ }\r\ + \n :return \$FileVal;\r\ + \n}\r\ + \n\r\ + \n:set RmDir do={\r\ + \n :local DirName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing directory: \". \$DirName);\r\ + \n :local DirVal [ \$FileGet \$DirName ];\r\ + \n :if (\$DirVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n };\r\ + \n :if (\$DirVal->\"type\" != \"directory\") do={\r\ + \n :log error (\$0 . \" Directory '\" . \$DirName . \"' is not a direct\ + ory.\");\r\ + \n :return false;\r\ + \n };\r\ + \n :onerror Err {\r\ + \n /file/remove \$DirName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing directory '\" . \$DirName . \"' failed\ + : \" . \$Err);\r\ + \n :return false;\r\ + \n };\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set RmFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing file: \". \$FileName);\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if (\$FileVal->\"type\" = \"directory\" || \$FileVal->\"type\" = \"di\ + sk\") do={\r\ + \n :log error (\$0 . \" File '\" . \$FileName . \"' is not a file.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/remove \$FileName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing file '\" . \$FileName . \"' failed: \"\ + \_. \$Err); :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MkDir do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CleanFilePath;\r\ + \n :global FileGet;\r\ + \n :global RmDir;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :local MkTmpfs do={\r\ + \n :global WaitForFile;\r\ + \n :local TmpFs [ /disk/find where slot=tmpfs type=tmpfs ];\r\ + \n :if ([ :len \$TmpFs ] = 1) do={\r\ + \n :if ([ /disk/get \$TmpFs disabled ] = true) do={\r\ + \n :log info (\$0 . \" The tmpfs is disabled, enabling.\");\r\ + \n /disk/enable \$TmpFs;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :log info (\$0 . \" Creating disk of type tmpfs.\");\r\ + \n \$RmDir \"tmpfs\";\r\ + \n :onerror Err {\r\ + \n /disk/add slot=tmpfs type=tmpfs tmpfs-max-size=([ /system/resource\ + /get total-memory ] / 3);\r\ + \n \$WaitForFile \"tmpfs\";\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Creating disk of type tmpfs failed: \" . \$\ + Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :set Path [ \$CleanFilePath \$Path ];\r\ + \n :if (\$Path = \"\") do={\r\ + \n :return true;\r\ + \n }\r\ + \n :log debug (\$0 . \" Making directory: \" . \$Path);\r\ + \n :local PathVal [ \$FileGet \$Path ];\r\ + \n :if (\$PathVal->\"type\" = \"directory\") do={\r\ + \n :log debug (\$0 . \" ... which already exists.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :pick \$Path 0 5 ] = \"tmpfs\") do={\r\ + \n :if ([ \$MkTmpfs ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/add type=\"directory\" name=\$Path;\r\ + \n \$WaitForFile \$Path;\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Making directory '\" . \$Path . \"' failed: \ + \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MAX do={:if (\$1 > \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n:set MIN do={:if (\$1 < \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set CharacterReplace do={\r\ + \n :local String [ :tostr \$1 ];\r\ + \n :local ReplaceFrom [ :tostr \$2 ];\r\ + \n :local ReplaceWith [ :tostr \$3 ];\r\ + \n :local Return \"\";\r\ + \n :if (\$ReplaceFrom = \"\") do={:return \$String}\r\ + \n :while ([ :typeof [ :find \$String \$ReplaceFrom ] ] != \"nil\") do={\ + \r\ + \n :local Pos [ :find \$String \$ReplaceFrom ];\r\ + \n :set Return (\$Return . [ :pick \$String 0 \$Pos ] . \$ReplaceWith);\ + \r\ + \n :set String [ :pick \$String (\$Pos + [ :len \$ReplaceFrom ]) [ :len\ + \_\$String ] ];\r\ + \n }\r\ + \n :return (\$Return . \$String);\r\ + \n}\r\ + \n\r\ + \n:set IfThenElse do={\r\ + \n :if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1 ] = true) do={:return \ + \$2};\r\ + \n :return \$3;\r\ + \n}\r\ + \n\r\ + \n:set EitherOr do={\r\ + \n :global IfThenElse;\r\ + \n :if ([ :typeof \$1 ] = \"num\") do={:return [ \$IfThenElse (\$1 != 0) \ + \$1 \$2 ]}\r\ + \n :if ([ :typeof \$1 ] = \"time\") do={:return [ \$IfThenElse (\$1 > 0s)\ + \_\$1 \$2 ]}\r\ + \n# this works for boolean values, literal ones with parentheses\r\ + \n :return [ \$IfThenElse ([ :len [ :tostr \$1 ] ] > 0) \$1 \$2 ];\r\ + \n}\r\ + \n\r\ + \n:set ParseKeyValueStore do={\r\ + \n :local Source \$1;\r\ + \n :if ([ :pick \$Source 0 1 ] = \"{\") do={\r\ + \n :do {\r\ + \n :return [ :deserialize from=json \$Source ];\r\ + \n } on-error={ }\r\ + \n } \r\ + \n :if ([ :typeof \$Source ] != \"array\") do={:set Source [ :tostr \$1 ]\ + }\r\ + \n :local Result ({});\r\ + \n :foreach KeyValue in=[ :toarray \$Source ] do={\r\ + \n :if ([ :find \$KeyValue \"=\" ]) do={\r\ + \n :local Key [ :pick \$KeyValue 0 [ :find \$KeyValue \"=\" ] ];\r\ + \n :local Value [ :pick \$KeyValue ([ :find \$KeyValue \"=\" ] + 1) [\ + \_:len \$KeyValue ] ];\r\ + \n :if (\$Value=\"true\") do={:set Value true}\r\ + \n :if (\$Value=\"false\") do={:set Value false}\r\ + \n :set (\$Result->\$Key) \$Value;\r\ + \n } else={:set (\$Result->\$KeyValue) true}\r\ + \n }\r\ + \n :return \$Result;\r\ + \n}\r\ + \n:set RequiredRouterOS do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :local Required [ :tostr \$2 ];\r\ + \n :local Warn [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :global VersionToNum;\r\ + \n :if (!(\$Required ~ \"^\\\\d+\\\\.\\\\d+((alpha|beta|rc|\\\\.)\\\\d+|)\ + \\\$\")) do={\r\ + \n :log error (\$0 . \" No valid RouterOS version: \" . \$Required);\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ \$VersionToNum \$Required ] > [ \$VersionToNum [ /system/packag\ + e/update/get installed-version ] ]) do={\r\ + \n :if (\$Warn = \"true\") do={\r\ + \n :log warning (\$0 . \" This \" . [ \$IfThenElse ([ :pick \$Caller \ + 0 ] = (\"\\\$\")) \"function\" \"script\" ] . \\\r\ + \n \" '\" . \$Caller . \"' (at least specific functionality) requir\ + es RouterOS \" . \$Required . \". Please update!\");\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set ValidateSyntax do={\r\ + \n :local Code [ :tostr \$1 ];\r\ + \n :global LogPrint;\r\ + \n :onerror Err {\r\ + \n [ :parse (\":local Validate do={\\n\" . \$Code . \"\\n}\") ];\r\ + \n } do={\r\ + \n \$LogPrint debug \$0 (\"Valdation failed: \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set VersionToNum do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Multi 0x1000000;\r\ + \n :local Return 0;\r\ + \n :global CharacterReplace;\r\ + \n :set Input [ \$CharacterReplace \$Input \".\" \",\" ];\r\ + \n :foreach I in={ \"zero\"; \"alpha\"; \"beta\"; \"rc\" } do={\r\ + \n :set Input [ \$CharacterReplace \$Input \$I (\",\" . \$I . \",\") ];\ + \r\ + \n }\r\ + \n :foreach Value in=([ :toarray \$Input ], 0) do={\r\ + \n :local Num [ :tonum \$Value ];\r\ + \n :if (\$Multi = 0x100) do={\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={\r\ + \n :set Return (\$Return + 0xff00);\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n } else={\r\ + \n :if (\$Value = \"zero\") do={ }\r\ + \n :if (\$Value = \"alpha\") do={ :set Return (\$Return + 0x3f00); \ + }\r\ + \n :if (\$Value = \"beta\") do={ :set Return (\$Return + 0x5f00); }\ + \r\ + \n :if (\$Value = \"rc\") do={ :set Return (\$Return + 0x7f00); }\r\ + \n }\r\ + \n }\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={ :set Return (\$Return + (\$Va\ + lue * \$Multi)); }\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ScriptLock do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local WaitMax [ :totime \$2 ];\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global ScriptLockOrder;\r\ + \n :if ([ :typeof \$ScriptLockOrder ] = \"nothing\") do={\r\ + \n :set ScriptLockOrder ({});\r\ + \n }\r\ + \n :if ([ :typeof (\$ScriptLockOrder->\$Script) ] = \"nothing\") do={\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :local JobCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :return [ :len [ /system/script/job/find where script=\$Script ] ];\ + \r\ + \n }\r\ + \n :local TicketCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :local Count 0;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:set Count (\$Count \ + + 1)}\r\ + \n }\r\ + \n :return \$Count;\r\ + \n }\r\ + \n :local IsFirstTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Check [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if (\$Ticket = \$Check) do={:return true}\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\" && \$Ticket != \$Check) d\ + o={:return false}\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :local AddTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Add [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :while (true) do={\r\ + \n :local Pos [ :len (\$ScriptLockOrder->\$Script) ];\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Pos) \$Add;\r\ + \n :delay 10ms;\r\ + \n :if ((\$ScriptLockOrder->\$Script->\$Pos) = \$Add) do={:return tru\ + e}\r\ + \n }\r\ + \n }\r\ + \n :local RemoveTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Remove [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Id,Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :while ((\$ScriptLockOrder->\$Script->\$Id) = \$Remove) do={\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Id);\r\ + \n :delay 10ms;\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n :local CleanupTickets do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:return false}\r\ + \n }\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :if ([ :typeof \$WaitMax ] = \"nil\" ) do={:set WaitMax 0s}\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\r\ + \n :log error (\$0 . \" A script named '\" . \$Script . \"' does not ex\ + ist!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$JobCount \$Script ] = 0) do={\r\ + \n \$LogPrint error \$0 (\"No script '\" . \$Script . \"' is running!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$TicketCount \$Script ] >= [ \$JobCount \$Script ]) do={\r\ + \n :log error (\$0 . \" More tickets than running scripts '\" . \$Scrip\ + t . \"', resetting!\");\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n /system/script/job/remove [ find where script=\$Script ];\r\ + \n }\r\ + \n :local MyTicket [ \$GetRandom20CharAlNum 6 ];\r\ + \n \$AddTicket \$Script \$MyTicket;\r\ + \n :local WaitInterval (\$WaitMax / 20);\r\ + \n :local WaitTime \$WaitMax;\r\ + \n :while (\$WaitTime > 0 && \\\r\ + \n ([ \$IsFirstTicket \$Script \$MyTicket ] = false || \\\r\ + \n [ \$TicketCount \$Script ] < [ \$JobCount \$Script ])) do={\r\ + \n :set WaitTime (\$WaitTime - \$WaitInterval);\r\ + \n :delay \$WaitInterval;\r\ + \n }\r\ + \n :if ([ \$IsFirstTicket \$Script \$MyTicket ] = true && \\\r\ + \n [ \$TicketCount \$Script ] = [ \$JobCount \$Script ]) do={\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$CleanupTickets \$Script;\r\ + \n :return true;\r\ + \n }\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$LogPrint debug \$0 (\"Script '\" . \$Script . \"' started more than \ + once\" . \\\r\ + \n [ \$IfThenElse (\$WaitTime < \$WaitMax) \" and timed out waiting for\ + \_lock\" \"\" ] . \"...\");\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set SendNotification do={ :onerror Err {\r\ + \n :global SendNotification2;\r\ + \n \$SendNotification2 ({ origin=\$0; subject=\$1; message=\$2; link=\$3;\ + \_silent=\$4 });\r\ + \n} do={:global ExitOnError; \$ExitOnError \$0 \$Err}\r\ + \n}\r\ + \n\r\ + \n:set SendNotification2 do={\r\ + \n :local Notification \$1;\r\ + \n :global NotificationFunctions;\r\ + \n :foreach FunctionName,Discard in=\$NotificationFunctions do={\r\ + \n (\$NotificationFunctions->\$FunctionName) (\"\\\$NotificationFunctio\ + ns->\\\"\" . \$FunctionName . \"\\\"\") \$Notification;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set SymbolByUnicodeName do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :global EitherOr;\r\ + \n :global SymbolsExtra;\r\ + \n :local Symbols ({\r\ + \n \"abacus\"=\"\\F0\\9F\\A7\\AE\";\r\ + \n \"alarm-clock\"=\"\\E2\\8F\\B0\";\r\ + \n \"arrow-down\"=\"\\E2\\AC\\87\";\r\ + \n \"arrow-up\"=\"\\E2\\AC\\86\";\r\ + \n \"calendar\"=\"\\F0\\9F\\93\\85\";\r\ + \n \"card-file-box\"=\"\\F0\\9F\\97\\83\";\r\ + \n \"chart-decreasing\"=\"\\F0\\9F\\93\\89\";\r\ + \n \"chart-increasing\"=\"\\F0\\9F\\93\\88\";\r\ + \n \"cloud\"=\"\\E2\\98\\81\";\r\ + \n \"cross-mark\"=\"\\E2\\9D\\8C\";\r\ + \n \"earth\"=\"\\F0\\9F\\8C\\8D\";\r\ + \n \"fire\"=\"\\F0\\9F\\94\\A5\";\r\ + \n \"floppy-disk\"=\"\\F0\\9F\\92\\BE\";\r\ + \n \"gear\"=\"\\E2\\9A\\99\";\r\ + \n \"heart\"=\"\\E2\\99\\A5\";\r\ + \n \"high-voltage-sign\"=\"\\E2\\9A\\A1\";\r\ + \n \"incoming-envelope\"=\"\\F0\\9F\\93\\A8\";\r\ + \n \"information\"=\"\\E2\\84\\B9\";\r\ + \n \"large-orange-circle\"=\"\\F0\\9F\\9F\\A0\";\r\ + \n \"large-red-circle\"=\"\\F0\\9F\\94\\B4\";\r\ + \n \"link\"=\"\\F0\\9F\\94\\97\";\r\ + \n \"lock-with-ink-pen\"=\"\\F0\\9F\\94\\8F\";\r\ + \n \"memo\"=\"\\F0\\9F\\93\\9D\";\r\ + \n \"mobile-phone\"=\"\\F0\\9F\\93\\B1\";\r\ + \n \"pushpin\"=\"\\F0\\9F\\93\\8C\";\r\ + \n \"scissors\"=\"\\E2\\9C\\82\";\r\ + \n \"scroll\"=\"\\F0\\9F\\93\\9C\";\r\ + \n \"smiley-partying-face\"=\"\\F0\\9F\\A5\\B3\";\r\ + \n \"smiley-smiling-face\"=\"\\E2\\98\\BA\";\r\ + \n \"smiley-winking-face-with-tongue\"=\"\\F0\\9F\\98\\9C\";\r\ + \n \"sparkles\"=\"\\E2\\9C\\A8\";\r\ + \n \"speech-balloon\"=\"\\F0\\9F\\92\\AC\";\r\ + \n \"star\"=\"\\E2\\AD\\90\";\r\ + \n \"warning-sign\"=\"\\E2\\9A\\A0\";\r\ + \n \"white-heavy-check-mark\"=\"\\E2\\9C\\85\"\r\ + \n }, \$SymbolsExtra);\r\ + \n :local Magic [ :pick [ /system/clock/get date ] 4 10 ];\r\ + \n :local Special {\r\ + \n \"information-04-01\"=\"\\F0\\9F\\9A\\BB\";\r\ + \n \"large-orange-circle-04-01\"=\"\\F0\\9F\\8D\\8A\";\r\ + \n \"large-orange-circle-10-31\"=\"\\F0\\9F\\8E\\83\";\r\ + \n \"large-red-circle-04-01\"=\"\\F0\\9F\\8D\\92\" };\r\ + \n :if ([ :len (\$Symbols->\$Name) ] = 0) do={\r\ + \n :log warning (\$0 . \"No symbol available for name '\" . \$Name . \"\ + '!\");\r\ + \n :return \"\";\r\ + \n }\r\ + \n :return ([ \$EitherOr (\$Special->(\$Name . \$Magic)) (\$Symbols->\$Na\ + me) ] . \"\\EF\\B8\\8F\");\r\ + \n}\r\ + \n\r\ + \n:set SymbolForNotification do={\r\ + \n :global NotificationsWithSymbols;\r\ + \n :global SymbolByUnicodeName;\r\ + \n :global IfThenElse;\r\ + \n :if (\$NotificationsWithSymbols != true) do={:return [ \$IfThenElse ([\ + \_:len \$2 ] > 0) ([ :tostr \$2 ] . \" \") \"\" ]}\r\ + \n :local Return \"\";\r\ + \n :foreach Symbol in=[ :toarray \$1 ] do={:set Return (\$Return . [ \$Sy\ + mbolByUnicodeName \$Symbol ])}\r\ + \n :return (\$Return . \" \");\r\ + \n}\r\ + \n\r\ + \n:set Unix2Dos do={:return [ :tocrlf [ :tostr \$1 ] ]}\r\ + \n:set UrlEncode do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"\\n\\r !\\\"#\\\$%&'()*+,:;<=>\?@[\\\\]^`{|}~\");\r\ + \n :local Subs { \"%0A\"; \"%0D\"; \"%20\"; \"%21\"; \"%22\"; \"%23\"; \"\ + %24\"; \"%25\"; \"%26\"; \"%27\";\r\ + \n \"%28\"; \"%29\"; \"%2A\"; \"%2B\"; \"%2C\"; \"%3A\"; \"%3B\"; \ + \"%3C\"; \"%3D\"; \"%3E\"; \"%3F\";\r\ + \n \"%40\"; \"%5B\"; \"%5C\"; \"%5D\"; \"%5E\"; \"%60\"; \"%7B\"; \ + \"%7C\"; \"%7D\"; \"%7E\" };\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :local Replace [ :find \$Chars \$Char ];\r\ + \n :if ([ :typeof \$Replace ] = \"num\") do={:set Char (\$Subs->\$Repla\ + ce)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set IsDNSResolving do={\r\ + \n :do {:resolve \"api.telegram.org\"} on-error={:return false}\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsFullyConnected do={\r\ + \n :global IsDNSResolving;\r\ + \n :global IsTimeSync;\r\ + \n :if ([ \$IsDNSResolving ] = false) do={:return false};\r\ + \n :if ([ \$IsTimeSync ] = false) do={:return false};\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsTimeSync do={\r\ + \n :global IsTimeSyncCached;\r\ + \n :global IsTimeSyncResetNtp;\r\ + \n :if (\$IsTimeSyncCached = true) do={:return true}\r\ + \n :if ([ /system/ntp/client/get enabled ] = true) do={\r\ + \n :if ([ /system/ntp/client/get status ] = \"synchronized\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :local Uptime [ /system/resource/get uptime ];\r\ + \n :if ([ :typeof \$IsTimeSyncResetNtp ] = \"nothing\") do={\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n }\r\ + \n :if (\$Uptime - \$IsTimeSyncResetNtp < 3m) do={:return false}\r\ + \n :log warning \"\$0 The ntp client is configured, but did not sync.\"\ + ;\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n /system/ntp/client/set enabled=no;\r\ + \n :delay 20ms;\r\ + \n /system/ntp/client/set enabled=yes;\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ /system/license/get ]->\"level\" = \"free\" || \\\r\ + \n [ /system/resource/get ]->\"board-name\" = \"x86\") do={\r\ + \n :log debug \"\$0 No ntp client configured, relying on RTC for CHR fr\ + ee license and x86.\";\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ /ip/cloud/get update-time ] = true) do={\r\ + \n :if ([ :typeof [ /ip/cloud/get public-address ] ] = \"ip\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :log debug \"\$0 No time source configured! Returning gracefully...\";\ + \r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set FetchHugeFiles do={\r\ + \n :local ScriptName [ :tostr \$1 ];\r\ + \n :local Url [ :tostr \$2 ];\r\ + \n :local CheckCert [ :tostr \$3 ];\r\ + \n\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global MkDir;\r\ + \n :global RmDir;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :set CheckCert [ \$IfThenElse (\$CheckCert = \"false\") \"no\" \"yes-w\ + ithout-crl\" ];\r\ + \n\r\ + \n :local DirName (\"tmpfs/\" . [ \$CleanName \$ScriptName ]);\r\ + \n :if ([ \$MkDir \$DirName ] = false) do={\r\ + \n \$LogPrint error \$0 (\"Failed creating directory!\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileName (\$DirName . \"/\" . [ \$CleanName \$0 ] . \"-\" . [ \ + \$GetRandom20CharAlNum ]);\r\ + \n :onerror Err {\r\ + \n /tool/fetch check-certificate=\$CheckCert \$Url dst-path=\$FileName \ + \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$ScriptName ] }) as-va\ + lue;\r\ + \n } do={\r\ + \n :if ([ \$WaitForFile \$FileName 500ms ] = true) do={\r\ + \n \$RmFile \$FileName;\r\ + \n }\r\ + \n \$LogPrint debug \$0 (\"Failed downloading from \" . \$Url . \" - \"\ + \_. \$Err);\r\ + \n \$RmDir \$DirName;\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$WaitForFile \$FileName 5s ] = false) do={\r\ + \n \$LogPrint debug \$0 (\"The file downloaded from \" . \$Url . \" did\ + \_not show up.\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileSize [ /file/get \$FileName size ];\r\ + \n :local Return \"\";\r\ + \n :local VarSize 0;\r\ + \n :while (\$VarSize != \$FileSize) do={\r\ + \n :set Return (\$Return . ([ /file/read offset=\$VarSize chunk-size=32\ + 768 file=\$FileName as-value ]->\"data\"));\r\ + \n :set FileSize [ /file/get \$FileName size ];\r\ + \n :set VarSize [ :len \$Return ];\r\ + \n :if (\$VarSize > \$FileSize) do={\r\ + \n :delay 100ms;\r\ + \n }\r\ + \n }\r\ + \n \$RmDir \$DirName;\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FetchUserAgentStr do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global CommitBrief;\r\ + \n :global IfThenElse;\r\ + \n :local Resource [ /system/resource/get ];\r\ + \n :return (\"User-Agent: Mikrotik/\" . \$Resource->\"version\" . \" \" .\ + \_\$Resource->\"architecture-name\" . \\\r\ + \n \" \" . \$Caller . \"/Fetch (\" . \$GlobalScriptsUrl . \"; \" . [ \$\ + CommitBrief ] . \")\");\r\ + \n}\r\ + \n\r\ + \n:set ScriptInstallUpdate do={ :onerror Err {\r\ + \n :local Scripts [ :toarray \$1 ];\r\ + \n :local NewComment [ :tostr \$2 ];\r\ + \n :global InitSetup;\r\ + \n\r\ + \n :global CommitId;\r\ + \n :global ScriptUpdatesCRLF;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global CertificateAvailable;\r\ + \n :global CheckSumsVerify;\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global SumsDirAndFileName;\r\ + \n :global GlobalScriptsUrlSuffix;\r\ + \n :global EitherOr;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global Grep;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global ParseKeyValueStore;\r\ + \n :global RequiredRouterOS;\r\ + \n :global SendNotification2;\r\ + \n :global SymbolForNotification;\r\ + \n :global ValidateSyntax;\r\ + \n :global CommentsInScripts;\r\ + \n\r\ + \n :if ([ \$CertificateAvailable \"Root YE\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, trying wit\ + hout.\");\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=\$Scripts do={\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\ + \r\ + \n \$LogPrint info \$0 (\"Adding new script: \" . \$Script);\r\ + \n /system/script/add name=\$Script owner=\$Script source=\"#!rsc by \ + Vados\\n\" comment=\$NewComment;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :local CommitIdBefore \$CommitId;\r\ + \n :local GlobalConfVersionBefore \$GlobalConfVersion;\r\ + \n :local ReloadGlobal false;\r\ + \n :local DeviceMode [ /system/device-mode/get ];\r\ + \n\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSums ({});\r\ + \n :do {\r\ + \n :local Url (\$GlobalScriptsUrl . \$SumsDirAndFileName . \$GlobalScr\ + iptsUrlSuffix);\r\ + \n \$LogPrint debug \$0 (\"Fetching checksums from url: \" . \$Url);\ + \r\ + \n :set CheckSums [ :deserialize from=json ([ /tool/fetch check-certi\ + ficate=yes-without-crl \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url output\ + =user as-value ]->\"data\") ];\r\ + \n } on-error={ \$LogPrint warning \$0 (\"Err = \" . \$Err); }\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=[ /system/script/find where source~\"^#!rsc by Vado\ + s\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :local ScriptInfo [ \$ParseKeyValueStore (\$ScriptVal->\"comment\") \ + ];\r\ + \n :local SourceNew;\r\ + \n\r\ + \n :foreach Scheduler in=[ /system/scheduler/find where on-event~(\"\\\ + \\b\" . \$ScriptVal->\"name\" . \"\\\\b\") ] do={\r\ + \n :local SchedulerVal [ /system/scheduler/get \$Scheduler ];\r\ + \n :if (\$ScriptVal->\"policy\" != \$SchedulerVal->\"policy\") do={\r\ + \n \$LogPrint warning \$0 (\"Policies differ for script '\" . \$Scr\ + iptVal->\"name\" . \"' and its scheduler '\" . \$SchedulerVal->\"name\" . \ + \"'!\");\r\ + \n }\r\ + \n }\r\ + \n :do {\r\ + \n :if (\$ScriptInfo->\"ignore\" = true) do={\r\ + \n \$LogPrint debug \$0 (\"Ignoring script '\" . \$ScriptVal->\"nam\ + e\" . \"', as requested.\");\r\ + \n :error true;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSum (\$CheckSums->(\$ScriptVal->\"name\"));\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Scri\ + ptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :convert transform=md5 to=hex [ :tolf (\$ScriptVal->\"sou\ + rce\") ] ] = \$CheckSum) do={\r\ + \n \$LogPrint debug \$0 (\"Checksum for script '\" . \$ScriptVal-\ + >\"name\" . \"' matches, ignoring.\");\r\ + \n :error true;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len (\$ScriptInfo->\"certificate\") ] > 0) do={\r\ + \n :if ([ \$CertificateAvailable (\$ScriptInfo->\"certificate\") \"\ + fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, tryi\ + ng without.\");\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n :local BaseUrl [ \$EitherOr (\$ScriptInfo->\"base-url\") \$Globa\ + lScriptsUrl ];\r\ + \n :local UrlSuffix [ \$EitherOr (\$ScriptInfo->\"url-suffix\") \$G\ + lobalScriptsUrlSuffix ];\r\ + \n :local Url (\$BaseUrl . \$ScriptVal->\"name\" . \".rsc\" . \$Url\ + Suffix);\r\ + \n \$LogPrint warning \$0 (\"Fetching script '\" . (\$ScriptVal->\"\ + name\") . \"' from url: \" . \$Url);\r\ + \n :local Result [ /tool/fetch check-certificate=yes-without-crl \\\ + \r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url outp\ + ut=user as-value ];\r\ + \n :if (\$Result->\"status\" = \"finished\") do={\r\ + \n :set SourceNew [ :tolf (\$Result->\"data\") ];\r\ + \n } \r\ + \n } do={\r\ + \n \$LogPrint warning \$0 (\"Failed fetching script '\" . \$ScriptV\ + al->\"name\" . \"': \" . \$Err);\r\ + \n :if (\$Err != \"Fetch failed with status 404\") do={\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$ScriptVal->\"source\" = \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Removing dummy. Typo on installation\ + \?\");\r\ + \n /system/script/remove \$Script;\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Sc\ + riptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :len \$CheckSum ] = 0) do={\r\ + \n \$LogPrint warning \$0 (\"Added the script manually\? Skip u\ + pdates with 'ignore=true' in comment.\");\r\ + \n }\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$SourceNew ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"No update for script '\" . (\$ScriptVal-\ + >\"name\") . \"'.\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :local SourceCRLF [ :tocrlf \$SourceNew ];\r\ + \n :if (\$InitSetup != true && ((\$ScriptVal->\"name\") != \"AM-Globa\ + lConfig\" || (\$ScriptVal->\"name\") != \"AM-GlobalFunc\")) do={\r\ + \n :if (\$SourceNew = (\$ScriptVal->\"source\") || \$SourceCRLF = (\ + \$ScriptVal->\"source\")) do={\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\") \ + . \"' source did not change. End of lines set to CRLF.\");\r\ + \n if (\$ScriptUpdatesCRLF = false) do={\r\ + \n :put [ :tolf \$SourceNew ];\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\"\ + ) . \"' source update end of lines to LF.\");\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([ :pick \$SourceNew 0 14 ] != \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Looks like new script '\" . \$ScriptVa\ + l->\"name\" . \"' is not valid (missing shebang = '\$shebang') Ignoring!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if (\$CommentsInScripts = true) do={\r\ + \n :local CommentLine [ \$Grep \$SourceNew (\"\\23 Script comment:\ + \_\") ];\r\ + \n :if ([ :len \$CommentLine ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"The search in new script '\" . (\$Scr\ + iptVal->\"name\") . \"' for comment in script body, is not found result Ig\ + noring!\");\r\ + \n :error false;\r\ + \n } else={\r\ + \n :if ([ :len (\$ScriptVal->\"comment\")] = 0 || [ :len (\$Scri\ + ptVal->\"comment\")] != [ :pick \$CommentLine ([ :find \$CommentLine \":\"\ + ] + 2) [ :len \$CommentLine] ]) do={\r\ + \n \$LogPrint debug \$0 (\"Exsiting comment for script '\" . (\ + \$ScriptVal->\"name\") . \"' is '\" . (\$ScriptVal->\"comment\") . \"'.\")\ + ;\r\ + \n :set (\$ScriptVal->\"comment\") [ :pick \$CommentLine ([ :f\ + ind \$CommentLine \":\"] + 2) [ :len \$CommentLine] ];\r\ + \n \$LogPrint debug \$0 (\"New comment (\" . (\$ScriptVal->\"c\ + omment\") . \") for script '\" . (\$ScriptVal->\"name\") . \"' has been ad\ + ded!\");\r\ + \n } else={\r\ + \n \$LogPrint debug \$0 (\"Comment for script '\" . (\$ScriptV\ + al->\"name\") . \"' did not change.\");\r\ + \n :error false;\r\ + \n }\r\ + \n } \r\ + \n }\r\ + \n\r\ + \n :local RequiredROS ([ \$ParseKeyValueStore [ \$Grep \$SourceNew (\ + \"\\23 requires RouterOS, \") ] ]->version);\r\ + \n :if ([ \$RequiredRouterOS \$0 [ \$EitherOr \$RequiredROS \"0.0\" ]\ + \_false ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires RouterOS \" . \\\r\ + \n \$RequiredROS . \", which is not met by your installation. I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :local RequiredDM [ \$ParseKeyValueStore [ \$Grep \$SourceNew (\"\ + \\23 requires device-mode, \") ] ];\r\ + \n :local MissingDM ({});\r\ + \n :foreach Feature,Value in=\$RequiredDM do={\r\ + \n :if ([ :typeof (\$DeviceMode->\$Feature) ] = \"bool\" && (\$Devi\ + ceMode->\$Feature) = false) do={\r\ + \n :set MissingDM (\$MissingDM, \$Feature);\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len \$MissingDM ] > 0) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires disabled \" . \\\r\ + \n \"device-mode features (\" . [ :tostr \$MissingDM ] . \"). I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$ValidateSyntax \$SourceNew ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Syntax validation for script '\" . (\$\ + ScriptVal->\"name\") . \"' failed! Ignoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n \r\ + \n :log info (\"Updating script: \" . (\$ScriptVal->\"name\") . \" Sc\ + ript comment: \" . (\$ScriptVal->\"comment\"));\r\ + \n\r\ + \n /system/script/set owner=(\$ScriptVal->\"name\") comment=(\$Script\ + Val->\"comment\") \\\r\ + \n source=[ \$IfThenElse (\$ScriptUpdatesCRLF = true) \$SourceCRL\ + F \$SourceNew ] \$Script;\r\ + \n :if (\$ScriptVal->\"name\" = \"AM-GlobalConfig\" || \\\r\ + \n \$ScriptVal->\"name\" = \"AM-GlobalFunc\" || \\\r\ + \n \$ScriptVal->\"name\" ~ (\"^(AM-GlobalFunc\\\\.d|mod)/.\")) d\ + o={\r\ + \n :set ReloadGlobal true;\r\ + \n }\r\ + \n } on-error={ }\r\ + \n }\r\ + \n :if (\$ReloadGlobal = true) do={\r\ + \n \$LogPrint info \$0 (\"Reloading global configuration and functions.\ + \");\r\ + \n :onerror Err {\r\ + \n :global InitSetup (a);\r\ + \n /system/script {run AM-GlobalConfig; run AM-GlobalFunc;};\r\ + \n } do={\$LogPrint error \$0 (\"Reloading global configuration and fun\ + ctions failed! \" . \$Err)}\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore > \$GlobalConfVersion) do={\r\ + \n \$LogPrint warning \$0 (\"The configuration version decreased from \ + \" . \\\r\ + \n \$GlobalConfVersionBefore . \" to \" . \$GlobalConfVersion . \\\r\ + \n \". Installed an older version\?\");\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore < \$GlobalConfVersion) do={\r\ + \n :global GlobalConfChanges;\r\ + \n :global GlobalConfMigration;\r\ + \n :local ChangeLogCode;\r\ + \n :if ([ :len \$ChangeLogCode ] > 0) do={\r\ + \n :if ([ \$ValidateSyntax \$ChangeLogCode ] = true) do={\r\ + \n :onerror Err {\r\ + \n [ :parse \$ChangeLogCode ];\r\ + \n } do={\$LogPrint warning \$0 (\"The changelog failed to run: \" \ + . \$Err)}\r\ + \n } else={\$LogPrint warning \$0 (\"The changelog failed syntax vali\ + dation!\")}\r\ + \n }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Migration (\$GlobalConfMigration->[ :tostr \$I ]);\r\ + \n :do {\r\ + \n :if ([ :typeof \$Migration ] != \"str\") do={\r\ + \n \$LogPrint debug \$0 (\"Migration code for change \" . \$I .\ + \_\" is not available.\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$ValidateSyntax \$Migration ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Migration code for change \" . \$I\ + \_. \" failed syntax validation!\");\r\ + \n :error false}\r\ + \n \$LogPrint info \$0 (\"Applying migration for change \" . \$I \ + . \": \" . \$Migration);\r\ + \n :onerror Err {\r\ + \n [ :parse \$Migration ];\r\ + \n } do={\$LogPrint warning \$0 (\"Migration code for change \" .\ + \_\$I . \" failed to run: \" . \$Err)}\r\ + \n } on-error={ }\r\ + \n }\r\ + \n }\r\ + \n :local NotificationMessage (\"The configuration version on \" . \$Id\ + entity . \" increased \" . \\\r\ + \n \"to \" . \$GlobalConfVersion . \", current configuration may nee\ + d modification. \" . \\\r\ + \n \"Please review and update global-config-overlay, then re-run glo\ + bal-config.\");\r\ + \n \$LogPrint info \$0 (\$NotificationMessage);\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nChanges:\ + \");\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Change (\$GlobalConfChanges->[ :tostr \$I ]);\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n \" . \\\ + \r\ + \n [ \$SymbolForNotification \"pushpin\" \"*\" ] . \$Change);\r\ + \n \$LogPrint info \$0 (\"Change \" . \$I . \": \" . \$Change);\r\ + \n }\r\ + \n } else={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nNews and\ + \_changes are not available.\");\r\ + \n\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={ :set GlobalConfChanges; \ + } else={ :global GlobalConfChanges (a); }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={ :set GlobalConfMigrati\ + on; } else={ :global GlobalConfMigration (a); }\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n\r\ + \n# load modules\r\ + \n:foreach Script in=[ /system/script/find where name ~ \"^(global-functio\ + ns\\\\.d|mod)/.\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :if ([ \$ValidateSyntax (\$ScriptVal->\"source\") ] = true) do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' f\ + ailed to run: \" . \$Err);\r\ + \n }\r\ + \n } else={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' fai\ + led syntax validation, skipping.\");\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# Add and fix global scheduler\r\ + \n:put \"Add or/and fix Global scheduler...\";\r\ + \n:local OnEvent \"/system/script { run AM-GlobalConfig; run AM-GlobalFunc\ + ; }\";\r\ + \n:local Comment \"Every Day Set Global Environment Variables\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-SetGlobalEnv\" ] ] \ + = 0) do={\r\ + \n /system/scheduler/add name=\"AM-SetGlobalEnv\" comment=\$Comment on-ev\ + ent=\$OnEvent start-time=startup;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" comment ] != \$Comment)\ + \_do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" comment=\$Comment;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" on-event ] != \$OnEvent\ + ) do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" on-event=\$OnEvent;\r\ + \n}\r\ + \n# Add Update Script scheduler\r\ + \n:put \"Add or/and fix Update Script scheduler\";\r\ + \n:set OnEvent \":global ScriptInstallUpdate; \\\$ScriptInstallUpdate;\";\ + \r\ + \n:set Comment \"Every Day Check For Update Scripts\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-ScriptInstallUpdate\ + \" ] ] = 0) do={\r\ + \n/system/scheduler/add name=\"AM-ScriptInstallUpdate\" comment=\$Comment \ + start-time=startup interval=1d on-event=\$OnEvent;\r\ + \n}\r\ + \n\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady;\r\ + \n :retry { :if (\$GlobalConfReady != true) do={ :error (\"Global configs\ + \_not ready.\"); }; } delay=100ms max=5;\r\ + \n\r\ + \n:log warning (\"This script \$ScriptName Loaded \" . \\\r\ + \n [ \$IfThenElse (\$CommitId != \"unknown\") ([ \$CommitBrief ] . \" \ + \") ] . \\\r\ + \n \"on \" . \$devBoardName . \"\\nRouterOS: \" . \$devCurVersion . \"\ + \\nArchitecture: \" . \$devCurArch . \"\\nIdentity: \" . \$Identity);\r\ + \n # signal we are ready\r\ + \n:set GlobalFuncReady true;\r\ + \n:set ExitOK true;\r\ + \n\r\ + \n:log info (\$0 . \"Global configs ready!\");\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n}\r\ + \n" +add comment=\ + "Lookup IP addresses for RKN list and add them to firewall address-list" \ + dont-require-permissions=no name=AM-RKN-dnsLookup owner=AM-RKN-dnsLookup \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="#!rsc by Vados\r\ + \n# RouterOS script: AM-RKN-dnsLookup\r\ + \n# Script comment: Lookup IP addresses for RKN list and add them to firew\ + all address-list\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n# Script description: This script is intended to be run periodically, e.\ + g. once a day, to update the IP addresses in the firewall address-list bas\ + ed on the RKN list.\r\ + \n# It resolves the domain names in the RKN list and a\ + dds the corresponding IP addresses to the specified firewall address-list.\ + \r\ + \n# The script also handles CNAME records by following\ + \_them until it finds A records.\r\ + \n# Usage:\r\ + \n# /system/script { run AM-RKN-dnsLookup; }\r\ + \n:local ScriptName [ :jobname ];\r\ + \n\r\ + \n:global dnsLookupReady false;\r\ + \n:if ([:typeof \$dnsLookupReady] != \"boolean\") do={ :set dnsLookupReady\ + \_true; }\r\ + \nwhile (!\$dnsLookupReady) do={ :nothing; }\r\ + \n:set dnsLookupReady false;\r\ + \n\r\ + \n:local arrRKN {\r\ + \n \"telegra.ph\"\r\ + \n \"grani.ru\"\r\ + \n \"xhamster.com\"\r\ + \n \"lostfilm.tv\"\r\ + \n \"rutracker.org\"\r\ + \n \"lurkmore.to\"\r\ + \n \"dou.ua\"\r\ + \n \"youporn.com\"\r\ + \n \"pornolab.net\"\r\ + \n \"crimerussia.com\"\r\ + \n \"telegram.org\"\r\ + \n \"api.telegram.org\"\r\ + \n \"facebook.com\"\r\ + \n \"instagram.com\"\r\ + \n \"x.com\"\r\ + \n \"twitter.com\"\r\ + \n \"youtube.com\"\r\ + \n \"googleapis.com\"\r\ + \n \"youtu.be\"\r\ + \n \"googlevideo.com\"\r\ + \n \"gstatic.com\"\r\ + \n \"googleusercontent.com\"\r\ + \n \"nhacmp3youtube.com\"\r\ + \n \"telegram.me\"\r\ + \n \"t.me\";}\r\ + \n\r\ + \n:local ListRKN \"RKN-dnsLookup\";\r\ + \n:local ListComment (\"ListRKN managed by \" . \$ScriptName);\r\ + \n:foreach aListItem in=[/ip/firewall/address-list find list=\$ListRKN] do\ + ={\r\ + \n /ip firewall address-list remove \$aListItem;\r\ + \n}\r\ + \n:foreach aItem in=\$arrRKN do={\r\ + \n# :do {\r\ + \n :retry delay=500ms max=5 on-error={:log error \"dns resolve fail\ + ure [ \$aItem - resolve ip ]\"} command={:resolve \$aItem}\r\ + \n# } on-error={\r\ + \n# :log error \"dns resolve failure [ \$arItem - resolve ip ]\";\r\ + \n# }\r\ + \n :foreach dnsRecord in=[/ip dns cache all find where (name=\$aItem)] \ + do={\r\ + \n :if ([/ip dns cache all get \$dnsRecord type]=\"A\") do={\r\ + \n :local newAddress [/ip dns cache all get \$dnsRecord data];\ + \r\ + \n :do {\r\ + \n /ip firewall address-list add list=\$ListRKN address=\$newA\ + ddress comment=\$aItem;\r\ + \n :log warning \"Successfully adding ip to list A record: \ + \$dnsRecord, address: \$newAddress domain: \$aItem\";\r\ + \n } on-error={\r\ + \n :log error \"error when adding ip to list A record: \$dn\ + sRecord, address: \$newAddress domain: \$aItem\";\r\ + \n }\r\ + \n }\r\ + \n :if ([/ip dns cache all get \$dnsRecord type]=\"CNAME\") do={\r\ + \n :local cname;\r\ + \n :local nextCname;\r\ + \n :local cnameAddress;\r\ + \n :set cname [/ip dns cache all find where (name=\$aItem && ty\ + pe=\"CNAME\")];\r\ + \n :set nextCname [/ip dns cache all find where (name=[/ip dns \ + cache all get \$cname data] && type=\"CNAME\")];\r\ + \n :while (\$nextCname != \"\") do={\r\ + \n :set cname \$nextCname;\r\ + \n :set nextCname [/ip dns cache all find where (name=[/ip \ + dns cache all get \$cname data] && type=\"CNAME\")];\r\ + \n }\r\ + \n :foreach aRecord in=[/ip dns cache all find where (name=[/ip\ + \_dns cache all get \$cname data] && type=\"A\")] do={\r\ + \n :do {\r\ + \n :set cnameAddress [/ip dns cache all get \$aRecord d\ + ata];\r\ + \n /ip firewall address-list add list=\$ListRKN address\ + =\$cnameAddress comment=\$aItem;\r\ + \n :log warning \"Succesfully adding ip to list CNAME r\ + ecord: \$cname, A record: \$aRecord, address: \$newAddress, from domain: \ + \$aItem.\";\r\ + \n# /ip firewall raw add action=add-dst-to-address-list\ + \_address-list=\$ListRKN address-list-timeout=none-dynamic chain=preroutin\ + g comment=\$aItem content=\$aItem;\r\ + \n } on-error={\r\ + \n :log error \"error when adding ip to list CNAME reco\ + rd: \$cname, A record: \$aRecord, address: \$newAddress, from domain: \$aI\ + tem.\";\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:if ([ :len [ /system/scheduler/find where name=\$ScriptName ] ] = 0) do\ + ={\r\ + \n :log error (\$ScriptName . \"System Scheduler for script: \" . \$Scri\ + ptName . \" is NOT SET! Fix It Now!\");\r\ + \n /system/scheduler/add name=\$ScriptName on-event=\"/system/script { \ + run \$ScriptName; }\" comment=\"Update RKN DNS Looup\" interval=00:30:00 p\ + olicy=\"ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon\"\ + \_start-time=startup;\r\ + \n }\r\ + \n:set dnsLookupReady true;\r\ + \ndelay 2s; \r\ + \n:global dnsLookupReady (a);\r\ + \n" +add dont-require-permissions=no name="renameLists " owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + local oldName \"Test\";\ + \n:local newName \"Test-IP\";\ + \n/ip firewall address-list\ + \n:foreach i in=[find list=\$oldName] do={\ + \nset \$i list=\$newName;\ + \n}\ + \n" +add dont-require-permissions=no name=IPv6-Settings owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + #############Script Settings##################\ + \n\ + \n:local \"IF-TUN\" \"6to4-tun\"\ + \n:local \"IF-LOC\" \"Bridge-LAN\"\ + \n:local \"POOL-6TO4\" \"poolV6-local\"\ + \n:local \"force-update\" false\ + \n############ use then static ip #######################\ + \n:local \"lease-address\" [/ip address get [find interface=\$\"IF-LOC\"] \ + address]\ + \n#:local \"interface\" \"ext\"\ + \n:local bound 0\ + \n\ + \n###############################################\ + \n\ + \n:local 6to4prefix do={\ + \n :global dec2hex\ + \n :local ipv4 \$1\ + \n :local ipv6 \"2002:\"\ + \n :for i from=0 to=2 step=1 do={ \ + \n :if ( \$i = 2) do={\ + \n :set ipv6 (\$ipv6.\":\")\ + \n }\ + \n :local fp [:find \$ipv4 \".\"]\ + \n :if ( \$fp > 0 ) do={\ + \n :set ipv6 (\$ipv6.[\$dec2hex [:pick \$ipv4 0 \$fp]])\ + \n :set ipv4 [:pick \$ipv4 (\$fp + 1 ) [:len \$ipv4]]\ + \n }\ + \n }\ + \n :return (\$ipv6.[\$dec2hex \$ipv4])\ + \n}\ + \n:local \"str_replace\" do={\ + \n :local \"str-pos\" [:find \$1 \$2]\ + \n :local \"str-len\" [:len \$2]\ + \n :local str \"\"\ + \n :local rest \$1\ + \n :while (\$\"str-pos\" >= 0) do={\ + \n :local t [:pick \$rest 0 \$\"str-pos\"]\ + \n :if ( \$4 > 0 ) do={\ + \n :for i from=1 to=(\$4 - [:len \$t]) step=1 do={\ + \n :set str (\$str.\$5)\ + \n }\ + \n }\ + \n :set str (\$str.\$t.\$3)\ + \n :set rest [:pick \$rest (\$\"str-pos\"+\$\"str-len\") [:len \$re\ + st]]\ + \n :set \"str-pos\" [:find \$rest \$2]\ + \n }\ + \n :return (\$str.\$rest)\ + \n}\ + \n\ + \n:if (\$bound=1) do={\ + \n :local t [:find \$\"lease-address\" \"/\"]\ + \n\ + \n :if ( \$t >= 0 ) do={\ + \n :set \"lease-address\" [:pick \$\"lease-address\" 0 \$t]\ + \n }\ + \n\ + \n :if ( [/interface 6to4 print count-only where name=\$\"IF-TUN\"] = 0\ + ) do={\ + \n /interface 6to4 add !keepalive local-address=\$\"lease-address\"\ + \_mtu=1280 name=\$\"IF-TUN\" remote-address=10.30.30.0\ + \n :set \"force-update\" true\ + \n }\ + \n\ + \n :local \"IPv4-TUN\" [/interface 6to4 get [find name=\$\"IF-TUN\"] lo\ + cal-address];\ + \n\ + \n :if ( \$\"lease-address\" != \$\"IPv4-TUN\" || \$\"force-update\" = \ + true) do={\ + \n /interface 6to4 set [find name=\$\"IF-TUN\"] local-address=\$\"l\ + ease-address\"\ + \n /ipv6 address remove [find interface=\$\"IF-TUN\"]\ + \n /ipv6 address remove [find interface=\$\"IF-LOC\"]\ + \n /ipv6 route remove [find gateway ~ \$\"IF-TUN\" and dst-address=\ + \"::/0\"]\ + \n :local \"pool-prefix\" [\$6to4prefix \$\"lease-address\"]\ + \n :if ( [/ipv6 pool print count-only where name=\$\"POOL-6TO4\"] =\ + \_0) do={ \ + \n /ipv6 pool add name=\$\"POOL-6TO4\" prefix=(\$\"pool-prefix\ + \".\":1::/64\") prefix-length=64\ + \n } else={\ + \n /ipv6 pool set [/ipv6 pool find where name=\$\"POOL-6TO4\"] \ + prefix=(\$\"pool-prefix\".\":1::/64\")\ + \n }\ + \n /ipv6 address add address=(\$\"pool-prefix\".\"::\".[\$MAC2EUI64\ + \_[/interface get [find name=\$\"interface\"] mac-address]].\"/3\") interf\ + ace=\$\"IF-TUN\" advertise=no\ + \n /ipv6 address add eui-64=yes from-pool=\$\"POOL-6TO4\" interface\ + =\$\"IF-LOC\" advertise=yes\ + \n /ipv6 route add dst-address=::/0 gateway=(\"::10.30.30.81%\".\$\ + \"IF-TUN\")\ + \n :local t [/ipv6 address get [find interface=\$\"IF-LOC\"] addres\ + s]\ + \n :set t [:pick \$t 0 [:find \$t \"/\"]]\ + \n :if ( [/ipv6 dhcp-server option print count-only where name=DNS]\ + \_= 0) do={\ + \n /ipv6 dhcp-server option add code=23 name=DNS value=(\"0x\".\ + [\$\"str_replace\" \$t \":\" \"\" 4 \"0\"])\ + \n } else={\ + \n /ipv6 dhcp-server option set [/ipv6 dhcp-server option find \ + where name=DNS] value=(\"0x\".[\$\"str_replace\" \$t \":\" \"\" 4 \"0\"])\ + \n }\ + \n :if ( [/ipv6 dhcp-server print count-only where interface=\$\"IF\ + -LOC\"] = 0) do={\ + \n /ipv6 dhcp-server add address-pool=\$\"POOL-6TO4\" dhcp-opti\ + on=DNS interface=\$\"IF-LOC\" lease-time=1d name=\$\"IF-LOC\"\ + \n }\ + \n :if ( [ipv6 nd print count-only where interface=\$\"IF-LOC\"] = \ + 0) do={\ + \n /ipv6 nd add advertise-dns=no interface=\$\"IF-LOC\" other-c\ + onfiguration=yes\ + \n }\ + \n }\ + \n}" +add dont-require-permissions=no name=IPv6-GlobalFunc owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + global MAC2EUI64 do={\ + \n :local EUI64 \"\"\ + \n :local MAC \$1\ + \n :local HEX2DEC \"0123456789ABCDEF\"\ + \n :for i from=0 to=5 step=1 do={\ + \n :if ( \$i = 0 ) do={\ + \n :set EUI64 [:pick \$MAC 0]\ + \n :set EUI64 (\$EUI64.[:tostr [:pick \$\"HEX2DEC\" ([:find \$\ + \"HEX2DEC\" [:pick \$MAC 1]] ^ 2)]])\ + \n }\ + \n :if ( \$i = 1 || \$i = 3) do={\ + \n :set EUI64 (\$EUI64.\":\")\ + \n }\ + \n :if ( \$i = 2 ) do={\ + \n :set EUI64 (\$EUI64.\"FF:FE\")\ + \n }\ + \n :if ( [:find \$MAC \":\"] = 2 ) do={\ + \n :set MAC [:pick \$MAC 3 [:len \$MAC]]\ + \n :set EUI64 (\$EUI64.[:pick \$MAC 0 2])\ + \n }\ + \n }\ + \n :return (\$EUI64)\ + \n}\ + \n\ + \n:global dec2hex do={\ + \n :local hex \"\"\ + \n :local dec [:tonum \$1]\ + \n :for i from=0 to=4 step=4 do={\ + \n :set hex ([:pick \"0123456789ABCDEF\" ((\$dec>>\$i)&0xf) (((\$de\ + c>>\$i)&0xf)+1)].\$hex)\ + \n }\ + \n :return ([:tostr \$hex])\ + \n}" +add comment=Generator dont-require-permissions=no name=proxySet owner=vados \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="# 2. WireGuard interface\r\ + \n/interface/wireguard/add name=wg-VDSina private-key=\"4CHyNhbOs923/EyNDg\ + uMXJXdghL2I3UyzXPwrANVgHs=\" listen-port=21641\r\ + \n/interface/wireguard/peers/add interface=wg-VDSina public-key=\"o5IgCl1/\ + G6xbl6j7sPaP4ZNYREeYOmqZjKbm+v0b9jU=\" preshared-key=\"RNvF7T1cg5tyXcYQnD9\ + N5BEp6e20vVJQTv0GMjyvHU0=\" endpoint-address=10.30.30.33 endpoint-port=518\ + 20 allowed-address=0.0.0.0/0 persistent-keepalive=11\r\ + \n/ip/address/add address=10.9.0.3/32 interface=wg-VDSina\r\ + \n\r\ + \n# 3. DNS\r\ + \n{ :local rawDNS [/ip/dns/get servers]; :local prevDNS \"\"; :foreach s i\ + n=[:toarray \$rawDNS] do={ :if ([:len \$prevDNS] > 0) do={ :set prevDNS \"\ + \$prevDNS,\$s\" } else={ :set prevDNS \$s } }; /system/script/add name=VDS\ + ina-prev-dns comment=\"\$prevDNS\" source=\"\" }\r\ + \n/ip/dns/set servers=8.8.4.4\r\ + \n/ip/route/add dst-address=8.8.4.4/32 gateway=wg-VDSina distance=1 commen\ + t=VDSina-dns\r\ + \n\r\ + \n# 4. Enable WireGuard\r\ + \n/interface/wireguard/enable wg-VDSina\r\ + \n" +add dont-require-permissions=no name=AM-Telegram-Send owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\ + \n# RouterOS script: AM-Telegram-Send\ + \n# Script comment: Mini script for send messages to admin Telegram chat\ + \n# Copyright (c) 2007-2026 Vados \ + \n#\ + \n#\ + \n# requires RouterOS, version=7.19\ + \n# requires device-mode, fetch\ + \n#\ + \n# Script description: \ + \n#\ + \n#\ + \n# Usage:\ + \n# /system/script { run AM-Telegram-Send \"Message here )))\"; }\ + \n\ + \n/system/script\ + \nadd name=telegram-send dont-require-permissions=no source={\ + \n :global TelegramTokenId;\ + \n :global TelegramChatId;\ + \n :local message [:tostr \$1];\ + \n :local identity [/system/identity/get name];\ + \n :local sendMsg (\"[\".\$identity.\"] \".\$message)\ + \n /tool/fetch url=(\"https://api.telegram.org/bot\".\$TelegramTokenId.\\\ + \n \"/sendMessage\\\?chat_id=\".\$TelegramChatId.\\\ + \n \"&text=\".\$sendMsg.\\\ + \n \"&parse_mode=HTML\") \\\ + \n mode=https keep-result=no\ + \n}" +add dont-require-permissions=no name=telegram-send owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="\ + \n :global TelegramTokenId;\ + \n :global TelegramChatId;\ + \n :local message [:tostr \$1];\ + \n :local identity [/system/identity/get name];\ + \n :local sendMsg (\"[\".\$identity.\"] \".\$message)\ + \n /tool/fetch url=(\"https://api.telegram.org/bot\".\$TelegramTokenId.\\\ + \n \"/sendMessage\\\?chat_id=\".\$TelegramChatId.\\\ + \n \"&text=\".\$sendMsg.\\\ + \n \"&parse_mode=HTML\") \\\ + \n mode=https keep-result=no\ + \n" +/user group +add name=backup policy="ssh,read,write,policy,test,web,sniff,sensitive,api,res\ + t-api,!local,!telnet,!ftp,!reboot,!winbox,!password,!romon" +/ip smb +set enabled=no interfaces=lo +/interface bridge port +add bridge=Bridge-LAN interface=ether5 +add bridge=Bridge-LAN interface=ether2 +add bridge=Bridge-LAN interface=ether4 trusted=yes +add bridge=Bridge-LAN interface=ether3 +add bridge=Bridge-LAN interface=wlan2 +add bridge=Bridge-LAN interface=wlan1 +/interface bridge settings +set use-ip-firewall=yes +/ip firewall connection tracking +set udp-timeout=10s +/ip neighbor discovery-settings +set discover-interface-list=LAN lldp-med=no lldp-poe-power=no +/ip settings +set accept-redirects=yes ipv4-multipath-hash-policy=l4 max-neighbor-entries=\ + 8192 +/ipv6 settings +set disable-ipv6=yes disable-link-local-address=yes forward=no \ + max-neighbor-entries=8192 multipath-hash-policy=l4 +/interface detect-internet +set detect-interface-list=WAN internet-interface-list=Internet \ + lan-interface-list=LAN request-interval=1m wan-interface-list=WAN +/interface l2tp-server server +set authentication=mschap2 use-ipsec=yes +/interface list member +add interface=Unet-ether1 list=Internet +add interface=WG list=WAN +add interface=Bridge-LAN list=LAN +add interface=Unet-ether1 list=WAN +/interface wireguard peers +add allowed-address=0.0.0.0/0 client-allowed-address=::/0 endpoint-address=\ + 109.107.177.79 endpoint-port=29185 interface=WG name=VDSina \ + persistent-keepalive=10s public-key=\ + "zuRsO+dL/j6awaBWZ/NgnHR+bCNUjRWbaHFpdzkNnw4=" +add allowed-address=0.0.0.0/0 client-allowed-address=::/0 disabled=yes \ + endpoint-address=10.30.30.33 endpoint-port=55055 interface=wg-nuc name=\ + NUC persistent-keepalive=11s public-key=\ + "+OvzAjLC45Td52hjkQPmpGvC3RGhZ+Q2CDMJ1U/D4Rw=" +/interface wireless cap +# +set bridge=Bridge-LAN caps-man-addresses=10.30.30.10 \ + caps-man-certificate-common-names=CAPsMAN-51B25E507D07 certificate=\ + CAP-488F5A8E1ABC discovery-interfaces=Bridge-LAN enabled=yes interfaces=\ + wlan2,wlan1 lock-to-caps-man=yes +/ip address +add address=10.30.30.81/24 interface=Bridge-LAN network=10.30.30.0 +add address=10.8.0.2/24 interface=WG network=10.8.0.0 +add address=192.168.33.2/24 disabled=yes interface=wg-nuc network=\ + 192.168.33.0 +/ip cloud +set update-time=no +/ip dhcp-client +add add-default-route=special-classless check-gateway=arp comment=Unet-client \ + default-route-distance=2 default-route-tables=main:2 dhcp-options=\ + hostname,clientid_duid interface=Unet-ether1 name=UnetDHCP use-peer-dns=\ + no use-peer-ntp=no +/ip dhcp-server config +set accounting=no +/ip dhcp-server lease +add address=10.30.30.101 client-id=1:48:8f:5a:8e:ee:a8 mac-address=\ + 48:8F:5A:8E:EE:A8 server=masterDHCP +add address=10.30.30.107 client-id=1:f0:de:f1:91:d:1e mac-address=\ + F0:DE:F1:91:0D:1E server=masterDHCP +add address=10.30.30.83 client-id=1:0:80:77:ec:43:86 mac-address=\ + 00:80:77:EC:43:86 server=masterDHCP +add address=10.30.30.70 dhcp-option=domain-search-list,HostName \ + dhcp-option-set=HP1505n mac-address=00:1E:0B:1A:88:8D server=masterDHCP +add address=10.30.30.50 client-id=1:4:7c:16:36:f5:37 mac-address=\ + 04:7C:16:36:F5:37 server=masterDHCP +add address=10.30.30.71 client-id=1:10:2d:41:2c:20:c2 mac-address=\ + 10:2D:41:2C:20:C2 server=masterDHCP +add address=10.30.30.61 client-id=1:10:8:c1:81:59:5f mac-address=\ + 10:08:C1:81:59:5F server=masterDHCP +add address=10.30.30.72 client-id=1:10:38:1f:d2:5f:13 mac-address=\ + 10:38:1F:D2:5F:13 server=masterDHCP +add address=10.30.30.99 client-id=1:10:b1:df:e9:d0:d5 mac-address=\ + 10:B1:DF:E9:D0:D5 server=masterDHCP +add address=10.30.30.111 client-id=1:fe:2:52:be:3f:16 mac-address=\ + FE:02:52:BE:3F:16 server=masterDHCP +add address=10.30.30.92 client-id=1:d4:6d:6d:fd:e5:49 mac-address=\ + D4:6D:6D:FD:E5:49 server=masterDHCP +add address=10.30.30.93 client-id=1:7e:8b:1e:cb:b8:cb mac-address=\ + CC:42:10:12:46:94 server=masterDHCP +add address=10.30.30.94 client-id=1:50:cc:f8:85:96:a mac-address=\ + 50:CC:F8:85:96:0A server=masterDHCP +add address=10.30.30.82 client-id=1:e4:18:6b:58:34:ab mac-address=\ + E4:18:6B:58:34:AB server=masterDHCP +add address=10.30.30.75 client-id=1:0:27:15:41:97:a9 mac-address=\ + 00:27:15:41:97:A9 server=masterDHCP +add address=10.30.30.77 dhcp-option=HostName dhcp-option-set=StartLife1 \ + mac-address=C4:82:E1:03:49:10 server=masterDHCP +add address=10.30.30.73 client-id=1:10:38:1f:82:81:f2 mac-address=\ + 10:38:1F:82:81:F2 server=masterDHCP +add address=10.30.30.95 client-id=1:88:ae:dd:60:39:54 dhcp-option=HostName \ + dhcp-option-set=VADOS-NUC mac-address=88:AE:DD:60:39:54 server=masterDHCP +add address=10.30.30.98 client-id=1:c8:5b:76:d8:cc:bf dhcp-option=HostName \ + dhcp-option-set=VADOS-LEN mac-address=C8:5B:76:D8:CC:BF server=masterDHCP +add address=10.30.30.96 client-id=iPhone mac-address=4A:91:ED:0E:1F:39 \ + server=masterDHCP +add address=10.30.30.74 client-id=MiTV dhcp-option=HostName dhcp-option-set=\ + MiTV mac-address=20:72:A9:D3:34:36 server=masterDHCP +add address=10.30.30.30 dhcp-option=HostName dhcp-option-set=miller \ + mac-address=F0:75:5D:1E:5F:71 server=masterDHCP +add address=10.30.30.100 client-id=iVados dhcp-option=HostName \ + dhcp-option-set=iVados mac-address=DE:4D:72:F3:1A:9F server=masterDHCP +add address=10.30.30.10 allow-dual-stack-queue=yes client-id=\ + ff:0:0:0:df:0:3:0:1:f0:75:5d:1e:5f:85 dhcp-option=HostName \ + dhcp-option-set=MikroNUC mac-address=F0:75:5D:1E:5F:85 server=masterDHCP \ + use-src-mac=yes +/ip dhcp-server network +add address=10.30.30.0/24 caps-manager=10.30.30.10 dhcp-option=\ + domain-search-list dhcp-option-set=domain-search-set dns-server=\ + 10.30.30.33,109.107.177.79 domain=WORKGROUP gateway=10.30.30.81 netmask=\ + 24 ntp-server=10.30.30.81,10.30.30.10 wins-server=10.30.30.95,10.30.30.98 +/ip dns +set allow-remote-requests=yes cache-max-ttl=1d doh-max-concurrent-queries=100 \ + doh-max-server-connections=20 servers=8.8.8.8 verify-doh-cert=yes +/ip dns static +add address=109.107.177.79 comment="VDSina Server DNS" name=ns1.vados.ru \ + type=A +add address=109.107.177.79 name=vds.vados.ru type=A +add comment="Local DNS server for vados.ru" disabled=yes ns=10.30.30.81 \ + regexp=*.vados.ru type=NS +add comment="*.vados.ru Regexp" disabled=yes forward-to=10.30.30.81 regexp=\ + "\".*\\\\.vados\\\\.ru\"" type=FWD +add comment="*.30.30.10.in-addr.arpa Regexp" disabled=yes forward-to=\ + 10.30.30.81 regexp="\".*\\\\.30\\\\.30\\\\.10\\\\.in-addr\\\\.arpa" type=\ + FWD +add address=10.30.30.81 comment=NS.vados.ru.local disabled=yes name=\ + ns.vados.ru.local type=A +add comment="DNS ns.vados.ru.local" disabled=yes match-subdomain=yes name=\ + vados.ru ns=ns.vados.ru.local type=NS +add address=10.30.30.33 comment=reg.vados.ru name=reg.vados.ru type=A +add address=8.8.8.8 comment="Google-dns IPv4 1" match-subdomain=yes name=\ + dns.google type=A +add address=8.8.4.4 comment="Google-dns IPv4 2" name=dns.google type=A +add address=10.30.30.33 comment=AMSTER name=amster.vados.ru type=A +add address=10.30.30.33 name=ros.vados.ru type=A +add address=10.30.30.95 name=vados-nuc type=A +add address=10.30.30.33 name=amster-nuc type=A +add address=10.30.30.33 name=amster type=A +add address=10.30.30.33 name=amster-reg type=A +add address=10.30.30.33 disabled=yes name=mng.vados.ru type=A +add address=10.30.30.33 name=mgr.vados.ru type=A +add address=10.30.30.33 name=prt.vados.ru type=A +add address=10.30.30.33 disabled=yes match-subdomain=yes name=\ + mikrots.vados.ru type=A +add address=10.30.30.30 comment=MILLER name=miller.vados.ru type=A +add address=10.30.30.30 name=MILLER-LEN type=A +add address=10.30.30.30 name=miller type=A +add address=1.0.0.1 comment="CloudFlare-doh IPv4 2" disabled=yes name=\ + security.cloudflare-dns.com type=A +add address=1.1.1.1 comment="CloudFlare-doh IPv4 1" disabled=yes name=\ + security.cloudflare-dns.com type=A +add address=9.9.9.9 comment="Cuad9-dns IPv4 1" disabled=yes name=\ + dns.quad9.net type=A +add address=149.112.112.112 comment="Cuad9-dns IPv4 2" disabled=yes name=\ + dns.quad9.net type=A +add address=195.133.25.16 comment="DNS Comss" disabled=yes name=dns.comss.one \ + type=A +add address=10.30.30.81 comment=LocalNet name=MikroVados type=A +add address=10.30.30.10 name=MikroNUC type=A +add address=10.30.30.86 disabled=yes name=MikroYellow type=A +add address=10.30.30.85 disabled=yes name=MikroMic type=A +add address=10.30.30.95 name=VADOS-NUC type=A +add address=10.30.30.98 name=VADOS-LEN type=A +add address=91.192.189.2 comment=Unet-DNS disabled=yes name=ns1.unet.ws type=\ + A +add address=10.30.30.20 disabled=yes name=garage type=A +/ip firewall address-list +add address=91.215.120.206 list=TRUSTED +add address=185.71.70.78 disabled=yes list=TRUSTED +add address=176.53.203.171 disabled=yes list=TRUSTED +add address=176.53.203.170 disabled=yes list=TRUSTED +add address=176.53.202.194 disabled=yes list=TRUSTED +add address=8.8.4.0/24 list=YouTube +add address=8.8.8.0/24 list=YouTube +add address=8.34.208.0/20 list=YouTube +add address=8.35.192.0/20 list=YouTube +add address=23.236.48.0/20 list=YouTube +add address=23.251.128.0/19 list=YouTube +add address=34.0.0.0/15 list=YouTube +add address=34.2.0.0/16 list=YouTube +add address=34.3.0.0/23 list=YouTube +add address=34.3.3.0/24 list=YouTube +add address=34.3.4.0/24 list=YouTube +add address=34.3.8.0/21 list=YouTube +add address=34.3.16.0/20 list=YouTube +add address=34.3.32.0/19 list=YouTube +add address=34.3.64.0/18 list=YouTube +add address=34.4.0.0/14 list=YouTube +add address=34.8.0.0/13 list=YouTube +add address=34.16.0.0/12 list=YouTube +add address=34.32.0.0/11 list=YouTube +add address=34.64.0.0/10 list=YouTube +add address=34.128.0.0/10 list=YouTube +add address=35.184.0.0/13 list=YouTube +add address=35.192.0.0/14 list=YouTube +add address=35.196.0.0/15 list=YouTube +add address=35.198.0.0/16 list=YouTube +add address=35.199.0.0/17 list=YouTube +add address=35.199.128.0/18 list=YouTube +add address=35.200.0.0/13 list=YouTube +add address=35.208.0.0/12 list=YouTube +add address=35.224.0.0/12 list=YouTube +add address=35.240.0.0/13 list=YouTube +add address=57.140.192.0/18 list=YouTube +add address=64.15.112.0/20 list=YouTube +add address=64.233.160.0/19 list=YouTube +add address=66.22.228.0/23 list=YouTube +add address=66.102.0.0/20 list=YouTube +add address=66.249.64.0/19 list=YouTube +add address=70.32.128.0/19 list=YouTube +add address=72.14.192.0/18 list=YouTube +add address=74.125.0.0/16 list=YouTube +add address=104.154.0.0/15 list=YouTube +add address=104.196.0.0/14 list=YouTube +add address=104.237.160.0/19 list=YouTube +add address=107.167.160.0/19 list=YouTube +add address=107.178.192.0/18 list=YouTube +add address=108.59.80.0/20 list=YouTube +add address=108.170.192.0/18 list=YouTube +add address=108.177.0.0/17 list=YouTube +add address=130.211.0.0/16 list=YouTube +add address=136.22.160.0/20 list=YouTube +add address=136.22.176.0/21 list=YouTube +add address=136.22.184.0/23 list=YouTube +add address=136.22.186.0/24 list=YouTube +add address=142.250.0.0/15 list=YouTube +add address=146.148.0.0/17 list=YouTube +add address=152.65.208.0/22 list=YouTube +add address=152.65.214.0/23 list=YouTube +add address=152.65.218.0/23 list=YouTube +add address=152.65.222.0/23 list=YouTube +add address=152.65.224.0/19 list=YouTube +add address=162.120.128.0/17 list=YouTube +add address=162.216.148.0/22 list=YouTube +add address=162.222.176.0/21 list=YouTube +add address=172.110.32.0/21 list=YouTube +add address=172.217.0.0/16 list=YouTube +add address=172.253.0.0/16 list=YouTube +add address=173.194.0.0/16 list=YouTube +add address=173.255.112.0/20 list=YouTube +add address=192.158.28.0/22 list=YouTube +add address=192.178.0.0/15 list=YouTube +add address=193.186.4.0/24 list=YouTube +add address=199.36.154.0/23 list=YouTube +add address=199.36.156.0/24 list=YouTube +add address=199.192.112.0/22 list=YouTube +add address=199.223.232.0/21 list=YouTube +add address=207.223.160.0/20 list=YouTube +add address=208.65.152.0/22 list=YouTube +add address=208.68.108.0/22 list=YouTube +add address=208.81.188.0/22 list=YouTube +add address=208.117.224.0/19 list=YouTube +add address=209.85.128.0/17 list=YouTube +add address=216.58.192.0/19 list=YouTube +add address=216.73.80.0/20 list=YouTube +add address=216.239.32.0/19 list=YouTube +add address=2ip.ru list=Test-IP +add address=wg.vados.ru comment=VDSIna list=TRUSTED +add address=10.8.0.0/24 comment="WG VDSIna" disabled=yes list=TRUSTED +add address=ikus.pesc.ru comment=ikus.pesc.ru list=NoVdsina +add address=love.ru comment=love.ru list=NoVdsina +add address=gosuslugi.ru comment=gosuslugi.ru list=NoVdsina +add address=pochta.ru comment=pochta.ru list=NoVdsina +add address=vdsina.com list=NoVdsina +add address=vdsina.ru list=NoVdsina +add address=youla.ru list=NoVdsina +add address=kupiprodai.ru list=NoVdsina +add address=myip.ru list=Test-IP +add address=geoip.co list=NoVdsina +add address=comp-lenta.ru list=NoVdsina +add address=128.85.102.70 comment=update.microsoft.com list=\ + windowsupdateslist +add address=wu-b-net.trafficmanager.net. comment=\ + ctldl.windowsupdate.com.delivery.microsoft.com list=windowsupdateslist +add address=ctldl.windowsupdate.com.delivery.microsoft.com. comment=\ + ctldl.windowsupdate.com list=windowsupdateslist +add address=redir.update.msft.com.trafficmanager.net. comment=\ + update.microsoft.com list=windowsupdateslist +add address=geo.prod.do.dsp.trafficmanager.net. comment=\ + geo.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=13.74.139.29 comment=array605.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=kv801.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + kv801.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=e12437.d.akamaiedge.net. comment=\ + kv801.prod.do.dsp.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=displaycatalog-rp.md.mp.microsoft.com.akadns.net. comment=\ + displaycatalog.mp.microsoft.com list=windowsupdateslist +add address=rp-consumer-prod-displaycatalog-geomap.trafficmanager.net. \ + comment=displaycatalog-rp.md.mp.microsoft.com.akadns.net list=\ + windowsupdateslist +add address=sls.update.microsoft.com. comment=slscr.update.microsoft.com \ + list=windowsupdateslist +add address=glb.sls.prod.dcat.dsp.trafficmanager.net. comment=\ + sls.update.microsoft.com list=windowsupdateslist +add address=fe3.delivery.mp.microsoft.com. comment=\ + fe3cr.delivery.mp.microsoft.com list=windowsupdateslist +add address=glb.cws.prod.dcat.dsp.trafficmanager.net. comment=\ + fe3.delivery.mp.microsoft.com list=windowsupdateslist +add address=fe2cr.update.msft.com.trafficmanager.net. comment=\ + fe2cr.update.microsoft.com list=windowsupdateslist +add address=download.windowsupdate.com.delivery.microsoft.com. comment=\ + download.windowsupdate.com list=windowsupdateslist +add address=wu-f-net.trafficmanager.net. comment=\ + download.windowsupdate.com.delivery.microsoft.com list=windowsupdateslist +add address=tsfe.trafficmanager.net. comment=\ + tsfe.trafficshaping.dsp.mp.microsoft.com list=windowsupdateslist +add address=edge.ds-c7110-microsoft.global.dns.qwilted-cds.cqloud.com. \ + comment=9.au.download.windowsupdate.com list=windowsupdateslist +add address=52.158.227.125 comment=array812.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=au.download.windowsupdate.com.delivery.microsoft.com. comment=\ + au.download.windowsupdate.com list=windowsupdateslist +add address=a767.dspw65.akamai.net. comment=\ + download.windowsupdate.com.edgesuite.net list=windowsupdateslist +add address=52.151.58.88 comment=array810.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=consumer-licensing-aks2aks.md.mp.microsoft.com.akadns.net. \ + comment=licensing.mp.microsoft.com list=windowsupdateslist +add address=licensing-prod-frontdoor-geomap.trafficmanager.net. comment=\ + consumer-licensing-aks2aks.md.mp.microsoft.com.akadns.net list=\ + windowsupdateslist +add address=star.f.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.f.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=cdp-f-nlu-net.trafficmanager.net. comment=\ + star.f.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=star.b.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.b.tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=cdp-f-tlu-net.trafficmanager.net. comment=\ + star.b.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=storeedgefd.xbetservices.akadns.net. comment=\ + storeedgefd.dsx.mp.microsoft.com list=windowsupdateslist +add address=\ + storeedgefd.dsx.mp.microsoft.com.edgekey.net.globalredir.akadns.net. \ + comment=storeedgefd.dsx.mp.microsoft.com.edgekey.net list=\ + windowsupdateslist +add address=e16646.dscg.akamaiedge.net. comment=\ + storeedgefd.dsx.mp.microsoft.com.edgekey.net.globalredir.akadns.net list=\ + windowsupdateslist +add address=40.69.76.189 comment=array621.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=bg.microsoft.map.fastly.net. comment=\ + 3.au.download.windowsupdate.com list=windowsupdateslist +add list=windowsupdateslist +add address=40.69.75.33 comment=array613.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=star.sf.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedgeextensions.sf.tlu.dl.delivery.mp.microsoft.com list=\ + windowsupdateslist +add address=cdp-f-ssl-tlu-net.trafficmanager.net. comment=\ + star.sf.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=a2033.dscd.akamai.net. comment=\ + star.sb.tlu.dl.delivery.mp.microsoft.com.edgesuite.net list=\ + windowsupdateslist +add address=13.74.138.242 comment=array602.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=dl.delivery.mp.microsoft.com.delivery.microsoft.com. comment=\ + dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=dcat-f-nlu-net.trafficmanager.net. comment=\ + dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=1d.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=1D.tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=dcat-b-tlu-net.trafficmanager.net. comment=\ + 1d.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=a122.dscg3.akamai.net. comment=\ + tlu.dl.delivery.mp.microsoft.com.edgesuite.net list=windowsupdateslist +add address=geover.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + geover.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=e10370.d.akamaiedge.net. comment=\ + geover.prod.do.dsp.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=52.232.182.21 comment=array511.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.70.77.95 comment=array519.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=a1683.dscd.akamai.net. comment=\ + dl.delivery.mp.microsoft.com-c.edgesuite.net list=windowsupdateslist +add address=consumer-collections-aks2eap.md.mp.microsoft.com.akadns.net. \ + comment=collections.mp.microsoft.com list=windowsupdateslist +add address=a1847.dscd.akamai.net. comment=\ + wildcard.f.tlu.dl.delivery.mp.microsoft.com.edgesuite.net list=\ + windowsupdateslist +add address=13.74.142.6 comment=array607.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=au.download.windowsupdate.com.edgesuite.net. comment=\ + 2.au.download.windowsupdate.com list=windowsupdateslist +add address=a767.dscg3.akamai.net. comment=\ + au.download.windowsupdate.com.edgesuite.net list=windowsupdateslist +add address=52.156.123.84 comment=array818.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.78.254 comment=array610.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=4.au.download.windowsupdate.com.delivery.microsoft.com. comment=\ + 4.au.download.windowsupdate.com list=windowsupdateslist +add address=cl-glcb907925.gcdn.co. comment=\ + 4.au.download.windowsupdate.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=52.167.167.111 comment=array510.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=40.69.70.43 comment=array623.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=tsfe.trafficshaping.dsp.mp.microsoft.com.delivery.microsoft.com. \ + comment=tsfe.trafficshaping.dsp.mp.microsoft.com list=windowsupdateslist +add address=ts-frontend.trafficmanager.net. comment=\ + tsfe.trafficshaping.dsp.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=40.69.75.248 comment=array616.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.74.84 comment=array615.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=sds.trafficmanager.net. comment=software-download.microsoft.com \ + list=windowsupdateslist +add address=40.69.74.80 comment=array611.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.175.242.182 comment=array804.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=dlc-shim.trafficmanager.net. comment=download.microsoft.com list=\ + windowsupdateslist +add address=e12671.dscd.akamaiedge.net. comment=\ + download.microsoft.com.edgekey.net list=windowsupdateslist +add address=20.187.55.34 comment=array808.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.167.231 comment=array513.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=52.167.222.13 comment=array518.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=kv601.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + kv601.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=e12358.d.akamaiedge.net. comment=\ + kv601.prod.do.dsp.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. comment=\ + tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=dcat-f-tlu-net.trafficmanager.net. comment=\ + tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=a1856.dspw65.akamai.net. comment=\ + tlu.dl.delivery.mp.microsoft.com-c.edgesuite.net list=windowsupdateslist +add address=52.167.164.252 comment=array506.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=13.74.137.54 comment=array601.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=kv501.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + kv501.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=collections-prod-frontdoor-geomap.trafficmanager.net. comment=\ + consumer-collections-aks2eap.md.mp.microsoft.com.akadns.net list=\ + windowsupdateslist +add address=52.167.160.173 comment=array509.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=40.70.77.148 comment=array516.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.200.126 comment=array507.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=52.167.167.59 comment=array508.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.76.172 comment=array620.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=smtp.mail.me.com comment=smtp.mail.me.com list=NoVdsina +add address=40.70.76.5 comment=array514.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.186.119 comment=array618.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.177.176.186 comment=array520.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=40.70.76.27 comment=array515.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.160.43 comment=array503.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.110 comment=array817.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.142.8 comment=array614.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.138.254 comment=array603.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.167.96 comment=array501.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.76.215 comment=array622.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.143.200 comment=array606.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.111 comment=array818.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.112 comment=array819.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.137.53 comment=array608.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.98 comment=array805.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.96 comment=array803.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.104 comment=array811.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.101 comment=array808.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.99 comment=array806.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.17 comment=array802.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.97 comment=array804.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.103 comment=array810.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.16 comment=array801.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.70.77.177 comment=array517.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.164.84 comment=array512.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.105 comment=array812.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.107 comment=array814.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.232.161.82 comment=array502.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.108 comment=array815.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.161.91 comment=array505.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.102 comment=array809.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.109 comment=array816.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.106 comment=array813.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.117 comment=array622.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.107 comment=array612.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.108 comment=array613.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.105 comment=array610.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.113 comment=array618.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.116 comment=array621.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.98 comment=array603.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.114 comment=array619.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.111 comment=array616.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.97 comment=array602.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.106 comment=array611.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.110 comment=array615.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.119 comment=array624.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.135 comment=array514.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.97 comment=array503.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.99 comment=array604.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.61 comment=array504.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.131 comment=array510.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.132 comment=array511.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.96 comment=array601.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.104 comment=array609.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.138 comment=array517.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.60 comment=array501.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.139 comment=array518.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.118 comment=array623.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.113 comment=array820.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.103 comment=array608.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.100 comment=array807.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.129 comment=array508.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.134 comment=array513.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.62 comment=array505.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.137 comment=array516.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.101 comment=array606.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.96 comment=array502.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.130 comment=array509.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.112 comment=array617.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.100 comment=array605.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.115 comment=array620.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.136 comment=array515.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=purchase.md.mp.microsoft.com.akadns.net. comment=\ + purchase.mp.microsoft.com list=windowsupdateslist +add address=e110219.a.akamaiedge.net. comment=\ + purchase.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=72.153.5.140 comment=array519.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.141 list=windowsupdateslist +add address=72.153.5.63 comment=array506.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.128 comment=array507.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.102 comment=array607.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=storesdk.xbetservices.akadns.net. comment=\ + storesdk.dsx.mp.microsoft.com list=windowsupdateslist +add address=e16646.g.akamaiedge.net. comment=\ + storesdk.dsx.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=software-download.microsoft.com.delivery.microsoft.com. comment=\ + software-download.microsoft.com list=windowsupdateslist +add address=download.prss.microsoft.com.akamaized.net. comment=\ + software-download.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.109 comment=array614.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.133 comment=array512.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=213.180.192.0/19 comment=YandexDisk list=NoVdsina +add address=87.250.250.50 comment=YandexDisk list=NoVdsina +add address=consumer-prod-bcfd-v7-afd.trafficmanager.net. comment=\ + displaycatalog.mp.microsoft.com list=windowsupdateslist +add address=purchase-europeeap.md.mp.microsoft.com.akadns.net. comment=\ + purchase.md.mp.microsoft.com.akadns.net list=windowsupdateslist +add address=ne-az-conspurchase-mp.trafficmanager.net. comment=\ + purchase-europeeap.md.mp.microsoft.com.akadns.net list=windowsupdateslist +add address=storeedgefd.dsx.mp.microsoft.com.edgekey.net. comment=\ + cdn.storeedgefd.dsx.mp.microsoft.com list=windowsupdateslist +add address=star.f.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.f.tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=a1996.dscd.akamai.net. comment=\ + wildcard.f.dl.delivery.mp.microsoft.com.edgesuite.net list=\ + windowsupdateslist +add address=cp801.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + cp801.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=glb.tas02.sls-prod.dcat.dsp.mp.microsoft.com. comment=\ + tas02.sls.update.microsoft.com list=windowsupdateslist +add address=cp601.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + cp601.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=star.sb.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedgeextensions.sb.tlu.dl.delivery.mp.microsoft.com list=\ + windowsupdateslist +add address=star.sf.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.sf.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=cdp-f-ssl-nlu-net.trafficmanager.net. comment=\ + star.sf.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=tm-pifd-prod-gbl-weight.trafficmanager.net. comment=\ + paymentinstruments.mp.microsoft.com list=windowsupdateslist +add address=10.30.30.33 comment=amster list=NoWG +add address=10.30.30.10 comment=MikroNUC disabled=yes list=NoWG +add address=89.110.101.17 comment=!!!VDSINA!!!! disabled=yes list=NoWG +add address=wg.vados.ru comment=VDSina-vpn list=VDSina +add address=80.85.244.9 list=TRUSTED +add address=disc801.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + disc801.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=185.71.70.78 disabled=yes list=TRUSTED +add address=176.53.203.171 disabled=yes list=TRUSTED +add address=176.53.203.170 disabled=yes list=TRUSTED +add address=176.53.202.194 disabled=yes list=TRUSTED +add address=89.110.101.17 comment="!!!VDSina Server!!!" list=NoVdsina +add address=dns.quad9.net comment=Quad9-dns disabled=yes list=Trusted-DNS +add address=dns.google comment=Google-dns list=Trusted-DNS +add address=security.cloudflare-dns.com comment=CloudFlare-dns list=\ + Trusted-DNS +add address=time.google.com comment=Google-time list=Trusted-NTP +add address=0.ru.pool.ntp.org comment=ru.pool.ntp.org disabled=yes list=\ + Trusted-NTP +add address=1.ru.pool.ntp.org disabled=yes list=Trusted-NTP +add address=2.ru.pool.ntp.org comment=2.ru.pool.ntp.org disabled=yes list=\ + Trusted-NTP +add address=3.ru.pool.ntp.org comment=3.ru.pool.ntp.org disabled=yes list=\ + Trusted-NTP +add address=cp.vdsina.com comment=VDSina-vpn list=VDSina +add address=vdsina.ru comment=VDSina-vpn list=VDSina +add address=ns.vados.ru comment=VDSina-vpn list=VDSina +add address=vdsina.com comment=VDSina-vpn list=VDSina +add address=cp.vdsina.ru comment=VDSina-vpn list=VDSina +add address=ns1.vdsina.ru comment=VDSina-vpn list=VDSina +add address=10.30.30.0/24 comment="Local Network" list=Local-Net +add address=10.8.0.0/24 comment="WireGuard Network" list=Local-Net +add address=146.120.64.208/29 comment="External Net" list=Unet +add address=esia.gosuslugi.ru comment=esia.gosuslugi.ru list=NoVdsina +add address=lk.gosuslugi.ru comment=lk.gosuslugi.ru list=NoVdsina +add address=trudvsem.ru comment=trudvsem.ru list=NoVdsina +add address=10.30.30.30 comment=miller disabled=yes list=NoWG +add address=10.30.30.33 comment="Direct connet to DNS servers" list=\ + Direct-DNS +add address=109.107.177.79 list=VPN-Server +add address=10.0.0.0/8 list=RFC1918 +add address=172.16.0.0/12 list=RFC1918 +add address=192.168.0.0/16 list=RFC1918 +add address=8.8.4.4 disabled=yes list=to_vpn +add address=myip.ru disabled=yes list=to_vpn +add address=10.30.30.98 disabled=yes list=to_vpn +/ip firewall filter +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=12h chain=input comment="block honeypot DNS" \ + connection-state=new dst-port=53 in-interface=Unet-ether1 protocol=udp \ + src-address-list=!Trusted-DNS +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input connection-state=new dst-port=53 \ + in-interface=Unet-ether1 protocol=tcp src-address-list=!Trusted-DNS +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input comment=\ + "block honeypot ssh rdp winbox" connection-state=new dst-port=22,8291 \ + in-interface=Unet-ether1 protocol=tcp src-address-list=!TRUSTED +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input comment="block honeypot asterisk" \ + connection-state=new dst-port=5060 in-interface=Unet-ether1 protocol=udp +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input comment="block honeypot NTP" \ + connection-state=new dst-port=123 in-interface=Unet-ether1 protocol=udp +add action=fasttrack-connection chain=forward comment=\ + "FastTrack for non-VPN connections" connection-mark=!VPN_No_FastTrack \ + connection-state=established,related,new disabled=yes +add action=accept chain=input dst-port=29185 in-interface=Unet-ether1 \ + protocol=tcp +add action=accept chain=input dst-port=29185 in-interface=Unet-ether1 \ + protocol=udp +add action=accept chain=input comment="tcp Accept input DNS queries from LAN" \ + dst-port=53 protocol=tcp src-address=10.30.30.0/24 +add action=accept chain=input comment="udp Accept input DNS queries" \ + dst-port=53 protocol=udp src-address=10.30.30.0/24 +add action=accept chain=output comment="accept WireGuard" disabled=yes \ + dst-address-list=VPN-Server out-interface=Unet-ether1 protocol=udp \ + src-port=13231 +add action=accept chain=output disabled=yes dst-address-list=VPN-Server \ + dst-port=29185 out-interface=Unet-ether1 protocol=udp +add action=fasttrack-connection chain=forward comment="defconf: fasttrack" \ + connection-state=established,related,new disabled=yes routing-mark=\ + !To-VPN +add action=accept chain=output disabled=yes protocol=tcp src-port=443 +add action=accept chain=output disabled=yes src-address=127.0.0.1 +add action=accept chain=forward comment="defconf: accept in ipsec policy" \ + disabled=yes ipsec-policy=in,ipsec +add action=accept chain=forward comment="defconf: accept out ipsec policy" \ + disabled=yes ipsec-policy=out,ipsec +add action=accept chain=forward comment=\ + "defconf: accept established,related, untracked" connection-state=\ + established,related,untracked disabled=yes +add action=drop chain=forward comment="defconf: drop invalid" \ + connection-state=invalid in-interface=Unet-ether1 src-address=\ + !10.30.30.0/24 +add action=drop chain=forward comment=\ + "defconf: drop all from WAN not DSTNATed" connection-nat-state=!dstnat \ + connection-state=new in-interface=Unet-ether1 src-address-list=!VDSina +add action=accept chain=input in-interface=Unet-ether1 protocol=icmp \ + src-address-list=VDSina +add action=accept chain=input connection-state=established +add action=accept chain=input connection-state=related +/ip firewall mangle +add action=accept chain=prerouting comment=RFC1918 dst-address-list=RFC1918 \ + in-interface-list=!WAN +add action=mark-routing chain=prerouting comment="Mark Routing main for NoWG" \ + new-routing-mark=main passthrough=no src-address-list=NoWG +add action=mark-routing chain=prerouting comment=\ + "Mark Routing main for NoVdsina" disabled=yes dst-address-list=NoVdsina \ + new-routing-mark=main passthrough=no src-address=10.30.30.0/24 +add action=mark-connection chain=prerouting comment=\ + "Transit for address-list \"to_vpn\"" connection-mark=no-mark disabled=\ + yes dst-address-list=to_vpn in-interface-list=!WAN new-connection-mark=\ + to-vpn-conn routing-mark=!main +add action=mark-routing chain=prerouting comment=\ + "Find routing over table r_to_vpn" connection-mark=to-vpn-conn disabled=\ + yes in-interface-list=!WAN new-routing-mark=r_to_vpn +add action=mark-routing chain=prerouting comment=\ + "Mark Routing main for VDSina" dst-address-list=VPN-Server \ + new-routing-mark=main passthrough=no src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=\ + "Mark Routing main for MikroNuc" disabled=yes dst-address-list=AWG-Proxy \ + new-routing-mark=main src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=\ + "!!!ERROR Mark Routing main for Unet!!!" disabled=yes dst-address-list=\ + Unet new-routing-mark=main passthrough=no +add action=mark-routing chain=prerouting comment="Test-IP sites" disabled=yes \ + dst-address=!10.30.30.0/24 dst-address-list=Test-IP new-routing-mark=\ + To-VPN src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=FuckRKN disabled=yes \ + dst-address=!10.30.30.0/24 dst-address-list=RKN-dnsLookup \ + new-routing-mark=To-VPN passthrough=no src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment="WG for all" \ + dst-address-list=!NoVdsina new-routing-mark=To-VPN src-address=\ + 10.30.30.0/24 +add action=change-mss chain=forward comment="MSS on WG" new-mss=1380 \ + out-interface=WG protocol=tcp routing-mark=To-VPN tcp-flags=syn tcp-mss=\ + 1381-65535 +add action=mark-connection chain=prerouting comment=\ + "Mark VPN connections to exclude from FastTrack" disabled=yes \ + new-connection-mark=VPN_No_FastTrack routing-mark=To-VPN +add action=mark-routing chain=prerouting comment="VPN Network" disabled=yes \ + dst-address=10.8.0.0/24 new-routing-mark=To-VPN src-address=10.30.30.0/24 +add action=mark-connection chain=output comment="For this router" \ + connection-mark=no-mark disabled=yes dst-address-list=to_vpn \ + new-connection-mark=to-vpn-conn-local +add action=mark-routing chain=output comment="For this router" \ + connection-mark=to-vpn-conn-local disabled=yes new-routing-mark=r_to_vpn +/ip firewall nat +add action=redirect chain=dstnat comment="udp Redirect DNS in NAT" disabled=\ + yes dst-port=53 protocol=udp src-address=10.30.30.0/24 src-address-list=\ + !NoWG to-addresses=10.30.30.33 to-ports=53 +add action=redirect chain=dstnat comment="tcp Redirect DNS in NAT" disabled=\ + yes dst-port=53 protocol=tcp src-address=10.30.30.0/24 src-address-list=\ + !NoWG to-addresses=10.30.30.33 to-ports=53 +add action=masquerade chain=srcnat comment=\ + "Nat Addresses for routing mark r_to_vpn" disabled=yes routing-mark=\ + r_to_vpn +add action=dst-nat chain=dstnat comment=\ + "53/tcp dst-nat to amster.loc. From VDSina Server" dst-port=53 \ + in-interface=Unet-ether1 protocol=tcp src-address-list=VPN-Server \ + to-addresses=10.30.30.33 to-ports=53 +add action=dst-nat chain=dstnat comment=\ + "53/udp dst-nat to amster.loc. From VDSina Server" dst-port=53 protocol=\ + udp src-address-list=VPN-Server to-addresses=10.30.30.33 to-ports=53 +add action=dst-nat chain=dstnat disabled=yes dst-port=44333 in-interface=\ + Unet-ether1 protocol=udp src-address=!10.30.30.0/24 to-addresses=\ + 10.30.30.33 to-ports=44333 +add action=masquerade chain=srcnat comment=WG dst-address-list=!NoVdsina \ + routing-mark=To-VPN +add action=masquerade chain=srcnat comment="Default NAT" out-interface=\ + Unet-ether1 routing-mark=!To-VPN src-address=10.30.30.0/24 +add action=dst-nat chain=dstnat dst-port=44044 in-interface=Unet-ether1 \ + protocol=udp src-address=109.107.177.79 to-addresses=10.30.30.33 \ + to-ports=44044 +add action=dst-nat chain=dstnat comment=amster.vados.ruhttps dst-port=443 \ + fragment=no in-interface=Unet-ether1 protocol=tcp routing-mark=main \ + to-addresses=10.30.30.33 to-ports=443 +add action=dst-nat chain=dstnat comment="amster.vados.ru For Certbot HTTP" \ + dst-port=80 in-interface=Unet-ether1 protocol=tcp to-addresses=\ + 10.30.30.33 to-ports=80 +add action=dst-nat chain=dstnat comment="amster.vados.ru 3x-ui" disabled=yes \ + dst-port=2904 in-interface=Unet-ether1 protocol=tcp to-addresses=\ + 10.30.30.33 to-ports=2904 +add action=dst-nat chain=dstnat comment=reg.vados.ru disabled=yes dst-port=\ + 5000 in-interface=Unet-ether1 protocol=tcp to-addresses=10.30.30.33 \ + to-ports=5000 +add action=dst-nat chain=dstnat comment="Docker swarm" disabled=yes dst-port=\ + 2377 in-interface=Unet-ether1 protocol=tcp to-addresses=10.30.30.33 \ + to-ports=2377 +add action=dst-nat chain=dstnat comment=npm dst-port=81 in-interface=\ + Unet-ether1 protocol=tcp routing-mark=To-VPN to-addresses=10.30.30.33 \ + to-ports=81 +add action=dst-nat chain=dstnat comment=SSH dst-port=22 in-interface=\ + Unet-ether1 protocol=tcp src-address-list=TRUSTED to-addresses=\ + 10.30.30.33 to-ports=22 +add action=dst-nat chain=dstnat comment="portainer agent" disabled=yes \ + dst-port=2377 in-interface=Unet-ether1 protocol=tcp to-addresses=\ + 10.30.30.33 to-ports=2377 +add action=dst-nat chain=dstnat disabled=yes dst-port=22 in-interface=\ + Unet-ether1 protocol=tcp routing-mark=!To-VPN to-addresses=10.30.30.33 \ + to-ports=22 +add action=dst-nat chain=dstnat comment="Yandex.DNS Family" disabled=yes \ + dst-port=53 in-interface=Bridge-LAN protocol=udp src-address-list=KIDS \ + time=6h-15m,sun,mon,tue,wed,thu,fri,sat to-addresses=77.88.8.3 +add action=dst-nat chain=dstnat disabled=yes dst-port=53 in-interface=\ + Bridge-LAN protocol=tcp src-address-list=KIDS time=\ + 6h-15m,sun,mon,tue,wed,thu,fri,sat to-addresses=77.88.8.3 +/ip firewall raw +add action=drop chain=prerouting comment="Drop Honeypot Hackers" \ + in-interface=Unet-ether1 src-address-list="Honeypot Hacker" +add action=accept chain=prerouting comment="Trusted DNS" disabled=yes \ + in-interface-list=WAN src-address-list=Trusted-DNS +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + extras.getpagespeed.com content=extras.getpagespeed.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=linux.duke.edu \ + content=linux.duke.edu disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=youtube.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + googleapis.com disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=ggpht.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=youtu.be \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=ytimg.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + googlevideo.com disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=gstatic.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + googleusercontent.com disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + nhacmp3youtube.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=rutube.ru \ + content=rutube.ru disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment="me.com raw" \ + content=me.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "icloud.com raw" content=icloud.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "smtp.mail.me.com raw" content=smtp.mail.me.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "smtp.mail.me.com.akadns.net raw" content=smtp.mail.me.com.akadns.net \ + disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "api.db-ip.com raw" content=api.db-ip.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + esia.gosuslugi.ru content=esia.gosuslugi.ru disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=gosuslugi.ru \ + content=gosuslugi.ru disabled=yes +/ip firewall service-port +set ftp disabled=yes +set tftp disabled=yes +set irc disabled=no +set sip disabled=yes +set pptp disabled=yes +set rtsp disabled=no +/ip ipsec settings +set accounting=no +/ip ipsec policy +set 0 disabled=yes dst-address=0.0.0.0/0 src-address=0.0.0.0/0 +/ip ipsec profile +set [ find default=yes ] dpd-interval=2m dpd-maximum-failures=5 +/ip proxy +set cache-administrator="" max-cache-size=none port=80,443 src-address=\ + 0.0.0.0 +/ip route +add check-gateway=none comment=WG-Default disabled=no distance=1 dst-address=\ + 0.0.0.0/0 gateway=WG routing-table=To-VPN scope=30 target-scope=10 +/ip service +set ftp disabled=yes +set telnet disabled=yes +set reverse-proxy disabled=yes +set ssh address=10.30.30.0/24 port=2222 +set winbox address=10.30.30.0/24 +set api address=10.30.30.0/24 +set api-ssl address=10.30.30.0/24 disabled=yes +set www-ssl certificate=Root-YE port=18443 +set www address=10.30.30.0/24 disabled=yes port=18880 +/ip service webserver +set acme-plain=no crl-plain=no graphs-plain=no graphs-secure=no index-plain=\ + no index-secure=no rest-secure=no scep-plain=no webfig-plain=no \ + webfig-secure=no +/ip ssh +set forwarding-enabled=both publickey-authentication-options=verify-required +/ip upnp +set enabled=yes +/ip upnp interfaces +add interface=Bridge-LAN type=internal +add forced-ip=146.120.64.211 interface=Unet-ether1 type=external +add forced-ip=109.107.177.79 interface=*E type=external +add interface=lo type=internal +add interface=wlan2 type=internal +add interface=wlan1 type=internal +/ipv6 dhcp-client +add add-default-route=yes disabled=yes interface=*F rapid-commit=no request=\ + info use-interface-duid=yes validate-server-duid=no +/ipv6 dhcp-server option sets +add name=domain-search-set options=domain-search-list +add name="=\"Client ID\"" options=HostName +/ipv6 firewall address-list +add address=::/128 comment="defconf: unspecified address" list=bad_ipv6 +add address=::1/128 comment="defconf: lo" list=bad_ipv6 +add address=fec0::/10 comment="defconf: site-local" list=bad_ipv6 +add address=::ffff:0.0.0.0/96 comment="defconf: ipv4-mapped" list=bad_ipv6 +add address=::/96 comment="defconf: ipv4 compat" list=bad_ipv6 +add address=100::/64 comment="defconf: discard only " list=bad_ipv6 +add address=2001:db8::/32 comment="defconf: documentation" list=bad_ipv6 +add address=2001:10::/28 comment="defconf: ORCHID" list=bad_ipv6 +add address=3ffe::/16 comment="defconf: 6bone" list=bad_ipv6 +/ipv6 firewall filter +add action=accept chain=input comment=\ + "defconf: accept established,related,untracked" connection-state=\ + established,related,untracked +add action=drop chain=input comment="defconf: drop invalid" connection-state=\ + invalid +add action=accept chain=input comment="defconf: accept ICMPv6" protocol=\ + icmpv6 +add action=accept chain=input comment="defconf: accept UDP traceroute" \ + dst-port=33434-33534 protocol=udp +add action=accept chain=input comment=\ + "defconf: accept DHCPv6-Client prefix delegation." dst-port=546 protocol=\ + udp src-address=fe80::/10 +add action=accept chain=input comment="defconf: accept IKE" dst-port=500,4500 \ + protocol=udp +add action=accept chain=input comment="defconf: accept ipsec AH" protocol=\ + ipsec-ah +add action=accept chain=input comment="defconf: accept ipsec ESP" protocol=\ + ipsec-esp +add action=accept chain=input comment=\ + "defconf: accept all that matches ipsec policy" ipsec-policy=in,ipsec +add action=drop chain=input comment=\ + "defconf: drop everything else not coming from LAN" in-interface-list=\ + !LAN +add action=accept chain=forward comment=\ + "defconf: accept established,related,untracked" connection-state=\ + established,related,untracked +add action=drop chain=forward comment="defconf: drop invalid" \ + connection-state=invalid +add action=drop chain=forward comment=\ + "defconf: drop packets with bad src ipv6" src-address-list=bad_ipv6 +add action=drop chain=forward comment=\ + "defconf: drop packets with bad dst ipv6" dst-address-list=bad_ipv6 +add action=drop chain=forward comment="defconf: rfc4890 drop hop-limit=1" \ + hop-limit=equal:1 protocol=icmpv6 +add action=accept chain=forward comment="defconf: accept ICMPv6" protocol=\ + icmpv6 +add action=accept chain=forward comment="defconf: accept HIP" protocol=139 +add action=accept chain=forward comment="defconf: accept IKE" dst-port=\ + 500,4500 protocol=udp +add action=accept chain=forward comment="defconf: accept ipsec AH" protocol=\ + ipsec-ah +add action=accept chain=forward comment="defconf: accept ipsec ESP" protocol=\ + ipsec-esp +add action=accept chain=forward comment=\ + "defconf: accept all that matches ipsec policy" ipsec-policy=in,ipsec +add action=drop chain=forward comment=\ + "defconf: drop everything else not coming from LAN" in-interface-list=\ + !LAN +/ipv6 nd +set [ find default=yes ] advertise-dns=yes disabled=yes interface=Bridge-LAN \ + managed-address-configuration=yes other-configuration=yes pref64=::/64 +/ipv6 nd prefix +add autonomous=no disabled=yes interface=Bridge-LAN on-link=no prefix=none +/ipv6 nd prefix default +set dhcp6-pd-preferred=yes +/ipv6 nd proxy +add disabled=yes interface=lo +/ipv6 nd settings +set router-advertisement-route-distance=100 +/routing settings +set single-process=no +/system clock +set time-zone-name=Europe/Moscow +/system clock manual +set time-zone=+03:00 +/system identity +set name=MikroMaster +/system logging +add action=email disabled=yes regex="* added|changed|removed *" topics=\ + system,info +/system ntp client +set enabled=yes +/system ntp server +set enabled=yes manycast=yes multicast=yes use-local-clock=yes +/system ntp client servers +add address=0.ru.pool.ntp.org +add address=1.ru.pool.ntp.org +add address=2.ru.pool.ntp.org +add address=3.ru.pool.ntp.org +add address=time.google.com disabled=yes +/system package update +set check-certificate=yes-without-crl ip-version=ipv4 +/system scheduler +add comment="Every Day Set Global Environment Variables" name=AM-SetGlobalEnv \ + on-event="/system/script { run AM-GlobalConfig; run AM-GlobalFunc; }" \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Every Day Check For Update Scripts" interval=1d name=\ + AM-ScriptInstallUpdate on-event=\ + ":global ScriptInstallUpdate; \$ScriptInstallUpdate;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Update RKN DNS Looup" disabled=yes interval=20m name=\ + AM-RKN-dnsLookup on-event="/system/script { run AM-RKN-dnsLookup; }" \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +/system watchdog +set watchdog-timer=no +/tool e-mail +set certificate-verification=no from=MikroVados port=587 \ + server=smtp.mail.me.com tls=starttls user=vadossio@icloud.com +/tool mac-server +set allowed-interface-list=LAN +/tool mac-server mac-winbox +set allowed-interface-list=LAN +/tool netwatch +add certificate=DigiCert-Global-Root-CA check-certificate=no comment="doh, doh\ + -url=https://security.cloudflare-dns.com/dns-query, notify, name=CloudFlar\ + e-doh IPv4 1" disabled=yes down-script="" host=1.1.1.1 http-codes="" \ + interval=10s name="CloudFlare-doh IPv4 1" port=443 test-script="" type=\ + https-get up-script="" +add check-certificate=no comment="doh-fallback, doh-url=https://security.cloud\ + flare-dns.com/dns-query, doh-cert=DigiCert Global Root CA, notify, name=Cl\ + oudFlare-doh IPv4" disabled=yes down-script="" host=1.0.0.1 http-codes="" \ + name="CloudFlare-doh IPv4" port=443 test-script="" type=https-get \ + up-script="" +add comment="dns, notify, name=Google-DNS IPv4" disabled=yes dns-server=\ + 8.8.8.8 down-script="" host=dns.google http-codes="" name=\ + "Google-DNS IPv4" test-script="" type=dns up-script="" +add certificate=GTS-Root-R4 check-certificate=yes comment="doh-fallback, doh-u\ + rl=https://dns.google/dns-query, doh-cert=GTS Root R1, notify, name=Google\ + -DOH IPv4" disabled=yes dns-server=8.8.4.4 down-script="" host=dns.google \ + http-codes="" name="Google-DOH IPv4" port=443 record-type=NS src-address=\ + "" test-script="" type=dns up-script="" +add comment="dns-fallback, notify, name=Cuad9-DOH IPv4" disabled=yes \ + dns-server=9.9.9.9 down-script="" host=dns9.quad9.net http-codes="" \ + interval=10s name="Cuad9-DOH IPv4" src-address=146.120.64.211 \ + test-script="" timeout=800ms type=dns up-script="" +add certificate=DigiCert-Assured-ID-Root-CA comment="doh-fallback, doh-url=htt\ + ps://dns.quad9.net/dns-query, name=Cuad9-DOH IPv4" disabled=yes \ + down-script="" host=149.112.112.112 http-codes="" interval=10s name=\ + "Cuad9-DOH IPv4" port=443 test-script="" type=https-get up-script="" +add comment="dns-fallback, notify, name=ns1.unet.ws" disabled=yes \ + down-script="" host=91.192.189.2 http-codes="" interval=10s name=\ + ns1.unet.ws test-script="" type=simple up-script="" +add check-certificate=yes comment=\ + "doh, doh-url=https://vds.vados.ru/vds-query, name=vds.vados.ru" \ + disabled=yes down-script="" host=109.107.177.79 http-codes="" name=\ + vds.vados.ru port=443 test-script="" type=https-get up-script="" +add comment="WireGuard NAT " disabled=yes down-script="/log info \"[WG-Monitor\ + ] WireGuard is NOT running - reverting rules\"\ + \n/ip firewall nat remove [find comment=\"Redirect to Router\"]\ + \n/ip dns set servers=8.8.8.8\ + \n/ip dns cache flush\ + \n/ip route remove [find comment=\"wg-auto-route\"]\ + \n" host=10.8.0.1 http-codes="" interval=15s name=WG test-script="" \ + timeout=5s type=simple up-script="/log info \"[WG-Monitor] WireGuard is RU\ + NNING - applying rules\";\ + \n:local routeFile \"ips.txt\";\ + \n:local routeDefAddr \"0.0.0.0/0\";\ + \n:local routeGateway \"10.8.0.1\";\ + \n:local routeIF \"WG\";\ + \n:local routeTable \"To-VPN\";\ + \n:local routeComment \"wg-auto-route\";\ + \n/ip dns cache flush;\ + \n:if ([:len [/ip firewall nat find comment=\"Redirect to Router\"]] > 0) \ + do={\ + \n /ip firewall nat remove [find comment=\"Redirect to Router\"];\ + \n}\ + \n/ip firewall nat add action=redirect chain=dstnat src-address-list=\"Red\ + irectDNS\" dst-port=53 protocol=udp comment=\"Redirect to Router\";\ + \n/ip dns set servers=\$routeGateway;\ + \n:local content [/file get \$routeFile contents];\ + \n:if ([:len \$content] > 0) do={\ + \n :local line \"\";\ + \n :for i from=0 to=([:len \$content] - 1) do={\ + \n :local char [:pick \$content \$i];\ + \n :if (\$char != \"\\r\" && \$char != \"\\n\") do={\ + \n :set line (\$line . \$char);\ + \n } else={\ + \n :if ([:len \$line] > 5) do={\ + \n :do { /ip route add disabled=no dst-address=\$routeDefAd\ + dr gateway=\$routeIF distance=1 scope=30 target-scope=10 routing-table=\$r\ + outeTable comment=\$routeComment check-gateway=none } on-error={}\ + \n }\ + \n :set line \"\";\ + \n }\ + \n }\ + \n :if ([:len \$line] > 5) do={\ + \n# :do { /ip route add dst-address=\$line gateway=\$routeGateway \ + distance=1 comment=\$routeComment } on-error={}\ + \n :do { /ip route add disabled=no dst-address=\$routeDefAddr ga\ + teway=\$routeIF distance=1 scope=30 target-scope=10 routing-table=\$routeT\ + able comment=\$routeComment check-gateway=none } on-error={}\ + \n }\ + \n}\ + \n:local dotPos [:find \$routeGateway \".\"];\ + \n:if (\$dotPos != \"\") do={\ + \n :local firstOctet [:pick \$routeGateway 0 \$dotPos];\ + \n :local derivedDstAddress (\$firstOctet . \".30.0.0/15\");\ + \n :do { /ip route add dst-address=\$derivedDstAddress gateway=\$routeG\ + ateway distance=1 comment=\$routeComment } on-error={}\ + \n}\ + \n\ + \n" +add comment=WG-Default disabled=yes down-script=\ + "/ip route disable [find comment=\"WG-Default\"];" host=10.8.0.1 \ + interval=15s name=WG packet-count=20 test-script="" thr-loss-count=5 \ + type=icmp up-script="/ip route enable [find comment=\"WG-Default\"];" +/user aaa +set accounting=no diff --git a/MikroMaster/20260711/20260711202002-Config.rsc b/MikroMaster/20260711/20260711202002-Config.rsc new file mode 100644 index 0000000..ddae42e --- /dev/null +++ b/MikroMaster/20260711/20260711202002-Config.rsc @@ -0,0 +1,3843 @@ +# 2026-07-11 20:20:07 by RouterOS 7.23.1 +# software id = A8L5-07YF +# +# model = RB952Ui-5ac2nD +# serial number = D3D50CEDCD38 +/interface bridge +add admin-mac=48:8F:5A:8E:1A:BF auto-mac=no dhcpv6-agent-circuit-id=\ + "\$(INTERFACE):\$(VID)" dhcpv6-agent-remote-id="\$(BRIDGEMAC)" name=\ + Bridge-LAN port-cost-mode=short +/interface ethernet +set [ find default-name=ether1 ] comment=Unet-cable loop-protect=off name=\ + Unet-ether1 rx-flow-control=auto tx-flow-control=auto +set [ find default-name=ether2 ] comment=LAN-2 +set [ find default-name=ether3 ] advertise="10M-baseT-half,10M-baseT-full,100M\ + -baseT-half,100M-baseT-full,1G-baseT-full,1G-baseX" comment=LAN-3 +set [ find default-name=ether4 ] comment=LAN-4 +set [ find default-name=ether5 ] advertise="10M-baseT-half,10M-baseT-full,100M\ + -baseT-half,100M-baseT-full,1G-baseT-full,1G-baseX" comment=LAN-5 \ + loop-protect=on poe-out=off rx-flow-control=auto tx-flow-control=auto +/interface wireless +# managed by CAPsMAN +# channel: 2422/20-Ce/gn(18dBm), SSID: vados, local forwarding +set [ find default-name=wlan1 ] country=belarus disabled=no mode=ap-bridge \ + ssid=vados wireless-protocol=802.11 +# managed by CAPsMAN +# channel: 5200/20-eCee/ac/P(21dBm), SSID: vados5g, local forwarding +set [ find default-name=wlan2 ] country=belarus disabled=no mode=ap-bridge \ + ssid=vados5g wireless-protocol=802.11 +/interface wireguard +add comment=WG listen-port=13231 mtu=1320 name=WG +add comment="WG interface for Amnezia route" disabled=yes listen-port=33033 \ + mtu=1360 name=wg-nuc +/interface list +add include=static name=LAN +add name=Internet +add include=Internet name=WAN +#error exporting "/interface/macsec/profile" (timeout) +/interface wireless security-profiles +set [ find default=yes ] authentication-types=wpa-psk,wpa2-psk mode=\ + dynamic-keys supplicant-identity=MikroTik +/iot wiliot servers +set *1 address=mqtt.us-east-2.prod.wiliot.cloud name="Wiliot US East" +/ip dhcp-server option +add code=12 name=HostName +add code=119 force=yes name=domain-search-list value="0x03'WORKGROUP'0x00" +/ip dhcp-server option sets +add name=HP1505n options=HostName +add name=StartLife1 options=HostName +add name=amsterDev options=HostName +add name=MiTV options=HostName +add name=miller options=HostName +add name=amster options=HostName +add name=garage options=HostName +add name=domain-search-set options=domain-search-list +add name=VADOS-LEN options=HostName +add name=VADOS-NUC options=HostName +add name=MikroNUC options=HostName +add name=master options=HostName +add name=MikroYellow options=HostName +add name="=\"Client ID\"" options=HostName +add name=iVados options=HostName +/ip dns forwarders +add disabled=yes dns-servers=1.0.0.1,1.1.1.1 name=blsp.at verify-doh-cert=no +add comment=Kra disabled=yes dns-servers=1.0.0.1,1.1.1.1 name=slon2.to \ + verify-doh-cert=no +add disabled=yes dns-servers=1.1.1.1,1.0.0.1 name=slon3.at verify-doh-cert=no +add disabled=yes dns-servers=1.0.0.1,1.1.1.1 name=slon3.cc verify-doh-cert=no +add disabled=yes dns-servers=1.1.1.1,1.0.0.1 name=bs2web.at verify-doh-cert=\ + no +add disabled=yes dns-servers=1.1.1.1,1.0.0.1 name=bs2best.at verify-doh-cert=\ + no +add disabled=yes dns-servers=1.0.0.1,1.1.1.1 name=psrms.is verify-doh-cert=no +/ip ipsec proposal +set [ find default=yes ] disabled=yes +/ip pool +add name=dhcp ranges=10.30.30.83-10.30.30.254 +/ip dhcp-server +add add-arp=yes address-pool=dhcp always-broadcast=yes bootp-support=none \ + dhcp-option-set=domain-search-set interface=Bridge-LAN lease-time=10m \ + name=masterDHCP server-address=10.30.30.81 +/ip smb users +set [ find default=yes ] disabled=yes +add disabled=yes name=vados +/ipv6 dhcp-client option +add code=119 name=domain-search-list value="0x03'WORKGROUP'0x00" +add code=12 name=HostName value="\$(HOSTNAME)" +add code=61 name=DUID value=0x00030001488f5a8e1abc +add code=61 name=clientid +/ipv6 dhcp-server option +add code=119 name=domain-search-list value="0x03'WORKGROUP'0x00" +add code=12 name=HostName value="\$(HOSTNAME)" +add code=61 name=CLIENT_DUID +add code=61 name=CLIENT_MAC +/ppp profile +set *FFFFFFFE use-upnp=no +#error exporting "/queue/interface" (timeout) +/routing bgp template +set default disabled=no output.network=bgp-networks +/routing table +add disabled=no fib name=To-VPN +add disabled=no fib name=r_to_vpn +/system logging action +add email-start-tls=yes email-to=vados@vados.ru name=email target=email +/system script +add dont-require-permissions=no name=fetchCustom owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n\r\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.86.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.86.pub\";\ + \n\r\ + \n#:local Script AM-setup\ + \n;\r\ + \n#/system/script/add name=\$Script owner=\$Script source=([ /tool/fetch c\ + heck-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".r\ + sc\") output=user as-value ]->\"data\");\r\ + \n\ + \n#/usb1/mikro_bak_rsa.pub\ + \n\ + \n\r\ + \n#\ + \n\r\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.10.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.10.pub\";\ + \n\r\ + \n#\r\ + \n\ + \n#root.mnuc.backuper\ + \n\ + \n\r\ + \n/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@10\ + .30.30.81.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.81.pub\";\r\ + \n\ + \n#:local Script AM-GlobalConfig;\ + \n\r\ + \n#/system/script/add name=\$Script owner=\$Script source=([ /tool/fetch c\ + heck-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".r\ + sc\") output=user as-value ]->\"data\");\r\ + \n\ + \n#/usb1/mikro_bak_rsa.pub\ + \n#:local FetchFile AM-DOHCert.rsc\ + \n\ + \n\r\ + \n\r\ + \n#\ + \n:global MkDir;\ + \n\r\ + \n\ + \n#:local FetchFile Binary.backup\ + \n:local localDir \"\";\ + \n\ + \n\r\ + \n#\$MkDir \$localDir;\r\ + \n\ + \n#\ + \n/tool/fetch mode=https url=(\$GlobalScriptsUrl . \$FetchFile) dst-path=(\ + \$localDir . \$FetchFile);\r\ + \n" +add comment="Global Config Script" dont-require-permissions=no name=\ + AM-GlobalConfig owner=AM-GlobalConfig policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GlobalConfig\r\ + \n# Script comment: Global Config Script\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# Warning: Dont touch this line!\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global GlobalConfReady false;\r\ + \n#\r\ + \n#\r\ + \n# Global Url For Amster scripts to fetch\r\ + \n# most be defined in AM-Init-Script or AM-setup scripts\r\ + \n:global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n:global ScriptsUrlSuffix (a);\r\ + \n:global CheckSumsVerify true;\r\ + \n\r\ + \n:global ScriptUpdatesCRLF true;\r\ + \n:global CommentsInScripts true;\r\ + \n\r\ + \n# Debug output:\r\ + \n:global PrintDebug true; #false;\r\ + \n\r\ + \n# Debug output for specific script:\r\ + \n#:set (\$PrintDebugOverride->\"AM-GlobalFunc\") true;\r\ + \n\r\ + \n# Debug logs\r\ + \n# Other actions (disk, email, remote or support) can be used as well.\r\ + \n# I do not recommend using echo - use debug output instead (https://rsc.\ + eworm.de/main/DEBUG.html#debug-output).\r\ + \n#/system/logging/add topics=script,debug action=memory;\r\ + \n\r\ + \n:global MikrotikUpgradeUrl \"https://upgrade.mikrotik.com/routeros\";\r\ + \n:global Domain \"WORKGROUP\";\r\ + \n\r\ + \n:global EmailGeneralTo \"vados@vados.ru\";\r\ + \n#:global EmailGeneralCc \"\";\r\ + \n\r\ + \n# \$ScriptInstallUpdate mod/notification-telegram\r\ + \n:global TelegramTokenId \"8390868324:AAG3_d1hfVR2M6pGAgDK6ZvhOe4J97nyopw\ + \";\r\ + \n:global TelegramChatId \"-1003631309063\"; #\"454403445\";\r\ + \n# Use this to send notifications to a specific topic in group.\r\ + \n#:global TelegramThreadId \"30\";\r\ + \n# Using telegram-chat you have to define trusted chat ids (not group ids\ + !)\r\ + \n# or user names. Groups allow to chat with devices simultaneously.\r\ + \n:global TelegramChatIdsTrusted {\r\ + \n \"454403445\"\r\ + \n \"-1003631309063\"\r\ + \n \"-5124482807\"\r\ + \n \"1087968824\";\r\ + \n};\r\ + \n# \"-5294757340\";\r\ + \n\r\ + \n:global TelegramChatGroups \"(all)\";\r\ + \n#:global TelegramChatGroups \"(all|home|office)\";\r\ + \n\r\ + \n:global NotificationFunctions {\r\ + \n \"email\"\r\ + \n \"telegram\";\r\ + \n};\r\ + \n# Toggle this to disable symbols in notifications.\r\ + \n:global NotificationsWithSymbols true;\r\ + \n:global TerminalColorOutput true;\r\ + \n\r\ + \n:global DetectIpAddrDefault \"https://ipv4.mikrotik.ovh/\";\r\ + \n:global DetectIpAddrFallback \"https://api.ipify.org/\";\r\ + \n\r\ + \n## Update channel. Possible values: stable, long-term, testing, developm\ + ent\r\ + \n:global updateChannel \"stable\";\r\ + \n\r\ + \n:global CertRenewUrl (\$GlobalScriptsUrl . \"certs/\" . \$Identity . \"/\ + \");\r\ + \n:global CertRenewTime 2w;\r\ + \n:global CertRenewPass (a); #{\r\ + \n# \"\";\r\ + \n# \"v3ry-s3cr3t\";\r\ + \n# \"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n:global CertWarnTime 1w;\r\ + \n:global CertIssuedExportPass (a); # {\r\ + \n# \"\";\r\ + \n# \"cert1-cn\"=\"v3ry-s3cr3t\";\r\ + \n# \"cert2-cn\"=\"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n\r\ + \n# Script BackupAndUpdate mode, possible values: backup,\r\ + \n# updateOnce - Update once as \"osnotify\" and back \"osnotify\" mode (i\ + n terminal run :set OSUpdateMode updateOnce; \$OSUpdateMode;). \r\ + \n# osupdate - Update if available and creates backups before/after update\ + \_(ignores `forceBackup`)\r\ + \n# Set `forceBackup` to true to always create backups, even\ + \_without updates\r\ + \n# osnotify - Set `forceBackup` to always create backups on every run\r\ + \n:global DevInfoIncludeIP true;\r\ + \n:global BackupAndUpdateMode \"osnotify\"\r\ + \n\r\ + \n:global forceBackup true;\r\ + \n:global BackupPassword \"Peskar55\";\r\ + \n\r\ + \n# SFTP Backup Variables\r\ + \n:global BackupRandomDelay 0;\r\ + \n:global BackupUploadUrl \"sftp://amster.vados.ru/upload/\";\r\ + \n:global BackupUploadUser \$Identity;\r\ + \n:global BackupUploadPass \"root.mnuc.backuper\";\r\ + \n\r\ + \n:global BackupSendBinary true;\r\ + \n:global BackupSendExport true;\r\ + \n:global BackupSendGlobalConfig true;\r\ + \n:global BackupSendScripts true;\r\ + \n\r\ + \n# Remove local file after uploading if no errors\r\ + \n:global BackupRmLocal true;\r\ + \n\r\ + \n# Local Backups Root Directory \r\ + \n:global BackupLocalRoot \"tmpfs\";\r\ + \n\r\ + \n# Encrypt Backup\r\ + \n:global BackupEncrypt true;\r\ + \n\r\ + \n# Sensitive information in Backups\r\ + \n:global BackupSens true;\r\ + \n\r\ + \n# User Export\r\ + \n:global BackupUser true;\r\ + \n\r\ + \n# License Export (not for CHR, will silently skip)\r\ + \n:global BackupLicense false;\r\ + \n\r\ + \n# SSH Keys\r\ + \n:global BackupSshKeys true;\r\ + \n\r\ + \n# Certificate Export\r\ + \n:global BackupCerts true;\r\ + \n\r\ + \n# Certificate Password\r\ + \n:global BackupCertsPasswd \"\";\r\ + \n\r\ + \n# User-Manager Export\r\ + \n:global BackupUserMan false;\r\ + \n\r\ + \n# The Dude Export\r\ + \n:global BackupDude false;\r\ + \n\r\ + \n# User Files to export, comma-separated string or array of strings\r\ + \n# User Files are not removed on backup\r\ + \n# Any directory paths will be removed (/ -> _) on remote file\r\ + \n# Nonpresent files are silently skipped\r\ + \n:global BackupUserList \"autosupout.rif,autosupout.old.rif\";\r\ + \n\r\ + \n# load functions and any custom scripts\r\ + \n# [ /system/script/find where name=\"global-config-ov\ + erlay\" ], \\\r\ + \n# [ /system/script/find where name~\"^global-config-o\ + verlay\\\\.d/.\" ]\r\ + \n#\r\ + \n#:foreach Script in=([ /system/script/find where name=\"Amster-TGglobalV\ + ars\" ], [ /system/script/find where name=\"Amster-TGglobalFunc\" ]) do={\ + \r\ + \n# :onerror Err {\r\ + \n# /system/script/run \$Script;\r\ + \n# } do={\r\ + \n# :log error (\"Loading configuration from configs and functions\"\ + \_. [ /system/script/get \$Script name ] . \" failed: \" . \$Err);\r\ + \n# }\r\ + \n#}\r\ + \n#:log info \"\\ndevMode = \$devMode\";\r\ + \n#:log info \"\\ndevRunning = \$devRunning\";\r\ + \n:foreach Script in=[ /system/script/find where source~\"^#!rsc by Vados \ + GlobalRUN\\r\?\\n\" ] do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n :log error (\"Run script \" . [ /system/script/get \$Script name \ + ] . \" width marker \\\" GlobalRUN\\\" failed: \" . \$Err);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# signal we are ready\r\ + \n:set GlobalConfReady true;\r\ + \n:while (\$GlobalConfReady != true) do={:delay 500ms};\r\ + \n" +add comment="Global functions" dont-require-permissions=no name=AM-GlobalFunc \ + owner=AM-GlobalFunc policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GglobalFunc\r\ + \n# Script comment: Global functions\r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch, scheduler\r\ + \n:local ExitOK false;\r\ + \n:local ScriptName [ :jobname ];\r\ + \n#:global checkRunJob \$SriptName;\r\ + \n#:global fixCRLF;\r\ + \n#:if (\$fixCRLF != false) do={\r\ + \n#:foreach fixScript in=([ /system/script/find where name~\"*\" ]) do={/s\ + ystem/script/set source=[ :tocrlf [ get \$fixScript source ] ] \$fixScript\ + }\r\ + \n#:if (\$fixCRLF != \"LF\") do={ /system/script/set source=[ :tocrlf [ g\ + et \$fixScript source ] ] \$fixScript};};\r\ + \n#}\r\ + \n:global GlobalFuncReady false;\r\ + \n\r\ + \n# Git commit id & info, expected configuration version\r\ + \n:global CommitId \"main/729/f42d43ae\";\r\ + \n:global CommitTAG \"unknown\";\r\ + \n:global CommitInfo \"Autocommit in repo: ros.vados.ru, generated by owne\ + r: root, in 00:05:03 01.07.2026.\";\r\ + \n\r\ + \n# The variables are received right here.\r\ + \n:global GlobalConfVersion 140;\r\ + \n\r\ + \n:global SumsDirAndFileName \"tmp/checksums.json\";\r\ + \n\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global IdentityShort [:pick \$Identity 0 18];\r\ + \n\r\ + \n:global devBoardName [/system/resource/get board-name];\r\ + \n:global devMode [/system/device-mode/get mode];\r\ + \n:global devCurArch [/system/resource/get architecture-name];\r\ + \n:global devCurVersion [/system/resource/get version];\r\ + \n\r\ + \n# Variables defined in AM-GlobalConfig.rsc\r\ + \n:global TerminalColorOutput;\r\ + \n:global CommentsInScripts;\r\ + \n:global DetectIpAddrDefault;\r\ + \n:global DetectIpAddrFallback;\r\ + \n:global updateChannel;\r\ + \n:global Domain;\r\ + \n:global EmailGeneralTo;\r\ + \n:global BackupAndUpdateMode;\r\ + \n\r\ + \n# Single functions, without nested functions.\r\ + \n:global MiniDateTimeStamp;\r\ + \n:global MicroDateTimeStamp;\r\ + \n\r\ + \n# Include globals \$PrintDebug and \$PrintDebugOverride\r\ + \n:global LogPrint;\r\ + \n\r\ + \n# DeviceInfo and Updates\r\ + \n:global IsCheckUpdates;\r\ + \n:global WaitCheckUpdates;\r\ + \n:global GetPubIp;\r\ + \n\r\ + \n:global GetArrDeviceInfo;\r\ + \n:global WaitArrDeviceInfo;\r\ + \n\r\ + \n:global devModel;\r\ + \n:global devRunningMode;\r\ + \n\r\ + \n:global devRbRevision;\r\ + \n:global devRbSerialNumber;\r\ + \n:global devRbVerAvail;\r\ + \n\r\ + \n:global runningChannelVersion;\r\ + \n:global runningChannel;\r\ + \n:global runningVersion;\r\ + \n\r\ + \n:global devBuildTime;\r\ + \n:global ROsVerAvail;\r\ + \n:global PkgUpdStatus;\r\ + \n:global currentUpdateChannel;\r\ + \n\r\ + \n:global ScriptInstallUpdate;\r\ + \n\r\ + \n:global DeviceInfo;\r\ + \n\r\ + \n:global OldMode;\r\ + \n\r\ + \n:global ParseKeyValueStore;\r\ + \n:global RandomDelay;\r\ + \n:global GetRandomNumber;\r\ + \n:global EscapeForRegEx;\r\ + \n:global CharacterMultiply;\r\ + \n:global CharacterReplace;\r\ + \n:global CleanFilePath;\r\ + \n:global CleanName;\r\ + \n\r\ + \n:global CommitBrief;\r\ + \n\r\ + \n:global EitherOr;\r\ + \n:global FormatLine;\r\ + \n:global GetRandom20CharAlNum;\r\ + \n:global GetRandom20CharHex;\r\ + \n:global GetRandomNumber;\r\ + \n:global Grep;\r\ + \n:global IfThenElse;\r\ + \n:global MAX;\r\ + \n:global MIN;\r\ + \n:global HumanReadableNum;\r\ + \n\r\ + \n:global IsFullyConnected;\r\ + \n:global IsDNSResolving;\r\ + \n:global IsTimeSync;\r\ + \n:global IsTimeSyncCached;\r\ + \n:global IsTimeSyncResetNtp;\r\ + \n\r\ + \n:global MkDir;\r\ + \n:global RmDir;\r\ + \n:global RmFile;\r\ + \n:global FileGet;\r\ + \n:global Unix2Dos;\r\ + \n:global UrlEncode;\r\ + \n:global WaitForFile;\r\ + \n:global ScriptLock;\r\ + \n:global SendTelegram;\r\ + \n:global NotificationFunctions;\r\ + \n:global SendNotification;\r\ + \n:global SendNotification2;\r\ + \n:global SymbolForNotification;\r\ + \n:global SymbolByUnicodeName;\r\ + \n:global ValidateSyntax;\r\ + \n:global CheckSumsVerify;\r\ + \n:global GlobalConfChanges;\r\ + \n:global GlobalConfMigration;\r\ + \n:global VersionToNum;\r\ + \n:global RequiredRouterOS;\r\ + \n:global ExitError;\r\ + \n:global ExitOnError;\r\ + \n\r\ + \n:global FetchHugeFiles;\r\ + \n:global FetchUserAgentStr;\r\ + \n:global FileExists;\r\ + \n:global CertificateAvailable;\r\ + \n:global CertificateDownload;\r\ + \n:global CertificateNameByCN;\r\ + \n:global CharacterMultiply;\r\ + \n\r\ + \n:global InspectVar;\r\ + \n:global InspectVarRet;\r\ + \n:global ParseCustom;\r\ + \n\r\ + \n:set IsCheckUpdates do={\r\ + \n :local isInfo [:tobool \$1];\r\ + \n :global updateChannel;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :log info (\$0 . \" \\\$isInfo = \" . \$isInfo); \r\ + \n :local CurrentChannel [ /system/package/update/get channel ];\r\ + \n :if ([ :len \$updateChannel ] > 0 && \$CurrentChannel != \$updateChann\ + el) do={\r\ + \n :retry command={/system/package/update set channel=\$updateChannel} \ + \_delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" Set updates channel to: \$updateChannel\");\r\ + \n }\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local Channel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set currentUpdateChannel \$Channel;\r\ + \n :log info (\$0 . \" Update Channel: \" . \$Channel . \".\");\r\ + \n :local StatusPkg \"undefined\";\r\ + \n :log info (\$0 . \" Checking for updates...\");\r\ + \n :retry command={:set \$StatusPkg ([/system/package/update/check-for-up\ + dates without-paging as-value]->\"status\")} \\\r\ + \n delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" \\\$StatusPkg = \" . \$StatusPkg);\r\ + \n :if (\$StatusPkg = \"New version is available\") do={ :set PkgUpdStatu\ + s true; } else={ :set PkgUpdStatus false; }\r\ + \n :set ROsVerAvail [ :tostr [ /system/package/update/get latest-version \ + ] ];\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if (\$isInfo != false || \$PkgUpdStatus = true) do={\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :retry command={:set (\$arrDeviceInfo->\"devUpdates\") [ /system/pac\ + kage/update/get ]} delay=500ms max=5 on-error={:log error (\"Error\")};\r\ + \n :return true;\r\ + \n } else={\r\ + \n :return \$PkgUpdStatus;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set WaitCheckUpdates do={\r\ + \n :global IsCheckUpdates;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :local Status \"\";\r\ + \n :local Update;\r\ + \n :local WaitCount 0;\r\ + \n :if ([ \$IsCheckUpdates true ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n :while ((\$Status = \"\" || \$Status = \"finding out latest version...\ + \") && \$WaitCount < 60) do={\r\ + \n :delay 1s;\r\ + \n :set WaitCount (\$WaitCount + 1);\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n }\r\ + \n :set Update [ /system/package/update/get ];\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n :if ((\$Update->\"status\") = \"New version is available\") do={\r\ + \n :set PkgUpdStatus true;\r\ + \n } else={\r\ + \n :set PkgUpdStatus false;\r\ + \n }\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :set (\$arrDeviceInfo->\"devUpdates\") \$Update;\r\ + \n :if (\$Status = \"\" || \$Status = \"finding out latest version...\") \ + do={\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set WaitArrDeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :while ([ \$GetArrDeviceInfo ] = false) do={\r\ + \n :delay 1s;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set GetArrDeviceInfo do={\r\ + \n :global LogPrint;\r\ + \n :global arrDeviceInfo;\r\ + \n :global devBoardName;\r\ + \n :global devModel;\r\ + \n :global devRunning;\r\ + \n :global devCurArch;\r\ + \n :global devRbRevision;\r\ + \n :global devRbSerialNumber;\r\ + \n :global devBuildTime;\r\ + \n :global runningChannelVersion;\r\ + \n :global runningChannel;\r\ + \n :global runningVersion;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n\r\ + \n :local DeviceInfo;\r\ + \n :local Resource [/system/resource/get];\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local VersionString [ :tostr (\$Resource->\"version\") ];\r\ + \n :local ChannelStart [ :find \$VersionString \" (\" ];\r\ + \n :local RouterBoard;\r\ + \n :local IsRouterBoard false;\r\ + \n :local RunningMode \"\";\r\ + \n\r\ + \n :set devCurArch [ :tostr (\$Resource->\"architecture-name\") ];\r\ + \n :set devBuildTime [ :tostr (\$Resource->\"build-time\") ];\r\ + \n :set runningChannelVersion \$VersionString;\r\ + \n :set runningVersion \$VersionString;\r\ + \n :set runningChannel \"\";\r\ + \n\r\ + \n :if ([ :typeof \$ChannelStart ] = \"num\" && \$ChannelStart >= 0) do=\ + {\r\ + \n :set runningVersion [ :pick \$VersionString 0 \$ChannelStart ];\r\ + \n :local ChannelRest [ :pick \$VersionString (\$ChannelStart + 2) [ :\ + len \$VersionString ] ];\r\ + \n :local ChannelEnd [ :find \$ChannelRest \")\" ];\r\ + \n :if ([ :typeof \$ChannelEnd ] = \"num\" && \$ChannelEnd >= 0) do={\ + \r\ + \n :set runningChannel [ :pick \$ChannelRest 0 \$ChannelEnd ];\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([:pick \$devBoardName 0 3] != \"CHR\" or [:pick \$devBoardName 0\ + \_3] != \"x86\") do={\r\ + \n :do {\r\ + \n :set RouterBoard [[ :parse \"/system/routerboard/get\" ]];\r\ + \n# :set RouterBoard [ /system/routerboard/get ];\r\ + \n :if ((\$RouterBoard->\"routerboard\") = true) do={\r\ + \n :set IsRouterBoard true;\r\ + \n }\r\ + \n } on-error={}\r\ + \n }\r\ + \n\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get mode ];\r\ + \n } on-error={\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get running ];\r\ + \n } on-error={\r\ + \n :set RunningMode \"\";\r\ + \n }\r\ + \n }\r\ + \n :set devRunning \$RunningMode;\r\ + \n\r\ + \n :if (\$IsRouterBoard = true) do={\r\ + \n :set devModel [ :tostr (\$RouterBoard->\"model\") ];\r\ + \n :set devRbRevision [ :tostr (\$RouterBoard->\"revision\") ];\r\ + \n :set devRbSerialNumber [ :tostr (\$RouterBoard->\"serial-number\") \ + ];\r\ + \n } else={\r\ + \n :set devModel \$devBoardName;\r\ + \n :set devRbRevision \"\";\r\ + \n :set devRbSerialNumber \"\";\r\ + \n }\r\ + \n\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n\r\ + \n :set (\$DeviceInfo->\"board-name\") \$devBoardName;\r\ + \n :set (\$DeviceInfo->\"architecture-name\") \$devCurArch;\r\ + \n :set (\$DeviceInfo->\"model\") \$devModel;\r\ + \n :set (\$DeviceInfo->\"revision\") \$devRbRevision;\r\ + \n :set (\$DeviceInfo->\"serial-number\") \$devRbSerialNumber;\r\ + \n :set (\$DeviceInfo->\"running-mode\") \$devRunning;\r\ + \n :set (\$DeviceInfo->\"build-time\") \$devBuildTime;\r\ + \n :set (\$DeviceInfo->\"running-channel-version\") \$runningChannelVers\ + ion;\r\ + \n :set (\$DeviceInfo->\"running-version\") \$runningVersion;\r\ + \n :set (\$DeviceInfo->\"running-channel\") \$runningChannel;\r\ + \n :set (\$DeviceInfo->\"routerboard\") \$IsRouterBoard;\r\ + \n :set (\$DeviceInfo->\"current-firmware\") [ :tostr (\$RouterBoard->\"\ + current-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"upgrade-firmware\") [ :tostr (\$RouterBoard->\"\ + upgrade-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"package-channel\") \$currentUpdateChannel;\r\ + \n :set (\$DeviceInfo->\"installed-version\") [ :tostr (\$Update->\"inst\ + alled-version\") ];\r\ + \n :set (\$DeviceInfo->\"latest-version\") \$ROsVerAvail;\r\ + \n :set (\$DeviceInfo->\"update-status\") [ :tostr (\$Update->\"status\"\ + ) ];\r\ + \n\r\ + \n :set arrDeviceInfo \$DeviceInfo;\r\ + \n :return \$DeviceInfo;\r\ + \n}\r\ + \n\r\ + \n:set DeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global IfThenElse;\r\ + \n :global FormatLine;\r\ + \n :global CommitBrief;\r\ + \n\r\ + \n :local License [/system/license/get];\r\ + \n :local Snmp [ /snmp/get ];\r\ + \n :local Info [ \$GetArrDeviceInfo ];\r\ + \n\r\ + \n :if ([ :typeof \$Info ] != \"array\") do={\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :return ( \\\r\ + \n [ \$FormatLine \"Hostname\" \$Identity ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"location\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Location\" (\$Snmp->\"location\") ] . \"\\n\") ]\ + \_. \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"contact\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Contact\" (\$Snmp->\"contact\") ] . \"\\n\") ] .\ + \_\\\r\ + \n \"Hardware:\\n\" . \\\r\ + \n [ \$FormatLine \" Board\" (\$Info->\"board-name\") ] . \"\\n\" . \ + \\\r\ + \n [ \$FormatLine \" Arch\" (\$Info->\"architecture-name\") ] . \"\\\ + n\" . \\\r\ + \n [ \$FormatLine \" Model\" (\$Info->\"model\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"revision\") ] > 0) \\\r\ + \n (\" \" . (\$Info->\"revision\")) ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"serial-number\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Serial\" (\$Info->\"serial-number\") ] . \"\ + \\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"nlevel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"nlevel\ + \")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS:\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"level\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"level\ + \")) ] . \"\\n\") ] . \\\r\ + \n [ \$FormatLine \" Channel\" (\$Info->\"package-channel\") ] . \"\ + \\n\" . \\\r\ + \n [ \$FormatLine \" Installed\" (\$Info->\"installed-version\") ] .\ + \_\"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"running-channel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Running\" ((\$Info->\"running-version\") . \ + \" (\" . (\$Info->\"running-channel\") . \")\") ] . \"\\n\") \\\r\ + \n ([ \$FormatLine \" Running\" (\$Info->\"running-version\") ] . \ + \"\\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"latest-version\") ] > 0 && \\\r\ + \n (\$Info->\"installed-version\") != (\$Info->\"latest-version\"))\ + \_\\\r\ + \n ([ \$FormatLine \" Available\" (\$Info->\"latest-version\") ] .\ + \_\"\\n\") ] . \\\r\ + \n [ \$IfThenElse ((\$Info->\"routerboard\") = true && \\\r\ + \n (\$Info->\"current-firmware\") != (\$Info->\"upgrade-firmware\")\ + ) \\\r\ + \n ([ \$FormatLine \" Firmware\" ((\$Info->\"current-firmware\") .\ + \_\" -> \" . (\$Info->\"upgrade-firmware\")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS-Scripts:\\n\" . \\\r\ + \n [ \$FormatLine \" Commit\" [ \$CommitBrief ] ] . \"\\n\" . \\\r\ + \n [ \$FormatLine \" Version\" \$GlobalConfVersion ]);\r\ + \n}\r\ + \n\r\ + \n:if ([ :typeof \$NotificationFunctions ] != \"array\") do={:set Notifica\ + tionFunctions ({})}\r\ + \n\r\ + \n:set GetRandom20CharAlNum do={\r\ + \n :global EitherOr;\r\ + \n :return [ :rndstr length=[ \$EitherOr [ :tonum \$1 ] 20 ] from=\"ABCDE\ + FGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\" ];\r\ + \n}\r\ + \n\r\ + \n:set GetRandom20CharHex do={:global EitherOr; :return [ :rndstr length=[\ + \_\$EitherOr [ :tonum \$1 ] 20 ] from=\"0123456789abcdef\" ]; }\r\ + \n:set GetRandomNumber do={:global EitherOr; :return [ :rndnum from=0 to=[\ + \_\$EitherOr [ :tonum \$1 ] 4294967295 ] ]; }\r\ + \n\r\ + \n:global checkRunJob;\r\ + \n:set checkRunJob do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLock; \r\ + \n :if ([ \$ScriptLock \$Script ] = false) do={:return false}\r\ + \n :foreach Job in=[ /system/script/job/find where script=\$Script ] do={\ + :set Job [ /system/script/job/get \$Job ];\r\ + \n :while ([ :typeof (\$Job->\"parent\") ] = \"id\") do={:set Job [ /sy\ + stem/script/job/get [ find where .id=(\$Job->\"parent\") ] ]}\r\ + \n :if ((\$Job->\"type\") = \"login\") do={:return true};}; :return fa\ + lse; \r\ + \n}\r\ + \n\r\ + \n:set InspectVar do={\r\ + \n :global InspectVarRet;\r\ + \n :put [ :tocrlf [ \$InspectVarRet \$1 ] ];\r\ + \n}\r\ + \n\r\ + \n:set CommitBrief do={\r\ + \n :global CommitId;\r\ + \n :global CommitInfo;\r\ + \n :if (\$CommitId = \"unknown\") do={\r\ + \n :return \"unknown\";\r\ + \n }\r\ + \n :return (\$CommitInfo . \"/\" . [ :pick \$CommitId 0 8 ]);\r\ + \n}\r\ + \n\r\ + \n:set InspectVarRet do={\r\ + \n :local Input \$1;\r\ + \n :local Level (0 + [ :tonum \$2 ]);\r\ + \n :global CharacterReplace;\r\ + \n :global IfThenElse;\r\ + \n :global InspectVarRet;\r\ + \n :local IndentReturn do={\r\ + \n :local Prefix [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Level [ :tonum \$3 ];\r\ + \n :global CharacterMultiply;\r\ + \n :return ([ \$CharacterMultiply \" \" (\$Level * 2) ] . \"-\" . \$Pre\ + fix . \"-> \" . \$Value);\r\ + \n }\r\ + \n :local TypeOf [ :typeof \$Input ];\r\ + \n :local Len [ :len \$Input ];\r\ + \n :local Return [ \$IndentReturn \"type\" \$TypeOf \$Level ];\r\ + \n :if (\$TypeOf = \"array\") do={\r\ + \n :foreach Key,Value in=\$Input do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"key\" \$Key (\$Level + 1) ] . \"\\n\" . \\\r\ + \n [ \$InspectVarRet \$Value (\$Level + 2) ]);\r\ + \n }\r\ + \n } else={\r\ + \n :if (\$TypeOf = \"str\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"len\" \$Len \$Level ]);\r\ + \n :if ([ :typeof [ :find \$Input (\"\\r\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\r\") \"\" ];\r\ + \n }\r\ + \n :if ([ :typeof [ :find \$Input (\"\\n\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\n\") \" \" ];\r\ + \n }\r\ + \n }\r\ + \n :if (\$TypeOf != \"nothing\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"value\" [ \$IfThenElse ([ :len \$Input ] > 80)\ + \_\\\r\ + \n ([ :pick \$Input 0 77 ] . \"...\") \$Input ] \$Level ]);\r\ + \n }\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set GetPubIp do={\r\ + \n :global DetectIpAddrDefault;\r\ + \n :global DetectIpAddrFallback;\r\ + \n\r\ + \n :local PubIpAddr false;\r\ + \n :local Url \$DetectIpAddrDefault;\r\ + \n :local JobName [ :jobname ];\r\ + \n\r\ + \n :do {\r\ + \n :log debug (\$0 . \" Trying to detect public IP using default serv\ + ice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=50ms max=5 on-error={:log error (\$JobName . \" Failed to \ + detect public IP using default service: \" . \$Url . \".\")}\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed to detect public IP using default serv\ + ice: \" . \$Url . \".\");\r\ + \n :set Url \$DetectIpAddrFallBack;\r\ + \n :log debug (\$0 . \" Trying to detect public IP using fallback ser\ + vice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=200ms max=5 on-error={:log error (\$JobName . \" Failed to\ + \_detect public IP using fallback service: \" . \$Url . \".\")}\r\ + \n };\r\ + \n :local IpLen ([ :len \$PubIpAddr ] -1);\r\ + \n :if (\$PubIpAddr = false) do={:set PubIpAddr \"not-detected\"} else=\ + {:set PubIpAddr [:pick \$PubIpAddr 0 \$IpLen]}\r\ + \n :return \$PubIpAddr;\r\ + \n}\r\ + \n\r\ + \n:set FormatLine do={\r\ + \n :local Key [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Indent [ :tonum \$3 ];\r\ + \n :local Spaces;\r\ + \n :local Return \"\";\r\ + \n :global CharacterMultiply;\r\ + \n :global EitherOr;\r\ + \n :set Indent [ \$EitherOr \$Indent 16 ];\r\ + \n :local Spaces [ \$CharacterMultiply \" \" \$Indent ];\r\ + \n :if ([ :len \$Key ] > 0) do={ :set Return (\$Key . \":\")}\r\ + \n :if ([ :len \$Key ] > (\$Indent - 2)) do={:set Return (\$Return . \"\\\ + n\" . [ :pick \$Spaces 0 \$Indent ] . \$Value);\r\ + \n } else={:set Return (\$Return . [ :pick \$Spaces 0 (\$Indent - [ :len \ + \$Return ]) ] . \$Value)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ParseCustom do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Start [:find [ \$Str ] [:tostr \$2]];\r\ + \n :local End [ :tostr \$3 ];\r\ + \n :local Num [ :tonum \$4 ];\r\ + \n :local Return;\r\ + \n :if ([:len \$Num] = 0) do={:set Num \"0\"}\r\ + \n :local rest [:pick \$Str (\$Start+\$Num) [:len \$Str]]; \r\ + \n :if ([:len \$End ] = 0) do={:set Return [:pick \$Str \$Num \$Start]} e\ + lse={\r\ + \n :set End [:find \$rest \$End];\r\ + \n :set Return [:pick \$rest 0 \$End];\r\ + \n }\r\ + \n:return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set LogPrint do={\r\ + \n :local Severity [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Message [ :tostr \$3 ];\r\ + \n :global PrintDebug;\r\ + \n :global PrintDebugOverride;\r\ + \n :global EitherOr;\r\ + \n :local Debug [ \$EitherOr (\$PrintDebugOverride->\$Name) \$PrintDebug \ + ];\r\ + \n :local PrintSeverity do={\r\ + \n :global TerminalColorOutput;\r\ + \n :if (\$TerminalColorOutput != true) do={:return \$1}\r\ + \n :local Color { debug=96; info=97; warning=93; error=91 };\r\ + \n :return (\"\\1B[\" . \$Color->\$1 . \"m\" . \$1 . \"\\1B[0m\");\r\ + \n}\r\ + \n :local Log ([ \$EitherOr \$Name \"\" ] . \": \" . \$Message);\ + \r\ + \n :if (\$Severity ~ (\"^(custom|debug|error|info)\\\$\")) do={\r\ + \n :if (\$Severity = \"debug\") do={:log debug \$Log}\r\ + \n :if (\$Severity = \"error\") do={:log error \$Log}\r\ + \n :if (\$Severity = \"info\" ) do={:log info \$Log}\r\ + \n } else={:log warning \$Log; :set Severity \"warning\"}\r\ + \n :if (\$Severity != \"debug\" || \$Debug = true) do={:put ([ \$PrintSev\ + erity \$Severity ] . \": \" . \$Message)}\r\ + \n}\r\ + \n\r\ + \n:set CertificateAvailable do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :local UseFor [ :tostr \$2 ];\r\ + \n\r\ + \n :global CertificateDownload;\r\ + \n :global ParseKeyValueStore;\r\ + \n\r\ + \n :if ([ :len \$UseFor ] = 0) do={\r\ + \n :log warning (\$0 . \"The intended use is undefined!\");\r\ + \n :set UseFor \"undefined\";\r\ + \n }\r\ + \n :if ([ /system/resource/get free-hdd-space ] < 8388608 && \\\r\ + \n [ /certificate/settings/get crl-download ] = true && \\\r\ + \n [ /certificate/settings/get crl-store ] = \"system\") do={\r\ + \n :log warning (\$0 . \"This system has low free flash space but \" . \ + \\\r\ + \n \"is configured to download certificate CRLs to system!\");\r\ + \n }\r\ + \n :if ([ :len \$CommonName ] = 0) do={\r\ + \n :log warning (\$0 . \"No CommonName given!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if (((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" || \\\ + \r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \$UseFor || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\r\ + \n [ :len [ /certificate/builtin/find where common-name=\$CommonName\ + \_] ] > 0) do={\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n :log info (\$0 . \"Certificate with CommonName '\" . \$CommonName . \ + \"' not available.\");\r\ + \n :if ([ \$CertificateDownload \$CommonName ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] > 1\ + ) do={\r\ + \n :log info (\$0 . \"There are \" . \$CertCount . \" Certificates with\ + \_CommonName '\" . \$CommonName . \"'. Should be ok.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :local CertVal [ /certificate/get [ find where common-name=\$CommonNam\ + e ] ];\r\ + \n :while ((\$CertVal->\"akid\") != \"\" && (\$CertVal->\"akid\") != (\$C\ + ertVal->\"skid\")) do={\r\ + \n :if ([ :len [ /certificate/find where skid=(\$CertVal->\"akid\") ] ]\ + \_= 0) do={\r\ + \n :local IssuerCN ([ \$ParseKeyValueStore (\$CertVal->\"issuer\") ]-\ + >\"CN\");\r\ + \n :log info (\$0 . \"Certificate chain for '\" . \$CommonName . \"' \ + is incomplete, missing '\" . \$IssuerCN . \"'.\");\r\ + \n :if ([ \$CertificateDownload \$IssuerCN ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :set CertVal [ /certificate/get [ find where skid=(\$CertVal->\"akid\ + \") ] ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateDownload do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :global ScriptUpdatesBaseUrl;\r\ + \n :global ScriptUpdatesUrlSuffix;\r\ + \n :global CertificateNameByCN;\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :log info (\$0 . \"Downloading and importing certificate with \" . \\\ + \r\ + \n \"CommonName '\" . \$CommonName . \"'.\");\r\ + \n :local FileName ([ \$CleanName \$CommonName ] . \".pem\");\r\ + \n :do {\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-header\ + -field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n (\$ScriptUpdatesBaseUrl . \"certs/\" . \$FileName . \$ScriptUpdate\ + sUrlSuffix) \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n } on-error={\r\ + \n :log warning (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \\\r\ + \n \"' from repository! Trying fallback to mkcert.org...\");\r\ + \n :do {\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if ([ :len [ /certificate/find where common-name=\"ISRG Root X1\"\ + \_] ] = 0 && \\\r\ + \n !(((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" \ + || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \"fetch\" || \ + \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\ + \r\ + \n [ :len [ /certificate/builtin/find where common-name=\"ISRG\ + \_Root X1\" ] ] > 0)) do={\r\ + \n :log error (\$0 . \" Required certificate is not available.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-head\ + er-field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n url=\"https://mkcert.org/generate/\" http-data=[ :serialize to=jso\ + n ({ \$CommonName }) ] \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n :if ([ /file/get \$FileName size ] = 0) do={\r\ + \n \$RmFile \$FileName;\r\ + \n :error false;\r\ + \n }\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \"'!\");\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n /certificate/import file-name=\$FileName passphrase=\"\" as-value;\r\ + \n :delay 1s;\r\ + \n \$RmFile \$FileName;\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n /certificate/remove [ find where name~(\"^\" . \$FileName . \"_[0-9]\ + +\\\$\") ];\r\ + \n :log warning (\$0 . \" Certificate with CommonName '\" . \$CommonNam\ + e . \"' still unavailable!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :foreach Cert in=[ /certificate/find where name~(\"^\" . \$FileName . \ + \"_[0-9]+\\\$\") ] do={\r\ + \n \$CertificateNameByCN [ /certificate/get \$Cert common-name ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateNameByCN do={\r\ + \n :local Match [ :tostr \$1 ];\r\ + \n :global CleanName;\r\ + \n :local Cert ([ /certificate/find where (common-name=\$Match or fingerp\ + rint=\$Match or name=\$Match) ]->0);\r\ + \n :if ([ :len \$Cert ] = 0) do={\r\ + \n :log warning (\$0 .\" No matching certificate found.\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CommonName [ /certificate/get \$Cert common-name ];\r\ + \n /certificate/set \$Cert name=[ \$CleanName \$CommonName ];\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FileExists do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local Type [ :tostr \$2 ];\r\ + \n :global FileGet;\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={ :return false; }\r\ + \n :if ([ :len (\$FileVal->\"size\") ] = 0) do={ :return false; }\r\ + \n :if ([ :len \$Type ] = 0 || \$FileVal->\"type\" = \$Type) do={ :return\ + \_true; }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set Grep do={\r\ + \n :local Input ([ :tostr \$1 ] . \"\\n\");\r\ + \n :local Pattern [ :tostr \$2 ];\r\ + \n :if ([ :typeof [ :find \$Input \$Pattern ] ] = \"nil\") do={:return []\ + }\r\ + \n :do {\r\ + \n :local Line [ :pick \$Input 0 [ :find \$Input \"\\n\" ] ];\r\ + \n :if ([ :typeof [ :find \$Line \$Pattern ] ] = \"num\") do={:return \ + \$Line}\r\ + \n :set Input [ :pick \$Input ([ :find \$Input \"\\n\" ] + 1) [ :len \$\ + Input ] ];\r\ + \n } while=([ :len \$Input ] > 0);\r\ + \n :return [];\r\ + \n}\r\ + \n\r\ + \n:set HumanReadableNum do={\r\ + \n :local Input [ :tonum \$1 ];\r\ + \n :local Base [ :tonum \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global IfThenElse;\r\ + \n :local Prefix \"kMGTPE\";\r\ + \n :local Pow 1;\r\ + \n :set Base [ \$EitherOr \$Base 1024 ];\r\ + \n :local Bin [ \$IfThenElse (\$Base = 1024) \"i\" \"\" ];\r\ + \n :if (\$Input < \$Base) do={:return \$Input}\r\ + \n :for I from=0 to=[ :len \$Prefix ] do={\r\ + \n :set Pow (\$Pow * \$Base);\r\ + \n :if (\$Input / \$Base < \$Pow) do={\r\ + \n :set Prefix [ :pick \$Prefix \$I ];\r\ + \n :local Tmp1 (\$Input * 100 / \$Pow);\r\ + \n :local Tmp2 (\$Tmp1 / 100);\r\ + \n :if (\$Tmp2 >= 100) do={:return (\$Tmp2 . \$Prefix . \$Bin)}\r\ + \n :return (\$Tmp2 . \".\" . \\\r\ + \n [ :pick \$Tmp1 [ :len \$Tmp2 ] ([ :len \$Tmp1 ] - [ :len \$Tmp\ + 2 ] + 1) ] . \\\r\ + \n \$Prefix . \$Bin);\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set CleanFilePath do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CharacterReplace;\r\ + \n :while (\$Path ~ \"//\") do={ :set \$Path [ \$CharacterReplace \$Path \ + \"//\" \"/\" ]; }\r\ + \n :if ([ :pick \$Path 0 ] = \"/\") do={ :set Path [ :pick \$Path 1 [ :le\ + n \$Path ] ]; }\r\ + \n :if ([ :pick \$Path ([ :len \$Path ] - 1) ] = \"/\") do={ :set Path [ \ + :pick \$Path 0 ([ :len \$Path ] - 1) ]; }\r\ + \n :return \$Path;\r\ + \n}\r\ + \n\r\ + \n:set CleanName do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Return \"\";\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :typeof [ find \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr\ + stuvwxyz0123456789\" \$Char ] ] = \"nil\") do={\r\ + \n :do {:if ([ :len \$Return ] = 0) do={:error true};\r\ + \n :if ([ :pick \$Return ([ :len \$Return ] - 1) ] = \"-\") do={:er\ + ror true}; :set Char \"-\"} on-error={:set Char \"\"}; \r\ + \n };\r\ + \n :set Return (\$Return . \$Char)};\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set RandomDelay do={\r\ + \n :local Time [ :tonum \$1 ];\r\ + \n :local Unit [ :tostr \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global GetRandomNumber;\r\ + \n :global MAX;\r\ + \n :if (\$Time = 0) do={:return false}\r\ + \n :delay ([ \$MAX 10 [ \$GetRandomNumber ([ :tonsec [ :totime (\$Time . \ + [ \$EitherOr \$Unit \"s\" ]) ] ] / 1000000) ] ] . \"ms\");\r\ + \n}\r\ + \n\r\ + \n:set EscapeForRegEx do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"^.[]\\\$()|*+\?{}\\\\\");\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :find \$Chars \$Char ]) do={:set Char (\"\\\\\" . \$Char)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ExitError do={\r\ + \n :local ExitOK [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Error [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :if (\$ExitOK = \"false\") do={\r\ + \n :log error (\$Name [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\\\$\") \ + \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with er\ + ror\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \ + \$Error) \".\" ]);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set ExitOnError do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :local Error [ :tostr \$2 ];\r\ + \n :global IfThenElse;\r\ + \n :log error (\$Name . \" \" . [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\ + \\\$\") \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with erro\ + r\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \$E\ + rror) \".\" ]);\r\ + \n}\r\ + \n\r\ + \n:set MiniDateTimeStamp do={\r\ + \n :local mikro [ :tobool \$1 ];\r\ + \n /system/clock;\r\ + \n :local vdate [get date];\r\ + \n :local vtime [get time];\r\ + \n :local vdoff [:toarray \"0,4,5,7,8,10\"];\r\ + \n :local MM [:pick \$vdate (\$vdoff->2) (\$vdoff->3)];\r\ + \n :local M [:tonum \$MM];\r\ + \n :if (\$vdate ~ \".../../....\") do={\r\ + \n :set vdoff [:toarray \"7,11,1,3,4,6\"];\r\ + \n :set M ([:find \"xxanebarprayunulugepctovecANEBARPRAYUNULUGE\ + PCTOVEC\" [:pick \$vdate (\$vdoff->2) (\$vdoff->3)] -1] / 2);\r\ + \n :if (\$M>12) do={:set M (\$M - 12)}\r\ + \n :set MM [:pick (100 + \$M) 1 3];\r\ + \n }\r\ + \n :local yyyy [:pick \$vdate (\$vdoff->0) (\$vdoff->1)];\r\ + \n :local dd [:pick \$vdate (\$vdoff->4) (\$vdoff->5)];\r\ + \n :local HH [:pick \$vtime 0 2];\r\ + \n :local mm [:pick \$vtime 3 5];\r\ + \n :local ss [:pick \$vtime 6 8];\r\ + \n :local Return;\r\ + \n :if (\$mikro = true) do={\r\ + \n :set Return ([:pick \$yyyy 2 4] . [:pick \$dd 0 1] . [:pick \$HH 0\ + \_1] . [:pick \$mm 3 4] . [:pick \$ss 6 7]);\r\ + \n } else={\r\ + \n :set Return ([\$yyyy] . [\$MM] . [\$dd] . [\$HH] . [\$mm] . [\$ss]);\ + \r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set WaitForFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local WaitTime [ :totime \$2 ];\r\ + \n :global CleanFilePath;\r\ + \n :global EitherOr;\r\ + \n :global MAX;\r\ + \n :set FileName [ \$CleanFilePath \$FileName ];\r\ + \n :local Delay ([ \$MAX [ \$EitherOr \$WaitTime 2s ] 100ms ] / 9);\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } delay=\$Delay max=10;\r\ + \n } on-error={ }\r\ + \n :while ([ :len [ /file/find where name=\$FileName ] ] > 0) do={\r\ + \n :do {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } on-error={ }\r\ + \n :delay \$Delay;\r\ + \n :set Delay (\$Delay * 3 / 2);\r\ + \n }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set FileGet do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global WaitForFile;\r\ + \n :if ([ \$WaitForFile \$FileName 0s ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :local FileVal false;\r\ + \n :do {\r\ + \n :set FileVal [ /file/get \$FileName ];\r\ + \n } on-error={ }\r\ + \n :return \$FileVal;\r\ + \n}\r\ + \n\r\ + \n:set RmDir do={\r\ + \n :local DirName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing directory: \". \$DirName);\r\ + \n :local DirVal [ \$FileGet \$DirName ];\r\ + \n :if (\$DirVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n };\r\ + \n :if (\$DirVal->\"type\" != \"directory\") do={\r\ + \n :log error (\$0 . \" Directory '\" . \$DirName . \"' is not a direct\ + ory.\");\r\ + \n :return false;\r\ + \n };\r\ + \n :onerror Err {\r\ + \n /file/remove \$DirName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing directory '\" . \$DirName . \"' failed\ + : \" . \$Err);\r\ + \n :return false;\r\ + \n };\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set RmFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing file: \". \$FileName);\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if (\$FileVal->\"type\" = \"directory\" || \$FileVal->\"type\" = \"di\ + sk\") do={\r\ + \n :log error (\$0 . \" File '\" . \$FileName . \"' is not a file.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/remove \$FileName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing file '\" . \$FileName . \"' failed: \"\ + \_. \$Err); :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MkDir do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CleanFilePath;\r\ + \n :global FileGet;\r\ + \n :global RmDir;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :local MkTmpfs do={\r\ + \n :global WaitForFile;\r\ + \n :local TmpFs [ /disk/find where slot=tmpfs type=tmpfs ];\r\ + \n :if ([ :len \$TmpFs ] = 1) do={\r\ + \n :if ([ /disk/get \$TmpFs disabled ] = true) do={\r\ + \n :log info (\$0 . \" The tmpfs is disabled, enabling.\");\r\ + \n /disk/enable \$TmpFs;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :log info (\$0 . \" Creating disk of type tmpfs.\");\r\ + \n \$RmDir \"tmpfs\";\r\ + \n :onerror Err {\r\ + \n /disk/add slot=tmpfs type=tmpfs tmpfs-max-size=([ /system/resource\ + /get total-memory ] / 3);\r\ + \n \$WaitForFile \"tmpfs\";\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Creating disk of type tmpfs failed: \" . \$\ + Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :set Path [ \$CleanFilePath \$Path ];\r\ + \n :if (\$Path = \"\") do={\r\ + \n :return true;\r\ + \n }\r\ + \n :log debug (\$0 . \" Making directory: \" . \$Path);\r\ + \n :local PathVal [ \$FileGet \$Path ];\r\ + \n :if (\$PathVal->\"type\" = \"directory\") do={\r\ + \n :log debug (\$0 . \" ... which already exists.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :pick \$Path 0 5 ] = \"tmpfs\") do={\r\ + \n :if ([ \$MkTmpfs ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/add type=\"directory\" name=\$Path;\r\ + \n \$WaitForFile \$Path;\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Making directory '\" . \$Path . \"' failed: \ + \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MAX do={:if (\$1 > \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n:set MIN do={:if (\$1 < \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set CharacterReplace do={\r\ + \n :local String [ :tostr \$1 ];\r\ + \n :local ReplaceFrom [ :tostr \$2 ];\r\ + \n :local ReplaceWith [ :tostr \$3 ];\r\ + \n :local Return \"\";\r\ + \n :if (\$ReplaceFrom = \"\") do={:return \$String}\r\ + \n :while ([ :typeof [ :find \$String \$ReplaceFrom ] ] != \"nil\") do={\ + \r\ + \n :local Pos [ :find \$String \$ReplaceFrom ];\r\ + \n :set Return (\$Return . [ :pick \$String 0 \$Pos ] . \$ReplaceWith);\ + \r\ + \n :set String [ :pick \$String (\$Pos + [ :len \$ReplaceFrom ]) [ :len\ + \_\$String ] ];\r\ + \n }\r\ + \n :return (\$Return . \$String);\r\ + \n}\r\ + \n\r\ + \n:set IfThenElse do={\r\ + \n :if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1 ] = true) do={:return \ + \$2};\r\ + \n :return \$3;\r\ + \n}\r\ + \n\r\ + \n:set EitherOr do={\r\ + \n :global IfThenElse;\r\ + \n :if ([ :typeof \$1 ] = \"num\") do={:return [ \$IfThenElse (\$1 != 0) \ + \$1 \$2 ]}\r\ + \n :if ([ :typeof \$1 ] = \"time\") do={:return [ \$IfThenElse (\$1 > 0s)\ + \_\$1 \$2 ]}\r\ + \n# this works for boolean values, literal ones with parentheses\r\ + \n :return [ \$IfThenElse ([ :len [ :tostr \$1 ] ] > 0) \$1 \$2 ];\r\ + \n}\r\ + \n\r\ + \n:set ParseKeyValueStore do={\r\ + \n :local Source \$1;\r\ + \n :if ([ :pick \$Source 0 1 ] = \"{\") do={\r\ + \n :do {\r\ + \n :return [ :deserialize from=json \$Source ];\r\ + \n } on-error={ }\r\ + \n } \r\ + \n :if ([ :typeof \$Source ] != \"array\") do={:set Source [ :tostr \$1 ]\ + }\r\ + \n :local Result ({});\r\ + \n :foreach KeyValue in=[ :toarray \$Source ] do={\r\ + \n :if ([ :find \$KeyValue \"=\" ]) do={\r\ + \n :local Key [ :pick \$KeyValue 0 [ :find \$KeyValue \"=\" ] ];\r\ + \n :local Value [ :pick \$KeyValue ([ :find \$KeyValue \"=\" ] + 1) [\ + \_:len \$KeyValue ] ];\r\ + \n :if (\$Value=\"true\") do={:set Value true}\r\ + \n :if (\$Value=\"false\") do={:set Value false}\r\ + \n :set (\$Result->\$Key) \$Value;\r\ + \n } else={:set (\$Result->\$KeyValue) true}\r\ + \n }\r\ + \n :return \$Result;\r\ + \n}\r\ + \n:set RequiredRouterOS do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :local Required [ :tostr \$2 ];\r\ + \n :local Warn [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :global VersionToNum;\r\ + \n :if (!(\$Required ~ \"^\\\\d+\\\\.\\\\d+((alpha|beta|rc|\\\\.)\\\\d+|)\ + \\\$\")) do={\r\ + \n :log error (\$0 . \" No valid RouterOS version: \" . \$Required);\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ \$VersionToNum \$Required ] > [ \$VersionToNum [ /system/packag\ + e/update/get installed-version ] ]) do={\r\ + \n :if (\$Warn = \"true\") do={\r\ + \n :log warning (\$0 . \" This \" . [ \$IfThenElse ([ :pick \$Caller \ + 0 ] = (\"\\\$\")) \"function\" \"script\" ] . \\\r\ + \n \" '\" . \$Caller . \"' (at least specific functionality) requir\ + es RouterOS \" . \$Required . \". Please update!\");\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set ValidateSyntax do={\r\ + \n :local Code [ :tostr \$1 ];\r\ + \n :global LogPrint;\r\ + \n :onerror Err {\r\ + \n [ :parse (\":local Validate do={\\n\" . \$Code . \"\\n}\") ];\r\ + \n } do={\r\ + \n \$LogPrint debug \$0 (\"Valdation failed: \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set VersionToNum do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Multi 0x1000000;\r\ + \n :local Return 0;\r\ + \n :global CharacterReplace;\r\ + \n :set Input [ \$CharacterReplace \$Input \".\" \",\" ];\r\ + \n :foreach I in={ \"zero\"; \"alpha\"; \"beta\"; \"rc\" } do={\r\ + \n :set Input [ \$CharacterReplace \$Input \$I (\",\" . \$I . \",\") ];\ + \r\ + \n }\r\ + \n :foreach Value in=([ :toarray \$Input ], 0) do={\r\ + \n :local Num [ :tonum \$Value ];\r\ + \n :if (\$Multi = 0x100) do={\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={\r\ + \n :set Return (\$Return + 0xff00);\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n } else={\r\ + \n :if (\$Value = \"zero\") do={ }\r\ + \n :if (\$Value = \"alpha\") do={ :set Return (\$Return + 0x3f00); \ + }\r\ + \n :if (\$Value = \"beta\") do={ :set Return (\$Return + 0x5f00); }\ + \r\ + \n :if (\$Value = \"rc\") do={ :set Return (\$Return + 0x7f00); }\r\ + \n }\r\ + \n }\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={ :set Return (\$Return + (\$Va\ + lue * \$Multi)); }\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ScriptLock do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local WaitMax [ :totime \$2 ];\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global ScriptLockOrder;\r\ + \n :if ([ :typeof \$ScriptLockOrder ] = \"nothing\") do={\r\ + \n :set ScriptLockOrder ({});\r\ + \n }\r\ + \n :if ([ :typeof (\$ScriptLockOrder->\$Script) ] = \"nothing\") do={\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :local JobCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :return [ :len [ /system/script/job/find where script=\$Script ] ];\ + \r\ + \n }\r\ + \n :local TicketCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :local Count 0;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:set Count (\$Count \ + + 1)}\r\ + \n }\r\ + \n :return \$Count;\r\ + \n }\r\ + \n :local IsFirstTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Check [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if (\$Ticket = \$Check) do={:return true}\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\" && \$Ticket != \$Check) d\ + o={:return false}\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :local AddTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Add [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :while (true) do={\r\ + \n :local Pos [ :len (\$ScriptLockOrder->\$Script) ];\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Pos) \$Add;\r\ + \n :delay 10ms;\r\ + \n :if ((\$ScriptLockOrder->\$Script->\$Pos) = \$Add) do={:return tru\ + e}\r\ + \n }\r\ + \n }\r\ + \n :local RemoveTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Remove [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Id,Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :while ((\$ScriptLockOrder->\$Script->\$Id) = \$Remove) do={\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Id);\r\ + \n :delay 10ms;\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n :local CleanupTickets do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:return false}\r\ + \n }\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :if ([ :typeof \$WaitMax ] = \"nil\" ) do={:set WaitMax 0s}\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\r\ + \n :log error (\$0 . \" A script named '\" . \$Script . \"' does not ex\ + ist!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$JobCount \$Script ] = 0) do={\r\ + \n \$LogPrint error \$0 (\"No script '\" . \$Script . \"' is running!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$TicketCount \$Script ] >= [ \$JobCount \$Script ]) do={\r\ + \n :log error (\$0 . \" More tickets than running scripts '\" . \$Scrip\ + t . \"', resetting!\");\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n /system/script/job/remove [ find where script=\$Script ];\r\ + \n }\r\ + \n :local MyTicket [ \$GetRandom20CharAlNum 6 ];\r\ + \n \$AddTicket \$Script \$MyTicket;\r\ + \n :local WaitInterval (\$WaitMax / 20);\r\ + \n :local WaitTime \$WaitMax;\r\ + \n :while (\$WaitTime > 0 && \\\r\ + \n ([ \$IsFirstTicket \$Script \$MyTicket ] = false || \\\r\ + \n [ \$TicketCount \$Script ] < [ \$JobCount \$Script ])) do={\r\ + \n :set WaitTime (\$WaitTime - \$WaitInterval);\r\ + \n :delay \$WaitInterval;\r\ + \n }\r\ + \n :if ([ \$IsFirstTicket \$Script \$MyTicket ] = true && \\\r\ + \n [ \$TicketCount \$Script ] = [ \$JobCount \$Script ]) do={\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$CleanupTickets \$Script;\r\ + \n :return true;\r\ + \n }\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$LogPrint debug \$0 (\"Script '\" . \$Script . \"' started more than \ + once\" . \\\r\ + \n [ \$IfThenElse (\$WaitTime < \$WaitMax) \" and timed out waiting for\ + \_lock\" \"\" ] . \"...\");\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set SendNotification do={ :onerror Err {\r\ + \n :global SendNotification2;\r\ + \n \$SendNotification2 ({ origin=\$0; subject=\$1; message=\$2; link=\$3;\ + \_silent=\$4 });\r\ + \n} do={:global ExitOnError; \$ExitOnError \$0 \$Err}\r\ + \n}\r\ + \n\r\ + \n:set SendNotification2 do={\r\ + \n :local Notification \$1;\r\ + \n :global NotificationFunctions;\r\ + \n :foreach FunctionName,Discard in=\$NotificationFunctions do={\r\ + \n (\$NotificationFunctions->\$FunctionName) (\"\\\$NotificationFunctio\ + ns->\\\"\" . \$FunctionName . \"\\\"\") \$Notification;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set SymbolByUnicodeName do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :global EitherOr;\r\ + \n :global SymbolsExtra;\r\ + \n :local Symbols ({\r\ + \n \"abacus\"=\"\\F0\\9F\\A7\\AE\";\r\ + \n \"alarm-clock\"=\"\\E2\\8F\\B0\";\r\ + \n \"arrow-down\"=\"\\E2\\AC\\87\";\r\ + \n \"arrow-up\"=\"\\E2\\AC\\86\";\r\ + \n \"calendar\"=\"\\F0\\9F\\93\\85\";\r\ + \n \"card-file-box\"=\"\\F0\\9F\\97\\83\";\r\ + \n \"chart-decreasing\"=\"\\F0\\9F\\93\\89\";\r\ + \n \"chart-increasing\"=\"\\F0\\9F\\93\\88\";\r\ + \n \"cloud\"=\"\\E2\\98\\81\";\r\ + \n \"cross-mark\"=\"\\E2\\9D\\8C\";\r\ + \n \"earth\"=\"\\F0\\9F\\8C\\8D\";\r\ + \n \"fire\"=\"\\F0\\9F\\94\\A5\";\r\ + \n \"floppy-disk\"=\"\\F0\\9F\\92\\BE\";\r\ + \n \"gear\"=\"\\E2\\9A\\99\";\r\ + \n \"heart\"=\"\\E2\\99\\A5\";\r\ + \n \"high-voltage-sign\"=\"\\E2\\9A\\A1\";\r\ + \n \"incoming-envelope\"=\"\\F0\\9F\\93\\A8\";\r\ + \n \"information\"=\"\\E2\\84\\B9\";\r\ + \n \"large-orange-circle\"=\"\\F0\\9F\\9F\\A0\";\r\ + \n \"large-red-circle\"=\"\\F0\\9F\\94\\B4\";\r\ + \n \"link\"=\"\\F0\\9F\\94\\97\";\r\ + \n \"lock-with-ink-pen\"=\"\\F0\\9F\\94\\8F\";\r\ + \n \"memo\"=\"\\F0\\9F\\93\\9D\";\r\ + \n \"mobile-phone\"=\"\\F0\\9F\\93\\B1\";\r\ + \n \"pushpin\"=\"\\F0\\9F\\93\\8C\";\r\ + \n \"scissors\"=\"\\E2\\9C\\82\";\r\ + \n \"scroll\"=\"\\F0\\9F\\93\\9C\";\r\ + \n \"smiley-partying-face\"=\"\\F0\\9F\\A5\\B3\";\r\ + \n \"smiley-smiling-face\"=\"\\E2\\98\\BA\";\r\ + \n \"smiley-winking-face-with-tongue\"=\"\\F0\\9F\\98\\9C\";\r\ + \n \"sparkles\"=\"\\E2\\9C\\A8\";\r\ + \n \"speech-balloon\"=\"\\F0\\9F\\92\\AC\";\r\ + \n \"star\"=\"\\E2\\AD\\90\";\r\ + \n \"warning-sign\"=\"\\E2\\9A\\A0\";\r\ + \n \"white-heavy-check-mark\"=\"\\E2\\9C\\85\"\r\ + \n }, \$SymbolsExtra);\r\ + \n :local Magic [ :pick [ /system/clock/get date ] 4 10 ];\r\ + \n :local Special {\r\ + \n \"information-04-01\"=\"\\F0\\9F\\9A\\BB\";\r\ + \n \"large-orange-circle-04-01\"=\"\\F0\\9F\\8D\\8A\";\r\ + \n \"large-orange-circle-10-31\"=\"\\F0\\9F\\8E\\83\";\r\ + \n \"large-red-circle-04-01\"=\"\\F0\\9F\\8D\\92\" };\r\ + \n :if ([ :len (\$Symbols->\$Name) ] = 0) do={\r\ + \n :log warning (\$0 . \"No symbol available for name '\" . \$Name . \"\ + '!\");\r\ + \n :return \"\";\r\ + \n }\r\ + \n :return ([ \$EitherOr (\$Special->(\$Name . \$Magic)) (\$Symbols->\$Na\ + me) ] . \"\\EF\\B8\\8F\");\r\ + \n}\r\ + \n\r\ + \n:set SymbolForNotification do={\r\ + \n :global NotificationsWithSymbols;\r\ + \n :global SymbolByUnicodeName;\r\ + \n :global IfThenElse;\r\ + \n :if (\$NotificationsWithSymbols != true) do={:return [ \$IfThenElse ([\ + \_:len \$2 ] > 0) ([ :tostr \$2 ] . \" \") \"\" ]}\r\ + \n :local Return \"\";\r\ + \n :foreach Symbol in=[ :toarray \$1 ] do={:set Return (\$Return . [ \$Sy\ + mbolByUnicodeName \$Symbol ])}\r\ + \n :return (\$Return . \" \");\r\ + \n}\r\ + \n\r\ + \n:set Unix2Dos do={:return [ :tocrlf [ :tostr \$1 ] ]}\r\ + \n:set UrlEncode do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"\\n\\r !\\\"#\\\$%&'()*+,:;<=>\?@[\\\\]^`{|}~\");\r\ + \n :local Subs { \"%0A\"; \"%0D\"; \"%20\"; \"%21\"; \"%22\"; \"%23\"; \"\ + %24\"; \"%25\"; \"%26\"; \"%27\";\r\ + \n \"%28\"; \"%29\"; \"%2A\"; \"%2B\"; \"%2C\"; \"%3A\"; \"%3B\"; \ + \"%3C\"; \"%3D\"; \"%3E\"; \"%3F\";\r\ + \n \"%40\"; \"%5B\"; \"%5C\"; \"%5D\"; \"%5E\"; \"%60\"; \"%7B\"; \ + \"%7C\"; \"%7D\"; \"%7E\" };\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :local Replace [ :find \$Chars \$Char ];\r\ + \n :if ([ :typeof \$Replace ] = \"num\") do={:set Char (\$Subs->\$Repla\ + ce)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set IsDNSResolving do={\r\ + \n :do {:resolve \"api.telegram.org\"} on-error={:return false}\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsFullyConnected do={\r\ + \n :global IsDNSResolving;\r\ + \n :global IsTimeSync;\r\ + \n :if ([ \$IsDNSResolving ] = false) do={:return false};\r\ + \n :if ([ \$IsTimeSync ] = false) do={:return false};\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsTimeSync do={\r\ + \n :global IsTimeSyncCached;\r\ + \n :global IsTimeSyncResetNtp;\r\ + \n :if (\$IsTimeSyncCached = true) do={:return true}\r\ + \n :if ([ /system/ntp/client/get enabled ] = true) do={\r\ + \n :if ([ /system/ntp/client/get status ] = \"synchronized\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :local Uptime [ /system/resource/get uptime ];\r\ + \n :if ([ :typeof \$IsTimeSyncResetNtp ] = \"nothing\") do={\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n }\r\ + \n :if (\$Uptime - \$IsTimeSyncResetNtp < 3m) do={:return false}\r\ + \n :log warning \"\$0 The ntp client is configured, but did not sync.\"\ + ;\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n /system/ntp/client/set enabled=no;\r\ + \n :delay 20ms;\r\ + \n /system/ntp/client/set enabled=yes;\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ /system/license/get ]->\"level\" = \"free\" || \\\r\ + \n [ /system/resource/get ]->\"board-name\" = \"x86\") do={\r\ + \n :log debug \"\$0 No ntp client configured, relying on RTC for CHR fr\ + ee license and x86.\";\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ /ip/cloud/get update-time ] = true) do={\r\ + \n :if ([ :typeof [ /ip/cloud/get public-address ] ] = \"ip\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :log debug \"\$0 No time source configured! Returning gracefully...\";\ + \r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set FetchHugeFiles do={\r\ + \n :local ScriptName [ :tostr \$1 ];\r\ + \n :local Url [ :tostr \$2 ];\r\ + \n :local CheckCert [ :tostr \$3 ];\r\ + \n\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global MkDir;\r\ + \n :global RmDir;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :set CheckCert [ \$IfThenElse (\$CheckCert = \"false\") \"no\" \"yes-w\ + ithout-crl\" ];\r\ + \n\r\ + \n :local DirName (\"tmpfs/\" . [ \$CleanName \$ScriptName ]);\r\ + \n :if ([ \$MkDir \$DirName ] = false) do={\r\ + \n \$LogPrint error \$0 (\"Failed creating directory!\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileName (\$DirName . \"/\" . [ \$CleanName \$0 ] . \"-\" . [ \ + \$GetRandom20CharAlNum ]);\r\ + \n :onerror Err {\r\ + \n /tool/fetch check-certificate=\$CheckCert \$Url dst-path=\$FileName \ + \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$ScriptName ] }) as-va\ + lue;\r\ + \n } do={\r\ + \n :if ([ \$WaitForFile \$FileName 500ms ] = true) do={\r\ + \n \$RmFile \$FileName;\r\ + \n }\r\ + \n \$LogPrint debug \$0 (\"Failed downloading from \" . \$Url . \" - \"\ + \_. \$Err);\r\ + \n \$RmDir \$DirName;\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$WaitForFile \$FileName 5s ] = false) do={\r\ + \n \$LogPrint debug \$0 (\"The file downloaded from \" . \$Url . \" did\ + \_not show up.\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileSize [ /file/get \$FileName size ];\r\ + \n :local Return \"\";\r\ + \n :local VarSize 0;\r\ + \n :while (\$VarSize != \$FileSize) do={\r\ + \n :set Return (\$Return . ([ /file/read offset=\$VarSize chunk-size=32\ + 768 file=\$FileName as-value ]->\"data\"));\r\ + \n :set FileSize [ /file/get \$FileName size ];\r\ + \n :set VarSize [ :len \$Return ];\r\ + \n :if (\$VarSize > \$FileSize) do={\r\ + \n :delay 100ms;\r\ + \n }\r\ + \n }\r\ + \n \$RmDir \$DirName;\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FetchUserAgentStr do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global CommitBrief;\r\ + \n :global IfThenElse;\r\ + \n :local Resource [ /system/resource/get ];\r\ + \n :return (\"User-Agent: Mikrotik/\" . \$Resource->\"version\" . \" \" .\ + \_\$Resource->\"architecture-name\" . \\\r\ + \n \" \" . \$Caller . \"/Fetch (\" . \$GlobalScriptsUrl . \"; \" . [ \$\ + CommitBrief ] . \")\");\r\ + \n}\r\ + \n\r\ + \n:set ScriptInstallUpdate do={ :onerror Err {\r\ + \n :local Scripts [ :toarray \$1 ];\r\ + \n :local NewComment [ :tostr \$2 ];\r\ + \n :global InitSetup;\r\ + \n\r\ + \n :global CommitId;\r\ + \n :global ScriptUpdatesCRLF;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global CertificateAvailable;\r\ + \n :global CheckSumsVerify;\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global SumsDirAndFileName;\r\ + \n :global GlobalScriptsUrlSuffix;\r\ + \n :global EitherOr;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global Grep;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global ParseKeyValueStore;\r\ + \n :global RequiredRouterOS;\r\ + \n :global SendNotification2;\r\ + \n :global SymbolForNotification;\r\ + \n :global ValidateSyntax;\r\ + \n :global CommentsInScripts;\r\ + \n\r\ + \n :if ([ \$CertificateAvailable \"Root YE\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, trying wit\ + hout.\");\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=\$Scripts do={\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\ + \r\ + \n \$LogPrint info \$0 (\"Adding new script: \" . \$Script);\r\ + \n /system/script/add name=\$Script owner=\$Script source=\"#!rsc by \ + Vados\\n\" comment=\$NewComment;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :local CommitIdBefore \$CommitId;\r\ + \n :local GlobalConfVersionBefore \$GlobalConfVersion;\r\ + \n :local ReloadGlobal false;\r\ + \n :local DeviceMode [ /system/device-mode/get ];\r\ + \n\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSums ({});\r\ + \n :do {\r\ + \n :local Url (\$GlobalScriptsUrl . \$SumsDirAndFileName . \$GlobalScr\ + iptsUrlSuffix);\r\ + \n \$LogPrint debug \$0 (\"Fetching checksums from url: \" . \$Url);\ + \r\ + \n :set CheckSums [ :deserialize from=json ([ /tool/fetch check-certi\ + ficate=yes-without-crl \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url output\ + =user as-value ]->\"data\") ];\r\ + \n } on-error={ \$LogPrint warning \$0 (\"Err = \" . \$Err); }\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=[ /system/script/find where source~\"^#!rsc by Vado\ + s\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :local ScriptInfo [ \$ParseKeyValueStore (\$ScriptVal->\"comment\") \ + ];\r\ + \n :local SourceNew;\r\ + \n\r\ + \n :foreach Scheduler in=[ /system/scheduler/find where on-event~(\"\\\ + \\b\" . \$ScriptVal->\"name\" . \"\\\\b\") ] do={\r\ + \n :local SchedulerVal [ /system/scheduler/get \$Scheduler ];\r\ + \n :if (\$ScriptVal->\"policy\" != \$SchedulerVal->\"policy\") do={\r\ + \n \$LogPrint warning \$0 (\"Policies differ for script '\" . \$Scr\ + iptVal->\"name\" . \"' and its scheduler '\" . \$SchedulerVal->\"name\" . \ + \"'!\");\r\ + \n }\r\ + \n }\r\ + \n :do {\r\ + \n :if (\$ScriptInfo->\"ignore\" = true) do={\r\ + \n \$LogPrint debug \$0 (\"Ignoring script '\" . \$ScriptVal->\"nam\ + e\" . \"', as requested.\");\r\ + \n :error true;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSum (\$CheckSums->(\$ScriptVal->\"name\"));\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Scri\ + ptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :convert transform=md5 to=hex [ :tolf (\$ScriptVal->\"sou\ + rce\") ] ] = \$CheckSum) do={\r\ + \n \$LogPrint debug \$0 (\"Checksum for script '\" . \$ScriptVal-\ + >\"name\" . \"' matches, ignoring.\");\r\ + \n :error true;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len (\$ScriptInfo->\"certificate\") ] > 0) do={\r\ + \n :if ([ \$CertificateAvailable (\$ScriptInfo->\"certificate\") \"\ + fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, tryi\ + ng without.\");\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n :local BaseUrl [ \$EitherOr (\$ScriptInfo->\"base-url\") \$Globa\ + lScriptsUrl ];\r\ + \n :local UrlSuffix [ \$EitherOr (\$ScriptInfo->\"url-suffix\") \$G\ + lobalScriptsUrlSuffix ];\r\ + \n :local Url (\$BaseUrl . \$ScriptVal->\"name\" . \".rsc\" . \$Url\ + Suffix);\r\ + \n \$LogPrint warning \$0 (\"Fetching script '\" . (\$ScriptVal->\"\ + name\") . \"' from url: \" . \$Url);\r\ + \n :local Result [ /tool/fetch check-certificate=yes-without-crl \\\ + \r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url outp\ + ut=user as-value ];\r\ + \n :if (\$Result->\"status\" = \"finished\") do={\r\ + \n :set SourceNew [ :tolf (\$Result->\"data\") ];\r\ + \n } \r\ + \n } do={\r\ + \n \$LogPrint warning \$0 (\"Failed fetching script '\" . \$ScriptV\ + al->\"name\" . \"': \" . \$Err);\r\ + \n :if (\$Err != \"Fetch failed with status 404\") do={\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$ScriptVal->\"source\" = \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Removing dummy. Typo on installation\ + \?\");\r\ + \n /system/script/remove \$Script;\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Sc\ + riptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :len \$CheckSum ] = 0) do={\r\ + \n \$LogPrint warning \$0 (\"Added the script manually\? Skip u\ + pdates with 'ignore=true' in comment.\");\r\ + \n }\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$SourceNew ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"No update for script '\" . (\$ScriptVal-\ + >\"name\") . \"'.\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :local SourceCRLF [ :tocrlf \$SourceNew ];\r\ + \n :if (\$InitSetup != true && ((\$ScriptVal->\"name\") != \"AM-Globa\ + lConfig\" || (\$ScriptVal->\"name\") != \"AM-GlobalFunc\")) do={\r\ + \n :if (\$SourceNew = (\$ScriptVal->\"source\") || \$SourceCRLF = (\ + \$ScriptVal->\"source\")) do={\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\") \ + . \"' source did not change. End of lines set to CRLF.\");\r\ + \n if (\$ScriptUpdatesCRLF = false) do={\r\ + \n :put [ :tolf \$SourceNew ];\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\"\ + ) . \"' source update end of lines to LF.\");\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([ :pick \$SourceNew 0 14 ] != \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Looks like new script '\" . \$ScriptVa\ + l->\"name\" . \"' is not valid (missing shebang = '\$shebang') Ignoring!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if (\$CommentsInScripts = true) do={\r\ + \n :local CommentLine [ \$Grep \$SourceNew (\"\\23 Script comment:\ + \_\") ];\r\ + \n :if ([ :len \$CommentLine ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"The search in new script '\" . (\$Scr\ + iptVal->\"name\") . \"' for comment in script body, is not found result Ig\ + noring!\");\r\ + \n :error false;\r\ + \n } else={\r\ + \n :if ([ :len (\$ScriptVal->\"comment\")] = 0 || [ :len (\$Scri\ + ptVal->\"comment\")] != [ :pick \$CommentLine ([ :find \$CommentLine \":\"\ + ] + 2) [ :len \$CommentLine] ]) do={\r\ + \n \$LogPrint debug \$0 (\"Exsiting comment for script '\" . (\ + \$ScriptVal->\"name\") . \"' is '\" . (\$ScriptVal->\"comment\") . \"'.\")\ + ;\r\ + \n :set (\$ScriptVal->\"comment\") [ :pick \$CommentLine ([ :f\ + ind \$CommentLine \":\"] + 2) [ :len \$CommentLine] ];\r\ + \n \$LogPrint debug \$0 (\"New comment (\" . (\$ScriptVal->\"c\ + omment\") . \") for script '\" . (\$ScriptVal->\"name\") . \"' has been ad\ + ded!\");\r\ + \n } else={\r\ + \n \$LogPrint debug \$0 (\"Comment for script '\" . (\$ScriptV\ + al->\"name\") . \"' did not change.\");\r\ + \n :error false;\r\ + \n }\r\ + \n } \r\ + \n }\r\ + \n\r\ + \n :local RequiredROS ([ \$ParseKeyValueStore [ \$Grep \$SourceNew (\ + \"\\23 requires RouterOS, \") ] ]->version);\r\ + \n :if ([ \$RequiredRouterOS \$0 [ \$EitherOr \$RequiredROS \"0.0\" ]\ + \_false ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires RouterOS \" . \\\r\ + \n \$RequiredROS . \", which is not met by your installation. I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :local RequiredDM [ \$ParseKeyValueStore [ \$Grep \$SourceNew (\"\ + \\23 requires device-mode, \") ] ];\r\ + \n :local MissingDM ({});\r\ + \n :foreach Feature,Value in=\$RequiredDM do={\r\ + \n :if ([ :typeof (\$DeviceMode->\$Feature) ] = \"bool\" && (\$Devi\ + ceMode->\$Feature) = false) do={\r\ + \n :set MissingDM (\$MissingDM, \$Feature);\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len \$MissingDM ] > 0) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires disabled \" . \\\r\ + \n \"device-mode features (\" . [ :tostr \$MissingDM ] . \"). I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$ValidateSyntax \$SourceNew ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Syntax validation for script '\" . (\$\ + ScriptVal->\"name\") . \"' failed! Ignoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n \r\ + \n :log info (\"Updating script: \" . (\$ScriptVal->\"name\") . \" Sc\ + ript comment: \" . (\$ScriptVal->\"comment\"));\r\ + \n\r\ + \n /system/script/set owner=(\$ScriptVal->\"name\") comment=(\$Script\ + Val->\"comment\") \\\r\ + \n source=[ \$IfThenElse (\$ScriptUpdatesCRLF = true) \$SourceCRL\ + F \$SourceNew ] \$Script;\r\ + \n :if (\$ScriptVal->\"name\" = \"AM-GlobalConfig\" || \\\r\ + \n \$ScriptVal->\"name\" = \"AM-GlobalFunc\" || \\\r\ + \n \$ScriptVal->\"name\" ~ (\"^(AM-GlobalFunc\\\\.d|mod)/.\")) d\ + o={\r\ + \n :set ReloadGlobal true;\r\ + \n }\r\ + \n } on-error={ }\r\ + \n }\r\ + \n :if (\$ReloadGlobal = true) do={\r\ + \n \$LogPrint info \$0 (\"Reloading global configuration and functions.\ + \");\r\ + \n :onerror Err {\r\ + \n :global InitSetup (a);\r\ + \n /system/script {run AM-GlobalConfig; run AM-GlobalFunc;};\r\ + \n } do={\$LogPrint error \$0 (\"Reloading global configuration and fun\ + ctions failed! \" . \$Err)}\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore > \$GlobalConfVersion) do={\r\ + \n \$LogPrint warning \$0 (\"The configuration version decreased from \ + \" . \\\r\ + \n \$GlobalConfVersionBefore . \" to \" . \$GlobalConfVersion . \\\r\ + \n \". Installed an older version\?\");\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore < \$GlobalConfVersion) do={\r\ + \n :global GlobalConfChanges;\r\ + \n :global GlobalConfMigration;\r\ + \n :local ChangeLogCode;\r\ + \n :if ([ :len \$ChangeLogCode ] > 0) do={\r\ + \n :if ([ \$ValidateSyntax \$ChangeLogCode ] = true) do={\r\ + \n :onerror Err {\r\ + \n [ :parse \$ChangeLogCode ];\r\ + \n } do={\$LogPrint warning \$0 (\"The changelog failed to run: \" \ + . \$Err)}\r\ + \n } else={\$LogPrint warning \$0 (\"The changelog failed syntax vali\ + dation!\")}\r\ + \n }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Migration (\$GlobalConfMigration->[ :tostr \$I ]);\r\ + \n :do {\r\ + \n :if ([ :typeof \$Migration ] != \"str\") do={\r\ + \n \$LogPrint debug \$0 (\"Migration code for change \" . \$I .\ + \_\" is not available.\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$ValidateSyntax \$Migration ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Migration code for change \" . \$I\ + \_. \" failed syntax validation!\");\r\ + \n :error false}\r\ + \n \$LogPrint info \$0 (\"Applying migration for change \" . \$I \ + . \": \" . \$Migration);\r\ + \n :onerror Err {\r\ + \n [ :parse \$Migration ];\r\ + \n } do={\$LogPrint warning \$0 (\"Migration code for change \" .\ + \_\$I . \" failed to run: \" . \$Err)}\r\ + \n } on-error={ }\r\ + \n }\r\ + \n }\r\ + \n :local NotificationMessage (\"The configuration version on \" . \$Id\ + entity . \" increased \" . \\\r\ + \n \"to \" . \$GlobalConfVersion . \", current configuration may nee\ + d modification. \" . \\\r\ + \n \"Please review and update global-config-overlay, then re-run glo\ + bal-config.\");\r\ + \n \$LogPrint info \$0 (\$NotificationMessage);\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nChanges:\ + \");\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Change (\$GlobalConfChanges->[ :tostr \$I ]);\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n \" . \\\ + \r\ + \n [ \$SymbolForNotification \"pushpin\" \"*\" ] . \$Change);\r\ + \n \$LogPrint info \$0 (\"Change \" . \$I . \": \" . \$Change);\r\ + \n }\r\ + \n } else={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nNews and\ + \_changes are not available.\");\r\ + \n\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={ :set GlobalConfChanges; \ + } else={ :global GlobalConfChanges (a); }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={ :set GlobalConfMigrati\ + on; } else={ :global GlobalConfMigration (a); }\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n\r\ + \n# load modules\r\ + \n:foreach Script in=[ /system/script/find where name ~ \"^(global-functio\ + ns\\\\.d|mod)/.\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :if ([ \$ValidateSyntax (\$ScriptVal->\"source\") ] = true) do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' f\ + ailed to run: \" . \$Err);\r\ + \n }\r\ + \n } else={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' fai\ + led syntax validation, skipping.\");\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# Add and fix global scheduler\r\ + \n:put \"Add or/and fix Global scheduler...\";\r\ + \n:local OnEvent \"/system/script { run AM-GlobalConfig; run AM-GlobalFunc\ + ; }\";\r\ + \n:local Comment \"Every Day Set Global Environment Variables\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-SetGlobalEnv\" ] ] \ + = 0) do={\r\ + \n /system/scheduler/add name=\"AM-SetGlobalEnv\" comment=\$Comment on-ev\ + ent=\$OnEvent start-time=startup;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" comment ] != \$Comment)\ + \_do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" comment=\$Comment;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" on-event ] != \$OnEvent\ + ) do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" on-event=\$OnEvent;\r\ + \n}\r\ + \n# Add Update Script scheduler\r\ + \n:put \"Add or/and fix Update Script scheduler\";\r\ + \n:set OnEvent \":global ScriptInstallUpdate; \\\$ScriptInstallUpdate;\";\ + \r\ + \n:set Comment \"Every Day Check For Update Scripts\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-ScriptInstallUpdate\ + \" ] ] = 0) do={\r\ + \n/system/scheduler/add name=\"AM-ScriptInstallUpdate\" comment=\$Comment \ + start-time=startup interval=1d on-event=\$OnEvent;\r\ + \n}\r\ + \n\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady;\r\ + \n :retry { :if (\$GlobalConfReady != true) do={ :error (\"Global configs\ + \_not ready.\"); }; } delay=100ms max=5;\r\ + \n\r\ + \n:log warning (\"This script \$ScriptName Loaded \" . \\\r\ + \n [ \$IfThenElse (\$CommitId != \"unknown\") ([ \$CommitBrief ] . \" \ + \") ] . \\\r\ + \n \"on \" . \$devBoardName . \"\\nRouterOS: \" . \$devCurVersion . \"\ + \\nArchitecture: \" . \$devCurArch . \"\\nIdentity: \" . \$Identity);\r\ + \n # signal we are ready\r\ + \n:set GlobalFuncReady true;\r\ + \n:set ExitOK true;\r\ + \n\r\ + \n:log info (\$0 . \"Global configs ready!\");\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n}\r\ + \n" +add comment=\ + "Lookup IP addresses for RKN list and add them to firewall address-list" \ + dont-require-permissions=no name=AM-RKN-dnsLookup owner=AM-RKN-dnsLookup \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="#!rsc by Vados\r\ + \n# RouterOS script: AM-RKN-dnsLookup\r\ + \n# Script comment: Lookup IP addresses for RKN list and add them to firew\ + all address-list\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n# Script description: This script is intended to be run periodically, e.\ + g. once a day, to update the IP addresses in the firewall address-list bas\ + ed on the RKN list.\r\ + \n# It resolves the domain names in the RKN list and a\ + dds the corresponding IP addresses to the specified firewall address-list.\ + \r\ + \n# The script also handles CNAME records by following\ + \_them until it finds A records.\r\ + \n# Usage:\r\ + \n# /system/script { run AM-RKN-dnsLookup; }\r\ + \n:local ScriptName [ :jobname ];\r\ + \n\r\ + \n:global dnsLookupReady false;\r\ + \n:if ([:typeof \$dnsLookupReady] != \"boolean\") do={ :set dnsLookupReady\ + \_true; }\r\ + \nwhile (!\$dnsLookupReady) do={ :nothing; }\r\ + \n:set dnsLookupReady false;\r\ + \n\r\ + \n:local arrRKN {\r\ + \n \"telegra.ph\"\r\ + \n \"grani.ru\"\r\ + \n \"xhamster.com\"\r\ + \n \"lostfilm.tv\"\r\ + \n \"rutracker.org\"\r\ + \n \"lurkmore.to\"\r\ + \n \"dou.ua\"\r\ + \n \"youporn.com\"\r\ + \n \"pornolab.net\"\r\ + \n \"crimerussia.com\"\r\ + \n \"telegram.org\"\r\ + \n \"api.telegram.org\"\r\ + \n \"facebook.com\"\r\ + \n \"instagram.com\"\r\ + \n \"x.com\"\r\ + \n \"twitter.com\"\r\ + \n \"youtube.com\"\r\ + \n \"googleapis.com\"\r\ + \n \"youtu.be\"\r\ + \n \"googlevideo.com\"\r\ + \n \"gstatic.com\"\r\ + \n \"googleusercontent.com\"\r\ + \n \"nhacmp3youtube.com\"\r\ + \n \"telegram.me\"\r\ + \n \"t.me\";}\r\ + \n\r\ + \n:local ListRKN \"RKN-dnsLookup\";\r\ + \n:local ListComment (\"ListRKN managed by \" . \$ScriptName);\r\ + \n:foreach aListItem in=[/ip/firewall/address-list find list=\$ListRKN] do\ + ={\r\ + \n /ip firewall address-list remove \$aListItem;\r\ + \n}\r\ + \n:foreach aItem in=\$arrRKN do={\r\ + \n# :do {\r\ + \n :retry delay=500ms max=5 on-error={:log error \"dns resolve fail\ + ure [ \$aItem - resolve ip ]\"} command={:resolve \$aItem}\r\ + \n# } on-error={\r\ + \n# :log error \"dns resolve failure [ \$arItem - resolve ip ]\";\r\ + \n# }\r\ + \n :foreach dnsRecord in=[/ip dns cache all find where (name=\$aItem)] \ + do={\r\ + \n :if ([/ip dns cache all get \$dnsRecord type]=\"A\") do={\r\ + \n :local newAddress [/ip dns cache all get \$dnsRecord data];\ + \r\ + \n :do {\r\ + \n /ip firewall address-list add list=\$ListRKN address=\$newA\ + ddress comment=\$aItem;\r\ + \n :log warning \"Successfully adding ip to list A record: \ + \$dnsRecord, address: \$newAddress domain: \$aItem\";\r\ + \n } on-error={\r\ + \n :log error \"error when adding ip to list A record: \$dn\ + sRecord, address: \$newAddress domain: \$aItem\";\r\ + \n }\r\ + \n }\r\ + \n :if ([/ip dns cache all get \$dnsRecord type]=\"CNAME\") do={\r\ + \n :local cname;\r\ + \n :local nextCname;\r\ + \n :local cnameAddress;\r\ + \n :set cname [/ip dns cache all find where (name=\$aItem && ty\ + pe=\"CNAME\")];\r\ + \n :set nextCname [/ip dns cache all find where (name=[/ip dns \ + cache all get \$cname data] && type=\"CNAME\")];\r\ + \n :while (\$nextCname != \"\") do={\r\ + \n :set cname \$nextCname;\r\ + \n :set nextCname [/ip dns cache all find where (name=[/ip \ + dns cache all get \$cname data] && type=\"CNAME\")];\r\ + \n }\r\ + \n :foreach aRecord in=[/ip dns cache all find where (name=[/ip\ + \_dns cache all get \$cname data] && type=\"A\")] do={\r\ + \n :do {\r\ + \n :set cnameAddress [/ip dns cache all get \$aRecord d\ + ata];\r\ + \n /ip firewall address-list add list=\$ListRKN address\ + =\$cnameAddress comment=\$aItem;\r\ + \n :log warning \"Succesfully adding ip to list CNAME r\ + ecord: \$cname, A record: \$aRecord, address: \$newAddress, from domain: \ + \$aItem.\";\r\ + \n# /ip firewall raw add action=add-dst-to-address-list\ + \_address-list=\$ListRKN address-list-timeout=none-dynamic chain=preroutin\ + g comment=\$aItem content=\$aItem;\r\ + \n } on-error={\r\ + \n :log error \"error when adding ip to list CNAME reco\ + rd: \$cname, A record: \$aRecord, address: \$newAddress, from domain: \$aI\ + tem.\";\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:if ([ :len [ /system/scheduler/find where name=\$ScriptName ] ] = 0) do\ + ={\r\ + \n :log error (\$ScriptName . \"System Scheduler for script: \" . \$Scri\ + ptName . \" is NOT SET! Fix It Now!\");\r\ + \n /system/scheduler/add name=\$ScriptName on-event=\"/system/script { \ + run \$ScriptName; }\" comment=\"Update RKN DNS Looup\" interval=00:30:00 p\ + olicy=\"ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon\"\ + \_start-time=startup;\r\ + \n }\r\ + \n:set dnsLookupReady true;\r\ + \ndelay 2s; \r\ + \n:global dnsLookupReady (a);\r\ + \n" +add dont-require-permissions=no name="renameLists " owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + local oldName \"Test\";\ + \n:local newName \"Test-IP\";\ + \n/ip firewall address-list\ + \n:foreach i in=[find list=\$oldName] do={\ + \nset \$i list=\$newName;\ + \n}\ + \n" +add dont-require-permissions=no name=IPv6-Settings owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + #############Script Settings##################\ + \n\ + \n:local \"IF-TUN\" \"6to4-tun\"\ + \n:local \"IF-LOC\" \"Bridge-LAN\"\ + \n:local \"POOL-6TO4\" \"poolV6-local\"\ + \n:local \"force-update\" false\ + \n############ use then static ip #######################\ + \n:local \"lease-address\" [/ip address get [find interface=\$\"IF-LOC\"] \ + address]\ + \n#:local \"interface\" \"ext\"\ + \n:local bound 0\ + \n\ + \n###############################################\ + \n\ + \n:local 6to4prefix do={\ + \n :global dec2hex\ + \n :local ipv4 \$1\ + \n :local ipv6 \"2002:\"\ + \n :for i from=0 to=2 step=1 do={ \ + \n :if ( \$i = 2) do={\ + \n :set ipv6 (\$ipv6.\":\")\ + \n }\ + \n :local fp [:find \$ipv4 \".\"]\ + \n :if ( \$fp > 0 ) do={\ + \n :set ipv6 (\$ipv6.[\$dec2hex [:pick \$ipv4 0 \$fp]])\ + \n :set ipv4 [:pick \$ipv4 (\$fp + 1 ) [:len \$ipv4]]\ + \n }\ + \n }\ + \n :return (\$ipv6.[\$dec2hex \$ipv4])\ + \n}\ + \n:local \"str_replace\" do={\ + \n :local \"str-pos\" [:find \$1 \$2]\ + \n :local \"str-len\" [:len \$2]\ + \n :local str \"\"\ + \n :local rest \$1\ + \n :while (\$\"str-pos\" >= 0) do={\ + \n :local t [:pick \$rest 0 \$\"str-pos\"]\ + \n :if ( \$4 > 0 ) do={\ + \n :for i from=1 to=(\$4 - [:len \$t]) step=1 do={\ + \n :set str (\$str.\$5)\ + \n }\ + \n }\ + \n :set str (\$str.\$t.\$3)\ + \n :set rest [:pick \$rest (\$\"str-pos\"+\$\"str-len\") [:len \$re\ + st]]\ + \n :set \"str-pos\" [:find \$rest \$2]\ + \n }\ + \n :return (\$str.\$rest)\ + \n}\ + \n\ + \n:if (\$bound=1) do={\ + \n :local t [:find \$\"lease-address\" \"/\"]\ + \n\ + \n :if ( \$t >= 0 ) do={\ + \n :set \"lease-address\" [:pick \$\"lease-address\" 0 \$t]\ + \n }\ + \n\ + \n :if ( [/interface 6to4 print count-only where name=\$\"IF-TUN\"] = 0\ + ) do={\ + \n /interface 6to4 add !keepalive local-address=\$\"lease-address\"\ + \_mtu=1280 name=\$\"IF-TUN\" remote-address=10.30.30.0\ + \n :set \"force-update\" true\ + \n }\ + \n\ + \n :local \"IPv4-TUN\" [/interface 6to4 get [find name=\$\"IF-TUN\"] lo\ + cal-address];\ + \n\ + \n :if ( \$\"lease-address\" != \$\"IPv4-TUN\" || \$\"force-update\" = \ + true) do={\ + \n /interface 6to4 set [find name=\$\"IF-TUN\"] local-address=\$\"l\ + ease-address\"\ + \n /ipv6 address remove [find interface=\$\"IF-TUN\"]\ + \n /ipv6 address remove [find interface=\$\"IF-LOC\"]\ + \n /ipv6 route remove [find gateway ~ \$\"IF-TUN\" and dst-address=\ + \"::/0\"]\ + \n :local \"pool-prefix\" [\$6to4prefix \$\"lease-address\"]\ + \n :if ( [/ipv6 pool print count-only where name=\$\"POOL-6TO4\"] =\ + \_0) do={ \ + \n /ipv6 pool add name=\$\"POOL-6TO4\" prefix=(\$\"pool-prefix\ + \".\":1::/64\") prefix-length=64\ + \n } else={\ + \n /ipv6 pool set [/ipv6 pool find where name=\$\"POOL-6TO4\"] \ + prefix=(\$\"pool-prefix\".\":1::/64\")\ + \n }\ + \n /ipv6 address add address=(\$\"pool-prefix\".\"::\".[\$MAC2EUI64\ + \_[/interface get [find name=\$\"interface\"] mac-address]].\"/3\") interf\ + ace=\$\"IF-TUN\" advertise=no\ + \n /ipv6 address add eui-64=yes from-pool=\$\"POOL-6TO4\" interface\ + =\$\"IF-LOC\" advertise=yes\ + \n /ipv6 route add dst-address=::/0 gateway=(\"::10.30.30.81%\".\$\ + \"IF-TUN\")\ + \n :local t [/ipv6 address get [find interface=\$\"IF-LOC\"] addres\ + s]\ + \n :set t [:pick \$t 0 [:find \$t \"/\"]]\ + \n :if ( [/ipv6 dhcp-server option print count-only where name=DNS]\ + \_= 0) do={\ + \n /ipv6 dhcp-server option add code=23 name=DNS value=(\"0x\".\ + [\$\"str_replace\" \$t \":\" \"\" 4 \"0\"])\ + \n } else={\ + \n /ipv6 dhcp-server option set [/ipv6 dhcp-server option find \ + where name=DNS] value=(\"0x\".[\$\"str_replace\" \$t \":\" \"\" 4 \"0\"])\ + \n }\ + \n :if ( [/ipv6 dhcp-server print count-only where interface=\$\"IF\ + -LOC\"] = 0) do={\ + \n /ipv6 dhcp-server add address-pool=\$\"POOL-6TO4\" dhcp-opti\ + on=DNS interface=\$\"IF-LOC\" lease-time=1d name=\$\"IF-LOC\"\ + \n }\ + \n :if ( [ipv6 nd print count-only where interface=\$\"IF-LOC\"] = \ + 0) do={\ + \n /ipv6 nd add advertise-dns=no interface=\$\"IF-LOC\" other-c\ + onfiguration=yes\ + \n }\ + \n }\ + \n}" +add dont-require-permissions=no name=IPv6-GlobalFunc owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + global MAC2EUI64 do={\ + \n :local EUI64 \"\"\ + \n :local MAC \$1\ + \n :local HEX2DEC \"0123456789ABCDEF\"\ + \n :for i from=0 to=5 step=1 do={\ + \n :if ( \$i = 0 ) do={\ + \n :set EUI64 [:pick \$MAC 0]\ + \n :set EUI64 (\$EUI64.[:tostr [:pick \$\"HEX2DEC\" ([:find \$\ + \"HEX2DEC\" [:pick \$MAC 1]] ^ 2)]])\ + \n }\ + \n :if ( \$i = 1 || \$i = 3) do={\ + \n :set EUI64 (\$EUI64.\":\")\ + \n }\ + \n :if ( \$i = 2 ) do={\ + \n :set EUI64 (\$EUI64.\"FF:FE\")\ + \n }\ + \n :if ( [:find \$MAC \":\"] = 2 ) do={\ + \n :set MAC [:pick \$MAC 3 [:len \$MAC]]\ + \n :set EUI64 (\$EUI64.[:pick \$MAC 0 2])\ + \n }\ + \n }\ + \n :return (\$EUI64)\ + \n}\ + \n\ + \n:global dec2hex do={\ + \n :local hex \"\"\ + \n :local dec [:tonum \$1]\ + \n :for i from=0 to=4 step=4 do={\ + \n :set hex ([:pick \"0123456789ABCDEF\" ((\$dec>>\$i)&0xf) (((\$de\ + c>>\$i)&0xf)+1)].\$hex)\ + \n }\ + \n :return ([:tostr \$hex])\ + \n}" +add comment=Generator dont-require-permissions=no name=proxySet owner=vados \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="# 2. WireGuard interface\r\ + \n/interface/wireguard/add name=wg-VDSina private-key=\"4CHyNhbOs923/EyNDg\ + uMXJXdghL2I3UyzXPwrANVgHs=\" listen-port=21641\r\ + \n/interface/wireguard/peers/add interface=wg-VDSina public-key=\"o5IgCl1/\ + G6xbl6j7sPaP4ZNYREeYOmqZjKbm+v0b9jU=\" preshared-key=\"RNvF7T1cg5tyXcYQnD9\ + N5BEp6e20vVJQTv0GMjyvHU0=\" endpoint-address=10.30.30.33 endpoint-port=518\ + 20 allowed-address=0.0.0.0/0 persistent-keepalive=11\r\ + \n/ip/address/add address=10.9.0.3/32 interface=wg-VDSina\r\ + \n\r\ + \n# 3. DNS\r\ + \n{ :local rawDNS [/ip/dns/get servers]; :local prevDNS \"\"; :foreach s i\ + n=[:toarray \$rawDNS] do={ :if ([:len \$prevDNS] > 0) do={ :set prevDNS \"\ + \$prevDNS,\$s\" } else={ :set prevDNS \$s } }; /system/script/add name=VDS\ + ina-prev-dns comment=\"\$prevDNS\" source=\"\" }\r\ + \n/ip/dns/set servers=8.8.4.4\r\ + \n/ip/route/add dst-address=8.8.4.4/32 gateway=wg-VDSina distance=1 commen\ + t=VDSina-dns\r\ + \n\r\ + \n# 4. Enable WireGuard\r\ + \n/interface/wireguard/enable wg-VDSina\r\ + \n" +add dont-require-permissions=no name=AM-Telegram-Send owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\ + \n# RouterOS script: AM-Telegram-Send\ + \n# Script comment: Mini script for send messages to admin Telegram chat\ + \n# Copyright (c) 2007-2026 Vados \ + \n#\ + \n#\ + \n# requires RouterOS, version=7.19\ + \n# requires device-mode, fetch\ + \n#\ + \n# Script description: \ + \n#\ + \n#\ + \n# Usage:\ + \n# /system/script { run AM-Telegram-Send \"Message here )))\"; }\ + \n\ + \n/system/script\ + \nadd name=telegram-send dont-require-permissions=no source={\ + \n :global TelegramTokenId;\ + \n :global TelegramChatId;\ + \n :local message [:tostr \$1];\ + \n :local identity [/system/identity/get name];\ + \n :local sendMsg (\"[\".\$identity.\"] \".\$message)\ + \n /tool/fetch url=(\"https://api.telegram.org/bot\".\$TelegramTokenId.\\\ + \n \"/sendMessage\\\?chat_id=\".\$TelegramChatId.\\\ + \n \"&text=\".\$sendMsg.\\\ + \n \"&parse_mode=HTML\") \\\ + \n mode=https keep-result=no\ + \n}" +add dont-require-permissions=no name=telegram-send owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="\ + \n :global TelegramTokenId;\ + \n :global TelegramChatId;\ + \n :local message [:tostr \$1];\ + \n :local identity [/system/identity/get name];\ + \n :local sendMsg (\"[\".\$identity.\"] \".\$message)\ + \n /tool/fetch url=(\"https://api.telegram.org/bot\".\$TelegramTokenId.\\\ + \n \"/sendMessage\\\?chat_id=\".\$TelegramChatId.\\\ + \n \"&text=\".\$sendMsg.\\\ + \n \"&parse_mode=HTML\") \\\ + \n mode=https keep-result=no\ + \n" +/user group +add name=backup policy="ssh,read,write,policy,test,web,sniff,sensitive,api,res\ + t-api,!local,!telnet,!ftp,!reboot,!winbox,!password,!romon" +/ip smb +set enabled=no interfaces=lo +/interface bridge port +add bridge=Bridge-LAN interface=ether5 +add bridge=Bridge-LAN interface=ether2 +add bridge=Bridge-LAN interface=ether4 trusted=yes +add bridge=Bridge-LAN interface=ether3 +add bridge=Bridge-LAN interface=wlan2 +add bridge=Bridge-LAN interface=wlan1 +/interface bridge settings +set use-ip-firewall=yes +/ip firewall connection tracking +set udp-timeout=10s +/ip neighbor discovery-settings +set discover-interface-list=LAN lldp-med=no lldp-poe-power=no +/ip settings +set accept-redirects=yes ipv4-multipath-hash-policy=l4 max-neighbor-entries=\ + 8192 +/ipv6 settings +set disable-ipv6=yes disable-link-local-address=yes forward=no \ + max-neighbor-entries=8192 multipath-hash-policy=l4 +/interface detect-internet +set detect-interface-list=WAN internet-interface-list=Internet \ + lan-interface-list=LAN request-interval=1m wan-interface-list=WAN +/interface l2tp-server server +set authentication=mschap2 use-ipsec=yes +/interface list member +add interface=Unet-ether1 list=Internet +add interface=WG list=WAN +add interface=Bridge-LAN list=LAN +add interface=Unet-ether1 list=WAN +/interface wireguard peers +add allowed-address=0.0.0.0/0 client-allowed-address=::/0 endpoint-address=\ + 109.107.177.79 endpoint-port=29185 interface=WG name=VDSina \ + persistent-keepalive=10s public-key=\ + "zuRsO+dL/j6awaBWZ/NgnHR+bCNUjRWbaHFpdzkNnw4=" +add allowed-address=0.0.0.0/0 client-allowed-address=::/0 disabled=yes \ + endpoint-address=10.30.30.33 endpoint-port=55055 interface=wg-nuc name=\ + NUC persistent-keepalive=11s public-key=\ + "+OvzAjLC45Td52hjkQPmpGvC3RGhZ+Q2CDMJ1U/D4Rw=" +/interface wireless cap +# +set bridge=Bridge-LAN caps-man-addresses=10.30.30.10 \ + caps-man-certificate-common-names=CAPsMAN-51B25E507D07 certificate=\ + CAP-488F5A8E1ABC discovery-interfaces=Bridge-LAN enabled=yes interfaces=\ + wlan2,wlan1 lock-to-caps-man=yes +/ip address +add address=10.30.30.81/24 interface=Bridge-LAN network=10.30.30.0 +add address=10.8.0.2/24 interface=WG network=10.8.0.0 +add address=192.168.33.2/24 disabled=yes interface=wg-nuc network=\ + 192.168.33.0 +/ip cloud +set update-time=no +/ip dhcp-client +add add-default-route=special-classless check-gateway=arp comment=Unet-client \ + default-route-distance=2 default-route-tables=main:2 dhcp-options=\ + hostname,clientid_duid interface=Unet-ether1 name=UnetDHCP use-peer-dns=\ + no use-peer-ntp=no +/ip dhcp-server config +set accounting=no +/ip dhcp-server lease +add address=10.30.30.101 client-id=1:48:8f:5a:8e:ee:a8 mac-address=\ + 48:8F:5A:8E:EE:A8 server=masterDHCP +add address=10.30.30.107 client-id=1:f0:de:f1:91:d:1e mac-address=\ + F0:DE:F1:91:0D:1E server=masterDHCP +add address=10.30.30.83 client-id=1:0:80:77:ec:43:86 mac-address=\ + 00:80:77:EC:43:86 server=masterDHCP +add address=10.30.30.70 dhcp-option=domain-search-list,HostName \ + dhcp-option-set=HP1505n mac-address=00:1E:0B:1A:88:8D server=masterDHCP +add address=10.30.30.50 client-id=1:4:7c:16:36:f5:37 mac-address=\ + 04:7C:16:36:F5:37 server=masterDHCP +add address=10.30.30.71 client-id=1:10:2d:41:2c:20:c2 mac-address=\ + 10:2D:41:2C:20:C2 server=masterDHCP +add address=10.30.30.61 client-id=1:10:8:c1:81:59:5f mac-address=\ + 10:08:C1:81:59:5F server=masterDHCP +add address=10.30.30.72 client-id=1:10:38:1f:d2:5f:13 mac-address=\ + 10:38:1F:D2:5F:13 server=masterDHCP +add address=10.30.30.99 client-id=1:10:b1:df:e9:d0:d5 mac-address=\ + 10:B1:DF:E9:D0:D5 server=masterDHCP +add address=10.30.30.111 client-id=1:fe:2:52:be:3f:16 mac-address=\ + FE:02:52:BE:3F:16 server=masterDHCP +add address=10.30.30.92 client-id=1:d4:6d:6d:fd:e5:49 mac-address=\ + D4:6D:6D:FD:E5:49 server=masterDHCP +add address=10.30.30.93 client-id=1:7e:8b:1e:cb:b8:cb mac-address=\ + CC:42:10:12:46:94 server=masterDHCP +add address=10.30.30.94 client-id=1:50:cc:f8:85:96:a mac-address=\ + 50:CC:F8:85:96:0A server=masterDHCP +add address=10.30.30.82 client-id=1:e4:18:6b:58:34:ab mac-address=\ + E4:18:6B:58:34:AB server=masterDHCP +add address=10.30.30.75 client-id=1:0:27:15:41:97:a9 mac-address=\ + 00:27:15:41:97:A9 server=masterDHCP +add address=10.30.30.77 dhcp-option=HostName dhcp-option-set=StartLife1 \ + mac-address=C4:82:E1:03:49:10 server=masterDHCP +add address=10.30.30.73 client-id=1:10:38:1f:82:81:f2 mac-address=\ + 10:38:1F:82:81:F2 server=masterDHCP +add address=10.30.30.95 client-id=1:88:ae:dd:60:39:54 dhcp-option=HostName \ + dhcp-option-set=VADOS-NUC mac-address=88:AE:DD:60:39:54 server=masterDHCP +add address=10.30.30.98 client-id=1:c8:5b:76:d8:cc:bf dhcp-option=HostName \ + dhcp-option-set=VADOS-LEN mac-address=C8:5B:76:D8:CC:BF server=masterDHCP +add address=10.30.30.96 client-id=iPhone mac-address=4A:91:ED:0E:1F:39 \ + server=masterDHCP +add address=10.30.30.74 client-id=MiTV dhcp-option=HostName dhcp-option-set=\ + MiTV mac-address=20:72:A9:D3:34:36 server=masterDHCP +add address=10.30.30.30 dhcp-option=HostName dhcp-option-set=miller \ + mac-address=F0:75:5D:1E:5F:71 server=masterDHCP +add address=10.30.30.100 client-id=iVados dhcp-option=HostName \ + dhcp-option-set=iVados mac-address=DE:4D:72:F3:1A:9F server=masterDHCP +add address=10.30.30.10 allow-dual-stack-queue=yes client-id=\ + ff:0:0:0:df:0:3:0:1:f0:75:5d:1e:5f:85 dhcp-option=HostName \ + dhcp-option-set=MikroNUC mac-address=F0:75:5D:1E:5F:85 server=masterDHCP \ + use-src-mac=yes +/ip dhcp-server network +add address=10.30.30.0/24 caps-manager=10.30.30.10 dhcp-option=\ + domain-search-list dhcp-option-set=domain-search-set dns-server=\ + 10.30.30.33,109.107.177.79 domain=WORKGROUP gateway=10.30.30.81 netmask=\ + 24 ntp-server=10.30.30.81,10.30.30.10 wins-server=10.30.30.95,10.30.30.98 +/ip dns +set allow-remote-requests=yes cache-max-ttl=1d doh-max-concurrent-queries=100 \ + doh-max-server-connections=20 servers=8.8.8.8 verify-doh-cert=yes +/ip dns static +add address=109.107.177.79 comment="VDSina Server DNS" name=ns1.vados.ru \ + type=A +add address=109.107.177.79 name=vds.vados.ru type=A +add comment="Local DNS server for vados.ru" disabled=yes ns=10.30.30.81 \ + regexp=*.vados.ru type=NS +add comment="*.vados.ru Regexp" disabled=yes forward-to=10.30.30.81 regexp=\ + "\".*\\\\.vados\\\\.ru\"" type=FWD +add comment="*.30.30.10.in-addr.arpa Regexp" disabled=yes forward-to=\ + 10.30.30.81 regexp="\".*\\\\.30\\\\.30\\\\.10\\\\.in-addr\\\\.arpa" type=\ + FWD +add address=10.30.30.81 comment=NS.vados.ru.local disabled=yes name=\ + ns.vados.ru.local type=A +add comment="DNS ns.vados.ru.local" disabled=yes match-subdomain=yes name=\ + vados.ru ns=ns.vados.ru.local type=NS +add address=10.30.30.33 comment=reg.vados.ru name=reg.vados.ru type=A +add address=8.8.8.8 comment="Google-dns IPv4 1" match-subdomain=yes name=\ + dns.google type=A +add address=8.8.4.4 comment="Google-dns IPv4 2" name=dns.google type=A +add address=10.30.30.33 comment=AMSTER name=amster.vados.ru type=A +add address=10.30.30.33 name=ros.vados.ru type=A +add address=10.30.30.95 name=vados-nuc type=A +add address=10.30.30.33 name=amster-nuc type=A +add address=10.30.30.33 name=amster type=A +add address=10.30.30.33 name=amster-reg type=A +add address=10.30.30.33 disabled=yes name=mng.vados.ru type=A +add address=10.30.30.33 name=mgr.vados.ru type=A +add address=10.30.30.33 name=prt.vados.ru type=A +add address=10.30.30.33 disabled=yes match-subdomain=yes name=\ + mikrots.vados.ru type=A +add address=10.30.30.30 comment=MILLER name=miller.vados.ru type=A +add address=10.30.30.30 name=MILLER-LEN type=A +add address=10.30.30.30 name=miller type=A +add address=1.0.0.1 comment="CloudFlare-doh IPv4 2" disabled=yes name=\ + security.cloudflare-dns.com type=A +add address=1.1.1.1 comment="CloudFlare-doh IPv4 1" disabled=yes name=\ + security.cloudflare-dns.com type=A +add address=9.9.9.9 comment="Cuad9-dns IPv4 1" disabled=yes name=\ + dns.quad9.net type=A +add address=149.112.112.112 comment="Cuad9-dns IPv4 2" disabled=yes name=\ + dns.quad9.net type=A +add address=195.133.25.16 comment="DNS Comss" disabled=yes name=dns.comss.one \ + type=A +add address=10.30.30.81 comment=LocalNet name=MikroVados type=A +add address=10.30.30.10 name=MikroNUC type=A +add address=10.30.30.86 disabled=yes name=MikroYellow type=A +add address=10.30.30.85 disabled=yes name=MikroMic type=A +add address=10.30.30.95 name=VADOS-NUC type=A +add address=10.30.30.98 name=VADOS-LEN type=A +add address=91.192.189.2 comment=Unet-DNS disabled=yes name=ns1.unet.ws type=\ + A +add address=10.30.30.20 disabled=yes name=garage type=A +/ip firewall address-list +add address=91.215.120.206 list=TRUSTED +add address=185.71.70.78 disabled=yes list=TRUSTED +add address=176.53.203.171 disabled=yes list=TRUSTED +add address=176.53.203.170 disabled=yes list=TRUSTED +add address=176.53.202.194 disabled=yes list=TRUSTED +add address=8.8.4.0/24 list=YouTube +add address=8.8.8.0/24 list=YouTube +add address=8.34.208.0/20 list=YouTube +add address=8.35.192.0/20 list=YouTube +add address=23.236.48.0/20 list=YouTube +add address=23.251.128.0/19 list=YouTube +add address=34.0.0.0/15 list=YouTube +add address=34.2.0.0/16 list=YouTube +add address=34.3.0.0/23 list=YouTube +add address=34.3.3.0/24 list=YouTube +add address=34.3.4.0/24 list=YouTube +add address=34.3.8.0/21 list=YouTube +add address=34.3.16.0/20 list=YouTube +add address=34.3.32.0/19 list=YouTube +add address=34.3.64.0/18 list=YouTube +add address=34.4.0.0/14 list=YouTube +add address=34.8.0.0/13 list=YouTube +add address=34.16.0.0/12 list=YouTube +add address=34.32.0.0/11 list=YouTube +add address=34.64.0.0/10 list=YouTube +add address=34.128.0.0/10 list=YouTube +add address=35.184.0.0/13 list=YouTube +add address=35.192.0.0/14 list=YouTube +add address=35.196.0.0/15 list=YouTube +add address=35.198.0.0/16 list=YouTube +add address=35.199.0.0/17 list=YouTube +add address=35.199.128.0/18 list=YouTube +add address=35.200.0.0/13 list=YouTube +add address=35.208.0.0/12 list=YouTube +add address=35.224.0.0/12 list=YouTube +add address=35.240.0.0/13 list=YouTube +add address=57.140.192.0/18 list=YouTube +add address=64.15.112.0/20 list=YouTube +add address=64.233.160.0/19 list=YouTube +add address=66.22.228.0/23 list=YouTube +add address=66.102.0.0/20 list=YouTube +add address=66.249.64.0/19 list=YouTube +add address=70.32.128.0/19 list=YouTube +add address=72.14.192.0/18 list=YouTube +add address=74.125.0.0/16 list=YouTube +add address=104.154.0.0/15 list=YouTube +add address=104.196.0.0/14 list=YouTube +add address=104.237.160.0/19 list=YouTube +add address=107.167.160.0/19 list=YouTube +add address=107.178.192.0/18 list=YouTube +add address=108.59.80.0/20 list=YouTube +add address=108.170.192.0/18 list=YouTube +add address=108.177.0.0/17 list=YouTube +add address=130.211.0.0/16 list=YouTube +add address=136.22.160.0/20 list=YouTube +add address=136.22.176.0/21 list=YouTube +add address=136.22.184.0/23 list=YouTube +add address=136.22.186.0/24 list=YouTube +add address=142.250.0.0/15 list=YouTube +add address=146.148.0.0/17 list=YouTube +add address=152.65.208.0/22 list=YouTube +add address=152.65.214.0/23 list=YouTube +add address=152.65.218.0/23 list=YouTube +add address=152.65.222.0/23 list=YouTube +add address=152.65.224.0/19 list=YouTube +add address=162.120.128.0/17 list=YouTube +add address=162.216.148.0/22 list=YouTube +add address=162.222.176.0/21 list=YouTube +add address=172.110.32.0/21 list=YouTube +add address=172.217.0.0/16 list=YouTube +add address=172.253.0.0/16 list=YouTube +add address=173.194.0.0/16 list=YouTube +add address=173.255.112.0/20 list=YouTube +add address=192.158.28.0/22 list=YouTube +add address=192.178.0.0/15 list=YouTube +add address=193.186.4.0/24 list=YouTube +add address=199.36.154.0/23 list=YouTube +add address=199.36.156.0/24 list=YouTube +add address=199.192.112.0/22 list=YouTube +add address=199.223.232.0/21 list=YouTube +add address=207.223.160.0/20 list=YouTube +add address=208.65.152.0/22 list=YouTube +add address=208.68.108.0/22 list=YouTube +add address=208.81.188.0/22 list=YouTube +add address=208.117.224.0/19 list=YouTube +add address=209.85.128.0/17 list=YouTube +add address=216.58.192.0/19 list=YouTube +add address=216.73.80.0/20 list=YouTube +add address=216.239.32.0/19 list=YouTube +add address=2ip.ru list=Test-IP +add address=wg.vados.ru comment=VDSIna list=TRUSTED +add address=10.8.0.0/24 comment="WG VDSIna" disabled=yes list=TRUSTED +add address=ikus.pesc.ru comment=ikus.pesc.ru list=NoVdsina +add address=love.ru comment=love.ru list=NoVdsina +add address=gosuslugi.ru comment=gosuslugi.ru list=NoVdsina +add address=pochta.ru comment=pochta.ru list=NoVdsina +add address=vdsina.com list=NoVdsina +add address=vdsina.ru list=NoVdsina +add address=youla.ru list=NoVdsina +add address=kupiprodai.ru list=NoVdsina +add address=myip.ru list=Test-IP +add address=geoip.co list=NoVdsina +add address=comp-lenta.ru list=NoVdsina +add address=128.85.102.70 comment=update.microsoft.com list=\ + windowsupdateslist +add address=wu-b-net.trafficmanager.net. comment=\ + ctldl.windowsupdate.com.delivery.microsoft.com list=windowsupdateslist +add address=ctldl.windowsupdate.com.delivery.microsoft.com. comment=\ + ctldl.windowsupdate.com list=windowsupdateslist +add address=redir.update.msft.com.trafficmanager.net. comment=\ + update.microsoft.com list=windowsupdateslist +add address=geo.prod.do.dsp.trafficmanager.net. comment=\ + geo.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=13.74.139.29 comment=array605.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=kv801.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + kv801.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=e12437.d.akamaiedge.net. comment=\ + kv801.prod.do.dsp.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=displaycatalog-rp.md.mp.microsoft.com.akadns.net. comment=\ + displaycatalog.mp.microsoft.com list=windowsupdateslist +add address=rp-consumer-prod-displaycatalog-geomap.trafficmanager.net. \ + comment=displaycatalog-rp.md.mp.microsoft.com.akadns.net list=\ + windowsupdateslist +add address=sls.update.microsoft.com. comment=slscr.update.microsoft.com \ + list=windowsupdateslist +add address=glb.sls.prod.dcat.dsp.trafficmanager.net. comment=\ + sls.update.microsoft.com list=windowsupdateslist +add address=fe3.delivery.mp.microsoft.com. comment=\ + fe3cr.delivery.mp.microsoft.com list=windowsupdateslist +add address=glb.cws.prod.dcat.dsp.trafficmanager.net. comment=\ + fe3.delivery.mp.microsoft.com list=windowsupdateslist +add address=fe2cr.update.msft.com.trafficmanager.net. comment=\ + fe2cr.update.microsoft.com list=windowsupdateslist +add address=download.windowsupdate.com.delivery.microsoft.com. comment=\ + download.windowsupdate.com list=windowsupdateslist +add address=wu-f-net.trafficmanager.net. comment=\ + download.windowsupdate.com.delivery.microsoft.com list=windowsupdateslist +add address=tsfe.trafficmanager.net. comment=\ + tsfe.trafficshaping.dsp.mp.microsoft.com list=windowsupdateslist +add address=edge.ds-c7110-microsoft.global.dns.qwilted-cds.cqloud.com. \ + comment=9.au.download.windowsupdate.com list=windowsupdateslist +add address=52.158.227.125 comment=array812.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=au.download.windowsupdate.com.delivery.microsoft.com. comment=\ + au.download.windowsupdate.com list=windowsupdateslist +add address=a767.dspw65.akamai.net. comment=\ + download.windowsupdate.com.edgesuite.net list=windowsupdateslist +add address=52.151.58.88 comment=array810.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=consumer-licensing-aks2aks.md.mp.microsoft.com.akadns.net. \ + comment=licensing.mp.microsoft.com list=windowsupdateslist +add address=licensing-prod-frontdoor-geomap.trafficmanager.net. comment=\ + consumer-licensing-aks2aks.md.mp.microsoft.com.akadns.net list=\ + windowsupdateslist +add address=star.f.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.f.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=cdp-f-nlu-net.trafficmanager.net. comment=\ + star.f.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=star.b.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.b.tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=cdp-f-tlu-net.trafficmanager.net. comment=\ + star.b.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=storeedgefd.xbetservices.akadns.net. comment=\ + storeedgefd.dsx.mp.microsoft.com list=windowsupdateslist +add address=\ + storeedgefd.dsx.mp.microsoft.com.edgekey.net.globalredir.akadns.net. \ + comment=storeedgefd.dsx.mp.microsoft.com.edgekey.net list=\ + windowsupdateslist +add address=e16646.dscg.akamaiedge.net. comment=\ + storeedgefd.dsx.mp.microsoft.com.edgekey.net.globalredir.akadns.net list=\ + windowsupdateslist +add address=40.69.76.189 comment=array621.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=bg.microsoft.map.fastly.net. comment=\ + 3.au.download.windowsupdate.com list=windowsupdateslist +add list=windowsupdateslist +add address=40.69.75.33 comment=array613.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=star.sf.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedgeextensions.sf.tlu.dl.delivery.mp.microsoft.com list=\ + windowsupdateslist +add address=cdp-f-ssl-tlu-net.trafficmanager.net. comment=\ + star.sf.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=a2033.dscd.akamai.net. comment=\ + star.sb.tlu.dl.delivery.mp.microsoft.com.edgesuite.net list=\ + windowsupdateslist +add address=13.74.138.242 comment=array602.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=dl.delivery.mp.microsoft.com.delivery.microsoft.com. comment=\ + dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=dcat-f-nlu-net.trafficmanager.net. comment=\ + dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=1d.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=1D.tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=dcat-b-tlu-net.trafficmanager.net. comment=\ + 1d.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=a122.dscg3.akamai.net. comment=\ + tlu.dl.delivery.mp.microsoft.com.edgesuite.net list=windowsupdateslist +add address=geover.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + geover.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=e10370.d.akamaiedge.net. comment=\ + geover.prod.do.dsp.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=52.232.182.21 comment=array511.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.70.77.95 comment=array519.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=a1683.dscd.akamai.net. comment=\ + dl.delivery.mp.microsoft.com-c.edgesuite.net list=windowsupdateslist +add address=consumer-collections-aks2eap.md.mp.microsoft.com.akadns.net. \ + comment=collections.mp.microsoft.com list=windowsupdateslist +add address=a1847.dscd.akamai.net. comment=\ + wildcard.f.tlu.dl.delivery.mp.microsoft.com.edgesuite.net list=\ + windowsupdateslist +add address=13.74.142.6 comment=array607.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=au.download.windowsupdate.com.edgesuite.net. comment=\ + 2.au.download.windowsupdate.com list=windowsupdateslist +add address=a767.dscg3.akamai.net. comment=\ + au.download.windowsupdate.com.edgesuite.net list=windowsupdateslist +add address=52.156.123.84 comment=array818.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.78.254 comment=array610.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=4.au.download.windowsupdate.com.delivery.microsoft.com. comment=\ + 4.au.download.windowsupdate.com list=windowsupdateslist +add address=cl-glcb907925.gcdn.co. comment=\ + 4.au.download.windowsupdate.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=52.167.167.111 comment=array510.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=40.69.70.43 comment=array623.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=tsfe.trafficshaping.dsp.mp.microsoft.com.delivery.microsoft.com. \ + comment=tsfe.trafficshaping.dsp.mp.microsoft.com list=windowsupdateslist +add address=ts-frontend.trafficmanager.net. comment=\ + tsfe.trafficshaping.dsp.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=40.69.75.248 comment=array616.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.74.84 comment=array615.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=sds.trafficmanager.net. comment=software-download.microsoft.com \ + list=windowsupdateslist +add address=40.69.74.80 comment=array611.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.175.242.182 comment=array804.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=dlc-shim.trafficmanager.net. comment=download.microsoft.com list=\ + windowsupdateslist +add address=e12671.dscd.akamaiedge.net. comment=\ + download.microsoft.com.edgekey.net list=windowsupdateslist +add address=20.187.55.34 comment=array808.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.167.231 comment=array513.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=52.167.222.13 comment=array518.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=kv601.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + kv601.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=e12358.d.akamaiedge.net. comment=\ + kv601.prod.do.dsp.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. comment=\ + tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=dcat-f-tlu-net.trafficmanager.net. comment=\ + tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=a1856.dspw65.akamai.net. comment=\ + tlu.dl.delivery.mp.microsoft.com-c.edgesuite.net list=windowsupdateslist +add address=52.167.164.252 comment=array506.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=13.74.137.54 comment=array601.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=kv501.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + kv501.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=collections-prod-frontdoor-geomap.trafficmanager.net. comment=\ + consumer-collections-aks2eap.md.mp.microsoft.com.akadns.net list=\ + windowsupdateslist +add address=52.167.160.173 comment=array509.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=40.70.77.148 comment=array516.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.200.126 comment=array507.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=52.167.167.59 comment=array508.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.76.172 comment=array620.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=smtp.mail.me.com comment=smtp.mail.me.com list=NoVdsina +add address=40.70.76.5 comment=array514.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.186.119 comment=array618.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.177.176.186 comment=array520.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=40.70.76.27 comment=array515.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.160.43 comment=array503.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.110 comment=array817.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.142.8 comment=array614.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.138.254 comment=array603.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.167.96 comment=array501.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.76.215 comment=array622.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.143.200 comment=array606.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.111 comment=array818.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.112 comment=array819.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.137.53 comment=array608.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.98 comment=array805.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.96 comment=array803.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.104 comment=array811.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.101 comment=array808.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.99 comment=array806.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.17 comment=array802.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.97 comment=array804.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.103 comment=array810.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.16 comment=array801.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.70.77.177 comment=array517.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.164.84 comment=array512.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.105 comment=array812.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.107 comment=array814.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.232.161.82 comment=array502.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.108 comment=array815.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.161.91 comment=array505.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.102 comment=array809.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.109 comment=array816.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.106 comment=array813.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.117 comment=array622.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.107 comment=array612.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.108 comment=array613.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.105 comment=array610.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.113 comment=array618.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.116 comment=array621.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.98 comment=array603.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.114 comment=array619.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.111 comment=array616.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.97 comment=array602.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.106 comment=array611.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.110 comment=array615.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.119 comment=array624.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.135 comment=array514.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.97 comment=array503.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.99 comment=array604.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.61 comment=array504.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.131 comment=array510.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.132 comment=array511.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.96 comment=array601.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.104 comment=array609.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.138 comment=array517.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.60 comment=array501.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.139 comment=array518.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.118 comment=array623.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.113 comment=array820.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.103 comment=array608.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.100 comment=array807.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.129 comment=array508.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.134 comment=array513.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.62 comment=array505.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.137 comment=array516.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.101 comment=array606.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.96 comment=array502.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.130 comment=array509.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.112 comment=array617.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.100 comment=array605.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.115 comment=array620.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.136 comment=array515.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=purchase.md.mp.microsoft.com.akadns.net. comment=\ + purchase.mp.microsoft.com list=windowsupdateslist +add address=e110219.a.akamaiedge.net. comment=\ + purchase.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=72.153.5.140 comment=array519.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.141 list=windowsupdateslist +add address=72.153.5.63 comment=array506.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.128 comment=array507.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.102 comment=array607.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=storesdk.xbetservices.akadns.net. comment=\ + storesdk.dsx.mp.microsoft.com list=windowsupdateslist +add address=e16646.g.akamaiedge.net. comment=\ + storesdk.dsx.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=software-download.microsoft.com.delivery.microsoft.com. comment=\ + software-download.microsoft.com list=windowsupdateslist +add address=download.prss.microsoft.com.akamaized.net. comment=\ + software-download.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.109 comment=array614.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.133 comment=array512.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=213.180.192.0/19 comment=YandexDisk list=NoVdsina +add address=87.250.250.50 comment=YandexDisk list=NoVdsina +add address=consumer-prod-bcfd-v7-afd.trafficmanager.net. comment=\ + displaycatalog.mp.microsoft.com list=windowsupdateslist +add address=purchase-europeeap.md.mp.microsoft.com.akadns.net. comment=\ + purchase.md.mp.microsoft.com.akadns.net list=windowsupdateslist +add address=ne-az-conspurchase-mp.trafficmanager.net. comment=\ + purchase-europeeap.md.mp.microsoft.com.akadns.net list=windowsupdateslist +add address=storeedgefd.dsx.mp.microsoft.com.edgekey.net. comment=\ + cdn.storeedgefd.dsx.mp.microsoft.com list=windowsupdateslist +add address=star.f.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.f.tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=a1996.dscd.akamai.net. comment=\ + wildcard.f.dl.delivery.mp.microsoft.com.edgesuite.net list=\ + windowsupdateslist +add address=cp801.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + cp801.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=glb.tas02.sls-prod.dcat.dsp.mp.microsoft.com. comment=\ + tas02.sls.update.microsoft.com list=windowsupdateslist +add address=cp601.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + cp601.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=star.sb.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedgeextensions.sb.tlu.dl.delivery.mp.microsoft.com list=\ + windowsupdateslist +add address=star.sf.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.sf.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=cdp-f-ssl-nlu-net.trafficmanager.net. comment=\ + star.sf.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=tm-pifd-prod-gbl-weight.trafficmanager.net. comment=\ + paymentinstruments.mp.microsoft.com list=windowsupdateslist +add address=10.30.30.33 comment=amster list=NoWG +add address=10.30.30.10 comment=MikroNUC disabled=yes list=NoWG +add address=89.110.101.17 comment=!!!VDSINA!!!! disabled=yes list=NoWG +add address=wg.vados.ru comment=VDSina-vpn list=VDSina +add address=80.85.244.9 list=TRUSTED +add address=disc801.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + disc801.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=185.71.70.78 disabled=yes list=TRUSTED +add address=176.53.203.171 disabled=yes list=TRUSTED +add address=176.53.203.170 disabled=yes list=TRUSTED +add address=176.53.202.194 disabled=yes list=TRUSTED +add address=89.110.101.17 comment="!!!VDSina Server!!!" list=NoVdsina +add address=dns.quad9.net comment=Quad9-dns disabled=yes list=Trusted-DNS +add address=dns.google comment=Google-dns list=Trusted-DNS +add address=security.cloudflare-dns.com comment=CloudFlare-dns list=\ + Trusted-DNS +add address=time.google.com comment=Google-time list=Trusted-NTP +add address=0.ru.pool.ntp.org comment=ru.pool.ntp.org disabled=yes list=\ + Trusted-NTP +add address=1.ru.pool.ntp.org disabled=yes list=Trusted-NTP +add address=2.ru.pool.ntp.org comment=2.ru.pool.ntp.org disabled=yes list=\ + Trusted-NTP +add address=3.ru.pool.ntp.org comment=3.ru.pool.ntp.org disabled=yes list=\ + Trusted-NTP +add address=cp.vdsina.com comment=VDSina-vpn list=VDSina +add address=vdsina.ru comment=VDSina-vpn list=VDSina +add address=ns.vados.ru comment=VDSina-vpn list=VDSina +add address=vdsina.com comment=VDSina-vpn list=VDSina +add address=cp.vdsina.ru comment=VDSina-vpn list=VDSina +add address=ns1.vdsina.ru comment=VDSina-vpn list=VDSina +add address=10.30.30.0/24 comment="Local Network" list=Local-Net +add address=10.8.0.0/24 comment="WireGuard Network" list=Local-Net +add address=146.120.64.208/29 comment="External Net" list=Unet +add address=esia.gosuslugi.ru comment=esia.gosuslugi.ru list=NoVdsina +add address=lk.gosuslugi.ru comment=lk.gosuslugi.ru list=NoVdsina +add address=trudvsem.ru comment=trudvsem.ru list=NoVdsina +add address=10.30.30.30 comment=miller disabled=yes list=NoWG +add address=10.30.30.33 comment="Direct connet to DNS servers" list=\ + Direct-DNS +add address=109.107.177.79 list=VPN-Server +add address=10.0.0.0/8 list=RFC1918 +add address=172.16.0.0/12 list=RFC1918 +add address=192.168.0.0/16 list=RFC1918 +add address=8.8.4.4 disabled=yes list=to_vpn +add address=myip.ru disabled=yes list=to_vpn +add address=10.30.30.98 disabled=yes list=to_vpn +/ip firewall filter +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=12h chain=input comment="block honeypot DNS" \ + connection-state=new dst-port=53 in-interface=Unet-ether1 protocol=udp \ + src-address-list=!Trusted-DNS +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input connection-state=new dst-port=53 \ + in-interface=Unet-ether1 protocol=tcp src-address-list=!Trusted-DNS +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input comment=\ + "block honeypot ssh rdp winbox" connection-state=new dst-port=22,8291 \ + in-interface=Unet-ether1 protocol=tcp src-address-list=!TRUSTED +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input comment="block honeypot asterisk" \ + connection-state=new dst-port=5060 in-interface=Unet-ether1 protocol=udp +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input comment="block honeypot NTP" \ + connection-state=new dst-port=123 in-interface=Unet-ether1 protocol=udp +add action=fasttrack-connection chain=forward comment=\ + "FastTrack for non-VPN connections" connection-mark=!VPN_No_FastTrack \ + connection-state=established,related,new disabled=yes +add action=accept chain=input dst-port=29185 in-interface=Unet-ether1 \ + protocol=tcp +add action=accept chain=input dst-port=29185 in-interface=Unet-ether1 \ + protocol=udp +add action=accept chain=input comment="tcp Accept input DNS queries from LAN" \ + dst-port=53 protocol=tcp src-address=10.30.30.0/24 +add action=accept chain=input comment="udp Accept input DNS queries" \ + dst-port=53 protocol=udp src-address=10.30.30.0/24 +add action=accept chain=output comment="accept WireGuard" disabled=yes \ + dst-address-list=VPN-Server out-interface=Unet-ether1 protocol=udp \ + src-port=13231 +add action=accept chain=output disabled=yes dst-address-list=VPN-Server \ + dst-port=29185 out-interface=Unet-ether1 protocol=udp +add action=fasttrack-connection chain=forward comment="defconf: fasttrack" \ + connection-state=established,related,new disabled=yes routing-mark=\ + !To-VPN +add action=accept chain=output disabled=yes protocol=tcp src-port=443 +add action=accept chain=output disabled=yes src-address=127.0.0.1 +add action=accept chain=forward comment="defconf: accept in ipsec policy" \ + disabled=yes ipsec-policy=in,ipsec +add action=accept chain=forward comment="defconf: accept out ipsec policy" \ + disabled=yes ipsec-policy=out,ipsec +add action=accept chain=forward comment=\ + "defconf: accept established,related, untracked" connection-state=\ + established,related,untracked disabled=yes +add action=drop chain=forward comment="defconf: drop invalid" \ + connection-state=invalid in-interface=Unet-ether1 src-address=\ + !10.30.30.0/24 +add action=drop chain=forward comment=\ + "defconf: drop all from WAN not DSTNATed" connection-nat-state=!dstnat \ + connection-state=new in-interface=Unet-ether1 src-address-list=!VDSina +add action=accept chain=input in-interface=Unet-ether1 protocol=icmp \ + src-address-list=VDSina +add action=accept chain=input connection-state=established +add action=accept chain=input connection-state=related +/ip firewall mangle +add action=accept chain=prerouting comment=RFC1918 dst-address-list=RFC1918 \ + in-interface-list=!WAN +add action=mark-routing chain=prerouting comment="Mark Routing main for NoWG" \ + new-routing-mark=main passthrough=no src-address-list=NoWG +add action=mark-routing chain=prerouting comment=\ + "Mark Routing main for NoVdsina" disabled=yes dst-address-list=NoVdsina \ + new-routing-mark=main passthrough=no src-address=10.30.30.0/24 +add action=mark-connection chain=prerouting comment=\ + "Transit for address-list \"to_vpn\"" connection-mark=no-mark disabled=\ + yes dst-address-list=to_vpn in-interface-list=!WAN new-connection-mark=\ + to-vpn-conn routing-mark=!main +add action=mark-routing chain=prerouting comment=\ + "Find routing over table r_to_vpn" connection-mark=to-vpn-conn disabled=\ + yes in-interface-list=!WAN new-routing-mark=r_to_vpn +add action=mark-routing chain=prerouting comment=\ + "Mark Routing main for VDSina" dst-address-list=VPN-Server \ + new-routing-mark=main passthrough=no src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=\ + "Mark Routing main for MikroNuc" disabled=yes dst-address-list=AWG-Proxy \ + new-routing-mark=main src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=\ + "!!!ERROR Mark Routing main for Unet!!!" disabled=yes dst-address-list=\ + Unet new-routing-mark=main passthrough=no +add action=mark-routing chain=prerouting comment="Test-IP sites" disabled=yes \ + dst-address=!10.30.30.0/24 dst-address-list=Test-IP new-routing-mark=\ + To-VPN src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=FuckRKN disabled=yes \ + dst-address=!10.30.30.0/24 dst-address-list=RKN-dnsLookup \ + new-routing-mark=To-VPN passthrough=no src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment="WG for all" \ + dst-address-list=!NoVdsina new-routing-mark=To-VPN src-address=\ + 10.30.30.0/24 +add action=change-mss chain=forward comment="MSS on WG" new-mss=1380 \ + out-interface=WG protocol=tcp routing-mark=To-VPN tcp-flags=syn tcp-mss=\ + 1381-65535 +add action=mark-connection chain=prerouting comment=\ + "Mark VPN connections to exclude from FastTrack" disabled=yes \ + new-connection-mark=VPN_No_FastTrack routing-mark=To-VPN +add action=mark-routing chain=prerouting comment="VPN Network" disabled=yes \ + dst-address=10.8.0.0/24 new-routing-mark=To-VPN src-address=10.30.30.0/24 +add action=mark-connection chain=output comment="For this router" \ + connection-mark=no-mark disabled=yes dst-address-list=to_vpn \ + new-connection-mark=to-vpn-conn-local +add action=mark-routing chain=output comment="For this router" \ + connection-mark=to-vpn-conn-local disabled=yes new-routing-mark=r_to_vpn +/ip firewall nat +add action=redirect chain=dstnat comment="udp Redirect DNS in NAT" disabled=\ + yes dst-port=53 protocol=udp src-address=10.30.30.0/24 src-address-list=\ + !NoWG to-addresses=10.30.30.33 to-ports=53 +add action=redirect chain=dstnat comment="tcp Redirect DNS in NAT" disabled=\ + yes dst-port=53 protocol=tcp src-address=10.30.30.0/24 src-address-list=\ + !NoWG to-addresses=10.30.30.33 to-ports=53 +add action=masquerade chain=srcnat comment=\ + "Nat Addresses for routing mark r_to_vpn" disabled=yes routing-mark=\ + r_to_vpn +add action=dst-nat chain=dstnat comment=\ + "53/tcp dst-nat to amster.loc. From VDSina Server" dst-port=53 \ + in-interface=Unet-ether1 protocol=tcp src-address-list=VPN-Server \ + to-addresses=10.30.30.33 to-ports=53 +add action=dst-nat chain=dstnat comment=\ + "53/udp dst-nat to amster.loc. From VDSina Server" dst-port=53 protocol=\ + udp src-address-list=VPN-Server to-addresses=10.30.30.33 to-ports=53 +add action=dst-nat chain=dstnat disabled=yes dst-port=44333 in-interface=\ + Unet-ether1 protocol=udp src-address=!10.30.30.0/24 to-addresses=\ + 10.30.30.33 to-ports=44333 +add action=masquerade chain=srcnat comment=WG dst-address-list=!NoVdsina \ + routing-mark=To-VPN +add action=masquerade chain=srcnat comment="Default NAT" out-interface=\ + Unet-ether1 routing-mark=!To-VPN src-address=10.30.30.0/24 +add action=dst-nat chain=dstnat dst-port=44044 in-interface=Unet-ether1 \ + protocol=udp src-address=109.107.177.79 to-addresses=10.30.30.33 \ + to-ports=44044 +add action=dst-nat chain=dstnat comment=amster.vados.ruhttps dst-port=443 \ + fragment=no in-interface=Unet-ether1 protocol=tcp routing-mark=main \ + to-addresses=10.30.30.33 to-ports=443 +add action=dst-nat chain=dstnat comment="amster.vados.ru For Certbot HTTP" \ + dst-port=80 in-interface=Unet-ether1 protocol=tcp to-addresses=\ + 10.30.30.33 to-ports=80 +add action=dst-nat chain=dstnat comment="amster.vados.ru 3x-ui" disabled=yes \ + dst-port=2904 in-interface=Unet-ether1 protocol=tcp to-addresses=\ + 10.30.30.33 to-ports=2904 +add action=dst-nat chain=dstnat comment=reg.vados.ru disabled=yes dst-port=\ + 5000 in-interface=Unet-ether1 protocol=tcp to-addresses=10.30.30.33 \ + to-ports=5000 +add action=dst-nat chain=dstnat comment="Docker swarm" disabled=yes dst-port=\ + 2377 in-interface=Unet-ether1 protocol=tcp to-addresses=10.30.30.33 \ + to-ports=2377 +add action=dst-nat chain=dstnat comment=npm dst-port=81 in-interface=\ + Unet-ether1 protocol=tcp routing-mark=To-VPN to-addresses=10.30.30.33 \ + to-ports=81 +add action=dst-nat chain=dstnat comment=SSH dst-port=22 in-interface=\ + Unet-ether1 protocol=tcp src-address-list=TRUSTED to-addresses=\ + 10.30.30.33 to-ports=22 +add action=dst-nat chain=dstnat comment="portainer agent" disabled=yes \ + dst-port=2377 in-interface=Unet-ether1 protocol=tcp to-addresses=\ + 10.30.30.33 to-ports=2377 +add action=dst-nat chain=dstnat disabled=yes dst-port=22 in-interface=\ + Unet-ether1 protocol=tcp routing-mark=!To-VPN to-addresses=10.30.30.33 \ + to-ports=22 +add action=dst-nat chain=dstnat comment="Yandex.DNS Family" disabled=yes \ + dst-port=53 in-interface=Bridge-LAN protocol=udp src-address-list=KIDS \ + time=6h-15m,sun,mon,tue,wed,thu,fri,sat to-addresses=77.88.8.3 +add action=dst-nat chain=dstnat disabled=yes dst-port=53 in-interface=\ + Bridge-LAN protocol=tcp src-address-list=KIDS time=\ + 6h-15m,sun,mon,tue,wed,thu,fri,sat to-addresses=77.88.8.3 +/ip firewall raw +add action=drop chain=prerouting comment="Drop Honeypot Hackers" \ + in-interface=Unet-ether1 src-address-list="Honeypot Hacker" +add action=accept chain=prerouting comment="Trusted DNS" disabled=yes \ + in-interface-list=WAN src-address-list=Trusted-DNS +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + extras.getpagespeed.com content=extras.getpagespeed.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=linux.duke.edu \ + content=linux.duke.edu disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=youtube.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + googleapis.com disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=ggpht.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=youtu.be \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=ytimg.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + googlevideo.com disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=gstatic.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + googleusercontent.com disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + nhacmp3youtube.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=rutube.ru \ + content=rutube.ru disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment="me.com raw" \ + content=me.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "icloud.com raw" content=icloud.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "smtp.mail.me.com raw" content=smtp.mail.me.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "smtp.mail.me.com.akadns.net raw" content=smtp.mail.me.com.akadns.net \ + disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "api.db-ip.com raw" content=api.db-ip.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + esia.gosuslugi.ru content=esia.gosuslugi.ru disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=gosuslugi.ru \ + content=gosuslugi.ru disabled=yes +/ip firewall service-port +set ftp disabled=yes +set tftp disabled=yes +set irc disabled=no +set sip disabled=yes +set pptp disabled=yes +set rtsp disabled=no +/ip ipsec settings +set accounting=no +/ip ipsec policy +set 0 disabled=yes dst-address=0.0.0.0/0 src-address=0.0.0.0/0 +/ip ipsec profile +set [ find default=yes ] dpd-interval=2m dpd-maximum-failures=5 +/ip proxy +set cache-administrator="" max-cache-size=none port=80,443 src-address=\ + 0.0.0.0 +/ip route +add check-gateway=none comment=WG-Default disabled=no distance=1 dst-address=\ + 0.0.0.0/0 gateway=WG routing-table=To-VPN scope=30 target-scope=10 +/ip service +set ftp disabled=yes +set telnet disabled=yes +set reverse-proxy disabled=yes +set ssh address=10.30.30.0/24 port=2222 +set winbox address=10.30.30.0/24 +set api address=10.30.30.0/24 +set api-ssl address=10.30.30.0/24 disabled=yes +set www-ssl certificate=Root-YE port=18443 +set www address=10.30.30.0/24 disabled=yes port=18880 +/ip service webserver +set acme-plain=no crl-plain=no graphs-plain=no graphs-secure=no index-plain=\ + no index-secure=no rest-secure=no scep-plain=no webfig-plain=no \ + webfig-secure=no +/ip ssh +set forwarding-enabled=both publickey-authentication-options=verify-required +/ip upnp +set enabled=yes +/ip upnp interfaces +add interface=Bridge-LAN type=internal +add forced-ip=146.120.64.211 interface=Unet-ether1 type=external +add forced-ip=109.107.177.79 interface=*E type=external +add interface=lo type=internal +add interface=wlan2 type=internal +add interface=wlan1 type=internal +/ipv6 dhcp-client +add add-default-route=yes disabled=yes interface=*F rapid-commit=no request=\ + info use-interface-duid=yes validate-server-duid=no +/ipv6 dhcp-server option sets +add name=domain-search-set options=domain-search-list +add name="=\"Client ID\"" options=HostName +/ipv6 firewall address-list +add address=::/128 comment="defconf: unspecified address" list=bad_ipv6 +add address=::1/128 comment="defconf: lo" list=bad_ipv6 +add address=fec0::/10 comment="defconf: site-local" list=bad_ipv6 +add address=::ffff:0.0.0.0/96 comment="defconf: ipv4-mapped" list=bad_ipv6 +add address=::/96 comment="defconf: ipv4 compat" list=bad_ipv6 +add address=100::/64 comment="defconf: discard only " list=bad_ipv6 +add address=2001:db8::/32 comment="defconf: documentation" list=bad_ipv6 +add address=2001:10::/28 comment="defconf: ORCHID" list=bad_ipv6 +add address=3ffe::/16 comment="defconf: 6bone" list=bad_ipv6 +/ipv6 firewall filter +add action=accept chain=input comment=\ + "defconf: accept established,related,untracked" connection-state=\ + established,related,untracked +add action=drop chain=input comment="defconf: drop invalid" connection-state=\ + invalid +add action=accept chain=input comment="defconf: accept ICMPv6" protocol=\ + icmpv6 +add action=accept chain=input comment="defconf: accept UDP traceroute" \ + dst-port=33434-33534 protocol=udp +add action=accept chain=input comment=\ + "defconf: accept DHCPv6-Client prefix delegation." dst-port=546 protocol=\ + udp src-address=fe80::/10 +add action=accept chain=input comment="defconf: accept IKE" dst-port=500,4500 \ + protocol=udp +add action=accept chain=input comment="defconf: accept ipsec AH" protocol=\ + ipsec-ah +add action=accept chain=input comment="defconf: accept ipsec ESP" protocol=\ + ipsec-esp +add action=accept chain=input comment=\ + "defconf: accept all that matches ipsec policy" ipsec-policy=in,ipsec +add action=drop chain=input comment=\ + "defconf: drop everything else not coming from LAN" in-interface-list=\ + !LAN +add action=accept chain=forward comment=\ + "defconf: accept established,related,untracked" connection-state=\ + established,related,untracked +add action=drop chain=forward comment="defconf: drop invalid" \ + connection-state=invalid +add action=drop chain=forward comment=\ + "defconf: drop packets with bad src ipv6" src-address-list=bad_ipv6 +add action=drop chain=forward comment=\ + "defconf: drop packets with bad dst ipv6" dst-address-list=bad_ipv6 +add action=drop chain=forward comment="defconf: rfc4890 drop hop-limit=1" \ + hop-limit=equal:1 protocol=icmpv6 +add action=accept chain=forward comment="defconf: accept ICMPv6" protocol=\ + icmpv6 +add action=accept chain=forward comment="defconf: accept HIP" protocol=139 +add action=accept chain=forward comment="defconf: accept IKE" dst-port=\ + 500,4500 protocol=udp +add action=accept chain=forward comment="defconf: accept ipsec AH" protocol=\ + ipsec-ah +add action=accept chain=forward comment="defconf: accept ipsec ESP" protocol=\ + ipsec-esp +add action=accept chain=forward comment=\ + "defconf: accept all that matches ipsec policy" ipsec-policy=in,ipsec +add action=drop chain=forward comment=\ + "defconf: drop everything else not coming from LAN" in-interface-list=\ + !LAN +/ipv6 nd +set [ find default=yes ] advertise-dns=yes disabled=yes interface=Bridge-LAN \ + managed-address-configuration=yes other-configuration=yes pref64=::/64 +/ipv6 nd prefix +add autonomous=no disabled=yes interface=Bridge-LAN on-link=no prefix=none +/ipv6 nd prefix default +set dhcp6-pd-preferred=yes +/ipv6 nd proxy +add disabled=yes interface=lo +/ipv6 nd settings +set router-advertisement-route-distance=100 +/routing settings +set single-process=no +/system clock +set time-zone-name=Europe/Moscow +/system clock manual +set time-zone=+03:00 +/system identity +set name=MikroMaster +/system logging +add action=email disabled=yes regex="* added|changed|removed *" topics=\ + system,info +/system ntp client +set enabled=yes +/system ntp server +set enabled=yes manycast=yes multicast=yes use-local-clock=yes +/system ntp client servers +add address=0.ru.pool.ntp.org +add address=1.ru.pool.ntp.org +add address=2.ru.pool.ntp.org +add address=3.ru.pool.ntp.org +add address=time.google.com disabled=yes +/system package update +set check-certificate=yes-without-crl ip-version=ipv4 +/system scheduler +add comment="Every Day Set Global Environment Variables" name=AM-SetGlobalEnv \ + on-event="/system/script { run AM-GlobalConfig; run AM-GlobalFunc; }" \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Every Day Check For Update Scripts" interval=1d name=\ + AM-ScriptInstallUpdate on-event=\ + ":global ScriptInstallUpdate; \$ScriptInstallUpdate;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Update RKN DNS Looup" disabled=yes interval=20m name=\ + AM-RKN-dnsLookup on-event="/system/script { run AM-RKN-dnsLookup; }" \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +/system watchdog +set watchdog-timer=no +/tool e-mail +set certificate-verification=no from=MikroVados port=587 \ + server=smtp.mail.me.com tls=starttls user=vadossio@icloud.com +/tool mac-server +set allowed-interface-list=LAN +/tool mac-server mac-winbox +set allowed-interface-list=LAN +/tool netwatch +add certificate=DigiCert-Global-Root-CA check-certificate=no comment="doh, doh\ + -url=https://security.cloudflare-dns.com/dns-query, notify, name=CloudFlar\ + e-doh IPv4 1" disabled=yes down-script="" host=1.1.1.1 http-codes="" \ + interval=10s name="CloudFlare-doh IPv4 1" port=443 test-script="" type=\ + https-get up-script="" +add check-certificate=no comment="doh-fallback, doh-url=https://security.cloud\ + flare-dns.com/dns-query, doh-cert=DigiCert Global Root CA, notify, name=Cl\ + oudFlare-doh IPv4" disabled=yes down-script="" host=1.0.0.1 http-codes="" \ + name="CloudFlare-doh IPv4" port=443 test-script="" type=https-get \ + up-script="" +add comment="dns, notify, name=Google-DNS IPv4" disabled=yes dns-server=\ + 8.8.8.8 down-script="" host=dns.google http-codes="" name=\ + "Google-DNS IPv4" test-script="" type=dns up-script="" +add certificate=GTS-Root-R4 check-certificate=yes comment="doh-fallback, doh-u\ + rl=https://dns.google/dns-query, doh-cert=GTS Root R1, notify, name=Google\ + -DOH IPv4" disabled=yes dns-server=8.8.4.4 down-script="" host=dns.google \ + http-codes="" name="Google-DOH IPv4" port=443 record-type=NS src-address=\ + "" test-script="" type=dns up-script="" +add comment="dns-fallback, notify, name=Cuad9-DOH IPv4" disabled=yes \ + dns-server=9.9.9.9 down-script="" host=dns9.quad9.net http-codes="" \ + interval=10s name="Cuad9-DOH IPv4" src-address=146.120.64.211 \ + test-script="" timeout=800ms type=dns up-script="" +add certificate=DigiCert-Assured-ID-Root-CA comment="doh-fallback, doh-url=htt\ + ps://dns.quad9.net/dns-query, name=Cuad9-DOH IPv4" disabled=yes \ + down-script="" host=149.112.112.112 http-codes="" interval=10s name=\ + "Cuad9-DOH IPv4" port=443 test-script="" type=https-get up-script="" +add comment="dns-fallback, notify, name=ns1.unet.ws" disabled=yes \ + down-script="" host=91.192.189.2 http-codes="" interval=10s name=\ + ns1.unet.ws test-script="" type=simple up-script="" +add check-certificate=yes comment=\ + "doh, doh-url=https://vds.vados.ru/vds-query, name=vds.vados.ru" \ + disabled=yes down-script="" host=109.107.177.79 http-codes="" name=\ + vds.vados.ru port=443 test-script="" type=https-get up-script="" +add comment="WireGuard NAT " disabled=yes down-script="/log info \"[WG-Monitor\ + ] WireGuard is NOT running - reverting rules\"\ + \n/ip firewall nat remove [find comment=\"Redirect to Router\"]\ + \n/ip dns set servers=8.8.8.8\ + \n/ip dns cache flush\ + \n/ip route remove [find comment=\"wg-auto-route\"]\ + \n" host=10.8.0.1 http-codes="" interval=15s name=WG test-script="" \ + timeout=5s type=simple up-script="/log info \"[WG-Monitor] WireGuard is RU\ + NNING - applying rules\";\ + \n:local routeFile \"ips.txt\";\ + \n:local routeDefAddr \"0.0.0.0/0\";\ + \n:local routeGateway \"10.8.0.1\";\ + \n:local routeIF \"WG\";\ + \n:local routeTable \"To-VPN\";\ + \n:local routeComment \"wg-auto-route\";\ + \n/ip dns cache flush;\ + \n:if ([:len [/ip firewall nat find comment=\"Redirect to Router\"]] > 0) \ + do={\ + \n /ip firewall nat remove [find comment=\"Redirect to Router\"];\ + \n}\ + \n/ip firewall nat add action=redirect chain=dstnat src-address-list=\"Red\ + irectDNS\" dst-port=53 protocol=udp comment=\"Redirect to Router\";\ + \n/ip dns set servers=\$routeGateway;\ + \n:local content [/file get \$routeFile contents];\ + \n:if ([:len \$content] > 0) do={\ + \n :local line \"\";\ + \n :for i from=0 to=([:len \$content] - 1) do={\ + \n :local char [:pick \$content \$i];\ + \n :if (\$char != \"\\r\" && \$char != \"\\n\") do={\ + \n :set line (\$line . \$char);\ + \n } else={\ + \n :if ([:len \$line] > 5) do={\ + \n :do { /ip route add disabled=no dst-address=\$routeDefAd\ + dr gateway=\$routeIF distance=1 scope=30 target-scope=10 routing-table=\$r\ + outeTable comment=\$routeComment check-gateway=none } on-error={}\ + \n }\ + \n :set line \"\";\ + \n }\ + \n }\ + \n :if ([:len \$line] > 5) do={\ + \n# :do { /ip route add dst-address=\$line gateway=\$routeGateway \ + distance=1 comment=\$routeComment } on-error={}\ + \n :do { /ip route add disabled=no dst-address=\$routeDefAddr ga\ + teway=\$routeIF distance=1 scope=30 target-scope=10 routing-table=\$routeT\ + able comment=\$routeComment check-gateway=none } on-error={}\ + \n }\ + \n}\ + \n:local dotPos [:find \$routeGateway \".\"];\ + \n:if (\$dotPos != \"\") do={\ + \n :local firstOctet [:pick \$routeGateway 0 \$dotPos];\ + \n :local derivedDstAddress (\$firstOctet . \".30.0.0/15\");\ + \n :do { /ip route add dst-address=\$derivedDstAddress gateway=\$routeG\ + ateway distance=1 comment=\$routeComment } on-error={}\ + \n}\ + \n\ + \n" +add comment=WG-Default disabled=yes down-script=\ + "/ip route disable [find comment=\"WG-Default\"];" host=10.8.0.1 \ + interval=15s name=WG packet-count=20 test-script="" thr-loss-count=5 \ + type=icmp up-script="/ip route enable [find comment=\"WG-Default\"];" +/user aaa +set accounting=no diff --git a/MikroMaster/20260711/20260711204002-Config.rsc b/MikroMaster/20260711/20260711204002-Config.rsc new file mode 100644 index 0000000..0fd2112 --- /dev/null +++ b/MikroMaster/20260711/20260711204002-Config.rsc @@ -0,0 +1,3841 @@ +# 2026-07-11 20:40:06 by RouterOS 7.23.1 +# software id = A8L5-07YF +# +# model = RB952Ui-5ac2nD +# serial number = D3D50CEDCD38 +/interface bridge +add admin-mac=48:8F:5A:8E:1A:BF auto-mac=no dhcpv6-agent-circuit-id=\ + "\$(INTERFACE):\$(VID)" dhcpv6-agent-remote-id="\$(BRIDGEMAC)" name=\ + Bridge-LAN port-cost-mode=short +/interface ethernet +set [ find default-name=ether1 ] comment=Unet-cable loop-protect=off name=\ + Unet-ether1 rx-flow-control=auto tx-flow-control=auto +set [ find default-name=ether2 ] comment=LAN-2 +set [ find default-name=ether3 ] advertise="10M-baseT-half,10M-baseT-full,100M\ + -baseT-half,100M-baseT-full,1G-baseT-full,1G-baseX" comment=LAN-3 +set [ find default-name=ether4 ] comment=LAN-4 +set [ find default-name=ether5 ] advertise="10M-baseT-half,10M-baseT-full,100M\ + -baseT-half,100M-baseT-full,1G-baseT-full,1G-baseX" comment=LAN-5 \ + loop-protect=on poe-out=off rx-flow-control=auto tx-flow-control=auto +/interface wireless +# managed by CAPsMAN +# channel: 2422/20-Ce/gn(18dBm), SSID: vados, local forwarding +set [ find default-name=wlan1 ] country=belarus disabled=no mode=ap-bridge \ + ssid=vados wireless-protocol=802.11 +# managed by CAPsMAN +# channel: 5200/20-eCee/ac/P(21dBm), SSID: vados5g, local forwarding +set [ find default-name=wlan2 ] country=belarus disabled=no mode=ap-bridge \ + ssid=vados5g wireless-protocol=802.11 +/interface wireguard +add comment=WG listen-port=13231 mtu=1320 name=WG +add comment="WG interface for Amnezia route" disabled=yes listen-port=33033 \ + mtu=1360 name=wg-nuc +/interface list +add include=static name=LAN +add name=Internet +add include=Internet name=WAN +/interface wireless security-profiles +set [ find default=yes ] authentication-types=wpa-psk,wpa2-psk mode=\ + dynamic-keys supplicant-identity=MikroTik +/iot wiliot servers +set *1 address=mqtt.us-east-2.prod.wiliot.cloud name="Wiliot US East" +/ip dhcp-server option +add code=12 name=HostName +add code=119 force=yes name=domain-search-list value="0x03'WORKGROUP'0x00" +/ip dhcp-server option sets +add name=HP1505n options=HostName +add name=StartLife1 options=HostName +add name=amsterDev options=HostName +add name=MiTV options=HostName +add name=miller options=HostName +add name=amster options=HostName +add name=garage options=HostName +add name=domain-search-set options=domain-search-list +add name=VADOS-LEN options=HostName +add name=VADOS-NUC options=HostName +add name=MikroNUC options=HostName +add name=master options=HostName +add name=MikroYellow options=HostName +add name="=\"Client ID\"" options=HostName +add name=iVados options=HostName +/ip dns forwarders +add disabled=yes dns-servers=1.0.0.1,1.1.1.1 name=blsp.at verify-doh-cert=no +add comment=Kra disabled=yes dns-servers=1.0.0.1,1.1.1.1 name=slon2.to \ + verify-doh-cert=no +add disabled=yes dns-servers=1.1.1.1,1.0.0.1 name=slon3.at verify-doh-cert=no +add disabled=yes dns-servers=1.0.0.1,1.1.1.1 name=slon3.cc verify-doh-cert=no +add disabled=yes dns-servers=1.1.1.1,1.0.0.1 name=bs2web.at verify-doh-cert=\ + no +add disabled=yes dns-servers=1.1.1.1,1.0.0.1 name=bs2best.at verify-doh-cert=\ + no +add disabled=yes dns-servers=1.0.0.1,1.1.1.1 name=psrms.is verify-doh-cert=no +/ip ipsec proposal +set [ find default=yes ] disabled=yes +/ip pool +add name=dhcp ranges=10.30.30.83-10.30.30.254 +/ip dhcp-server +add add-arp=yes address-pool=dhcp always-broadcast=yes bootp-support=none \ + dhcp-option-set=domain-search-set interface=Bridge-LAN lease-time=10m \ + name=masterDHCP server-address=10.30.30.81 +/ip smb users +set [ find default=yes ] disabled=yes +add disabled=yes name=vados +/ipv6 dhcp-client option +add code=119 name=domain-search-list value="0x03'WORKGROUP'0x00" +add code=12 name=HostName value="\$(HOSTNAME)" +add code=61 name=DUID value=0x00030001488f5a8e1abc +add code=61 name=clientid +/ipv6 dhcp-server option +add code=119 name=domain-search-list value="0x03'WORKGROUP'0x00" +add code=12 name=HostName value="\$(HOSTNAME)" +add code=61 name=CLIENT_DUID +add code=61 name=CLIENT_MAC +/ppp profile +set *FFFFFFFE use-upnp=no +/routing bgp template +set default disabled=no output.network=bgp-networks +/routing table +add disabled=no fib name=To-VPN +add disabled=no fib name=r_to_vpn +/system logging action +add email-start-tls=yes email-to=vados@vados.ru name=email target=email +/system script +add dont-require-permissions=no name=fetchCustom owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n\r\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.86.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.86.pub\";\ + \n\r\ + \n#:local Script AM-setup\ + \n;\r\ + \n#/system/script/add name=\$Script owner=\$Script source=([ /tool/fetch c\ + heck-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".r\ + sc\") output=user as-value ]->\"data\");\r\ + \n\ + \n#/usb1/mikro_bak_rsa.pub\ + \n\ + \n\r\ + \n#\ + \n\r\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.10.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.10.pub\";\ + \n\r\ + \n#\r\ + \n\ + \n#root.mnuc.backuper\ + \n\ + \n\r\ + \n/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@10\ + .30.30.81.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.81.pub\";\r\ + \n\ + \n#:local Script AM-GlobalConfig;\ + \n\r\ + \n#/system/script/add name=\$Script owner=\$Script source=([ /tool/fetch c\ + heck-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".r\ + sc\") output=user as-value ]->\"data\");\r\ + \n\ + \n#/usb1/mikro_bak_rsa.pub\ + \n#:local FetchFile AM-DOHCert.rsc\ + \n\ + \n\r\ + \n\r\ + \n#\ + \n:global MkDir;\ + \n\r\ + \n\ + \n#:local FetchFile Binary.backup\ + \n:local localDir \"\";\ + \n\ + \n\r\ + \n#\$MkDir \$localDir;\r\ + \n\ + \n#\ + \n/tool/fetch mode=https url=(\$GlobalScriptsUrl . \$FetchFile) dst-path=(\ + \$localDir . \$FetchFile);\r\ + \n" +add comment="Global Config Script" dont-require-permissions=no name=\ + AM-GlobalConfig owner=AM-GlobalConfig policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GlobalConfig\r\ + \n# Script comment: Global Config Script\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# Warning: Dont touch this line!\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global GlobalConfReady false;\r\ + \n#\r\ + \n#\r\ + \n# Global Url For Amster scripts to fetch\r\ + \n# most be defined in AM-Init-Script or AM-setup scripts\r\ + \n:global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n:global ScriptsUrlSuffix (a);\r\ + \n:global CheckSumsVerify true;\r\ + \n\r\ + \n:global ScriptUpdatesCRLF true;\r\ + \n:global CommentsInScripts true;\r\ + \n\r\ + \n# Debug output:\r\ + \n:global PrintDebug true; #false;\r\ + \n\r\ + \n# Debug output for specific script:\r\ + \n#:set (\$PrintDebugOverride->\"AM-GlobalFunc\") true;\r\ + \n\r\ + \n# Debug logs\r\ + \n# Other actions (disk, email, remote or support) can be used as well.\r\ + \n# I do not recommend using echo - use debug output instead (https://rsc.\ + eworm.de/main/DEBUG.html#debug-output).\r\ + \n#/system/logging/add topics=script,debug action=memory;\r\ + \n\r\ + \n:global MikrotikUpgradeUrl \"https://upgrade.mikrotik.com/routeros\";\r\ + \n:global Domain \"WORKGROUP\";\r\ + \n\r\ + \n:global EmailGeneralTo \"vados@vados.ru\";\r\ + \n#:global EmailGeneralCc \"\";\r\ + \n\r\ + \n# \$ScriptInstallUpdate mod/notification-telegram\r\ + \n:global TelegramTokenId \"8390868324:AAG3_d1hfVR2M6pGAgDK6ZvhOe4J97nyopw\ + \";\r\ + \n:global TelegramChatId \"-1003631309063\"; #\"454403445\";\r\ + \n# Use this to send notifications to a specific topic in group.\r\ + \n#:global TelegramThreadId \"30\";\r\ + \n# Using telegram-chat you have to define trusted chat ids (not group ids\ + !)\r\ + \n# or user names. Groups allow to chat with devices simultaneously.\r\ + \n:global TelegramChatIdsTrusted {\r\ + \n \"454403445\"\r\ + \n \"-1003631309063\"\r\ + \n \"-5124482807\"\r\ + \n \"1087968824\";\r\ + \n};\r\ + \n# \"-5294757340\";\r\ + \n\r\ + \n:global TelegramChatGroups \"(all)\";\r\ + \n#:global TelegramChatGroups \"(all|home|office)\";\r\ + \n\r\ + \n:global NotificationFunctions {\r\ + \n \"email\"\r\ + \n \"telegram\";\r\ + \n};\r\ + \n# Toggle this to disable symbols in notifications.\r\ + \n:global NotificationsWithSymbols true;\r\ + \n:global TerminalColorOutput true;\r\ + \n\r\ + \n:global DetectIpAddrDefault \"https://ipv4.mikrotik.ovh/\";\r\ + \n:global DetectIpAddrFallback \"https://api.ipify.org/\";\r\ + \n\r\ + \n## Update channel. Possible values: stable, long-term, testing, developm\ + ent\r\ + \n:global updateChannel \"stable\";\r\ + \n\r\ + \n:global CertRenewUrl (\$GlobalScriptsUrl . \"certs/\" . \$Identity . \"/\ + \");\r\ + \n:global CertRenewTime 2w;\r\ + \n:global CertRenewPass (a); #{\r\ + \n# \"\";\r\ + \n# \"v3ry-s3cr3t\";\r\ + \n# \"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n:global CertWarnTime 1w;\r\ + \n:global CertIssuedExportPass (a); # {\r\ + \n# \"\";\r\ + \n# \"cert1-cn\"=\"v3ry-s3cr3t\";\r\ + \n# \"cert2-cn\"=\"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n\r\ + \n# Script BackupAndUpdate mode, possible values: backup,\r\ + \n# updateOnce - Update once as \"osnotify\" and back \"osnotify\" mode (i\ + n terminal run :set OSUpdateMode updateOnce; \$OSUpdateMode;). \r\ + \n# osupdate - Update if available and creates backups before/after update\ + \_(ignores `forceBackup`)\r\ + \n# Set `forceBackup` to true to always create backups, even\ + \_without updates\r\ + \n# osnotify - Set `forceBackup` to always create backups on every run\r\ + \n:global DevInfoIncludeIP true;\r\ + \n:global BackupAndUpdateMode \"osnotify\"\r\ + \n\r\ + \n:global forceBackup true;\r\ + \n:global BackupPassword \"Peskar55\";\r\ + \n\r\ + \n# SFTP Backup Variables\r\ + \n:global BackupRandomDelay 0;\r\ + \n:global BackupUploadUrl \"sftp://amster.vados.ru/upload/\";\r\ + \n:global BackupUploadUser \$Identity;\r\ + \n:global BackupUploadPass \"root.mnuc.backuper\";\r\ + \n\r\ + \n:global BackupSendBinary true;\r\ + \n:global BackupSendExport true;\r\ + \n:global BackupSendGlobalConfig true;\r\ + \n:global BackupSendScripts true;\r\ + \n\r\ + \n# Remove local file after uploading if no errors\r\ + \n:global BackupRmLocal true;\r\ + \n\r\ + \n# Local Backups Root Directory \r\ + \n:global BackupLocalRoot \"tmpfs\";\r\ + \n\r\ + \n# Encrypt Backup\r\ + \n:global BackupEncrypt true;\r\ + \n\r\ + \n# Sensitive information in Backups\r\ + \n:global BackupSens true;\r\ + \n\r\ + \n# User Export\r\ + \n:global BackupUser true;\r\ + \n\r\ + \n# License Export (not for CHR, will silently skip)\r\ + \n:global BackupLicense false;\r\ + \n\r\ + \n# SSH Keys\r\ + \n:global BackupSshKeys true;\r\ + \n\r\ + \n# Certificate Export\r\ + \n:global BackupCerts true;\r\ + \n\r\ + \n# Certificate Password\r\ + \n:global BackupCertsPasswd \"\";\r\ + \n\r\ + \n# User-Manager Export\r\ + \n:global BackupUserMan false;\r\ + \n\r\ + \n# The Dude Export\r\ + \n:global BackupDude false;\r\ + \n\r\ + \n# User Files to export, comma-separated string or array of strings\r\ + \n# User Files are not removed on backup\r\ + \n# Any directory paths will be removed (/ -> _) on remote file\r\ + \n# Nonpresent files are silently skipped\r\ + \n:global BackupUserList \"autosupout.rif,autosupout.old.rif\";\r\ + \n\r\ + \n# load functions and any custom scripts\r\ + \n# [ /system/script/find where name=\"global-config-ov\ + erlay\" ], \\\r\ + \n# [ /system/script/find where name~\"^global-config-o\ + verlay\\\\.d/.\" ]\r\ + \n#\r\ + \n#:foreach Script in=([ /system/script/find where name=\"Amster-TGglobalV\ + ars\" ], [ /system/script/find where name=\"Amster-TGglobalFunc\" ]) do={\ + \r\ + \n# :onerror Err {\r\ + \n# /system/script/run \$Script;\r\ + \n# } do={\r\ + \n# :log error (\"Loading configuration from configs and functions\"\ + \_. [ /system/script/get \$Script name ] . \" failed: \" . \$Err);\r\ + \n# }\r\ + \n#}\r\ + \n#:log info \"\\ndevMode = \$devMode\";\r\ + \n#:log info \"\\ndevRunning = \$devRunning\";\r\ + \n:foreach Script in=[ /system/script/find where source~\"^#!rsc by Vados \ + GlobalRUN\\r\?\\n\" ] do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n :log error (\"Run script \" . [ /system/script/get \$Script name \ + ] . \" width marker \\\" GlobalRUN\\\" failed: \" . \$Err);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# signal we are ready\r\ + \n:set GlobalConfReady true;\r\ + \n:while (\$GlobalConfReady != true) do={:delay 500ms};\r\ + \n" +add comment="Global functions" dont-require-permissions=no name=AM-GlobalFunc \ + owner=AM-GlobalFunc policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GglobalFunc\r\ + \n# Script comment: Global functions\r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch, scheduler\r\ + \n:local ExitOK false;\r\ + \n:local ScriptName [ :jobname ];\r\ + \n#:global checkRunJob \$SriptName;\r\ + \n#:global fixCRLF;\r\ + \n#:if (\$fixCRLF != false) do={\r\ + \n#:foreach fixScript in=([ /system/script/find where name~\"*\" ]) do={/s\ + ystem/script/set source=[ :tocrlf [ get \$fixScript source ] ] \$fixScript\ + }\r\ + \n#:if (\$fixCRLF != \"LF\") do={ /system/script/set source=[ :tocrlf [ g\ + et \$fixScript source ] ] \$fixScript};};\r\ + \n#}\r\ + \n:global GlobalFuncReady false;\r\ + \n\r\ + \n# Git commit id & info, expected configuration version\r\ + \n:global CommitId \"main/729/f42d43ae\";\r\ + \n:global CommitTAG \"unknown\";\r\ + \n:global CommitInfo \"Autocommit in repo: ros.vados.ru, generated by owne\ + r: root, in 00:05:03 01.07.2026.\";\r\ + \n\r\ + \n# The variables are received right here.\r\ + \n:global GlobalConfVersion 140;\r\ + \n\r\ + \n:global SumsDirAndFileName \"tmp/checksums.json\";\r\ + \n\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global IdentityShort [:pick \$Identity 0 18];\r\ + \n\r\ + \n:global devBoardName [/system/resource/get board-name];\r\ + \n:global devMode [/system/device-mode/get mode];\r\ + \n:global devCurArch [/system/resource/get architecture-name];\r\ + \n:global devCurVersion [/system/resource/get version];\r\ + \n\r\ + \n# Variables defined in AM-GlobalConfig.rsc\r\ + \n:global TerminalColorOutput;\r\ + \n:global CommentsInScripts;\r\ + \n:global DetectIpAddrDefault;\r\ + \n:global DetectIpAddrFallback;\r\ + \n:global updateChannel;\r\ + \n:global Domain;\r\ + \n:global EmailGeneralTo;\r\ + \n:global BackupAndUpdateMode;\r\ + \n\r\ + \n# Single functions, without nested functions.\r\ + \n:global MiniDateTimeStamp;\r\ + \n:global MicroDateTimeStamp;\r\ + \n\r\ + \n# Include globals \$PrintDebug and \$PrintDebugOverride\r\ + \n:global LogPrint;\r\ + \n\r\ + \n# DeviceInfo and Updates\r\ + \n:global IsCheckUpdates;\r\ + \n:global WaitCheckUpdates;\r\ + \n:global GetPubIp;\r\ + \n\r\ + \n:global GetArrDeviceInfo;\r\ + \n:global WaitArrDeviceInfo;\r\ + \n\r\ + \n:global devModel;\r\ + \n:global devRunningMode;\r\ + \n\r\ + \n:global devRbRevision;\r\ + \n:global devRbSerialNumber;\r\ + \n:global devRbVerAvail;\r\ + \n\r\ + \n:global runningChannelVersion;\r\ + \n:global runningChannel;\r\ + \n:global runningVersion;\r\ + \n\r\ + \n:global devBuildTime;\r\ + \n:global ROsVerAvail;\r\ + \n:global PkgUpdStatus;\r\ + \n:global currentUpdateChannel;\r\ + \n\r\ + \n:global ScriptInstallUpdate;\r\ + \n\r\ + \n:global DeviceInfo;\r\ + \n\r\ + \n:global OldMode;\r\ + \n\r\ + \n:global ParseKeyValueStore;\r\ + \n:global RandomDelay;\r\ + \n:global GetRandomNumber;\r\ + \n:global EscapeForRegEx;\r\ + \n:global CharacterMultiply;\r\ + \n:global CharacterReplace;\r\ + \n:global CleanFilePath;\r\ + \n:global CleanName;\r\ + \n\r\ + \n:global CommitBrief;\r\ + \n\r\ + \n:global EitherOr;\r\ + \n:global FormatLine;\r\ + \n:global GetRandom20CharAlNum;\r\ + \n:global GetRandom20CharHex;\r\ + \n:global GetRandomNumber;\r\ + \n:global Grep;\r\ + \n:global IfThenElse;\r\ + \n:global MAX;\r\ + \n:global MIN;\r\ + \n:global HumanReadableNum;\r\ + \n\r\ + \n:global IsFullyConnected;\r\ + \n:global IsDNSResolving;\r\ + \n:global IsTimeSync;\r\ + \n:global IsTimeSyncCached;\r\ + \n:global IsTimeSyncResetNtp;\r\ + \n\r\ + \n:global MkDir;\r\ + \n:global RmDir;\r\ + \n:global RmFile;\r\ + \n:global FileGet;\r\ + \n:global Unix2Dos;\r\ + \n:global UrlEncode;\r\ + \n:global WaitForFile;\r\ + \n:global ScriptLock;\r\ + \n:global SendTelegram;\r\ + \n:global NotificationFunctions;\r\ + \n:global SendNotification;\r\ + \n:global SendNotification2;\r\ + \n:global SymbolForNotification;\r\ + \n:global SymbolByUnicodeName;\r\ + \n:global ValidateSyntax;\r\ + \n:global CheckSumsVerify;\r\ + \n:global GlobalConfChanges;\r\ + \n:global GlobalConfMigration;\r\ + \n:global VersionToNum;\r\ + \n:global RequiredRouterOS;\r\ + \n:global ExitError;\r\ + \n:global ExitOnError;\r\ + \n\r\ + \n:global FetchHugeFiles;\r\ + \n:global FetchUserAgentStr;\r\ + \n:global FileExists;\r\ + \n:global CertificateAvailable;\r\ + \n:global CertificateDownload;\r\ + \n:global CertificateNameByCN;\r\ + \n:global CharacterMultiply;\r\ + \n\r\ + \n:global InspectVar;\r\ + \n:global InspectVarRet;\r\ + \n:global ParseCustom;\r\ + \n\r\ + \n:set IsCheckUpdates do={\r\ + \n :local isInfo [:tobool \$1];\r\ + \n :global updateChannel;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :log info (\$0 . \" \\\$isInfo = \" . \$isInfo); \r\ + \n :local CurrentChannel [ /system/package/update/get channel ];\r\ + \n :if ([ :len \$updateChannel ] > 0 && \$CurrentChannel != \$updateChann\ + el) do={\r\ + \n :retry command={/system/package/update set channel=\$updateChannel} \ + \_delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" Set updates channel to: \$updateChannel\");\r\ + \n }\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local Channel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set currentUpdateChannel \$Channel;\r\ + \n :log info (\$0 . \" Update Channel: \" . \$Channel . \".\");\r\ + \n :local StatusPkg \"undefined\";\r\ + \n :log info (\$0 . \" Checking for updates...\");\r\ + \n :retry command={:set \$StatusPkg ([/system/package/update/check-for-up\ + dates without-paging as-value]->\"status\")} \\\r\ + \n delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" \\\$StatusPkg = \" . \$StatusPkg);\r\ + \n :if (\$StatusPkg = \"New version is available\") do={ :set PkgUpdStatu\ + s true; } else={ :set PkgUpdStatus false; }\r\ + \n :set ROsVerAvail [ :tostr [ /system/package/update/get latest-version \ + ] ];\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if (\$isInfo != false || \$PkgUpdStatus = true) do={\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :retry command={:set (\$arrDeviceInfo->\"devUpdates\") [ /system/pac\ + kage/update/get ]} delay=500ms max=5 on-error={:log error (\"Error\")};\r\ + \n :return true;\r\ + \n } else={\r\ + \n :return \$PkgUpdStatus;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set WaitCheckUpdates do={\r\ + \n :global IsCheckUpdates;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :local Status \"\";\r\ + \n :local Update;\r\ + \n :local WaitCount 0;\r\ + \n :if ([ \$IsCheckUpdates true ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n :while ((\$Status = \"\" || \$Status = \"finding out latest version...\ + \") && \$WaitCount < 60) do={\r\ + \n :delay 1s;\r\ + \n :set WaitCount (\$WaitCount + 1);\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n }\r\ + \n :set Update [ /system/package/update/get ];\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n :if ((\$Update->\"status\") = \"New version is available\") do={\r\ + \n :set PkgUpdStatus true;\r\ + \n } else={\r\ + \n :set PkgUpdStatus false;\r\ + \n }\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :set (\$arrDeviceInfo->\"devUpdates\") \$Update;\r\ + \n :if (\$Status = \"\" || \$Status = \"finding out latest version...\") \ + do={\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set WaitArrDeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :while ([ \$GetArrDeviceInfo ] = false) do={\r\ + \n :delay 1s;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set GetArrDeviceInfo do={\r\ + \n :global LogPrint;\r\ + \n :global arrDeviceInfo;\r\ + \n :global devBoardName;\r\ + \n :global devModel;\r\ + \n :global devRunning;\r\ + \n :global devCurArch;\r\ + \n :global devRbRevision;\r\ + \n :global devRbSerialNumber;\r\ + \n :global devBuildTime;\r\ + \n :global runningChannelVersion;\r\ + \n :global runningChannel;\r\ + \n :global runningVersion;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n\r\ + \n :local DeviceInfo;\r\ + \n :local Resource [/system/resource/get];\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local VersionString [ :tostr (\$Resource->\"version\") ];\r\ + \n :local ChannelStart [ :find \$VersionString \" (\" ];\r\ + \n :local RouterBoard;\r\ + \n :local IsRouterBoard false;\r\ + \n :local RunningMode \"\";\r\ + \n\r\ + \n :set devCurArch [ :tostr (\$Resource->\"architecture-name\") ];\r\ + \n :set devBuildTime [ :tostr (\$Resource->\"build-time\") ];\r\ + \n :set runningChannelVersion \$VersionString;\r\ + \n :set runningVersion \$VersionString;\r\ + \n :set runningChannel \"\";\r\ + \n\r\ + \n :if ([ :typeof \$ChannelStart ] = \"num\" && \$ChannelStart >= 0) do=\ + {\r\ + \n :set runningVersion [ :pick \$VersionString 0 \$ChannelStart ];\r\ + \n :local ChannelRest [ :pick \$VersionString (\$ChannelStart + 2) [ :\ + len \$VersionString ] ];\r\ + \n :local ChannelEnd [ :find \$ChannelRest \")\" ];\r\ + \n :if ([ :typeof \$ChannelEnd ] = \"num\" && \$ChannelEnd >= 0) do={\ + \r\ + \n :set runningChannel [ :pick \$ChannelRest 0 \$ChannelEnd ];\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([:pick \$devBoardName 0 3] != \"CHR\" or [:pick \$devBoardName 0\ + \_3] != \"x86\") do={\r\ + \n :do {\r\ + \n :set RouterBoard [[ :parse \"/system/routerboard/get\" ]];\r\ + \n# :set RouterBoard [ /system/routerboard/get ];\r\ + \n :if ((\$RouterBoard->\"routerboard\") = true) do={\r\ + \n :set IsRouterBoard true;\r\ + \n }\r\ + \n } on-error={}\r\ + \n }\r\ + \n\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get mode ];\r\ + \n } on-error={\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get running ];\r\ + \n } on-error={\r\ + \n :set RunningMode \"\";\r\ + \n }\r\ + \n }\r\ + \n :set devRunning \$RunningMode;\r\ + \n\r\ + \n :if (\$IsRouterBoard = true) do={\r\ + \n :set devModel [ :tostr (\$RouterBoard->\"model\") ];\r\ + \n :set devRbRevision [ :tostr (\$RouterBoard->\"revision\") ];\r\ + \n :set devRbSerialNumber [ :tostr (\$RouterBoard->\"serial-number\") \ + ];\r\ + \n } else={\r\ + \n :set devModel \$devBoardName;\r\ + \n :set devRbRevision \"\";\r\ + \n :set devRbSerialNumber \"\";\r\ + \n }\r\ + \n\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n\r\ + \n :set (\$DeviceInfo->\"board-name\") \$devBoardName;\r\ + \n :set (\$DeviceInfo->\"architecture-name\") \$devCurArch;\r\ + \n :set (\$DeviceInfo->\"model\") \$devModel;\r\ + \n :set (\$DeviceInfo->\"revision\") \$devRbRevision;\r\ + \n :set (\$DeviceInfo->\"serial-number\") \$devRbSerialNumber;\r\ + \n :set (\$DeviceInfo->\"running-mode\") \$devRunning;\r\ + \n :set (\$DeviceInfo->\"build-time\") \$devBuildTime;\r\ + \n :set (\$DeviceInfo->\"running-channel-version\") \$runningChannelVers\ + ion;\r\ + \n :set (\$DeviceInfo->\"running-version\") \$runningVersion;\r\ + \n :set (\$DeviceInfo->\"running-channel\") \$runningChannel;\r\ + \n :set (\$DeviceInfo->\"routerboard\") \$IsRouterBoard;\r\ + \n :set (\$DeviceInfo->\"current-firmware\") [ :tostr (\$RouterBoard->\"\ + current-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"upgrade-firmware\") [ :tostr (\$RouterBoard->\"\ + upgrade-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"package-channel\") \$currentUpdateChannel;\r\ + \n :set (\$DeviceInfo->\"installed-version\") [ :tostr (\$Update->\"inst\ + alled-version\") ];\r\ + \n :set (\$DeviceInfo->\"latest-version\") \$ROsVerAvail;\r\ + \n :set (\$DeviceInfo->\"update-status\") [ :tostr (\$Update->\"status\"\ + ) ];\r\ + \n\r\ + \n :set arrDeviceInfo \$DeviceInfo;\r\ + \n :return \$DeviceInfo;\r\ + \n}\r\ + \n\r\ + \n:set DeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global IfThenElse;\r\ + \n :global FormatLine;\r\ + \n :global CommitBrief;\r\ + \n\r\ + \n :local License [/system/license/get];\r\ + \n :local Snmp [ /snmp/get ];\r\ + \n :local Info [ \$GetArrDeviceInfo ];\r\ + \n\r\ + \n :if ([ :typeof \$Info ] != \"array\") do={\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :return ( \\\r\ + \n [ \$FormatLine \"Hostname\" \$Identity ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"location\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Location\" (\$Snmp->\"location\") ] . \"\\n\") ]\ + \_. \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"contact\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Contact\" (\$Snmp->\"contact\") ] . \"\\n\") ] .\ + \_\\\r\ + \n \"Hardware:\\n\" . \\\r\ + \n [ \$FormatLine \" Board\" (\$Info->\"board-name\") ] . \"\\n\" . \ + \\\r\ + \n [ \$FormatLine \" Arch\" (\$Info->\"architecture-name\") ] . \"\\\ + n\" . \\\r\ + \n [ \$FormatLine \" Model\" (\$Info->\"model\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"revision\") ] > 0) \\\r\ + \n (\" \" . (\$Info->\"revision\")) ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"serial-number\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Serial\" (\$Info->\"serial-number\") ] . \"\ + \\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"nlevel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"nlevel\ + \")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS:\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"level\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"level\ + \")) ] . \"\\n\") ] . \\\r\ + \n [ \$FormatLine \" Channel\" (\$Info->\"package-channel\") ] . \"\ + \\n\" . \\\r\ + \n [ \$FormatLine \" Installed\" (\$Info->\"installed-version\") ] .\ + \_\"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"running-channel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Running\" ((\$Info->\"running-version\") . \ + \" (\" . (\$Info->\"running-channel\") . \")\") ] . \"\\n\") \\\r\ + \n ([ \$FormatLine \" Running\" (\$Info->\"running-version\") ] . \ + \"\\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"latest-version\") ] > 0 && \\\r\ + \n (\$Info->\"installed-version\") != (\$Info->\"latest-version\"))\ + \_\\\r\ + \n ([ \$FormatLine \" Available\" (\$Info->\"latest-version\") ] .\ + \_\"\\n\") ] . \\\r\ + \n [ \$IfThenElse ((\$Info->\"routerboard\") = true && \\\r\ + \n (\$Info->\"current-firmware\") != (\$Info->\"upgrade-firmware\")\ + ) \\\r\ + \n ([ \$FormatLine \" Firmware\" ((\$Info->\"current-firmware\") .\ + \_\" -> \" . (\$Info->\"upgrade-firmware\")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS-Scripts:\\n\" . \\\r\ + \n [ \$FormatLine \" Commit\" [ \$CommitBrief ] ] . \"\\n\" . \\\r\ + \n [ \$FormatLine \" Version\" \$GlobalConfVersion ]);\r\ + \n}\r\ + \n\r\ + \n:if ([ :typeof \$NotificationFunctions ] != \"array\") do={:set Notifica\ + tionFunctions ({})}\r\ + \n\r\ + \n:set GetRandom20CharAlNum do={\r\ + \n :global EitherOr;\r\ + \n :return [ :rndstr length=[ \$EitherOr [ :tonum \$1 ] 20 ] from=\"ABCDE\ + FGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\" ];\r\ + \n}\r\ + \n\r\ + \n:set GetRandom20CharHex do={:global EitherOr; :return [ :rndstr length=[\ + \_\$EitherOr [ :tonum \$1 ] 20 ] from=\"0123456789abcdef\" ]; }\r\ + \n:set GetRandomNumber do={:global EitherOr; :return [ :rndnum from=0 to=[\ + \_\$EitherOr [ :tonum \$1 ] 4294967295 ] ]; }\r\ + \n\r\ + \n:global checkRunJob;\r\ + \n:set checkRunJob do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLock; \r\ + \n :if ([ \$ScriptLock \$Script ] = false) do={:return false}\r\ + \n :foreach Job in=[ /system/script/job/find where script=\$Script ] do={\ + :set Job [ /system/script/job/get \$Job ];\r\ + \n :while ([ :typeof (\$Job->\"parent\") ] = \"id\") do={:set Job [ /sy\ + stem/script/job/get [ find where .id=(\$Job->\"parent\") ] ]}\r\ + \n :if ((\$Job->\"type\") = \"login\") do={:return true};}; :return fa\ + lse; \r\ + \n}\r\ + \n\r\ + \n:set InspectVar do={\r\ + \n :global InspectVarRet;\r\ + \n :put [ :tocrlf [ \$InspectVarRet \$1 ] ];\r\ + \n}\r\ + \n\r\ + \n:set CommitBrief do={\r\ + \n :global CommitId;\r\ + \n :global CommitInfo;\r\ + \n :if (\$CommitId = \"unknown\") do={\r\ + \n :return \"unknown\";\r\ + \n }\r\ + \n :return (\$CommitInfo . \"/\" . [ :pick \$CommitId 0 8 ]);\r\ + \n}\r\ + \n\r\ + \n:set InspectVarRet do={\r\ + \n :local Input \$1;\r\ + \n :local Level (0 + [ :tonum \$2 ]);\r\ + \n :global CharacterReplace;\r\ + \n :global IfThenElse;\r\ + \n :global InspectVarRet;\r\ + \n :local IndentReturn do={\r\ + \n :local Prefix [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Level [ :tonum \$3 ];\r\ + \n :global CharacterMultiply;\r\ + \n :return ([ \$CharacterMultiply \" \" (\$Level * 2) ] . \"-\" . \$Pre\ + fix . \"-> \" . \$Value);\r\ + \n }\r\ + \n :local TypeOf [ :typeof \$Input ];\r\ + \n :local Len [ :len \$Input ];\r\ + \n :local Return [ \$IndentReturn \"type\" \$TypeOf \$Level ];\r\ + \n :if (\$TypeOf = \"array\") do={\r\ + \n :foreach Key,Value in=\$Input do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"key\" \$Key (\$Level + 1) ] . \"\\n\" . \\\r\ + \n [ \$InspectVarRet \$Value (\$Level + 2) ]);\r\ + \n }\r\ + \n } else={\r\ + \n :if (\$TypeOf = \"str\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"len\" \$Len \$Level ]);\r\ + \n :if ([ :typeof [ :find \$Input (\"\\r\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\r\") \"\" ];\r\ + \n }\r\ + \n :if ([ :typeof [ :find \$Input (\"\\n\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\n\") \" \" ];\r\ + \n }\r\ + \n }\r\ + \n :if (\$TypeOf != \"nothing\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"value\" [ \$IfThenElse ([ :len \$Input ] > 80)\ + \_\\\r\ + \n ([ :pick \$Input 0 77 ] . \"...\") \$Input ] \$Level ]);\r\ + \n }\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set GetPubIp do={\r\ + \n :global DetectIpAddrDefault;\r\ + \n :global DetectIpAddrFallback;\r\ + \n\r\ + \n :local PubIpAddr false;\r\ + \n :local Url \$DetectIpAddrDefault;\r\ + \n :local JobName [ :jobname ];\r\ + \n\r\ + \n :do {\r\ + \n :log debug (\$0 . \" Trying to detect public IP using default serv\ + ice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=50ms max=5 on-error={:log error (\$JobName . \" Failed to \ + detect public IP using default service: \" . \$Url . \".\")}\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed to detect public IP using default serv\ + ice: \" . \$Url . \".\");\r\ + \n :set Url \$DetectIpAddrFallBack;\r\ + \n :log debug (\$0 . \" Trying to detect public IP using fallback ser\ + vice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=200ms max=5 on-error={:log error (\$JobName . \" Failed to\ + \_detect public IP using fallback service: \" . \$Url . \".\")}\r\ + \n };\r\ + \n :local IpLen ([ :len \$PubIpAddr ] -1);\r\ + \n :if (\$PubIpAddr = false) do={:set PubIpAddr \"not-detected\"} else=\ + {:set PubIpAddr [:pick \$PubIpAddr 0 \$IpLen]}\r\ + \n :return \$PubIpAddr;\r\ + \n}\r\ + \n\r\ + \n:set FormatLine do={\r\ + \n :local Key [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Indent [ :tonum \$3 ];\r\ + \n :local Spaces;\r\ + \n :local Return \"\";\r\ + \n :global CharacterMultiply;\r\ + \n :global EitherOr;\r\ + \n :set Indent [ \$EitherOr \$Indent 16 ];\r\ + \n :local Spaces [ \$CharacterMultiply \" \" \$Indent ];\r\ + \n :if ([ :len \$Key ] > 0) do={ :set Return (\$Key . \":\")}\r\ + \n :if ([ :len \$Key ] > (\$Indent - 2)) do={:set Return (\$Return . \"\\\ + n\" . [ :pick \$Spaces 0 \$Indent ] . \$Value);\r\ + \n } else={:set Return (\$Return . [ :pick \$Spaces 0 (\$Indent - [ :len \ + \$Return ]) ] . \$Value)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ParseCustom do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Start [:find [ \$Str ] [:tostr \$2]];\r\ + \n :local End [ :tostr \$3 ];\r\ + \n :local Num [ :tonum \$4 ];\r\ + \n :local Return;\r\ + \n :if ([:len \$Num] = 0) do={:set Num \"0\"}\r\ + \n :local rest [:pick \$Str (\$Start+\$Num) [:len \$Str]]; \r\ + \n :if ([:len \$End ] = 0) do={:set Return [:pick \$Str \$Num \$Start]} e\ + lse={\r\ + \n :set End [:find \$rest \$End];\r\ + \n :set Return [:pick \$rest 0 \$End];\r\ + \n }\r\ + \n:return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set LogPrint do={\r\ + \n :local Severity [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Message [ :tostr \$3 ];\r\ + \n :global PrintDebug;\r\ + \n :global PrintDebugOverride;\r\ + \n :global EitherOr;\r\ + \n :local Debug [ \$EitherOr (\$PrintDebugOverride->\$Name) \$PrintDebug \ + ];\r\ + \n :local PrintSeverity do={\r\ + \n :global TerminalColorOutput;\r\ + \n :if (\$TerminalColorOutput != true) do={:return \$1}\r\ + \n :local Color { debug=96; info=97; warning=93; error=91 };\r\ + \n :return (\"\\1B[\" . \$Color->\$1 . \"m\" . \$1 . \"\\1B[0m\");\r\ + \n}\r\ + \n :local Log ([ \$EitherOr \$Name \"\" ] . \": \" . \$Message);\ + \r\ + \n :if (\$Severity ~ (\"^(custom|debug|error|info)\\\$\")) do={\r\ + \n :if (\$Severity = \"debug\") do={:log debug \$Log}\r\ + \n :if (\$Severity = \"error\") do={:log error \$Log}\r\ + \n :if (\$Severity = \"info\" ) do={:log info \$Log}\r\ + \n } else={:log warning \$Log; :set Severity \"warning\"}\r\ + \n :if (\$Severity != \"debug\" || \$Debug = true) do={:put ([ \$PrintSev\ + erity \$Severity ] . \": \" . \$Message)}\r\ + \n}\r\ + \n\r\ + \n:set CertificateAvailable do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :local UseFor [ :tostr \$2 ];\r\ + \n\r\ + \n :global CertificateDownload;\r\ + \n :global ParseKeyValueStore;\r\ + \n\r\ + \n :if ([ :len \$UseFor ] = 0) do={\r\ + \n :log warning (\$0 . \"The intended use is undefined!\");\r\ + \n :set UseFor \"undefined\";\r\ + \n }\r\ + \n :if ([ /system/resource/get free-hdd-space ] < 8388608 && \\\r\ + \n [ /certificate/settings/get crl-download ] = true && \\\r\ + \n [ /certificate/settings/get crl-store ] = \"system\") do={\r\ + \n :log warning (\$0 . \"This system has low free flash space but \" . \ + \\\r\ + \n \"is configured to download certificate CRLs to system!\");\r\ + \n }\r\ + \n :if ([ :len \$CommonName ] = 0) do={\r\ + \n :log warning (\$0 . \"No CommonName given!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if (((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" || \\\ + \r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \$UseFor || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\r\ + \n [ :len [ /certificate/builtin/find where common-name=\$CommonName\ + \_] ] > 0) do={\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n :log info (\$0 . \"Certificate with CommonName '\" . \$CommonName . \ + \"' not available.\");\r\ + \n :if ([ \$CertificateDownload \$CommonName ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] > 1\ + ) do={\r\ + \n :log info (\$0 . \"There are \" . \$CertCount . \" Certificates with\ + \_CommonName '\" . \$CommonName . \"'. Should be ok.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :local CertVal [ /certificate/get [ find where common-name=\$CommonNam\ + e ] ];\r\ + \n :while ((\$CertVal->\"akid\") != \"\" && (\$CertVal->\"akid\") != (\$C\ + ertVal->\"skid\")) do={\r\ + \n :if ([ :len [ /certificate/find where skid=(\$CertVal->\"akid\") ] ]\ + \_= 0) do={\r\ + \n :local IssuerCN ([ \$ParseKeyValueStore (\$CertVal->\"issuer\") ]-\ + >\"CN\");\r\ + \n :log info (\$0 . \"Certificate chain for '\" . \$CommonName . \"' \ + is incomplete, missing '\" . \$IssuerCN . \"'.\");\r\ + \n :if ([ \$CertificateDownload \$IssuerCN ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :set CertVal [ /certificate/get [ find where skid=(\$CertVal->\"akid\ + \") ] ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateDownload do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :global ScriptUpdatesBaseUrl;\r\ + \n :global ScriptUpdatesUrlSuffix;\r\ + \n :global CertificateNameByCN;\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :log info (\$0 . \"Downloading and importing certificate with \" . \\\ + \r\ + \n \"CommonName '\" . \$CommonName . \"'.\");\r\ + \n :local FileName ([ \$CleanName \$CommonName ] . \".pem\");\r\ + \n :do {\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-header\ + -field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n (\$ScriptUpdatesBaseUrl . \"certs/\" . \$FileName . \$ScriptUpdate\ + sUrlSuffix) \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n } on-error={\r\ + \n :log warning (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \\\r\ + \n \"' from repository! Trying fallback to mkcert.org...\");\r\ + \n :do {\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if ([ :len [ /certificate/find where common-name=\"ISRG Root X1\"\ + \_] ] = 0 && \\\r\ + \n !(((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" \ + || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \"fetch\" || \ + \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\ + \r\ + \n [ :len [ /certificate/builtin/find where common-name=\"ISRG\ + \_Root X1\" ] ] > 0)) do={\r\ + \n :log error (\$0 . \" Required certificate is not available.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-head\ + er-field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n url=\"https://mkcert.org/generate/\" http-data=[ :serialize to=jso\ + n ({ \$CommonName }) ] \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n :if ([ /file/get \$FileName size ] = 0) do={\r\ + \n \$RmFile \$FileName;\r\ + \n :error false;\r\ + \n }\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \"'!\");\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n /certificate/import file-name=\$FileName passphrase=\"\" as-value;\r\ + \n :delay 1s;\r\ + \n \$RmFile \$FileName;\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n /certificate/remove [ find where name~(\"^\" . \$FileName . \"_[0-9]\ + +\\\$\") ];\r\ + \n :log warning (\$0 . \" Certificate with CommonName '\" . \$CommonNam\ + e . \"' still unavailable!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :foreach Cert in=[ /certificate/find where name~(\"^\" . \$FileName . \ + \"_[0-9]+\\\$\") ] do={\r\ + \n \$CertificateNameByCN [ /certificate/get \$Cert common-name ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateNameByCN do={\r\ + \n :local Match [ :tostr \$1 ];\r\ + \n :global CleanName;\r\ + \n :local Cert ([ /certificate/find where (common-name=\$Match or fingerp\ + rint=\$Match or name=\$Match) ]->0);\r\ + \n :if ([ :len \$Cert ] = 0) do={\r\ + \n :log warning (\$0 .\" No matching certificate found.\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CommonName [ /certificate/get \$Cert common-name ];\r\ + \n /certificate/set \$Cert name=[ \$CleanName \$CommonName ];\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FileExists do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local Type [ :tostr \$2 ];\r\ + \n :global FileGet;\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={ :return false; }\r\ + \n :if ([ :len (\$FileVal->\"size\") ] = 0) do={ :return false; }\r\ + \n :if ([ :len \$Type ] = 0 || \$FileVal->\"type\" = \$Type) do={ :return\ + \_true; }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set Grep do={\r\ + \n :local Input ([ :tostr \$1 ] . \"\\n\");\r\ + \n :local Pattern [ :tostr \$2 ];\r\ + \n :if ([ :typeof [ :find \$Input \$Pattern ] ] = \"nil\") do={:return []\ + }\r\ + \n :do {\r\ + \n :local Line [ :pick \$Input 0 [ :find \$Input \"\\n\" ] ];\r\ + \n :if ([ :typeof [ :find \$Line \$Pattern ] ] = \"num\") do={:return \ + \$Line}\r\ + \n :set Input [ :pick \$Input ([ :find \$Input \"\\n\" ] + 1) [ :len \$\ + Input ] ];\r\ + \n } while=([ :len \$Input ] > 0);\r\ + \n :return [];\r\ + \n}\r\ + \n\r\ + \n:set HumanReadableNum do={\r\ + \n :local Input [ :tonum \$1 ];\r\ + \n :local Base [ :tonum \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global IfThenElse;\r\ + \n :local Prefix \"kMGTPE\";\r\ + \n :local Pow 1;\r\ + \n :set Base [ \$EitherOr \$Base 1024 ];\r\ + \n :local Bin [ \$IfThenElse (\$Base = 1024) \"i\" \"\" ];\r\ + \n :if (\$Input < \$Base) do={:return \$Input}\r\ + \n :for I from=0 to=[ :len \$Prefix ] do={\r\ + \n :set Pow (\$Pow * \$Base);\r\ + \n :if (\$Input / \$Base < \$Pow) do={\r\ + \n :set Prefix [ :pick \$Prefix \$I ];\r\ + \n :local Tmp1 (\$Input * 100 / \$Pow);\r\ + \n :local Tmp2 (\$Tmp1 / 100);\r\ + \n :if (\$Tmp2 >= 100) do={:return (\$Tmp2 . \$Prefix . \$Bin)}\r\ + \n :return (\$Tmp2 . \".\" . \\\r\ + \n [ :pick \$Tmp1 [ :len \$Tmp2 ] ([ :len \$Tmp1 ] - [ :len \$Tmp\ + 2 ] + 1) ] . \\\r\ + \n \$Prefix . \$Bin);\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set CleanFilePath do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CharacterReplace;\r\ + \n :while (\$Path ~ \"//\") do={ :set \$Path [ \$CharacterReplace \$Path \ + \"//\" \"/\" ]; }\r\ + \n :if ([ :pick \$Path 0 ] = \"/\") do={ :set Path [ :pick \$Path 1 [ :le\ + n \$Path ] ]; }\r\ + \n :if ([ :pick \$Path ([ :len \$Path ] - 1) ] = \"/\") do={ :set Path [ \ + :pick \$Path 0 ([ :len \$Path ] - 1) ]; }\r\ + \n :return \$Path;\r\ + \n}\r\ + \n\r\ + \n:set CleanName do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Return \"\";\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :typeof [ find \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr\ + stuvwxyz0123456789\" \$Char ] ] = \"nil\") do={\r\ + \n :do {:if ([ :len \$Return ] = 0) do={:error true};\r\ + \n :if ([ :pick \$Return ([ :len \$Return ] - 1) ] = \"-\") do={:er\ + ror true}; :set Char \"-\"} on-error={:set Char \"\"}; \r\ + \n };\r\ + \n :set Return (\$Return . \$Char)};\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set RandomDelay do={\r\ + \n :local Time [ :tonum \$1 ];\r\ + \n :local Unit [ :tostr \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global GetRandomNumber;\r\ + \n :global MAX;\r\ + \n :if (\$Time = 0) do={:return false}\r\ + \n :delay ([ \$MAX 10 [ \$GetRandomNumber ([ :tonsec [ :totime (\$Time . \ + [ \$EitherOr \$Unit \"s\" ]) ] ] / 1000000) ] ] . \"ms\");\r\ + \n}\r\ + \n\r\ + \n:set EscapeForRegEx do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"^.[]\\\$()|*+\?{}\\\\\");\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :find \$Chars \$Char ]) do={:set Char (\"\\\\\" . \$Char)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ExitError do={\r\ + \n :local ExitOK [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Error [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :if (\$ExitOK = \"false\") do={\r\ + \n :log error (\$Name [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\\\$\") \ + \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with er\ + ror\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \ + \$Error) \".\" ]);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set ExitOnError do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :local Error [ :tostr \$2 ];\r\ + \n :global IfThenElse;\r\ + \n :log error (\$Name . \" \" . [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\ + \\\$\") \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with erro\ + r\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \$E\ + rror) \".\" ]);\r\ + \n}\r\ + \n\r\ + \n:set MiniDateTimeStamp do={\r\ + \n :local mikro [ :tobool \$1 ];\r\ + \n /system/clock;\r\ + \n :local vdate [get date];\r\ + \n :local vtime [get time];\r\ + \n :local vdoff [:toarray \"0,4,5,7,8,10\"];\r\ + \n :local MM [:pick \$vdate (\$vdoff->2) (\$vdoff->3)];\r\ + \n :local M [:tonum \$MM];\r\ + \n :if (\$vdate ~ \".../../....\") do={\r\ + \n :set vdoff [:toarray \"7,11,1,3,4,6\"];\r\ + \n :set M ([:find \"xxanebarprayunulugepctovecANEBARPRAYUNULUGE\ + PCTOVEC\" [:pick \$vdate (\$vdoff->2) (\$vdoff->3)] -1] / 2);\r\ + \n :if (\$M>12) do={:set M (\$M - 12)}\r\ + \n :set MM [:pick (100 + \$M) 1 3];\r\ + \n }\r\ + \n :local yyyy [:pick \$vdate (\$vdoff->0) (\$vdoff->1)];\r\ + \n :local dd [:pick \$vdate (\$vdoff->4) (\$vdoff->5)];\r\ + \n :local HH [:pick \$vtime 0 2];\r\ + \n :local mm [:pick \$vtime 3 5];\r\ + \n :local ss [:pick \$vtime 6 8];\r\ + \n :local Return;\r\ + \n :if (\$mikro = true) do={\r\ + \n :set Return ([:pick \$yyyy 2 4] . [:pick \$dd 0 1] . [:pick \$HH 0\ + \_1] . [:pick \$mm 3 4] . [:pick \$ss 6 7]);\r\ + \n } else={\r\ + \n :set Return ([\$yyyy] . [\$MM] . [\$dd] . [\$HH] . [\$mm] . [\$ss]);\ + \r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set WaitForFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local WaitTime [ :totime \$2 ];\r\ + \n :global CleanFilePath;\r\ + \n :global EitherOr;\r\ + \n :global MAX;\r\ + \n :set FileName [ \$CleanFilePath \$FileName ];\r\ + \n :local Delay ([ \$MAX [ \$EitherOr \$WaitTime 2s ] 100ms ] / 9);\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } delay=\$Delay max=10;\r\ + \n } on-error={ }\r\ + \n :while ([ :len [ /file/find where name=\$FileName ] ] > 0) do={\r\ + \n :do {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } on-error={ }\r\ + \n :delay \$Delay;\r\ + \n :set Delay (\$Delay * 3 / 2);\r\ + \n }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set FileGet do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global WaitForFile;\r\ + \n :if ([ \$WaitForFile \$FileName 0s ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :local FileVal false;\r\ + \n :do {\r\ + \n :set FileVal [ /file/get \$FileName ];\r\ + \n } on-error={ }\r\ + \n :return \$FileVal;\r\ + \n}\r\ + \n\r\ + \n:set RmDir do={\r\ + \n :local DirName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing directory: \". \$DirName);\r\ + \n :local DirVal [ \$FileGet \$DirName ];\r\ + \n :if (\$DirVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n };\r\ + \n :if (\$DirVal->\"type\" != \"directory\") do={\r\ + \n :log error (\$0 . \" Directory '\" . \$DirName . \"' is not a direct\ + ory.\");\r\ + \n :return false;\r\ + \n };\r\ + \n :onerror Err {\r\ + \n /file/remove \$DirName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing directory '\" . \$DirName . \"' failed\ + : \" . \$Err);\r\ + \n :return false;\r\ + \n };\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set RmFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing file: \". \$FileName);\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if (\$FileVal->\"type\" = \"directory\" || \$FileVal->\"type\" = \"di\ + sk\") do={\r\ + \n :log error (\$0 . \" File '\" . \$FileName . \"' is not a file.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/remove \$FileName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing file '\" . \$FileName . \"' failed: \"\ + \_. \$Err); :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MkDir do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CleanFilePath;\r\ + \n :global FileGet;\r\ + \n :global RmDir;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :local MkTmpfs do={\r\ + \n :global WaitForFile;\r\ + \n :local TmpFs [ /disk/find where slot=tmpfs type=tmpfs ];\r\ + \n :if ([ :len \$TmpFs ] = 1) do={\r\ + \n :if ([ /disk/get \$TmpFs disabled ] = true) do={\r\ + \n :log info (\$0 . \" The tmpfs is disabled, enabling.\");\r\ + \n /disk/enable \$TmpFs;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :log info (\$0 . \" Creating disk of type tmpfs.\");\r\ + \n \$RmDir \"tmpfs\";\r\ + \n :onerror Err {\r\ + \n /disk/add slot=tmpfs type=tmpfs tmpfs-max-size=([ /system/resource\ + /get total-memory ] / 3);\r\ + \n \$WaitForFile \"tmpfs\";\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Creating disk of type tmpfs failed: \" . \$\ + Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :set Path [ \$CleanFilePath \$Path ];\r\ + \n :if (\$Path = \"\") do={\r\ + \n :return true;\r\ + \n }\r\ + \n :log debug (\$0 . \" Making directory: \" . \$Path);\r\ + \n :local PathVal [ \$FileGet \$Path ];\r\ + \n :if (\$PathVal->\"type\" = \"directory\") do={\r\ + \n :log debug (\$0 . \" ... which already exists.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :pick \$Path 0 5 ] = \"tmpfs\") do={\r\ + \n :if ([ \$MkTmpfs ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/add type=\"directory\" name=\$Path;\r\ + \n \$WaitForFile \$Path;\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Making directory '\" . \$Path . \"' failed: \ + \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MAX do={:if (\$1 > \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n:set MIN do={:if (\$1 < \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set CharacterReplace do={\r\ + \n :local String [ :tostr \$1 ];\r\ + \n :local ReplaceFrom [ :tostr \$2 ];\r\ + \n :local ReplaceWith [ :tostr \$3 ];\r\ + \n :local Return \"\";\r\ + \n :if (\$ReplaceFrom = \"\") do={:return \$String}\r\ + \n :while ([ :typeof [ :find \$String \$ReplaceFrom ] ] != \"nil\") do={\ + \r\ + \n :local Pos [ :find \$String \$ReplaceFrom ];\r\ + \n :set Return (\$Return . [ :pick \$String 0 \$Pos ] . \$ReplaceWith);\ + \r\ + \n :set String [ :pick \$String (\$Pos + [ :len \$ReplaceFrom ]) [ :len\ + \_\$String ] ];\r\ + \n }\r\ + \n :return (\$Return . \$String);\r\ + \n}\r\ + \n\r\ + \n:set IfThenElse do={\r\ + \n :if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1 ] = true) do={:return \ + \$2};\r\ + \n :return \$3;\r\ + \n}\r\ + \n\r\ + \n:set EitherOr do={\r\ + \n :global IfThenElse;\r\ + \n :if ([ :typeof \$1 ] = \"num\") do={:return [ \$IfThenElse (\$1 != 0) \ + \$1 \$2 ]}\r\ + \n :if ([ :typeof \$1 ] = \"time\") do={:return [ \$IfThenElse (\$1 > 0s)\ + \_\$1 \$2 ]}\r\ + \n# this works for boolean values, literal ones with parentheses\r\ + \n :return [ \$IfThenElse ([ :len [ :tostr \$1 ] ] > 0) \$1 \$2 ];\r\ + \n}\r\ + \n\r\ + \n:set ParseKeyValueStore do={\r\ + \n :local Source \$1;\r\ + \n :if ([ :pick \$Source 0 1 ] = \"{\") do={\r\ + \n :do {\r\ + \n :return [ :deserialize from=json \$Source ];\r\ + \n } on-error={ }\r\ + \n } \r\ + \n :if ([ :typeof \$Source ] != \"array\") do={:set Source [ :tostr \$1 ]\ + }\r\ + \n :local Result ({});\r\ + \n :foreach KeyValue in=[ :toarray \$Source ] do={\r\ + \n :if ([ :find \$KeyValue \"=\" ]) do={\r\ + \n :local Key [ :pick \$KeyValue 0 [ :find \$KeyValue \"=\" ] ];\r\ + \n :local Value [ :pick \$KeyValue ([ :find \$KeyValue \"=\" ] + 1) [\ + \_:len \$KeyValue ] ];\r\ + \n :if (\$Value=\"true\") do={:set Value true}\r\ + \n :if (\$Value=\"false\") do={:set Value false}\r\ + \n :set (\$Result->\$Key) \$Value;\r\ + \n } else={:set (\$Result->\$KeyValue) true}\r\ + \n }\r\ + \n :return \$Result;\r\ + \n}\r\ + \n:set RequiredRouterOS do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :local Required [ :tostr \$2 ];\r\ + \n :local Warn [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :global VersionToNum;\r\ + \n :if (!(\$Required ~ \"^\\\\d+\\\\.\\\\d+((alpha|beta|rc|\\\\.)\\\\d+|)\ + \\\$\")) do={\r\ + \n :log error (\$0 . \" No valid RouterOS version: \" . \$Required);\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ \$VersionToNum \$Required ] > [ \$VersionToNum [ /system/packag\ + e/update/get installed-version ] ]) do={\r\ + \n :if (\$Warn = \"true\") do={\r\ + \n :log warning (\$0 . \" This \" . [ \$IfThenElse ([ :pick \$Caller \ + 0 ] = (\"\\\$\")) \"function\" \"script\" ] . \\\r\ + \n \" '\" . \$Caller . \"' (at least specific functionality) requir\ + es RouterOS \" . \$Required . \". Please update!\");\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set ValidateSyntax do={\r\ + \n :local Code [ :tostr \$1 ];\r\ + \n :global LogPrint;\r\ + \n :onerror Err {\r\ + \n [ :parse (\":local Validate do={\\n\" . \$Code . \"\\n}\") ];\r\ + \n } do={\r\ + \n \$LogPrint debug \$0 (\"Valdation failed: \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set VersionToNum do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Multi 0x1000000;\r\ + \n :local Return 0;\r\ + \n :global CharacterReplace;\r\ + \n :set Input [ \$CharacterReplace \$Input \".\" \",\" ];\r\ + \n :foreach I in={ \"zero\"; \"alpha\"; \"beta\"; \"rc\" } do={\r\ + \n :set Input [ \$CharacterReplace \$Input \$I (\",\" . \$I . \",\") ];\ + \r\ + \n }\r\ + \n :foreach Value in=([ :toarray \$Input ], 0) do={\r\ + \n :local Num [ :tonum \$Value ];\r\ + \n :if (\$Multi = 0x100) do={\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={\r\ + \n :set Return (\$Return + 0xff00);\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n } else={\r\ + \n :if (\$Value = \"zero\") do={ }\r\ + \n :if (\$Value = \"alpha\") do={ :set Return (\$Return + 0x3f00); \ + }\r\ + \n :if (\$Value = \"beta\") do={ :set Return (\$Return + 0x5f00); }\ + \r\ + \n :if (\$Value = \"rc\") do={ :set Return (\$Return + 0x7f00); }\r\ + \n }\r\ + \n }\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={ :set Return (\$Return + (\$Va\ + lue * \$Multi)); }\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ScriptLock do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local WaitMax [ :totime \$2 ];\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global ScriptLockOrder;\r\ + \n :if ([ :typeof \$ScriptLockOrder ] = \"nothing\") do={\r\ + \n :set ScriptLockOrder ({});\r\ + \n }\r\ + \n :if ([ :typeof (\$ScriptLockOrder->\$Script) ] = \"nothing\") do={\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :local JobCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :return [ :len [ /system/script/job/find where script=\$Script ] ];\ + \r\ + \n }\r\ + \n :local TicketCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :local Count 0;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:set Count (\$Count \ + + 1)}\r\ + \n }\r\ + \n :return \$Count;\r\ + \n }\r\ + \n :local IsFirstTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Check [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if (\$Ticket = \$Check) do={:return true}\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\" && \$Ticket != \$Check) d\ + o={:return false}\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :local AddTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Add [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :while (true) do={\r\ + \n :local Pos [ :len (\$ScriptLockOrder->\$Script) ];\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Pos) \$Add;\r\ + \n :delay 10ms;\r\ + \n :if ((\$ScriptLockOrder->\$Script->\$Pos) = \$Add) do={:return tru\ + e}\r\ + \n }\r\ + \n }\r\ + \n :local RemoveTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Remove [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Id,Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :while ((\$ScriptLockOrder->\$Script->\$Id) = \$Remove) do={\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Id);\r\ + \n :delay 10ms;\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n :local CleanupTickets do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:return false}\r\ + \n }\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :if ([ :typeof \$WaitMax ] = \"nil\" ) do={:set WaitMax 0s}\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\r\ + \n :log error (\$0 . \" A script named '\" . \$Script . \"' does not ex\ + ist!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$JobCount \$Script ] = 0) do={\r\ + \n \$LogPrint error \$0 (\"No script '\" . \$Script . \"' is running!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$TicketCount \$Script ] >= [ \$JobCount \$Script ]) do={\r\ + \n :log error (\$0 . \" More tickets than running scripts '\" . \$Scrip\ + t . \"', resetting!\");\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n /system/script/job/remove [ find where script=\$Script ];\r\ + \n }\r\ + \n :local MyTicket [ \$GetRandom20CharAlNum 6 ];\r\ + \n \$AddTicket \$Script \$MyTicket;\r\ + \n :local WaitInterval (\$WaitMax / 20);\r\ + \n :local WaitTime \$WaitMax;\r\ + \n :while (\$WaitTime > 0 && \\\r\ + \n ([ \$IsFirstTicket \$Script \$MyTicket ] = false || \\\r\ + \n [ \$TicketCount \$Script ] < [ \$JobCount \$Script ])) do={\r\ + \n :set WaitTime (\$WaitTime - \$WaitInterval);\r\ + \n :delay \$WaitInterval;\r\ + \n }\r\ + \n :if ([ \$IsFirstTicket \$Script \$MyTicket ] = true && \\\r\ + \n [ \$TicketCount \$Script ] = [ \$JobCount \$Script ]) do={\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$CleanupTickets \$Script;\r\ + \n :return true;\r\ + \n }\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$LogPrint debug \$0 (\"Script '\" . \$Script . \"' started more than \ + once\" . \\\r\ + \n [ \$IfThenElse (\$WaitTime < \$WaitMax) \" and timed out waiting for\ + \_lock\" \"\" ] . \"...\");\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set SendNotification do={ :onerror Err {\r\ + \n :global SendNotification2;\r\ + \n \$SendNotification2 ({ origin=\$0; subject=\$1; message=\$2; link=\$3;\ + \_silent=\$4 });\r\ + \n} do={:global ExitOnError; \$ExitOnError \$0 \$Err}\r\ + \n}\r\ + \n\r\ + \n:set SendNotification2 do={\r\ + \n :local Notification \$1;\r\ + \n :global NotificationFunctions;\r\ + \n :foreach FunctionName,Discard in=\$NotificationFunctions do={\r\ + \n (\$NotificationFunctions->\$FunctionName) (\"\\\$NotificationFunctio\ + ns->\\\"\" . \$FunctionName . \"\\\"\") \$Notification;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set SymbolByUnicodeName do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :global EitherOr;\r\ + \n :global SymbolsExtra;\r\ + \n :local Symbols ({\r\ + \n \"abacus\"=\"\\F0\\9F\\A7\\AE\";\r\ + \n \"alarm-clock\"=\"\\E2\\8F\\B0\";\r\ + \n \"arrow-down\"=\"\\E2\\AC\\87\";\r\ + \n \"arrow-up\"=\"\\E2\\AC\\86\";\r\ + \n \"calendar\"=\"\\F0\\9F\\93\\85\";\r\ + \n \"card-file-box\"=\"\\F0\\9F\\97\\83\";\r\ + \n \"chart-decreasing\"=\"\\F0\\9F\\93\\89\";\r\ + \n \"chart-increasing\"=\"\\F0\\9F\\93\\88\";\r\ + \n \"cloud\"=\"\\E2\\98\\81\";\r\ + \n \"cross-mark\"=\"\\E2\\9D\\8C\";\r\ + \n \"earth\"=\"\\F0\\9F\\8C\\8D\";\r\ + \n \"fire\"=\"\\F0\\9F\\94\\A5\";\r\ + \n \"floppy-disk\"=\"\\F0\\9F\\92\\BE\";\r\ + \n \"gear\"=\"\\E2\\9A\\99\";\r\ + \n \"heart\"=\"\\E2\\99\\A5\";\r\ + \n \"high-voltage-sign\"=\"\\E2\\9A\\A1\";\r\ + \n \"incoming-envelope\"=\"\\F0\\9F\\93\\A8\";\r\ + \n \"information\"=\"\\E2\\84\\B9\";\r\ + \n \"large-orange-circle\"=\"\\F0\\9F\\9F\\A0\";\r\ + \n \"large-red-circle\"=\"\\F0\\9F\\94\\B4\";\r\ + \n \"link\"=\"\\F0\\9F\\94\\97\";\r\ + \n \"lock-with-ink-pen\"=\"\\F0\\9F\\94\\8F\";\r\ + \n \"memo\"=\"\\F0\\9F\\93\\9D\";\r\ + \n \"mobile-phone\"=\"\\F0\\9F\\93\\B1\";\r\ + \n \"pushpin\"=\"\\F0\\9F\\93\\8C\";\r\ + \n \"scissors\"=\"\\E2\\9C\\82\";\r\ + \n \"scroll\"=\"\\F0\\9F\\93\\9C\";\r\ + \n \"smiley-partying-face\"=\"\\F0\\9F\\A5\\B3\";\r\ + \n \"smiley-smiling-face\"=\"\\E2\\98\\BA\";\r\ + \n \"smiley-winking-face-with-tongue\"=\"\\F0\\9F\\98\\9C\";\r\ + \n \"sparkles\"=\"\\E2\\9C\\A8\";\r\ + \n \"speech-balloon\"=\"\\F0\\9F\\92\\AC\";\r\ + \n \"star\"=\"\\E2\\AD\\90\";\r\ + \n \"warning-sign\"=\"\\E2\\9A\\A0\";\r\ + \n \"white-heavy-check-mark\"=\"\\E2\\9C\\85\"\r\ + \n }, \$SymbolsExtra);\r\ + \n :local Magic [ :pick [ /system/clock/get date ] 4 10 ];\r\ + \n :local Special {\r\ + \n \"information-04-01\"=\"\\F0\\9F\\9A\\BB\";\r\ + \n \"large-orange-circle-04-01\"=\"\\F0\\9F\\8D\\8A\";\r\ + \n \"large-orange-circle-10-31\"=\"\\F0\\9F\\8E\\83\";\r\ + \n \"large-red-circle-04-01\"=\"\\F0\\9F\\8D\\92\" };\r\ + \n :if ([ :len (\$Symbols->\$Name) ] = 0) do={\r\ + \n :log warning (\$0 . \"No symbol available for name '\" . \$Name . \"\ + '!\");\r\ + \n :return \"\";\r\ + \n }\r\ + \n :return ([ \$EitherOr (\$Special->(\$Name . \$Magic)) (\$Symbols->\$Na\ + me) ] . \"\\EF\\B8\\8F\");\r\ + \n}\r\ + \n\r\ + \n:set SymbolForNotification do={\r\ + \n :global NotificationsWithSymbols;\r\ + \n :global SymbolByUnicodeName;\r\ + \n :global IfThenElse;\r\ + \n :if (\$NotificationsWithSymbols != true) do={:return [ \$IfThenElse ([\ + \_:len \$2 ] > 0) ([ :tostr \$2 ] . \" \") \"\" ]}\r\ + \n :local Return \"\";\r\ + \n :foreach Symbol in=[ :toarray \$1 ] do={:set Return (\$Return . [ \$Sy\ + mbolByUnicodeName \$Symbol ])}\r\ + \n :return (\$Return . \" \");\r\ + \n}\r\ + \n\r\ + \n:set Unix2Dos do={:return [ :tocrlf [ :tostr \$1 ] ]}\r\ + \n:set UrlEncode do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"\\n\\r !\\\"#\\\$%&'()*+,:;<=>\?@[\\\\]^`{|}~\");\r\ + \n :local Subs { \"%0A\"; \"%0D\"; \"%20\"; \"%21\"; \"%22\"; \"%23\"; \"\ + %24\"; \"%25\"; \"%26\"; \"%27\";\r\ + \n \"%28\"; \"%29\"; \"%2A\"; \"%2B\"; \"%2C\"; \"%3A\"; \"%3B\"; \ + \"%3C\"; \"%3D\"; \"%3E\"; \"%3F\";\r\ + \n \"%40\"; \"%5B\"; \"%5C\"; \"%5D\"; \"%5E\"; \"%60\"; \"%7B\"; \ + \"%7C\"; \"%7D\"; \"%7E\" };\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :local Replace [ :find \$Chars \$Char ];\r\ + \n :if ([ :typeof \$Replace ] = \"num\") do={:set Char (\$Subs->\$Repla\ + ce)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set IsDNSResolving do={\r\ + \n :do {:resolve \"api.telegram.org\"} on-error={:return false}\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsFullyConnected do={\r\ + \n :global IsDNSResolving;\r\ + \n :global IsTimeSync;\r\ + \n :if ([ \$IsDNSResolving ] = false) do={:return false};\r\ + \n :if ([ \$IsTimeSync ] = false) do={:return false};\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsTimeSync do={\r\ + \n :global IsTimeSyncCached;\r\ + \n :global IsTimeSyncResetNtp;\r\ + \n :if (\$IsTimeSyncCached = true) do={:return true}\r\ + \n :if ([ /system/ntp/client/get enabled ] = true) do={\r\ + \n :if ([ /system/ntp/client/get status ] = \"synchronized\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :local Uptime [ /system/resource/get uptime ];\r\ + \n :if ([ :typeof \$IsTimeSyncResetNtp ] = \"nothing\") do={\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n }\r\ + \n :if (\$Uptime - \$IsTimeSyncResetNtp < 3m) do={:return false}\r\ + \n :log warning \"\$0 The ntp client is configured, but did not sync.\"\ + ;\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n /system/ntp/client/set enabled=no;\r\ + \n :delay 20ms;\r\ + \n /system/ntp/client/set enabled=yes;\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ /system/license/get ]->\"level\" = \"free\" || \\\r\ + \n [ /system/resource/get ]->\"board-name\" = \"x86\") do={\r\ + \n :log debug \"\$0 No ntp client configured, relying on RTC for CHR fr\ + ee license and x86.\";\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ /ip/cloud/get update-time ] = true) do={\r\ + \n :if ([ :typeof [ /ip/cloud/get public-address ] ] = \"ip\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :log debug \"\$0 No time source configured! Returning gracefully...\";\ + \r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set FetchHugeFiles do={\r\ + \n :local ScriptName [ :tostr \$1 ];\r\ + \n :local Url [ :tostr \$2 ];\r\ + \n :local CheckCert [ :tostr \$3 ];\r\ + \n\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global MkDir;\r\ + \n :global RmDir;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :set CheckCert [ \$IfThenElse (\$CheckCert = \"false\") \"no\" \"yes-w\ + ithout-crl\" ];\r\ + \n\r\ + \n :local DirName (\"tmpfs/\" . [ \$CleanName \$ScriptName ]);\r\ + \n :if ([ \$MkDir \$DirName ] = false) do={\r\ + \n \$LogPrint error \$0 (\"Failed creating directory!\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileName (\$DirName . \"/\" . [ \$CleanName \$0 ] . \"-\" . [ \ + \$GetRandom20CharAlNum ]);\r\ + \n :onerror Err {\r\ + \n /tool/fetch check-certificate=\$CheckCert \$Url dst-path=\$FileName \ + \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$ScriptName ] }) as-va\ + lue;\r\ + \n } do={\r\ + \n :if ([ \$WaitForFile \$FileName 500ms ] = true) do={\r\ + \n \$RmFile \$FileName;\r\ + \n }\r\ + \n \$LogPrint debug \$0 (\"Failed downloading from \" . \$Url . \" - \"\ + \_. \$Err);\r\ + \n \$RmDir \$DirName;\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$WaitForFile \$FileName 5s ] = false) do={\r\ + \n \$LogPrint debug \$0 (\"The file downloaded from \" . \$Url . \" did\ + \_not show up.\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileSize [ /file/get \$FileName size ];\r\ + \n :local Return \"\";\r\ + \n :local VarSize 0;\r\ + \n :while (\$VarSize != \$FileSize) do={\r\ + \n :set Return (\$Return . ([ /file/read offset=\$VarSize chunk-size=32\ + 768 file=\$FileName as-value ]->\"data\"));\r\ + \n :set FileSize [ /file/get \$FileName size ];\r\ + \n :set VarSize [ :len \$Return ];\r\ + \n :if (\$VarSize > \$FileSize) do={\r\ + \n :delay 100ms;\r\ + \n }\r\ + \n }\r\ + \n \$RmDir \$DirName;\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FetchUserAgentStr do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global CommitBrief;\r\ + \n :global IfThenElse;\r\ + \n :local Resource [ /system/resource/get ];\r\ + \n :return (\"User-Agent: Mikrotik/\" . \$Resource->\"version\" . \" \" .\ + \_\$Resource->\"architecture-name\" . \\\r\ + \n \" \" . \$Caller . \"/Fetch (\" . \$GlobalScriptsUrl . \"; \" . [ \$\ + CommitBrief ] . \")\");\r\ + \n}\r\ + \n\r\ + \n:set ScriptInstallUpdate do={ :onerror Err {\r\ + \n :local Scripts [ :toarray \$1 ];\r\ + \n :local NewComment [ :tostr \$2 ];\r\ + \n :global InitSetup;\r\ + \n\r\ + \n :global CommitId;\r\ + \n :global ScriptUpdatesCRLF;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global CertificateAvailable;\r\ + \n :global CheckSumsVerify;\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global SumsDirAndFileName;\r\ + \n :global GlobalScriptsUrlSuffix;\r\ + \n :global EitherOr;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global Grep;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global ParseKeyValueStore;\r\ + \n :global RequiredRouterOS;\r\ + \n :global SendNotification2;\r\ + \n :global SymbolForNotification;\r\ + \n :global ValidateSyntax;\r\ + \n :global CommentsInScripts;\r\ + \n\r\ + \n :if ([ \$CertificateAvailable \"Root YE\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, trying wit\ + hout.\");\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=\$Scripts do={\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\ + \r\ + \n \$LogPrint info \$0 (\"Adding new script: \" . \$Script);\r\ + \n /system/script/add name=\$Script owner=\$Script source=\"#!rsc by \ + Vados\\n\" comment=\$NewComment;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :local CommitIdBefore \$CommitId;\r\ + \n :local GlobalConfVersionBefore \$GlobalConfVersion;\r\ + \n :local ReloadGlobal false;\r\ + \n :local DeviceMode [ /system/device-mode/get ];\r\ + \n\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSums ({});\r\ + \n :do {\r\ + \n :local Url (\$GlobalScriptsUrl . \$SumsDirAndFileName . \$GlobalScr\ + iptsUrlSuffix);\r\ + \n \$LogPrint debug \$0 (\"Fetching checksums from url: \" . \$Url);\ + \r\ + \n :set CheckSums [ :deserialize from=json ([ /tool/fetch check-certi\ + ficate=yes-without-crl \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url output\ + =user as-value ]->\"data\") ];\r\ + \n } on-error={ \$LogPrint warning \$0 (\"Err = \" . \$Err); }\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=[ /system/script/find where source~\"^#!rsc by Vado\ + s\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :local ScriptInfo [ \$ParseKeyValueStore (\$ScriptVal->\"comment\") \ + ];\r\ + \n :local SourceNew;\r\ + \n\r\ + \n :foreach Scheduler in=[ /system/scheduler/find where on-event~(\"\\\ + \\b\" . \$ScriptVal->\"name\" . \"\\\\b\") ] do={\r\ + \n :local SchedulerVal [ /system/scheduler/get \$Scheduler ];\r\ + \n :if (\$ScriptVal->\"policy\" != \$SchedulerVal->\"policy\") do={\r\ + \n \$LogPrint warning \$0 (\"Policies differ for script '\" . \$Scr\ + iptVal->\"name\" . \"' and its scheduler '\" . \$SchedulerVal->\"name\" . \ + \"'!\");\r\ + \n }\r\ + \n }\r\ + \n :do {\r\ + \n :if (\$ScriptInfo->\"ignore\" = true) do={\r\ + \n \$LogPrint debug \$0 (\"Ignoring script '\" . \$ScriptVal->\"nam\ + e\" . \"', as requested.\");\r\ + \n :error true;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSum (\$CheckSums->(\$ScriptVal->\"name\"));\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Scri\ + ptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :convert transform=md5 to=hex [ :tolf (\$ScriptVal->\"sou\ + rce\") ] ] = \$CheckSum) do={\r\ + \n \$LogPrint debug \$0 (\"Checksum for script '\" . \$ScriptVal-\ + >\"name\" . \"' matches, ignoring.\");\r\ + \n :error true;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len (\$ScriptInfo->\"certificate\") ] > 0) do={\r\ + \n :if ([ \$CertificateAvailable (\$ScriptInfo->\"certificate\") \"\ + fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, tryi\ + ng without.\");\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n :local BaseUrl [ \$EitherOr (\$ScriptInfo->\"base-url\") \$Globa\ + lScriptsUrl ];\r\ + \n :local UrlSuffix [ \$EitherOr (\$ScriptInfo->\"url-suffix\") \$G\ + lobalScriptsUrlSuffix ];\r\ + \n :local Url (\$BaseUrl . \$ScriptVal->\"name\" . \".rsc\" . \$Url\ + Suffix);\r\ + \n \$LogPrint warning \$0 (\"Fetching script '\" . (\$ScriptVal->\"\ + name\") . \"' from url: \" . \$Url);\r\ + \n :local Result [ /tool/fetch check-certificate=yes-without-crl \\\ + \r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url outp\ + ut=user as-value ];\r\ + \n :if (\$Result->\"status\" = \"finished\") do={\r\ + \n :set SourceNew [ :tolf (\$Result->\"data\") ];\r\ + \n } \r\ + \n } do={\r\ + \n \$LogPrint warning \$0 (\"Failed fetching script '\" . \$ScriptV\ + al->\"name\" . \"': \" . \$Err);\r\ + \n :if (\$Err != \"Fetch failed with status 404\") do={\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$ScriptVal->\"source\" = \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Removing dummy. Typo on installation\ + \?\");\r\ + \n /system/script/remove \$Script;\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Sc\ + riptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :len \$CheckSum ] = 0) do={\r\ + \n \$LogPrint warning \$0 (\"Added the script manually\? Skip u\ + pdates with 'ignore=true' in comment.\");\r\ + \n }\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$SourceNew ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"No update for script '\" . (\$ScriptVal-\ + >\"name\") . \"'.\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :local SourceCRLF [ :tocrlf \$SourceNew ];\r\ + \n :if (\$InitSetup != true && ((\$ScriptVal->\"name\") != \"AM-Globa\ + lConfig\" || (\$ScriptVal->\"name\") != \"AM-GlobalFunc\")) do={\r\ + \n :if (\$SourceNew = (\$ScriptVal->\"source\") || \$SourceCRLF = (\ + \$ScriptVal->\"source\")) do={\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\") \ + . \"' source did not change. End of lines set to CRLF.\");\r\ + \n if (\$ScriptUpdatesCRLF = false) do={\r\ + \n :put [ :tolf \$SourceNew ];\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\"\ + ) . \"' source update end of lines to LF.\");\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([ :pick \$SourceNew 0 14 ] != \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Looks like new script '\" . \$ScriptVa\ + l->\"name\" . \"' is not valid (missing shebang = '\$shebang') Ignoring!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if (\$CommentsInScripts = true) do={\r\ + \n :local CommentLine [ \$Grep \$SourceNew (\"\\23 Script comment:\ + \_\") ];\r\ + \n :if ([ :len \$CommentLine ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"The search in new script '\" . (\$Scr\ + iptVal->\"name\") . \"' for comment in script body, is not found result Ig\ + noring!\");\r\ + \n :error false;\r\ + \n } else={\r\ + \n :if ([ :len (\$ScriptVal->\"comment\")] = 0 || [ :len (\$Scri\ + ptVal->\"comment\")] != [ :pick \$CommentLine ([ :find \$CommentLine \":\"\ + ] + 2) [ :len \$CommentLine] ]) do={\r\ + \n \$LogPrint debug \$0 (\"Exsiting comment for script '\" . (\ + \$ScriptVal->\"name\") . \"' is '\" . (\$ScriptVal->\"comment\") . \"'.\")\ + ;\r\ + \n :set (\$ScriptVal->\"comment\") [ :pick \$CommentLine ([ :f\ + ind \$CommentLine \":\"] + 2) [ :len \$CommentLine] ];\r\ + \n \$LogPrint debug \$0 (\"New comment (\" . (\$ScriptVal->\"c\ + omment\") . \") for script '\" . (\$ScriptVal->\"name\") . \"' has been ad\ + ded!\");\r\ + \n } else={\r\ + \n \$LogPrint debug \$0 (\"Comment for script '\" . (\$ScriptV\ + al->\"name\") . \"' did not change.\");\r\ + \n :error false;\r\ + \n }\r\ + \n } \r\ + \n }\r\ + \n\r\ + \n :local RequiredROS ([ \$ParseKeyValueStore [ \$Grep \$SourceNew (\ + \"\\23 requires RouterOS, \") ] ]->version);\r\ + \n :if ([ \$RequiredRouterOS \$0 [ \$EitherOr \$RequiredROS \"0.0\" ]\ + \_false ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires RouterOS \" . \\\r\ + \n \$RequiredROS . \", which is not met by your installation. I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :local RequiredDM [ \$ParseKeyValueStore [ \$Grep \$SourceNew (\"\ + \\23 requires device-mode, \") ] ];\r\ + \n :local MissingDM ({});\r\ + \n :foreach Feature,Value in=\$RequiredDM do={\r\ + \n :if ([ :typeof (\$DeviceMode->\$Feature) ] = \"bool\" && (\$Devi\ + ceMode->\$Feature) = false) do={\r\ + \n :set MissingDM (\$MissingDM, \$Feature);\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len \$MissingDM ] > 0) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires disabled \" . \\\r\ + \n \"device-mode features (\" . [ :tostr \$MissingDM ] . \"). I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$ValidateSyntax \$SourceNew ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Syntax validation for script '\" . (\$\ + ScriptVal->\"name\") . \"' failed! Ignoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n \r\ + \n :log info (\"Updating script: \" . (\$ScriptVal->\"name\") . \" Sc\ + ript comment: \" . (\$ScriptVal->\"comment\"));\r\ + \n\r\ + \n /system/script/set owner=(\$ScriptVal->\"name\") comment=(\$Script\ + Val->\"comment\") \\\r\ + \n source=[ \$IfThenElse (\$ScriptUpdatesCRLF = true) \$SourceCRL\ + F \$SourceNew ] \$Script;\r\ + \n :if (\$ScriptVal->\"name\" = \"AM-GlobalConfig\" || \\\r\ + \n \$ScriptVal->\"name\" = \"AM-GlobalFunc\" || \\\r\ + \n \$ScriptVal->\"name\" ~ (\"^(AM-GlobalFunc\\\\.d|mod)/.\")) d\ + o={\r\ + \n :set ReloadGlobal true;\r\ + \n }\r\ + \n } on-error={ }\r\ + \n }\r\ + \n :if (\$ReloadGlobal = true) do={\r\ + \n \$LogPrint info \$0 (\"Reloading global configuration and functions.\ + \");\r\ + \n :onerror Err {\r\ + \n :global InitSetup (a);\r\ + \n /system/script {run AM-GlobalConfig; run AM-GlobalFunc;};\r\ + \n } do={\$LogPrint error \$0 (\"Reloading global configuration and fun\ + ctions failed! \" . \$Err)}\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore > \$GlobalConfVersion) do={\r\ + \n \$LogPrint warning \$0 (\"The configuration version decreased from \ + \" . \\\r\ + \n \$GlobalConfVersionBefore . \" to \" . \$GlobalConfVersion . \\\r\ + \n \". Installed an older version\?\");\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore < \$GlobalConfVersion) do={\r\ + \n :global GlobalConfChanges;\r\ + \n :global GlobalConfMigration;\r\ + \n :local ChangeLogCode;\r\ + \n :if ([ :len \$ChangeLogCode ] > 0) do={\r\ + \n :if ([ \$ValidateSyntax \$ChangeLogCode ] = true) do={\r\ + \n :onerror Err {\r\ + \n [ :parse \$ChangeLogCode ];\r\ + \n } do={\$LogPrint warning \$0 (\"The changelog failed to run: \" \ + . \$Err)}\r\ + \n } else={\$LogPrint warning \$0 (\"The changelog failed syntax vali\ + dation!\")}\r\ + \n }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Migration (\$GlobalConfMigration->[ :tostr \$I ]);\r\ + \n :do {\r\ + \n :if ([ :typeof \$Migration ] != \"str\") do={\r\ + \n \$LogPrint debug \$0 (\"Migration code for change \" . \$I .\ + \_\" is not available.\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$ValidateSyntax \$Migration ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Migration code for change \" . \$I\ + \_. \" failed syntax validation!\");\r\ + \n :error false}\r\ + \n \$LogPrint info \$0 (\"Applying migration for change \" . \$I \ + . \": \" . \$Migration);\r\ + \n :onerror Err {\r\ + \n [ :parse \$Migration ];\r\ + \n } do={\$LogPrint warning \$0 (\"Migration code for change \" .\ + \_\$I . \" failed to run: \" . \$Err)}\r\ + \n } on-error={ }\r\ + \n }\r\ + \n }\r\ + \n :local NotificationMessage (\"The configuration version on \" . \$Id\ + entity . \" increased \" . \\\r\ + \n \"to \" . \$GlobalConfVersion . \", current configuration may nee\ + d modification. \" . \\\r\ + \n \"Please review and update global-config-overlay, then re-run glo\ + bal-config.\");\r\ + \n \$LogPrint info \$0 (\$NotificationMessage);\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nChanges:\ + \");\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Change (\$GlobalConfChanges->[ :tostr \$I ]);\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n \" . \\\ + \r\ + \n [ \$SymbolForNotification \"pushpin\" \"*\" ] . \$Change);\r\ + \n \$LogPrint info \$0 (\"Change \" . \$I . \": \" . \$Change);\r\ + \n }\r\ + \n } else={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nNews and\ + \_changes are not available.\");\r\ + \n\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={ :set GlobalConfChanges; \ + } else={ :global GlobalConfChanges (a); }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={ :set GlobalConfMigrati\ + on; } else={ :global GlobalConfMigration (a); }\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n\r\ + \n# load modules\r\ + \n:foreach Script in=[ /system/script/find where name ~ \"^(global-functio\ + ns\\\\.d|mod)/.\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :if ([ \$ValidateSyntax (\$ScriptVal->\"source\") ] = true) do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' f\ + ailed to run: \" . \$Err);\r\ + \n }\r\ + \n } else={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' fai\ + led syntax validation, skipping.\");\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# Add and fix global scheduler\r\ + \n:put \"Add or/and fix Global scheduler...\";\r\ + \n:local OnEvent \"/system/script { run AM-GlobalConfig; run AM-GlobalFunc\ + ; }\";\r\ + \n:local Comment \"Every Day Set Global Environment Variables\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-SetGlobalEnv\" ] ] \ + = 0) do={\r\ + \n /system/scheduler/add name=\"AM-SetGlobalEnv\" comment=\$Comment on-ev\ + ent=\$OnEvent start-time=startup;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" comment ] != \$Comment)\ + \_do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" comment=\$Comment;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" on-event ] != \$OnEvent\ + ) do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" on-event=\$OnEvent;\r\ + \n}\r\ + \n# Add Update Script scheduler\r\ + \n:put \"Add or/and fix Update Script scheduler\";\r\ + \n:set OnEvent \":global ScriptInstallUpdate; \\\$ScriptInstallUpdate;\";\ + \r\ + \n:set Comment \"Every Day Check For Update Scripts\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-ScriptInstallUpdate\ + \" ] ] = 0) do={\r\ + \n/system/scheduler/add name=\"AM-ScriptInstallUpdate\" comment=\$Comment \ + start-time=startup interval=1d on-event=\$OnEvent;\r\ + \n}\r\ + \n\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady;\r\ + \n :retry { :if (\$GlobalConfReady != true) do={ :error (\"Global configs\ + \_not ready.\"); }; } delay=100ms max=5;\r\ + \n\r\ + \n:log warning (\"This script \$ScriptName Loaded \" . \\\r\ + \n [ \$IfThenElse (\$CommitId != \"unknown\") ([ \$CommitBrief ] . \" \ + \") ] . \\\r\ + \n \"on \" . \$devBoardName . \"\\nRouterOS: \" . \$devCurVersion . \"\ + \\nArchitecture: \" . \$devCurArch . \"\\nIdentity: \" . \$Identity);\r\ + \n # signal we are ready\r\ + \n:set GlobalFuncReady true;\r\ + \n:set ExitOK true;\r\ + \n\r\ + \n:log info (\$0 . \"Global configs ready!\");\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n}\r\ + \n" +add comment=\ + "Lookup IP addresses for RKN list and add them to firewall address-list" \ + dont-require-permissions=no name=AM-RKN-dnsLookup owner=AM-RKN-dnsLookup \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="#!rsc by Vados\r\ + \n# RouterOS script: AM-RKN-dnsLookup\r\ + \n# Script comment: Lookup IP addresses for RKN list and add them to firew\ + all address-list\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n# Script description: This script is intended to be run periodically, e.\ + g. once a day, to update the IP addresses in the firewall address-list bas\ + ed on the RKN list.\r\ + \n# It resolves the domain names in the RKN list and a\ + dds the corresponding IP addresses to the specified firewall address-list.\ + \r\ + \n# The script also handles CNAME records by following\ + \_them until it finds A records.\r\ + \n# Usage:\r\ + \n# /system/script { run AM-RKN-dnsLookup; }\r\ + \n:local ScriptName [ :jobname ];\r\ + \n\r\ + \n:global dnsLookupReady false;\r\ + \n:if ([:typeof \$dnsLookupReady] != \"boolean\") do={ :set dnsLookupReady\ + \_true; }\r\ + \nwhile (!\$dnsLookupReady) do={ :nothing; }\r\ + \n:set dnsLookupReady false;\r\ + \n\r\ + \n:local arrRKN {\r\ + \n \"telegra.ph\"\r\ + \n \"grani.ru\"\r\ + \n \"xhamster.com\"\r\ + \n \"lostfilm.tv\"\r\ + \n \"rutracker.org\"\r\ + \n \"lurkmore.to\"\r\ + \n \"dou.ua\"\r\ + \n \"youporn.com\"\r\ + \n \"pornolab.net\"\r\ + \n \"crimerussia.com\"\r\ + \n \"telegram.org\"\r\ + \n \"api.telegram.org\"\r\ + \n \"facebook.com\"\r\ + \n \"instagram.com\"\r\ + \n \"x.com\"\r\ + \n \"twitter.com\"\r\ + \n \"youtube.com\"\r\ + \n \"googleapis.com\"\r\ + \n \"youtu.be\"\r\ + \n \"googlevideo.com\"\r\ + \n \"gstatic.com\"\r\ + \n \"googleusercontent.com\"\r\ + \n \"nhacmp3youtube.com\"\r\ + \n \"telegram.me\"\r\ + \n \"t.me\";}\r\ + \n\r\ + \n:local ListRKN \"RKN-dnsLookup\";\r\ + \n:local ListComment (\"ListRKN managed by \" . \$ScriptName);\r\ + \n:foreach aListItem in=[/ip/firewall/address-list find list=\$ListRKN] do\ + ={\r\ + \n /ip firewall address-list remove \$aListItem;\r\ + \n}\r\ + \n:foreach aItem in=\$arrRKN do={\r\ + \n# :do {\r\ + \n :retry delay=500ms max=5 on-error={:log error \"dns resolve fail\ + ure [ \$aItem - resolve ip ]\"} command={:resolve \$aItem}\r\ + \n# } on-error={\r\ + \n# :log error \"dns resolve failure [ \$arItem - resolve ip ]\";\r\ + \n# }\r\ + \n :foreach dnsRecord in=[/ip dns cache all find where (name=\$aItem)] \ + do={\r\ + \n :if ([/ip dns cache all get \$dnsRecord type]=\"A\") do={\r\ + \n :local newAddress [/ip dns cache all get \$dnsRecord data];\ + \r\ + \n :do {\r\ + \n /ip firewall address-list add list=\$ListRKN address=\$newA\ + ddress comment=\$aItem;\r\ + \n :log warning \"Successfully adding ip to list A record: \ + \$dnsRecord, address: \$newAddress domain: \$aItem\";\r\ + \n } on-error={\r\ + \n :log error \"error when adding ip to list A record: \$dn\ + sRecord, address: \$newAddress domain: \$aItem\";\r\ + \n }\r\ + \n }\r\ + \n :if ([/ip dns cache all get \$dnsRecord type]=\"CNAME\") do={\r\ + \n :local cname;\r\ + \n :local nextCname;\r\ + \n :local cnameAddress;\r\ + \n :set cname [/ip dns cache all find where (name=\$aItem && ty\ + pe=\"CNAME\")];\r\ + \n :set nextCname [/ip dns cache all find where (name=[/ip dns \ + cache all get \$cname data] && type=\"CNAME\")];\r\ + \n :while (\$nextCname != \"\") do={\r\ + \n :set cname \$nextCname;\r\ + \n :set nextCname [/ip dns cache all find where (name=[/ip \ + dns cache all get \$cname data] && type=\"CNAME\")];\r\ + \n }\r\ + \n :foreach aRecord in=[/ip dns cache all find where (name=[/ip\ + \_dns cache all get \$cname data] && type=\"A\")] do={\r\ + \n :do {\r\ + \n :set cnameAddress [/ip dns cache all get \$aRecord d\ + ata];\r\ + \n /ip firewall address-list add list=\$ListRKN address\ + =\$cnameAddress comment=\$aItem;\r\ + \n :log warning \"Succesfully adding ip to list CNAME r\ + ecord: \$cname, A record: \$aRecord, address: \$newAddress, from domain: \ + \$aItem.\";\r\ + \n# /ip firewall raw add action=add-dst-to-address-list\ + \_address-list=\$ListRKN address-list-timeout=none-dynamic chain=preroutin\ + g comment=\$aItem content=\$aItem;\r\ + \n } on-error={\r\ + \n :log error \"error when adding ip to list CNAME reco\ + rd: \$cname, A record: \$aRecord, address: \$newAddress, from domain: \$aI\ + tem.\";\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:if ([ :len [ /system/scheduler/find where name=\$ScriptName ] ] = 0) do\ + ={\r\ + \n :log error (\$ScriptName . \"System Scheduler for script: \" . \$Scri\ + ptName . \" is NOT SET! Fix It Now!\");\r\ + \n /system/scheduler/add name=\$ScriptName on-event=\"/system/script { \ + run \$ScriptName; }\" comment=\"Update RKN DNS Looup\" interval=00:30:00 p\ + olicy=\"ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon\"\ + \_start-time=startup;\r\ + \n }\r\ + \n:set dnsLookupReady true;\r\ + \ndelay 2s; \r\ + \n:global dnsLookupReady (a);\r\ + \n" +add dont-require-permissions=no name="renameLists " owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + local oldName \"Test\";\ + \n:local newName \"Test-IP\";\ + \n/ip firewall address-list\ + \n:foreach i in=[find list=\$oldName] do={\ + \nset \$i list=\$newName;\ + \n}\ + \n" +add dont-require-permissions=no name=IPv6-Settings owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + #############Script Settings##################\ + \n\ + \n:local \"IF-TUN\" \"6to4-tun\"\ + \n:local \"IF-LOC\" \"Bridge-LAN\"\ + \n:local \"POOL-6TO4\" \"poolV6-local\"\ + \n:local \"force-update\" false\ + \n############ use then static ip #######################\ + \n:local \"lease-address\" [/ip address get [find interface=\$\"IF-LOC\"] \ + address]\ + \n#:local \"interface\" \"ext\"\ + \n:local bound 0\ + \n\ + \n###############################################\ + \n\ + \n:local 6to4prefix do={\ + \n :global dec2hex\ + \n :local ipv4 \$1\ + \n :local ipv6 \"2002:\"\ + \n :for i from=0 to=2 step=1 do={ \ + \n :if ( \$i = 2) do={\ + \n :set ipv6 (\$ipv6.\":\")\ + \n }\ + \n :local fp [:find \$ipv4 \".\"]\ + \n :if ( \$fp > 0 ) do={\ + \n :set ipv6 (\$ipv6.[\$dec2hex [:pick \$ipv4 0 \$fp]])\ + \n :set ipv4 [:pick \$ipv4 (\$fp + 1 ) [:len \$ipv4]]\ + \n }\ + \n }\ + \n :return (\$ipv6.[\$dec2hex \$ipv4])\ + \n}\ + \n:local \"str_replace\" do={\ + \n :local \"str-pos\" [:find \$1 \$2]\ + \n :local \"str-len\" [:len \$2]\ + \n :local str \"\"\ + \n :local rest \$1\ + \n :while (\$\"str-pos\" >= 0) do={\ + \n :local t [:pick \$rest 0 \$\"str-pos\"]\ + \n :if ( \$4 > 0 ) do={\ + \n :for i from=1 to=(\$4 - [:len \$t]) step=1 do={\ + \n :set str (\$str.\$5)\ + \n }\ + \n }\ + \n :set str (\$str.\$t.\$3)\ + \n :set rest [:pick \$rest (\$\"str-pos\"+\$\"str-len\") [:len \$re\ + st]]\ + \n :set \"str-pos\" [:find \$rest \$2]\ + \n }\ + \n :return (\$str.\$rest)\ + \n}\ + \n\ + \n:if (\$bound=1) do={\ + \n :local t [:find \$\"lease-address\" \"/\"]\ + \n\ + \n :if ( \$t >= 0 ) do={\ + \n :set \"lease-address\" [:pick \$\"lease-address\" 0 \$t]\ + \n }\ + \n\ + \n :if ( [/interface 6to4 print count-only where name=\$\"IF-TUN\"] = 0\ + ) do={\ + \n /interface 6to4 add !keepalive local-address=\$\"lease-address\"\ + \_mtu=1280 name=\$\"IF-TUN\" remote-address=10.30.30.0\ + \n :set \"force-update\" true\ + \n }\ + \n\ + \n :local \"IPv4-TUN\" [/interface 6to4 get [find name=\$\"IF-TUN\"] lo\ + cal-address];\ + \n\ + \n :if ( \$\"lease-address\" != \$\"IPv4-TUN\" || \$\"force-update\" = \ + true) do={\ + \n /interface 6to4 set [find name=\$\"IF-TUN\"] local-address=\$\"l\ + ease-address\"\ + \n /ipv6 address remove [find interface=\$\"IF-TUN\"]\ + \n /ipv6 address remove [find interface=\$\"IF-LOC\"]\ + \n /ipv6 route remove [find gateway ~ \$\"IF-TUN\" and dst-address=\ + \"::/0\"]\ + \n :local \"pool-prefix\" [\$6to4prefix \$\"lease-address\"]\ + \n :if ( [/ipv6 pool print count-only where name=\$\"POOL-6TO4\"] =\ + \_0) do={ \ + \n /ipv6 pool add name=\$\"POOL-6TO4\" prefix=(\$\"pool-prefix\ + \".\":1::/64\") prefix-length=64\ + \n } else={\ + \n /ipv6 pool set [/ipv6 pool find where name=\$\"POOL-6TO4\"] \ + prefix=(\$\"pool-prefix\".\":1::/64\")\ + \n }\ + \n /ipv6 address add address=(\$\"pool-prefix\".\"::\".[\$MAC2EUI64\ + \_[/interface get [find name=\$\"interface\"] mac-address]].\"/3\") interf\ + ace=\$\"IF-TUN\" advertise=no\ + \n /ipv6 address add eui-64=yes from-pool=\$\"POOL-6TO4\" interface\ + =\$\"IF-LOC\" advertise=yes\ + \n /ipv6 route add dst-address=::/0 gateway=(\"::10.30.30.81%\".\$\ + \"IF-TUN\")\ + \n :local t [/ipv6 address get [find interface=\$\"IF-LOC\"] addres\ + s]\ + \n :set t [:pick \$t 0 [:find \$t \"/\"]]\ + \n :if ( [/ipv6 dhcp-server option print count-only where name=DNS]\ + \_= 0) do={\ + \n /ipv6 dhcp-server option add code=23 name=DNS value=(\"0x\".\ + [\$\"str_replace\" \$t \":\" \"\" 4 \"0\"])\ + \n } else={\ + \n /ipv6 dhcp-server option set [/ipv6 dhcp-server option find \ + where name=DNS] value=(\"0x\".[\$\"str_replace\" \$t \":\" \"\" 4 \"0\"])\ + \n }\ + \n :if ( [/ipv6 dhcp-server print count-only where interface=\$\"IF\ + -LOC\"] = 0) do={\ + \n /ipv6 dhcp-server add address-pool=\$\"POOL-6TO4\" dhcp-opti\ + on=DNS interface=\$\"IF-LOC\" lease-time=1d name=\$\"IF-LOC\"\ + \n }\ + \n :if ( [ipv6 nd print count-only where interface=\$\"IF-LOC\"] = \ + 0) do={\ + \n /ipv6 nd add advertise-dns=no interface=\$\"IF-LOC\" other-c\ + onfiguration=yes\ + \n }\ + \n }\ + \n}" +add dont-require-permissions=no name=IPv6-GlobalFunc owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + global MAC2EUI64 do={\ + \n :local EUI64 \"\"\ + \n :local MAC \$1\ + \n :local HEX2DEC \"0123456789ABCDEF\"\ + \n :for i from=0 to=5 step=1 do={\ + \n :if ( \$i = 0 ) do={\ + \n :set EUI64 [:pick \$MAC 0]\ + \n :set EUI64 (\$EUI64.[:tostr [:pick \$\"HEX2DEC\" ([:find \$\ + \"HEX2DEC\" [:pick \$MAC 1]] ^ 2)]])\ + \n }\ + \n :if ( \$i = 1 || \$i = 3) do={\ + \n :set EUI64 (\$EUI64.\":\")\ + \n }\ + \n :if ( \$i = 2 ) do={\ + \n :set EUI64 (\$EUI64.\"FF:FE\")\ + \n }\ + \n :if ( [:find \$MAC \":\"] = 2 ) do={\ + \n :set MAC [:pick \$MAC 3 [:len \$MAC]]\ + \n :set EUI64 (\$EUI64.[:pick \$MAC 0 2])\ + \n }\ + \n }\ + \n :return (\$EUI64)\ + \n}\ + \n\ + \n:global dec2hex do={\ + \n :local hex \"\"\ + \n :local dec [:tonum \$1]\ + \n :for i from=0 to=4 step=4 do={\ + \n :set hex ([:pick \"0123456789ABCDEF\" ((\$dec>>\$i)&0xf) (((\$de\ + c>>\$i)&0xf)+1)].\$hex)\ + \n }\ + \n :return ([:tostr \$hex])\ + \n}" +add comment=Generator dont-require-permissions=no name=proxySet owner=vados \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="# 2. WireGuard interface\r\ + \n/interface/wireguard/add name=wg-VDSina private-key=\"4CHyNhbOs923/EyNDg\ + uMXJXdghL2I3UyzXPwrANVgHs=\" listen-port=21641\r\ + \n/interface/wireguard/peers/add interface=wg-VDSina public-key=\"o5IgCl1/\ + G6xbl6j7sPaP4ZNYREeYOmqZjKbm+v0b9jU=\" preshared-key=\"RNvF7T1cg5tyXcYQnD9\ + N5BEp6e20vVJQTv0GMjyvHU0=\" endpoint-address=10.30.30.33 endpoint-port=518\ + 20 allowed-address=0.0.0.0/0 persistent-keepalive=11\r\ + \n/ip/address/add address=10.9.0.3/32 interface=wg-VDSina\r\ + \n\r\ + \n# 3. DNS\r\ + \n{ :local rawDNS [/ip/dns/get servers]; :local prevDNS \"\"; :foreach s i\ + n=[:toarray \$rawDNS] do={ :if ([:len \$prevDNS] > 0) do={ :set prevDNS \"\ + \$prevDNS,\$s\" } else={ :set prevDNS \$s } }; /system/script/add name=VDS\ + ina-prev-dns comment=\"\$prevDNS\" source=\"\" }\r\ + \n/ip/dns/set servers=8.8.4.4\r\ + \n/ip/route/add dst-address=8.8.4.4/32 gateway=wg-VDSina distance=1 commen\ + t=VDSina-dns\r\ + \n\r\ + \n# 4. Enable WireGuard\r\ + \n/interface/wireguard/enable wg-VDSina\r\ + \n" +add dont-require-permissions=no name=AM-Telegram-Send owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\ + \n# RouterOS script: AM-Telegram-Send\ + \n# Script comment: Mini script for send messages to admin Telegram chat\ + \n# Copyright (c) 2007-2026 Vados \ + \n#\ + \n#\ + \n# requires RouterOS, version=7.19\ + \n# requires device-mode, fetch\ + \n#\ + \n# Script description: \ + \n#\ + \n#\ + \n# Usage:\ + \n# /system/script { run AM-Telegram-Send \"Message here )))\"; }\ + \n\ + \n/system/script\ + \nadd name=telegram-send dont-require-permissions=no source={\ + \n :global TelegramTokenId;\ + \n :global TelegramChatId;\ + \n :local message [:tostr \$1];\ + \n :local identity [/system/identity/get name];\ + \n :local sendMsg (\"[\".\$identity.\"] \".\$message)\ + \n /tool/fetch url=(\"https://api.telegram.org/bot\".\$TelegramTokenId.\\\ + \n \"/sendMessage\\\?chat_id=\".\$TelegramChatId.\\\ + \n \"&text=\".\$sendMsg.\\\ + \n \"&parse_mode=HTML\") \\\ + \n mode=https keep-result=no\ + \n}" +add dont-require-permissions=no name=telegram-send owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="\ + \n :global TelegramTokenId;\ + \n :global TelegramChatId;\ + \n :local message [:tostr \$1];\ + \n :local identity [/system/identity/get name];\ + \n :local sendMsg (\"[\".\$identity.\"] \".\$message)\ + \n /tool/fetch url=(\"https://api.telegram.org/bot\".\$TelegramTokenId.\\\ + \n \"/sendMessage\\\?chat_id=\".\$TelegramChatId.\\\ + \n \"&text=\".\$sendMsg.\\\ + \n \"&parse_mode=HTML\") \\\ + \n mode=https keep-result=no\ + \n" +/user group +add name=backup policy="ssh,read,write,policy,test,web,sniff,sensitive,api,res\ + t-api,!local,!telnet,!ftp,!reboot,!winbox,!password,!romon" +/ip smb +set enabled=no interfaces=lo +/interface bridge port +add bridge=Bridge-LAN interface=ether5 +add bridge=Bridge-LAN interface=ether2 +add bridge=Bridge-LAN interface=ether4 trusted=yes +add bridge=Bridge-LAN interface=ether3 +add bridge=Bridge-LAN interface=wlan2 +add bridge=Bridge-LAN interface=wlan1 +/interface bridge settings +set use-ip-firewall=yes +/ip firewall connection tracking +set udp-timeout=10s +/ip neighbor discovery-settings +set discover-interface-list=LAN lldp-med=no lldp-poe-power=no +/ip settings +set accept-redirects=yes ipv4-multipath-hash-policy=l4 max-neighbor-entries=\ + 8192 +/ipv6 settings +set disable-ipv6=yes disable-link-local-address=yes forward=no \ + max-neighbor-entries=8192 multipath-hash-policy=l4 +/interface detect-internet +set detect-interface-list=WAN internet-interface-list=Internet \ + lan-interface-list=LAN request-interval=1m wan-interface-list=WAN +/interface l2tp-server server +set authentication=mschap2 use-ipsec=yes +/interface list member +add interface=Unet-ether1 list=Internet +add interface=WG list=WAN +add interface=Bridge-LAN list=LAN +add interface=Unet-ether1 list=WAN +/interface wireguard peers +add allowed-address=0.0.0.0/0 client-allowed-address=::/0 endpoint-address=\ + 109.107.177.79 endpoint-port=29185 interface=WG name=VDSina \ + persistent-keepalive=10s public-key=\ + "zuRsO+dL/j6awaBWZ/NgnHR+bCNUjRWbaHFpdzkNnw4=" +add allowed-address=0.0.0.0/0 client-allowed-address=::/0 disabled=yes \ + endpoint-address=10.30.30.33 endpoint-port=55055 interface=wg-nuc name=\ + NUC persistent-keepalive=11s public-key=\ + "+OvzAjLC45Td52hjkQPmpGvC3RGhZ+Q2CDMJ1U/D4Rw=" +/interface wireless cap +# +set bridge=Bridge-LAN caps-man-addresses=10.30.30.10 \ + caps-man-certificate-common-names=CAPsMAN-51B25E507D07 certificate=\ + CAP-488F5A8E1ABC discovery-interfaces=Bridge-LAN enabled=yes interfaces=\ + wlan2,wlan1 lock-to-caps-man=yes +/ip address +add address=10.30.30.81/24 interface=Bridge-LAN network=10.30.30.0 +add address=10.8.0.2/24 interface=WG network=10.8.0.0 +add address=192.168.33.2/24 disabled=yes interface=wg-nuc network=\ + 192.168.33.0 +/ip cloud +set update-time=no +/ip dhcp-client +add add-default-route=special-classless check-gateway=arp comment=Unet-client \ + default-route-distance=2 default-route-tables=main:2 dhcp-options=\ + hostname,clientid_duid interface=Unet-ether1 name=UnetDHCP use-peer-dns=\ + no use-peer-ntp=no +/ip dhcp-server config +set accounting=no +/ip dhcp-server lease +add address=10.30.30.101 client-id=1:48:8f:5a:8e:ee:a8 mac-address=\ + 48:8F:5A:8E:EE:A8 server=masterDHCP +add address=10.30.30.107 client-id=1:f0:de:f1:91:d:1e mac-address=\ + F0:DE:F1:91:0D:1E server=masterDHCP +add address=10.30.30.83 client-id=1:0:80:77:ec:43:86 mac-address=\ + 00:80:77:EC:43:86 server=masterDHCP +add address=10.30.30.70 dhcp-option=domain-search-list,HostName \ + dhcp-option-set=HP1505n mac-address=00:1E:0B:1A:88:8D server=masterDHCP +add address=10.30.30.50 client-id=1:4:7c:16:36:f5:37 mac-address=\ + 04:7C:16:36:F5:37 server=masterDHCP +add address=10.30.30.71 client-id=1:10:2d:41:2c:20:c2 mac-address=\ + 10:2D:41:2C:20:C2 server=masterDHCP +add address=10.30.30.61 client-id=1:10:8:c1:81:59:5f mac-address=\ + 10:08:C1:81:59:5F server=masterDHCP +add address=10.30.30.72 client-id=1:10:38:1f:d2:5f:13 mac-address=\ + 10:38:1F:D2:5F:13 server=masterDHCP +add address=10.30.30.99 client-id=1:10:b1:df:e9:d0:d5 mac-address=\ + 10:B1:DF:E9:D0:D5 server=masterDHCP +add address=10.30.30.111 client-id=1:fe:2:52:be:3f:16 mac-address=\ + FE:02:52:BE:3F:16 server=masterDHCP +add address=10.30.30.92 client-id=1:d4:6d:6d:fd:e5:49 mac-address=\ + D4:6D:6D:FD:E5:49 server=masterDHCP +add address=10.30.30.93 client-id=1:7e:8b:1e:cb:b8:cb mac-address=\ + CC:42:10:12:46:94 server=masterDHCP +add address=10.30.30.94 client-id=1:50:cc:f8:85:96:a mac-address=\ + 50:CC:F8:85:96:0A server=masterDHCP +add address=10.30.30.82 client-id=1:e4:18:6b:58:34:ab mac-address=\ + E4:18:6B:58:34:AB server=masterDHCP +add address=10.30.30.75 client-id=1:0:27:15:41:97:a9 mac-address=\ + 00:27:15:41:97:A9 server=masterDHCP +add address=10.30.30.77 dhcp-option=HostName dhcp-option-set=StartLife1 \ + mac-address=C4:82:E1:03:49:10 server=masterDHCP +add address=10.30.30.73 client-id=1:10:38:1f:82:81:f2 mac-address=\ + 10:38:1F:82:81:F2 server=masterDHCP +add address=10.30.30.95 client-id=1:88:ae:dd:60:39:54 dhcp-option=HostName \ + dhcp-option-set=VADOS-NUC mac-address=88:AE:DD:60:39:54 server=masterDHCP +add address=10.30.30.98 client-id=1:c8:5b:76:d8:cc:bf dhcp-option=HostName \ + dhcp-option-set=VADOS-LEN mac-address=C8:5B:76:D8:CC:BF server=masterDHCP +add address=10.30.30.96 client-id=iPhone mac-address=4A:91:ED:0E:1F:39 \ + server=masterDHCP +add address=10.30.30.74 client-id=MiTV dhcp-option=HostName dhcp-option-set=\ + MiTV mac-address=20:72:A9:D3:34:36 server=masterDHCP +add address=10.30.30.30 dhcp-option=HostName dhcp-option-set=miller \ + mac-address=F0:75:5D:1E:5F:71 server=masterDHCP +add address=10.30.30.100 client-id=iVados dhcp-option=HostName \ + dhcp-option-set=iVados mac-address=DE:4D:72:F3:1A:9F server=masterDHCP +add address=10.30.30.10 allow-dual-stack-queue=yes client-id=\ + ff:0:0:0:df:0:3:0:1:f0:75:5d:1e:5f:85 dhcp-option=HostName \ + dhcp-option-set=MikroNUC mac-address=F0:75:5D:1E:5F:85 server=masterDHCP \ + use-src-mac=yes +/ip dhcp-server network +add address=10.30.30.0/24 caps-manager=10.30.30.10 dhcp-option=\ + domain-search-list dhcp-option-set=domain-search-set dns-server=\ + 10.30.30.33,109.107.177.79 domain=WORKGROUP gateway=10.30.30.81 netmask=\ + 24 ntp-server=10.30.30.81,10.30.30.10 wins-server=10.30.30.95,10.30.30.98 +/ip dns +set allow-remote-requests=yes cache-max-ttl=1d doh-max-concurrent-queries=100 \ + doh-max-server-connections=20 servers=8.8.8.8 verify-doh-cert=yes +/ip dns static +add address=109.107.177.79 comment="VDSina Server DNS" name=ns1.vados.ru \ + type=A +add address=109.107.177.79 name=vds.vados.ru type=A +add comment="Local DNS server for vados.ru" disabled=yes ns=10.30.30.81 \ + regexp=*.vados.ru type=NS +add comment="*.vados.ru Regexp" disabled=yes forward-to=10.30.30.81 regexp=\ + "\".*\\\\.vados\\\\.ru\"" type=FWD +add comment="*.30.30.10.in-addr.arpa Regexp" disabled=yes forward-to=\ + 10.30.30.81 regexp="\".*\\\\.30\\\\.30\\\\.10\\\\.in-addr\\\\.arpa" type=\ + FWD +add address=10.30.30.81 comment=NS.vados.ru.local disabled=yes name=\ + ns.vados.ru.local type=A +add comment="DNS ns.vados.ru.local" disabled=yes match-subdomain=yes name=\ + vados.ru ns=ns.vados.ru.local type=NS +add address=10.30.30.33 comment=reg.vados.ru name=reg.vados.ru type=A +add address=8.8.8.8 comment="Google-dns IPv4 1" match-subdomain=yes name=\ + dns.google type=A +add address=8.8.4.4 comment="Google-dns IPv4 2" name=dns.google type=A +add address=10.30.30.33 comment=AMSTER name=amster.vados.ru type=A +add address=10.30.30.33 name=ros.vados.ru type=A +add address=10.30.30.95 name=vados-nuc type=A +add address=10.30.30.33 name=amster-nuc type=A +add address=10.30.30.33 name=amster type=A +add address=10.30.30.33 name=amster-reg type=A +add address=10.30.30.33 disabled=yes name=mng.vados.ru type=A +add address=10.30.30.33 name=mgr.vados.ru type=A +add address=10.30.30.33 name=prt.vados.ru type=A +add address=10.30.30.33 disabled=yes match-subdomain=yes name=\ + mikrots.vados.ru type=A +add address=10.30.30.30 comment=MILLER name=miller.vados.ru type=A +add address=10.30.30.30 name=MILLER-LEN type=A +add address=10.30.30.30 name=miller type=A +add address=1.0.0.1 comment="CloudFlare-doh IPv4 2" disabled=yes name=\ + security.cloudflare-dns.com type=A +add address=1.1.1.1 comment="CloudFlare-doh IPv4 1" disabled=yes name=\ + security.cloudflare-dns.com type=A +add address=9.9.9.9 comment="Cuad9-dns IPv4 1" disabled=yes name=\ + dns.quad9.net type=A +add address=149.112.112.112 comment="Cuad9-dns IPv4 2" disabled=yes name=\ + dns.quad9.net type=A +add address=195.133.25.16 comment="DNS Comss" disabled=yes name=dns.comss.one \ + type=A +add address=10.30.30.81 comment=LocalNet name=MikroVados type=A +add address=10.30.30.10 name=MikroNUC type=A +add address=10.30.30.86 disabled=yes name=MikroYellow type=A +add address=10.30.30.85 disabled=yes name=MikroMic type=A +add address=10.30.30.95 name=VADOS-NUC type=A +add address=10.30.30.98 name=VADOS-LEN type=A +add address=91.192.189.2 comment=Unet-DNS disabled=yes name=ns1.unet.ws type=\ + A +add address=10.30.30.20 disabled=yes name=garage type=A +/ip firewall address-list +add address=91.215.120.206 list=TRUSTED +add address=185.71.70.78 disabled=yes list=TRUSTED +add address=176.53.203.171 disabled=yes list=TRUSTED +add address=176.53.203.170 disabled=yes list=TRUSTED +add address=176.53.202.194 disabled=yes list=TRUSTED +add address=8.8.4.0/24 list=YouTube +add address=8.8.8.0/24 list=YouTube +add address=8.34.208.0/20 list=YouTube +add address=8.35.192.0/20 list=YouTube +add address=23.236.48.0/20 list=YouTube +add address=23.251.128.0/19 list=YouTube +add address=34.0.0.0/15 list=YouTube +add address=34.2.0.0/16 list=YouTube +add address=34.3.0.0/23 list=YouTube +add address=34.3.3.0/24 list=YouTube +add address=34.3.4.0/24 list=YouTube +add address=34.3.8.0/21 list=YouTube +add address=34.3.16.0/20 list=YouTube +add address=34.3.32.0/19 list=YouTube +add address=34.3.64.0/18 list=YouTube +add address=34.4.0.0/14 list=YouTube +add address=34.8.0.0/13 list=YouTube +add address=34.16.0.0/12 list=YouTube +add address=34.32.0.0/11 list=YouTube +add address=34.64.0.0/10 list=YouTube +add address=34.128.0.0/10 list=YouTube +add address=35.184.0.0/13 list=YouTube +add address=35.192.0.0/14 list=YouTube +add address=35.196.0.0/15 list=YouTube +add address=35.198.0.0/16 list=YouTube +add address=35.199.0.0/17 list=YouTube +add address=35.199.128.0/18 list=YouTube +add address=35.200.0.0/13 list=YouTube +add address=35.208.0.0/12 list=YouTube +add address=35.224.0.0/12 list=YouTube +add address=35.240.0.0/13 list=YouTube +add address=57.140.192.0/18 list=YouTube +add address=64.15.112.0/20 list=YouTube +add address=64.233.160.0/19 list=YouTube +add address=66.22.228.0/23 list=YouTube +add address=66.102.0.0/20 list=YouTube +add address=66.249.64.0/19 list=YouTube +add address=70.32.128.0/19 list=YouTube +add address=72.14.192.0/18 list=YouTube +add address=74.125.0.0/16 list=YouTube +add address=104.154.0.0/15 list=YouTube +add address=104.196.0.0/14 list=YouTube +add address=104.237.160.0/19 list=YouTube +add address=107.167.160.0/19 list=YouTube +add address=107.178.192.0/18 list=YouTube +add address=108.59.80.0/20 list=YouTube +add address=108.170.192.0/18 list=YouTube +add address=108.177.0.0/17 list=YouTube +add address=130.211.0.0/16 list=YouTube +add address=136.22.160.0/20 list=YouTube +add address=136.22.176.0/21 list=YouTube +add address=136.22.184.0/23 list=YouTube +add address=136.22.186.0/24 list=YouTube +add address=142.250.0.0/15 list=YouTube +add address=146.148.0.0/17 list=YouTube +add address=152.65.208.0/22 list=YouTube +add address=152.65.214.0/23 list=YouTube +add address=152.65.218.0/23 list=YouTube +add address=152.65.222.0/23 list=YouTube +add address=152.65.224.0/19 list=YouTube +add address=162.120.128.0/17 list=YouTube +add address=162.216.148.0/22 list=YouTube +add address=162.222.176.0/21 list=YouTube +add address=172.110.32.0/21 list=YouTube +add address=172.217.0.0/16 list=YouTube +add address=172.253.0.0/16 list=YouTube +add address=173.194.0.0/16 list=YouTube +add address=173.255.112.0/20 list=YouTube +add address=192.158.28.0/22 list=YouTube +add address=192.178.0.0/15 list=YouTube +add address=193.186.4.0/24 list=YouTube +add address=199.36.154.0/23 list=YouTube +add address=199.36.156.0/24 list=YouTube +add address=199.192.112.0/22 list=YouTube +add address=199.223.232.0/21 list=YouTube +add address=207.223.160.0/20 list=YouTube +add address=208.65.152.0/22 list=YouTube +add address=208.68.108.0/22 list=YouTube +add address=208.81.188.0/22 list=YouTube +add address=208.117.224.0/19 list=YouTube +add address=209.85.128.0/17 list=YouTube +add address=216.58.192.0/19 list=YouTube +add address=216.73.80.0/20 list=YouTube +add address=216.239.32.0/19 list=YouTube +add address=2ip.ru list=Test-IP +add address=wg.vados.ru comment=VDSIna list=TRUSTED +add address=10.8.0.0/24 comment="WG VDSIna" disabled=yes list=TRUSTED +add address=ikus.pesc.ru comment=ikus.pesc.ru list=NoVdsina +add address=love.ru comment=love.ru list=NoVdsina +add address=gosuslugi.ru comment=gosuslugi.ru list=NoVdsina +add address=pochta.ru comment=pochta.ru list=NoVdsina +add address=vdsina.com list=NoVdsina +add address=vdsina.ru list=NoVdsina +add address=youla.ru list=NoVdsina +add address=kupiprodai.ru list=NoVdsina +add address=myip.ru list=Test-IP +add address=geoip.co list=NoVdsina +add address=comp-lenta.ru list=NoVdsina +add address=128.85.102.70 comment=update.microsoft.com list=\ + windowsupdateslist +add address=wu-b-net.trafficmanager.net. comment=\ + ctldl.windowsupdate.com.delivery.microsoft.com list=windowsupdateslist +add address=ctldl.windowsupdate.com.delivery.microsoft.com. comment=\ + ctldl.windowsupdate.com list=windowsupdateslist +add address=redir.update.msft.com.trafficmanager.net. comment=\ + update.microsoft.com list=windowsupdateslist +add address=geo.prod.do.dsp.trafficmanager.net. comment=\ + geo.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=13.74.139.29 comment=array605.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=kv801.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + kv801.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=e12437.d.akamaiedge.net. comment=\ + kv801.prod.do.dsp.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=displaycatalog-rp.md.mp.microsoft.com.akadns.net. comment=\ + displaycatalog.mp.microsoft.com list=windowsupdateslist +add address=rp-consumer-prod-displaycatalog-geomap.trafficmanager.net. \ + comment=displaycatalog-rp.md.mp.microsoft.com.akadns.net list=\ + windowsupdateslist +add address=sls.update.microsoft.com. comment=slscr.update.microsoft.com \ + list=windowsupdateslist +add address=glb.sls.prod.dcat.dsp.trafficmanager.net. comment=\ + sls.update.microsoft.com list=windowsupdateslist +add address=fe3.delivery.mp.microsoft.com. comment=\ + fe3cr.delivery.mp.microsoft.com list=windowsupdateslist +add address=glb.cws.prod.dcat.dsp.trafficmanager.net. comment=\ + fe3.delivery.mp.microsoft.com list=windowsupdateslist +add address=fe2cr.update.msft.com.trafficmanager.net. comment=\ + fe2cr.update.microsoft.com list=windowsupdateslist +add address=download.windowsupdate.com.delivery.microsoft.com. comment=\ + download.windowsupdate.com list=windowsupdateslist +add address=wu-f-net.trafficmanager.net. comment=\ + download.windowsupdate.com.delivery.microsoft.com list=windowsupdateslist +add address=tsfe.trafficmanager.net. comment=\ + tsfe.trafficshaping.dsp.mp.microsoft.com list=windowsupdateslist +add address=edge.ds-c7110-microsoft.global.dns.qwilted-cds.cqloud.com. \ + comment=9.au.download.windowsupdate.com list=windowsupdateslist +add address=52.158.227.125 comment=array812.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=au.download.windowsupdate.com.delivery.microsoft.com. comment=\ + au.download.windowsupdate.com list=windowsupdateslist +add address=a767.dspw65.akamai.net. comment=\ + download.windowsupdate.com.edgesuite.net list=windowsupdateslist +add address=52.151.58.88 comment=array810.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=consumer-licensing-aks2aks.md.mp.microsoft.com.akadns.net. \ + comment=licensing.mp.microsoft.com list=windowsupdateslist +add address=licensing-prod-frontdoor-geomap.trafficmanager.net. comment=\ + consumer-licensing-aks2aks.md.mp.microsoft.com.akadns.net list=\ + windowsupdateslist +add address=star.f.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.f.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=cdp-f-nlu-net.trafficmanager.net. comment=\ + star.f.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=star.b.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.b.tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=cdp-f-tlu-net.trafficmanager.net. comment=\ + star.b.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=storeedgefd.xbetservices.akadns.net. comment=\ + storeedgefd.dsx.mp.microsoft.com list=windowsupdateslist +add address=\ + storeedgefd.dsx.mp.microsoft.com.edgekey.net.globalredir.akadns.net. \ + comment=storeedgefd.dsx.mp.microsoft.com.edgekey.net list=\ + windowsupdateslist +add address=e16646.dscg.akamaiedge.net. comment=\ + storeedgefd.dsx.mp.microsoft.com.edgekey.net.globalredir.akadns.net list=\ + windowsupdateslist +add address=40.69.76.189 comment=array621.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=bg.microsoft.map.fastly.net. comment=\ + 3.au.download.windowsupdate.com list=windowsupdateslist +add list=windowsupdateslist +add address=40.69.75.33 comment=array613.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=star.sf.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedgeextensions.sf.tlu.dl.delivery.mp.microsoft.com list=\ + windowsupdateslist +add address=cdp-f-ssl-tlu-net.trafficmanager.net. comment=\ + star.sf.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=a2033.dscd.akamai.net. comment=\ + star.sb.tlu.dl.delivery.mp.microsoft.com.edgesuite.net list=\ + windowsupdateslist +add address=13.74.138.242 comment=array602.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=dl.delivery.mp.microsoft.com.delivery.microsoft.com. comment=\ + dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=dcat-f-nlu-net.trafficmanager.net. comment=\ + dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=1d.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=1D.tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=dcat-b-tlu-net.trafficmanager.net. comment=\ + 1d.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=a122.dscg3.akamai.net. comment=\ + tlu.dl.delivery.mp.microsoft.com.edgesuite.net list=windowsupdateslist +add address=geover.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + geover.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=e10370.d.akamaiedge.net. comment=\ + geover.prod.do.dsp.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=52.232.182.21 comment=array511.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.70.77.95 comment=array519.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=a1683.dscd.akamai.net. comment=\ + dl.delivery.mp.microsoft.com-c.edgesuite.net list=windowsupdateslist +add address=consumer-collections-aks2eap.md.mp.microsoft.com.akadns.net. \ + comment=collections.mp.microsoft.com list=windowsupdateslist +add address=a1847.dscd.akamai.net. comment=\ + wildcard.f.tlu.dl.delivery.mp.microsoft.com.edgesuite.net list=\ + windowsupdateslist +add address=13.74.142.6 comment=array607.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=au.download.windowsupdate.com.edgesuite.net. comment=\ + 2.au.download.windowsupdate.com list=windowsupdateslist +add address=a767.dscg3.akamai.net. comment=\ + au.download.windowsupdate.com.edgesuite.net list=windowsupdateslist +add address=52.156.123.84 comment=array818.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.78.254 comment=array610.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=4.au.download.windowsupdate.com.delivery.microsoft.com. comment=\ + 4.au.download.windowsupdate.com list=windowsupdateslist +add address=cl-glcb907925.gcdn.co. comment=\ + 4.au.download.windowsupdate.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=52.167.167.111 comment=array510.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=40.69.70.43 comment=array623.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=tsfe.trafficshaping.dsp.mp.microsoft.com.delivery.microsoft.com. \ + comment=tsfe.trafficshaping.dsp.mp.microsoft.com list=windowsupdateslist +add address=ts-frontend.trafficmanager.net. comment=\ + tsfe.trafficshaping.dsp.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=40.69.75.248 comment=array616.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.74.84 comment=array615.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=sds.trafficmanager.net. comment=software-download.microsoft.com \ + list=windowsupdateslist +add address=40.69.74.80 comment=array611.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.175.242.182 comment=array804.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=dlc-shim.trafficmanager.net. comment=download.microsoft.com list=\ + windowsupdateslist +add address=e12671.dscd.akamaiedge.net. comment=\ + download.microsoft.com.edgekey.net list=windowsupdateslist +add address=20.187.55.34 comment=array808.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.167.231 comment=array513.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=52.167.222.13 comment=array518.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=kv601.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + kv601.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=e12358.d.akamaiedge.net. comment=\ + kv601.prod.do.dsp.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. comment=\ + tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=dcat-f-tlu-net.trafficmanager.net. comment=\ + tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=a1856.dspw65.akamai.net. comment=\ + tlu.dl.delivery.mp.microsoft.com-c.edgesuite.net list=windowsupdateslist +add address=52.167.164.252 comment=array506.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=13.74.137.54 comment=array601.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=kv501.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + kv501.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=collections-prod-frontdoor-geomap.trafficmanager.net. comment=\ + consumer-collections-aks2eap.md.mp.microsoft.com.akadns.net list=\ + windowsupdateslist +add address=52.167.160.173 comment=array509.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=40.70.77.148 comment=array516.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.200.126 comment=array507.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=52.167.167.59 comment=array508.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.76.172 comment=array620.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=smtp.mail.me.com comment=smtp.mail.me.com list=NoVdsina +add address=40.70.76.5 comment=array514.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.186.119 comment=array618.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.177.176.186 comment=array520.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=40.70.76.27 comment=array515.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.160.43 comment=array503.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.110 comment=array817.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.142.8 comment=array614.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.138.254 comment=array603.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.167.96 comment=array501.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.76.215 comment=array622.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.143.200 comment=array606.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.111 comment=array818.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.112 comment=array819.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.137.53 comment=array608.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.98 comment=array805.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.96 comment=array803.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.104 comment=array811.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.101 comment=array808.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.99 comment=array806.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.17 comment=array802.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.97 comment=array804.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.103 comment=array810.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.16 comment=array801.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.70.77.177 comment=array517.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.164.84 comment=array512.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.105 comment=array812.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.107 comment=array814.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.232.161.82 comment=array502.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.108 comment=array815.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.161.91 comment=array505.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.102 comment=array809.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.109 comment=array816.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.106 comment=array813.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.117 comment=array622.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.107 comment=array612.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.108 comment=array613.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.105 comment=array610.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.113 comment=array618.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.116 comment=array621.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.98 comment=array603.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.114 comment=array619.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.111 comment=array616.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.97 comment=array602.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.106 comment=array611.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.110 comment=array615.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.119 comment=array624.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.135 comment=array514.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.97 comment=array503.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.99 comment=array604.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.61 comment=array504.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.131 comment=array510.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.132 comment=array511.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.96 comment=array601.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.104 comment=array609.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.138 comment=array517.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.60 comment=array501.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.139 comment=array518.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.118 comment=array623.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.113 comment=array820.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.103 comment=array608.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.100 comment=array807.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.129 comment=array508.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.134 comment=array513.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.62 comment=array505.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.137 comment=array516.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.101 comment=array606.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.96 comment=array502.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.130 comment=array509.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.112 comment=array617.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.100 comment=array605.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.115 comment=array620.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.136 comment=array515.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=purchase.md.mp.microsoft.com.akadns.net. comment=\ + purchase.mp.microsoft.com list=windowsupdateslist +add address=e110219.a.akamaiedge.net. comment=\ + purchase.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=72.153.5.140 comment=array519.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.141 list=windowsupdateslist +add address=72.153.5.63 comment=array506.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.128 comment=array507.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.102 comment=array607.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=storesdk.xbetservices.akadns.net. comment=\ + storesdk.dsx.mp.microsoft.com list=windowsupdateslist +add address=e16646.g.akamaiedge.net. comment=\ + storesdk.dsx.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=software-download.microsoft.com.delivery.microsoft.com. comment=\ + software-download.microsoft.com list=windowsupdateslist +add address=download.prss.microsoft.com.akamaized.net. comment=\ + software-download.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.109 comment=array614.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.133 comment=array512.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=213.180.192.0/19 comment=YandexDisk list=NoVdsina +add address=87.250.250.50 comment=YandexDisk list=NoVdsina +add address=consumer-prod-bcfd-v7-afd.trafficmanager.net. comment=\ + displaycatalog.mp.microsoft.com list=windowsupdateslist +add address=purchase-europeeap.md.mp.microsoft.com.akadns.net. comment=\ + purchase.md.mp.microsoft.com.akadns.net list=windowsupdateslist +add address=ne-az-conspurchase-mp.trafficmanager.net. comment=\ + purchase-europeeap.md.mp.microsoft.com.akadns.net list=windowsupdateslist +add address=storeedgefd.dsx.mp.microsoft.com.edgekey.net. comment=\ + cdn.storeedgefd.dsx.mp.microsoft.com list=windowsupdateslist +add address=star.f.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.f.tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=a1996.dscd.akamai.net. comment=\ + wildcard.f.dl.delivery.mp.microsoft.com.edgesuite.net list=\ + windowsupdateslist +add address=cp801.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + cp801.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=glb.tas02.sls-prod.dcat.dsp.mp.microsoft.com. comment=\ + tas02.sls.update.microsoft.com list=windowsupdateslist +add address=cp601.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + cp601.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=star.sb.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedgeextensions.sb.tlu.dl.delivery.mp.microsoft.com list=\ + windowsupdateslist +add address=star.sf.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.sf.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=cdp-f-ssl-nlu-net.trafficmanager.net. comment=\ + star.sf.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=tm-pifd-prod-gbl-weight.trafficmanager.net. comment=\ + paymentinstruments.mp.microsoft.com list=windowsupdateslist +add address=10.30.30.33 comment=amster list=NoWG +add address=10.30.30.10 comment=MikroNUC disabled=yes list=NoWG +add address=89.110.101.17 comment=!!!VDSINA!!!! disabled=yes list=NoWG +add address=wg.vados.ru comment=VDSina-vpn list=VDSina +add address=80.85.244.9 list=TRUSTED +add address=disc801.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + disc801.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=185.71.70.78 disabled=yes list=TRUSTED +add address=176.53.203.171 disabled=yes list=TRUSTED +add address=176.53.203.170 disabled=yes list=TRUSTED +add address=176.53.202.194 disabled=yes list=TRUSTED +add address=89.110.101.17 comment="!!!VDSina Server!!!" list=NoVdsina +add address=dns.quad9.net comment=Quad9-dns disabled=yes list=Trusted-DNS +add address=dns.google comment=Google-dns list=Trusted-DNS +add address=security.cloudflare-dns.com comment=CloudFlare-dns list=\ + Trusted-DNS +add address=time.google.com comment=Google-time list=Trusted-NTP +add address=0.ru.pool.ntp.org comment=ru.pool.ntp.org disabled=yes list=\ + Trusted-NTP +add address=1.ru.pool.ntp.org disabled=yes list=Trusted-NTP +add address=2.ru.pool.ntp.org comment=2.ru.pool.ntp.org disabled=yes list=\ + Trusted-NTP +add address=3.ru.pool.ntp.org comment=3.ru.pool.ntp.org disabled=yes list=\ + Trusted-NTP +add address=cp.vdsina.com comment=VDSina-vpn list=VDSina +add address=vdsina.ru comment=VDSina-vpn list=VDSina +add address=ns.vados.ru comment=VDSina-vpn list=VDSina +add address=vdsina.com comment=VDSina-vpn list=VDSina +add address=cp.vdsina.ru comment=VDSina-vpn list=VDSina +add address=ns1.vdsina.ru comment=VDSina-vpn list=VDSina +add address=10.30.30.0/24 comment="Local Network" list=Local-Net +add address=10.8.0.0/24 comment="WireGuard Network" list=Local-Net +add address=146.120.64.208/29 comment="External Net" list=Unet +add address=esia.gosuslugi.ru comment=esia.gosuslugi.ru list=NoVdsina +add address=lk.gosuslugi.ru comment=lk.gosuslugi.ru list=NoVdsina +add address=trudvsem.ru comment=trudvsem.ru list=NoVdsina +add address=10.30.30.30 comment=miller disabled=yes list=NoWG +add address=10.30.30.33 comment="Direct connet to DNS servers" list=\ + Direct-DNS +add address=109.107.177.79 list=VPN-Server +add address=10.0.0.0/8 list=RFC1918 +add address=172.16.0.0/12 list=RFC1918 +add address=192.168.0.0/16 list=RFC1918 +add address=8.8.4.4 disabled=yes list=to_vpn +add address=myip.ru disabled=yes list=to_vpn +add address=10.30.30.98 disabled=yes list=to_vpn +/ip firewall filter +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=12h chain=input comment="block honeypot DNS" \ + connection-state=new dst-port=53 in-interface=Unet-ether1 protocol=udp \ + src-address-list=!Trusted-DNS +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input connection-state=new dst-port=53 \ + in-interface=Unet-ether1 protocol=tcp src-address-list=!Trusted-DNS +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input comment=\ + "block honeypot ssh rdp winbox" connection-state=new dst-port=22,8291 \ + in-interface=Unet-ether1 protocol=tcp src-address-list=!TRUSTED +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input comment="block honeypot asterisk" \ + connection-state=new dst-port=5060 in-interface=Unet-ether1 protocol=udp +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input comment="block honeypot NTP" \ + connection-state=new dst-port=123 in-interface=Unet-ether1 protocol=udp +add action=fasttrack-connection chain=forward comment=\ + "FastTrack for non-VPN connections" connection-mark=!VPN_No_FastTrack \ + connection-state=established,related,new disabled=yes +add action=accept chain=input dst-port=29185 in-interface=Unet-ether1 \ + protocol=tcp +add action=accept chain=input dst-port=29185 in-interface=Unet-ether1 \ + protocol=udp +add action=accept chain=input comment="tcp Accept input DNS queries from LAN" \ + dst-port=53 protocol=tcp src-address=10.30.30.0/24 +add action=accept chain=input comment="udp Accept input DNS queries" \ + dst-port=53 protocol=udp src-address=10.30.30.0/24 +add action=accept chain=output comment="accept WireGuard" disabled=yes \ + dst-address-list=VPN-Server out-interface=Unet-ether1 protocol=udp \ + src-port=13231 +add action=accept chain=output disabled=yes dst-address-list=VPN-Server \ + dst-port=29185 out-interface=Unet-ether1 protocol=udp +add action=fasttrack-connection chain=forward comment="defconf: fasttrack" \ + connection-state=established,related,new disabled=yes routing-mark=\ + !To-VPN +add action=accept chain=output disabled=yes protocol=tcp src-port=443 +add action=accept chain=output disabled=yes src-address=127.0.0.1 +add action=accept chain=forward comment="defconf: accept in ipsec policy" \ + disabled=yes ipsec-policy=in,ipsec +add action=accept chain=forward comment="defconf: accept out ipsec policy" \ + disabled=yes ipsec-policy=out,ipsec +add action=accept chain=forward comment=\ + "defconf: accept established,related, untracked" connection-state=\ + established,related,untracked disabled=yes +add action=drop chain=forward comment="defconf: drop invalid" \ + connection-state=invalid in-interface=Unet-ether1 src-address=\ + !10.30.30.0/24 +add action=drop chain=forward comment=\ + "defconf: drop all from WAN not DSTNATed" connection-nat-state=!dstnat \ + connection-state=new in-interface=Unet-ether1 src-address-list=!VDSina +add action=accept chain=input in-interface=Unet-ether1 protocol=icmp \ + src-address-list=VDSina +add action=accept chain=input connection-state=established +add action=accept chain=input connection-state=related +/ip firewall mangle +add action=accept chain=prerouting comment=RFC1918 dst-address-list=RFC1918 \ + in-interface-list=!WAN +add action=mark-routing chain=prerouting comment="Mark Routing main for NoWG" \ + new-routing-mark=main passthrough=no src-address-list=NoWG +add action=mark-routing chain=prerouting comment=\ + "Mark Routing main for NoVdsina" disabled=yes dst-address-list=NoVdsina \ + new-routing-mark=main passthrough=no src-address=10.30.30.0/24 +add action=mark-connection chain=prerouting comment=\ + "Transit for address-list \"to_vpn\"" connection-mark=no-mark disabled=\ + yes dst-address-list=to_vpn in-interface-list=!WAN new-connection-mark=\ + to-vpn-conn routing-mark=!main +add action=mark-routing chain=prerouting comment=\ + "Find routing over table r_to_vpn" connection-mark=to-vpn-conn disabled=\ + yes in-interface-list=!WAN new-routing-mark=r_to_vpn +add action=mark-routing chain=prerouting comment=\ + "Mark Routing main for VDSina" dst-address-list=VPN-Server \ + new-routing-mark=main passthrough=no src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=\ + "Mark Routing main for MikroNuc" disabled=yes dst-address-list=AWG-Proxy \ + new-routing-mark=main src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=\ + "!!!ERROR Mark Routing main for Unet!!!" disabled=yes dst-address-list=\ + Unet new-routing-mark=main passthrough=no +add action=mark-routing chain=prerouting comment="Test-IP sites" disabled=yes \ + dst-address=!10.30.30.0/24 dst-address-list=Test-IP new-routing-mark=\ + To-VPN src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=FuckRKN disabled=yes \ + dst-address=!10.30.30.0/24 dst-address-list=RKN-dnsLookup \ + new-routing-mark=To-VPN passthrough=no src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment="WG for all" \ + dst-address-list=!NoVdsina new-routing-mark=To-VPN src-address=\ + 10.30.30.0/24 +add action=change-mss chain=forward comment="MSS on WG" new-mss=1380 \ + out-interface=WG protocol=tcp routing-mark=To-VPN tcp-flags=syn tcp-mss=\ + 1381-65535 +add action=mark-connection chain=prerouting comment=\ + "Mark VPN connections to exclude from FastTrack" disabled=yes \ + new-connection-mark=VPN_No_FastTrack routing-mark=To-VPN +add action=mark-routing chain=prerouting comment="VPN Network" disabled=yes \ + dst-address=10.8.0.0/24 new-routing-mark=To-VPN src-address=10.30.30.0/24 +add action=mark-connection chain=output comment="For this router" \ + connection-mark=no-mark disabled=yes dst-address-list=to_vpn \ + new-connection-mark=to-vpn-conn-local +add action=mark-routing chain=output comment="For this router" \ + connection-mark=to-vpn-conn-local disabled=yes new-routing-mark=r_to_vpn +/ip firewall nat +add action=redirect chain=dstnat comment="udp Redirect DNS in NAT" disabled=\ + yes dst-port=53 protocol=udp src-address=10.30.30.0/24 src-address-list=\ + !NoWG to-addresses=10.30.30.33 to-ports=53 +add action=redirect chain=dstnat comment="tcp Redirect DNS in NAT" disabled=\ + yes dst-port=53 protocol=tcp src-address=10.30.30.0/24 src-address-list=\ + !NoWG to-addresses=10.30.30.33 to-ports=53 +add action=masquerade chain=srcnat comment=\ + "Nat Addresses for routing mark r_to_vpn" disabled=yes routing-mark=\ + r_to_vpn +add action=dst-nat chain=dstnat comment=\ + "53/tcp dst-nat to amster.loc. From VDSina Server" dst-port=53 \ + in-interface=Unet-ether1 protocol=tcp src-address-list=VPN-Server \ + to-addresses=10.30.30.33 to-ports=53 +add action=dst-nat chain=dstnat comment=\ + "53/udp dst-nat to amster.loc. From VDSina Server" dst-port=53 protocol=\ + udp src-address-list=VPN-Server to-addresses=10.30.30.33 to-ports=53 +add action=dst-nat chain=dstnat disabled=yes dst-port=44333 in-interface=\ + Unet-ether1 protocol=udp src-address=!10.30.30.0/24 to-addresses=\ + 10.30.30.33 to-ports=44333 +add action=masquerade chain=srcnat comment=WG dst-address-list=!NoVdsina \ + routing-mark=To-VPN +add action=masquerade chain=srcnat comment="Default NAT" out-interface=\ + Unet-ether1 routing-mark=!To-VPN src-address=10.30.30.0/24 +add action=dst-nat chain=dstnat dst-port=44044 in-interface=Unet-ether1 \ + protocol=udp src-address=109.107.177.79 to-addresses=10.30.30.33 \ + to-ports=44044 +add action=dst-nat chain=dstnat comment=amster.vados.ruhttps dst-port=443 \ + fragment=no in-interface=Unet-ether1 protocol=tcp routing-mark=main \ + to-addresses=10.30.30.33 to-ports=443 +add action=dst-nat chain=dstnat comment="amster.vados.ru For Certbot HTTP" \ + dst-port=80 in-interface=Unet-ether1 protocol=tcp to-addresses=\ + 10.30.30.33 to-ports=80 +add action=dst-nat chain=dstnat comment="amster.vados.ru 3x-ui" disabled=yes \ + dst-port=2904 in-interface=Unet-ether1 protocol=tcp to-addresses=\ + 10.30.30.33 to-ports=2904 +add action=dst-nat chain=dstnat comment=reg.vados.ru disabled=yes dst-port=\ + 5000 in-interface=Unet-ether1 protocol=tcp to-addresses=10.30.30.33 \ + to-ports=5000 +add action=dst-nat chain=dstnat comment="Docker swarm" disabled=yes dst-port=\ + 2377 in-interface=Unet-ether1 protocol=tcp to-addresses=10.30.30.33 \ + to-ports=2377 +add action=dst-nat chain=dstnat comment=npm dst-port=81 in-interface=\ + Unet-ether1 protocol=tcp routing-mark=To-VPN to-addresses=10.30.30.33 \ + to-ports=81 +add action=dst-nat chain=dstnat comment=SSH dst-port=22 in-interface=\ + Unet-ether1 protocol=tcp src-address-list=TRUSTED to-addresses=\ + 10.30.30.33 to-ports=22 +add action=dst-nat chain=dstnat comment="portainer agent" disabled=yes \ + dst-port=2377 in-interface=Unet-ether1 protocol=tcp to-addresses=\ + 10.30.30.33 to-ports=2377 +add action=dst-nat chain=dstnat disabled=yes dst-port=22 in-interface=\ + Unet-ether1 protocol=tcp routing-mark=!To-VPN to-addresses=10.30.30.33 \ + to-ports=22 +add action=dst-nat chain=dstnat comment="Yandex.DNS Family" disabled=yes \ + dst-port=53 in-interface=Bridge-LAN protocol=udp src-address-list=KIDS \ + time=6h-15m,sun,mon,tue,wed,thu,fri,sat to-addresses=77.88.8.3 +add action=dst-nat chain=dstnat disabled=yes dst-port=53 in-interface=\ + Bridge-LAN protocol=tcp src-address-list=KIDS time=\ + 6h-15m,sun,mon,tue,wed,thu,fri,sat to-addresses=77.88.8.3 +/ip firewall raw +add action=drop chain=prerouting comment="Drop Honeypot Hackers" \ + in-interface=Unet-ether1 src-address-list="Honeypot Hacker" +add action=accept chain=prerouting comment="Trusted DNS" disabled=yes \ + in-interface-list=WAN src-address-list=Trusted-DNS +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + extras.getpagespeed.com content=extras.getpagespeed.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=linux.duke.edu \ + content=linux.duke.edu disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=youtube.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + googleapis.com disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=ggpht.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=youtu.be \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=ytimg.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + googlevideo.com disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=gstatic.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + googleusercontent.com disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + nhacmp3youtube.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=rutube.ru \ + content=rutube.ru disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment="me.com raw" \ + content=me.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "icloud.com raw" content=icloud.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "smtp.mail.me.com raw" content=smtp.mail.me.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "smtp.mail.me.com.akadns.net raw" content=smtp.mail.me.com.akadns.net \ + disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "api.db-ip.com raw" content=api.db-ip.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + esia.gosuslugi.ru content=esia.gosuslugi.ru disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=gosuslugi.ru \ + content=gosuslugi.ru disabled=yes +/ip firewall service-port +set ftp disabled=yes +set tftp disabled=yes +set irc disabled=no +set sip disabled=yes +set pptp disabled=yes +set rtsp disabled=no +/ip ipsec settings +set accounting=no +/ip ipsec policy +set 0 disabled=yes dst-address=0.0.0.0/0 src-address=0.0.0.0/0 +/ip ipsec profile +set [ find default=yes ] dpd-interval=2m dpd-maximum-failures=5 +/ip proxy +set cache-administrator="" max-cache-size=none port=80,443 src-address=\ + 0.0.0.0 +/ip route +add check-gateway=none comment=WG-Default disabled=no distance=1 dst-address=\ + 0.0.0.0/0 gateway=WG routing-table=To-VPN scope=30 target-scope=10 +/ip service +set ftp disabled=yes +set telnet disabled=yes +set reverse-proxy disabled=yes +set ssh address=10.30.30.0/24 port=2222 +set winbox address=10.30.30.0/24 +set api address=10.30.30.0/24 +set api-ssl address=10.30.30.0/24 disabled=yes +set www-ssl certificate=Root-YE port=18443 +set www address=10.30.30.0/24 disabled=yes port=18880 +/ip service webserver +set acme-plain=no crl-plain=no graphs-plain=no graphs-secure=no index-plain=\ + no index-secure=no rest-secure=no scep-plain=no webfig-plain=no \ + webfig-secure=no +/ip ssh +set forwarding-enabled=both publickey-authentication-options=verify-required +/ip upnp +set enabled=yes +/ip upnp interfaces +add interface=Bridge-LAN type=internal +add forced-ip=146.120.64.211 interface=Unet-ether1 type=external +add forced-ip=109.107.177.79 interface=*E type=external +add interface=lo type=internal +add interface=wlan2 type=internal +add interface=wlan1 type=internal +/ipv6 dhcp-client +add add-default-route=yes disabled=yes interface=*F rapid-commit=no request=\ + info use-interface-duid=yes validate-server-duid=no +/ipv6 dhcp-server option sets +add name=domain-search-set options=domain-search-list +add name="=\"Client ID\"" options=HostName +/ipv6 firewall address-list +add address=::/128 comment="defconf: unspecified address" list=bad_ipv6 +add address=::1/128 comment="defconf: lo" list=bad_ipv6 +add address=fec0::/10 comment="defconf: site-local" list=bad_ipv6 +add address=::ffff:0.0.0.0/96 comment="defconf: ipv4-mapped" list=bad_ipv6 +add address=::/96 comment="defconf: ipv4 compat" list=bad_ipv6 +add address=100::/64 comment="defconf: discard only " list=bad_ipv6 +add address=2001:db8::/32 comment="defconf: documentation" list=bad_ipv6 +add address=2001:10::/28 comment="defconf: ORCHID" list=bad_ipv6 +add address=3ffe::/16 comment="defconf: 6bone" list=bad_ipv6 +/ipv6 firewall filter +add action=accept chain=input comment=\ + "defconf: accept established,related,untracked" connection-state=\ + established,related,untracked +add action=drop chain=input comment="defconf: drop invalid" connection-state=\ + invalid +add action=accept chain=input comment="defconf: accept ICMPv6" protocol=\ + icmpv6 +add action=accept chain=input comment="defconf: accept UDP traceroute" \ + dst-port=33434-33534 protocol=udp +add action=accept chain=input comment=\ + "defconf: accept DHCPv6-Client prefix delegation." dst-port=546 protocol=\ + udp src-address=fe80::/10 +add action=accept chain=input comment="defconf: accept IKE" dst-port=500,4500 \ + protocol=udp +add action=accept chain=input comment="defconf: accept ipsec AH" protocol=\ + ipsec-ah +add action=accept chain=input comment="defconf: accept ipsec ESP" protocol=\ + ipsec-esp +add action=accept chain=input comment=\ + "defconf: accept all that matches ipsec policy" ipsec-policy=in,ipsec +add action=drop chain=input comment=\ + "defconf: drop everything else not coming from LAN" in-interface-list=\ + !LAN +add action=accept chain=forward comment=\ + "defconf: accept established,related,untracked" connection-state=\ + established,related,untracked +add action=drop chain=forward comment="defconf: drop invalid" \ + connection-state=invalid +add action=drop chain=forward comment=\ + "defconf: drop packets with bad src ipv6" src-address-list=bad_ipv6 +add action=drop chain=forward comment=\ + "defconf: drop packets with bad dst ipv6" dst-address-list=bad_ipv6 +add action=drop chain=forward comment="defconf: rfc4890 drop hop-limit=1" \ + hop-limit=equal:1 protocol=icmpv6 +add action=accept chain=forward comment="defconf: accept ICMPv6" protocol=\ + icmpv6 +add action=accept chain=forward comment="defconf: accept HIP" protocol=139 +add action=accept chain=forward comment="defconf: accept IKE" dst-port=\ + 500,4500 protocol=udp +add action=accept chain=forward comment="defconf: accept ipsec AH" protocol=\ + ipsec-ah +add action=accept chain=forward comment="defconf: accept ipsec ESP" protocol=\ + ipsec-esp +add action=accept chain=forward comment=\ + "defconf: accept all that matches ipsec policy" ipsec-policy=in,ipsec +add action=drop chain=forward comment=\ + "defconf: drop everything else not coming from LAN" in-interface-list=\ + !LAN +/ipv6 nd +set [ find default=yes ] advertise-dns=yes disabled=yes interface=Bridge-LAN \ + managed-address-configuration=yes other-configuration=yes pref64=::/64 +/ipv6 nd prefix +add autonomous=no disabled=yes interface=Bridge-LAN on-link=no prefix=none +/ipv6 nd prefix default +set dhcp6-pd-preferred=yes +/ipv6 nd proxy +add disabled=yes interface=lo +/ipv6 nd settings +set router-advertisement-route-distance=100 +/routing settings +set single-process=no +/system clock +set time-zone-name=Europe/Moscow +/system clock manual +set time-zone=+03:00 +/system identity +set name=MikroMaster +/system logging +add action=email disabled=yes regex="* added|changed|removed *" topics=\ + system,info +/system ntp client +set enabled=yes +/system ntp server +set enabled=yes manycast=yes multicast=yes use-local-clock=yes +/system ntp client servers +add address=0.ru.pool.ntp.org +add address=1.ru.pool.ntp.org +add address=2.ru.pool.ntp.org +add address=3.ru.pool.ntp.org +add address=time.google.com disabled=yes +/system package update +set check-certificate=yes-without-crl ip-version=ipv4 +/system scheduler +add comment="Every Day Set Global Environment Variables" name=AM-SetGlobalEnv \ + on-event="/system/script { run AM-GlobalConfig; run AM-GlobalFunc; }" \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Every Day Check For Update Scripts" interval=1d name=\ + AM-ScriptInstallUpdate on-event=\ + ":global ScriptInstallUpdate; \$ScriptInstallUpdate;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Update RKN DNS Looup" disabled=yes interval=20m name=\ + AM-RKN-dnsLookup on-event="/system/script { run AM-RKN-dnsLookup; }" \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +/system watchdog +set watchdog-timer=no +/tool e-mail +set certificate-verification=no from=MikroVados port=587 \ + server=smtp.mail.me.com tls=starttls user=vadossio@icloud.com +/tool mac-server +set allowed-interface-list=LAN +/tool mac-server mac-winbox +set allowed-interface-list=LAN +/tool netwatch +add certificate=DigiCert-Global-Root-CA check-certificate=no comment="doh, doh\ + -url=https://security.cloudflare-dns.com/dns-query, notify, name=CloudFlar\ + e-doh IPv4 1" disabled=yes down-script="" host=1.1.1.1 http-codes="" \ + interval=10s name="CloudFlare-doh IPv4 1" port=443 test-script="" type=\ + https-get up-script="" +add check-certificate=no comment="doh-fallback, doh-url=https://security.cloud\ + flare-dns.com/dns-query, doh-cert=DigiCert Global Root CA, notify, name=Cl\ + oudFlare-doh IPv4" disabled=yes down-script="" host=1.0.0.1 http-codes="" \ + name="CloudFlare-doh IPv4" port=443 test-script="" type=https-get \ + up-script="" +add comment="dns, notify, name=Google-DNS IPv4" disabled=yes dns-server=\ + 8.8.8.8 down-script="" host=dns.google http-codes="" name=\ + "Google-DNS IPv4" test-script="" type=dns up-script="" +add certificate=GTS-Root-R4 check-certificate=yes comment="doh-fallback, doh-u\ + rl=https://dns.google/dns-query, doh-cert=GTS Root R1, notify, name=Google\ + -DOH IPv4" disabled=yes dns-server=8.8.4.4 down-script="" host=dns.google \ + http-codes="" name="Google-DOH IPv4" port=443 record-type=NS src-address=\ + "" test-script="" type=dns up-script="" +add comment="dns-fallback, notify, name=Cuad9-DOH IPv4" disabled=yes \ + dns-server=9.9.9.9 down-script="" host=dns9.quad9.net http-codes="" \ + interval=10s name="Cuad9-DOH IPv4" src-address=146.120.64.211 \ + test-script="" timeout=800ms type=dns up-script="" +add certificate=DigiCert-Assured-ID-Root-CA comment="doh-fallback, doh-url=htt\ + ps://dns.quad9.net/dns-query, name=Cuad9-DOH IPv4" disabled=yes \ + down-script="" host=149.112.112.112 http-codes="" interval=10s name=\ + "Cuad9-DOH IPv4" port=443 test-script="" type=https-get up-script="" +add comment="dns-fallback, notify, name=ns1.unet.ws" disabled=yes \ + down-script="" host=91.192.189.2 http-codes="" interval=10s name=\ + ns1.unet.ws test-script="" type=simple up-script="" +add check-certificate=yes comment=\ + "doh, doh-url=https://vds.vados.ru/vds-query, name=vds.vados.ru" \ + disabled=yes down-script="" host=109.107.177.79 http-codes="" name=\ + vds.vados.ru port=443 test-script="" type=https-get up-script="" +add comment="WireGuard NAT " disabled=yes down-script="/log info \"[WG-Monitor\ + ] WireGuard is NOT running - reverting rules\"\ + \n/ip firewall nat remove [find comment=\"Redirect to Router\"]\ + \n/ip dns set servers=8.8.8.8\ + \n/ip dns cache flush\ + \n/ip route remove [find comment=\"wg-auto-route\"]\ + \n" host=10.8.0.1 http-codes="" interval=15s name=WG test-script="" \ + timeout=5s type=simple up-script="/log info \"[WG-Monitor] WireGuard is RU\ + NNING - applying rules\";\ + \n:local routeFile \"ips.txt\";\ + \n:local routeDefAddr \"0.0.0.0/0\";\ + \n:local routeGateway \"10.8.0.1\";\ + \n:local routeIF \"WG\";\ + \n:local routeTable \"To-VPN\";\ + \n:local routeComment \"wg-auto-route\";\ + \n/ip dns cache flush;\ + \n:if ([:len [/ip firewall nat find comment=\"Redirect to Router\"]] > 0) \ + do={\ + \n /ip firewall nat remove [find comment=\"Redirect to Router\"];\ + \n}\ + \n/ip firewall nat add action=redirect chain=dstnat src-address-list=\"Red\ + irectDNS\" dst-port=53 protocol=udp comment=\"Redirect to Router\";\ + \n/ip dns set servers=\$routeGateway;\ + \n:local content [/file get \$routeFile contents];\ + \n:if ([:len \$content] > 0) do={\ + \n :local line \"\";\ + \n :for i from=0 to=([:len \$content] - 1) do={\ + \n :local char [:pick \$content \$i];\ + \n :if (\$char != \"\\r\" && \$char != \"\\n\") do={\ + \n :set line (\$line . \$char);\ + \n } else={\ + \n :if ([:len \$line] > 5) do={\ + \n :do { /ip route add disabled=no dst-address=\$routeDefAd\ + dr gateway=\$routeIF distance=1 scope=30 target-scope=10 routing-table=\$r\ + outeTable comment=\$routeComment check-gateway=none } on-error={}\ + \n }\ + \n :set line \"\";\ + \n }\ + \n }\ + \n :if ([:len \$line] > 5) do={\ + \n# :do { /ip route add dst-address=\$line gateway=\$routeGateway \ + distance=1 comment=\$routeComment } on-error={}\ + \n :do { /ip route add disabled=no dst-address=\$routeDefAddr ga\ + teway=\$routeIF distance=1 scope=30 target-scope=10 routing-table=\$routeT\ + able comment=\$routeComment check-gateway=none } on-error={}\ + \n }\ + \n}\ + \n:local dotPos [:find \$routeGateway \".\"];\ + \n:if (\$dotPos != \"\") do={\ + \n :local firstOctet [:pick \$routeGateway 0 \$dotPos];\ + \n :local derivedDstAddress (\$firstOctet . \".30.0.0/15\");\ + \n :do { /ip route add dst-address=\$derivedDstAddress gateway=\$routeG\ + ateway distance=1 comment=\$routeComment } on-error={}\ + \n}\ + \n\ + \n" +add comment=WG-Default disabled=yes down-script=\ + "/ip route disable [find comment=\"WG-Default\"];" host=10.8.0.1 \ + interval=15s name=WG packet-count=20 test-script="" thr-loss-count=5 \ + type=icmp up-script="/ip route enable [find comment=\"WG-Default\"];" +/user aaa +set accounting=no diff --git a/MikroMaster/tmp/Diff-Config.rsc b/MikroMaster/tmp/Diff-Config.rsc deleted file mode 100644 index c3dd41f..0000000 --- a/MikroMaster/tmp/Diff-Config.rsc +++ /dev/null @@ -1,3 +0,0 @@ -33,34d32 -< #error exporting "/disk" (timeout) -< #error exporting "/interface/ethernet/switch" (timeout) -- 2.52.0