rename scripts and add file extension ".rsc"

No functional change for the user... The migration is done
automatically.
This commit is contained in:
Christian Hesse 2023-03-07 10:57:42 +01:00
parent 1e6e0646e2
commit a832fd04ef
125 changed files with 5622 additions and 5175 deletions

View file

@ -19,7 +19,7 @@ Run the complete base installation:
/file/remove "letsencrypt-R3.pem";
:delay 1s;
:foreach Script in={ "global-config"; "global-config-overlay"; "global-functions" } do={
/system/script/add name=$Script source=([ /tool/fetch check-certificate=yes-without-crl ("https://git.eworm.de/cgit/routeros-scripts/plain/" . $Script) output=user as-value]->"data");
/system/script/add name=$Script source=([ /tool/fetch check-certificate=yes-without-crl ("https://git.eworm.de/cgit/routeros-scripts/plain/" . $Script . ".rsc") output=user as-value]->"data");
};
/system/script { run global-config; run global-functions; };
/system/scheduler/add name="global-scripts" start-time=startup on-event="/system/script { run global-config; run global-functions; }";

View file

@ -2,9 +2,9 @@
# template scripts -> final scripts
# markdown files -> html files
TEMPLATE = $(wildcard *.template)
CAPSMAN = $(TEMPLATE:.template=.capsman)
LOCAL = $(TEMPLATE:.template=.local)
TEMPLATE = $(wildcard *.template.rsc)
CAPSMAN = $(TEMPLATE:.template.rsc=.capsman.rsc)
LOCAL = $(TEMPLATE:.template.rsc=.local.rsc)
MARKDOWN = $(wildcard *.md doc/*.md doc/mod/*.md)
HTML = $(MARKDOWN:.md=.html)
@ -14,13 +14,13 @@ all: $(CAPSMAN) $(LOCAL) $(HTML)
%.html: %.md Makefile
markdown $< | sed 's/href="\([-_\./[:alnum:]]*\)\.md"/href="\1.html"/g' > $@
%.local: %.template Makefile
sed -e '/\/caps-man/d' -e 's|%PATH%|interface\/wireless|' -e 's|%TEMPL%|$(suffix $@)|' \
%.local.rsc: %.template.rsc Makefile
sed -e '/\/caps-man/d' -e 's|%PATH%|interface\/wireless|' -e 's|%TEMPL%|.local|' \
-e '/^# !!/,/^# !!/c # !! Do not edit this file, it is generated from template!' \
< $< > $@
%.capsman: %.template Makefile
sed -e '/\/interface\/wireless/d' -e 's/%PATH%/caps-man/' -e 's/%TEMPL%/$(suffix $@)/' \
%.capsman.rsc: %.template.rsc Makefile
sed -e '/\/interface\/wireless/d' -e 's|%PATH%|caps-man|' -e 's|%TEMPL%|.capsman|' \
-e '/^# !!/,/^# !!/c # !! Do not edit this file, it is generated from template!' \
< $< > $@

View file

@ -87,7 +87,7 @@ date and time is set correctly!
Now let's download the main scripts and add them in configuration on the fly.
:foreach Script in={ "global-config"; "global-config-overlay"; "global-functions" } do={ /system/script/add name=$Script source=([ /tool/fetch check-certificate=yes-without-crl ("https://git.eworm.de/cgit/routeros-scripts/plain/" . $Script) output=user as-value]->"data"); };
:foreach Script in={ "global-config"; "global-config-overlay"; "global-functions" } do={ /system/script/add name=$Script source=([ /tool/fetch check-certificate=yes-without-crl ("https://git.eworm.de/cgit/routeros-scripts/plain/" . $Script . ".rsc") output=user as-value]->"data"); };
![screenshot: import scripts](README.d/04-import-scripts.avif)
@ -112,7 +112,7 @@ Editing configuration
The configuration needs to be tweaked for your needs. Edit
`global-config-overlay`, copy relevant configuration from
[`global-config`](global-config) (the one without `-overlay`).
[`global-config`](global-config.rsc) (the one without `-overlay`).
Save changes and exit with `Ctrl-o`.
/system/script/edit global-config-overlay source;
@ -247,7 +247,7 @@ still use my scripts to manage and deploy yours, by specifying `base-url`
This will fetch and install a script `hello-world.rsc` from the given url:
$ScriptInstallUpdate hello-world.rsc "base-url=https://git.eworm.de/cgit/routeros-scripts-custom/plain/";
$ScriptInstallUpdate hello-world "base-url=https://git.eworm.de/cgit/routeros-scripts-custom/plain/";
![screenshot: install custom script](README.d/13-install-custom-script.avif)

View file

@ -1,42 +1,3 @@
#!rsc by RouterOS
# RouterOS script: accesslist-duplicates.capsman
# Copyright (c) 2018-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# print duplicate antries in wireless access list
# https://git.eworm.de/cgit/routeros-scripts/about/doc/accesslist-duplicates.md
#
# !! Do not edit this file, it is generated from template!
:local 0 "accesslist-duplicates.capsman";
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:global Read;
:local Seen ({});
:local Shown ({});
:foreach AccList in=[ /caps-man/access-list/find where mac-address!="00:00:00:00:00:00" ] do={
:local Mac [ /caps-man/access-list/get $AccList mac-address ];
:foreach SeenMac in=$Seen do={
:if ($SeenMac = $Mac) do={
:local Skip 0;
:foreach ShownMac in=$Shown do={
:if ($ShownMac = $Mac) do={ :set Skip 1; }
}
:if ($Skip = 0) do={
/caps-man/access-list/print where mac-address=$Mac;
:set Shown ($Shown, $Mac);
:put "\nNumeric id to remove, any key to skip!";
:local Remove [ :tonum [ $Read ] ];
:if ([ :typeof $Remove ] = "num") do={
:put ("Removing numeric id " . $Remove . "...\n");
/caps-man/access-list/remove $Remove;
}
}
}
}
:set Seen ($Seen, $Mac);
}
# dummy for migration

View file

@ -0,0 +1,42 @@
#!rsc by RouterOS
# RouterOS script: accesslist-duplicates.capsman
# Copyright (c) 2018-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# print duplicate antries in wireless access list
# https://git.eworm.de/cgit/routeros-scripts/about/doc/accesslist-duplicates.md
#
# !! Do not edit this file, it is generated from template!
:local 0 "accesslist-duplicates.capsman";
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:global Read;
:local Seen ({});
:local Shown ({});
:foreach AccList in=[ /caps-man/access-list/find where mac-address!="00:00:00:00:00:00" ] do={
:local Mac [ /caps-man/access-list/get $AccList mac-address ];
:foreach SeenMac in=$Seen do={
:if ($SeenMac = $Mac) do={
:local Skip 0;
:foreach ShownMac in=$Shown do={
:if ($ShownMac = $Mac) do={ :set Skip 1; }
}
:if ($Skip = 0) do={
/caps-man/access-list/print where mac-address=$Mac;
:set Shown ($Shown, $Mac);
:put "\nNumeric id to remove, any key to skip!";
:local Remove [ :tonum [ $Read ] ];
:if ([ :typeof $Remove ] = "num") do={
:put ("Removing numeric id " . $Remove . "...\n");
/caps-man/access-list/remove $Remove;
}
}
}
}
:set Seen ($Seen, $Mac);
}

View file

@ -1,42 +1,3 @@
#!rsc by RouterOS
# RouterOS script: accesslist-duplicates.local
# Copyright (c) 2018-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# print duplicate antries in wireless access list
# https://git.eworm.de/cgit/routeros-scripts/about/doc/accesslist-duplicates.md
#
# !! Do not edit this file, it is generated from template!
:local 0 "accesslist-duplicates.local";
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:global Read;
:local Seen ({});
:local Shown ({});
:foreach AccList in=[ /interface/wireless/access-list/find where mac-address!="00:00:00:00:00:00" ] do={
:local Mac [ /interface/wireless/access-list/get $AccList mac-address ];
:foreach SeenMac in=$Seen do={
:if ($SeenMac = $Mac) do={
:local Skip 0;
:foreach ShownMac in=$Shown do={
:if ($ShownMac = $Mac) do={ :set Skip 1; }
}
:if ($Skip = 0) do={
/interface/wireless/access-list/print where mac-address=$Mac;
:set Shown ($Shown, $Mac);
:put "\nNumeric id to remove, any key to skip!";
:local Remove [ :tonum [ $Read ] ];
:if ([ :typeof $Remove ] = "num") do={
:put ("Removing numeric id " . $Remove . "...\n");
/interface/wireless/access-list/remove $Remove;
}
}
}
}
:set Seen ($Seen, $Mac);
}
# dummy for migration

View file

@ -0,0 +1,42 @@
#!rsc by RouterOS
# RouterOS script: accesslist-duplicates.local
# Copyright (c) 2018-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# print duplicate antries in wireless access list
# https://git.eworm.de/cgit/routeros-scripts/about/doc/accesslist-duplicates.md
#
# !! Do not edit this file, it is generated from template!
:local 0 "accesslist-duplicates.local";
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:global Read;
:local Seen ({});
:local Shown ({});
:foreach AccList in=[ /interface/wireless/access-list/find where mac-address!="00:00:00:00:00:00" ] do={
:local Mac [ /interface/wireless/access-list/get $AccList mac-address ];
:foreach SeenMac in=$Seen do={
:if ($SeenMac = $Mac) do={
:local Skip 0;
:foreach ShownMac in=$Shown do={
:if ($ShownMac = $Mac) do={ :set Skip 1; }
}
:if ($Skip = 0) do={
/interface/wireless/access-list/print where mac-address=$Mac;
:set Shown ($Shown, $Mac);
:put "\nNumeric id to remove, any key to skip!";
:local Remove [ :tonum [ $Read ] ];
:if ([ :typeof $Remove ] = "num") do={
:put ("Removing numeric id " . $Remove . "...\n");
/interface/wireless/access-list/remove $Remove;
}
}
}
}
:set Seen ($Seen, $Mac);
}

View file

@ -1,58 +1,3 @@
#!rsc by RouterOS
# RouterOS script: backup-cloud
# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# provides: backup-script
#
# upload backup to MikroTik cloud
# https://git.eworm.de/cgit/routeros-scripts/about/doc/backup-cloud.md
:local 0 "backup-cloud";
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:global BackupPassword;
:global BackupRandomDelay;
:global Identity;
:global DeviceInfo;
:global LogPrintExit2;
:global RandomDelay;
:global ScriptFromTerminal;
:global SendNotification2;
:global SymbolForNotification;
:global WaitFullyConnected;
$WaitFullyConnected;
:if ([ $ScriptFromTerminal $0 ] = false && $BackupRandomDelay > 0) do={
$RandomDelay $BackupRandomDelay;
}
:do {
# we are not interested in output, but print is
# required to fetch information from cloud
/system/backup/cloud/print as-value;
:if ([ :len [ /system/backup/cloud/find ] ] > 0) do={
/system/backup/cloud/upload-file action=create-and-upload \
password=$BackupPassword replace=[ get ([ find ]->0) name ];
} else={
/system/backup/cloud/upload-file action=create-and-upload \
password=$BackupPassword;
}
:local Cloud [ /system/backup/cloud/get ([ find ]->0) ];
$SendNotification2 ({ origin=$0; \
subject=([ $SymbolForNotification "floppy-disk,cloud" ] . "Cloud backup"); \
message=("Uploaded backup for " . $Identity . " to cloud.\n\n" . \
[ $DeviceInfo ] . "\n\n" . \
"Name: " . $Cloud->"name" . "\n" . \
"Size: " . $Cloud->"size" . " B (" . ($Cloud->"size" / 1024) . " KiB)\n" . \
"Download key: " . $Cloud->"secret-download-key"); silent=true });
} on-error={
$SendNotification2 ({ origin=$0; \
subject=([ $SymbolForNotification "floppy-disk,warning-sign" ] . "Cloud backup failed"); \
message=("Failed uploading backup for " . $Identity . " to cloud!\n\n" . [ $DeviceInfo ]) });
$LogPrintExit2 error $0 ("Failed uploading backup for " . $Identity . " to cloud!") true;
}
# dummy for migration

58
backup-cloud.rsc Normal file
View file

@ -0,0 +1,58 @@
#!rsc by RouterOS
# RouterOS script: backup-cloud
# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# provides: backup-script
#
# upload backup to MikroTik cloud
# https://git.eworm.de/cgit/routeros-scripts/about/doc/backup-cloud.md
:local 0 "backup-cloud";
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:global BackupPassword;
:global BackupRandomDelay;
:global Identity;
:global DeviceInfo;
:global LogPrintExit2;
:global RandomDelay;
:global ScriptFromTerminal;
:global SendNotification2;
:global SymbolForNotification;
:global WaitFullyConnected;
$WaitFullyConnected;
:if ([ $ScriptFromTerminal $0 ] = false && $BackupRandomDelay > 0) do={
$RandomDelay $BackupRandomDelay;
}
:do {
# we are not interested in output, but print is
# required to fetch information from cloud
/system/backup/cloud/print as-value;
:if ([ :len [ /system/backup/cloud/find ] ] > 0) do={
/system/backup/cloud/upload-file action=create-and-upload \
password=$BackupPassword replace=[ get ([ find ]->0) name ];
} else={
/system/backup/cloud/upload-file action=create-and-upload \
password=$BackupPassword;
}
:local Cloud [ /system/backup/cloud/get ([ find ]->0) ];
$SendNotification2 ({ origin=$0; \
subject=([ $SymbolForNotification "floppy-disk,cloud" ] . "Cloud backup"); \
message=("Uploaded backup for " . $Identity . " to cloud.\n\n" . \
[ $DeviceInfo ] . "\n\n" . \
"Name: " . $Cloud->"name" . "\n" . \
"Size: " . $Cloud->"size" . " B (" . ($Cloud->"size" / 1024) . " KiB)\n" . \
"Download key: " . $Cloud->"secret-download-key"); silent=true });
} on-error={
$SendNotification2 ({ origin=$0; \
subject=([ $SymbolForNotification "floppy-disk,warning-sign" ] . "Cloud backup failed"); \
message=("Failed uploading backup for " . $Identity . " to cloud!\n\n" . [ $DeviceInfo ]) });
$LogPrintExit2 error $0 ("Failed uploading backup for " . $Identity . " to cloud!") true;
}

View file

@ -1,107 +1,3 @@
#!rsc by RouterOS
# RouterOS script: backup-email
# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# provides: backup-script
#
# create and email backup and config file
# https://git.eworm.de/cgit/routeros-scripts/about/doc/backup-email.md
:local 0 "backup-email";
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:global BackupPassword;
:global BackupRandomDelay;
:global BackupSendBinary;
:global BackupSendExport;
:global BackupSendGlobalConfig;
:global Domain;
:global Identity;
:global CharacterReplace;
:global DeviceInfo;
:global LogPrintExit2;
:global MkDir;
:global RandomDelay;
:global ScriptFromTerminal;
:global SendEMail2;
:global SymbolForNotification;
:global WaitForFile;
:global WaitFullyConnected;
:if ([ :typeof $SendEMail2 ] = "nothing") do={
$LogPrintExit2 error $0 ("The module for sending notifications via e-mail is not installed.") true;
}
:if ($BackupSendBinary != true && \
$BackupSendExport != true) do={
$LogPrintExit2 error $0 ("Configured to send neither backup nor config export.") true;
}
$WaitFullyConnected;
:if ([ $ScriptFromTerminal $0 ] = false && $BackupRandomDelay > 0) do={
$RandomDelay $BackupRandomDelay;
}
# filename based on identity
:local DirName ("tmpfs/" . $0);
:local FileName [ $CharacterReplace ($Identity . "." . $Domain) "." "_" ];
:local FilePath ($DirName . "/" . $FileName);
:local BackupFile "none";
:local ExportFile "none";
:local ConfigFile "none";
:local Attach ({});
:if ([ $MkDir $DirName ] = false) do={
$LogPrintExit2 error $0 ("Failed creating directory!") true;
}
# binary backup
:if ($BackupSendBinary = true) do={
/system/backup/save encryption=aes-sha256 name=$FilePath password=$BackupPassword;
$WaitForFile ($FilePath . ".backup");
:set BackupFile ($FileName . ".backup");
:set Attach ($Attach, ($FilePath . ".backup"));
}
# create configuration export
:if ($BackupSendExport = true) do={
/export terse show-sensitive file=$FilePath;
$WaitForFile ($FilePath . ".rsc");
:set ExportFile ($FileName . ".rsc");
:set Attach ($Attach, ($FilePath . ".rsc"));
}
# global-config-overlay
:if ($BackupSendGlobalConfig = true) do={
:execute script={ :put [ /system/script/get global-config-overlay source ]; } \
file=($FilePath . ".conf");
$WaitForFile ($FilePath . ".conf.txt");
:set ConfigFile ($FileName . ".conf.txt");
:set Attach ($Attach, ($FilePath . ".conf.txt"));
}
# send email with status and files
$SendEMail2 ({ origin=$0; \
subject=([ $SymbolForNotification "floppy-disk,incoming-envelope" ] . \
"Backup & Config"); \
message=("See attached files for backup and config export for " . \
$Identity . ".\n\n" . \
[ $DeviceInfo ] . "\n\n" . \
"Backup file: " . $BackupFile . "\n" . \
"Export file: " . $ExportFile . "\n" . \
"Config file: " . $ConfigFile); \
attach=$Attach; remove-attach=true });
# wait for the mail to be sent
:local I 0;
:while ([ :len [ /file/find where name ~ ($FilePath . "\\.(backup|rsc)\$") ] ] > 0) do={
:if ($I >= 120) do={
$LogPrintExit2 warning $0 ("Files are still available, sending e-mail failed.") true;
}
:delay 1s;
:set I ($I + 1);
}
# dummy for migration

107
backup-email.rsc Normal file
View file

@ -0,0 +1,107 @@
#!rsc by RouterOS
# RouterOS script: backup-email
# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# provides: backup-script
#
# create and email backup and config file
# https://git.eworm.de/cgit/routeros-scripts/about/doc/backup-email.md
:local 0 "backup-email";
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:global BackupPassword;
:global BackupRandomDelay;
:global BackupSendBinary;
:global BackupSendExport;
:global BackupSendGlobalConfig;
:global Domain;
:global Identity;
:global CharacterReplace;
:global DeviceInfo;
:global LogPrintExit2;
:global MkDir;
:global RandomDelay;
:global ScriptFromTerminal;
:global SendEMail2;
:global SymbolForNotification;
:global WaitForFile;
:global WaitFullyConnected;
:if ([ :typeof $SendEMail2 ] = "nothing") do={
$LogPrintExit2 error $0 ("The module for sending notifications via e-mail is not installed.") true;
}
:if ($BackupSendBinary != true && \
$BackupSendExport != true) do={
$LogPrintExit2 error $0 ("Configured to send neither backup nor config export.") true;
}
$WaitFullyConnected;
:if ([ $ScriptFromTerminal $0 ] = false && $BackupRandomDelay > 0) do={
$RandomDelay $BackupRandomDelay;
}
# filename based on identity
:local DirName ("tmpfs/" . $0);
:local FileName [ $CharacterReplace ($Identity . "." . $Domain) "." "_" ];
:local FilePath ($DirName . "/" . $FileName);
:local BackupFile "none";
:local ExportFile "none";
:local ConfigFile "none";
:local Attach ({});
:if ([ $MkDir $DirName ] = false) do={
$LogPrintExit2 error $0 ("Failed creating directory!") true;
}
# binary backup
:if ($BackupSendBinary = true) do={
/system/backup/save encryption=aes-sha256 name=$FilePath password=$BackupPassword;
$WaitForFile ($FilePath . ".backup");
:set BackupFile ($FileName . ".backup");
:set Attach ($Attach, ($FilePath . ".backup"));
}
# create configuration export
:if ($BackupSendExport = true) do={
/export terse show-sensitive file=$FilePath;
$WaitForFile ($FilePath . ".rsc");
:set ExportFile ($FileName . ".rsc");
:set Attach ($Attach, ($FilePath . ".rsc"));
}
# global-config-overlay
:if ($BackupSendGlobalConfig = true) do={
:execute script={ :put [ /system/script/get global-config-overlay source ]; } \
file=($FilePath . ".conf");
$WaitForFile ($FilePath . ".conf.txt");
:set ConfigFile ($FileName . ".conf.txt");
:set Attach ($Attach, ($FilePath . ".conf.txt"));
}
# send email with status and files
$SendEMail2 ({ origin=$0; \
subject=([ $SymbolForNotification "floppy-disk,incoming-envelope" ] . \
"Backup & Config"); \
message=("See attached files for backup and config export for " . \
$Identity . ".\n\n" . \
[ $DeviceInfo ] . "\n\n" . \
"Backup file: " . $BackupFile . "\n" . \
"Export file: " . $ExportFile . "\n" . \
"Config file: " . $ConfigFile); \
attach=$Attach; remove-attach=true });
# wait for the mail to be sent
:local I 0;
:while ([ :len [ /file/find where name ~ ($FilePath . "\\.(backup|rsc)\$") ] ] > 0) do={
:if ($I >= 120) do={
$LogPrintExit2 warning $0 ("Files are still available, sending e-mail failed.") true;
}
:delay 1s;
:set I ($I + 1);
}

View file

@ -1,36 +1,3 @@
#!rsc by RouterOS
# RouterOS script: backup-partition
# Copyright (c) 2022-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# provides: backup-script
#
# save configuration to fallback partition
# https://git.eworm.de/cgit/routeros-scripts/about/doc/backup-partition.md
:local 0 "backup-partition";
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:global LogPrintExit2;
:if ([ :len [ /partitions/find ] ] < 2) do={
$LogPrintExit2 error $0 ("Device does not have a fallback partition.") true;
}
:local ActiveRunning [ /partitions/find where active running ];
:if ([ :len $ActiveRunning ] < 1) do={
$LogPrintExit2 error $0 ("Device is not running from active partition.") true;
}
:local ActiveRunningVar [ /partitions/get $ActiveRunning ];
:do {
/partitions/save-config-to ($ActiveRunningVar->"fallback-to");
$LogPrintExit2 info $0 ("Saved configuration to partition '" . \
($ActiveRunningVar->"fallback-to") . "'.") false;
} on-error={
$LogPrintExit2 error $0 ("Failed saving configuration to partition '" . \
($ActiveRunningVar->"fallback-to") . "'!") true;
}
# dummy for migration

36
backup-partition.rsc Normal file
View file

@ -0,0 +1,36 @@
#!rsc by RouterOS
# RouterOS script: backup-partition
# Copyright (c) 2022-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# provides: backup-script
#
# save configuration to fallback partition
# https://git.eworm.de/cgit/routeros-scripts/about/doc/backup-partition.md
:local 0 "backup-partition";
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:global LogPrintExit2;
:if ([ :len [ /partitions/find ] ] < 2) do={
$LogPrintExit2 error $0 ("Device does not have a fallback partition.") true;
}
:local ActiveRunning [ /partitions/find where active running ];
:if ([ :len $ActiveRunning ] < 1) do={
$LogPrintExit2 error $0 ("Device is not running from active partition.") true;
}
:local ActiveRunningVar [ /partitions/get $ActiveRunning ];
:do {
/partitions/save-config-to ($ActiveRunningVar->"fallback-to");
$LogPrintExit2 info $0 ("Saved configuration to partition '" . \
($ActiveRunningVar->"fallback-to") . "'.") false;
} on-error={
$LogPrintExit2 error $0 ("Failed saving configuration to partition '" . \
($ActiveRunningVar->"fallback-to") . "'!") true;
}

View file

@ -1,129 +1,3 @@
#!rsc by RouterOS
# RouterOS script: backup-upload
# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# provides: backup-script
#
# create and upload backup and config file
# https://git.eworm.de/cgit/routeros-scripts/about/doc/backup-upload.md
:local 0 "backup-upload";
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:global BackupPassword;
:global BackupRandomDelay;
:global BackupSendBinary;
:global BackupSendExport;
:global BackupSendGlobalConfig;
:global BackupUploadPass;
:global BackupUploadUrl;
:global BackupUploadUser;
:global Domain;
:global Identity;
:global CharacterReplace;
:global DeviceInfo;
:global IfThenElse;
:global LogPrintExit2;
:global MkDir;
:global RandomDelay;
:global ScriptFromTerminal;
:global SendNotification2;
:global SymbolForNotification;
:global WaitForFile;
:global WaitFullyConnected;
:if ($BackupSendBinary != true && \
$BackupSendExport != true) do={
$LogPrintExit2 error $0 ("Configured to send neither backup nor config export.") true;
}
$WaitFullyConnected;
:if ([ $ScriptFromTerminal $0 ] = false && $BackupRandomDelay > 0) do={
$RandomDelay $BackupRandomDelay;
}
# filename based on identity
:local DirName ("tmpfs/" . $0);
:local FileName [ $CharacterReplace ($Identity . "." . $Domain) "." "_" ];
:local FilePath ($DirName . "/" . $FileName);
:local BackupFile "none";
:local ExportFile "none";
:local ConfigFile "none";
:local Failed 0;
:if ([ $MkDir $DirName ] = false) do={
$LogPrintExit2 error $0 ("Failed creating directory!") true;
}
# binary backup
:if ($BackupSendBinary = true) do={
/system/backup/save encryption=aes-sha256 name=$FilePath password=$BackupPassword;
$WaitForFile ($FilePath . ".backup");
:do {
/tool/fetch upload=yes url=($BackupUploadUrl . "/" . $FileName . ".backup") \
user=$BackupUploadUser password=$BackupUploadPass src-path=($FilePath . ".backup");
:set BackupFile ($FileName . ".backup");
} on-error={
$LogPrintExit2 error $0 ("Uploading backup file failed!") false;
:set BackupFile "failed";
:set Failed 1;
}
/file/remove ($FilePath . ".backup");
}
# create configuration export
:if ($BackupSendExport = true) do={
/export terse show-sensitive file=$FilePath;
$WaitForFile ($FilePath . ".rsc");
:do {
/tool/fetch upload=yes url=($BackupUploadUrl . "/" . $FileName . ".rsc") \
user=$BackupUploadUser password=$BackupUploadPass src-path=($FilePath . ".rsc");
:set ExportFile ($FileName . ".rsc");
} on-error={
$LogPrintExit2 error $0 ("Uploading configuration export failed!") false;
:set ExportFile "failed";
:set Failed 1;
}
/file/remove ($FilePath . ".rsc");
}
# global-config-overlay
:if ($BackupSendGlobalConfig = true) do={
:execute script={ :put [ /system/script/get global-config-overlay source ]; } \
file=($FilePath . ".conf");
$WaitForFile ($FilePath . ".conf.txt");
:do {
/tool/fetch upload=yes url=($BackupUploadUrl . "/" . $FileName . ".conf") \
user=$BackupUploadUser password=$BackupUploadPass src-path=($FilePath . ".conf.txt");
:set ConfigFile ($FileName . ".conf");
} on-error={
$LogPrintExit2 error $0 ("Uploading global-config-overlay failed!") false;
:set ConfigFile "failed";
:set Failed 1;
}
/file/remove ($FilePath . ".conf.txt");
}
$SendNotification2 ({ origin=$0; \
subject=[ $IfThenElse ($Failed > 0) \
([ $SymbolForNotification "floppy-disk,warning-sign" ] . "Backup & Config upload with failure") \
([ $SymbolForNotification "floppy-disk,up-arrow" ] . "Backup & Config upload") ]; \
message=("Backup and config export upload for " . $Identity . ".\n\n" . \
[ $DeviceInfo ] . "\n\n" . \
"Backup file: " . $BackupFile . "\n" . \
"Export file: " . $ExportFile . "\n" . \
"Config file: " . $ConfigFile); silent=true });
:if ($Failed = 1) do={
:error "An error occured!";
}
# dummy for migration

129
backup-upload.rsc Normal file
View file

@ -0,0 +1,129 @@
#!rsc by RouterOS
# RouterOS script: backup-upload
# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# provides: backup-script
#
# create and upload backup and config file
# https://git.eworm.de/cgit/routeros-scripts/about/doc/backup-upload.md
:local 0 "backup-upload";
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:global BackupPassword;
:global BackupRandomDelay;
:global BackupSendBinary;
:global BackupSendExport;
:global BackupSendGlobalConfig;
:global BackupUploadPass;
:global BackupUploadUrl;
:global BackupUploadUser;
:global Domain;
:global Identity;
:global CharacterReplace;
:global DeviceInfo;
:global IfThenElse;
:global LogPrintExit2;
:global MkDir;
:global RandomDelay;
:global ScriptFromTerminal;
:global SendNotification2;
:global SymbolForNotification;
:global WaitForFile;
:global WaitFullyConnected;
:if ($BackupSendBinary != true && \
$BackupSendExport != true) do={
$LogPrintExit2 error $0 ("Configured to send neither backup nor config export.") true;
}
$WaitFullyConnected;
:if ([ $ScriptFromTerminal $0 ] = false && $BackupRandomDelay > 0) do={
$RandomDelay $BackupRandomDelay;
}
# filename based on identity
:local DirName ("tmpfs/" . $0);
:local FileName [ $CharacterReplace ($Identity . "." . $Domain) "." "_" ];
:local FilePath ($DirName . "/" . $FileName);
:local BackupFile "none";
:local ExportFile "none";
:local ConfigFile "none";
:local Failed 0;
:if ([ $MkDir $DirName ] = false) do={
$LogPrintExit2 error $0 ("Failed creating directory!") true;
}
# binary backup
:if ($BackupSendBinary = true) do={
/system/backup/save encryption=aes-sha256 name=$FilePath password=$BackupPassword;
$WaitForFile ($FilePath . ".backup");
:do {
/tool/fetch upload=yes url=($BackupUploadUrl . "/" . $FileName . ".backup") \
user=$BackupUploadUser password=$BackupUploadPass src-path=($FilePath . ".backup");
:set BackupFile ($FileName . ".backup");
} on-error={
$LogPrintExit2 error $0 ("Uploading backup file failed!") false;
:set BackupFile "failed";
:set Failed 1;
}
/file/remove ($FilePath . ".backup");
}
# create configuration export
:if ($BackupSendExport = true) do={
/export terse show-sensitive file=$FilePath;
$WaitForFile ($FilePath . ".rsc");
:do {
/tool/fetch upload=yes url=($BackupUploadUrl . "/" . $FileName . ".rsc") \
user=$BackupUploadUser password=$BackupUploadPass src-path=($FilePath . ".rsc");
:set ExportFile ($FileName . ".rsc");
} on-error={
$LogPrintExit2 error $0 ("Uploading configuration export failed!") false;
:set ExportFile "failed";
:set Failed 1;
}
/file/remove ($FilePath . ".rsc");
}
# global-config-overlay
:if ($BackupSendGlobalConfig = true) do={
:execute script={ :put [ /system/script/get global-config-overlay source ]; } \
file=($FilePath . ".conf");
$WaitForFile ($FilePath . ".conf.txt");
:do {
/tool/fetch upload=yes url=($BackupUploadUrl . "/" . $FileName . ".conf") \
user=$BackupUploadUser password=$BackupUploadPass src-path=($FilePath . ".conf.txt");
:set ConfigFile ($FileName . ".conf");
} on-error={
$LogPrintExit2 error $0 ("Uploading global-config-overlay failed!") false;
:set ConfigFile "failed";
:set Failed 1;
}
/file/remove ($FilePath . ".conf.txt");
}
$SendNotification2 ({ origin=$0; \
subject=[ $IfThenElse ($Failed > 0) \
([ $SymbolForNotification "floppy-disk,warning-sign" ] . "Backup & Config upload with failure") \
([ $SymbolForNotification "floppy-disk,up-arrow" ] . "Backup & Config upload") ]; \
message=("Backup and config export upload for " . $Identity . ".\n\n" . \
[ $DeviceInfo ] . "\n\n" . \
"Backup file: " . $BackupFile . "\n" . \
"Export file: " . $ExportFile . "\n" . \
"Config file: " . $ConfigFile); silent=true });
:if ($Failed = 1) do={
:error "An error occured!";
}

View file

@ -1,86 +1,3 @@
#!rsc by RouterOS
# RouterOS script: capsman-download-packages
# Copyright (c) 2018-2023 Christian Hesse <mail@eworm.de>
# Michael Gisbers <michael@gisbers.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# download and cleanup packages for CAP installation from CAPsMAN
# https://git.eworm.de/cgit/routeros-scripts/about/doc/capsman-download-packages.md
:local 0 "capsman-download-packages";
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:global CleanFilePath;
:global DownloadPackage;
:global LogPrintExit2;
:global MkDir;
:global ScriptLock;
:global WaitFullyConnected;
$ScriptLock $0;
$WaitFullyConnected;
:local PackagePath [ $CleanFilePath [ /caps-man/manager/get package-path ] ];
:local InstalledVersion [ /system/package/update/get installed-version ];
:local Updated false;
:if ([ :len $PackagePath ] = 0) do={
$LogPrintExit2 warning $0 ("The CAPsMAN package path is not defined, can not download packages.") true;
}
:if ([ :len [ /file/find where name=$PackagePath type="directory" ] ] = 0) do={
:if ([ $MkDir $PackagePath ] = false) do={
$LogPrintExit2 warning $0 ("Creating directory at CAPsMAN package path (" . \
$PackagePath . ") failed!") true;
}
$LogPrintExit2 info $0 ("Created directory at CAPsMAN package path (" . $PackagePath . \
"). Please place your packages!") false;
}
:foreach Package in=[ /file/find where type=package \
package-version!=$InstalledVersion name~("^" . $PackagePath) ] do={
:local File [ /file/get $Package ];
:if ($File->"package-architecture" = "mips") do={
:set ($File->"package-architecture") "mipsbe";
}
:if ([ $DownloadPackage ($File->"package-name") $InstalledVersion \
($File->"package-architecture") $PackagePath ] = true) do={
:set Updated true;
/file/remove $Package;
}
}
:if ([ :len [ /system/logging/find where topics~"error" !(topics~"!error") \
!(topics~"!caps") action=memory !disabled !invalid ] ] < 1) do={
$LogPrintExit2 warning $0 ("Looks like error messages for 'caps' are not sent to memory. " . \
"Probably can not download packages automatically.") false;
} else={
:if ($Updated = false && [ /system/resource/get uptime ] < 2m) do={
$LogPrintExit2 info $0 ("No packages downloaded, yet. Delaying for logs.") false;
:delay 2m;
}
}
:foreach Log in=[ /log/find where topics=({"caps"; "error"}) \
message~("upgrade status: failed, failed to download file '.*-" . $InstalledVersion . \
"-.*\\.npk', no such file") ] do={
:local Message [ /log/get $Log message ];
:local Package [ :pick $Message \
([ :find $Message "'" ] + 1) \
[ :find $Message ("-" . $InstalledVersion . "-") ] ];
:local Arch [ :pick $Message \
([ :find $Message ("-" . $InstalledVersion . "-") ] + 2 + [ :len $InstalledVersion ]) \
[ :find $Message ".npk" ] ];
:if ([ $DownloadPackage $Package $InstalledVersion $Arch $PackagePath ] = true) do={
:set Updated true;
}
}
:if ($Updated = true) do={
:if ([ :len [ /system/script/find where name="capsman-rolling-upgrade" ] ] > 0) do={
/system/script/run capsman-rolling-upgrade;
} else={
/caps-man/remote-cap/upgrade [ find where version!=$InstalledVersion ];
}
}
# dummy for migration

View file

@ -0,0 +1,86 @@
#!rsc by RouterOS
# RouterOS script: capsman-download-packages
# Copyright (c) 2018-2023 Christian Hesse <mail@eworm.de>
# Michael Gisbers <michael@gisbers.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# download and cleanup packages for CAP installation from CAPsMAN
# https://git.eworm.de/cgit/routeros-scripts/about/doc/capsman-download-packages.md
:local 0 "capsman-download-packages";
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:global CleanFilePath;
:global DownloadPackage;
:global LogPrintExit2;
:global MkDir;
:global ScriptLock;
:global WaitFullyConnected;
$ScriptLock $0;
$WaitFullyConnected;
:local PackagePath [ $CleanFilePath [ /caps-man/manager/get package-path ] ];
:local InstalledVersion [ /system/package/update/get installed-version ];
:local Updated false;
:if ([ :len $PackagePath ] = 0) do={
$LogPrintExit2 warning $0 ("The CAPsMAN package path is not defined, can not download packages.") true;
}
:if ([ :len [ /file/find where name=$PackagePath type="directory" ] ] = 0) do={
:if ([ $MkDir $PackagePath ] = false) do={
$LogPrintExit2 warning $0 ("Creating directory at CAPsMAN package path (" . \
$PackagePath . ") failed!") true;
}
$LogPrintExit2 info $0 ("Created directory at CAPsMAN package path (" . $PackagePath . \
"). Please place your packages!") false;
}
:foreach Package in=[ /file/find where type=package \
package-version!=$InstalledVersion name~("^" . $PackagePath) ] do={
:local File [ /file/get $Package ];
:if ($File->"package-architecture" = "mips") do={
:set ($File->"package-architecture") "mipsbe";
}
:if ([ $DownloadPackage ($File->"package-name") $InstalledVersion \
($File->"package-architecture") $PackagePath ] = true) do={
:set Updated true;
/file/remove $Package;
}
}
:if ([ :len [ /system/logging/find where topics~"error" !(topics~"!error") \
!(topics~"!caps") action=memory !disabled !invalid ] ] < 1) do={
$LogPrintExit2 warning $0 ("Looks like error messages for 'caps' are not sent to memory. " . \
"Probably can not download packages automatically.") false;
} else={
:if ($Updated = false && [ /system/resource/get uptime ] < 2m) do={
$LogPrintExit2 info $0 ("No packages downloaded, yet. Delaying for logs.") false;
:delay 2m;
}
}
:foreach Log in=[ /log/find where topics=({"caps"; "error"}) \
message~("upgrade status: failed, failed to download file '.*-" . $InstalledVersion . \
"-.*\\.npk', no such file") ] do={
:local Message [ /log/get $Log message ];
:local Package [ :pick $Message \
([ :find $Message "'" ] + 1) \
[ :find $Message ("-" . $InstalledVersion . "-") ] ];
:local Arch [ :pick $Message \
([ :find $Message ("-" . $InstalledVersion . "-") ] + 2 + [ :len $InstalledVersion ]) \
[ :find $Message ".npk" ] ];
:if ([ $DownloadPackage $Package $InstalledVersion $Arch $PackagePath ] = true) do={
:set Updated true;
}
}
:if ($Updated = true) do={
:if ([ :len [ /system/script/find where name="capsman-rolling-upgrade" ] ] > 0) do={
/system/script/run capsman-rolling-upgrade;
} else={
/caps-man/remote-cap/upgrade [ find where version!=$InstalledVersion ];
}
}

View file

@ -1,36 +1,3 @@
#!rsc by RouterOS
# RouterOS script: capsman-rolling-upgrade
# Copyright (c) 2018-2023 Christian Hesse <mail@eworm.de>
# Michael Gisbers <michael@gisbers.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# upgrade CAPs one after another
# https://git.eworm.de/cgit/routeros-scripts/about/doc/capsman-rolling-upgrade.md
:local 0 "capsman-rolling-upgrade";
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:global LogPrintExit2;
:global ScriptLock;
$ScriptLock $0;
:local InstalledVersion [ /system/package/update/get installed-version ];
:local RemoteCapCount [ :len [ /caps-man/remote-cap/find ] ];
:if ($RemoteCapCount > 0) do={
:local Delay (600 / $RemoteCapCount);
:if ($Delay > 120) do={ :set Delay 120; }
:foreach RemoteCap in=[ /caps-man/remote-cap/find where version!=$InstalledVersion ] do={
:local RemoteCapVal [ /caps-man/remote-cap/get $RemoteCap ];
:if ([ :len $RemoteCapVal ] > 1) do={
$LogPrintExit2 info $0 ("Starting upgrade for " . $RemoteCapVal->"name" . \
" (" . $RemoteCapVal->"identity" . ")...") false;
/caps-man/remote-cap/upgrade $RemoteCap;
} else={
$LogPrintExit2 warning $0 ("Remote CAP vanished, skipping upgrade.") false;
}
:delay ($Delay . "s");
}
}
# dummy for migration

View file

@ -0,0 +1,36 @@
#!rsc by RouterOS
# RouterOS script: capsman-rolling-upgrade
# Copyright (c) 2018-2023 Christian Hesse <mail@eworm.de>
# Michael Gisbers <michael@gisbers.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# upgrade CAPs one after another
# https://git.eworm.de/cgit/routeros-scripts/about/doc/capsman-rolling-upgrade.md
:local 0 "capsman-rolling-upgrade";
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:global LogPrintExit2;
:global ScriptLock;
$ScriptLock $0;
:local InstalledVersion [ /system/package/update/get installed-version ];
:local RemoteCapCount [ :len [ /caps-man/remote-cap/find ] ];
:if ($RemoteCapCount > 0) do={
:local Delay (600 / $RemoteCapCount);
:if ($Delay > 120) do={ :set Delay 120; }
:foreach RemoteCap in=[ /caps-man/remote-cap/find where version!=$InstalledVersion ] do={
:local RemoteCapVal [ /caps-man/remote-cap/get $RemoteCap ];
:if ([ :len $RemoteCapVal ] > 1) do={
$LogPrintExit2 info $0 ("Starting upgrade for " . $RemoteCapVal->"name" . \
" (" . $RemoteCapVal->"identity" . ")...") false;
/caps-man/remote-cap/upgrade $RemoteCap;
} else={
$LogPrintExit2 warning $0 ("Remote CAP vanished, skipping upgrade.") false;
}
:delay ($Delay . "s");
}
}

View file

@ -1,38 +1,3 @@
#!rsc by RouterOS
# RouterOS script: certificate-renew-issued
# Copyright (c) 2019-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# renew locally issued certificates
# https://git.eworm.de/cgit/routeros-scripts/about/doc/certificate-renew-issued.md
:local 0 "certificate-renew-issued";
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:global CertIssuedExportPass;
:global LogPrintExit2;
:global MkDir;
:foreach Cert in=[ /certificate/find where issued expires-after<3w ] do={
:local CertVal [ /certificate/get $Cert ];
/certificate/issued-revoke $Cert;
/certificate/set name=($CertVal->"name" . "-revoked-" . [ /system/clock/get date ]) $Cert;
/certificate/add name=($CertVal->"name") common-name=($CertVal->"common-name") \
key-usage=($CertVal->"key-usage") subject-alt-name=($CertVal->"subject-alt-name");
/certificate/sign ($CertVal->"name") ca=($CertVal->"ca");
:if ([ :typeof ($CertIssuedExportPass->($CertVal->"common-name")) ] = "str") do={
:if ([ $MkDir "cert-issued" ] = true) do={
/certificate/export-certificate ($CertVal->"name") type=pkcs12 \
file-name=("cert-issued/" . $CertVal->"common-name") \
export-passphrase=($CertIssuedExportPass->($CertVal->"common-name"));
$LogPrintExit2 info $0 ("Issued a new certificate for \"" . $CertVal->"common-name" . \
"\", exported to \"cert-issued/" . $CertVal->"common-name" . ".p12\".") false;
} else={
$LogPrintExit2 warning $0 ("Failed creating directory, not exporting certificate.") false;
}
} else={
$LogPrintExit2 info $0 ("Issued a new certificate for \"" . $CertVal->"common-name" . "\".") false;
}
}
# dummy for migration

View file

@ -0,0 +1,38 @@
#!rsc by RouterOS
# RouterOS script: certificate-renew-issued
# Copyright (c) 2019-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# renew locally issued certificates
# https://git.eworm.de/cgit/routeros-scripts/about/doc/certificate-renew-issued.md
:local 0 "certificate-renew-issued";
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:global CertIssuedExportPass;
:global LogPrintExit2;
:global MkDir;
:foreach Cert in=[ /certificate/find where issued expires-after<3w ] do={
:local CertVal [ /certificate/get $Cert ];
/certificate/issued-revoke $Cert;
/certificate/set name=($CertVal->"name" . "-revoked-" . [ /system/clock/get date ]) $Cert;
/certificate/add name=($CertVal->"name") common-name=($CertVal->"common-name") \
key-usage=($CertVal->"key-usage") subject-alt-name=($CertVal->"subject-alt-name");
/certificate/sign ($CertVal->"name") ca=($CertVal->"ca");
:if ([ :typeof ($CertIssuedExportPass->($CertVal->"common-name")) ] = "str") do={
:if ([ $MkDir "cert-issued" ] = true) do={
/certificate/export-certificate ($CertVal->"name") type=pkcs12 \
file-name=("cert-issued/" . $CertVal->"common-name") \
export-passphrase=($CertIssuedExportPass->($CertVal->"common-name"));
$LogPrintExit2 info $0 ("Issued a new certificate for \"" . $CertVal->"common-name" . \
"\", exported to \"cert-issued/" . $CertVal->"common-name" . ".p12\".") false;
} else={
$LogPrintExit2 warning $0 ("Failed creating directory, not exporting certificate.") false;
}
} else={
$LogPrintExit2 info $0 ("Issued a new certificate for \"" . $CertVal->"common-name" . "\".") false;
}
}

View file

@ -1,138 +1,3 @@
#!rsc by RouterOS
# RouterOS script: check-certificates
# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# check for certificate validity
# https://git.eworm.de/cgit/routeros-scripts/about/doc/check-certificates.md
:local 0 "check-certificates";
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:global CertRenewPass;
:global CertRenewTime;
:global CertRenewUrl;
:global CertWarnTime;
:global Identity;
:global CertificateAvailable
:global CertificateNameByCN;
:global IfThenElse;
:global LogPrintExit2;
:global ParseKeyValueStore;
:global SendNotification2;
:global SymbolForNotification;
:global UrlEncode;
:global WaitForFile;
:global WaitFullyConnected;
:local FormatExpire do={
:global CharacterReplace;
:return [ $CharacterReplace [ $CharacterReplace [ :tostr $1 ] "w" "w " ] "d" "d " ];
}
$WaitFullyConnected;
:foreach Cert in=[ /certificate/find where !revoked !ca !scep-url expires-after<$CertRenewTime ] do={
:local CertVal [ /certificate/get $Cert ];
:do {
:if ([ :len $CertRenewUrl ] = 0) do={
$LogPrintExit2 info $0 ("No CertRenewUrl given.") true;
}
$LogPrintExit2 info $0 ("Attempting to renew certificate " . ($CertVal->"name") . ".") false;
:foreach Type in={ ".pem"; ".p12" } do={
:local CertFileName ([ $UrlEncode ($CertVal->"common-name") ] . $Type);
:do {
/tool/fetch check-certificate=yes-without-crl \
($CertRenewUrl . $CertFileName) dst-path=$CertFileName as-value;
$WaitForFile $CertFileName;
:local DecryptionFailed true;
:foreach PassPhrase in=$CertRenewPass do={
:local Result [ /certificate/import file-name=$CertFileName passphrase=$PassPhrase as-value ];
:if ($Result->"decryption-failures" = 0) do={
:set DecryptionFailed false;
}
}
/file/remove [ find where name=$CertFileName ];
:if ($DecryptionFailed = true) do={
$LogPrintExit2 warning $0 ("Decryption failed for certificate file " . $CertFileName) false;
}
:foreach CertInChain in=[ /certificate/find where name~("^" . $CertFileName . "_[0-9]+\$") common-name!=($CertVal->"common-name") ] do={
$CertificateNameByCN [ /certificate/get $CertInChain common-name ];
}
} on-error={
$LogPrintExit2 debug $0 ("Could not download certificate file " . $CertFileName) false;
}
}
:local CertNew [ /certificate/find where common-name=($CertVal->"common-name") fingerprint!=[ :tostr ($CertVal->"fingerprint") ] expires-after>$CertRenewTime ];
:local CertNewVal [ /certificate/get $CertNew ];
:if ([ $CertificateAvailable ([ $ParseKeyValueStore ($CertNewVal->"issuer") ]->"CN") ] = false) do={
$LogPrintExit2 warning $0 ("The certificate chain is not available!") false;
}
:if ($Cert != $CertNew) do={
$LogPrintExit2 debug $0 ("Certificate '" . $CertVal->"name" . "' was not updated, but replaced.") false;
:if (($CertVal->"private-key") = true && ($CertVal->"private-key") != ($CertNewVal->"private-key")) do={
/certificate/remove $CertNew;
$LogPrintExit2 warning $0 ("Old certificate '" . ($CertVal->"name") . "' has a private key, new certificate does not. Aborting renew.") true;
}
/ip/service/set certificate=($CertNewVal->"name") [ find where certificate=($CertVal->"name") ];
/ip/ipsec/identity/set certificate=($CertNewVal->"name") [ find where certificate=($CertVal->"name") ];
/ip/ipsec/identity/set remote-certificate=($CertNewVal->"name") [ find where remote-certificate=($CertVal->"name") ];
/ip/hotspot/profile/set ssl-certificate=($CertNewVal->"name") [ find where ssl-certificate=($CertVal->"name") ];
/certificate/remove $Cert;
/certificate/set $CertNew name=($CertVal->"name");
}
$SendNotification2 ({ origin=$0; \
subject=([ $SymbolForNotification "lock-with-ink-pen" ] . "Certificate renewed"); \
message=("A certificate on " . $Identity . " has been renewed.\n\n" . \
"Name: " . ($CertVal->"name") . "\n" . \
"CommonName: " . ($CertNewVal->"common-name") . "\n" . \
"Private key: " . [ $IfThenElse (($CertNewVal->"private-key") = true) "available" "missing" ] . "\n" . \
"Fingerprint: " . ($CertNewVal->"fingerprint") . "\n" . \
"Issuer: " . ([ $ParseKeyValueStore ($CertNewVal->"issuer") ]->"CN") . "\n" . \
"Validity: " . ($CertNewVal->"invalid-before") . " to " . ($CertNewVal->"invalid-after") . "\n" . \
"Expires in: " . [ $FormatExpire ($CertNewVal->"expires-after") ]); silent=true });
$LogPrintExit2 info $0 ("The certificate " . ($CertVal->"name") . " has been renewed.") false;
} on-error={
$LogPrintExit2 debug $0 ("Could not renew certificate " . ($CertVal->"name") . ".") false;
}
}
:foreach Cert in=[ /certificate/find where !revoked !scep-url !(expires-after=[]) \
expires-after<$CertWarnTime !(fingerprint=[]) ] do={
:local CertVal [ /certificate/get $Cert ];
:if ([ :len [ /certificate/scep-server/find where ca-cert=($CertVal->"ca") ] ] > 0) do={
$LogPrintExit2 debug $0 ("Certificate \"" . ($CertVal->"name") . "\" is handled by SCEP, skipping.") false;
} else={
:local State [ $IfThenElse (($CertVal->"expired") = true) "expired" "is about to expire" ];
$SendNotification2 ({ origin=$0; \
subject=([ $SymbolForNotification "warning-sign" ] . "Certificate warning!"); \
message=("A certificate on " . $Identity . " " . $State . ".\n\n" . \
"Name: " . ($CertVal->"name") . "\n" . \
"CommonName: " . ($CertVal->"common-name") . "\n" . \
"Private key: " . [ $IfThenElse (($CertVal->"private-key") = true) "available" "missing" ] . "\n" . \
"Fingerprint: " . ($CertVal->"fingerprint") . "\n" . \
"Issuer: " . ($CertVal->"ca") . ([ $ParseKeyValueStore ($CertVal->"issuer") ]->"CN") . "\n" . \
"Validity: " . ($CertVal->"invalid-before") . " to " . ($CertVal->"invalid-after") . "\n" . \
"Expires in: " . [ $IfThenElse (($CertVal->"expired") = true) "expired" [ $FormatExpire ($CertVal->"expires-after") ] ]) });
$LogPrintExit2 info $0 ("The certificate " . ($CertVal->"name") . " " . $State . \
", it is invalid after " . ($CertVal->"invalid-after") . ".") false;
}
}
# dummy for migration

138
check-certificates.rsc Normal file
View file

@ -0,0 +1,138 @@
#!rsc by RouterOS
# RouterOS script: check-certificates
# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# check for certificate validity
# https://git.eworm.de/cgit/routeros-scripts/about/doc/check-certificates.md
:local 0 "check-certificates";
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:global CertRenewPass;
:global CertRenewTime;
:global CertRenewUrl;
:global CertWarnTime;
:global Identity;
:global CertificateAvailable
:global CertificateNameByCN;
:global IfThenElse;
:global LogPrintExit2;
:global ParseKeyValueStore;
:global SendNotification2;
:global SymbolForNotification;
:global UrlEncode;
:global WaitForFile;
:global WaitFullyConnected;
:local FormatExpire do={
:global CharacterReplace;
:return [ $CharacterReplace [ $CharacterReplace [ :tostr $1 ] "w" "w " ] "d" "d " ];
}
$WaitFullyConnected;
:foreach Cert in=[ /certificate/find where !revoked !ca !scep-url expires-after<$CertRenewTime ] do={
:local CertVal [ /certificate/get $Cert ];
:do {
:if ([ :len $CertRenewUrl ] = 0) do={
$LogPrintExit2 info $0 ("No CertRenewUrl given.") true;
}
$LogPrintExit2 info $0 ("Attempting to renew certificate " . ($CertVal->"name") . ".") false;
:foreach Type in={ ".pem"; ".p12" } do={
:local CertFileName ([ $UrlEncode ($CertVal->"common-name") ] . $Type);
:do {
/tool/fetch check-certificate=yes-without-crl \
($CertRenewUrl . $CertFileName) dst-path=$CertFileName as-value;
$WaitForFile $CertFileName;
:local DecryptionFailed true;
:foreach PassPhrase in=$CertRenewPass do={
:local Result [ /certificate/import file-name=$CertFileName passphrase=$PassPhrase as-value ];
:if ($Result->"decryption-failures" = 0) do={
:set DecryptionFailed false;
}
}
/file/remove [ find where name=$CertFileName ];
:if ($DecryptionFailed = true) do={
$LogPrintExit2 warning $0 ("Decryption failed for certificate file " . $CertFileName) false;
}
:foreach CertInChain in=[ /certificate/find where name~("^" . $CertFileName . "_[0-9]+\$") common-name!=($CertVal->"common-name") ] do={
$CertificateNameByCN [ /certificate/get $CertInChain common-name ];
}
} on-error={
$LogPrintExit2 debug $0 ("Could not download certificate file " . $CertFileName) false;
}
}
:local CertNew [ /certificate/find where common-name=($CertVal->"common-name") fingerprint!=[ :tostr ($CertVal->"fingerprint") ] expires-after>$CertRenewTime ];
:local CertNewVal [ /certificate/get $CertNew ];
:if ([ $CertificateAvailable ([ $ParseKeyValueStore ($CertNewVal->"issuer") ]->"CN") ] = false) do={
$LogPrintExit2 warning $0 ("The certificate chain is not available!") false;
}
:if ($Cert != $CertNew) do={
$LogPrintExit2 debug $0 ("Certificate '" . $CertVal->"name" . "' was not updated, but replaced.") false;
:if (($CertVal->"private-key") = true && ($CertVal->"private-key") != ($CertNewVal->"private-key")) do={
/certificate/remove $CertNew;
$LogPrintExit2 warning $0 ("Old certificate '" . ($CertVal->"name") . "' has a private key, new certificate does not. Aborting renew.") true;
}
/ip/service/set certificate=($CertNewVal->"name") [ find where certificate=($CertVal->"name") ];
/ip/ipsec/identity/set certificate=($CertNewVal->"name") [ find where certificate=($CertVal->"name") ];
/ip/ipsec/identity/set remote-certificate=($CertNewVal->"name") [ find where remote-certificate=($CertVal->"name") ];
/ip/hotspot/profile/set ssl-certificate=($CertNewVal->"name") [ find where ssl-certificate=($CertVal->"name") ];
/certificate/remove $Cert;
/certificate/set $CertNew name=($CertVal->"name");
}
$SendNotification2 ({ origin=$0; \
subject=([ $SymbolForNotification "lock-with-ink-pen" ] . "Certificate renewed"); \
message=("A certificate on " . $Identity . " has been renewed.\n\n" . \
"Name: " . ($CertVal->"name") . "\n" . \
"CommonName: " . ($CertNewVal->"common-name") . "\n" . \
"Private key: " . [ $IfThenElse (($CertNewVal->"private-key") = true) "available" "missing" ] . "\n" . \
"Fingerprint: " . ($CertNewVal->"fingerprint") . "\n" . \
"Issuer: " . ([ $ParseKeyValueStore ($CertNewVal->"issuer") ]->"CN") . "\n" . \
"Validity: " . ($CertNewVal->"invalid-before") . " to " . ($CertNewVal->"invalid-after") . "\n" . \
"Expires in: " . [ $FormatExpire ($CertNewVal->"expires-after") ]); silent=true });
$LogPrintExit2 info $0 ("The certificate " . ($CertVal->"name") . " has been renewed.") false;
} on-error={
$LogPrintExit2 debug $0 ("Could not renew certificate " . ($CertVal->"name") . ".") false;
}
}
:foreach Cert in=[ /certificate/find where !revoked !scep-url !(expires-after=[]) \
expires-after<$CertWarnTime !(fingerprint=[]) ] do={
:local CertVal [ /certificate/get $Cert ];
:if ([ :len [ /certificate/scep-server/find where ca-cert=($CertVal->"ca") ] ] > 0) do={
$LogPrintExit2 debug $0 ("Certificate \"" . ($CertVal->"name") . "\" is handled by SCEP, skipping.") false;
} else={
:local State [ $IfThenElse (($CertVal->"expired") = true) "expired" "is about to expire" ];
$SendNotification2 ({ origin=$0; \
subject=([ $SymbolForNotification "warning-sign" ] . "Certificate warning!"); \
message=("A certificate on " . $Identity . " " . $State . ".\n\n" . \
"Name: " . ($CertVal->"name") . "\n" . \
"CommonName: " . ($CertVal->"common-name") . "\n" . \
"Private key: " . [ $IfThenElse (($CertVal->"private-key") = true) "available" "missing" ] . "\n" . \
"Fingerprint: " . ($CertVal->"fingerprint") . "\n" . \
"Issuer: " . ($CertVal->"ca") . ([ $ParseKeyValueStore ($CertVal->"issuer") ]->"CN") . "\n" . \
"Validity: " . ($CertVal->"invalid-before") . " to " . ($CertVal->"invalid-after") . "\n" . \
"Expires in: " . [ $IfThenElse (($CertVal->"expired") = true) "expired" [ $FormatExpire ($CertVal->"expires-after") ] ]) });
$LogPrintExit2 info $0 ("The certificate " . ($CertVal->"name") . " " . $State . \
", it is invalid after " . ($CertVal->"invalid-after") . ".") false;
}
}

View file

@ -1,167 +1,3 @@
#!rsc by RouterOS
# RouterOS script: check-health
# Copyright (c) 2019-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# check for RouterOS health state
# https://git.eworm.de/cgit/routeros-scripts/about/doc/check-health.md
:local 0 "check-health";
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:global CheckHealthCPUUtilization;
:global CheckHealthCPUUtilizationNotified;
:global CheckHealthFreeRAMNotified;
:global CheckHealthLast;
:global CheckHealthTemperature;
:global CheckHealthTemperatureDeviation;
:global CheckHealthTemperatureNotified;
:global CheckHealthVoltageLow;
:global CheckHealthVoltagePercent;
:global Identity;
:global IfThenElse;
:global LogPrintExit2;
:global ScriptLock;
:global SendNotification2;
:global SymbolForNotification;
:local TempToNum do={
:global CharacterReplace;
:local T [ :toarray [ $CharacterReplace $1 "." "," ] ];
:return ($T->0 * 10 + $T->1);
}
$ScriptLock $0;
:local Resource [ /system/resource/get ];
:set CheckHealthCPUUtilization (($CheckHealthCPUUtilization * 4 + ($Resource->"cpu-load") * 10) / 5);
:if ($CheckHealthCPUUtilization > 750 && $CheckHealthCPUUtilizationNotified != true) do={
$SendNotification2 ({ origin=$0; \
subject=([ $SymbolForNotification "abacus,chart-increasing" ] . "Health warning: CPU utilization"); \
message=("The average CPU utilization on " . $Identity . " is at " . ($CheckHealthCPUUtilization / 10) . "%!") });
:set CheckHealthCPUUtilizationNotified true;
}
:if ($CheckHealthCPUUtilization < 650 && $CheckHealthCPUUtilizationNotified = true) do={
$SendNotification2 ({ origin=$0; \
subject=([ $SymbolForNotification "abacus,chart-decreasing" ] . "Health recovery: CPU utilization"); \
message=("The average CPU utilization on " . $Identity . " decreased to " . ($CheckHealthCPUUtilization / 10) . "%.") });
:set CheckHealthCPUUtilizationNotified false;
}
:local CheckHealthFreeRAM ($Resource->"free-memory" * 100 / $Resource->"total-memory");
:if ($CheckHealthFreeRAM < 20 && $CheckHealthFreeRAMNotified != true) do={
$SendNotification2 ({ origin=$0; \
subject=([ $SymbolForNotification "card-file-box,chart-decreasing" ] . "Health warning: free RAM"); \
message=("The available free RAM on " . $Identity . " is at " . $CheckHealthFreeRAM . "% (" . \
($Resource->"free-memory" / 1024 / 1024) . "MiB)!") });
:set CheckHealthFreeRAMNotified true;
}
:if ($CheckHealthFreeRAM > 30 && $CheckHealthFreeRAMNotified = true) do={
$SendNotification2 ({ origin=$0; \
subject=([ $SymbolForNotification "card-file-box,chart-increasing" ] . "Health recovery: free RAM"); \
message=("The available free RAM on " . $Identity . " increased to " . $CheckHealthFreeRAM . "% (" . \
($Resource->"free-memory" / 1024 / 1024) . "MiB).") });
:set CheckHealthFreeRAMNotified false;
}
:if ([ :len [ /system/health/find ] ] = 0) do={
$LogPrintExit2 debug $0 ("Your device does not provide any health values.") true;
}
:if ([ :typeof $CheckHealthLast ] != "array") do={
:set CheckHealthLast ({});
}
:if ([ :typeof $CheckHealthTemperatureNotified ] != "array") do={
:set CheckHealthTemperatureNotified ({});
}
:foreach Voltage in=[ /system/health/find where type="V" ] do={
:local Name [ /system/health/get $Voltage name ];
:local Value [ /system/health/get $Voltage value ];
:if ([ :typeof ($CheckHealthLast->$Name) ] != "nothing") do={
:local NumCurr [ $TempToNum $Value ];
:local NumLast [ $TempToNum ($CheckHealthLast->$Name) ];
:if ($NumLast * (100 + $CheckHealthVoltagePercent) < $NumCurr * 100 || \
$NumLast * 100 > $NumCurr * (100 + $CheckHealthVoltagePercent)) do={
$SendNotification2 ({ origin=$0; \
subject=([ $SymbolForNotification ("high-voltage-sign,chart-" . [ $IfThenElse ($NumLast < \
$NumCurr) "in" "de" ] . "creasing") ] . "Health warning: " . $Name); \
message=("The " . $Name . " on " . $Identity . " jumped more than " . $CheckHealthVoltagePercent . "%.\n\n" . \
"old value: " . ($CheckHealthLast->$Name) . " V\n" . \
"new value: " . $Value . " V") });
} else={
:if ($NumCurr <= $CheckHealthVoltageLow && $NumLast > $CheckHealthVoltageLow) do={
$SendNotification2 ({ origin=$0; \
subject=([ $SymbolForNotification "high-voltage-sign,chart-decreasing" ] . "Health warning: Low " . $Name); \
message=("The " . $Name . " on " . $Identity . " dropped to " . $Value . " V below hard limit.") });
}
:if ($NumCurr > $CheckHealthVoltageLow && $NumLast <= $CheckHealthVoltageLow) do={
$SendNotification2 ({ origin=$0; \
subject=([ $SymbolForNotification "high-voltage-sign,chart-increasing" ] . "Health recovery: Low " . $Name); \
message=("The " . $Name . " on " . $Identity . " recovered to " . $Value . " V above hard limit.") });
}
}
}
:set ($CheckHealthLast->$Name) $Value;
}
:foreach PSU in=[ /system/health/find where name~"^psu.*-state\$" ] do={
:local Name [ /system/health/get $PSU name ];
:local Value [ /system/health/get $PSU value ];
:if ([ :typeof ($CheckHealthLast->$Name) ] != "nothing") do={
:if ($CheckHealthLast->$Name = "ok" && \
$Value != "ok") do={
$SendNotification2 ({ origin=$0; \
subject=([ $SymbolForNotification "cross-mark" ] . "Health warning: " . $Name); \
message=("The power supply unit '" . $Name . "' on " . $Identity . " failed!") });
}
:if ($CheckHealthLast->$Name != "ok" && \
$Value = "ok") do={
$SendNotification2 ({ origin=$0; \
subject=([ $SymbolForNotification "white-heavy-check-mark" ] . "Health recovery: " . $Name); \
message=("The power supply unit '" . $Name . "' on " . $Identity . " recovered!") });
}
}
:set ($CheckHealthLast->$Name) $Value;
}
:foreach Temperature in=[ /system/health/find where type="C" ] do={
:local Name [ /system/health/get $Temperature name ];
:local Value [ /system/health/get $Temperature value ];
:if ([ :typeof ($CheckHealthLast->$Name) ] != "nothing") do={
:if ([ :typeof ($CheckHealthTemperature->$Name) ] != "num" ) do={
$LogPrintExit2 info $0 ("No threshold given for " . $Name . ", assuming 50C.") false;
:set ($CheckHealthTemperature->$Name) 50;
}
:local Validate [ /system/health/get [ find where name=$Name ] value ];
:while ($Value != $Validate) do={
:set Value $Validate;
:set Validate [ /system/health/get [ find where name=$Name ] value ];
}
:if ($Value > $CheckHealthTemperature->$Name && \
$CheckHealthTemperatureNotified->$Name != true) do={
$SendNotification2 ({ origin=$0; \
subject=([ $SymbolForNotification "fire" ] . "Health warning: " . $Name); \
message=("The " . $Name . " on " . $Identity . " is above threshold: " . \
$Value . "\C2\B0" . "C") });
:set ($CheckHealthTemperatureNotified->$Name) true;
}
:if ($Value <= ($CheckHealthTemperature->$Name - $CheckHealthTemperatureDeviation) && \
$CheckHealthTemperatureNotified->$Name = true) do={
$SendNotification2 ({ origin=$0; \
subject=([ $SymbolForNotification "white-heavy-check-mark" ] . "Health recovery: " . $Name); \
message=("The " . $Name . " on " . $Identity . " dropped below threshold: " . \
$Value . "\C2\B0" . "C") });
:set ($CheckHealthTemperatureNotified->$Name) false;
}
}
:set ($CheckHealthLast->$Name) $Value;
}
# dummy for migration

167
check-health.rsc Normal file
View file

@ -0,0 +1,167 @@
#!rsc by RouterOS
# RouterOS script: check-health
# Copyright (c) 2019-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# check for RouterOS health state
# https://git.eworm.de/cgit/routeros-scripts/about/doc/check-health.md
:local 0 "check-health";
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:global CheckHealthCPUUtilization;
:global CheckHealthCPUUtilizationNotified;
:global CheckHealthFreeRAMNotified;
:global CheckHealthLast;
:global CheckHealthTemperature;
:global CheckHealthTemperatureDeviation;
:global CheckHealthTemperatureNotified;
:global CheckHealthVoltageLow;
:global CheckHealthVoltagePercent;
:global Identity;
:global IfThenElse;
:global LogPrintExit2;
:global ScriptLock;
:global SendNotification2;
:global SymbolForNotification;
:local TempToNum do={
:global CharacterReplace;
:local T [ :toarray [ $CharacterReplace $1 "." "," ] ];
:return ($T->0 * 10 + $T->1);
}
$ScriptLock $0;
:local Resource [ /system/resource/get ];
:set CheckHealthCPUUtilization (($CheckHealthCPUUtilization * 4 + ($Resource->"cpu-load") * 10) / 5);
:if ($CheckHealthCPUUtilization > 750 && $CheckHealthCPUUtilizationNotified != true) do={
$SendNotification2 ({ origin=$0; \
subject=([ $SymbolForNotification "abacus,chart-increasing" ] . "Health warning: CPU utilization"); \
message=("The average CPU utilization on " . $Identity . " is at " . ($CheckHealthCPUUtilization / 10) . "%!") });
:set CheckHealthCPUUtilizationNotified true;
}
:if ($CheckHealthCPUUtilization < 650 && $CheckHealthCPUUtilizationNotified = true) do={
$SendNotification2 ({ origin=$0; \
subject=([ $SymbolForNotification "abacus,chart-decreasing" ] . "Health recovery: CPU utilization"); \
message=("The average CPU utilization on " . $Identity . " decreased to " . ($CheckHealthCPUUtilization / 10) . "%.") });
:set CheckHealthCPUUtilizationNotified false;
}
:local CheckHealthFreeRAM ($Resource->"free-memory" * 100 / $Resource->"total-memory");
:if ($CheckHealthFreeRAM < 20 && $CheckHealthFreeRAMNotified != true) do={
$SendNotification2 ({ origin=$0; \
subject=([ $SymbolForNotification "card-file-box,chart-decreasing" ] . "Health warning: free RAM"); \
message=("The available free RAM on " . $Identity . " is at " . $CheckHealthFreeRAM . "% (" . \
($Resource->"free-memory" / 1024 / 1024) . "MiB)!") });
:set CheckHealthFreeRAMNotified true;
}
:if ($CheckHealthFreeRAM > 30 && $CheckHealthFreeRAMNotified = true) do={
$SendNotification2 ({ origin=$0; \
subject=([ $SymbolForNotification "card-file-box,chart-increasing" ] . "Health recovery: free RAM"); \
message=("The available free RAM on " . $Identity . " increased to " . $CheckHealthFreeRAM . "% (" . \
($Resource->"free-memory" / 1024 / 1024) . "MiB).") });
:set CheckHealthFreeRAMNotified false;
}
:if ([ :len [ /system/health/find ] ] = 0) do={
$LogPrintExit2 debug $0 ("Your device does not provide any health values.") true;
}
:if ([ :typeof $CheckHealthLast ] != "array") do={
:set CheckHealthLast ({});
}
:if ([ :typeof $CheckHealthTemperatureNotified ] != "array") do={
:set CheckHealthTemperatureNotified ({});
}
:foreach Voltage in=[ /system/health/find where type="V" ] do={
:local Name [ /system/health/get $Voltage name ];
:local Value [ /system/health/get $Voltage value ];
:if ([ :typeof ($CheckHealthLast->$Name) ] != "nothing") do={
:local NumCurr [ $TempToNum $Value ];
:local NumLast [ $TempToNum ($CheckHealthLast->$Name) ];
:if ($NumLast * (100 + $CheckHealthVoltagePercent) < $NumCurr * 100 || \
$NumLast * 100 > $NumCurr * (100 + $CheckHealthVoltagePercent)) do={
$SendNotification2 ({ origin=$0; \
subject=([ $SymbolForNotification ("high-voltage-sign,chart-" . [ $IfThenElse ($NumLast < \
$NumCurr) "in" "de" ] . "creasing") ] . "Health warning: " . $Name); \
message=("The " . $Name . " on " . $Identity . " jumped more than " . $CheckHealthVoltagePercent . "%.\n\n" . \
"old value: " . ($CheckHealthLast->$Name) . " V\n" . \
"new value: " . $Value . " V") });
} else={
:if ($NumCurr <= $CheckHealthVoltageLow && $NumLast > $CheckHealthVoltageLow) do={
$SendNotification2 ({ origin=$0; \
subject=([ $SymbolForNotification "high-voltage-sign,chart-decreasing" ] . "Health warning: Low " . $Name); \
message=("The " . $Name . " on " . $Identity . " dropped to " . $Value . " V below hard limit.") });
}
:if ($NumCurr > $CheckHealthVoltageLow && $NumLast <= $CheckHealthVoltageLow) do={
$SendNotification2 ({ origin=$0; \
subject=([ $SymbolForNotification "high-voltage-sign,chart-increasing" ] . "Health recovery: Low " . $Name); \
message=("The " . $Name . " on " . $Identity . " recovered to " . $Value . " V above hard limit.") });
}
}
}
:set ($CheckHealthLast->$Name) $Value;
}
:foreach PSU in=[ /system/health/find where name~"^psu.*-state\$" ] do={
:local Name [ /system/health/get $PSU name ];
:local Value [ /system/health/get $PSU value ];
:if ([ :typeof ($CheckHealthLast->$Name) ] != "nothing") do={
:if ($CheckHealthLast->$Name = "ok" && \
$Value != "ok") do={
$SendNotification2 ({ origin=$0; \
subject=([ $SymbolForNotification "cross-mark" ] . "Health warning: " . $Name); \
message=("The power supply unit '" . $Name . "' on " . $Identity . " failed!") });
}
:if ($CheckHealthLast->$Name != "ok" && \
$Value = "ok") do={
$SendNotification2 ({ origin=$0; \
subject=([ $SymbolForNotification "white-heavy-check-mark" ] . "Health recovery: " . $Name); \
message=("The power supply unit '" . $Name . "' on " . $Identity . " recovered!") });
}
}
:set ($CheckHealthLast->$Name) $Value;
}
:foreach Temperature in=[ /system/health/find where type="C" ] do={
:local Name [ /system/health/get $Temperature name ];
:local Value [ /system/health/get $Temperature value ];
:if ([ :typeof ($CheckHealthLast->$Name) ] != "nothing") do={
:if ([ :typeof ($CheckHealthTemperature->$Name) ] != "num" ) do={
$LogPrintExit2 info $0 ("No threshold given for " . $Name . ", assuming 50C.") false;
:set ($CheckHealthTemperature->$Name) 50;
}
:local Validate [ /system/health/get [ find where name=$Name ] value ];
:while ($Value != $Validate) do={
:set Value $Validate;
:set Validate [ /system/health/get [ find where name=$Name ] value ];
}
:if ($Value > $CheckHealthTemperature->$Name && \
$CheckHealthTemperatureNotified->$Name != true) do={
$SendNotification2 ({ origin=$0; \
subject=([ $SymbolForNotification "fire" ] . "Health warning: " . $Name); \
message=("The " . $Name . " on " . $Identity . " is above threshold: " . \
$Value . "\C2\B0" . "C") });
:set ($CheckHealthTemperatureNotified->$Name) true;
}
:if ($Value <= ($CheckHealthTemperature->$Name - $CheckHealthTemperatureDeviation) && \
$CheckHealthTemperatureNotified->$Name = true) do={
$SendNotification2 ({ origin=$0; \
subject=([ $SymbolForNotification "white-heavy-check-mark" ] . "Health recovery: " . $Name); \
message=("The " . $Name . " on " . $Identity . " dropped below threshold: " . \
$Value . "\C2\B0" . "C") });
:set ($CheckHealthTemperatureNotified->$Name) false;
}
}
:set ($CheckHealthLast->$Name) $Value;
}

View file

@ -1,82 +1,3 @@
#!rsc by RouterOS
# RouterOS script: check-lte-firmware-upgrade
# Copyright (c) 2018-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# check for LTE firmware upgrade, send notification
# https://git.eworm.de/cgit/routeros-scripts/about/doc/check-lte-firmware-upgrade.md
:local 0 "check-lte-firmware-upgrade";
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:global SentLteFirmwareUpgradeNotification;
:if ([ :typeof $SentLteFirmwareUpgradeNotification ] != "array") do={
:global SentLteFirmwareUpgradeNotification ({});
}
:local CheckInterface do={
:local Interface $1;
:global Identity;
:global SentLteFirmwareUpgradeNotification;
:global CharacterReplace;
:global LogPrintExit2;
:global ScriptFromTerminal;
:global SendNotification2;
:global SymbolForNotification;
:local IntName [ /interface/lte/get $Interface name ];
:local Firmware;
:local Info;
:do {
:set Firmware [ /interface/lte/firmware-upgrade $Interface once as-value ];
:set Info [ /interface/lte/monitor $Interface once as-value ];
} on-error={
$LogPrintExit2 debug $0 ("Could not get latest LTE firmware version for interface " . \
$IntName . ".") false;
:return false;
}
:if (($Firmware->"installed") = ($Firmware->"latest")) do={
:if ([ $ScriptFromTerminal $0 ] = true) do={
$LogPrintExit2 info $0 ("No firmware upgrade available for LTE interface " . $IntName . ".") false;
}
:return true;
}
:if ([ $ScriptFromTerminal $0 ] = true && \
[ :len [ /system/script/find where name="unattended-lte-firmware-upgrade" ] ] > 0) do={
:put ("Do you want to start unattended lte firmware upgrade for interface " . $IntName . "? [y/N]");
:if (([ /terminal/inkey timeout=60 ] % 32) = 25) do={
/system/script/run unattended-lte-firmware-upgrade;
$LogPrintExit2 info $0 ("Scheduled lte firmware upgrade for interface " . $IntName . "...") false;
:return true;
} else={
:put "Canceled...";
}
}
:if (($SentLteFirmwareUpgradeNotification->$IntName) = ($Firmware->"latest")) do={
$LogPrintExit2 debug $0 ("Already sent the LTE firmware upgrade notification for version " . \
($Firmware->"latest") . ".") false;
:return false;
}
$LogPrintExit2 info $0 ("A new firmware version " . ($Firmware->"latest") . " is available for " . \
"LTE interface " . $IntName . ".") false;
$SendNotification2 ({ origin=$0; \
subject=([ $SymbolForNotification "sparkles" ] . "LTE firmware upgrade"); \
message=("A new firmware version " . ($Firmware->"latest") . " is available for " . \
"LTE interface " . $IntName . " on " . $Identity . ".\n\n" . \
"Interface: " . [ $CharacterReplace ($Info->"manufacturer" . " " . $Info->"model") ("\"") "" ] . "\n" . \
"Installed: " . ($Firmware->"installed") . "\n" . \
"Available: " . ($Firmware->"latest")); silent=true });
:set ($SentLteFirmwareUpgradeNotification->$IntName) ($Firmware->"latest");
}
:foreach Interface in=[ /interface/lte/find ] do={
$CheckInterface $Interface;
}
# dummy for migration

View file

@ -0,0 +1,82 @@
#!rsc by RouterOS
# RouterOS script: check-lte-firmware-upgrade
# Copyright (c) 2018-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# check for LTE firmware upgrade, send notification
# https://git.eworm.de/cgit/routeros-scripts/about/doc/check-lte-firmware-upgrade.md
:local 0 "check-lte-firmware-upgrade";
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:global SentLteFirmwareUpgradeNotification;
:if ([ :typeof $SentLteFirmwareUpgradeNotification ] != "array") do={
:global SentLteFirmwareUpgradeNotification ({});
}
:local CheckInterface do={
:local Interface $1;
:global Identity;
:global SentLteFirmwareUpgradeNotification;
:global CharacterReplace;
:global LogPrintExit2;
:global ScriptFromTerminal;
:global SendNotification2;
:global SymbolForNotification;
:local IntName [ /interface/lte/get $Interface name ];
:local Firmware;
:local Info;
:do {
:set Firmware [ /interface/lte/firmware-upgrade $Interface once as-value ];
:set Info [ /interface/lte/monitor $Interface once as-value ];
} on-error={
$LogPrintExit2 debug $0 ("Could not get latest LTE firmware version for interface " . \
$IntName . ".") false;
:return false;
}
:if (($Firmware->"installed") = ($Firmware->"latest")) do={
:if ([ $ScriptFromTerminal $0 ] = true) do={
$LogPrintExit2 info $0 ("No firmware upgrade available for LTE interface " . $IntName . ".") false;
}
:return true;
}
:if ([ $ScriptFromTerminal $0 ] = true && \
[ :len [ /system/script/find where name="unattended-lte-firmware-upgrade" ] ] > 0) do={
:put ("Do you want to start unattended lte firmware upgrade for interface " . $IntName . "? [y/N]");
:if (([ /terminal/inkey timeout=60 ] % 32) = 25) do={
/system/script/run unattended-lte-firmware-upgrade;
$LogPrintExit2 info $0 ("Scheduled lte firmware upgrade for interface " . $IntName . "...") false;
:return true;
} else={
:put "Canceled...";
}
}
:if (($SentLteFirmwareUpgradeNotification->$IntName) = ($Firmware->"latest")) do={
$LogPrintExit2 debug $0 ("Already sent the LTE firmware upgrade notification for version " . \
($Firmware->"latest") . ".") false;
:return false;
}
$LogPrintExit2 info $0 ("A new firmware version " . ($Firmware->"latest") . " is available for " . \
"LTE interface " . $IntName . ".") false;
$SendNotification2 ({ origin=$0; \
subject=([ $SymbolForNotification "sparkles" ] . "LTE firmware upgrade"); \
message=("A new firmware version " . ($Firmware->"latest") . " is available for " . \
"LTE interface " . $IntName . " on " . $Identity . ".\n\n" . \
"Interface: " . [ $CharacterReplace ($Info->"manufacturer" . " " . $Info->"model") ("\"") "" ] . "\n" . \
"Installed: " . ($Firmware->"installed") . "\n" . \
"Available: " . ($Firmware->"latest")); silent=true });
:set ($SentLteFirmwareUpgradeNotification->$IntName) ($Firmware->"latest");
}
:foreach Interface in=[ /interface/lte/find ] do={
$CheckInterface $Interface;
}

View file

@ -1,147 +1,3 @@
#!rsc by RouterOS
# RouterOS script: check-routeros-update
# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# check for RouterOS update, send notification and/or install
# https://git.eworm.de/cgit/routeros-scripts/about/doc/check-routeros-update.md
:local 0 "check-routeros-update";
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:global Identity;
:global SafeUpdateAll;
:global SafeUpdateNeighbor;
:global SafeUpdatePatch;
:global SafeUpdateUrl;
:global SentRouterosUpdateNotification;
:global DeviceInfo;
:global LogPrintExit2;
:global ScriptFromTerminal;
:global ScriptLock;
:global SendNotification2;
:global SymbolForNotification;
:global VersionToNum;
:global WaitFullyConnected;
:local DoUpdate do={
:if ([ :len [ /system/script/find where name="packages-update" ] ] > 0) do={
/system/script/run packages-update;
} else={
/system/package/update/install without-paging;
}
:error "Waiting for system to reboot.";
}
$ScriptLock $0;
$WaitFullyConnected;
:if ([ :len [ /system/scheduler/find where name="reboot-for-update" ] ] > 0) do={
:error "A reboot for update is already scheduled.";
}
$LogPrintExit2 debug $0 ("Checking for updates...") false;
/system/package/update/check-for-updates without-paging as-value;
:local Update [ /system/package/update/get ];
:if ([ $ScriptFromTerminal $0 ] = true && ($Update->"installed-version") = ($Update->"latest-version")) do={
$LogPrintExit2 info $0 ("System is already up to date.") true;
}
:local NumInstalled [ $VersionToNum ($Update->"installed-version") ];
:local NumLatest [ $VersionToNum ($Update->"latest-version") ];
:local Link ("https://mikrotik.com/download/changelogs/" . $Update->"channel" . "-release-tree");
:if ($NumLatest < 117505792) do={
$LogPrintExit2 info $0 ("The version '" . ($Update->"latest-version") . "' is not a valid version.") true;
}
:if ($NumInstalled < $NumLatest) do={
:if ($SafeUpdateAll ~ "^YES,? ?PLEASE!?\$") do={
$LogPrintExit2 info $0 ("Installing ALL versions automatically, including " . \
$Update->"latest-version" . "...") false;
$SendNotification2 ({ origin=$0; \
subject=([ $SymbolForNotification "sparkles" ] . "RouterOS update"); \
message=("Installing ALL versions automatically, including " . $Update->"latest-version" . \
"... Updating on " . $Identity . "..."); link=$Link; silent=true });
$DoUpdate;
}
:if ($SafeUpdatePatch = true && ($NumInstalled & 0xffff0000) = ($NumLatest & 0xffff0000)) do={
$LogPrintExit2 info $0 ("Version " . $Update->"latest-version" . " is a patch release, updating...") false;
$SendNotification2 ({ origin=$0; \
subject=([ $SymbolForNotification "sparkles" ] . "RouterOS update"); \
message=("Version " . $Update->"latest-version" . " is a patch update for " . $Update->"channel" . \
", updating on " . $Identity . "..."); link=$Link; silent=true });
$DoUpdate;
}
:if ($SafeUpdateNeighbor = true && [ :len [ /ip/neighbor/find where \
version=($Update->"latest-version" . " (" . $Update->"channel" . ")") ] ] > 0) do={
$LogPrintExit2 info $0 ("Seen a neighbor running version " . $Update->"latest-version" . ", updating...") false;
$SendNotification2 ({ origin=$0; \
subject=([ $SymbolForNotification "sparkles" ] . "RouterOS update"); \
message=("Seen a neighbor running version " . $Update->"latest-version" . " from " . $Update->"channel" . \
", updating on " . $Identity . "..."); link=$Link; silent=true });
$DoUpdate;
}
:if ([ :len $SafeUpdateUrl ] > 0) do={
:local Result;
:do {
:set Result [ /tool/fetch check-certificate=yes-without-crl \
($SafeUpdateUrl . $Update->"channel" . "?installed=" . $Update->"installed-version" . \
"&latest=" . $Update->"latest-version") output=user as-value ];
} on-error={
$LogPrintExit2 warning $0 ("Failed receiving safe version for " . $Update->"channel" . ".") false;
}
:if ($Result->"status" = "finished" && $Result->"data" = $Update->"latest-version") do={
$LogPrintExit2 info $0 ("Version " . $Update->"latest-version" . " is considered safe, updating...") false;
$SendNotification2 ({ origin=$0; \
subject=([ $SymbolForNotification "sparkles" ] . "RouterOS update"); \
message=("Version " . $Update->"latest-version" . " is considered safe for " . $Update->"channel" . \
", updating on " . $Identity . "..."); link=$Link; silent=true });
$DoUpdate;
}
}
:if ([ $ScriptFromTerminal $0 ] = true) do={
:put ("Do you want to install RouterOS version " . $Update->"latest-version" . "? [y/N]");
:if (([ /terminal/inkey timeout=60 ] % 32) = 25) do={
$DoUpdate;
} else={
:put "Canceled...";
}
}
:if ($SentRouterosUpdateNotification = $Update->"latest-version") do={
$LogPrintExit2 info $0 ("Already sent the RouterOS update notification for version " . \
$Update->"latest-version" . ".") true;
}
$SendNotification2 ({ origin=$0; \
subject=([ $SymbolForNotification "sparkles" ] . "RouterOS update"); \
message=("A new RouterOS version " . ($Update->"latest-version") . \
" is available for " . $Identity . ".\n\n" . \
[ $DeviceInfo ]); link=$Link; silent=true });
:set SentRouterosUpdateNotification ($Update->"latest-version");
}
:if ($NumInstalled > $NumLatest) do={
:if ($SentRouterosUpdateNotification = $Update->"latest-version") do={
$LogPrintExit2 info $0 ("Already sent the RouterOS downgrade notification for version " . \
$Update->"latest-version" . ".") true;
}
$SendNotification2 ({ origin=$0; \
subject=([ $SymbolForNotification "warning-sign" ] . "RouterOS version"); \
message=("A different RouterOS version " . ($Update->"latest-version") . \
" is available for " . $Identity . ", but it is a downgrade.\n\n" . \
[ $DeviceInfo ]); link=$Link; silent=true });
$LogPrintExit2 info $0 ("A different RouterOS version " . ($Update->"latest-version") . \
" is available for downgrade.") false;
:set SentRouterosUpdateNotification ($Update->"latest-version");
}
# dummy for migration

147
check-routeros-update.rsc Normal file
View file

@ -0,0 +1,147 @@
#!rsc by RouterOS
# RouterOS script: check-routeros-update
# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# check for RouterOS update, send notification and/or install
# https://git.eworm.de/cgit/routeros-scripts/about/doc/check-routeros-update.md
:local 0 "check-routeros-update";
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:global Identity;
:global SafeUpdateAll;
:global SafeUpdateNeighbor;
:global SafeUpdatePatch;
:global SafeUpdateUrl;
:global SentRouterosUpdateNotification;
:global DeviceInfo;
:global LogPrintExit2;
:global ScriptFromTerminal;
:global ScriptLock;
:global SendNotification2;
:global SymbolForNotification;
:global VersionToNum;
:global WaitFullyConnected;
:local DoUpdate do={
:if ([ :len [ /system/script/find where name="packages-update" ] ] > 0) do={
/system/script/run packages-update;
} else={
/system/package/update/install without-paging;
}
:error "Waiting for system to reboot.";
}
$ScriptLock $0;
$WaitFullyConnected;
:if ([ :len [ /system/scheduler/find where name="reboot-for-update" ] ] > 0) do={
:error "A reboot for update is already scheduled.";
}
$LogPrintExit2 debug $0 ("Checking for updates...") false;
/system/package/update/check-for-updates without-paging as-value;
:local Update [ /system/package/update/get ];
:if ([ $ScriptFromTerminal $0 ] = true && ($Update->"installed-version") = ($Update->"latest-version")) do={
$LogPrintExit2 info $0 ("System is already up to date.") true;
}
:local NumInstalled [ $VersionToNum ($Update->"installed-version") ];
:local NumLatest [ $VersionToNum ($Update->"latest-version") ];
:local Link ("https://mikrotik.com/download/changelogs/" . $Update->"channel" . "-release-tree");
:if ($NumLatest < 117505792) do={
$LogPrintExit2 info $0 ("The version '" . ($Update->"latest-version") . "' is not a valid version.") true;
}
:if ($NumInstalled < $NumLatest) do={
:if ($SafeUpdateAll ~ "^YES,? ?PLEASE!?\$") do={
$LogPrintExit2 info $0 ("Installing ALL versions automatically, including " . \
$Update->"latest-version" . "...") false;
$SendNotification2 ({ origin=$0; \
subject=([ $SymbolForNotification "sparkles" ] . "RouterOS update"); \
message=("Installing ALL versions automatically, including " . $Update->"latest-version" . \
"... Updating on " . $Identity . "..."); link=$Link; silent=true });
$DoUpdate;
}
:if ($SafeUpdatePatch = true && ($NumInstalled & 0xffff0000) = ($NumLatest & 0xffff0000)) do={
$LogPrintExit2 info $0 ("Version " . $Update->"latest-version" . " is a patch release, updating...") false;
$SendNotification2 ({ origin=$0; \
subject=([ $SymbolForNotification "sparkles" ] . "RouterOS update"); \
message=("Version " . $Update->"latest-version" . " is a patch update for " . $Update->"channel" . \
", updating on " . $Identity . "..."); link=$Link; silent=true });
$DoUpdate;
}
:if ($SafeUpdateNeighbor = true && [ :len [ /ip/neighbor/find where \
version=($Update->"latest-version" . " (" . $Update->"channel" . ")") ] ] > 0) do={
$LogPrintExit2 info $0 ("Seen a neighbor running version " . $Update->"latest-version" . ", updating...") false;
$SendNotification2 ({ origin=$0; \
subject=([ $SymbolForNotification "sparkles" ] . "RouterOS update"); \
message=("Seen a neighbor running version " . $Update->"latest-version" . " from " . $Update->"channel" . \
", updating on " . $Identity . "..."); link=$Link; silent=true });
$DoUpdate;
}
:if ([ :len $SafeUpdateUrl ] > 0) do={
:local Result;
:do {
:set Result [ /tool/fetch check-certificate=yes-without-crl \
($SafeUpdateUrl . $Update->"channel" . "?installed=" . $Update->"installed-version" . \
"&latest=" . $Update->"latest-version") output=user as-value ];
} on-error={
$LogPrintExit2 warning $0 ("Failed receiving safe version for " . $Update->"channel" . ".") false;
}
:if ($Result->"status" = "finished" && $Result->"data" = $Update->"latest-version") do={
$LogPrintExit2 info $0 ("Version " . $Update->"latest-version" . " is considered safe, updating...") false;
$SendNotification2 ({ origin=$0; \
subject=([ $SymbolForNotification "sparkles" ] . "RouterOS update"); \
message=("Version " . $Update->"latest-version" . " is considered safe for " . $Update->"channel" . \
", updating on " . $Identity . "..."); link=$Link; silent=true });
$DoUpdate;
}
}
:if ([ $ScriptFromTerminal $0 ] = true) do={
:put ("Do you want to install RouterOS version " . $Update->"latest-version" . "? [y/N]");
:if (([ /terminal/inkey timeout=60 ] % 32) = 25) do={
$DoUpdate;
} else={
:put "Canceled...";
}
}
:if ($SentRouterosUpdateNotification = $Update->"latest-version") do={
$LogPrintExit2 info $0 ("Already sent the RouterOS update notification for version " . \
$Update->"latest-version" . ".") true;
}
$SendNotification2 ({ origin=$0; \
subject=([ $SymbolForNotification "sparkles" ] . "RouterOS update"); \
message=("A new RouterOS version " . ($Update->"latest-version") . \
" is available for " . $Identity . ".\n\n" . \
[ $DeviceInfo ]); link=$Link; silent=true });
:set SentRouterosUpdateNotification ($Update->"latest-version");
}
:if ($NumInstalled > $NumLatest) do={
:if ($SentRouterosUpdateNotification = $Update->"latest-version") do={
$LogPrintExit2 info $0 ("Already sent the RouterOS downgrade notification for version " . \
$Update->"latest-version" . ".") true;
}
$SendNotification2 ({ origin=$0; \
subject=([ $SymbolForNotification "warning-sign" ] . "RouterOS version"); \
message=("A different RouterOS version " . ($Update->"latest-version") . \
" is available for " . $Identity . ", but it is a downgrade.\n\n" . \
[ $DeviceInfo ]); link=$Link; silent=true });
$LogPrintExit2 info $0 ("A different RouterOS version " . ($Update->"latest-version") . \
" is available for downgrade.") false;
:set SentRouterosUpdateNotification ($Update->"latest-version");
}

View file

@ -1,85 +1,3 @@
#!rsc by RouterOS
# RouterOS script: collect-wireless-mac.capsman
# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# collect wireless mac adresses in access list
# https://git.eworm.de/cgit/routeros-scripts/about/doc/collect-wireless-mac.md
#
# provides: lease-script, order=40
#
# !! Do not edit this file, it is generated from template!
:local 0 "collect-wireless-mac.capsman";
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:global Identity;
:global EitherOr;
:global GetMacVendor;
:global LogPrintExit2;
:global ScriptLock;
:global SendNotification2;
:global SymbolForNotification;
$ScriptLock $0 false 10;
:if ([ :len [ /caps-man/access-list/find where comment="--- collected above ---" disabled ] ] = 0) do={
/caps-man/access-list/add comment="--- collected above ---" disabled=yes;
$LogPrintExit2 warning $0 ("Added disabled access-list entry with comment '--- collected above ---'.") false;
}
:local PlaceBefore ([ /caps-man/access-list/find where comment="--- collected above ---" disabled ]->0);
:foreach Reg in=[ /caps-man/registration-table/find ] do={
:local RegVal;
:do {
:set RegVal [ /caps-man/registration-table/get $Reg ];
} on-error={
$LogPrintExit2 debug $0 ("Device already gone... Ignoring.") false;
}
:if ([ :len ($RegVal->"mac-address") ] > 0) do={
:local AccessList ([ /caps-man/access-list/find where mac-address=($RegVal->"mac-address") ]->0);
:if ([ :len $AccessList ] > 0) do={
$LogPrintExit2 debug $0 ("MAC address " . $RegVal->"mac-address" . " already known: " . \
[ /caps-man/access-list/get $AccessList comment ]) false;
}
:if ([ :len $AccessList ] = 0) do={
:local Address "no dhcp lease";
:local DnsName "no dhcp lease";
:local HostName "no dhcp lease";
:local Lease ([ /ip/dhcp-server/lease/find where mac-address=($RegVal->"mac-address") dynamic=yes status=bound ]->0);
:if ([ :len $Lease ] > 0) do={
:set Address [ /ip/dhcp-server/lease/get $Lease address ];
:set HostName [ $EitherOr [ /ip/dhcp-server/lease/get $Lease host-name ] "no hostname" ];
:set DnsName "no dns name";
:local DnsRec ([ /ip/dns/static/find where address=$Address ]->0);
:if ([ :len $DnsRec ] > 0) do={
:set DnsName [ /ip/dns/static/get $DnsRec name ];
}
}
:local DateTime ([ /system/clock/get date ] . " " . [ /system/clock/get time ]);
:local Vendor [ $GetMacVendor ($RegVal->"mac-address") ];
:local Message ("MAC address " . $RegVal->"mac-address" . " (" . $Vendor . ", " . $HostName . ") " . \
"first seen on " . $DateTime . " connected to SSID " . $RegVal->"ssid" . ", interface " . $RegVal->"interface");
$LogPrintExit2 info $0 $Message false;
/caps-man/access-list/add place-before=$PlaceBefore comment=$Message mac-address=($RegVal->"mac-address") disabled=yes;
$SendNotification2 ({ origin=$0; \
subject=([ $SymbolForNotification "mobile-phone" ] . $RegVal->"mac-address" . " connected to " . $RegVal->"ssid"); \
message=("A device with unknown MAC address connected to " . $RegVal->"ssid" . " on " . $Identity . ".\n\n" . \
"Controller: " . $Identity . "\n" . \
"Interface: " . $RegVal->"interface" . "\n" . \
"SSID: " . $RegVal->"ssid" . "\n" . \
"MAC: " . $RegVal->"mac-address" . "\n" . \
"Vendor: " . $Vendor . "\n" . \
"Hostname: " . $HostName . "\n" . \
"Address: " . $Address . "\n" . \
"DNS name: " . $DnsName . "\n" . \
"Date: " . $DateTime) });
}
} else={
$LogPrintExit2 debug $0 ("No mac address available... Ignoring.") false;
}
}
# dummy for migration

View file

@ -0,0 +1,85 @@
#!rsc by RouterOS
# RouterOS script: collect-wireless-mac.capsman
# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# collect wireless mac adresses in access list
# https://git.eworm.de/cgit/routeros-scripts/about/doc/collect-wireless-mac.md
#
# provides: lease-script, order=40
#
# !! Do not edit this file, it is generated from template!
:local 0 "collect-wireless-mac.capsman";
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:global Identity;
:global EitherOr;
:global GetMacVendor;
:global LogPrintExit2;
:global ScriptLock;
:global SendNotification2;
:global SymbolForNotification;
$ScriptLock $0 false 10;
:if ([ :len [ /caps-man/access-list/find where comment="--- collected above ---" disabled ] ] = 0) do={
/caps-man/access-list/add comment="--- collected above ---" disabled=yes;
$LogPrintExit2 warning $0 ("Added disabled access-list entry with comment '--- collected above ---'.") false;
}
:local PlaceBefore ([ /caps-man/access-list/find where comment="--- collected above ---" disabled ]->0);
:foreach Reg in=[ /caps-man/registration-table/find ] do={
:local RegVal;
:do {
:set RegVal [ /caps-man/registration-table/get $Reg ];
} on-error={
$LogPrintExit2 debug $0 ("Device already gone... Ignoring.") false;
}
:if ([ :len ($RegVal->"mac-address") ] > 0) do={
:local AccessList ([ /caps-man/access-list/find where mac-address=($RegVal->"mac-address") ]->0);
:if ([ :len $AccessList ] > 0) do={
$LogPrintExit2 debug $0 ("MAC address " . $RegVal->"mac-address" . " already known: " . \
[ /caps-man/access-list/get $AccessList comment ]) false;
}
:if ([ :len $AccessList ] = 0) do={
:local Address "no dhcp lease";
:local DnsName "no dhcp lease";
:local HostName "no dhcp lease";
:local Lease ([ /ip/dhcp-server/lease/find where mac-address=($RegVal->"mac-address") dynamic=yes status=bound ]->0);
:if ([ :len $Lease ] > 0) do={
:set Address [ /ip/dhcp-server/lease/get $Lease address ];
:set HostName [ $EitherOr [ /ip/dhcp-server/lease/get $Lease host-name ] "no hostname" ];
:set DnsName "no dns name";
:local DnsRec ([ /ip/dns/static/find where address=$Address ]->0);
:if ([ :len $DnsRec ] > 0) do={
:set DnsName [ /ip/dns/static/get $DnsRec name ];
}
}
:local DateTime ([ /system/clock/get date ] . " " . [ /system/clock/get time ]);
:local Vendor [ $GetMacVendor ($RegVal->"mac-address") ];
:local Message ("MAC address " . $RegVal->"mac-address" . " (" . $Vendor . ", " . $HostName . ") " . \
"first seen on " . $DateTime . " connected to SSID " . $RegVal->"ssid" . ", interface " . $RegVal->"interface");
$LogPrintExit2 info $0 $Message false;
/caps-man/access-list/add place-before=$PlaceBefore comment=$Message mac-address=($RegVal->"mac-address") disabled=yes;
$SendNotification2 ({ origin=$0; \
subject=([ $SymbolForNotification "mobile-phone" ] . $RegVal->"mac-address" . " connected to " . $RegVal->"ssid"); \
message=("A device with unknown MAC address connected to " . $RegVal->"ssid" . " on " . $Identity . ".\n\n" . \
"Controller: " . $Identity . "\n" . \
"Interface: " . $RegVal->"interface" . "\n" . \
"SSID: " . $RegVal->"ssid" . "\n" . \
"MAC: " . $RegVal->"mac-address" . "\n" . \
"Vendor: " . $Vendor . "\n" . \
"Hostname: " . $HostName . "\n" . \
"Address: " . $Address . "\n" . \
"DNS name: " . $DnsName . "\n" . \
"Date: " . $DateTime) });
}
} else={
$LogPrintExit2 debug $0 ("No mac address available... Ignoring.") false;
}
}

View file

@ -1,86 +1,3 @@
#!rsc by RouterOS
# RouterOS script: collect-wireless-mac.local
# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# collect wireless mac adresses in access list
# https://git.eworm.de/cgit/routeros-scripts/about/doc/collect-wireless-mac.md
#
# provides: lease-script, order=40
#
# !! Do not edit this file, it is generated from template!
:local 0 "collect-wireless-mac.local";
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:global Identity;
:global EitherOr;
:global GetMacVendor;
:global LogPrintExit2;
:global ScriptLock;
:global SendNotification2;
:global SymbolForNotification;
$ScriptLock $0 false 10;
:if ([ :len [ /interface/wireless/access-list/find where comment="--- collected above ---" disabled ] ] = 0) do={
/interface/wireless/access-list/add comment="--- collected above ---" disabled=yes;
$LogPrintExit2 warning $0 ("Added disabled access-list entry with comment '--- collected above ---'.") false;
}
:local PlaceBefore ([ /interface/wireless/access-list/find where comment="--- collected above ---" disabled ]->0);
:foreach Reg in=[ /interface/wireless/registration-table/find ] do={
:local RegVal;
:do {
:set RegVal [ /interface/wireless/registration-table/get $Reg ];
} on-error={
$LogPrintExit2 debug $0 ("Device already gone... Ignoring.") false;
}
:if ([ :len ($RegVal->"mac-address") ] > 0) do={
:local AccessList ([ /interface/wireless/access-list/find where mac-address=($RegVal->"mac-address") ]->0);
:if ([ :len $AccessList ] > 0) do={
$LogPrintExit2 debug $0 ("MAC address " . $RegVal->"mac-address" . " already known: " . \
[ /interface/wireless/access-list/get $AccessList comment ]) false;
}
:if ([ :len $AccessList ] = 0) do={
:local Address "no dhcp lease";
:local DnsName "no dhcp lease";
:local HostName "no dhcp lease";
:local Lease ([ /ip/dhcp-server/lease/find where mac-address=($RegVal->"mac-address") dynamic=yes status=bound ]->0);
:if ([ :len $Lease ] > 0) do={
:set Address [ /ip/dhcp-server/lease/get $Lease address ];
:set HostName [ $EitherOr [ /ip/dhcp-server/lease/get $Lease host-name ] "no hostname" ];
:set DnsName "no dns name";
:local DnsRec ([ /ip/dns/static/find where address=$Address ]->0);
:if ([ :len $DnsRec ] > 0) do={
:set DnsName [ /ip/dns/static/get $DnsRec name ];
}
}
:set ($RegVal->"ssid") [ /interface/wireless/get [ find where name=($RegVal->"interface") ] ssid ];
:local DateTime ([ /system/clock/get date ] . " " . [ /system/clock/get time ]);
:local Vendor [ $GetMacVendor ($RegVal->"mac-address") ];
:local Message ("MAC address " . $RegVal->"mac-address" . " (" . $Vendor . ", " . $HostName . ") " . \
"first seen on " . $DateTime . " connected to SSID " . $RegVal->"ssid" . ", interface " . $RegVal->"interface");
$LogPrintExit2 info $0 $Message false;
/interface/wireless/access-list/add place-before=$PlaceBefore comment=$Message mac-address=($RegVal->"mac-address") disabled=yes;
$SendNotification2 ({ origin=$0; \
subject=([ $SymbolForNotification "mobile-phone" ] . $RegVal->"mac-address" . " connected to " . $RegVal->"ssid"); \
message=("A device with unknown MAC address connected to " . $RegVal->"ssid" . " on " . $Identity . ".\n\n" . \
"Controller: " . $Identity . "\n" . \
"Interface: " . $RegVal->"interface" . "\n" . \
"SSID: " . $RegVal->"ssid" . "\n" . \
"MAC: " . $RegVal->"mac-address" . "\n" . \
"Vendor: " . $Vendor . "\n" . \
"Hostname: " . $HostName . "\n" . \
"Address: " . $Address . "\n" . \
"DNS name: " . $DnsName . "\n" . \
"Date: " . $DateTime) });
}
} else={
$LogPrintExit2 debug $0 ("No mac address available... Ignoring.") false;
}
}
# dummy for migration

View file

@ -0,0 +1,86 @@
#!rsc by RouterOS
# RouterOS script: collect-wireless-mac.local
# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# collect wireless mac adresses in access list
# https://git.eworm.de/cgit/routeros-scripts/about/doc/collect-wireless-mac.md
#
# provides: lease-script, order=40
#
# !! Do not edit this file, it is generated from template!
:local 0 "collect-wireless-mac.local";
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:global Identity;
:global EitherOr;
:global GetMacVendor;
:global LogPrintExit2;
:global ScriptLock;
:global SendNotification2;
:global SymbolForNotification;
$ScriptLock $0 false 10;
:if ([ :len [ /interface/wireless/access-list/find where comment="--- collected above ---" disabled ] ] = 0) do={
/interface/wireless/access-list/add comment="--- collected above ---" disabled=yes;
$LogPrintExit2 warning $0 ("Added disabled access-list entry with comment '--- collected above ---'.") false;
}
:local PlaceBefore ([ /interface/wireless/access-list/find where comment="--- collected above ---" disabled ]->0);
:foreach Reg in=[ /interface/wireless/registration-table/find ] do={
:local RegVal;
:do {
:set RegVal [ /interface/wireless/registration-table/get $Reg ];
} on-error={
$LogPrintExit2 debug $0 ("Device already gone... Ignoring.") false;
}
:if ([ :len ($RegVal->"mac-address") ] > 0) do={
:local AccessList ([ /interface/wireless/access-list/find where mac-address=($RegVal->"mac-address") ]->0);
:if ([ :len $AccessList ] > 0) do={
$LogPrintExit2 debug $0 ("MAC address " . $RegVal->"mac-address" . " already known: " . \
[ /interface/wireless/access-list/get $AccessList comment ]) false;
}
:if ([ :len $AccessList ] = 0) do={
:local Address "no dhcp lease";
:local DnsName "no dhcp lease";
:local HostName "no dhcp lease";
:local Lease ([ /ip/dhcp-server/lease/find where mac-address=($RegVal->"mac-address") dynamic=yes status=bound ]->0);
:if ([ :len $Lease ] > 0) do={
:set Address [ /ip/dhcp-server/lease/get $Lease address ];
:set HostName [ $EitherOr [ /ip/dhcp-server/lease/get $Lease host-name ] "no hostname" ];
:set DnsName "no dns name";
:local DnsRec ([ /ip/dns/static/find where address=$Address ]->0);
:if ([ :len $DnsRec ] > 0) do={
:set DnsName [ /ip/dns/static/get $DnsRec name ];
}
}
:set ($RegVal->"ssid") [ /interface/wireless/get [ find where name=($RegVal->"interface") ] ssid ];
:local DateTime ([ /system/clock/get date ] . " " . [ /system/clock/get time ]);
:local Vendor [ $GetMacVendor ($RegVal->"mac-address") ];
:local Message ("MAC address " . $RegVal->"mac-address" . " (" . $Vendor . ", " . $HostName . ") " . \
"first seen on " . $DateTime . " connected to SSID " . $RegVal->"ssid" . ", interface " . $RegVal->"interface");
$LogPrintExit2 info $0 $Message false;
/interface/wireless/access-list/add place-before=$PlaceBefore comment=$Message mac-address=($RegVal->"mac-address") disabled=yes;
$SendNotification2 ({ origin=$0; \
subject=([ $SymbolForNotification "mobile-phone" ] . $RegVal->"mac-address" . " connected to " . $RegVal->"ssid"); \
message=("A device with unknown MAC address connected to " . $RegVal->"ssid" . " on " . $Identity . ".\n\n" . \
"Controller: " . $Identity . "\n" . \
"Interface: " . $RegVal->"interface" . "\n" . \
"SSID: " . $RegVal->"ssid" . "\n" . \
"MAC: " . $RegVal->"mac-address" . "\n" . \
"Vendor: " . $Vendor . "\n" . \
"Hostname: " . $HostName . "\n" . \
"Address: " . $Address . "\n" . \
"DNS name: " . $DnsName . "\n" . \
"Date: " . $DateTime) });
}
} else={
$LogPrintExit2 debug $0 ("No mac address available... Ignoring.") false;
}
}

View file

@ -1,96 +1,3 @@
#!rsc by RouterOS
# RouterOS script: daily-psk.capsman
# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
# Michael Gisbers <michael@gisbers.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# update daily PSK (pre shared key)
# https://git.eworm.de/cgit/routeros-scripts/about/doc/daily-psk.md
#
# !! Do not edit this file, it is generated from template!
:local 0 "daily-psk.capsman";
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:global DailyPskMatchComment;
:global DailyPskQrCodeUrl;
:global Identity;
:global LogPrintExit2;
:global SendNotification2;
:global SymbolForNotification;
:global UrlEncode;
:global WaitForFile;
:global WaitFullyConnected;
$WaitFullyConnected;
# return pseudo-random string for PSK
:local GeneratePSK do={
:local Date [ :tostr $1 ];
:global DailyPskSecrets;
:local Months { "jan"; "feb"; "mar"; "apr"; "may"; "jun";
"jul"; "aug"; "sep"; "oct"; "nov"; "dec" };
:local Month [ :pick $Date 0 3 ];
:local Day [ :tonum [ :pick $Date 4 6 ] ];
:local Year [ :pick $Date 7 11 ];
:for MIndex from=0 to=[ :len $Months ] do={
:if ($Months->$MIndex = $Month) do={
:set Month ($MIndex + 1);
}
}
:local A ((14 - $Month) / 12);
:local B ($Year - $A);
:local C ($Month + 12 * $A - 2);
:local WeekDay (7000 + $Day + $B + ($B / 4) - ($B / 100) + ($B / 400) + ((31 * $C) / 12));
:set WeekDay ($WeekDay - (($WeekDay / 7) * 7));
:return (($DailyPskSecrets->0->($Day - 1)) . \
($DailyPskSecrets->1->($Month - 1)) . \
($DailyPskSecrets->2->$WeekDay));
}
:local Seen ({});
:local Date [ /system/clock/get date ];
:local NewPsk [ $GeneratePSK $Date ];
:foreach AccList in=[ /caps-man/access-list/find where comment~$DailyPskMatchComment ] do={
:local SsidRegExp [ /caps-man/access-list/get $AccList ssid-regexp ];
:local Configuration ([ /caps-man/configuration/find where ssid~$SsidRegExp ]->0);
:local Ssid [ /caps-man/configuration/get $Configuration ssid ];
:local OldPsk [ /caps-man/access-list/get $AccList private-passphrase ];
:local Skip 0;
:if ($NewPsk != $OldPsk) do={
$LogPrintExit2 info $0 ("Updating daily PSK for " . $Ssid . " to " . $NewPsk . " (was " . $OldPsk . ")") false;
/caps-man/access-list/set $AccList private-passphrase=$NewPsk;
:if ([ :len [ /caps-man/actual-interface-configuration/find where configuration.ssid=$Ssid !disabled ] ] > 0) do={
:foreach SeenSsid in=$Seen do={
:if ($SeenSsid = $Ssid) do={
$LogPrintExit2 debug $0 ("Already sent a mail for SSID " . $Ssid . ", skipping.") false;
:set Skip 1;
}
}
:if ($Skip = 0) do={
:set Seen ($Seen, $Ssid);
:local Link ($DailyPskQrCodeUrl . \
"?scale=8&level=1&ssid=" . [ $UrlEncode $Ssid ] . "&pass=" . [ $UrlEncode $NewPsk ]);
$SendNotification2 ({ origin=$0; \
subject=([ $SymbolForNotification "calendar" ] . "daily PSK " . $Ssid); \
message=("This is the daily PSK on " . $Identity . ":\n\n" . \
"SSID: " . $Ssid . "\n" . \
"PSK: " . $NewPsk . "\n" . \
"Date: " . $Date . "\n\n" . \
"A client device specific rule must not exist!"); link=$Link });
}
}
}
}
# dummy for migration

96
daily-psk.capsman.rsc Normal file
View file

@ -0,0 +1,96 @@
#!rsc by RouterOS
# RouterOS script: daily-psk.capsman
# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
# Michael Gisbers <michael@gisbers.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# update daily PSK (pre shared key)
# https://git.eworm.de/cgit/routeros-scripts/about/doc/daily-psk.md
#
# !! Do not edit this file, it is generated from template!
:local 0 "daily-psk.capsman";
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:global DailyPskMatchComment;
:global DailyPskQrCodeUrl;
:global Identity;
:global LogPrintExit2;
:global SendNotification2;
:global SymbolForNotification;
:global UrlEncode;
:global WaitForFile;
:global WaitFullyConnected;
$WaitFullyConnected;
# return pseudo-random string for PSK
:local GeneratePSK do={
:local Date [ :tostr $1 ];
:global DailyPskSecrets;
:local Months { "jan"; "feb"; "mar"; "apr"; "may"; "jun";
"jul"; "aug"; "sep"; "oct"; "nov"; "dec" };
:local Month [ :pick $Date 0 3 ];
:local Day [ :tonum [ :pick $Date 4 6 ] ];
:local Year [ :pick $Date 7 11 ];
:for MIndex from=0 to=[ :len $Months ] do={
:if ($Months->$MIndex = $Month) do={
:set Month ($MIndex + 1);
}
}
:local A ((14 - $Month) / 12);
:local B ($Year - $A);
:local C ($Month + 12 * $A - 2);
:local WeekDay (7000 + $Day + $B + ($B / 4) - ($B / 100) + ($B / 400) + ((31 * $C) / 12));
:set WeekDay ($WeekDay - (($WeekDay / 7) * 7));
:return (($DailyPskSecrets->0->($Day - 1)) . \
($DailyPskSecrets->1->($Month - 1)) . \
($DailyPskSecrets->2->$WeekDay));
}
:local Seen ({});
:local Date [ /system/clock/get date ];
:local NewPsk [ $GeneratePSK $Date ];
:foreach AccList in=[ /caps-man/access-list/find where comment~$DailyPskMatchComment ] do={
:local SsidRegExp [ /caps-man/access-list/get $AccList ssid-regexp ];
:local Configuration ([ /caps-man/configuration/find where ssid~$SsidRegExp ]->0);
:local Ssid [ /caps-man/configuration/get $Configuration ssid ];
:local OldPsk [ /caps-man/access-list/get $AccList private-passphrase ];
:local Skip 0;
:if ($NewPsk != $OldPsk) do={
$LogPrintExit2 info $0 ("Updating daily PSK for " . $Ssid . " to " . $NewPsk . " (was " . $OldPsk . ")") false;
/caps-man/access-list/set $AccList private-passphrase=$NewPsk;
:if ([ :len [ /caps-man/actual-interface-configuration/find where configuration.ssid=$Ssid !disabled ] ] > 0) do={
:foreach SeenSsid in=$Seen do={
:if ($SeenSsid = $Ssid) do={
$LogPrintExit2 debug $0 ("Already sent a mail for SSID " . $Ssid . ", skipping.") false;
:set Skip 1;
}
}
:if ($Skip = 0) do={
:set Seen ($Seen, $Ssid);
:local Link ($DailyPskQrCodeUrl . \
"?scale=8&level=1&ssid=" . [ $UrlEncode $Ssid ] . "&pass=" . [ $UrlEncode $NewPsk ]);
$SendNotification2 ({ origin=$0; \
subject=([ $SymbolForNotification "calendar" ] . "daily PSK " . $Ssid); \
message=("This is the daily PSK on " . $Identity . ":\n\n" . \
"SSID: " . $Ssid . "\n" . \
"PSK: " . $NewPsk . "\n" . \
"Date: " . $Date . "\n\n" . \
"A client device specific rule must not exist!"); link=$Link });
}
}
}
}

View file

@ -1,95 +1,3 @@
#!rsc by RouterOS
# RouterOS script: daily-psk.local
# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
# Michael Gisbers <michael@gisbers.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# update daily PSK (pre shared key)
# https://git.eworm.de/cgit/routeros-scripts/about/doc/daily-psk.md
#
# !! Do not edit this file, it is generated from template!
:local 0 "daily-psk.local";
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:global DailyPskMatchComment;
:global DailyPskQrCodeUrl;
:global Identity;
:global LogPrintExit2;
:global SendNotification2;
:global SymbolForNotification;
:global UrlEncode;
:global WaitForFile;
:global WaitFullyConnected;
$WaitFullyConnected;
# return pseudo-random string for PSK
:local GeneratePSK do={
:local Date [ :tostr $1 ];
:global DailyPskSecrets;
:local Months { "jan"; "feb"; "mar"; "apr"; "may"; "jun";
"jul"; "aug"; "sep"; "oct"; "nov"; "dec" };
:local Month [ :pick $Date 0 3 ];
:local Day [ :tonum [ :pick $Date 4 6 ] ];
:local Year [ :pick $Date 7 11 ];
:for MIndex from=0 to=[ :len $Months ] do={
:if ($Months->$MIndex = $Month) do={
:set Month ($MIndex + 1);
}
}
:local A ((14 - $Month) / 12);
:local B ($Year - $A);
:local C ($Month + 12 * $A - 2);
:local WeekDay (7000 + $Day + $B + ($B / 4) - ($B / 100) + ($B / 400) + ((31 * $C) / 12));
:set WeekDay ($WeekDay - (($WeekDay / 7) * 7));
:return (($DailyPskSecrets->0->($Day - 1)) . \
($DailyPskSecrets->1->($Month - 1)) . \
($DailyPskSecrets->2->$WeekDay));
}
:local Seen ({});
:local Date [ /system/clock/get date ];
:local NewPsk [ $GeneratePSK $Date ];
:foreach AccList in=[ /interface/wireless/access-list/find where comment~$DailyPskMatchComment ] do={
:local IntName [ /interface/wireless/access-list/get $AccList interface ];
:local Ssid [ /interface/wireless/get $IntName ssid ];
:local OldPsk [ /interface/wireless/access-list/get $AccList private-pre-shared-key ];
:local Skip 0;
:if ($NewPsk != $OldPsk) do={
$LogPrintExit2 info $0 ("Updating daily PSK for " . $Ssid . " to " . $NewPsk . " (was " . $OldPsk . ")") false;
/interface/wireless/access-list/set $AccList private-pre-shared-key=$NewPsk;
:if ([ :len [ /interface/wireless/find where name=$IntName !disabled ] ] = 1) do={
:foreach SeenSsid in=$Seen do={
:if ($SeenSsid = $Ssid) do={
$LogPrintExit2 debug $0 ("Already sent a mail for SSID " . $Ssid . ", skipping.") false;
:set Skip 1;
}
}
:if ($Skip = 0) do={
:set Seen ($Seen, $Ssid);
:local Link ($DailyPskQrCodeUrl . \
"?scale=8&level=1&ssid=" . [ $UrlEncode $Ssid ] . "&pass=" . [ $UrlEncode $NewPsk ]);
$SendNotification2 ({ origin=$0; \
subject=([ $SymbolForNotification "calendar" ] . "daily PSK " . $Ssid); \
message=("This is the daily PSK on " . $Identity . ":\n\n" . \
"SSID: " . $Ssid . "\n" . \
"PSK: " . $NewPsk . "\n" . \
"Date: " . $Date . "\n\n" . \
"A client device specific rule must not exist!"); link=$Link });
}
}
}
}
# dummy for migration

95
daily-psk.local.rsc Normal file
View file

@ -0,0 +1,95 @@
#!rsc by RouterOS
# RouterOS script: daily-psk.local
# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
# Michael Gisbers <michael@gisbers.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# update daily PSK (pre shared key)
# https://git.eworm.de/cgit/routeros-scripts/about/doc/daily-psk.md
#
# !! Do not edit this file, it is generated from template!
:local 0 "daily-psk.local";
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:global DailyPskMatchComment;
:global DailyPskQrCodeUrl;
:global Identity;
:global LogPrintExit2;
:global SendNotification2;
:global SymbolForNotification;
:global UrlEncode;
:global WaitForFile;
:global WaitFullyConnected;
$WaitFullyConnected;
# return pseudo-random string for PSK
:local GeneratePSK do={
:local Date [ :tostr $1 ];
:global DailyPskSecrets;
:local Months { "jan"; "feb"; "mar"; "apr"; "may"; "jun";
"jul"; "aug"; "sep"; "oct"; "nov"; "dec" };
:local Month [ :pick $Date 0 3 ];
:local Day [ :tonum [ :pick $Date 4 6 ] ];
:local Year [ :pick $Date 7 11 ];
:for MIndex from=0 to=[ :len $Months ] do={
:if ($Months->$MIndex = $Month) do={
:set Month ($MIndex + 1);
}
}
:local A ((14 - $Month) / 12);
:local B ($Year - $A);
:local C ($Month + 12 * $A - 2);
:local WeekDay (7000 + $Day + $B + ($B / 4) - ($B / 100) + ($B / 400) + ((31 * $C) / 12));
:set WeekDay ($WeekDay - (($WeekDay / 7) * 7));
:return (($DailyPskSecrets->0->($Day - 1)) . \
($DailyPskSecrets->1->($Month - 1)) . \
($DailyPskSecrets->2->$WeekDay));
}
:local Seen ({});
:local Date [ /system/clock/get date ];
:local NewPsk [ $GeneratePSK $Date ];
:foreach AccList in=[ /interface/wireless/access-list/find where comment~$DailyPskMatchComment ] do={
:local IntName [ /interface/wireless/access-list/get $AccList interface ];
:local Ssid [ /interface/wireless/get $IntName ssid ];
:local OldPsk [ /interface/wireless/access-list/get $AccList private-pre-shared-key ];
:local Skip 0;
:if ($NewPsk != $OldPsk) do={
$LogPrintExit2 info $0 ("Updating daily PSK for " . $Ssid . " to " . $NewPsk . " (was " . $OldPsk . ")") false;
/interface/wireless/access-list/set $AccList private-pre-shared-key=$NewPsk;
:if ([ :len [ /interface/wireless/find where name=$IntName !disabled ] ] = 1) do={
:foreach SeenSsid in=$Seen do={
:if ($SeenSsid = $Ssid) do={
$LogPrintExit2 debug $0 ("Already sent a mail for SSID " . $Ssid . ", skipping.") false;
:set Skip 1;
}
}
:if ($Skip = 0) do={
:set Seen ($Seen, $Ssid);
:local Link ($DailyPskQrCodeUrl . \
"?scale=8&level=1&ssid=" . [ $UrlEncode $Ssid ] . "&pass=" . [ $UrlEncode $NewPsk ]);
$SendNotification2 ({ origin=$0; \
subject=([ $SymbolForNotification "calendar" ] . "daily PSK " . $Ssid); \
message=("This is the daily PSK on " . $Identity . ":\n\n" . \
"SSID: " . $Ssid . "\n" . \
"PSK: " . $NewPsk . "\n" . \
"Date: " . $Date . "\n\n" . \
"A client device specific rule must not exist!"); link=$Link });
}
}
}
}

View file

@ -1,30 +1,3 @@
#!rsc by RouterOS
# RouterOS script: dhcp-lease-comment.capsman
# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# provides: lease-script, order=60
#
# update dhcp-server lease comment with infos from access-list
# https://git.eworm.de/cgit/routeros-scripts/about/doc/dhcp-lease-comment.md
#
# !! Do not edit this file, it is generated from template!
:local 0 "dhcp-lease-comment.capsman";
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:global LogPrintExit2;
:foreach Lease in=[ /ip/dhcp-server/lease/find where dynamic=yes status=bound ] do={
:local LeaseVal [ /ip/dhcp-server/lease/get $Lease ];
:local NewComment;
:local AccessList ([ /caps-man/access-list/find where mac-address=($LeaseVal->"mac-address") ]->0);
:if ([ :len $AccessList ] > 0) do={
:set NewComment [ /caps-man/access-list/get $AccessList comment ];
}
:if ([ :len $NewComment ] != 0 && $LeaseVal->"comment" != $NewComment) do={
$LogPrintExit2 info $0 ("Updating comment for DHCP lease " . $LeaseVal->"mac-address" . ": " . $NewComment) false;
/ip/dhcp-server/lease/set comment=$NewComment $Lease;
}
}
# dummy for migration

View file

@ -0,0 +1,30 @@
#!rsc by RouterOS
# RouterOS script: dhcp-lease-comment.capsman
# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# provides: lease-script, order=60
#
# update dhcp-server lease comment with infos from access-list
# https://git.eworm.de/cgit/routeros-scripts/about/doc/dhcp-lease-comment.md
#
# !! Do not edit this file, it is generated from template!
:local 0 "dhcp-lease-comment.capsman";
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:global LogPrintExit2;
:foreach Lease in=[ /ip/dhcp-server/lease/find where dynamic=yes status=bound ] do={
:local LeaseVal [ /ip/dhcp-server/lease/get $Lease ];
:local NewComment;
:local AccessList ([ /caps-man/access-list/find where mac-address=($LeaseVal->"mac-address") ]->0);
:if ([ :len $AccessList ] > 0) do={
:set NewComment [ /caps-man/access-list/get $AccessList comment ];
}
:if ([ :len $NewComment ] != 0 && $LeaseVal->"comment" != $NewComment) do={
$LogPrintExit2 info $0 ("Updating comment for DHCP lease " . $LeaseVal->"mac-address" . ": " . $NewComment) false;
/ip/dhcp-server/lease/set comment=$NewComment $Lease;
}
}

View file

@ -1,30 +1,3 @@
#!rsc by RouterOS
# RouterOS script: dhcp-lease-comment.local
# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# provides: lease-script, order=60
#
# update dhcp-server lease comment with infos from access-list
# https://git.eworm.de/cgit/routeros-scripts/about/doc/dhcp-lease-comment.md
#
# !! Do not edit this file, it is generated from template!
:local 0 "dhcp-lease-comment.local";
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:global LogPrintExit2;
:foreach Lease in=[ /ip/dhcp-server/lease/find where dynamic=yes status=bound ] do={
:local LeaseVal [ /ip/dhcp-server/lease/get $Lease ];
:local NewComment;
:local AccessList ([ /interface/wireless/access-list/find where mac-address=($LeaseVal->"mac-address") ]->0);
:if ([ :len $AccessList ] > 0) do={
:set NewComment [ /interface/wireless/access-list/get $AccessList comment ];
}
:if ([ :len $NewComment ] != 0 && $LeaseVal->"comment" != $NewComment) do={
$LogPrintExit2 info $0 ("Updating comment for DHCP lease " . $LeaseVal->"mac-address" . ": " . $NewComment) false;
/ip/dhcp-server/lease/set comment=$NewComment $Lease;
}
}
# dummy for migration

View file

@ -0,0 +1,30 @@
#!rsc by RouterOS
# RouterOS script: dhcp-lease-comment.local
# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# provides: lease-script, order=60
#
# update dhcp-server lease comment with infos from access-list
# https://git.eworm.de/cgit/routeros-scripts/about/doc/dhcp-lease-comment.md
#
# !! Do not edit this file, it is generated from template!
:local 0 "dhcp-lease-comment.local";
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:global LogPrintExit2;
:foreach Lease in=[ /ip/dhcp-server/lease/find where dynamic=yes status=bound ] do={
:local LeaseVal [ /ip/dhcp-server/lease/get $Lease ];
:local NewComment;
:local AccessList ([ /interface/wireless/access-list/find where mac-address=($LeaseVal->"mac-address") ]->0);
:if ([ :len $AccessList ] > 0) do={
:set NewComment [ /interface/wireless/access-list/get $AccessList comment ];
}
:if ([ :len $NewComment ] != 0 && $LeaseVal->"comment" != $NewComment) do={
$LogPrintExit2 info $0 ("Updating comment for DHCP lease " . $LeaseVal->"mac-address" . ": " . $NewComment) false;
/ip/dhcp-server/lease/set comment=$NewComment $Lease;
}
}

View file

@ -1,97 +1,3 @@
#!rsc by RouterOS
# RouterOS script: dhcp-to-dns
# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# provides: lease-script, order=20
#
# check DHCP leases and add/remove/update DNS entries
# https://git.eworm.de/cgit/routeros-scripts/about/doc/dhcp-to-dns.md
:local 0 "dhcp-to-dns";
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:global Domain;
:global HostNameInZone;
:global Identity;
:global PrefixInZone;
:global ServerNameInZone;
:global CharacterReplace;
:global IfThenElse;
:global LogPrintExit2;
:global ScriptLock;
$ScriptLock $0 false 10;
:local Zone \
([ $IfThenElse ($PrefixInZone = true) "dhcp." ] . \
[ $IfThenElse ($HostNameInZone = true) ($Identity . ".") ] . $Domain);
:local Ttl 5m;
:local CommentPrefix ("managed by " . $0 . " for ");
:local CommentString ("--- " . $0 . " above ---");
:if ([ :len [ /ip/dns/static/find where comment=$CommentString name=- type=NXDOMAIN disabled ] ] = 0) do={
/ip/dns/static/add comment=$CommentString name=- type=NXDOMAIN disabled=yes;
$LogPrintExit2 warning $0 ("Added disabled static dns record with comment '" . $CommentString . "'.") false;
}
:local PlaceBefore ([ /ip/dns/static/find where comment=$CommentString name=- type=NXDOMAIN disabled ]->0);
:foreach DnsRecord in=[ /ip/dns/static/find where comment ~ $CommentPrefix ] do={
:local DnsRecordVal [ /ip/dns/static/get $DnsRecord ];
:local MacAddress [ $CharacterReplace ($DnsRecordVal->"comment") $CommentPrefix "" ];
:if ([ :len [ /ip/dhcp-server/lease/find where mac-address=$MacAddress address=($DnsRecordVal->"address") status=bound ] ] > 0) do={
$LogPrintExit2 debug $0 ("Lease for " . $MacAddress . " (" . $DnsRecordVal->"name" . ") still exists. Not deleting DNS entry.") false;
} else={
:local Found false;
$LogPrintExit2 info $0 ("Lease expired for " . $MacAddress . " (" . $DnsRecordVal->"name" . "), deleting DNS entry.") false;
/ip/dns/static/remove $DnsRecord;
}
}
:foreach Lease in=[ /ip/dhcp-server/lease/find where status=bound ] do={
:local LeaseVal;
:do {
:set LeaseVal [ /ip/dhcp-server/lease/get $Lease ];
} on-error={
$LogPrintExit2 debug $0 ("A lease just vanished, ignoring.") false;
}
:if ([ :len ($LeaseVal->"address") ] > 0) do={
:local Comment ($CommentPrefix . $LeaseVal->"mac-address");
:local HostName [ $IfThenElse ([ :len ($LeaseVal->"host-name") ] = 0) \
[ $CharacterReplace ($LeaseVal->"mac-address") ":" "-" ] \
[ $CharacterReplace ($LeaseVal->"host-name") " " "" ] ];
:local Fqdn ($HostName . "." . [ $IfThenElse ($ServerNameInZone = true) ($LeaseVal->"server" . ".") ] . $Zone);
:local DnsRecord [ /ip/dns/static/find where name=$Fqdn ];
:if ([ :len $DnsRecord ] > 0) do={
:local DnsIp [ /ip/dns/static/get $DnsRecord address ];
:local DupMacLeases [ /ip/dhcp-server/lease/find where mac-address=($LeaseVal->"mac-address") status=bound ];
:if ([ :len $DupMacLeases ] > 1) do={
:set ($LeaseVal->"address") [ /ip/dhcp-server/lease/get ($DupMacLeases->([ :len $DupMacLeases ] - 1)) address ];
}
:if ([ :len ($LeaseVal->"host-name") ] > 0) do={
:local HostNameLeases [ /ip/dhcp-server/lease/find where host-name=($LeaseVal->"host-name") status=bound ];
:if ([ :len $HostNameLeases ] > 1) do={
:set ($LeaseVal->"address") [ /ip/dhcp-server/lease/get ($HostNameLeases->0) address ];
}
}
:if ($DnsIp = $LeaseVal->"address") do={
$LogPrintExit2 debug $0 ("DNS entry for " . $Fqdn . " does not need updating.") false;
} else={
$LogPrintExit2 info $0 ("Replacing DNS entry for " . $Fqdn . ", new address is " . $LeaseVal->"address" . ".") false;
/ip/dns/static/set name=$Fqdn address=($LeaseVal->"address") ttl=$Ttl comment=$Comment $DnsRecord;
}
} else={
$LogPrintExit2 info $0 ("Adding new DNS entry for " . $Fqdn . ", address is " . $LeaseVal->"address" . ".") false;
/ip/dns/static/add name=$Fqdn address=($LeaseVal->"address") ttl=$Ttl comment=$Comment place-before=$PlaceBefore;
}
} else={
$LogPrintExit2 debug $0 ("No address available... Ignoring.") false;
}
}
# dummy for migration

97
dhcp-to-dns.rsc Normal file
View file

@ -0,0 +1,97 @@
#!rsc by RouterOS
# RouterOS script: dhcp-to-dns
# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# provides: lease-script, order=20
#
# check DHCP leases and add/remove/update DNS entries
# https://git.eworm.de/cgit/routeros-scripts/about/doc/dhcp-to-dns.md
:local 0 "dhcp-to-dns";
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:global Domain;
:global HostNameInZone;
:global Identity;
:global PrefixInZone;
:global ServerNameInZone;
:global CharacterReplace;
:global IfThenElse;
:global LogPrintExit2;
:global ScriptLock;
$ScriptLock $0 false 10;
:local Zone \
([ $IfThenElse ($PrefixInZone = true) "dhcp." ] . \
[ $IfThenElse ($HostNameInZone = true) ($Identity . ".") ] . $Domain);
:local Ttl 5m;
:local CommentPrefix ("managed by " . $0 . " for ");
:local CommentString ("--- " . $0 . " above ---");
:if ([ :len [ /ip/dns/static/find where comment=$CommentString name=- type=NXDOMAIN disabled ] ] = 0) do={
/ip/dns/static/add comment=$CommentString name=- type=NXDOMAIN disabled=yes;
$LogPrintExit2 warning $0 ("Added disabled static dns record with comment '" . $CommentString . "'.") false;
}
:local PlaceBefore ([ /ip/dns/static/find where comment=$CommentString name=- type=NXDOMAIN disabled ]->0);
:foreach DnsRecord in=[ /ip/dns/static/find where comment ~ $CommentPrefix ] do={
:local DnsRecordVal [ /ip/dns/static/get $DnsRecord ];
:local MacAddress [ $CharacterReplace ($DnsRecordVal->"comment") $CommentPrefix "" ];
:if ([ :len [ /ip/dhcp-server/lease/find where mac-address=$MacAddress address=($DnsRecordVal->"address") status=bound ] ] > 0) do={
$LogPrintExit2 debug $0 ("Lease for " . $MacAddress . " (" . $DnsRecordVal->"name" . ") still exists. Not deleting DNS entry.") false;
} else={
:local Found false;
$LogPrintExit2 info $0 ("Lease expired for " . $MacAddress . " (" . $DnsRecordVal->"name" . "), deleting DNS entry.") false;
/ip/dns/static/remove $DnsRecord;
}
}
:foreach Lease in=[ /ip/dhcp-server/lease/find where status=bound ] do={
:local LeaseVal;
:do {
:set LeaseVal [ /ip/dhcp-server/lease/get $Lease ];
} on-error={
$LogPrintExit2 debug $0 ("A lease just vanished, ignoring.") false;
}
:if ([ :len ($LeaseVal->"address") ] > 0) do={
:local Comment ($CommentPrefix . $LeaseVal->"mac-address");
:local HostName [ $IfThenElse ([ :len ($LeaseVal->"host-name") ] = 0) \
[ $CharacterReplace ($LeaseVal->"mac-address") ":" "-" ] \
[ $CharacterReplace ($LeaseVal->"host-name") " " "" ] ];
:local Fqdn ($HostName . "." . [ $IfThenElse ($ServerNameInZone = true) ($LeaseVal->"server" . ".") ] . $Zone);
:local DnsRecord [ /ip/dns/static/find where name=$Fqdn ];
:if ([ :len $DnsRecord ] > 0) do={
:local DnsIp [ /ip/dns/static/get $DnsRecord address ];
:local DupMacLeases [ /ip/dhcp-server/lease/find where mac-address=($LeaseVal->"mac-address") status=bound ];
:if ([ :len $DupMacLeases ] > 1) do={
:set ($LeaseVal->"address") [ /ip/dhcp-server/lease/get ($DupMacLeases->([ :len $DupMacLeases ] - 1)) address ];
}
:if ([ :len ($LeaseVal->"host-name") ] > 0) do={
:local HostNameLeases [ /ip/dhcp-server/lease/find where host-name=($LeaseVal->"host-name") status=bound ];
:if ([ :len $HostNameLeases ] > 1) do={
:set ($LeaseVal->"address") [ /ip/dhcp-server/lease/get ($HostNameLeases->0) address ];
}
}
:if ($DnsIp = $LeaseVal->"address") do={
$LogPrintExit2 debug $0 ("DNS entry for " . $Fqdn . " does not need updating.") false;
} else={
$LogPrintExit2 info $0 ("Replacing DNS entry for " . $Fqdn . ", new address is " . $LeaseVal->"address" . ".") false;
/ip/dns/static/set name=$Fqdn address=($LeaseVal->"address") ttl=$Ttl comment=$Comment $DnsRecord;
}
} else={
$LogPrintExit2 info $0 ("Adding new DNS entry for " . $Fqdn . ", address is " . $LeaseVal->"address" . ".") false;
/ip/dns/static/add name=$Fqdn address=($LeaseVal->"address") ttl=$Ttl comment=$Comment place-before=$PlaceBefore;
}
} else={
$LogPrintExit2 debug $0 ("No address available... Ignoring.") false;
}
}

View file

@ -28,8 +28,8 @@ The optional configuration goes to `global-config-overlay`.
* `ScriptRunOnceUrlSuffix`: url suffix, appended to parameter
If the parameter passed to the function is not a complete URL (starting
with protocol `ftp://`, `http://`, `https://` or `sftp://`) the values are
prepended and appended.
with protocol `ftp://`, `http://`, `https://` or `sftp://`) the base-url is
prepended, and file extension `.rsc` and url-suffix are appended.
Usage and invocation
--------------------

View file

@ -1,42 +1,3 @@
#!rsc by RouterOS
# RouterOS script: firmware-upgrade-reboot
# Copyright (c) 2022-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# install firmware upgrade, and reboot
# https://git.eworm.de/cgit/routeros-scripts/about/doc/firmware-upgrade-reboot.md
:local 0 "firmware-upgrade-reboot";
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:global LogPrintExit2;
:global VersionToNum;
:local RouterBoard [ /system/routerboard/get ];
:if ($RouterBoard->"current-firmware" = $RouterBoard->"upgrade-firmware") do={
$LogPrintExit2 info $0 ("Current and upgrade firmware match with version " . \
$RouterBoard->"current-firmware" . ".") true;
}
:if ([ $VersionToNum ($RouterBoard->"current-firmware") ] > [ $VersionToNum ($RouterBoard->"upgrade-firmware") ]) do={
$LogPrintExit2 info $0 ("Different firmware version is available, but it is a downgrade. Ignoring.") true;
}
:if ([ /system/routerboard/settings/get auto-upgrade ] = false) do={
$LogPrintExit2 info $0 ("Firmware version " . $RouterBoard->"upgrade-firmware" . \
" is available, upgrading.") false;
/system/routerboard/upgrade;
}
:while ([ :len [ /log/find where topics=({"system";"info";"critical"}) \
message="Firmware upgraded successfully, please reboot for changes to take effect!" ] ] = 0) do={
:delay 1s;
}
:local Uptime [ /system/resource/get uptime ];
:if ($Uptime < 1m) do={
:delay $Uptime;
}
$LogPrintExit2 info $0 ("Firmware upgrade successful, rebooting.") false;
/system/reboot;
# dummy for migration

View file

@ -0,0 +1,42 @@
#!rsc by RouterOS
# RouterOS script: firmware-upgrade-reboot
# Copyright (c) 2022-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# install firmware upgrade, and reboot
# https://git.eworm.de/cgit/routeros-scripts/about/doc/firmware-upgrade-reboot.md
:local 0 "firmware-upgrade-reboot";
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:global LogPrintExit2;
:global VersionToNum;
:local RouterBoard [ /system/routerboard/get ];
:if ($RouterBoard->"current-firmware" = $RouterBoard->"upgrade-firmware") do={
$LogPrintExit2 info $0 ("Current and upgrade firmware match with version " . \
$RouterBoard->"current-firmware" . ".") true;
}
:if ([ $VersionToNum ($RouterBoard->"current-firmware") ] > [ $VersionToNum ($RouterBoard->"upgrade-firmware") ]) do={
$LogPrintExit2 info $0 ("Different firmware version is available, but it is a downgrade. Ignoring.") true;
}
:if ([ /system/routerboard/settings/get auto-upgrade ] = false) do={
$LogPrintExit2 info $0 ("Firmware version " . $RouterBoard->"upgrade-firmware" . \
" is available, upgrading.") false;
/system/routerboard/upgrade;
}
:while ([ :len [ /log/find where topics=({"system";"info";"critical"}) \
message="Firmware upgraded successfully, please reboot for changes to take effect!" ] ] = 0) do={
:delay 1s;
}
:local Uptime [ /system/resource/get uptime ];
:if ($Uptime < 1m) do={
:delay $Uptime;
}
$LogPrintExit2 info $0 ("Firmware upgrade successful, rebooting.") false;
/system/reboot;

View file

@ -1,220 +1,3 @@
#!rsc by RouterOS
# RouterOS script: global-config
# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# global configuration
# https://git.eworm.de/cgit/routeros-scripts/about/
# Set this to 'true' to disable news and change notifications.
:global NoNewsAndChangesNotification false;
# Add extra text (or emojis) in notification tags.
:global IdentityExtra "";
# This is used for DNS and backup file.
:global Domain "example.com";
:global HostNameInZone true;
:global PrefixInZone true;
:global ServerNameInZone false;
# You can send e-mail notifications. Configure the system's mail settings
# (/tool/e-mail), then install the module:
# $ScriptInstallUpdate mod/notification-email
# The to-address needs to be filled; cc-address can be empty, one address
# or a comma separated list of addresses.
:global EmailGeneralTo "";
:global EmailGeneralCc "";
#:global EmailGeneralTo "mail@example.com";
#:global EmailGeneralCc "another@example.com,third@example.com";
# You can send Telegram notifications. Register a bot
# and add the token and chat ids here, then install the module:
# $ScriptInstallUpdate mod/notification-telegram
:global TelegramTokenId "";
:global TelegramChatId "";
#:global TelegramTokenId "123456:ABCDEF-GHI";
#:global TelegramChatId "12345678";
# Using telegram-chat you have to define trusted chat ids (not group ids!)
# or user names. Groups allow to chat with devices simultaneously.
#:global TelegramChatIdsTrusted {
# "12345678";
# "example_user";
#};
:global TelegramChatGroups "(all)";
#:global TelegramChatGroups "(all|home|office)";
# This is whether or not to send Telegram messages with fixed-width font.
:global TelegramFixedWidthFont true;
# You can send Matrix notifications. Configure these settings and
# install the module:
# $ScriptInstallUpdate mod/notification-matrix
:global MatrixHomeServer "";
:global MatrixAccessToken "";
:global MatrixRoom "";
#:global MatrixHomeServer "matrix.org";
#:global MatrixAccessToken "123456ABCDEFGHI...";
#:global MatrixRoom "!example:matrix.org";
# It is possible to override e-mail, Telegram and Matrix setting for every
# script. This is done in arrays, where 'Override' is appended to the
# variable name, like this:
#:global EmailGeneralToOverride {
# "check-certificates"="override@example.com";
# "backup-email"="backup@example.com";
#}
# Toggle this to disable symbols in notifications.
:global NotificationsWithSymbols true;
# Toggle this to disable color output in terminal/cli.
:global TerminalColorOutput true;
# This defines what backups to generate and what password to use.
:global BackupSendBinary false;
:global BackupSendExport true;
:global BackupSendGlobalConfig true;
:global BackupPassword "v3ry-s3cr3t";
:global BackupRandomDelay 0;
# These credentials are used to upload backup and config export files.
# SFTP authentication is tricky, you may have to limit authentication
# methods for your SSH server.
:global BackupUploadUrl "sftp://example.com/backup/";
:global BackupUploadUser "mikrotik";
:global BackupUploadPass "v3ry-s3cr3t";
# This defines what log messages to filter or include by topic or message
# text. Regular expressions are supported. Do *NOT* set an empty string,
# that will filter or include everything!
# These are filters, so excluding messages from forwarding.
:global LogForwardFilter "(debug|info)";
:global LogForwardFilterMessage [];
#:global LogForwardFilterMessage "message text";
#:global LogForwardFilterMessage "(message text|another text|...)";
# ... and another setting with reverse logic. This includes messages even
# if filtered above.
:global LogForwardInclude [];
:global LogForwardIncludeMessage [];
#:global LogForwardInclude "account";
#:global LogForwardIncludeMessage "message text";
# Specify an address to enable auto update to version assumed safe.
# The configured channel (bugfix, current, release-candidate) is appended.
:global SafeUpdateUrl "";
#:global SafeUpdateUrl "https://example.com/ros/safe-update/";
# Allow to install patch updates automatically.
:global SafeUpdatePatch false;
# Allow to install updates automatically if seen in neighbor list.
:global SafeUpdateNeighbor false;
# Install *ALL* updates automatically!
# Set to all upper-case "Yes, please!" to enable.
:global SafeUpdateAll "no";
# These thresholds control when to send health notification
# on temperature and voltage.
:global CheckHealthTemperature {
temperature=50;
cpu-temperature=70;
board-temperature1=50;
board-temperature2=50;
}
# This is deviation on recovery threshold against notification flooding.
:global CheckHealthTemperatureDeviation 3;
:global CheckHealthVoltageLow 115;
:global CheckHealthVoltagePercent 10;
# Access-list entries matching this comment are updated
# with daily pseudo-random PSK.
:global DailyPskMatchComment "Daily PSK";
:global DailyPskQrCodeUrl "https://www.eworm.de/cgi-bin/cqrlogo-wifi.cgi";
:global DailyPskSecrets {
{ "Abusive"; "Aggressive"; "Bored"; "Chemical"; "Cold";
"Cruel"; "Curved"; "Delightful"; "Discreet"; "Elite";
"Evasive"; "Faded"; "Flat"; "Future"; "Grandiose";
"Hanging"; "Humorous"; "Interesting"; "Magenta";
"Magnificent"; "Numerous"; "Optimal"; "Pathetic";
"Possessive"; "Remarkable"; "Rightful"; "Ruthless";
"Stale"; "Unusual"; "Useless"; "Various" };
{ "Adhesive"; "Amusing"; "Astonishing"; "Frantic";
"Kindhearted"; "Limping"; "Roasted"; "Robust";
"Staking"; "Thundering"; "Ultra"; "Unreal" };
{ "Belief"; "Button"; "Curtain"; "Edge"; "Jewel";
"String"; "Whistle" }
}
# Run different commands with multiple mode-button presses.
:global ModeButton {
1="/system/script/run leds-toggle-mode;";
2=":global Identity; :global SendNotification; :global SymbolForNotification; \$SendNotification ([ \$SymbolForNotification \"earth\" ] . \"Hello...\") (\"Hello world, \" . \$Identity . \" calling!\");";
3="/system/shutdown;";
4="/system/reboot;";
5=":global BridgePortVlan; \$BridgePortVlan alt;";
# add more here...
};
# This led gives visual feedback if type is 'on' or 'off'.
:global ModeButtonLED "user-led";
# Run commands on SMS action.
:global SmsAction {
bridge-port-vlan-alt=":global BridgePortVlan; \$BridgePortVlan alt;";
reboot="/system/reboot;";
shutdown="/system/shutdown;";
# add more here...
};
# Run commands by hooking into SMS forward.
:global SmsForwardHooks {
{ match="magic string";
allowed-number="12345678";
command="/system/script/run ..." };
# add more here...
};
# This is the address used to send gps data to.
:global GpsTrackUrl "https://example.com/index.php";
# Enable this to fetch scripts from given url.
:global ScriptUpdatesFetch true;
:global ScriptUpdatesBaseUrl "https://git.eworm.de/cgit/routeros-scripts/plain/";
# alternative urls - main: stable code - next: currently in development
#:global ScriptUpdatesBaseUrl "https://raw.githubusercontent.com/eworm-de/routeros-scripts/main/";
#:global ScriptUpdatesBaseUrl "https://raw.githubusercontent.com/eworm-de/routeros-scripts/next/";
#:global ScriptUpdatesBaseUrl "https://gitlab.com/eworm-de/routeros-scripts/raw/main/";
#:global ScriptUpdatesBaseUrl "https://gitlab.com/eworm-de/routeros-scripts/raw/next/";
:global ScriptUpdatesUrlSuffix "";
# use next branch with default url (git.eworm.de)
#:global ScriptUpdatesUrlSuffix "\?h=next";
# Use this for defaults with $ScriptRunOnce
# Install module with:
# $ScriptInstallUpdate mod/scriptrunonce
:global ScriptRunOnceBaseUrl "";
:global ScriptRunOnceUrlSuffix "";
# This project is developed in private spare time and usage is free of charge
# for you. If you like the scripts and think this is of value for you or your
# business please consider a donation:
# https://git.eworm.de/cgit/routeros-scripts/about/#donate
# Enable this to silence donation hint.
:global IDonate false;
# Use this for certificate auto-renew
:global CertRenewUrl "";
#:global CertRenewUrl "https://example.com/certificates/";
:global CertRenewTime 3w;
:global CertRenewPass {
"v3ry-s3cr3t";
"4n0th3r-s3cr3t";
}
:global CertWarnTime 2w;
:global CertIssuedExportPass {
"cert1-cn"="v3ry-s3cr3t";
"cert2-cn"="4n0th3r-s3cr3t";
}
# load custom settings from overlay
# Warning: Do *NOT* copy this code to overlay!
:do {
/system/script/run global-config-overlay;
} on-error={
:log error ("Loading configuration from overlay failed!");
}
# dummy for migration

View file

@ -103,6 +103,7 @@
92="Made qr-code url configurable for 'daily-psk'.";
93="Added support to backup global-config-overlay in 'backup-email' and 'backup-upload'.";
94="Added support for host addresses in address-list for 'ipv6-update'.";
95="Renamed script files in repository, running migration. No user interaction is required.";
};
# Migration steps to be applied on script updates
@ -118,4 +119,5 @@
81=":global NtpPool; :if ([ :len [ /system/script/find where name=\"rotate-ntp\" ] ] > 0) do={ /system/script/remove [ find where name=\"rotate-ntp\" ]; /system/scheduler/remove [ find where name=\"rotate-ntp\" ]; /system/ntp/client/set servers=\$NtpPool; };";
82=":global CharacterReplace; :foreach Netwatch in=[ /tool/netwatch/find where comment~\"notify\" !disabled ] do={ /tool/netwatch/set \$Netwatch comment=[ \$CharacterReplace [ get \$Netwatch comment ] \"hostname=\" \"name=\" ]; };";
84=":global ScriptInstallUpdate; :global EmailGeneralTo; :if ([ /tool/e-mail/get address ] != \"0.0.0.0\" && [ :len \$EmailGeneralTo ] > 0) do={ \$ScriptInstallUpdate mod/notification-email; }";
95=":global ScriptInstallUpdate; :global CharacterReplace; :foreach Script in=[ /system/script/find where name~\"\\\\.rsc\\\$\" source~\"^#!rsc by RouterOS\\n\" ] do={ /system/script/set \$Script name=[ \$CharacterReplace [ get \$Script name ] \".rsc\" \"\" ]; }; \$ScriptInstallUpdate;";
};

220
global-config.rsc Normal file
View file

@ -0,0 +1,220 @@
#!rsc by RouterOS
# RouterOS script: global-config
# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# global configuration
# https://git.eworm.de/cgit/routeros-scripts/about/
# Set this to 'true' to disable news and change notifications.
:global NoNewsAndChangesNotification false;
# Add extra text (or emojis) in notification tags.
:global IdentityExtra "";
# This is used for DNS and backup file.
:global Domain "example.com";
:global HostNameInZone true;
:global PrefixInZone true;
:global ServerNameInZone false;
# You can send e-mail notifications. Configure the system's mail settings
# (/tool/e-mail), then install the module:
# $ScriptInstallUpdate mod/notification-email
# The to-address needs to be filled; cc-address can be empty, one address
# or a comma separated list of addresses.
:global EmailGeneralTo "";
:global EmailGeneralCc "";
#:global EmailGeneralTo "mail@example.com";
#:global EmailGeneralCc "another@example.com,third@example.com";
# You can send Telegram notifications. Register a bot
# and add the token and chat ids here, then install the module:
# $ScriptInstallUpdate mod/notification-telegram
:global TelegramTokenId "";
:global TelegramChatId "";
#:global TelegramTokenId "123456:ABCDEF-GHI";
#:global TelegramChatId "12345678";
# Using telegram-chat you have to define trusted chat ids (not group ids!)
# or user names. Groups allow to chat with devices simultaneously.
#:global TelegramChatIdsTrusted {
# "12345678";
# "example_user";
#};
:global TelegramChatGroups "(all)";
#:global TelegramChatGroups "(all|home|office)";
# This is whether or not to send Telegram messages with fixed-width font.
:global TelegramFixedWidthFont true;
# You can send Matrix notifications. Configure these settings and
# install the module:
# $ScriptInstallUpdate mod/notification-matrix
:global MatrixHomeServer "";
:global MatrixAccessToken "";
:global MatrixRoom "";
#:global MatrixHomeServer "matrix.org";
#:global MatrixAccessToken "123456ABCDEFGHI...";
#:global MatrixRoom "!example:matrix.org";
# It is possible to override e-mail, Telegram and Matrix setting for every
# script. This is done in arrays, where 'Override' is appended to the
# variable name, like this:
#:global EmailGeneralToOverride {
# "check-certificates"="override@example.com";
# "backup-email"="backup@example.com";
#}
# Toggle this to disable symbols in notifications.
:global NotificationsWithSymbols true;
# Toggle this to disable color output in terminal/cli.
:global TerminalColorOutput true;
# This defines what backups to generate and what password to use.
:global BackupSendBinary false;
:global BackupSendExport true;
:global BackupSendGlobalConfig true;
:global BackupPassword "v3ry-s3cr3t";
:global BackupRandomDelay 0;
# These credentials are used to upload backup and config export files.
# SFTP authentication is tricky, you may have to limit authentication
# methods for your SSH server.
:global BackupUploadUrl "sftp://example.com/backup/";
:global BackupUploadUser "mikrotik";
:global BackupUploadPass "v3ry-s3cr3t";
# This defines what log messages to filter or include by topic or message
# text. Regular expressions are supported. Do *NOT* set an empty string,
# that will filter or include everything!
# These are filters, so excluding messages from forwarding.
:global LogForwardFilter "(debug|info)";
:global LogForwardFilterMessage [];
#:global LogForwardFilterMessage "message text";
#:global LogForwardFilterMessage "(message text|another text|...)";
# ... and another setting with reverse logic. This includes messages even
# if filtered above.
:global LogForwardInclude [];
:global LogForwardIncludeMessage [];
#:global LogForwardInclude "account";
#:global LogForwardIncludeMessage "message text";
# Specify an address to enable auto update to version assumed safe.
# The configured channel (bugfix, current, release-candidate) is appended.
:global SafeUpdateUrl "";
#:global SafeUpdateUrl "https://example.com/ros/safe-update/";
# Allow to install patch updates automatically.
:global SafeUpdatePatch false;
# Allow to install updates automatically if seen in neighbor list.
:global SafeUpdateNeighbor false;
# Install *ALL* updates automatically!
# Set to all upper-case "Yes, please!" to enable.
:global SafeUpdateAll "no";
# These thresholds control when to send health notification
# on temperature and voltage.
:global CheckHealthTemperature {
temperature=50;
cpu-temperature=70;
board-temperature1=50;
board-temperature2=50;
}
# This is deviation on recovery threshold against notification flooding.
:global CheckHealthTemperatureDeviation 3;
:global CheckHealthVoltageLow 115;
:global CheckHealthVoltagePercent 10;
# Access-list entries matching this comment are updated
# with daily pseudo-random PSK.
:global DailyPskMatchComment "Daily PSK";
:global DailyPskQrCodeUrl "https://www.eworm.de/cgi-bin/cqrlogo-wifi.cgi";
:global DailyPskSecrets {
{ "Abusive"; "Aggressive"; "Bored"; "Chemical"; "Cold";
"Cruel"; "Curved"; "Delightful"; "Discreet"; "Elite";
"Evasive"; "Faded"; "Flat"; "Future"; "Grandiose";
"Hanging"; "Humorous"; "Interesting"; "Magenta";
"Magnificent"; "Numerous"; "Optimal"; "Pathetic";
"Possessive"; "Remarkable"; "Rightful"; "Ruthless";
"Stale"; "Unusual"; "Useless"; "Various" };
{ "Adhesive"; "Amusing"; "Astonishing"; "Frantic";
"Kindhearted"; "Limping"; "Roasted"; "Robust";
"Staking"; "Thundering"; "Ultra"; "Unreal" };
{ "Belief"; "Button"; "Curtain"; "Edge"; "Jewel";
"String"; "Whistle" }
}
# Run different commands with multiple mode-button presses.
:global ModeButton {
1="/system/script/run leds-toggle-mode;";
2=":global Identity; :global SendNotification; :global SymbolForNotification; \$SendNotification ([ \$SymbolForNotification \"earth\" ] . \"Hello...\") (\"Hello world, \" . \$Identity . \" calling!\");";
3="/system/shutdown;";
4="/system/reboot;";
5=":global BridgePortVlan; \$BridgePortVlan alt;";
# add more here...
};
# This led gives visual feedback if type is 'on' or 'off'.
:global ModeButtonLED "user-led";
# Run commands on SMS action.
:global SmsAction {
bridge-port-vlan-alt=":global BridgePortVlan; \$BridgePortVlan alt;";
reboot="/system/reboot;";
shutdown="/system/shutdown;";
# add more here...
};
# Run commands by hooking into SMS forward.
:global SmsForwardHooks {
{ match="magic string";
allowed-number="12345678";
command="/system/script/run ..." };
# add more here...
};
# This is the address used to send gps data to.
:global GpsTrackUrl "https://example.com/index.php";
# Enable this to fetch scripts from given url.
:global ScriptUpdatesFetch true;
:global ScriptUpdatesBaseUrl "https://git.eworm.de/cgit/routeros-scripts/plain/";
# alternative urls - main: stable code - next: currently in development
#:global ScriptUpdatesBaseUrl "https://raw.githubusercontent.com/eworm-de/routeros-scripts/main/";
#:global ScriptUpdatesBaseUrl "https://raw.githubusercontent.com/eworm-de/routeros-scripts/next/";
#:global ScriptUpdatesBaseUrl "https://gitlab.com/eworm-de/routeros-scripts/raw/main/";
#:global ScriptUpdatesBaseUrl "https://gitlab.com/eworm-de/routeros-scripts/raw/next/";
:global ScriptUpdatesUrlSuffix "";
# use next branch with default url (git.eworm.de)
#:global ScriptUpdatesUrlSuffix "\?h=next";
# Use this for defaults with $ScriptRunOnce
# Install module with:
# $ScriptInstallUpdate mod/scriptrunonce
:global ScriptRunOnceBaseUrl "";
:global ScriptRunOnceUrlSuffix "";
# This project is developed in private spare time and usage is free of charge
# for you. If you like the scripts and think this is of value for you or your
# business please consider a donation:
# https://git.eworm.de/cgit/routeros-scripts/about/#donate
# Enable this to silence donation hint.
:global IDonate false;
# Use this for certificate auto-renew
:global CertRenewUrl "";
#:global CertRenewUrl "https://example.com/certificates/";
:global CertRenewTime 3w;
:global CertRenewPass {
"v3ry-s3cr3t";
"4n0th3r-s3cr3t";
}
:global CertWarnTime 2w;
:global CertIssuedExportPass {
"cert1-cn"="v3ry-s3cr3t";
"cert2-cn"="4n0th3r-s3cr3t";
}
# load custom settings from overlay
# Warning: Do *NOT* copy this code to overlay!
:do {
/system/script/run global-config-overlay;
} on-error={
:log error ("Loading configuration from overlay failed!");
}

File diff suppressed because it is too large Load diff

1292
global-functions.rsc Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,11 +1,3 @@
#!rsc by RouterOS
# RouterOS script: global-wait
# Copyright (c) 2020-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# wait for global-functions to finish
# https://git.eworm.de/cgit/routeros-scripts/about/doc/global-wait.md
:local 0 "global-wait";
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
# dummy for migration

11
global-wait.rsc Normal file
View file

@ -0,0 +1,11 @@
#!rsc by RouterOS
# RouterOS script: global-wait
# Copyright (c) 2020-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# wait for global-functions to finish
# https://git.eworm.de/cgit/routeros-scripts/about/doc/global-wait.md
:local 0 "global-wait";
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }

View file

@ -1,34 +1,3 @@
#!rsc by RouterOS
# RouterOS script: gps-track
# Copyright (c) 2018-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# track gps data by sending json data to http server
# https://git.eworm.de/cgit/routeros-scripts/about/doc/gps-track.md
:local 0 "gps-track";
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:global GpsTrackUrl;
:global Identity;
:global LogPrintExit2;
:local CoordinateFormat [ /system/gps/get coordinate-format ];
:local Gps [ /system/gps/monitor once as-value ];
:if ($Gps->"valid" = true) do={
/tool/fetch check-certificate=yes-without-crl $GpsTrackUrl output=none \
http-method=post http-header-field="Content-Type: application/json" \
http-data=("{" . \
"\"lat\":\"" . ($Gps->"latitude") . "\"," . \
"\"lon\":\"" . ($Gps->"longitude") . "\"," . \
"\"identity\":\"" . $Identity . "\"" . \
"}") as-value;
$LogPrintExit2 debug $0 ("Sending GPS data in " . $CoordinateFormat . " format: " . \
"lat: " . ($Gps->"latitude") . " " . \
"lon: " . ($Gps->"longitude")) false;
} else={
$LogPrintExit2 debug $0 ("GPS data not valid.") false;
}
# dummy for migration

34
gps-track.rsc Normal file
View file

@ -0,0 +1,34 @@
#!rsc by RouterOS
# RouterOS script: gps-track
# Copyright (c) 2018-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# track gps data by sending json data to http server
# https://git.eworm.de/cgit/routeros-scripts/about/doc/gps-track.md
:local 0 "gps-track";
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:global GpsTrackUrl;
:global Identity;
:global LogPrintExit2;
:local CoordinateFormat [ /system/gps/get coordinate-format ];
:local Gps [ /system/gps/monitor once as-value ];
:if ($Gps->"valid" = true) do={
/tool/fetch check-certificate=yes-without-crl $GpsTrackUrl output=none \
http-method=post http-header-field="Content-Type: application/json" \
http-data=("{" . \
"\"lat\":\"" . ($Gps->"latitude") . "\"," . \
"\"lon\":\"" . ($Gps->"longitude") . "\"," . \
"\"identity\":\"" . $Identity . "\"" . \
"}") as-value;
$LogPrintExit2 debug $0 ("Sending GPS data in " . $CoordinateFormat . " format: " . \
"lat: " . ($Gps->"latitude") . " " . \
"lon: " . ($Gps->"longitude")) false;
} else={
$LogPrintExit2 debug $0 ("GPS data not valid.") false;
}

View file

@ -1,72 +1,3 @@
#!rsc by RouterOS
# RouterOS script: hotspot-to-wpa
# Copyright (c) 2019-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# add private WPA passphrase after hotspot login
# https://git.eworm.de/cgit/routeros-scripts/about/doc/hotspot-to-wpa.md
:local 0 "hotspot-to-wpa";
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:global EitherOr;
:global LogPrintExit2;
:global ParseKeyValueStore;
:local MacAddress $"mac-address";
:local UserName $username;
:local Date [ /system/clock/get date ];
:local UserVal [ /ip/hotspot/user/get [ find where name=$UserName ] ];
:local UserInfo [ $ParseKeyValueStore ($UserVal->"comment") ];
:local Hotspot [ /ip/hotspot/host/get [ find where mac-address=$MacAddress authorized ] server ];
:if ([ :len [ /caps-man/access-list/find where comment="--- hotspot-to-wpa above ---" disabled ] ] = 0) do={
/caps-man/access-list/add comment="--- hotspot-to-wpa above ---" disabled=yes;
$LogPrintExit2 warning $0 ("Added disabled access-list entry with comment '--- hotspot-to-wpa above ---'.") false;
}
:local PlaceBefore ([ /caps-man/access-list/find where comment="--- hotspot-to-wpa above ---" disabled ]->0);
:if ([ :len [ /caps-man/access-list/find where \
comment=("hotspot-to-wpa template " . $Hotspot) disabled ] ] = 0) do={
/caps-man/access-list/add comment=("hotspot-to-wpa template " . $Hotspot) disabled=yes place-before=$PlaceBefore;
$LogPrintExit2 warning $0 ("Added template in access-list for hotspot '" . $Hotspot . "'.") false;
}
:local Template [ /caps-man/access-list/get ([ find where \
comment=("hotspot-to-wpa template " . $Hotspot) disabled ]->0) ];
:if ($Template->"action" = "reject") do={
$LogPrintExit2 info $0 ("Ignoring login for hotspot '" . $Hotspot . "'.") true;
}
# allow login page to load
:delay 1s;
$LogPrintExit2 info $0 ("Adding/updating access-list entry for mac address " . $MacAddress . \
" (user " . $UserName . ").") false;
/caps-man/access-list/remove [ find where mac-address=$MacAddress comment~"^hotspot-to-wpa: " ];
/caps-man/access-list/add comment=("hotspot-to-wpa: " . $UserName . ", " . $MacAddress . ", " . $Date) \
mac-address=$MacAddress private-passphrase=($UserVal->"password") ssid-regexp="-wpa\$" place-before=$PlaceBefore;
:local Entry [ /caps-man/access-list/find where mac-address=$MacAddress \
comment=("hotspot-to-wpa: " . $UserName . ", " . $MacAddress . ", " . $Date) ];
:local PrivatePassphrase [ $EitherOr ($UserInfo->"private-passphrase") ($Template->"private-passphrase") ];
:if ([ :len $PrivatePassphrase ] > 0) do={
:if ($PrivatePassphrase = "ignore") do={
/caps-man/access-list/set $Entry !private-passphrase;
} else={
/caps-man/access-list/set $Entry private-passphrase=$PrivatePassphrase;
}
}
:local SsidRegexp [ $EitherOr ($UserInfo->"ssid-regexp") ($Template->"ssid-regexp") ];
:if ([ :len $SsidRegexp ] > 0) do={
/caps-man/access-list/set $Entry ssid-regexp=$SsidRegexp;
}
:local VlanId [ $EitherOr ($UserInfo->"vlan-id") ($Template->"vlan-id") ];
:if ([ :len $VlanId ] > 0) do={
/caps-man/access-list/set $Entry vlan-id=$VlanId;
}
:local VlanMode [ $EitherOr ($UserInfo->"vlan-mode") ($Template->"vlan-mode") ];
:if ([ :len $VlanMode] > 0) do={
/caps-man/access-list/set $Entry vlan-mode=$VlanMode;
}
# dummy for migration

View file

@ -1,51 +1,3 @@
#!rsc by RouterOS
# RouterOS script: hotspot-to-wpa-cleanup
# Copyright (c) 2021-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# provides: lease-script, order=80
#
# manage and clean up private WPA passphrase after hotspot login
# https://git.eworm.de/cgit/routeros-scripts/about/doc/hotspot-to-wpa.md
:local 0 "hotspot-to-wpa-cleanup";
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:global LogPrintExit2;
:global ScriptLock;
$ScriptLock $0 false 10;
:foreach Client in=[ /caps-man/registration-table/find where comment~"^hotspot-to-wpa:" ] do={
:local ClientVal [ /caps-man/registration-table/get $Client ];
:local Lease [ /ip/dhcp-server/lease/find where server~"wpa" dynamic \
mac-address=($ClientVal->"mac-address") ];
:if ([ :len $Lease ] > 0) do={
$LogPrintExit2 info $0 ("Client with mac address " . ($ClientVal->"mac-address") . \
" connected to WPA, making lease static.") false;
/ip/dhcp-server/lease/make-static $Lease;
/ip/dhcp-server/lease/set comment=($ClientVal->"comment") $Lease;
}
}
:foreach Client in=[ /caps-man/access-list/find where comment~"^hotspot-to-wpa:" and \
!(comment~[ /system/clock/get date ]) ] do={
:local ClientVal [ /caps-man/access-list/get $Client ];
:if ([ :len [ /ip/dhcp-server/lease/find where server~"wpa" !dynamic \
mac-address=($ClientVal->"mac-address") ] ] = 0) do={
$LogPrintExit2 info $0 ("Client with mac address " . ($ClientVal->"mac-address") . \
" did not connect to WPA, removing from access list.") false;
/caps-man/access-list/remove $Client;
}
}
:foreach Lease in=[ /ip/dhcp-server/lease/find where !dynamic status=waiting \
last-seen>4w comment~"^hotspot-to-wpa:" ] do={
:local LeaseVal [ /ip/dhcp-server/lease/get $Lease ];
$LogPrintExit2 info $0 ("Client with mac address " . ($LeaseVal->"mac-address") . \
" was not seen for long time, removing.") false;
/caps-man/access-list/remove [ find where comment~"^hotspot-to-wpa:" \
mac-address=($LeaseVal->"mac-address") ];
/ip/dhcp-server/lease/remove $Lease;
}
# dummy for migration

View file

@ -0,0 +1,51 @@
#!rsc by RouterOS
# RouterOS script: hotspot-to-wpa-cleanup
# Copyright (c) 2021-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# provides: lease-script, order=80
#
# manage and clean up private WPA passphrase after hotspot login
# https://git.eworm.de/cgit/routeros-scripts/about/doc/hotspot-to-wpa.md
:local 0 "hotspot-to-wpa-cleanup";
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:global LogPrintExit2;
:global ScriptLock;
$ScriptLock $0 false 10;
:foreach Client in=[ /caps-man/registration-table/find where comment~"^hotspot-to-wpa:" ] do={
:local ClientVal [ /caps-man/registration-table/get $Client ];
:local Lease [ /ip/dhcp-server/lease/find where server~"wpa" dynamic \
mac-address=($ClientVal->"mac-address") ];
:if ([ :len $Lease ] > 0) do={
$LogPrintExit2 info $0 ("Client with mac address " . ($ClientVal->"mac-address") . \
" connected to WPA, making lease static.") false;
/ip/dhcp-server/lease/make-static $Lease;
/ip/dhcp-server/lease/set comment=($ClientVal->"comment") $Lease;
}
}
:foreach Client in=[ /caps-man/access-list/find where comment~"^hotspot-to-wpa:" and \
!(comment~[ /system/clock/get date ]) ] do={
:local ClientVal [ /caps-man/access-list/get $Client ];
:if ([ :len [ /ip/dhcp-server/lease/find where server~"wpa" !dynamic \
mac-address=($ClientVal->"mac-address") ] ] = 0) do={
$LogPrintExit2 info $0 ("Client with mac address " . ($ClientVal->"mac-address") . \
" did not connect to WPA, removing from access list.") false;
/caps-man/access-list/remove $Client;
}
}
:foreach Lease in=[ /ip/dhcp-server/lease/find where !dynamic status=waiting \
last-seen>4w comment~"^hotspot-to-wpa:" ] do={
:local LeaseVal [ /ip/dhcp-server/lease/get $Lease ];
$LogPrintExit2 info $0 ("Client with mac address " . ($LeaseVal->"mac-address") . \
" was not seen for long time, removing.") false;
/caps-man/access-list/remove [ find where comment~"^hotspot-to-wpa:" \
mac-address=($LeaseVal->"mac-address") ];
/ip/dhcp-server/lease/remove $Lease;
}

72
hotspot-to-wpa.rsc Normal file
View file

@ -0,0 +1,72 @@
#!rsc by RouterOS
# RouterOS script: hotspot-to-wpa
# Copyright (c) 2019-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# add private WPA passphrase after hotspot login
# https://git.eworm.de/cgit/routeros-scripts/about/doc/hotspot-to-wpa.md
:local 0 "hotspot-to-wpa";
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:global EitherOr;
:global LogPrintExit2;
:global ParseKeyValueStore;
:local MacAddress $"mac-address";
:local UserName $username;
:local Date [ /system/clock/get date ];
:local UserVal [ /ip/hotspot/user/get [ find where name=$UserName ] ];
:local UserInfo [ $ParseKeyValueStore ($UserVal->"comment") ];
:local Hotspot [ /ip/hotspot/host/get [ find where mac-address=$MacAddress authorized ] server ];
:if ([ :len [ /caps-man/access-list/find where comment="--- hotspot-to-wpa above ---" disabled ] ] = 0) do={
/caps-man/access-list/add comment="--- hotspot-to-wpa above ---" disabled=yes;
$LogPrintExit2 warning $0 ("Added disabled access-list entry with comment '--- hotspot-to-wpa above ---'.") false;
}
:local PlaceBefore ([ /caps-man/access-list/find where comment="--- hotspot-to-wpa above ---" disabled ]->0);
:if ([ :len [ /caps-man/access-list/find where \
comment=("hotspot-to-wpa template " . $Hotspot) disabled ] ] = 0) do={
/caps-man/access-list/add comment=("hotspot-to-wpa template " . $Hotspot) disabled=yes place-before=$PlaceBefore;
$LogPrintExit2 warning $0 ("Added template in access-list for hotspot '" . $Hotspot . "'.") false;
}
:local Template [ /caps-man/access-list/get ([ find where \
comment=("hotspot-to-wpa template " . $Hotspot) disabled ]->0) ];
:if ($Template->"action" = "reject") do={
$LogPrintExit2 info $0 ("Ignoring login for hotspot '" . $Hotspot . "'.") true;
}
# allow login page to load
:delay 1s;
$LogPrintExit2 info $0 ("Adding/updating access-list entry for mac address " . $MacAddress . \
" (user " . $UserName . ").") false;
/caps-man/access-list/remove [ find where mac-address=$MacAddress comment~"^hotspot-to-wpa: " ];
/caps-man/access-list/add comment=("hotspot-to-wpa: " . $UserName . ", " . $MacAddress . ", " . $Date) \
mac-address=$MacAddress private-passphrase=($UserVal->"password") ssid-regexp="-wpa\$" place-before=$PlaceBefore;
:local Entry [ /caps-man/access-list/find where mac-address=$MacAddress \
comment=("hotspot-to-wpa: " . $UserName . ", " . $MacAddress . ", " . $Date) ];
:local PrivatePassphrase [ $EitherOr ($UserInfo->"private-passphrase") ($Template->"private-passphrase") ];
:if ([ :len $PrivatePassphrase ] > 0) do={
:if ($PrivatePassphrase = "ignore") do={
/caps-man/access-list/set $Entry !private-passphrase;
} else={
/caps-man/access-list/set $Entry private-passphrase=$PrivatePassphrase;
}
}
:local SsidRegexp [ $EitherOr ($UserInfo->"ssid-regexp") ($Template->"ssid-regexp") ];
:if ([ :len $SsidRegexp ] > 0) do={
/caps-man/access-list/set $Entry ssid-regexp=$SsidRegexp;
}
:local VlanId [ $EitherOr ($UserInfo->"vlan-id") ($Template->"vlan-id") ];
:if ([ :len $VlanId ] > 0) do={
/caps-man/access-list/set $Entry vlan-id=$VlanId;
}
:local VlanMode [ $EitherOr ($UserInfo->"vlan-mode") ($Template->"vlan-mode") ];
:if ([ :len $VlanMode] > 0) do={
/caps-man/access-list/set $Entry vlan-mode=$VlanMode;
}

View file

@ -1,18 +1,3 @@
#!rsc by RouterOS
# RouterOS script: ip-addr-bridge
# Copyright (c) 2018-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# enable or disable ip addresses based on bridge port state
# https://git.eworm.de/cgit/routeros-scripts/about/doc/ip-addr-bridge.md
:foreach Bridge in=[ /interface/bridge/find ] do={
:local BrName [ /interface/bridge/get $Bridge name ];
:if ([ :len [ /interface/bridge/port/find where bridge=$BrName ] ] > 0) do={
:if ([ :len [ /interface/bridge/port/find where bridge=$BrName and inactive=no ] ] = 0) do={
/ip/address/disable [ find where !dynamic interface=$BrName ];
} else={
/ip/address/enable [ find where !dynamic interface=$BrName ];
}
}
}
# dummy for migration

18
ip-addr-bridge.rsc Normal file
View file

@ -0,0 +1,18 @@
#!rsc by RouterOS
# RouterOS script: ip-addr-bridge
# Copyright (c) 2018-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# enable or disable ip addresses based on bridge port state
# https://git.eworm.de/cgit/routeros-scripts/about/doc/ip-addr-bridge.md
:foreach Bridge in=[ /interface/bridge/find ] do={
:local BrName [ /interface/bridge/get $Bridge name ];
:if ([ :len [ /interface/bridge/port/find where bridge=$BrName ] ] > 0) do={
:if ([ :len [ /interface/bridge/port/find where bridge=$BrName and inactive=no ] ] = 0) do={
/ip/address/disable [ find where !dynamic interface=$BrName ];
} else={
/ip/address/enable [ find where !dynamic interface=$BrName ];
}
}
}

View file

@ -1,69 +1,3 @@
#!rsc by RouterOS
# RouterOS script: ipsec-to-dns
# Copyright (c) 2021-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# and add/remove/update DNS entries from IPSec mode-config
# https://git.eworm.de/cgit/routeros-scripts/about/doc/ipsec-to-dns.md
:local 0 "ipsec-to-dns";
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:global Domain;
:global HostNameInZone;
:global Identity;
:global PrefixInZone;
:global CharacterReplace;
:global EscapeForRegEx;
:global IfThenElse;
:global LogPrintExit2;
:local Zone \
([ $IfThenElse ($PrefixInZone = true) "ipsec." ] . \
[ $IfThenElse ($HostNameInZone = true) ($Identity . ".") ] . $Domain);
:local Ttl 5m;
:local CommentPrefix ("managed by " . $0 . " for ");
:local CommentString ("--- " . $0 . " above ---");
:if ([ :len [ /ip/dns/static/find where comment=$CommentString name=- type=NXDOMAIN disabled ] ] = 0) do={
/ip/dns/static/add comment=$CommentString name=- type=NXDOMAIN disabled=yes;
$LogPrintExit2 warning $0 ("Added disabled static dns record with comment '" . $CommentString . "'.") false;
}
:local PlaceBefore ([ /ip/dns/static/find where comment=$CommentString name=- type=NXDOMAIN disabled ]->0);
:foreach DnsRecord in=[ /ip/dns/static/find where comment ~ $CommentPrefix ] do={
:local DnsRecordVal [ /ip/dns/static/get $DnsRecord ];
:local PeerId [ $CharacterReplace ($DnsRecordVal->"comment") $CommentPrefix "" ];
:if ([ :len [ /ip/ipsec/active-peers/find where id~("^(CN=)?" . [ $EscapeForRegEx $PeerId ] . "\$") \
dynamic-address=($DnsRecordVal->"address") ] ] > 0) do={
$LogPrintExit2 debug $0 ("Peer " . $PeerId . " (" . $DnsRecordVal->"name" . ") still exists. Not deleting DNS entry.") false;
} else={
:local Found false;
$LogPrintExit2 info $0 ("Peer " . $PeerId . " (" . $DnsRecordVal->"name" . ") has gone, deleting DNS entry.") false;
/ip/dns/static/remove $DnsRecord;
}
}
:foreach Peer in=[ /ip/ipsec/active-peers/find where !(dynamic-address=[]) ] do={
:local PeerVal [ /ip/ipsec/active-peers/get $Peer ];
:local PeerId [ $CharacterReplace ($PeerVal->"id") "CN=" "" ];
:local Comment ($CommentPrefix . $PeerId);
:local HostName [ :pick $PeerId 0 [ :find ($PeerId . ".") "." ] ];
:local Fqdn ($HostName . "." . $Zone);
:local DnsRecord [ /ip/dns/static/find where name=$Fqdn ];
:if ([ :len $DnsRecord ] > 0) do={
:local DnsIp [ /ip/dns/static/get $DnsRecord address ];
:if ($DnsIp = $PeerVal->"dynamic-address") do={
$LogPrintExit2 debug $0 ("DNS entry for " . $Fqdn . " does not need updating.") false;
} else={
$LogPrintExit2 info $0 ("Replacing DNS entry for " . $Fqdn . ", new address is " . $PeerVal->"dynamic-address" . ".") false;
/ip/dns/static/set name=$Fqdn address=($PeerVal->"dynamic-address") ttl=$Ttl comment=$Comment $DnsRecord;
}
} else={
$LogPrintExit2 info $0 ("Adding new DNS entry for " . $Fqdn . ", address is " . $PeerVal->"dynamic-address" . ".") false;
/ip/dns/static/add name=$Fqdn address=($PeerVal->"dynamic-address") ttl=$Ttl comment=$Comment place-before=$PlaceBefore;
}
}
# dummy for migration

69
ipsec-to-dns.rsc Normal file
View file

@ -0,0 +1,69 @@
#!rsc by RouterOS
# RouterOS script: ipsec-to-dns
# Copyright (c) 2021-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# and add/remove/update DNS entries from IPSec mode-config
# https://git.eworm.de/cgit/routeros-scripts/about/doc/ipsec-to-dns.md
:local 0 "ipsec-to-dns";
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:global Domain;
:global HostNameInZone;
:global Identity;
:global PrefixInZone;
:global CharacterReplace;
:global EscapeForRegEx;
:global IfThenElse;
:global LogPrintExit2;
:local Zone \
([ $IfThenElse ($PrefixInZone = true) "ipsec." ] . \
[ $IfThenElse ($HostNameInZone = true) ($Identity . ".") ] . $Domain);
:local Ttl 5m;
:local CommentPrefix ("managed by " . $0 . " for ");
:local CommentString ("--- " . $0 . " above ---");
:if ([ :len [ /ip/dns/static/find where comment=$CommentString name=- type=NXDOMAIN disabled ] ] = 0) do={
/ip/dns/static/add comment=$CommentString name=- type=NXDOMAIN disabled=yes;
$LogPrintExit2 warning $0 ("Added disabled static dns record with comment '" . $CommentString . "'.") false;
}
:local PlaceBefore ([ /ip/dns/static/find where comment=$CommentString name=- type=NXDOMAIN disabled ]->0);
:foreach DnsRecord in=[ /ip/dns/static/find where comment ~ $CommentPrefix ] do={
:local DnsRecordVal [ /ip/dns/static/get $DnsRecord ];
:local PeerId [ $CharacterReplace ($DnsRecordVal->"comment") $CommentPrefix "" ];
:if ([ :len [ /ip/ipsec/active-peers/find where id~("^(CN=)?" . [ $EscapeForRegEx $PeerId ] . "\$") \
dynamic-address=($DnsRecordVal->"address") ] ] > 0) do={
$LogPrintExit2 debug $0 ("Peer " . $PeerId . " (" . $DnsRecordVal->"name" . ") still exists. Not deleting DNS entry.") false;
} else={
:local Found false;
$LogPrintExit2 info $0 ("Peer " . $PeerId . " (" . $DnsRecordVal->"name" . ") has gone, deleting DNS entry.") false;
/ip/dns/static/remove $DnsRecord;
}
}
:foreach Peer in=[ /ip/ipsec/active-peers/find where !(dynamic-address=[]) ] do={
:local PeerVal [ /ip/ipsec/active-peers/get $Peer ];
:local PeerId [ $CharacterReplace ($PeerVal->"id") "CN=" "" ];
:local Comment ($CommentPrefix . $PeerId);
:local HostName [ :pick $PeerId 0 [ :find ($PeerId . ".") "." ] ];
:local Fqdn ($HostName . "." . $Zone);
:local DnsRecord [ /ip/dns/static/find where name=$Fqdn ];
:if ([ :len $DnsRecord ] > 0) do={
:local DnsIp [ /ip/dns/static/get $DnsRecord address ];
:if ($DnsIp = $PeerVal->"dynamic-address") do={
$LogPrintExit2 debug $0 ("DNS entry for " . $Fqdn . " does not need updating.") false;
} else={
$LogPrintExit2 info $0 ("Replacing DNS entry for " . $Fqdn . ", new address is " . $PeerVal->"dynamic-address" . ".") false;
/ip/dns/static/set name=$Fqdn address=($PeerVal->"dynamic-address") ttl=$Ttl comment=$Comment $DnsRecord;
}
} else={
$LogPrintExit2 info $0 ("Adding new DNS entry for " . $Fqdn . ", address is " . $PeerVal->"dynamic-address" . ".") false;
/ip/dns/static/add name=$Fqdn address=($PeerVal->"dynamic-address") ttl=$Ttl comment=$Comment place-before=$PlaceBefore;
}
}

View file

@ -1,76 +1,3 @@
#!rsc by RouterOS
# RouterOS script: ipv6-update
# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# update firewall and dns settings on IPv6 prefix change
# https://git.eworm.de/cgit/routeros-scripts/about/doc/ipv6-update.md
:local 0 "ipv6-update";
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local PdPrefix $"pd-prefix";
:global LogPrintExit2;
:global ParseKeyValueStore;
:if ([ :typeof $PdPrefix ] = "nothing") do={
$LogPrintExit2 error $0 ("This script is supposed to run from ipv6 dhcp-client.") true;
}
:local Pool [ /ipv6/pool/get [ find where prefix=$PdPrefix ] name ];
:if ([ :len [ /ipv6/firewall/address-list/find where comment=("ipv6-pool-" . $Pool) ] ] = 0) do={
/ipv6/firewall/address-list/add list=("ipv6-pool-" . $Pool) address=:: comment=("ipv6-pool-" . $Pool);
$LogPrintExit2 warning $0 ("Added ipv6 address list entry for ipv6-pool-" . $Pool) false;
}
:local AddrList [ /ipv6/firewall/address-list/find where comment=("ipv6-pool-" . $Pool) ];
:local OldPrefix [ /ipv6/firewall/address-list/get ($AddrList->0) address ];
:if ($OldPrefix != $PdPrefix) do={
$LogPrintExit2 info $0 ("Updating IPv6 address list with new IPv6 prefix " . $PdPrefix) false;
/ipv6/firewall/address-list/set address=$PdPrefix $AddrList;
# give the interfaces a moment to receive their addresses
:delay 2s;
:foreach ListEntry in=[ /ipv6/firewall/address-list/find where comment~("^ipv6-pool-" . $Pool . ",") ] do={
:local ListEntryVal [ /ipv6/firewall/address-list/get $ListEntry ];
:local Comment [ $ParseKeyValueStore ($ListEntryVal->"comment") ];
:local Prefix [ /ipv6/address/find where from-pool=$Pool interface=($Comment->"interface") global ];
:if ([ :len $Prefix ] = 1) do={
:set Prefix [ /ipv6/address/get $Prefix address ];
:if ([ :typeof [ :find ($ListEntryVal->"address") "/128" ] ] = "num" ) do={
:set Prefix ([ :toip6 [ :pick $Prefix 0 [ :find $Prefix "/64" ] ] ] & ffff:ffff:ffff:ffff::);
:local Address ($ListEntryVal->"address");
:local Address ($Prefix | ([ :toip6 [ :pick $Address 0 [ :find $Address "/128" ] ] ] & ::ffff:ffff:ffff:ffff));
$LogPrintExit2 info $0 ("Updating IPv6 address list with new IPv6 host address " . $Address . \
" from interface " . ($Comment->"interface")) false;
/ipv6/firewall/address-list/set address=$Address $ListEntry;
} else={
$LogPrintExit2 info $0 ("Updating IPv6 address list with new IPv6 prefix " . $Prefix . \
" from interface " . ($Comment->"interface")) false;
/ipv6/firewall/address-list/set address=$Prefix $ListEntry;
}
}
}
:foreach Record in=[ /ip/dns/static/find where comment~("^ipv6-pool-" . $Pool . ",") ] do={
:local RecordVal [ /ip/dns/static/get $Record ];
:local Comment [ $ParseKeyValueStore ($RecordVal->"comment") ];
:local Prefix [ /ipv6/address/find where from-pool=$Pool interface=($Comment->"interface") global ];
:if ([ :len $Prefix ] = 1) do={
:set Prefix [ /ipv6/address/get $Prefix address ];
:set Prefix ([ :toip6 [ :pick $Prefix 0 [ :find $Prefix "/64" ] ] ] & ffff:ffff:ffff:ffff::);
:local Address ($Prefix | ([ :toip6 ($RecordVal->"address") ] & ::ffff:ffff:ffff:ffff));
$LogPrintExit2 info $0 ("Updating DNS record for " . ($RecordVal->"name") . \
($RecordVal->"regexp") . " to " . $Address) false;
/ip/dns/static/set address=$Address $Record;
}
}
}
# dummy for migration

76
ipv6-update.rsc Normal file
View file

@ -0,0 +1,76 @@
#!rsc by RouterOS
# RouterOS script: ipv6-update
# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# update firewall and dns settings on IPv6 prefix change
# https://git.eworm.de/cgit/routeros-scripts/about/doc/ipv6-update.md
:local 0 "ipv6-update";
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:local PdPrefix $"pd-prefix";
:global LogPrintExit2;
:global ParseKeyValueStore;
:if ([ :typeof $PdPrefix ] = "nothing") do={
$LogPrintExit2 error $0 ("This script is supposed to run from ipv6 dhcp-client.") true;
}
:local Pool [ /ipv6/pool/get [ find where prefix=$PdPrefix ] name ];
:if ([ :len [ /ipv6/firewall/address-list/find where comment=("ipv6-pool-" . $Pool) ] ] = 0) do={
/ipv6/firewall/address-list/add list=("ipv6-pool-" . $Pool) address=:: comment=("ipv6-pool-" . $Pool);
$LogPrintExit2 warning $0 ("Added ipv6 address list entry for ipv6-pool-" . $Pool) false;
}
:local AddrList [ /ipv6/firewall/address-list/find where comment=("ipv6-pool-" . $Pool) ];
:local OldPrefix [ /ipv6/firewall/address-list/get ($AddrList->0) address ];
:if ($OldPrefix != $PdPrefix) do={
$LogPrintExit2 info $0 ("Updating IPv6 address list with new IPv6 prefix " . $PdPrefix) false;
/ipv6/firewall/address-list/set address=$PdPrefix $AddrList;
# give the interfaces a moment to receive their addresses
:delay 2s;
:foreach ListEntry in=[ /ipv6/firewall/address-list/find where comment~("^ipv6-pool-" . $Pool . ",") ] do={
:local ListEntryVal [ /ipv6/firewall/address-list/get $ListEntry ];
:local Comment [ $ParseKeyValueStore ($ListEntryVal->"comment") ];
:local Prefix [ /ipv6/address/find where from-pool=$Pool interface=($Comment->"interface") global ];
:if ([ :len $Prefix ] = 1) do={
:set Prefix [ /ipv6/address/get $Prefix address ];
:if ([ :typeof [ :find ($ListEntryVal->"address") "/128" ] ] = "num" ) do={
:set Prefix ([ :toip6 [ :pick $Prefix 0 [ :find $Prefix "/64" ] ] ] & ffff:ffff:ffff:ffff::);
:local Address ($ListEntryVal->"address");
:local Address ($Prefix | ([ :toip6 [ :pick $Address 0 [ :find $Address "/128" ] ] ] & ::ffff:ffff:ffff:ffff));
$LogPrintExit2 info $0 ("Updating IPv6 address list with new IPv6 host address " . $Address . \
" from interface " . ($Comment->"interface")) false;
/ipv6/firewall/address-list/set address=$Address $ListEntry;
} else={
$LogPrintExit2 info $0 ("Updating IPv6 address list with new IPv6 prefix " . $Prefix . \
" from interface " . ($Comment->"interface")) false;
/ipv6/firewall/address-list/set address=$Prefix $ListEntry;
}
}
}
:foreach Record in=[ /ip/dns/static/find where comment~("^ipv6-pool-" . $Pool . ",") ] do={
:local RecordVal [ /ip/dns/static/get $Record ];
:local Comment [ $ParseKeyValueStore ($RecordVal->"comment") ];
:local Prefix [ /ipv6/address/find where from-pool=$Pool interface=($Comment->"interface") global ];
:if ([ :len $Prefix ] = 1) do={
:set Prefix [ /ipv6/address/get $Prefix address ];
:set Prefix ([ :toip6 [ :pick $Prefix 0 [ :find $Prefix "/64" ] ] ] & ffff:ffff:ffff:ffff::);
:local Address ($Prefix | ([ :toip6 ($RecordVal->"address") ] & ::ffff:ffff:ffff:ffff));
$LogPrintExit2 info $0 ("Updating DNS record for " . ($RecordVal->"name") . \
($RecordVal->"regexp") . " to " . $Address) false;
/ip/dns/static/set address=$Address $Record;
}
}
}

View file

@ -1,51 +1,3 @@
#!rsc by RouterOS
# RouterOS script: lease-script
# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# run scripts on DHCP lease
# https://git.eworm.de/cgit/routeros-scripts/about/doc/lease-script.md
:local 0 "lease-script";
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:global Grep;
:global IfThenElse;
:global LogPrintExit2;
:global ParseKeyValueStore;
:global ScriptLock;
:if ([ :typeof $leaseActIP ] = "nothing" || \
[ :typeof $leaseActMAC ] = "nothing" || \
[ :typeof $leaseServerName ] = "nothing" || \
[ :typeof $leaseBound ] = "nothing") do={
$LogPrintExit2 error $0 ("This script is supposed to run from ip dhcp-server.") true;
}
$LogPrintExit2 debug $0 ("DHCP Server " . $leaseServerName . " " . [ $IfThenElse ($leaseBound = 0) \
"de" "" ] . "assigned lease " . $leaseActIP . " to " . $leaseActMAC) false;
$ScriptLock $0 false 10;
:if ([ :len [ /system/script/job/find where script=$0 ] ] > 1) do={
$LogPrintExit2 debug $0 ("More invocations are waiting, exiting early.") true;
}
:local RunOrder ({});
:foreach Script in=[ /system/script/find where source~("\n# provides: lease-script, ") ] do={
:local ScriptVal [ /system/script/get $Script ];
:local Store [ $ParseKeyValueStore [ $Grep ($ScriptVal->"source") "# provides: lease-script, " ] ];
:set ($RunOrder->($Store->"order")) ($ScriptVal->"name");
}
:foreach Order,Script in=$RunOrder do={
:do {
$LogPrintExit2 debug $0 ("Running script with order " . $Order . ": " . $Script) false;
/system/script/run $Script;
} on-error={
$LogPrintExit2 warning $0 ("Running script '" . $Script . "' failed!") false;
}
}
# dummy for migration

51
lease-script.rsc Normal file
View file

@ -0,0 +1,51 @@
#!rsc by RouterOS
# RouterOS script: lease-script
# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# run scripts on DHCP lease
# https://git.eworm.de/cgit/routeros-scripts/about/doc/lease-script.md
:local 0 "lease-script";
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:global Grep;
:global IfThenElse;
:global LogPrintExit2;
:global ParseKeyValueStore;
:global ScriptLock;
:if ([ :typeof $leaseActIP ] = "nothing" || \
[ :typeof $leaseActMAC ] = "nothing" || \
[ :typeof $leaseServerName ] = "nothing" || \
[ :typeof $leaseBound ] = "nothing") do={
$LogPrintExit2 error $0 ("This script is supposed to run from ip dhcp-server.") true;
}
$LogPrintExit2 debug $0 ("DHCP Server " . $leaseServerName . " " . [ $IfThenElse ($leaseBound = 0) \
"de" "" ] . "assigned lease " . $leaseActIP . " to " . $leaseActMAC) false;
$ScriptLock $0 false 10;
:if ([ :len [ /system/script/job/find where script=$0 ] ] > 1) do={
$LogPrintExit2 debug $0 ("More invocations are waiting, exiting early.") true;
}
:local RunOrder ({});
:foreach Script in=[ /system/script/find where source~("\n# provides: lease-script, ") ] do={
:local ScriptVal [ /system/script/get $Script ];
:local Store [ $ParseKeyValueStore [ $Grep ($ScriptVal->"source") "# provides: lease-script, " ] ];
:set ($RunOrder->($Store->"order")) ($ScriptVal->"name");
}
:foreach Order,Script in=$RunOrder do={
:do {
$LogPrintExit2 debug $0 ("Running script with order " . $Order . ": " . $Script) false;
/system/script/run $Script;
} on-error={
$LogPrintExit2 warning $0 ("Running script '" . $Script . "' failed!") false;
}
}

View file

@ -1,9 +1,3 @@
#!rsc by RouterOS
# RouterOS script: leds-day-mode
# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# enable LEDs
# https://git.eworm.de/cgit/routeros-scripts/about/doc/leds-mode.md
/system/leds/settings/set all-leds-off=never;
# dummy for migration

9
leds-day-mode.rsc Normal file
View file

@ -0,0 +1,9 @@
#!rsc by RouterOS
# RouterOS script: leds-day-mode
# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# enable LEDs
# https://git.eworm.de/cgit/routeros-scripts/about/doc/leds-mode.md
/system/leds/settings/set all-leds-off=never;

View file

@ -1,9 +1,3 @@
#!rsc by RouterOS
# RouterOS script: leds-night-mode
# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# disable LEDs
# https://git.eworm.de/cgit/routeros-scripts/about/doc/leds-mode.md
/system/leds/settings/set all-leds-off=immediate;
# dummy for migration

9
leds-night-mode.rsc Normal file
View file

@ -0,0 +1,9 @@
#!rsc by RouterOS
# RouterOS script: leds-night-mode
# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# disable LEDs
# https://git.eworm.de/cgit/routeros-scripts/about/doc/leds-mode.md
/system/leds/settings/set all-leds-off=immediate;

View file

@ -1,13 +1,3 @@
#!rsc by RouterOS
# RouterOS script: leds-toggle-mode
# Copyright (c) 2018-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# toggle LEDs mode
# https://git.eworm.de/cgit/routeros-scripts/about/doc/leds-mode.md
:if ([ /system/leds/settings/get all-leds-off ] = "never") do={
/system/leds/settings/set all-leds-off=immediate;
} else={
/system/leds/settings/set all-leds-off=never;
}
# dummy for migration

13
leds-toggle-mode.rsc Normal file
View file

@ -0,0 +1,13 @@
#!rsc by RouterOS
# RouterOS script: leds-toggle-mode
# Copyright (c) 2018-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# toggle LEDs mode
# https://git.eworm.de/cgit/routeros-scripts/about/doc/leds-mode.md
:if ([ /system/leds/settings/get all-leds-off ] = "never") do={
/system/leds/settings/set all-leds-off=immediate;
} else={
/system/leds/settings/set all-leds-off=never;
}

View file

@ -1,90 +1,3 @@
#!rsc by RouterOS
# RouterOS script: log-forward
# Copyright (c) 2020-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# forward log messages via notification
# https://git.eworm.de/cgit/routeros-scripts/about/doc/log-forward.md
:local 0 "log-forward";
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:global Identity;
:global LogForwardFilter;
:global LogForwardFilterMessage;
:global LogForwardInclude;
:global LogForwardIncludeMessage;
:global LogForwardLast;
:global LogForwardRateLimit;
:global NotificationsWithSymbols;
:global EitherOr;
:global HexToNum;
:global IfThenElse;
:global LogForwardFilterLogForwarding;
:global LogPrintExit2;
:global ScriptLock;
:global SendNotification2;
:global SymbolForNotification;
$ScriptLock $0;
:if ([ :typeof $LogForwardRateLimit ] = "nothing") do={
:set LogForwardRateLimit 0;
}
:if ($LogForwardRateLimit > 30) do={
:set LogForwardRateLimit ($LogForwardRateLimit - 1);
$LogPrintExit2 info $0 ("Rate limit in action, not forwarding logs, if any!") true;
}
:local Count 0;
:local Duplicates false;
:local Last [ $IfThenElse ([ :len $LogForwardLast ] > 0) [ $HexToNum $LogForwardLast ] -1 ];
:local Messages "";
:local Warning false;
:local MessageVal;
:local MessageDups ({});
:local LogForwardFilterLogForwardingCached [ $EitherOr [ $LogForwardFilterLogForwarding ] ("\$^") ];
:foreach Message in=[ /log/find where (!(message="") and \
!(message~$LogForwardFilterLogForwardingCached) and \
!(topics~$LogForwardFilter) and !(message~$LogForwardFilterMessage)) or \
topics~$LogForwardInclude or message~$LogForwardIncludeMessage ] do={
:set MessageVal [ /log/get $Message ];
:if ($Last < [ $HexToNum ($MessageVal->".id") ]) do={
:local DupCount ($MessageDups->($MessageVal->"message"));
:if ($MessageVal->"topics" ~ "(emergency|alert|critical|error|warning)") do={
:set Warning true;
}
:if ($DupCount < 3) do={
:set Messages ($Messages . "\n" . [ $IfThenElse ($NotificationsWithSymbols = true) (" \E2\97\8F ") ] . \
$MessageVal->"time" . " " . [ :tostr ($MessageVal->"topics") ] . " " . $MessageVal->"message");
} else={
:set Duplicates true;
}
:set ($MessageDups->($MessageVal->"message")) ($DupCount + 1);
:set Count ($Count + 1);
}
}
:if ($Count > 0) do={
:set LogForwardRateLimit ($LogForwardRateLimit + 10);
$SendNotification2 ({ origin=$0; \
subject=([ $SymbolForNotification [ $IfThenElse ($Warning = true) "warning-sign" "memo" ] ] . \
"Log Forwarding"); \
message=("The log on " . $Identity . " contains " . [ $IfThenElse ($Count = 1) "this message" \
("these " . $Count . " messages") ] . " after " . [ /system/resource/get uptime ] . " uptime." . \
[ $IfThenElse ($Duplicates = true) (" Multi-repeated messages have been skipped.") ] . \
[ $IfThenElse ($LogForwardRateLimit > 30) ("\nRate limit in action, delaying forwarding.") ] . \
"\n" . $Messages) });
:set LogForwardLast ($MessageVal->".id");
} else={
:if ($LogForwardRateLimit > 0) do={
:set LogForwardRateLimit ($LogForwardRateLimit - 1);
}
}
# dummy for migration

90
log-forward.rsc Normal file
View file

@ -0,0 +1,90 @@
#!rsc by RouterOS
# RouterOS script: log-forward
# Copyright (c) 2020-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# forward log messages via notification
# https://git.eworm.de/cgit/routeros-scripts/about/doc/log-forward.md
:local 0 "log-forward";
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:global Identity;
:global LogForwardFilter;
:global LogForwardFilterMessage;
:global LogForwardInclude;
:global LogForwardIncludeMessage;
:global LogForwardLast;
:global LogForwardRateLimit;
:global NotificationsWithSymbols;
:global EitherOr;
:global HexToNum;
:global IfThenElse;
:global LogForwardFilterLogForwarding;
:global LogPrintExit2;
:global ScriptLock;
:global SendNotification2;
:global SymbolForNotification;
$ScriptLock $0;
:if ([ :typeof $LogForwardRateLimit ] = "nothing") do={
:set LogForwardRateLimit 0;
}
:if ($LogForwardRateLimit > 30) do={
:set LogForwardRateLimit ($LogForwardRateLimit - 1);
$LogPrintExit2 info $0 ("Rate limit in action, not forwarding logs, if any!") true;
}
:local Count 0;
:local Duplicates false;
:local Last [ $IfThenElse ([ :len $LogForwardLast ] > 0) [ $HexToNum $LogForwardLast ] -1 ];
:local Messages "";
:local Warning false;
:local MessageVal;
:local MessageDups ({});
:local LogForwardFilterLogForwardingCached [ $EitherOr [ $LogForwardFilterLogForwarding ] ("\$^") ];
:foreach Message in=[ /log/find where (!(message="") and \
!(message~$LogForwardFilterLogForwardingCached) and \
!(topics~$LogForwardFilter) and !(message~$LogForwardFilterMessage)) or \
topics~$LogForwardInclude or message~$LogForwardIncludeMessage ] do={
:set MessageVal [ /log/get $Message ];
:if ($Last < [ $HexToNum ($MessageVal->".id") ]) do={
:local DupCount ($MessageDups->($MessageVal->"message"));
:if ($MessageVal->"topics" ~ "(emergency|alert|critical|error|warning)") do={
:set Warning true;
}
:if ($DupCount < 3) do={
:set Messages ($Messages . "\n" . [ $IfThenElse ($NotificationsWithSymbols = true) (" \E2\97\8F ") ] . \
$MessageVal->"time" . " " . [ :tostr ($MessageVal->"topics") ] . " " . $MessageVal->"message");
} else={
:set Duplicates true;
}
:set ($MessageDups->($MessageVal->"message")) ($DupCount + 1);
:set Count ($Count + 1);
}
}
:if ($Count > 0) do={
:set LogForwardRateLimit ($LogForwardRateLimit + 10);
$SendNotification2 ({ origin=$0; \
subject=([ $SymbolForNotification [ $IfThenElse ($Warning = true) "warning-sign" "memo" ] ] . \
"Log Forwarding"); \
message=("The log on " . $Identity . " contains " . [ $IfThenElse ($Count = 1) "this message" \
("these " . $Count . " messages") ] . " after " . [ /system/resource/get uptime ] . " uptime." . \
[ $IfThenElse ($Duplicates = true) (" Multi-repeated messages have been skipped.") ] . \
[ $IfThenElse ($LogForwardRateLimit > 30) ("\nRate limit in action, delaying forwarding.") ] . \
"\n" . $Messages) });
:set LogForwardLast ($MessageVal->".id");
} else={
:if ($LogForwardRateLimit > 0) do={
:set LogForwardRateLimit ($LogForwardRateLimit - 1);
}
}

View file

@ -1,65 +1,3 @@
#!rsc by RouterOS
# RouterOS script: mod/bridge-port-to
# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# reset bridge ports to default bridge
# https://git.eworm.de/cgit/routeros-scripts/about/doc/mod/bridge-port-to.md
:global BridgePortTo;
:set BridgePortTo do={
:local BridgePortTo [ :tostr $1 ];
:global IfThenElse;
:global LogPrintExit2;
:global ParseKeyValueStore;
:local InterfaceReEnable ({});
:foreach BridgePort in=[ /interface/bridge/port/find where !(comment=[]) ] do={
:local BridgePortVal [ /interface/bridge/port/get $BridgePort ];
:foreach Config,BridgeDefault in=[ $ParseKeyValueStore ($BridgePortVal->"comment") ] do={
:if ($Config = $BridgePortTo) do={
:local DHCPClient [ /ip/dhcp-client/find where interface=$BridgePortVal->"interface" comment="toggle with bridge port" ];
:if ($BridgeDefault = "dhcp-client") do={
:if ([ :len $DHCPClient ] != 1) do={
$LogPrintExit2 warning $0 ([ $IfThenElse ([ :len $DHCPClient ] = 0) "Missing" "Duplicate" ] . \
" dhcp client configuration for interface " . $BridgePortVal->"interface" . "!") true;
}
:local DHCPClientDisabled [ /ip/dhcp-client/get $DHCPClient disabled ];
:if ($BridgePortVal->"disabled" = false || $DHCPClientDisabled = true) do={
$LogPrintExit2 info $0 ("Disabling bridge port for interface " . $BridgePortVal->"interface" . ", enabling dhcp client.") false;
/interface/bridge/port/disable $BridgePort;
:delay 200ms;
/ip/dhcp-client/enable $DHCPClient;
}
} else={
:if ($BridgePortVal->"disabled" = true || $BridgeDefault != $BridgePortVal->"bridge") do={
$LogPrintExit2 info $0 ("Enabling bridge port for interface " . $BridgePortVal->"interface" . ", changing to " . $BridgePortTo . \
" bridge " . $BridgeDefault . ", disabling dhcp client.") false;
:if ([ :len $DHCPClient ] = 1) do={
/ip/dhcp-client/disable $DHCPClient;
:delay 200ms;
}
:local Disable [ /interface/ethernet/find where name=$BridgePortVal->"interface" ];
:if ([ :len $Disable ] > 0) do={
/interface/ethernet/disable $Disable;
:set InterfaceReEnable ($InterfaceReEnable, $Disable);
}
/interface/bridge/port/set disabled=no bridge=$BridgeDefault $BridgePort;
} else={
$LogPrintExit2 debug $0 ("Interface " . $BridgePortVal->"interface" . " already connected to " . $BridgePortTo . \
" bridge " . $BridgeDefault . ".") false;
}
}
}
}
}
:if ([ :len $InterfaceReEnable ] > 0) do={
:delay 2s;
$LogPrintExit2 info $0 ("Re-enabling interfaces...") false;
/interface/ethernet/enable $InterfaceReEnable;
}
}
# dummy for migration

65
mod/bridge-port-to.rsc Normal file
View file

@ -0,0 +1,65 @@
#!rsc by RouterOS
# RouterOS script: mod/bridge-port-to
# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# reset bridge ports to default bridge
# https://git.eworm.de/cgit/routeros-scripts/about/doc/mod/bridge-port-to.md
:global BridgePortTo;
:set BridgePortTo do={
:local BridgePortTo [ :tostr $1 ];
:global IfThenElse;
:global LogPrintExit2;
:global ParseKeyValueStore;
:local InterfaceReEnable ({});
:foreach BridgePort in=[ /interface/bridge/port/find where !(comment=[]) ] do={
:local BridgePortVal [ /interface/bridge/port/get $BridgePort ];
:foreach Config,BridgeDefault in=[ $ParseKeyValueStore ($BridgePortVal->"comment") ] do={
:if ($Config = $BridgePortTo) do={
:local DHCPClient [ /ip/dhcp-client/find where interface=$BridgePortVal->"interface" comment="toggle with bridge port" ];
:if ($BridgeDefault = "dhcp-client") do={
:if ([ :len $DHCPClient ] != 1) do={
$LogPrintExit2 warning $0 ([ $IfThenElse ([ :len $DHCPClient ] = 0) "Missing" "Duplicate" ] . \
" dhcp client configuration for interface " . $BridgePortVal->"interface" . "!") true;
}
:local DHCPClientDisabled [ /ip/dhcp-client/get $DHCPClient disabled ];
:if ($BridgePortVal->"disabled" = false || $DHCPClientDisabled = true) do={
$LogPrintExit2 info $0 ("Disabling bridge port for interface " . $BridgePortVal->"interface" . ", enabling dhcp client.") false;
/interface/bridge/port/disable $BridgePort;
:delay 200ms;
/ip/dhcp-client/enable $DHCPClient;
}
} else={
:if ($BridgePortVal->"disabled" = true || $BridgeDefault != $BridgePortVal->"bridge") do={
$LogPrintExit2 info $0 ("Enabling bridge port for interface " . $BridgePortVal->"interface" . ", changing to " . $BridgePortTo . \
" bridge " . $BridgeDefault . ", disabling dhcp client.") false;
:if ([ :len $DHCPClient ] = 1) do={
/ip/dhcp-client/disable $DHCPClient;
:delay 200ms;
}
:local Disable [ /interface/ethernet/find where name=$BridgePortVal->"interface" ];
:if ([ :len $Disable ] > 0) do={
/interface/ethernet/disable $Disable;
:set InterfaceReEnable ($InterfaceReEnable, $Disable);
}
/interface/bridge/port/set disabled=no bridge=$BridgeDefault $BridgePort;
} else={
$LogPrintExit2 debug $0 ("Interface " . $BridgePortVal->"interface" . " already connected to " . $BridgePortTo . \
" bridge " . $BridgeDefault . ".") false;
}
}
}
}
}
:if ([ :len $InterfaceReEnable ] > 0) do={
:delay 2s;
$LogPrintExit2 info $0 ("Re-enabling interfaces...") false;
/interface/ethernet/enable $InterfaceReEnable;
}
}

View file

@ -1,73 +1,3 @@
#!rsc by RouterOS
# RouterOS script: mod/bridge-port-vlan
# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# manage VLANs on bridge ports
# https://git.eworm.de/cgit/routeros-scripts/about/doc/mod/bridge-port-vlan.md
:global BridgePortVlan;
:global BridgePortVlan do={
:local ConfigTo [ :tostr $1 ];
:global IfThenElse;
:global LogPrintExit2;
:global ParseKeyValueStore;
:local InterfaceReEnable ({});
:foreach BridgePort in=[ /interface/bridge/port/find where !(comment=[]) ] do={
:local BridgePortVal [ /interface/bridge/port/get $BridgePort ];
:foreach Config,Vlan in=[ $ParseKeyValueStore ($BridgePortVal->"comment") ] do={
:if ($Config = $ConfigTo) do={
:local DHCPClient [ /ip/dhcp-client/find where interface=$BridgePortVal->"interface" comment="toggle with bridge port" ];
:if ($Vlan = "dhcp-client") do={
:if ([ :len $DHCPClient ] != 1) do={
$LogPrintExit2 warning $0 ([ $IfThenElse ([ :len $DHCPClient ] = 0) "Missing" "Duplicate" ] . \
" dhcp client configuration for interface " . $BridgePortVal->"interface" . "!") true;
}
:local DHCPClientDisabled [ /ip/dhcp-client/get $DHCPClient disabled ];
:if ($BridgePortVal->"disabled" = false || $DHCPClientDisabled = true) do={
$LogPrintExit2 info $0 ("Disabling bridge port for interface " . $BridgePortVal->"interface" . ", enabling dhcp client.") false;
/interface/bridge/port/disable $BridgePort;
:delay 200ms;
/ip/dhcp-client/enable $DHCPClient;
}
} else={
:local VlanName $Vlan;
:if ($Vlan != [ :tostr [ :tonum $Vlan ] ]) do={
:do {
:set $Vlan ([ /interface/bridge/vlan/get [ find where comment=$Vlan ] vlan-ids ]->0);
} on-error={
$LogPrintExit2 warning $0 ("Could not find VLAN '" . $Vlan . "' for interface " . $BridgePortVal->"interface" . "!") true;
}
}
:if ($BridgePortVal->"disabled" = true || $Vlan != $BridgePortVal->"pvid") do={
$LogPrintExit2 info $0 ("Enabling bridge port for interface " . $BridgePortVal->"interface" . ", changing to " . $ConfigTo . \
" vlan " . $Vlan . [ $IfThenElse ($Vlan != $VlanName) (" (" . $VlanName . ")") ] . ", disabling dhcp client.") false;
:if ([ :len $DHCPClient ] = 1) do={
/ip/dhcp-client/disable $DHCPClient;
:delay 200ms;
}
:local Disable [ /interface/ethernet/find where name=$BridgePortVal->"interface" ];
:if ([ :len $Disable ] > 0) do={
/interface/ethernet/disable $Disable;
:set InterfaceReEnable ($InterfaceReEnable, $Disable);
}
/interface/bridge/port/set disabled=no pvid=$Vlan $BridgePort;
} else={
$LogPrintExit2 debug $0 ("Interface " . $BridgePortVal->"interface" . " already connected to " . $ConfigTo . \
" vlan " . $Vlan . ".") false;
}
}
}
}
}
:if ([ :len $InterfaceReEnable ] > 0) do={
:delay 2s;
$LogPrintExit2 info $0 ("Re-enabling interfaces...") false;
/interface/ethernet/enable $InterfaceReEnable;
}
}
# dummy for migration

73
mod/bridge-port-vlan.rsc Normal file
View file

@ -0,0 +1,73 @@
#!rsc by RouterOS
# RouterOS script: mod/bridge-port-vlan
# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# manage VLANs on bridge ports
# https://git.eworm.de/cgit/routeros-scripts/about/doc/mod/bridge-port-vlan.md
:global BridgePortVlan;
:global BridgePortVlan do={
:local ConfigTo [ :tostr $1 ];
:global IfThenElse;
:global LogPrintExit2;
:global ParseKeyValueStore;
:local InterfaceReEnable ({});
:foreach BridgePort in=[ /interface/bridge/port/find where !(comment=[]) ] do={
:local BridgePortVal [ /interface/bridge/port/get $BridgePort ];
:foreach Config,Vlan in=[ $ParseKeyValueStore ($BridgePortVal->"comment") ] do={
:if ($Config = $ConfigTo) do={
:local DHCPClient [ /ip/dhcp-client/find where interface=$BridgePortVal->"interface" comment="toggle with bridge port" ];
:if ($Vlan = "dhcp-client") do={
:if ([ :len $DHCPClient ] != 1) do={
$LogPrintExit2 warning $0 ([ $IfThenElse ([ :len $DHCPClient ] = 0) "Missing" "Duplicate" ] . \
" dhcp client configuration for interface " . $BridgePortVal->"interface" . "!") true;
}
:local DHCPClientDisabled [ /ip/dhcp-client/get $DHCPClient disabled ];
:if ($BridgePortVal->"disabled" = false || $DHCPClientDisabled = true) do={
$LogPrintExit2 info $0 ("Disabling bridge port for interface " . $BridgePortVal->"interface" . ", enabling dhcp client.") false;
/interface/bridge/port/disable $BridgePort;
:delay 200ms;
/ip/dhcp-client/enable $DHCPClient;
}
} else={
:local VlanName $Vlan;
:if ($Vlan != [ :tostr [ :tonum $Vlan ] ]) do={
:do {
:set $Vlan ([ /interface/bridge/vlan/get [ find where comment=$Vlan ] vlan-ids ]->0);
} on-error={
$LogPrintExit2 warning $0 ("Could not find VLAN '" . $Vlan . "' for interface " . $BridgePortVal->"interface" . "!") true;
}
}
:if ($BridgePortVal->"disabled" = true || $Vlan != $BridgePortVal->"pvid") do={
$LogPrintExit2 info $0 ("Enabling bridge port for interface " . $BridgePortVal->"interface" . ", changing to " . $ConfigTo . \
" vlan " . $Vlan . [ $IfThenElse ($Vlan != $VlanName) (" (" . $VlanName . ")") ] . ", disabling dhcp client.") false;
:if ([ :len $DHCPClient ] = 1) do={
/ip/dhcp-client/disable $DHCPClient;
:delay 200ms;
}
:local Disable [ /interface/ethernet/find where name=$BridgePortVal->"interface" ];
:if ([ :len $Disable ] > 0) do={
/interface/ethernet/disable $Disable;
:set InterfaceReEnable ($InterfaceReEnable, $Disable);
}
/interface/bridge/port/set disabled=no pvid=$Vlan $BridgePort;
} else={
$LogPrintExit2 debug $0 ("Interface " . $BridgePortVal->"interface" . " already connected to " . $ConfigTo . \
" vlan " . $Vlan . ".") false;
}
}
}
}
}
:if ([ :len $InterfaceReEnable ] > 0) do={
:delay 2s;
$LogPrintExit2 info $0 ("Re-enabling interfaces...") false;
/interface/ethernet/enable $InterfaceReEnable;
}
}

View file

@ -1,54 +1,3 @@
#!rsc by RouterOS
# RouterOS script: mod/inspectvar
# Copyright (c) 2020-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
:global InspectVar;
:global InspectVarReturn;
# inspect variable and print on terminal
:set InspectVar do={
:global InspectVarReturn;
:global PrettyPrint;
$PrettyPrint [ $InspectVarReturn $1 ];
}
# inspect variable and return formatted string
:set InspectVarReturn do={
:local Input $1;
:local Level (0 + [ :tonum $2 ]);
:global IfThenElse;
:global InspectVarReturn;
:local IndentReturn do={
:local Prefix [ :tostr $1 ];
:local Value [ :tostr $2 ];
:local Level [ :tonum $3 ];
:local Indent "";
:for I from=1 to=$Level step=1 do={
:set Indent ($Indent . " ");
}
:return ($Indent . "-" . $Prefix . "-> " . $Value);
}
:local TypeOf [ :typeof $Input ];
:local Return [ $IndentReturn "type" $TypeOf $Level ];
:if ($TypeOf = "array") do={
:foreach Key,Value in=$Input do={
:set $Return ($Return . "\n" . \
[ $IndentReturn "key" $Key ($Level + 1) ] . "\n" . \
[ $InspectVarReturn $Value ($Level + 2) ]);
}
} else={
:if ($TypeOf != "nothing") do={
:set $Return ($Return . "\n" . \
[ $IndentReturn "value" [ $IfThenElse ([ :len $Input ] > 80) \
([ :pick $Input 0 77 ] . "...") $Input ] $Level ]);
}
}
:return $Return;
}
#
# dummy for migration

54
mod/inspectvar.rsc Normal file
View file

@ -0,0 +1,54 @@
#!rsc by RouterOS
# RouterOS script: mod/inspectvar
# Copyright (c) 2020-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
:global InspectVar;
:global InspectVarReturn;
# inspect variable and print on terminal
:set InspectVar do={
:global InspectVarReturn;
:global PrettyPrint;
$PrettyPrint [ $InspectVarReturn $1 ];
}
# inspect variable and return formatted string
:set InspectVarReturn do={
:local Input $1;
:local Level (0 + [ :tonum $2 ]);
:global IfThenElse;
:global InspectVarReturn;
:local IndentReturn do={
:local Prefix [ :tostr $1 ];
:local Value [ :tostr $2 ];
:local Level [ :tonum $3 ];
:local Indent "";
:for I from=1 to=$Level step=1 do={
:set Indent ($Indent . " ");
}
:return ($Indent . "-" . $Prefix . "-> " . $Value);
}
:local TypeOf [ :typeof $Input ];
:local Return [ $IndentReturn "type" $TypeOf $Level ];
:if ($TypeOf = "array") do={
:foreach Key,Value in=$Input do={
:set $Return ($Return . "\n" . \
[ $IndentReturn "key" $Key ($Level + 1) ] . "\n" . \
[ $InspectVarReturn $Value ($Level + 2) ]);
}
} else={
:if ($TypeOf != "nothing") do={
:set $Return ($Return . "\n" . \
[ $IndentReturn "value" [ $IfThenElse ([ :len $Input ] > 80) \
([ :pick $Input 0 77 ] . "...") $Input ] $Level ]);
}
}
:return $Return;
}

View file

@ -1,46 +1,3 @@
#!rsc by RouterOS
# RouterOS script: mod/ipcalc
# Copyright (c) 2020-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
:global IPCalc;
:global IPCalcReturn;
# print netmask, network, min host, max host and broadcast
:set IPCalc do={
:local Input [ :tostr $1 ];
:global IPCalcReturn;
:global PrettyPrint;
:local Values [ $IPCalcReturn $1 ];
$PrettyPrint ( \
"Address: " . $Values->"address" . "\n" . \
"Netmask: " . $Values->"netmask" . "\n" . \
"Network: " . $Values->"network" . "\n" . \
"HostMin: " . $Values->"hostmin" . "\n" . \
"HostMax: " . $Values->"hostmax" . "\n" . \
"Broadcast: " . $Values->"broadcast");
}
# calculate and return netmask, network, min host, max host and broadcast
:set IPCalcReturn do={
:local Input [ :tostr $1 ];
:local Address [ :toip [ :pick $Input 0 [ :find $Input "/" ] ] ];
:local Bits [ :tonum [ :pick $Input ([ :find $Input "/" ] + 1) [ :len $Input ] ] ];
:local Mask ((255.255.255.255 << (32 - $Bits)) & 255.255.255.255);
:local Return {
"address"=$Address;
"netmask"=$Mask;
"networkaddress"=($Address & $Mask);
"networkbits"=$Bits;
"network"=(($Address & $Mask) . "/" . $Bits);
"hostmin"=(($Address & $Mask) | 0.0.0.1);
"hostmax"=(($Address | ~$Mask) ^ 0.0.0.1);
"broadcast"=($Address | ~$Mask);
}
:return $Return;
}
#
# dummy for migration

46
mod/ipcalc.rsc Normal file
View file

@ -0,0 +1,46 @@
#!rsc by RouterOS
# RouterOS script: mod/ipcalc
# Copyright (c) 2020-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
:global IPCalc;
:global IPCalcReturn;
# print netmask, network, min host, max host and broadcast
:set IPCalc do={
:local Input [ :tostr $1 ];
:global IPCalcReturn;
:global PrettyPrint;
:local Values [ $IPCalcReturn $1 ];
$PrettyPrint ( \
"Address: " . $Values->"address" . "\n" . \
"Netmask: " . $Values->"netmask" . "\n" . \
"Network: " . $Values->"network" . "\n" . \
"HostMin: " . $Values->"hostmin" . "\n" . \
"HostMax: " . $Values->"hostmax" . "\n" . \
"Broadcast: " . $Values->"broadcast");
}
# calculate and return netmask, network, min host, max host and broadcast
:set IPCalcReturn do={
:local Input [ :tostr $1 ];
:local Address [ :toip [ :pick $Input 0 [ :find $Input "/" ] ] ];
:local Bits [ :tonum [ :pick $Input ([ :find $Input "/" ] + 1) [ :len $Input ] ] ];
:local Mask ((255.255.255.255 << (32 - $Bits)) & 255.255.255.255);
:local Return {
"address"=$Address;
"netmask"=$Mask;
"networkaddress"=($Address & $Mask);
"networkbits"=$Bits;
"network"=(($Address & $Mask) . "/" . $Bits);
"hostmin"=(($Address & $Mask) | 0.0.0.1);
"hostmax"=(($Address | ~$Mask) ^ 0.0.0.1);
"broadcast"=($Address | ~$Mask);
}
:return $Return;
}

View file

@ -1,206 +1,3 @@
#!rsc by RouterOS
# RouterOS script: mod/notification-email
# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
:global FlushEmailQueue;
:global LogForwardFilterLogForwarding;
:global NotificationEMailSubject;
:global NotificationFunctions;
:global QuotedPrintable;
:global SendEMail;
:global SendEMail2;
# flush e-mail queue
:set FlushEmailQueue do={
:global EmailQueue;
:global EitherOr;
:global IsDNSResolving;
:global IsTimeSync;
:global LogPrintExit2;
:local AllDone true;
:local QueueLen [ :len $EmailQueue ];
:local Scheduler [ /system/scheduler/find where name=$0 ];
:if ([ :len $Scheduler ] > 0 && [ /system/scheduler/get $Scheduler interval ] < 1m) do={
/system/scheduler/set interval=1m comment="Doing initial checks..." $Scheduler;
}
:if ([ /tool/e-mail/get last-status ] = "in-progress") do={
$LogPrintExit2 debug $0 ("Sending mail is currently in progress, not flushing.") false;
:return false;
}
:if ([ $IsTimeSync ] = false) do={
$LogPrintExit2 debug $0 ("Time is not synced, not flushing.") false;
:return false;
}
:if ([ :typeof [ :toip [ /tool/e-mail/get address ] ] ] != "ip" && [ $IsDNSResolving ] = false) do={
$LogPrintExit2 debug $0 ("Server address is a DNS name and resolving fails, not flushing.") false;
:return false;
}
:if ([ :len $Scheduler ] > 0 && $QueueLen = 0) do={
$LogPrintExit2 warning $0 ("Flushing E-Mail messages from scheduler, but queue is empty.") false;
}
/system/scheduler/set interval=([ $EitherOr $QueueLen 1 ] . "m") comment="Sending..." $Scheduler;
:foreach Id,Message in=$EmailQueue do={
:if ([ :typeof $Message ] = "array" ) do={
:local Attach ({});
:while ([ /tool/e-mail/get last-status ] = "in-progress") do={ :delay 1s; }
:foreach File in=[ :toarray [ $EitherOr ($Message->"attach") "" ] ] do={
:if ([ :len [ /file/find where name=$File ] ] = 1) do={
:set Attach ($Attach, $File);
} else={
$LogPrintExit2 warning $0 ("File '" . $File . "' does not exist, can not attach.") false;
}
}
/tool/e-mail/send to=($Message->"to") cc=($Message->"cc") subject=($Message->"subject") \
body=($Message->"body") file=$Attach;
:local Wait true;
:do {
:delay 1s;
:local Status [ /tool/e-mail/get last-status ];
:if ($Status = "succeeded") do={
:set ($EmailQueue->$Id);
:set Wait false;
:if (($Message->"remove-attach") = true) do={
:foreach File in=$Attach do={
/file/remove $File;
}
}
}
:if ($Status = "failed") do={
:set AllDone false;
:set Wait false;
}
} while=($Wait = true);
}
}
:if ($AllDone = true && $QueueLen = [ :len $EmailQueue ]) do={
/system/scheduler/remove $Scheduler;
:set EmailQueue;
} else={
/system/scheduler/set interval=1m comment="Waiting for retry..." $Scheduler;
}
}
# generate filter for log-forward
:set LogForwardFilterLogForwarding do={
:global EscapeForRegEx;
:global NotificationEMailSubject;
:global SymbolForNotification;
:return ("^Error sending e-mail <(" . \
[ $EscapeForRegEx [ $NotificationEMailSubject ([ $SymbolForNotification \
"memo" ] . "Log Forwarding") ] ] . "|" . \
[ $EscapeForRegEx [ $NotificationEMailSubject ([ $SymbolForNotification \
"warning-sign" ] . "Log Forwarding") ] ] . ")>:");
}
# generate the e-mail subject
:set NotificationEMailSubject do={
:global Identity;
:global IdentityExtra;
:global QuotedPrintable;
:return [ $QuotedPrintable ("[" . $IdentityExtra . $Identity . "] " . $1) ];
}
# send notification via e-mail - expects one array argument
:set ($NotificationFunctions->"email") do={
:local Notification $1;
:global EmailGeneralTo;
:global EmailGeneralToOverride;
:global EmailGeneralCc;
:global EmailGeneralCcOverride;
:global EmailQueue;
:global EitherOr;
:global IfThenElse;
:global NotificationEMailSubject;
:local To [ $EitherOr ($EmailGeneralToOverride->($Notification->"origin")) $EmailGeneralTo ];
:local Cc [ $EitherOr ($EmailGeneralCcOverride->($Notification->"origin")) $EmailGeneralCc ];
:local EMailSettings [ /tool/e-mail/get ];
:if ([ :len $To ] = 0 || ($EMailSettings->"address") = "0.0.0.0" || ($EMailSettings->"from") = "<>") do={
:return false;
}
:if ([ :typeof $EmailQueue ] = "nothing") do={
:set EmailQueue ({});
}
:local Signature [ /system/note/get note ];
:set ($EmailQueue->[ :len $EmailQueue ]) {
to=$To; cc=$Cc;
subject=[ $NotificationEMailSubject ($Notification->"subject") ];
body=(($Notification->"message") . \
[ $IfThenElse ([ :len ($Notification->"link") ] > 0) ("\n\n" . ($Notification->"link")) "" ] . \
[ $IfThenElse ([ :len $Signature ] > 0) ("\n-- \n" . $Signature) "" ]); \
attach=($Notification->"attach"); remove-attach=($Notification->"remove-attach") };
:if ([ :len [ /system/scheduler/find where name="\$FlushEmailQueue" ] ] = 0) do={
/system/scheduler/add name="\$FlushEmailQueue" interval=1s start-time=startup \
comment="Queuing new mail..." on-event=(":global FlushEmailQueue; \$FlushEmailQueue;");
}
}
# convert string to quoted-printable
:global QuotedPrintable do={
:local Input [ :tostr $1 ];
:if ([ :len $Input ] = 0) do={
:return $Input;
}
:local Return "";
:local Chars ("\80\81\82\83\84\85\86\87\88\89\8A\8B\8C\8D\8E\8F\90\91\92\93\94\95\96\97" . \
"\98\99\9A\9B\9C\9D\9E\9F\A0\A1\A2\A3\A4\A5\A6\A7\A8\A9\AA\AB\AC\AD\AE\AF\B0\B1\B2\B3" . \
"\B4\B5\B6\B7\B8\B9\BA\BB\BC\BD\BE\BF\C0\C1\C2\C3\C4\C5\C6\C7\C8\C9\CA\CB\CC\CD\CE\CF" . \
"\D0\D1\D2\D3\D4\D5\D6\D7\D8\D9\DA\DB\DC\DD\DE\DF\E0\E1\E2\E3\E4\E5\E6\E7\E8\E9\EA\EB" . \
"\EC\ED\EE\EF\F0\F1\F2\F3\F4\F5\F6\F7\F8\F9\FA\FB\FC\FD\FE\FF");
:local Hex { "0"; "1"; "2"; "3"; "4"; "5"; "6"; "7"; "8"; "9"; "A"; "B"; "C"; "D"; "E"; "F" };
:for I from=0 to=([ :len $Input ] - 1) do={
:local Char [ :pick $Input $I ];
:local Replace [ :find $Chars $Char ];
:if ($Char = "=") do={
:set Char "=3D";
}
:if ([ :typeof $Replace ] = "num") do={
:set Char ("=" . ($Hex->($Replace / 16 + 8)) . ($Hex->($Replace % 16)));
}
:set Return ($Return . $Char);
}
:if ($Input = $Return) do={
:return $Input;
}
:return ("=\?utf-8\?Q\?" . $Return . "\?=");
}
# send notification via e-mail - expects at least two string arguments
:set SendEMail do={
:global SendEMail2;
$SendEMail2 ({ subject=$1; message=$2; link=$3 });
}
# send notification via e-mail - expects one array argument
:set SendEMail2 do={
:local Notification $1;
:global NotificationFunctions;
($NotificationFunctions->"email") ("\$NotificationFunctions->\"email\"") $Notification;
}
#
# dummy for migration

206
mod/notification-email.rsc Normal file
View file

@ -0,0 +1,206 @@
#!rsc by RouterOS
# RouterOS script: mod/notification-email
# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
:global FlushEmailQueue;
:global LogForwardFilterLogForwarding;
:global NotificationEMailSubject;
:global NotificationFunctions;
:global QuotedPrintable;
:global SendEMail;
:global SendEMail2;
# flush e-mail queue
:set FlushEmailQueue do={
:global EmailQueue;
:global EitherOr;
:global IsDNSResolving;
:global IsTimeSync;
:global LogPrintExit2;
:local AllDone true;
:local QueueLen [ :len $EmailQueue ];
:local Scheduler [ /system/scheduler/find where name=$0 ];
:if ([ :len $Scheduler ] > 0 && [ /system/scheduler/get $Scheduler interval ] < 1m) do={
/system/scheduler/set interval=1m comment="Doing initial checks..." $Scheduler;
}
:if ([ /tool/e-mail/get last-status ] = "in-progress") do={
$LogPrintExit2 debug $0 ("Sending mail is currently in progress, not flushing.") false;
:return false;
}
:if ([ $IsTimeSync ] = false) do={
$LogPrintExit2 debug $0 ("Time is not synced, not flushing.") false;
:return false;
}
:if ([ :typeof [ :toip [ /tool/e-mail/get address ] ] ] != "ip" && [ $IsDNSResolving ] = false) do={
$LogPrintExit2 debug $0 ("Server address is a DNS name and resolving fails, not flushing.") false;
:return false;
}
:if ([ :len $Scheduler ] > 0 && $QueueLen = 0) do={
$LogPrintExit2 warning $0 ("Flushing E-Mail messages from scheduler, but queue is empty.") false;
}
/system/scheduler/set interval=([ $EitherOr $QueueLen 1 ] . "m") comment="Sending..." $Scheduler;
:foreach Id,Message in=$EmailQueue do={
:if ([ :typeof $Message ] = "array" ) do={
:local Attach ({});
:while ([ /tool/e-mail/get last-status ] = "in-progress") do={ :delay 1s; }
:foreach File in=[ :toarray [ $EitherOr ($Message->"attach") "" ] ] do={
:if ([ :len [ /file/find where name=$File ] ] = 1) do={
:set Attach ($Attach, $File);
} else={
$LogPrintExit2 warning $0 ("File '" . $File . "' does not exist, can not attach.") false;
}
}
/tool/e-mail/send to=($Message->"to") cc=($Message->"cc") subject=($Message->"subject") \
body=($Message->"body") file=$Attach;
:local Wait true;
:do {
:delay 1s;
:local Status [ /tool/e-mail/get last-status ];
:if ($Status = "succeeded") do={
:set ($EmailQueue->$Id);
:set Wait false;
:if (($Message->"remove-attach") = true) do={
:foreach File in=$Attach do={
/file/remove $File;
}
}
}
:if ($Status = "failed") do={
:set AllDone false;
:set Wait false;
}
} while=($Wait = true);
}
}
:if ($AllDone = true && $QueueLen = [ :len $EmailQueue ]) do={
/system/scheduler/remove $Scheduler;
:set EmailQueue;
} else={
/system/scheduler/set interval=1m comment="Waiting for retry..." $Scheduler;
}
}
# generate filter for log-forward
:set LogForwardFilterLogForwarding do={
:global EscapeForRegEx;
:global NotificationEMailSubject;
:global SymbolForNotification;
:return ("^Error sending e-mail <(" . \
[ $EscapeForRegEx [ $NotificationEMailSubject ([ $SymbolForNotification \
"memo" ] . "Log Forwarding") ] ] . "|" . \
[ $EscapeForRegEx [ $NotificationEMailSubject ([ $SymbolForNotification \
"warning-sign" ] . "Log Forwarding") ] ] . ")>:");
}
# generate the e-mail subject
:set NotificationEMailSubject do={
:global Identity;
:global IdentityExtra;
:global QuotedPrintable;
:return [ $QuotedPrintable ("[" . $IdentityExtra . $Identity . "] " . $1) ];
}
# send notification via e-mail - expects one array argument
:set ($NotificationFunctions->"email") do={
:local Notification $1;
:global EmailGeneralTo;
:global EmailGeneralToOverride;
:global EmailGeneralCc;
:global EmailGeneralCcOverride;
:global EmailQueue;
:global EitherOr;
:global IfThenElse;
:global NotificationEMailSubject;
:local To [ $EitherOr ($EmailGeneralToOverride->($Notification->"origin")) $EmailGeneralTo ];
:local Cc [ $EitherOr ($EmailGeneralCcOverride->($Notification->"origin")) $EmailGeneralCc ];
:local EMailSettings [ /tool/e-mail/get ];
:if ([ :len $To ] = 0 || ($EMailSettings->"address") = "0.0.0.0" || ($EMailSettings->"from") = "<>") do={
:return false;
}
:if ([ :typeof $EmailQueue ] = "nothing") do={
:set EmailQueue ({});
}
:local Signature [ /system/note/get note ];
:set ($EmailQueue->[ :len $EmailQueue ]) {
to=$To; cc=$Cc;
subject=[ $NotificationEMailSubject ($Notification->"subject") ];
body=(($Notification->"message") . \
[ $IfThenElse ([ :len ($Notification->"link") ] > 0) ("\n\n" . ($Notification->"link")) "" ] . \
[ $IfThenElse ([ :len $Signature ] > 0) ("\n-- \n" . $Signature) "" ]); \
attach=($Notification->"attach"); remove-attach=($Notification->"remove-attach") };
:if ([ :len [ /system/scheduler/find where name="\$FlushEmailQueue" ] ] = 0) do={
/system/scheduler/add name="\$FlushEmailQueue" interval=1s start-time=startup \
comment="Queuing new mail..." on-event=(":global FlushEmailQueue; \$FlushEmailQueue;");
}
}
# convert string to quoted-printable
:global QuotedPrintable do={
:local Input [ :tostr $1 ];
:if ([ :len $Input ] = 0) do={
:return $Input;
}
:local Return "";
:local Chars ("\80\81\82\83\84\85\86\87\88\89\8A\8B\8C\8D\8E\8F\90\91\92\93\94\95\96\97" . \
"\98\99\9A\9B\9C\9D\9E\9F\A0\A1\A2\A3\A4\A5\A6\A7\A8\A9\AA\AB\AC\AD\AE\AF\B0\B1\B2\B3" . \
"\B4\B5\B6\B7\B8\B9\BA\BB\BC\BD\BE\BF\C0\C1\C2\C3\C4\C5\C6\C7\C8\C9\CA\CB\CC\CD\CE\CF" . \
"\D0\D1\D2\D3\D4\D5\D6\D7\D8\D9\DA\DB\DC\DD\DE\DF\E0\E1\E2\E3\E4\E5\E6\E7\E8\E9\EA\EB" . \
"\EC\ED\EE\EF\F0\F1\F2\F3\F4\F5\F6\F7\F8\F9\FA\FB\FC\FD\FE\FF");
:local Hex { "0"; "1"; "2"; "3"; "4"; "5"; "6"; "7"; "8"; "9"; "A"; "B"; "C"; "D"; "E"; "F" };
:for I from=0 to=([ :len $Input ] - 1) do={
:local Char [ :pick $Input $I ];
:local Replace [ :find $Chars $Char ];
:if ($Char = "=") do={
:set Char "=3D";
}
:if ([ :typeof $Replace ] = "num") do={
:set Char ("=" . ($Hex->($Replace / 16 + 8)) . ($Hex->($Replace % 16)));
}
:set Return ($Return . $Char);
}
:if ($Input = $Return) do={
:return $Input;
}
:return ("=\?utf-8\?Q\?" . $Return . "\?=");
}
# send notification via e-mail - expects at least two string arguments
:set SendEMail do={
:global SendEMail2;
$SendEMail2 ({ subject=$1; message=$2; link=$3 });
}
# send notification via e-mail - expects one array argument
:set SendEMail2 do={
:local Notification $1;
:global NotificationFunctions;
($NotificationFunctions->"email") ("\$NotificationFunctions->\"email\"") $Notification;
}

View file

@ -1,165 +1,3 @@
#!rsc by RouterOS
# RouterOS script: mod/notification-matrix
# Copyright (c) 2013-2023 Michael Gisbers <michael@gisbers.de>
# Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
:global FlushMatrixQueue;
:global NotificationFunctions;
:global SendMatrix;
:global SendMatrix2;
# flush Matrix queue
:set FlushMatrixQueue do={
:global MatrixQueue;
:global IsFullyConnected;
:global LogPrintExit2;
:if ([ $IsFullyConnected ] = false) do={
$LogPrintExit2 debug $0 ("System is not fully connected, not flushing.") false;
:return false;
}
:local AllDone true;
:local QueueLen [ :len $MatrixQueue ];
:if ([ :len [ /system/scheduler/find where name=$0 ] ] > 0 && $QueueLen = 0) do={
$LogPrintExit2 warning $0 ("Flushing Matrix messages from scheduler, but queue is empty.") false;
}
:foreach Id,Message in=$MatrixQueue do={
:if ([ :typeof $Message ] = "array" ) do={
:do {
/tool/fetch check-certificate=yes-without-crl output=none http-method=post \
("https://" . $Message->"homeserver" . "/_matrix/client/r0/rooms/" . $Message->"room" . \
"/send/m.room.message?access_token=" . $Message->"accesstoken") \
http-data=("{ \"msgtype\": \"m.text\", \"body\": \"" . $Message->"plain" . "\"," . \
"\"format\": \"org.matrix.custom.html\", \"formatted_body\": \"" . \
$Message->"formatted" . "\" }") as-value;
:set ($MatrixQueue->$Id);
} on-error={
$LogPrintExit2 debug $0 ("Sending queued Matrix message failed.") false;
:set AllDone false;
}
}
}
:if ($AllDone = true && $QueueLen = [ :len $MatrixQueue ]) do={
/system/scheduler/remove [ find where name=$0 ];
:set MatrixQueue;
}
}
# send notification via Matrix - expects one array argument
:set ($NotificationFunctions->"matrix") do={
:local Notification $1;
:global Identity;
:global IdentityExtra;
:global MatrixAccessToken;
:global MatrixAccessTokenOverride;
:global MatrixHomeServer;
:global MatrixHomeServerOverride;
:global MatrixQueue;
:global MatrixRoom;
:global MatrixRoomOverride;
:global EitherOr;
:global LogPrintExit2;
:global SymbolForNotification;
:local PrepareText do={
:local Input [ :tostr $1 ];
:if ([ :len $Input ] = 0) do={
:return "";
}
:local Return "";
:local Chars {
"plain"={ "\\"; "\""; "\n" };
"format"={ "\\"; "\""; "\n"; "&"; "<"; ">" };
}
:local Subs {
"plain"={ "\\\\"; "\\\""; "\\n" };
"format"={ "\\\\"; "&quot;"; "<br/>"; "&amp;"; "&lt;"; "&gt;" };
}
:for I from=0 to=([ :len $Input ] - 1) do={
:local Char [ :pick $Input $I ];
:local Replace [ :find ($Chars->$2) $Char ];
:if ([ :typeof $Replace ] = "num") do={
:set Char ($Subs->$2->$Replace);
}
:set Return ($Return . $Char);
}
:return $Return;
}
:local AccessToken [ $EitherOr ($MatrixAccessTokenOverride->($Notification->"origin")) $MatrixAccessToken ];
:local HomeServer [ $EitherOr ($MatrixHomeServerOverride->($Notification->"origin")) $MatrixHomeServer ];
:local Room [ $EitherOr ($MatrixRoomOverride->($Notification->"origin")) $MatrixRoom ];
:if ([ :len $AccessToken ] = 0 || [ :len $HomeServer ] = 0 || [ :len $Room ] = 0) do={
:return false;
}
:local Plain [ $PrepareText ("## [" . $IdentityExtra . $Identity . "] " . \
($Notification->"subject") . "\n```\n" . ($Notification->"message") . "\n```") "plain" ];
:local Formatted ("<h2>" . [ $PrepareText ("[" . $IdentityExtra . $Identity . "] " . \
($Notification->"subject")) "format" ] . "</h2>" . "<pre><code>" . \
[ $PrepareText ($Notification->"message") "format" ] . "</code></pre>");
:if ([ :len ($Notification->"link") ] > 0) do={
:set Plain ($Plain . "\\n" . [ $SymbolForNotification "link" ] . \
[ $PrepareText ("[" . $Notification->"link" . "](" . $Notification->"link" . ")") "plain" ]);
:set Formatted ($Formatted . "<br/>" . [ $SymbolForNotification "link" ] . \
"<a href=\\\"" . [ $PrepareText ($Notification->"link") "format" ] . "\\\">" . \
[ $PrepareText ($Notification->"link") "format" ] . "</a>");
}
:do {
/tool/fetch check-certificate=yes-without-crl output=none http-method=post \
("https://" . $HomeServer . "/_matrix/client/r0/rooms/" . $Room . \
"/send/m.room.message?access_token=" . $AccessToken) \
http-data=("{ \"msgtype\": \"m.text\", \"body\": \"" . $Plain . "\"," . \
"\"format\": \"org.matrix.custom.html\", \"formatted_body\": \"" . \
$Formatted . "\" }") as-value;
} on-error={
$LogPrintExit2 info $0 ("Failed sending Matrix notification! Queuing...") false;
:if ([ :typeof $MatrixQueue ] = "nothing") do={
:set MatrixQueue ({});
}
:local Text ([ $SymbolForNotification "alarm-clock" ] . \
"This message was queued since " . [ /system/clock/get date ] . \
" " . [ /system/clock/get time ] . " and may be obsolete.");
:set Plain ($Plain . "\\n" . $Text);
:set Formatted ($Formatted . "<br/>" . $Text);
:set ($MatrixQueue->[ :len $MatrixQueue ]) { room=$Room; \
accesstoken=$AccessToken; homeserver=$HomeServer; \
plain=$Plain; formatted=$Formatted };
:if ([ :len [ /system/scheduler/find where name="\$FlushMatrixQueue" ] ] = 0) do={
/system/scheduler/add name="\$FlushMatrixQueue" interval=1m start-time=startup \
on-event=(":global FlushMatrixQueue; \$FlushMatrixQueue;");
}
}
}
# send notification via Matrix - expects at least two string arguments
:set SendMatrix do={
:global SendMatrix2;
$SendMatrix2 ({ subject=$1; message=$2; link=$3 });
}
# send notification via Matrix - expects one array argument
:set SendMatrix2 do={
:local Notification $1;
:global NotificationFunctions;
($NotificationFunctions->"matrix") ("\$NotificationFunctions->\"matrix\"") $Notification;
}
#
# dummy for migration

165
mod/notification-matrix.rsc Normal file
View file

@ -0,0 +1,165 @@
#!rsc by RouterOS
# RouterOS script: mod/notification-matrix
# Copyright (c) 2013-2023 Michael Gisbers <michael@gisbers.de>
# Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
:global FlushMatrixQueue;
:global NotificationFunctions;
:global SendMatrix;
:global SendMatrix2;
# flush Matrix queue
:set FlushMatrixQueue do={
:global MatrixQueue;
:global IsFullyConnected;
:global LogPrintExit2;
:if ([ $IsFullyConnected ] = false) do={
$LogPrintExit2 debug $0 ("System is not fully connected, not flushing.") false;
:return false;
}
:local AllDone true;
:local QueueLen [ :len $MatrixQueue ];
:if ([ :len [ /system/scheduler/find where name=$0 ] ] > 0 && $QueueLen = 0) do={
$LogPrintExit2 warning $0 ("Flushing Matrix messages from scheduler, but queue is empty.") false;
}
:foreach Id,Message in=$MatrixQueue do={
:if ([ :typeof $Message ] = "array" ) do={
:do {
/tool/fetch check-certificate=yes-without-crl output=none http-method=post \
("https://" . $Message->"homeserver" . "/_matrix/client/r0/rooms/" . $Message->"room" . \
"/send/m.room.message?access_token=" . $Message->"accesstoken") \
http-data=("{ \"msgtype\": \"m.text\", \"body\": \"" . $Message->"plain" . "\"," . \
"\"format\": \"org.matrix.custom.html\", \"formatted_body\": \"" . \
$Message->"formatted" . "\" }") as-value;
:set ($MatrixQueue->$Id);
} on-error={
$LogPrintExit2 debug $0 ("Sending queued Matrix message failed.") false;
:set AllDone false;
}
}
}
:if ($AllDone = true && $QueueLen = [ :len $MatrixQueue ]) do={
/system/scheduler/remove [ find where name=$0 ];
:set MatrixQueue;
}
}
# send notification via Matrix - expects one array argument
:set ($NotificationFunctions->"matrix") do={
:local Notification $1;
:global Identity;
:global IdentityExtra;
:global MatrixAccessToken;
:global MatrixAccessTokenOverride;
:global MatrixHomeServer;
:global MatrixHomeServerOverride;
:global MatrixQueue;
:global MatrixRoom;
:global MatrixRoomOverride;
:global EitherOr;
:global LogPrintExit2;
:global SymbolForNotification;
:local PrepareText do={
:local Input [ :tostr $1 ];
:if ([ :len $Input ] = 0) do={
:return "";
}
:local Return "";
:local Chars {
"plain"={ "\\"; "\""; "\n" };
"format"={ "\\"; "\""; "\n"; "&"; "<"; ">" };
}
:local Subs {
"plain"={ "\\\\"; "\\\""; "\\n" };
"format"={ "\\\\"; "&quot;"; "<br/>"; "&amp;"; "&lt;"; "&gt;" };
}
:for I from=0 to=([ :len $Input ] - 1) do={
:local Char [ :pick $Input $I ];
:local Replace [ :find ($Chars->$2) $Char ];
:if ([ :typeof $Replace ] = "num") do={
:set Char ($Subs->$2->$Replace);
}
:set Return ($Return . $Char);
}
:return $Return;
}
:local AccessToken [ $EitherOr ($MatrixAccessTokenOverride->($Notification->"origin")) $MatrixAccessToken ];
:local HomeServer [ $EitherOr ($MatrixHomeServerOverride->($Notification->"origin")) $MatrixHomeServer ];
:local Room [ $EitherOr ($MatrixRoomOverride->($Notification->"origin")) $MatrixRoom ];
:if ([ :len $AccessToken ] = 0 || [ :len $HomeServer ] = 0 || [ :len $Room ] = 0) do={
:return false;
}
:local Plain [ $PrepareText ("## [" . $IdentityExtra . $Identity . "] " . \
($Notification->"subject") . "\n```\n" . ($Notification->"message") . "\n```") "plain" ];
:local Formatted ("<h2>" . [ $PrepareText ("[" . $IdentityExtra . $Identity . "] " . \
($Notification->"subject")) "format" ] . "</h2>" . "<pre><code>" . \
[ $PrepareText ($Notification->"message") "format" ] . "</code></pre>");
:if ([ :len ($Notification->"link") ] > 0) do={
:set Plain ($Plain . "\\n" . [ $SymbolForNotification "link" ] . \
[ $PrepareText ("[" . $Notification->"link" . "](" . $Notification->"link" . ")") "plain" ]);
:set Formatted ($Formatted . "<br/>" . [ $SymbolForNotification "link" ] . \
"<a href=\\\"" . [ $PrepareText ($Notification->"link") "format" ] . "\\\">" . \
[ $PrepareText ($Notification->"link") "format" ] . "</a>");
}
:do {
/tool/fetch check-certificate=yes-without-crl output=none http-method=post \
("https://" . $HomeServer . "/_matrix/client/r0/rooms/" . $Room . \
"/send/m.room.message?access_token=" . $AccessToken) \
http-data=("{ \"msgtype\": \"m.text\", \"body\": \"" . $Plain . "\"," . \
"\"format\": \"org.matrix.custom.html\", \"formatted_body\": \"" . \
$Formatted . "\" }") as-value;
} on-error={
$LogPrintExit2 info $0 ("Failed sending Matrix notification! Queuing...") false;
:if ([ :typeof $MatrixQueue ] = "nothing") do={
:set MatrixQueue ({});
}
:local Text ([ $SymbolForNotification "alarm-clock" ] . \
"This message was queued since " . [ /system/clock/get date ] . \
" " . [ /system/clock/get time ] . " and may be obsolete.");
:set Plain ($Plain . "\\n" . $Text);
:set Formatted ($Formatted . "<br/>" . $Text);
:set ($MatrixQueue->[ :len $MatrixQueue ]) { room=$Room; \
accesstoken=$AccessToken; homeserver=$HomeServer; \
plain=$Plain; formatted=$Formatted };
:if ([ :len [ /system/scheduler/find where name="\$FlushMatrixQueue" ] ] = 0) do={
/system/scheduler/add name="\$FlushMatrixQueue" interval=1m start-time=startup \
on-event=(":global FlushMatrixQueue; \$FlushMatrixQueue;");
}
}
}
# send notification via Matrix - expects at least two string arguments
:set SendMatrix do={
:global SendMatrix2;
$SendMatrix2 ({ subject=$1; message=$2; link=$3 });
}
# send notification via Matrix - expects one array argument
:set SendMatrix2 do={
:local Notification $1;
:global NotificationFunctions;
($NotificationFunctions->"matrix") ("\$NotificationFunctions->\"matrix\"") $Notification;
}

View file

@ -1,176 +1,3 @@
#!rsc by RouterOS
# RouterOS script: mod/notification-telegram
# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
:global FlushTelegramQueue;
:global NotificationFunctions;
:global SendTelegram;
:global SendTelegram2;
# flush telegram queue
:set FlushTelegramQueue do={
:global TelegramQueue;
:global IsFullyConnected;
:global LogPrintExit2;
:if ([ $IsFullyConnected ] = false) do={
$LogPrintExit2 debug $0 ("System is not fully connected, not flushing.") false;
:return false;
}
:local AllDone true;
:local QueueLen [ :len $TelegramQueue ];
:if ([ :len [ /system/scheduler/find where name=$0 ] ] > 0 && $QueueLen = 0) do={
$LogPrintExit2 warning $0 ("Flushing Telegram messages from scheduler, but queue is empty.") false;
}
:foreach Id,Message in=$TelegramQueue do={
:if ([ :typeof $Message ] = "array" ) do={
:do {
/tool/fetch check-certificate=yes-without-crl output=none http-method=post \
("https://api.telegram.org/bot" . ($Message->"tokenid") . "/sendMessage") \
http-data=("chat_id=" . ($Message->"chatid") . \
"&disable_notification=" . ($Message->"silent") . \
"&reply_to_message_id=" . ($Notification->"replyto") . \
"&disable_web_page_preview=true&parse_mode=" . ($Message->"parsemode") . \
"&text=" . ($Message->"text")) as-value;
:set ($TelegramQueue->$Id);
} on-error={
$LogPrintExit2 debug $0 ("Sending queued Telegram message failed.") false;
:set AllDone false;
}
}
}
:if ($AllDone = true && $QueueLen = [ :len $TelegramQueue ]) do={
/system/scheduler/remove [ find where name=$0 ];
:set TelegramQueue;
}
}
# send notification via telegram - expects one array argument
:set ($NotificationFunctions->"telegram") do={
:local Notification $1;
:global Identity;
:global IdentityExtra;
:global TelegramChatId;
:global TelegramChatIdOverride;
:global TelegramFixedWidthFont;
:global TelegramQueue;
:global TelegramTokenId;
:global TelegramTokenIdOverride;
:global CertificateAvailable;
:global CharacterReplace;
:global EitherOr;
:global IfThenElse;
:global LogPrintExit2;
:global SymbolForNotification;
:global UrlEncode;
:local EscapeMD do={
:global TelegramFixedWidthFont;
:global CharacterReplace;
:global IfThenElse;
:if ($TelegramFixedWidthFont != true) do={
:return ($1 . [ $IfThenElse ($2 = "body") ("\n") "" ]);
}
:local Return $1;
:local Chars {
"body"={ "\\"; "`" };
"plain"={ "_"; "*"; "["; "]"; "("; ")"; "~"; "`"; ">";
"#"; "+"; "-"; "="; "|"; "{"; "}"; "."; "!" };
}
:foreach Char in=($Chars->$2) do={
:set Return [ $CharacterReplace $Return $Char ("\\" . $Char) ];
}
:if ($2 = "body") do={
:return ("```\n" . $Return . "\n```");
}
:return $Return;
}
:local ChatId [ $EitherOr ($Notification->"chatid") \
[ $EitherOr ($TelegramChatIdOverride->($Notification->"origin")) $TelegramChatId ] ];
:local TokenId [ $EitherOr ($TelegramTokenIdOverride->($Notification->"origin")) $TelegramTokenId ];
:if ([ :len $TokenId ] = 0 || [ :len $ChatId ] = 0) do={
:return false;
}
:local Truncated false;
:local Text ("*__" . [ $EscapeMD ("[" . $IdentityExtra . $Identity . "] " . \
($Notification->"subject")) "plain" ] . "__*\n\n");
:local LenSubject [ :len $Text ];
:local LenMessage [ :len ($Notification->"message") ];
:local LenLink [ :len ($Notification->"link") ];
:local LenSum ($LenSubject + $LenMessage + $LenLink);
:if ($LenSum > 3968) do={
:set Text ($Text . [ $EscapeMD ([ :pick ($Notification->"message") 0 (3840 - $LenSubject - $LenLink) ] . "...") "body" ]);
:set Truncated true;
} else={
:set Text ($Text . [ $EscapeMD ($Notification->"message") "body" ]);
}
:if ($LenLink > 0) do={
:set Text ($Text . "\n" . [ $SymbolForNotification "link" ] . [ $EscapeMD ($Notification->"link") "plain" ]);
}
:if ($Truncated = true) do={
:set Text ($Text . "\n" . [ $SymbolForNotification "scissors" ] . \
[ $EscapeMD ("The message was too long and has been truncated, cut off " . \
(($LenSum - [ :len $Text ]) * 100 / $LenSum) . "%!") "plain" ]);
}
:set Text [ $UrlEncode $Text ];
:local ParseMode [ $IfThenElse ($TelegramFixedWidthFont = true) "MarkdownV2" "" ];
:do {
:if ([ $CertificateAvailable "Go Daddy Secure Certificate Authority - G2" ] = false) do={
$LogPrintExit2 warning $0 ("Downloading required certificate failed.") true;
}
/tool/fetch check-certificate=yes-without-crl output=none http-method=post \
("https://api.telegram.org/bot" . $TokenId . "/sendMessage") \
http-data=("chat_id=" . $ChatId . "&disable_notification=" . ($Notification->"silent") . \
"&reply_to_message_id=" . ($Notification->"replyto") . \
"&disable_web_page_preview=true&parse_mode=" . $ParseMode . "&text=" . $Text) as-value;
} on-error={
$LogPrintExit2 info $0 ("Failed sending telegram notification! Queuing...") false;
:if ([ :typeof $TelegramQueue ] = "nothing") do={
:set TelegramQueue ({});
}
:set Text ($Text . [ $UrlEncode ("\n" . [ $SymbolForNotification "alarm-clock" ] . \
[ $EscapeMD ("This message was queued since " . [ /system/clock/get date ] . \
" " . [ /system/clock/get time ] . " and may be obsolete.") "plain" ]) ]);
:set ($TelegramQueue->[ :len $TelegramQueue ]) { chatid=$ChatId; tokenid=$TokenId;
parsemode=$ParseMode; text=$Text; silent=($Notification->"silent");
replyto=($Notification->"replyto") };
:if ([ :len [ /system/scheduler/find where name="\$FlushTelegramQueue" ] ] = 0) do={
/system/scheduler/add name="\$FlushTelegramQueue" interval=1m start-time=startup \
on-event=(":global FlushTelegramQueue; \$FlushTelegramQueue;");
}
}
}
# send notification via telegram - expects at least two string arguments
:set SendTelegram do={
:global SendTelegram2;
$SendTelegram2 ({ subject=$1; message=$2; link=$3; silent=$4 });
}
# send notification via telegram - expects one array argument
:set SendTelegram2 do={
:local Notification $1;
:global NotificationFunctions;
($NotificationFunctions->"telegram") ("\$NotificationFunctions->\"telegram\"") $Notification;
}
#
# dummy for migration

View file

@ -0,0 +1,176 @@
#!rsc by RouterOS
# RouterOS script: mod/notification-telegram
# Copyright (c) 2013-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
:global FlushTelegramQueue;
:global NotificationFunctions;
:global SendTelegram;
:global SendTelegram2;
# flush telegram queue
:set FlushTelegramQueue do={
:global TelegramQueue;
:global IsFullyConnected;
:global LogPrintExit2;
:if ([ $IsFullyConnected ] = false) do={
$LogPrintExit2 debug $0 ("System is not fully connected, not flushing.") false;
:return false;
}
:local AllDone true;
:local QueueLen [ :len $TelegramQueue ];
:if ([ :len [ /system/scheduler/find where name=$0 ] ] > 0 && $QueueLen = 0) do={
$LogPrintExit2 warning $0 ("Flushing Telegram messages from scheduler, but queue is empty.") false;
}
:foreach Id,Message in=$TelegramQueue do={
:if ([ :typeof $Message ] = "array" ) do={
:do {
/tool/fetch check-certificate=yes-without-crl output=none http-method=post \
("https://api.telegram.org/bot" . ($Message->"tokenid") . "/sendMessage") \
http-data=("chat_id=" . ($Message->"chatid") . \
"&disable_notification=" . ($Message->"silent") . \
"&reply_to_message_id=" . ($Notification->"replyto") . \
"&disable_web_page_preview=true&parse_mode=" . ($Message->"parsemode") . \
"&text=" . ($Message->"text")) as-value;
:set ($TelegramQueue->$Id);
} on-error={
$LogPrintExit2 debug $0 ("Sending queued Telegram message failed.") false;
:set AllDone false;
}
}
}
:if ($AllDone = true && $QueueLen = [ :len $TelegramQueue ]) do={
/system/scheduler/remove [ find where name=$0 ];
:set TelegramQueue;
}
}
# send notification via telegram - expects one array argument
:set ($NotificationFunctions->"telegram") do={
:local Notification $1;
:global Identity;
:global IdentityExtra;
:global TelegramChatId;
:global TelegramChatIdOverride;
:global TelegramFixedWidthFont;
:global TelegramQueue;
:global TelegramTokenId;
:global TelegramTokenIdOverride;
:global CertificateAvailable;
:global CharacterReplace;
:global EitherOr;
:global IfThenElse;
:global LogPrintExit2;
:global SymbolForNotification;
:global UrlEncode;
:local EscapeMD do={
:global TelegramFixedWidthFont;
:global CharacterReplace;
:global IfThenElse;
:if ($TelegramFixedWidthFont != true) do={
:return ($1 . [ $IfThenElse ($2 = "body") ("\n") "" ]);
}
:local Return $1;
:local Chars {
"body"={ "\\"; "`" };
"plain"={ "_"; "*"; "["; "]"; "("; ")"; "~"; "`"; ">";
"#"; "+"; "-"; "="; "|"; "{"; "}"; "."; "!" };
}
:foreach Char in=($Chars->$2) do={
:set Return [ $CharacterReplace $Return $Char ("\\" . $Char) ];
}
:if ($2 = "body") do={
:return ("```\n" . $Return . "\n```");
}
:return $Return;
}
:local ChatId [ $EitherOr ($Notification->"chatid") \
[ $EitherOr ($TelegramChatIdOverride->($Notification->"origin")) $TelegramChatId ] ];
:local TokenId [ $EitherOr ($TelegramTokenIdOverride->($Notification->"origin")) $TelegramTokenId ];
:if ([ :len $TokenId ] = 0 || [ :len $ChatId ] = 0) do={
:return false;
}
:local Truncated false;
:local Text ("*__" . [ $EscapeMD ("[" . $IdentityExtra . $Identity . "] " . \
($Notification->"subject")) "plain" ] . "__*\n\n");
:local LenSubject [ :len $Text ];
:local LenMessage [ :len ($Notification->"message") ];
:local LenLink [ :len ($Notification->"link") ];
:local LenSum ($LenSubject + $LenMessage + $LenLink);
:if ($LenSum > 3968) do={
:set Text ($Text . [ $EscapeMD ([ :pick ($Notification->"message") 0 (3840 - $LenSubject - $LenLink) ] . "...") "body" ]);
:set Truncated true;
} else={
:set Text ($Text . [ $EscapeMD ($Notification->"message") "body" ]);
}
:if ($LenLink > 0) do={
:set Text ($Text . "\n" . [ $SymbolForNotification "link" ] . [ $EscapeMD ($Notification->"link") "plain" ]);
}
:if ($Truncated = true) do={
:set Text ($Text . "\n" . [ $SymbolForNotification "scissors" ] . \
[ $EscapeMD ("The message was too long and has been truncated, cut off " . \
(($LenSum - [ :len $Text ]) * 100 / $LenSum) . "%!") "plain" ]);
}
:set Text [ $UrlEncode $Text ];
:local ParseMode [ $IfThenElse ($TelegramFixedWidthFont = true) "MarkdownV2" "" ];
:do {
:if ([ $CertificateAvailable "Go Daddy Secure Certificate Authority - G2" ] = false) do={
$LogPrintExit2 warning $0 ("Downloading required certificate failed.") true;
}
/tool/fetch check-certificate=yes-without-crl output=none http-method=post \
("https://api.telegram.org/bot" . $TokenId . "/sendMessage") \
http-data=("chat_id=" . $ChatId . "&disable_notification=" . ($Notification->"silent") . \
"&reply_to_message_id=" . ($Notification->"replyto") . \
"&disable_web_page_preview=true&parse_mode=" . $ParseMode . "&text=" . $Text) as-value;
} on-error={
$LogPrintExit2 info $0 ("Failed sending telegram notification! Queuing...") false;
:if ([ :typeof $TelegramQueue ] = "nothing") do={
:set TelegramQueue ({});
}
:set Text ($Text . [ $UrlEncode ("\n" . [ $SymbolForNotification "alarm-clock" ] . \
[ $EscapeMD ("This message was queued since " . [ /system/clock/get date ] . \
" " . [ /system/clock/get time ] . " and may be obsolete.") "plain" ]) ]);
:set ($TelegramQueue->[ :len $TelegramQueue ]) { chatid=$ChatId; tokenid=$TokenId;
parsemode=$ParseMode; text=$Text; silent=($Notification->"silent");
replyto=($Notification->"replyto") };
:if ([ :len [ /system/scheduler/find where name="\$FlushTelegramQueue" ] ] = 0) do={
/system/scheduler/add name="\$FlushTelegramQueue" interval=1m start-time=startup \
on-event=(":global FlushTelegramQueue; \$FlushTelegramQueue;");
}
}
}
# send notification via telegram - expects at least two string arguments
:set SendTelegram do={
:global SendTelegram2;
$SendTelegram2 ({ subject=$1; message=$2; link=$3; silent=$4 });
}
# send notification via telegram - expects one array argument
:set SendTelegram2 do={
:local Notification $1;
:global NotificationFunctions;
($NotificationFunctions->"telegram") ("\$NotificationFunctions->\"telegram\"") $Notification;
}

View file

@ -1,46 +1,3 @@
#!rsc by RouterOS
# RouterOS script: mod/scriptrunonece
# Copyright (c) 2020-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
:global ScriptRunOnce;
# fetch and run script(s) once
:set ScriptRunOnce do={
:local Scripts [ :toarray $1 ];
:global ScriptRunOnceBaseUrl;
:global ScriptRunOnceUrlSuffix;
:global LogPrintExit2;
:global ValidateSyntax;
:foreach Script in=$Scripts do={
:if (!($Script ~ "^(ftp|https\?|sftp)://")) do={
:if ([ :len $ScriptRunOnceBaseUrl ] = 0) do={
$LogPrintExit2 warning $0 ("Script '" . $Script . "' is not an url and base url is not available.") true;
}
:set Script ($ScriptRunOnceBaseUrl . $Script . $ScriptRunOnceUrlSuffix);
}
:local Source;
:do {
:set Source ([ /tool/fetch check-certificate=yes-without-crl $Script output=user as-value ]->"data");
} on-error={
$LogPrintExit2 warning $0 ("Failed fetching script '" . $Script . "'!") false;
}
:if ([ :len $Source ] > 0) do={
:if ([ $ValidateSyntax $Source ] = true) do={
:do {
$LogPrintExit2 info $0 ("Running script '" . $Script . "' now.") false;
[ :parse $Source ];
} on-error={
$LogPrintExit2 warning $0 ("The script '" . $Script . "' failed to run!") false;
}
} else={
$LogPrintExit2 warning $0 ("The script '" . $Script . "' failed syntax validation!") false;
}
}
}
}
#
# dummy for migration

46
mod/scriptrunonce.rsc Normal file
View file

@ -0,0 +1,46 @@
#!rsc by RouterOS
# RouterOS script: mod/scriptrunonece
# Copyright (c) 2020-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
:global ScriptRunOnce;
# fetch and run script(s) once
:set ScriptRunOnce do={
:local Scripts [ :toarray $1 ];
:global ScriptRunOnceBaseUrl;
:global ScriptRunOnceUrlSuffix;
:global LogPrintExit2;
:global ValidateSyntax;
:foreach Script in=$Scripts do={
:if (!($Script ~ "^(ftp|https\?|sftp)://")) do={
:if ([ :len $ScriptRunOnceBaseUrl ] = 0) do={
$LogPrintExit2 warning $0 ("Script '" . $Script . "' is not an url and base url is not available.") true;
}
:set Script ($ScriptRunOnceBaseUrl . $Script . ".rsc" . $ScriptRunOnceUrlSuffix);
}
:local Source;
:do {
:set Source ([ /tool/fetch check-certificate=yes-without-crl $Script output=user as-value ]->"data");
} on-error={
$LogPrintExit2 warning $0 ("Failed fetching script '" . $Script . "'!") false;
}
:if ([ :len $Source ] > 0) do={
:if ([ $ValidateSyntax $Source ] = true) do={
:do {
$LogPrintExit2 info $0 ("Running script '" . $Script . "' now.") false;
[ :parse $Source ];
} on-error={
$LogPrintExit2 warning $0 ("The script '" . $Script . "' failed to run!") false;
}
} else={
$LogPrintExit2 warning $0 ("The script '" . $Script . "' failed syntax validation!") false;
}
}
}
}

View file

@ -1,76 +1,3 @@
#!rsc by RouterOS
# RouterOS script: mode-button
# Copyright (c) 2018-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# act on multiple mode and reset button presses
# https://git.eworm.de/cgit/routeros-scripts/about/doc/mode-button.md
:local 0 "mode-button";
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:global ModeButton;
:global LogPrintExit2;
:set ($ModeButton->"count") ($ModeButton->"count" + 1);
:local Scheduler [ /system/scheduler/find where name="ModeButtonScheduler" ];
:if ([ :len $Scheduler ] = 0) do={
$LogPrintExit2 info $0 ("Creating scheduler ModeButtonScheduler, counting presses...") false;
:global ModeButtonScheduler do={
:global ModeButton;
:global LogPrintExit2;
:global ModeButtonScheduler;
:global ValidateSyntax;
:local LEDInvert do={
:global ModeButtonLED;
:global IfThenElse;
:local LED [ /system/leds/find where leds=$ModeButtonLED type~"^(on|off)\$" interface=[] ];
:if ([ :len $LED ] = 0) do={
:return false;
}
/system/leds/set type=[ $IfThenElse ([ get $LED type ] = "on") "off" "on" ] $LED;
}
:local Count ($ModeButton->"count");
:local Code ($ModeButton->[ :tostr $Count ]);
:set ($ModeButton->"count") 0;
:set ModeButtonScheduler;
/system/scheduler/remove ModeButtonScheduler;
:if ([ :len $Code ] > 0) do={
:if ([ $ValidateSyntax $Code ] = true) do={
$LogPrintExit2 info $0 ("Acting on " . $Count . " mode-button presses: " . $Code) false;
:for I from=1 to=$Count do={
$LEDInvert;
:if ([ /system/routerboard/settings/get silent-boot ] = false) do={
:beep length=200ms;
}
:delay 200ms;
$LEDInvert;
:delay 200ms;
}
[ :parse $Code ];
} else={
$LogPrintExit2 warning $0 ("The code for " . $Count . " mode-button presses failed syntax validation!") false;
}
} else={
$LogPrintExit2 info $0 ("No action defined for " . $Count . " mode-button presses.") false;
}
}
/system/scheduler/add name="ModeButtonScheduler" \
on-event=":global ModeButtonScheduler; \$ModeButtonScheduler;" interval=3s;
} else={
$LogPrintExit2 debug $0 ("Updating scheduler ModeButtonScheduler...") false;
/system/scheduler/set $Scheduler start-time=[ /system/clock/get time ];
}
# dummy for migration

76
mode-button.rsc Normal file
View file

@ -0,0 +1,76 @@
#!rsc by RouterOS
# RouterOS script: mode-button
# Copyright (c) 2018-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# act on multiple mode and reset button presses
# https://git.eworm.de/cgit/routeros-scripts/about/doc/mode-button.md
:local 0 "mode-button";
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:global ModeButton;
:global LogPrintExit2;
:set ($ModeButton->"count") ($ModeButton->"count" + 1);
:local Scheduler [ /system/scheduler/find where name="ModeButtonScheduler" ];
:if ([ :len $Scheduler ] = 0) do={
$LogPrintExit2 info $0 ("Creating scheduler ModeButtonScheduler, counting presses...") false;
:global ModeButtonScheduler do={
:global ModeButton;
:global LogPrintExit2;
:global ModeButtonScheduler;
:global ValidateSyntax;
:local LEDInvert do={
:global ModeButtonLED;
:global IfThenElse;
:local LED [ /system/leds/find where leds=$ModeButtonLED type~"^(on|off)\$" interface=[] ];
:if ([ :len $LED ] = 0) do={
:return false;
}
/system/leds/set type=[ $IfThenElse ([ get $LED type ] = "on") "off" "on" ] $LED;
}
:local Count ($ModeButton->"count");
:local Code ($ModeButton->[ :tostr $Count ]);
:set ($ModeButton->"count") 0;
:set ModeButtonScheduler;
/system/scheduler/remove ModeButtonScheduler;
:if ([ :len $Code ] > 0) do={
:if ([ $ValidateSyntax $Code ] = true) do={
$LogPrintExit2 info $0 ("Acting on " . $Count . " mode-button presses: " . $Code) false;
:for I from=1 to=$Count do={
$LEDInvert;
:if ([ /system/routerboard/settings/get silent-boot ] = false) do={
:beep length=200ms;
}
:delay 200ms;
$LEDInvert;
:delay 200ms;
}
[ :parse $Code ];
} else={
$LogPrintExit2 warning $0 ("The code for " . $Count . " mode-button presses failed syntax validation!") false;
}
} else={
$LogPrintExit2 info $0 ("No action defined for " . $Count . " mode-button presses.") false;
}
}
/system/scheduler/add name="ModeButtonScheduler" \
on-event=":global ModeButtonScheduler; \$ModeButtonScheduler;" interval=3s;
} else={
$LogPrintExit2 debug $0 ("Updating scheduler ModeButtonScheduler...") false;
/system/scheduler/set $Scheduler start-time=[ /system/clock/get time ];
}

View file

@ -1,94 +1,3 @@
#!rsc by RouterOS
# RouterOS script: netwatch-dns
# Copyright (c) 2022-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# monitor and manage dns/doh with netwatch
# https://git.eworm.de/cgit/routeros-scripts/about/doc/netwatch-dns.md
:local 0 "netwatch-dns";
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:global CertificateAvailable;
:global EitherOr;
:global LogPrintExit2;
:global ParseKeyValueStore;
:global ScriptLock;
$ScriptLock $0;
:if ([ /system/resource/get uptime ] < 5m) do={
$LogPrintExit2 info $0 ("System just booted, giving netwatch some time to settle.") true;
}
:local DnsServers ({});
:local DnsFallback ({});
:local DnsCurrent [ /ip/dns/get servers ];
:foreach Host in=[ /tool/netwatch/find where comment~"dns" !disabled ] do={
:local HostVal [ /tool/netwatch/get $Host ];
:local HostInfo [ $ParseKeyValueStore ($HostVal->"comment") ];
:if ($HostVal->"status" = "up" && $HostInfo->"disabled" != true) do={
:if ($HostInfo->"dns" = true) do={
:set DnsServers ($DnsServers, $HostVal->"host");
}
:if ($HostInfo->"dns-fallback" = true) do={
:set DnsFallback ($DnsFallback, $HostVal->"host");
}
}
}
:if ([ :len $DnsServers ] > 0) do={
:if ($DnsServers != $DnsCurrent) do={
$LogPrintExit2 info $0 ("Updating DNS servers: " . [ :tostr $DnsServers ]) false;
/ip/dns/set servers=$DnsServers;
/ip/dns/cache/flush;
}
} else={
:if ([ :len $DnsFallback ] > 0) do={
:if ($DnsFallback != $DnsCurrent) do={
$LogPrintExit2 info $0 ("Updating DNS servers to fallback: " . \
[ :tostr $DnsFallback ]) false;
/ip/dns/set servers=$DnsFallback;
/ip/dns/cache/flush;
}
}
}
:local DohServer "";
:local DohCurrent [ /ip/dns/get use-doh-server ];
:local DohCert "";
:foreach Host in=[ /tool/netwatch/find where comment~"doh" !disabled ] do={
:local HostVal [ /tool/netwatch/get $Host ];
:local HostInfo [ $ParseKeyValueStore ($HostVal->"comment") ];
:if ($HostVal->"status" = "up" && $HostInfo->"doh" = true && \
$HostInfo->"disabled" != true && $DohServer = "") do={
:set DohServer [ $EitherOr ($HostInfo->"doh-url") \
("https://" . $HostVal->"host" . "/dns-query") ];
:set DohCert ($HostInfo->"doh-cert");
}
}
:if ($DohServer != "") do={
:if ($DohServer != $DohCurrent) do={
$LogPrintExit2 info $0 ("Updating DoH server: " . $DohServer) false;
:if ([ :len $DohCert ] > 0) do={
/ip/dns/set use-doh-server="";
:if ([ $CertificateAvailable $DohCert ] = false) do={
$LogPrintExit2 warning $0 ("Downloading certificate failed, trying without.") false;
}
}
/ip/dns/set use-doh-server=$DohServer;
/ip/dns/cache/flush;
}
} else={
:if ($DohCurrent != "") do={
$LogPrintExit2 info $0 ("DoH server (" . $DohCurrent . ") is down, disabling.") false;
/ip/dns/set use-doh-server="";
/ip/dns/cache/flush;
}
}
# dummy for migration

94
netwatch-dns.rsc Normal file
View file

@ -0,0 +1,94 @@
#!rsc by RouterOS
# RouterOS script: netwatch-dns
# Copyright (c) 2022-2023 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# monitor and manage dns/doh with netwatch
# https://git.eworm.de/cgit/routeros-scripts/about/doc/netwatch-dns.md
:local 0 "netwatch-dns";
:global GlobalFunctionsReady;
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
:global CertificateAvailable;
:global EitherOr;
:global LogPrintExit2;
:global ParseKeyValueStore;
:global ScriptLock;
$ScriptLock $0;
:if ([ /system/resource/get uptime ] < 5m) do={
$LogPrintExit2 info $0 ("System just booted, giving netwatch some time to settle.") true;
}
:local DnsServers ({});
:local DnsFallback ({});
:local DnsCurrent [ /ip/dns/get servers ];
:foreach Host in=[ /tool/netwatch/find where comment~"dns" !disabled ] do={
:local HostVal [ /tool/netwatch/get $Host ];
:local HostInfo [ $ParseKeyValueStore ($HostVal->"comment") ];
:if ($HostVal->"status" = "up" && $HostInfo->"disabled" != true) do={
:if ($HostInfo->"dns" = true) do={
:set DnsServers ($DnsServers, $HostVal->"host");
}
:if ($HostInfo->"dns-fallback" = true) do={
:set DnsFallback ($DnsFallback, $HostVal->"host");
}
}
}
:if ([ :len $DnsServers ] > 0) do={
:if ($DnsServers != $DnsCurrent) do={
$LogPrintExit2 info $0 ("Updating DNS servers: " . [ :tostr $DnsServers ]) false;
/ip/dns/set servers=$DnsServers;
/ip/dns/cache/flush;
}
} else={
:if ([ :len $DnsFallback ] > 0) do={
:if ($DnsFallback != $DnsCurrent) do={
$LogPrintExit2 info $0 ("Updating DNS servers to fallback: " . \
[ :tostr $DnsFallback ]) false;
/ip/dns/set servers=$DnsFallback;
/ip/dns/cache/flush;
}
}
}
:local DohServer "";
:local DohCurrent [ /ip/dns/get use-doh-server ];
:local DohCert "";
:foreach Host in=[ /tool/netwatch/find where comment~"doh" !disabled ] do={
:local HostVal [ /tool/netwatch/get $Host ];
:local HostInfo [ $ParseKeyValueStore ($HostVal->"comment") ];
:if ($HostVal->"status" = "up" && $HostInfo->"doh" = true && \
$HostInfo->"disabled" != true && $DohServer = "") do={
:set DohServer [ $EitherOr ($HostInfo->"doh-url") \
("https://" . $HostVal->"host" . "/dns-query") ];
:set DohCert ($HostInfo->"doh-cert");
}
}
:if ($DohServer != "") do={
:if ($DohServer != $DohCurrent) do={
$LogPrintExit2 info $0 ("Updating DoH server: " . $DohServer) false;
:if ([ :len $DohCert ] > 0) do={
/ip/dns/set use-doh-server="";
:if ([ $CertificateAvailable $DohCert ] = false) do={
$LogPrintExit2 warning $0 ("Downloading certificate failed, trying without.") false;
}
}
/ip/dns/set use-doh-server=$DohServer;
/ip/dns/cache/flush;
}
} else={
:if ($DohCurrent != "") do={
$LogPrintExit2 info $0 ("DoH server (" . $DohCurrent . ") is down, disabling.") false;
/ip/dns/set use-doh-server="";
/ip/dns/cache/flush;
}
}

Some files were not shown because too many files have changed in this diff Show more