aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore10
-rw-r--r--BRANCHES.md50
-rw-r--r--CONTRIBUTIONS.md19
-rw-r--r--INITIAL-COMMANDS.md42
-rw-r--r--Makefile24
-rw-r--r--README.d/01-download-certs.avifbin2105 -> 4420 bytes
-rw-r--r--README.d/02-import-certs.avifbin2266 -> 3606 bytes
-rw-r--r--README.d/03-check-certs.avifbin4850 -> 9339 bytes
-rw-r--r--README.d/05-edit-global-config-overlay.avifbin5124 -> 0 bytes
-rw-r--r--README.d/05-run-and-schedule-scripts.avif (renamed from README.d/06-run-and-schedule-scripts.avif)bin1946 -> 1946 bytes
-rw-r--r--README.d/06-schedule-update.avif (renamed from README.d/07-schedule-update.avif)bin2200 -> 2200 bytes
-rw-r--r--README.d/07-edit-global-config-overlay.avifbin0 -> 5103 bytes
-rw-r--r--README.d/08-apply-configuration.avifbin0 -> 2437 bytes
-rw-r--r--README.d/09-update-scripts.avif (renamed from README.d/08-update-scripts.avif)bin1733 -> 1733 bytes
-rw-r--r--README.d/10-install-scripts.avif (renamed from README.d/09-install-scripts.avif)bin2423 -> 2423 bytes
-rw-r--r--README.d/11-schedule-script.avif (renamed from README.d/10-schedule-script.avif)bin1847 -> 1847 bytes
-rw-r--r--README.d/12-install-custom-script.avifbin2242 -> 0 bytes
-rw-r--r--README.d/12-setup-lease-script.avif (renamed from README.d/11-setup-lease-script.avif)bin1686 -> 1686 bytes
-rw-r--r--README.d/13-install-custom-script.avifbin0 -> 4589 bytes
-rw-r--r--README.d/14-remove-script.avif (renamed from README.d/13-remove-script.avif)bin1093 -> 1093 bytes
-rw-r--r--README.d/news-and-changes-notification.svg40
-rw-r--r--README.d/notification-news-and-changes.avifbin0 -> 14861 bytes
-rw-r--r--README.d/upstream.pngbin0 -> 207 bytes
-rw-r--r--README.md154
-rw-r--r--accesslist-duplicates.capsman42
-rw-r--r--accesslist-duplicates.capsman.rsc34
-rw-r--r--accesslist-duplicates.local42
-rw-r--r--accesslist-duplicates.local.rsc34
-rw-r--r--accesslist-duplicates.template43
-rw-r--r--accesslist-duplicates.template.rsc43
-rw-r--r--accesslist-duplicates.wifi.rsc34
-rw-r--r--backup-cloud58
-rw-r--r--backup-cloud.rsc85
-rw-r--r--backup-email80
-rw-r--r--backup-email.rsc124
-rw-r--r--backup-partition36
-rw-r--r--backup-partition.rsc57
-rw-r--r--backup-upload106
-rw-r--r--backup-upload.rsc161
-rw-r--r--capsman-download-packages86
-rw-r--r--capsman-download-packages.capsman.rsc83
-rw-r--r--capsman-download-packages.template.rsc94
-rw-r--r--capsman-download-packages.wifi.rsc85
-rw-r--r--capsman-rolling-upgrade36
-rw-r--r--capsman-rolling-upgrade.capsman.rsc46
-rw-r--r--capsman-rolling-upgrade.template.rsc54
-rw-r--r--capsman-rolling-upgrade.wifi.rsc47
-rw-r--r--certificate-renew-issued38
-rw-r--r--certificate-renew-issued.rsc48
-rw-r--r--certs/Cloudflare-Inc-ECC-CA-3.pem163
-rw-r--r--certs/DigiCert-Global-G2-TLS-RSA-SHA256-2020-CA1.pem182
-rw-r--r--certs/DigiCert-TLS-Hybrid-ECC-SHA384-2020-CA1.pem (renamed from certs/DigiCert TLS Hybrid ECC SHA384 2020 CA1.pem)0
-rw-r--r--certs/E1.pem119
-rw-r--r--certs/GTS-CA-1C3.pem (renamed from certs/GTS CA 1C3.pem)0
-rw-r--r--certs/GTS-CA-1P5.pem238
-rw-r--r--certs/GlobalSign-Atlas-R3-DV-TLS-CA-2022-Q3.pem177
-rw-r--r--certs/Go-Daddy-Secure-Certificate-Authority-G2.pem (renamed from certs/Go Daddy Secure Certificate Authority - G2.pem)0
-rw-r--r--certs/Starfield-Secure-Certificate-Authority-G2.pem (renamed from certs/Starfield Secure Certificate Authority - G2.pem)0
-rw-r--r--check-certificates127
-rw-r--r--check-certificates.rsc222
-rw-r--r--check-health131
-rw-r--r--check-health.rsc178
-rw-r--r--check-lte-firmware-upgrade82
-rw-r--r--check-lte-firmware-upgrade.rsc103
-rw-r--r--check-routeros-update143
-rw-r--r--check-routeros-update.rsc166
-rw-r--r--collect-wireless-mac.capsman85
-rw-r--r--collect-wireless-mac.capsman.rsc96
-rw-r--r--collect-wireless-mac.local86
-rw-r--r--collect-wireless-mac.local.rsc97
-rw-r--r--collect-wireless-mac.template87
-rw-r--r--collect-wireless-mac.template.rsc114
-rw-r--r--collect-wireless-mac.wifi.rsc96
-rw-r--r--contrib/logo-color.d/browser-01.avifbin0 -> 42058 bytes
-rw-r--r--contrib/logo-color.d/browser-02.avifbin0 -> 29025 bytes
-rw-r--r--contrib/logo-color.d/browser-03.avifbin0 -> 26034 bytes
-rw-r--r--contrib/logo-color.d/script.js12
-rw-r--r--contrib/logo-color.d/style.css5
-rw-r--r--contrib/logo-color.html40
-rw-r--r--contrib/notification.d/script.js6
-rw-r--r--contrib/notification.d/style.css36
-rw-r--r--contrib/notification.html35
-rw-r--r--daily-psk.capsman94
-rw-r--r--daily-psk.capsman.rsc92
-rw-r--r--daily-psk.local94
-rw-r--r--daily-psk.local.rsc91
-rw-r--r--daily-psk.template100
-rw-r--r--daily-psk.template.rsc107
-rw-r--r--daily-psk.wifi.rsc92
-rw-r--r--dhcp-lease-comment.capsman30
-rw-r--r--dhcp-lease-comment.capsman.rsc39
-rw-r--r--dhcp-lease-comment.local30
-rw-r--r--dhcp-lease-comment.local.rsc39
-rw-r--r--dhcp-lease-comment.template31
-rw-r--r--dhcp-lease-comment.template.rsc44
-rw-r--r--dhcp-lease-comment.wifi.rsc39
-rw-r--r--dhcp-to-dns97
-rw-r--r--dhcp-to-dns.rsc126
-rw-r--r--doc/accesslist-duplicates.md28
-rw-r--r--doc/backup-cloud.d/notification.avifbin0 -> 11629 bytes
-rw-r--r--doc/backup-cloud.d/notification.svg46
-rw-r--r--doc/backup-cloud.md30
-rw-r--r--doc/backup-email.md18
-rw-r--r--doc/backup-partition.md22
-rw-r--r--doc/backup-upload.d/notification.avifbin0 -> 11776 bytes
-rw-r--r--doc/backup-upload.d/notification.svg45
-rw-r--r--doc/backup-upload.md23
-rw-r--r--doc/capsman-download-packages.md50
-rw-r--r--doc/capsman-rolling-upgrade.md27
-rw-r--r--doc/certificate-renew-issued.md17
-rw-r--r--doc/check-certificates.d/notification.avifbin0 -> 25274 bytes
-rw-r--r--doc/check-certificates.d/notification.svg41
-rw-r--r--doc/check-certificates.md50
-rw-r--r--doc/check-health.d/notification-01-cpu-utilization-high.avifbin0 -> 6481 bytes
-rw-r--r--doc/check-health.d/notification-01-voltage.svg36
-rw-r--r--doc/check-health.d/notification-02-cpu-utilization-ok.avifbin0 -> 6797 bytes
-rw-r--r--doc/check-health.d/notification-02-temperature-high.svg33
-rw-r--r--doc/check-health.d/notification-03-ram-utilization-high.avifbin0 -> 7527 bytes
-rw-r--r--doc/check-health.d/notification-03-temperature-ok.svg33
-rw-r--r--doc/check-health.d/notification-04-psu-fail.svg33
-rw-r--r--doc/check-health.d/notification-04-ram-utilization-ok.avifbin0 -> 6637 bytes
-rw-r--r--doc/check-health.d/notification-05-psu-ok.svg33
-rw-r--r--doc/check-health.d/notification-05-voltage.avifbin0 -> 3829 bytes
-rw-r--r--doc/check-health.d/notification-06-temperature-high.avifbin0 -> 3519 bytes
-rw-r--r--doc/check-health.d/notification-07-temperature-ok.avifbin0 -> 3727 bytes
-rw-r--r--doc/check-health.d/notification-08-psu-fail.avifbin0 -> 3474 bytes
-rw-r--r--doc/check-health.d/notification-09-psu-ok.avifbin0 -> 3531 bytes
-rw-r--r--doc/check-health.md56
-rw-r--r--doc/check-lte-firmware-upgrade.d/notification.avifbin0 -> 5077 bytes
-rw-r--r--doc/check-lte-firmware-upgrade.d/notification.svg38
-rw-r--r--doc/check-lte-firmware-upgrade.md15
-rw-r--r--doc/check-routeros-update.d/notification.avifbin0 -> 6392 bytes
-rw-r--r--doc/check-routeros-update.d/notification.svg45
-rw-r--r--doc/check-routeros-update.md50
-rw-r--r--doc/collect-wireless-mac.d/notification.avifbin0 -> 13378 bytes
-rw-r--r--doc/collect-wireless-mac.d/notification.svg44
-rw-r--r--doc/collect-wireless-mac.md31
-rw-r--r--doc/daily-psk.d/notification.avifbin0 -> 7040 bytes
-rw-r--r--doc/daily-psk.d/notification.svg40
-rw-r--r--doc/daily-psk.md57
-rw-r--r--doc/dhcp-lease-comment.md26
-rw-r--r--doc/dhcp-to-dns.md57
-rw-r--r--doc/firmware-upgrade-reboot.md13
-rw-r--r--doc/fw-addr-lists.md128
-rw-r--r--doc/global-wait.md13
-rw-r--r--doc/gps-track.md17
-rw-r--r--doc/hotspot-to-wpa.md80
-rw-r--r--doc/ip-addr-bridge.md13
-rw-r--r--doc/ipsec-to-dns.md17
-rw-r--r--doc/ipv6-update.md20
-rw-r--r--doc/lease-script.md15
-rw-r--r--doc/leds-mode.md13
-rw-r--r--doc/log-forward.d/notification.avifbin0 -> 6178 bytes
-rw-r--r--doc/log-forward.d/notification.svg37
-rw-r--r--doc/log-forward.md59
-rw-r--r--doc/mod/bridge-port-to.md13
-rw-r--r--doc/mod/bridge-port-vlan.md13
-rw-r--r--doc/mod/inspectvar.md13
-rw-r--r--doc/mod/ipcalc.md13
-rw-r--r--doc/mod/notification-email.md39
-rw-r--r--doc/mod/notification-matrix.d/01-authenticate.avifbin0 -> 4209 bytes
-rw-r--r--doc/mod/notification-matrix.d/01-home-server.avifbin2317 -> 0 bytes
-rw-r--r--doc/mod/notification-matrix.d/02-access-token.avifbin4105 -> 0 bytes
-rw-r--r--doc/mod/notification-matrix.d/02-join-room.avifbin0 -> 3955 bytes
-rw-r--r--doc/mod/notification-matrix.d/03-join-room.avifbin3166 -> 0 bytes
-rw-r--r--doc/mod/notification-matrix.md108
-rw-r--r--doc/mod/notification-ntfy.md86
-rw-r--r--doc/mod/notification-telegram.d/setuserpic.avifbin0 -> 38522 bytes
-rw-r--r--doc/mod/notification-telegram.md52
-rw-r--r--doc/mod/scriptrunonce.md21
-rw-r--r--doc/mod/ssh-keys-import.md73
-rw-r--r--doc/mode-button.md19
-rw-r--r--doc/netwatch-dns.md24
-rw-r--r--doc/netwatch-notify.d/notification-01-down.avifbin0 -> 4193 bytes
-rw-r--r--doc/netwatch-notify.d/notification-01-down.svg34
-rw-r--r--doc/netwatch-notify.d/notification-02-up.avifbin0 -> 4744 bytes
-rw-r--r--doc/netwatch-notify.d/notification-02-up.svg35
-rw-r--r--doc/netwatch-notify.md49
-rw-r--r--doc/ospf-to-leds.md13
-rw-r--r--doc/packages-update.md38
-rw-r--r--doc/ppp-on-up.md13
-rw-r--r--doc/sms-action.md17
-rw-r--r--doc/sms-forward.d/notification.avifbin0 -> 4619 bytes
-rw-r--r--doc/sms-forward.d/notification.svg36
-rw-r--r--doc/sms-forward.md63
-rw-r--r--doc/ssh-keys-import.md35
-rw-r--r--doc/super-mario-theme.md13
-rw-r--r--doc/telegram-chat.d/01-chat-specific.avifbin0 -> 38468 bytes
-rw-r--r--doc/telegram-chat.d/02-chat-all.avifbin0 -> 54713 bytes
-rw-r--r--doc/telegram-chat.d/03-reply.avifbin0 -> 50126 bytes
-rw-r--r--doc/telegram-chat.md153
-rw-r--r--doc/unattended-lte-firmware-upgrade.md14
-rw-r--r--doc/update-gre-address.md13
-rw-r--r--doc/update-tunnelbroker.md20
-rw-r--r--firmware-upgrade-reboot41
-rw-r--r--firmware-upgrade-reboot.rsc54
-rw-r--r--fw-addr-lists.d/allow3
-rw-r--r--fw-addr-lists.d/block5
-rw-r--r--fw-addr-lists.d/mikrotik5
-rw-r--r--fw-addr-lists.rsc159
-rw-r--r--global-config-overlay11
-rw-r--r--global-config-overlay.rsc12
-rw-r--r--global-config.changes112
-rw-r--r--global-config.rsc (renamed from global-config)113
-rw-r--r--global-functions.rsc (renamed from global-functions)858
-rw-r--r--global-wait.rsc (renamed from global-wait)5
-rw-r--r--gps-track34
-rw-r--r--gps-track.rsc50
-rw-r--r--hotspot-to-wpa72
-rw-r--r--hotspot-to-wpa-cleanup51
-rw-r--r--hotspot-to-wpa-cleanup.capsman.rsc74
-rw-r--r--hotspot-to-wpa-cleanup.template.rsc81
-rw-r--r--hotspot-to-wpa-cleanup.wifi.rsc74
-rw-r--r--hotspot-to-wpa.capsman.rsc98
-rw-r--r--hotspot-to-wpa.template.rsc118
-rw-r--r--hotspot-to-wpa.wifi.rsc95
-rw-r--r--ip-addr-bridge.rsc (renamed from ip-addr-bridge)2
-rw-r--r--ipsec-to-dns68
-rw-r--r--ipsec-to-dns.rsc79
-rw-r--r--ipv6-update62
-rw-r--r--ipv6-update.rsc87
-rw-r--r--lease-script54
-rw-r--r--lease-script.rsc59
-rw-r--r--leds-day-mode.rsc (renamed from leds-day-mode)2
-rw-r--r--leds-night-mode.rsc (renamed from leds-night-mode)2
-rw-r--r--leds-toggle-mode.rsc (renamed from leds-toggle-mode)2
-rw-r--r--log-forward94
-rw-r--r--log-forward.rsc102
-rw-r--r--logo.avifbin2607 -> 2001 bytes
-rw-r--r--logo.pngbin3807 -> 4428 bytes
-rw-r--r--logo.svg46
-rw-r--r--mod/bridge-port-to.rsc (renamed from mod/bridge-port-to)23
-rw-r--r--mod/bridge-port-vlan.rsc (renamed from mod/bridge-port-vlan)26
-rw-r--r--mod/inspectvar.rsc (renamed from mod/inspectvar)7
-rw-r--r--mod/ipcalc.rsc (renamed from mod/ipcalc)18
-rw-r--r--mod/notification-email133
-rw-r--r--mod/notification-email.rsc240
-rw-r--r--mod/notification-matrix.rsc (renamed from mod/notification-matrix)127
-rw-r--r--mod/notification-ntfy.rsc136
-rw-r--r--mod/notification-telegram.rsc (renamed from mod/notification-telegram)86
-rw-r--r--mod/scriptrunonce.rsc (renamed from mod/scriptrunonce)22
-rw-r--r--mod/ssh-keys-import.rsc112
-rw-r--r--mode-button76
-rw-r--r--mode-button.rsc81
-rw-r--r--netwatch-dns94
-rw-r--r--netwatch-dns.rsc130
-rw-r--r--netwatch-notify180
-rw-r--r--netwatch-notify.rsc219
-rw-r--r--news-and-changes.rsc60
-rw-r--r--ospf-to-leds35
-rw-r--r--ospf-to-leds.rsc45
-rw-r--r--packages-update93
-rw-r--r--packages-update.rsc145
-rw-r--r--ppp-on-up34
-rw-r--r--ppp-on-up.rsc40
-rw-r--r--sms-action31
-rw-r--r--sms-action.rsc37
-rw-r--r--sms-forward62
-rw-r--r--sms-forward.rsc95
-rw-r--r--ssh-keys-import11
-rw-r--r--super-mario-theme.rsc (renamed from super-mario-theme)2
-rw-r--r--telegram-chat.rsc180
-rw-r--r--unattended-lte-firmware-upgrade.rsc (renamed from unattended-lte-firmware-upgrade)20
-rw-r--r--update-gre-address31
-rw-r--r--update-gre-address.rsc42
-rw-r--r--update-tunnelbroker48
-rw-r--r--update-tunnelbroker.rsc67
267 files changed, 9157 insertions, 4790 deletions
diff --git a/.gitignore b/.gitignore
index f29d4e8..cf89f87 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,13 @@
+# backup and temporary files
*~
+
+# patches and related files
+*.orig
*.patch
+*.rej
+
+# html files (as generated from markdown)
*.html
+
+# Mac OS X folder settings file
+.DS_Store
diff --git a/BRANCHES.md b/BRANCHES.md
new file mode 100644
index 0000000..0fdbdb4
--- /dev/null
+++ b/BRANCHES.md
@@ -0,0 +1,50 @@
+Installing from branches
+========================
+
+[![GitHub stars](https://img.shields.io/github/stars/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=red)](https://github.com/eworm-de/routeros-scripts/stargazers)
+[![GitHub forks](https://img.shields.io/github/forks/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=green)](https://github.com/eworm-de/routeros-scripts/network)
+[![GitHub watchers](https://img.shields.io/github/watchers/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=blue)](https://github.com/eworm-de/routeros-scripts/watchers)
+[![required RouterOS version](https://img.shields.io/badge/RouterOS-7.12-yellow?style=flat)](https://mikrotik.com/download/changelogs/)
+[![Telegram group @routeros_scripts](https://img.shields.io/badge/Telegram-%40routeros__scripts-%2326A5E4?logo=telegram&style=flat)](https://t.me/routeros_scripts)
+[![donate with PayPal](https://img.shields.io/badge/Like_it%3F-Donate!-orange?logo=githubsponsors&logoColor=orange&style=flat)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)
+
+[⬅️ Go back to main README](README.md)
+
+> ⚠️ **Warning**: Living on the edge? Great, read on!
+> If not: Please use the `main` branch and leave this page!
+
+These scripts are developed in a [git](https://git-scm.com/) repository.
+Development and experimental branches are used to provide early access
+for specific changes. You can install scripts from these branches
+for testing.
+
+## Install single script
+
+To install a single script from `next` branch:
+
+ $ScriptInstallUpdate script-name "url-suffix=?h=next";
+
+## Switch existing script
+
+Alternatively switch an existing script to update from `next` branch:
+
+ /system/script/set comment="url-suffix=?h=next" script-name;
+ $ScriptInstallUpdate;
+
+## Switch installation
+
+Last but not least - to switch the complete installation to the `next`
+branch edit `global-config-overlay` and add:
+
+ :global ScriptUpdatesUrlSuffix "?h=next";
+
+... then reload the configuration and update:
+
+ /system/script/run global-config;
+ $ScriptInstallUpdate;
+
+> ℹ️ **Info**: Replace `next` with *whatever* to use another specific branch.
+
+---
+[⬅️ Go back to main README](README.md)
+[⬆️ Go back to top](#top)
diff --git a/CONTRIBUTIONS.md b/CONTRIBUTIONS.md
index 54b3228..dff933b 100644
--- a/CONTRIBUTIONS.md
+++ b/CONTRIBUTIONS.md
@@ -1,7 +1,14 @@
Past Contributions
==================
-[◀ Go back to main README](README.md)
+[![GitHub stars](https://img.shields.io/github/stars/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=red)](https://github.com/eworm-de/routeros-scripts/stargazers)
+[![GitHub forks](https://img.shields.io/github/forks/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=green)](https://github.com/eworm-de/routeros-scripts/network)
+[![GitHub watchers](https://img.shields.io/github/watchers/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=blue)](https://github.com/eworm-de/routeros-scripts/watchers)
+[![required RouterOS version](https://img.shields.io/badge/RouterOS-7.12-yellow?style=flat)](https://mikrotik.com/download/changelogs/)
+[![Telegram group @routeros_scripts](https://img.shields.io/badge/Telegram-%40routeros__scripts-%2326A5E4?logo=telegram&style=flat)](https://t.me/routeros_scripts)
+[![donate with PayPal](https://img.shields.io/badge/Like_it%3F-Donate!-orange?logo=githubsponsors&logoColor=orange&style=flat)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)
+
+[⬅️ Go back to main README](README.md)
Thanks a lot for your contributions! ❤️
@@ -10,6 +17,7 @@ Thanks a lot for your contributions! ❤️
These persons contributed code or documentation. See the git history
for details!
+* [Anatoly Bubenkov](mailto:bubenkoff@gmail.com) (@bubenkoff)
* [Ben Harris](mailto:mail@bharr.is) (@bharrisau)
* [Daniel Ziegenberg](mailto:daniel@ziegenberg.at) (@ziegenberg)
* [Michael Gisbers](mailto:michael@gisbers.de) (@mgisbers)
@@ -27,7 +35,11 @@ Add yourself to the list,
* Christoph Boss (@Kampfwurst)
* Devin Dean (@dd2594gh)
* Evaldo Gardenal
+* Giorgio Bikos
+* Harold Schoemaker
+* Hugo BV
* Klaus Michael Rübsam
+* Leonardo Valeri Manera
* Linux-Schmie.de Michael Gisbers
* Manuel Kuhn
* Marek Čábák
@@ -37,8 +49,9 @@ Add yourself to the list,
* Richard Österreicher
* Simon Hitzemann
* Sunny Chu (@sunnychuchu)
+* Ulrich Wessendorf
* Zac Kornilakis
---
-[◀ Go back to main README](README.md)
-[▲ Go back to top](#top)
+[⬅️ Go back to main README](README.md)
+[⬆️ Go back to top](#top)
diff --git a/INITIAL-COMMANDS.md b/INITIAL-COMMANDS.md
index e8be3e1..0de50ae 100644
--- a/INITIAL-COMMANDS.md
+++ b/INITIAL-COMMANDS.md
@@ -1,7 +1,14 @@
Initial commands
================
-[◀ Go back to main README](README.md)
+[![GitHub stars](https://img.shields.io/github/stars/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=red)](https://github.com/eworm-de/routeros-scripts/stargazers)
+[![GitHub forks](https://img.shields.io/github/forks/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=green)](https://github.com/eworm-de/routeros-scripts/network)
+[![GitHub watchers](https://img.shields.io/github/watchers/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=blue)](https://github.com/eworm-de/routeros-scripts/watchers)
+[![required RouterOS version](https://img.shields.io/badge/RouterOS-7.12-yellow?style=flat)](https://mikrotik.com/download/changelogs/)
+[![Telegram group @routeros_scripts](https://img.shields.io/badge/Telegram-%40routeros__scripts-%2326A5E4?logo=telegram&style=flat)](https://t.me/routeros_scripts)
+[![donate with PayPal](https://img.shields.io/badge/Like_it%3F-Donate!-orange?logo=githubsponsors&logoColor=orange&style=flat)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)
+
+[⬅️ Go back to main README](README.md)
> ⚠️ **Warning**: These command are inteneded for initial setup. If you are
> not aware of the procedure please follow
@@ -10,28 +17,39 @@ Initial commands
Run the complete base installation:
{
- /tool/fetch "https://git.eworm.de/cgit/routeros-scripts/plain/certs/R3.pem" dst-path="letsencrypt-R3.pem" as-value;
+ /tool/fetch "https://git.eworm.de/cgit/routeros-scripts/plain/certs/E1.pem" dst-path="letsencrypt-E1.pem" as-value;
:delay 1s;
- /certificate/import file-name=letsencrypt-R3.pem passphrase="";
- :if ([ :len [ /certificate/find where fingerprint="67add1166b020ae61b8f5fc96813c04c2aa589960796865572a3c7e737613dfd" or fingerprint="96bcec06264976f37460779acf28c5a7cfe8a3c0aae11a8ffcee05c0bddf08c6" ] ] != 2) do={
+ /certificate/import file-name=letsencrypt-E1.pem passphrase="";
+ :if ([ :len [ /certificate/find where fingerprint="46494e30379059df18be52124305e606fc59070e5b21076ce113954b60517cda" or fingerprint="69729b8e15a86efc177a57afb7171dfc64add28c2fca8cf1507e34453ccb1470" ] ] != 2) do={
:error "Something is wrong with your certificates!";
};
- /file/remove "letsencrypt-R3.pem";
+ /file/remove "letsencrypt-E1.pem";
:delay 1s;
+ /system/script/set name=("global-config-overlay-" . [ /system/clock/get date ] . "-" . [ /system/clock/get time ]) [ find where name="global-config-overlay" ];
: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/remove [ find where name=$Script ];
+ /system/script/add name=$Script owner=$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/remove [ find where name="global-scripts" ];
/system/scheduler/add name="global-scripts" start-time=startup on-event="/system/script { run global-config; run global-functions; }";
:global CertificateNameByCN;
- $CertificateNameByCN "R3";
- $CertificateNameByCN "ISRG Root X1";
+ $CertificateNameByCN "E1";
+ $CertificateNameByCN "ISRG Root X2";
};
-Optional to update the scripts automatically:
+Then continue setup with
+[scheduled automatic updates](README.md#scheduled-automatic-updates) or
+[editing configuration](README.md#editing-configuration).
+
+## Fix existing installation
- /system/scheduler/add name="ScriptInstallUpdate" start-time=startup interval=1d on-event=":global ScriptInstallUpdate; \$ScriptInstallUpdate;";
+The [initial commands](#initial-commands) above allow to fix an existing
+installation in case it ever breaks. If `global-config-overlay` did exist
+before it is renamed with a date and time suffix (like
+`global-config-overlay-2024-01-25-09:33:12`). Make sure to restore the
+configuration overlay if required.
---
-[◀ Go back to main README](README.md)
-[▲ Go back to top](#top)
+[⬅️ Go back to main README](README.md)
+[⬆️ Go back to top](#top)
diff --git a/Makefile b/Makefile
index b0737ab..d21713c 100644
--- a/Makefile
+++ b/Makefile
@@ -2,25 +2,33 @@
# template scripts -> final scripts
# markdown files -> html files
-TEMPLATE = $(wildcard *.template)
-CAPSMAN = $(TEMPLATE:.template=.capsman)
-LOCAL = $(TEMPLATE:.template=.local)
+CAPSMAN = $(wildcard *.capsman.rsc)
+LOCAL = $(wildcard *.local.rsc)
+WIFI = $(wildcard *.wifi.rsc)
MARKDOWN = $(wildcard *.md doc/*.md doc/mod/*.md)
HTML = $(MARKDOWN:.md=.html)
-all: $(CAPSMAN) $(LOCAL) $(HTML)
+all: $(CAPSMAN) $(LOCAL) $(WIFI) $(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 $@)|' \
+%.capsman.rsc: %.template.rsc Makefile
+ sed -e '/\/interface\/wifi\//d' -e '/\/interface\/wireless\//d' -e 's|%TEMPL%|.capsman|' \
+ -e '/^# NOT \/caps-man\/ #$$/,/^# NOT \/caps-man\/ #$$/d' \
-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 $@)/' \
+%.local.rsc: %.template.rsc Makefile
+ sed -e '/\/caps-man\//d' -e '/\/interface\/wifi\//d' -e 's|%TEMPL%|.local|' \
+ -e '/^# NOT \/interface\/wireless\/ #$$/,/^# NOT \/interface\/wireless\/ #$$/d' \
+ -e '/^# !!/,/^# !!/c # !! Do not edit this file, it is generated from template!' \
+ < $< > $@
+
+%.wifi.rsc: %.template.rsc Makefile
+ sed -e '/\/caps-man\//d' -e '/\/interface\/wireless\//d' -e 's|%TEMPL%|.wifi|' \
+ -e '/^# NOT \/interface\/wifi\/ #$$/,/^# NOT \/interface\/wifi\/ #$$/d' \
-e '/^# !!/,/^# !!/c # !! Do not edit this file, it is generated from template!' \
< $< > $@
diff --git a/README.d/01-download-certs.avif b/README.d/01-download-certs.avif
index 4da73fa..b27b23b 100644
--- a/README.d/01-download-certs.avif
+++ b/README.d/01-download-certs.avif
Binary files differ
diff --git a/README.d/02-import-certs.avif b/README.d/02-import-certs.avif
index 308be5b..d42994b 100644
--- a/README.d/02-import-certs.avif
+++ b/README.d/02-import-certs.avif
Binary files differ
diff --git a/README.d/03-check-certs.avif b/README.d/03-check-certs.avif
index adf1161..33bdc40 100644
--- a/README.d/03-check-certs.avif
+++ b/README.d/03-check-certs.avif
Binary files differ
diff --git a/README.d/05-edit-global-config-overlay.avif b/README.d/05-edit-global-config-overlay.avif
deleted file mode 100644
index f2f0f2d..0000000
--- a/README.d/05-edit-global-config-overlay.avif
+++ /dev/null
Binary files differ
diff --git a/README.d/06-run-and-schedule-scripts.avif b/README.d/05-run-and-schedule-scripts.avif
index 37e1173..37e1173 100644
--- a/README.d/06-run-and-schedule-scripts.avif
+++ b/README.d/05-run-and-schedule-scripts.avif
Binary files differ
diff --git a/README.d/07-schedule-update.avif b/README.d/06-schedule-update.avif
index 7c96f3a..7c96f3a 100644
--- a/README.d/07-schedule-update.avif
+++ b/README.d/06-schedule-update.avif
Binary files differ
diff --git a/README.d/07-edit-global-config-overlay.avif b/README.d/07-edit-global-config-overlay.avif
new file mode 100644
index 0000000..f87fda8
--- /dev/null
+++ b/README.d/07-edit-global-config-overlay.avif
Binary files differ
diff --git a/README.d/08-apply-configuration.avif b/README.d/08-apply-configuration.avif
new file mode 100644
index 0000000..b66af1a
--- /dev/null
+++ b/README.d/08-apply-configuration.avif
Binary files differ
diff --git a/README.d/08-update-scripts.avif b/README.d/09-update-scripts.avif
index f549fef..f549fef 100644
--- a/README.d/08-update-scripts.avif
+++ b/README.d/09-update-scripts.avif
Binary files differ
diff --git a/README.d/09-install-scripts.avif b/README.d/10-install-scripts.avif
index 00225b1..00225b1 100644
--- a/README.d/09-install-scripts.avif
+++ b/README.d/10-install-scripts.avif
Binary files differ
diff --git a/README.d/10-schedule-script.avif b/README.d/11-schedule-script.avif
index 27541b7..27541b7 100644
--- a/README.d/10-schedule-script.avif
+++ b/README.d/11-schedule-script.avif
Binary files differ
diff --git a/README.d/12-install-custom-script.avif b/README.d/12-install-custom-script.avif
deleted file mode 100644
index c27408f..0000000
--- a/README.d/12-install-custom-script.avif
+++ /dev/null
Binary files differ
diff --git a/README.d/11-setup-lease-script.avif b/README.d/12-setup-lease-script.avif
index 365e0e8..365e0e8 100644
--- a/README.d/11-setup-lease-script.avif
+++ b/README.d/12-setup-lease-script.avif
Binary files differ
diff --git a/README.d/13-install-custom-script.avif b/README.d/13-install-custom-script.avif
new file mode 100644
index 0000000..2f01c43
--- /dev/null
+++ b/README.d/13-install-custom-script.avif
Binary files differ
diff --git a/README.d/13-remove-script.avif b/README.d/14-remove-script.avif
index a5c7daf..a5c7daf 100644
--- a/README.d/13-remove-script.avif
+++ b/README.d/14-remove-script.avif
Binary files differ
diff --git a/README.d/news-and-changes-notification.svg b/README.d/news-and-changes-notification.svg
deleted file mode 100644
index ab8d611..0000000
--- a/README.d/news-and-changes-notification.svg
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<svg id="svg8" width="490" height="200" version="1.1" viewBox="0 0 129.65 52.917" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
- <metadata id="metadata5">
- <rdf:RDF>
- <cc:Work rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <g id="layer1" transform="translate(16.375 11.083)">
- <rect id="rect857" x="-15.875" y="-10.583" width="128.65" height="51.917" rx="1.6081" fill="#e6e6e6" stroke="#6c5d53" stroke-linecap="round" stroke-linejoin="round"/>
- <g id="g884" transform="matrix(.5 0 0 .5 -12.406 -7.1146)" stroke-width="2">
- <path id="path899" d="m23.177 23.177c-2.9635 2.9635-17.991 2.9635-20.955 0-2.9635-2.9635-2.9635-17.991 0-20.955 2.9635-2.9635 17.991-2.9635 20.955 0 2.9635 2.9635 2.9635 17.991 0 20.955z" fill="#fff"/>
- <g id="g871">
- <g id="text837" stroke-width=".52916px" aria-label="#!rsc">
- <path id="path851" d="m7.4832 16.646v-1.0239h-0.54606l0.16577-1.2774h-1.2774l-0.16577 1.2774h-0.97511l0.16577-1.2774h-1.2774l-0.16577 1.2774h-0.70208v1.0239h0.56556l-0.24378 1.8722h-0.68257v1.0239h0.54606l-0.18527 1.3944h1.2774l0.18527-1.3944h0.97511l-0.18527 1.3944h1.2774l0.18527-1.3944h0.70208v-1.0239h-0.56556l0.24378-1.8722zm-2.2037 1.8722h-0.97511l0.24378-1.8722h0.97511z"/>
- <path id="path853" d="m9.6187 14.179h-1.6382l0.19502 4.271h1.2481zm-0.81909 5.1583c-0.48755 0-0.8776 0.39979-0.8776 0.8776 0 0.48755 0.39004 0.88735 0.8776 0.88735 0.4973 0 0.88735-0.39979 0.88735-0.88735 0-0.4778-0.39004-0.8776-0.88735-0.8776z"/>
- <path id="path855" d="m13.373 15.612c-0.59482 0-1.1019 0.42905-1.3359 1.1506l-0.13652-1.0044h-1.3359v5.1778h1.5407v-2.6035c0.17552-0.77033 0.4388-1.2286 1.0726-1.2286 0.16577 0 0.30228 0.02925 0.46805 0.06826l0.24378-1.4919c-0.17552-0.04875-0.32178-0.06826-0.51681-0.06826z"/>
- <path id="path857" d="m16.181 15.592c-1.3066 0-2.116 0.69233-2.116 1.5797 0 0.79959 0.50706 1.3261 1.5309 1.6187 0.9361 0.26328 1.0921 0.37054 1.0921 0.72158 0 0.31203-0.28278 0.48755-0.75083 0.48755-0.50706 0-0.98486-0.20477-1.3749-0.50706l-0.75083 0.83859c0.50706 0.4583 1.2676 0.77033 2.1647 0.77033 1.2871 0 2.3012-0.63382 2.3012-1.7064 0-0.92635-0.57531-1.3554-1.5992-1.6479-0.92635-0.27303-1.0629-0.39004-1.0629-0.66307 0-0.23402 0.20477-0.39004 0.62407-0.39004 0.44855 0 0.8776 0.14627 1.2774 0.39979l0.56556-0.86784c-0.4778-0.39004-1.1506-0.63382-1.9015-0.63382z"/>
- <path id="path859" d="m21.281 15.592c-1.5504 0-2.5353 1.1506-2.5353 2.7986 0 1.6382 0.97511 2.7108 2.5645 2.7108 0.71183 0 1.2676-0.23403 1.7454-0.61432l-0.67282-0.9556c-0.37054 0.23403-0.62407 0.35104-0.99461 0.35104-0.61432 0-1.0239-0.35104-1.0239-1.5017 0-1.1604 0.38029-1.6089 1.0434-1.6089 0.35104 0 0.65332 0.11701 0.98486 0.37054l0.66307-0.9166c-0.4973-0.4193-1.0531-0.63382-1.7747-0.63382z"/>
- </g>
- <g id="g1542" transform="matrix(2 0 0 2 -.41134 3.175)" fill="#676867" fill-rule="evenodd" stroke-width=".26458">
- <path id="path943" d="m4.9596-1.0196c0.40797 2.8312 1.9272 4.5499 5.0239 4.691-2.918 1.1164-5.9253-1.5076-5.0239-4.691"/>
- <path id="path945" d="m3.3407-0.52096c0.034969-0.00777 0.038854 0.015542 0.041445 0.041445 0.098431 1.8054 0.85998 3.1744 1.8689 4.1108 1.0089 0.93639 2.3636 1.6941 4.274 1.6604-3.5772 1.4247-7.337-1.9984-6.1856-5.8126"/>
- </g>
- </g>
- </g>
- <text id="text4811" transform="matrix(.26458 0 0 .26458 -44.95 -6.6039)" x="-248.88142" fill="#000000" font-family="'Fira Mono', 'Roboto Mono', monospace" font-size="12px" letter-spacing="0px" stroke-width="1px" word-spacing="0px" style="font-variant-caps:normal;font-variant-east-asian:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:15px;shape-inside:url(#rect4813);white-space:pre" xml:space="preserve"><tspan id="tspan490" x="180" y="10.85">[MikroTik] 📌 News and configuration changes
-</tspan><tspan id="tspan492" x="180" y="25.85">
-</tspan><tspan id="tspan494" x="180" y="40.85">The configuration version on MikroTik increased to 84,
-</tspan><tspan id="tspan496" x="180" y="55.85">current configuration may need modification. Please
-</tspan><tspan id="tspan498" x="180" y="70.85">review and update global-config-overlay, then re-run
-</tspan><tspan id="tspan500" x="180" y="85.85">global-config.
-</tspan><tspan id="tspan502" x="180" y="100.85">
-</tspan><tspan id="tspan504" x="180" y="115.85">Changes:
-</tspan><tspan id="tspan506" x="180" y="130.85"> ● Introduced new setting to disable news and change
-</tspan><tspan id="tspan508" x="180" y="145.85">notifications, dropped version from configuration.</tspan></text>
- </g>
-</svg>
diff --git a/README.d/notification-news-and-changes.avif b/README.d/notification-news-and-changes.avif
new file mode 100644
index 0000000..d91b8a0
--- /dev/null
+++ b/README.d/notification-news-and-changes.avif
Binary files differ
diff --git a/README.d/upstream.png b/README.d/upstream.png
new file mode 100644
index 0000000..fd5e877
--- /dev/null
+++ b/README.d/upstream.png
Binary files differ
diff --git a/README.md b/README.md
index 56d39e8..614095a 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,12 @@
RouterOS Scripts
================
-[![GitHub stars](https://img.shields.io/github/stars/eworm-de/routeros-scripts?style=social)](https://github.com/eworm-de/routeros-scripts/stargazers)
-[![GitHub forks](https://img.shields.io/github/forks/eworm-de/routeros-scripts?style=social)](https://github.com/eworm-de/routeros-scripts/network)
-[![GitHub watchers](https://img.shields.io/github/watchers/eworm-de/routeros-scripts?style=social)](https://github.com/eworm-de/routeros-scripts/watchers)
+[![GitHub stars](https://img.shields.io/github/stars/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=red)](https://github.com/eworm-de/routeros-scripts/stargazers)
+[![GitHub forks](https://img.shields.io/github/forks/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=green)](https://github.com/eworm-de/routeros-scripts/network)
+[![GitHub watchers](https://img.shields.io/github/watchers/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=blue)](https://github.com/eworm-de/routeros-scripts/watchers)
+[![required RouterOS version](https://img.shields.io/badge/RouterOS-7.12-yellow?style=flat)](https://mikrotik.com/download/changelogs/)
+[![Telegram group @routeros_scripts](https://img.shields.io/badge/Telegram-%40routeros__scripts-%2326A5E4?logo=telegram&style=flat)](https://t.me/routeros_scripts)
+[![donate with PayPal](https://img.shields.io/badge/Like_it%3F-Donate!-orange?logo=githubsponsors&logoColor=orange&style=flat)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)
![RouterOS Scripts Logo](logo.svg)
@@ -18,14 +21,27 @@ to manage RouterOS devices or extend their functionality.
Requirements
------------
+### Software (RouterOS)
+
Latest version of the scripts require recent RouterOS to function properly.
-Make sure to install latest updates before you begin.
+Make sure to install latest updates before you begin. If new functionality
+or a breaking change in RouterOS `7.n` is used in my scripts I push my
+change some time after `7.(n+1)` was released. At any time you should have
+at least two minor and their bugfix releases to choose from.
Specific scripts may require even newer RouterOS version.
> ℹ️ **Info**: The `main` branch is now RouterOS v7 only. If you are still
> running RouterOS v6 switch to `routeros-v6` branch!
+### Hardware
+
+RouterOS packages increase in size with each release. This becomes a
+problem for devices with 16MB storage and below, those with an ARM CPU
+are specifically affected.
+
+Huge configuration and lots of scripts give an extra risk. **Take care!**
+
Initial setup
-------------
@@ -53,7 +69,7 @@ download the certificates. If you intend to download the scripts from a
different location (for example from github.com) install the corresponding
certificate chain.
- /tool/fetch "https://git.eworm.de/cgit/routeros-scripts/plain/certs/R3.pem" dst-path="letsencrypt-R3.pem";
+ /tool/fetch "https://git.eworm.de/cgit/routeros-scripts/plain/certs/E1.pem" dst-path="letsencrypt-E1.pem";
![screenshot: download certs](README.d/01-download-certs.avif)
@@ -61,21 +77,25 @@ Note that the commands above do *not* verify server certificate, so if you
want to be safe download with your workstations's browser and transfer the
files to your MikroTik device.
-* [ISRG Root X1](https://letsencrypt.org/certs/isrgrootx1.pem)
-* Let's Encrypt [R3](https://letsencrypt.org/certs/lets-encrypt-r3.pem)
+* [ISRG Root X2](https://letsencrypt.org/certs/isrg-root-x2.pem)
+* Let's Encrypt [E1](https://letsencrypt.org/certs/lets-encrypt-e1.pem)
Then we import the certificates.
- /certificate/import file-name=letsencrypt-R3.pem passphrase="";
+ /certificate/import file-name=letsencrypt-E1.pem passphrase="";
+
+Do not worry that the command is not shown - that happens because it contains
+a sensitive property, the passphrase.
![screenshot: import certs](README.d/02-import-certs.avif)
-For basic verification we rename the certificates and print their count. Make
-sure the certificate count is **two**.
+For basic verification we rename the certificates and print them by
+fingerprint. Make sure exactly these two certificates ("*E1*" and
+"*ISRG-Root-X2*") are shown.
- /certificate/set name="R3" [ find where fingerprint="67add1166b020ae61b8f5fc96813c04c2aa589960796865572a3c7e737613dfd" ];
- /certificate/set name="ISRG-Root-X1" [ find where fingerprint="96bcec06264976f37460779acf28c5a7cfe8a3c0aae11a8ffcee05c0bddf08c6" ];
- /certificate/print count-only where fingerprint="67add1166b020ae61b8f5fc96813c04c2aa589960796865572a3c7e737613dfd" or fingerprint="96bcec06264976f37460779acf28c5a7cfe8a3c0aae11a8ffcee05c0bddf08c6";
+ /certificate/set name="E1" [ find where common-name="E1" ];
+ /certificate/set name="ISRG-Root-X2" [ find where common-name="ISRG Root X2" ];
+ /certificate/print proplist=name where fingerprint="46494e30379059df18be52124305e606fc59070e5b21076ce113954b60517cda" or fingerprint="69729b8e15a86efc177a57afb7171dfc64add28c2fca8cf1507e34453ccb1470";
![screenshot: check certs](README.d/03-check-certs.avif)
@@ -87,32 +107,56 @@ 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 owner=$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)
+And finally load configuration and functions and add the scheduler.
+
+ /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; }";
+
+![screenshot: run and schedule scripts](README.d/05-run-and-schedule-scripts.avif)
+
+### Scheduled automatic updates
+
+The last step is optional: Add this scheduler **only** if you want the
+scripts to be updated automatically!
+
+ /system/scheduler/add name="ScriptInstallUpdate" start-time=startup interval=1d on-event=":global ScriptInstallUpdate; \$ScriptInstallUpdate;";
+
+![screenshot: schedule update](README.d/06-schedule-update.avif)
+
+Editing configuration
+---------------------
+
The configuration needs to be tweaked for your needs. Edit
-`global-config-overlay`, copy configuration from
-[`global-config`](global-config) (the one without `-overlay`).
+`global-config-overlay`, copy relevant configuration from
+[`global-config`](global-config.rsc) (the one without `-overlay`).
Save changes and exit with `Ctrl-o`.
- /system/script edit global-config-overlay source;
+ /system/script/edit global-config-overlay source;
-![screenshot: edit global-config-overlay](README.d/05-edit-global-config-overlay.avif)
+![screenshot: edit global-config-overlay](README.d/07-edit-global-config-overlay.avif)
-And finally load configuration and functions and add the scheduler.
+Additionally creating configuration snippets is supported. The script name
+of these snippets has to start with `global-config-overlay.d/` to make them
+being loaded automatically. This allows to split off parts of the
+configuration.
- /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; }";
+To apply your changes run `global-config`, which will automatically load
+the overlay as well:
-![screenshot: run and schedule scripts](README.d/06-run-and-schedule-scripts.avif)
+ /system/script/run global-config;
-The last step is optional: Add this scheduler **only** if you want the scripts
-to be updated automatically!
+![screenshot: apply configuration](README.d/08-apply-configuration.avif)
- /system/scheduler/add name="ScriptInstallUpdate" start-time=startup interval=1d on-event=":global ScriptInstallUpdate; \$ScriptInstallUpdate;";
+This last step is required when ever you make changes to your configuration.
-![screenshot: schedule update](README.d/07-schedule-update.avif)
+> ℹ️ **Info**: It is recommended to edit the configuration using the command
+> line interface. If using Winbox on Windows OS, the line endings may be
+> missing. To fix this run:
+> `/system/script/set source=[ $Unix2Dos [ get global-config-overlay source ] ] global-config-overlay;`
Updating scripts
----------------
@@ -122,12 +166,12 @@ everything is up-to-date it will not produce any output.
$ScriptInstallUpdate;
-![screenshot: update scripts](README.d/08-update-scripts.avif)
+![screenshot: update scripts](README.d/09-update-scripts.avif)
If the update includes news or requires configuration changes a notification
is sent - in addition to terminal output and log messages.
-![news and changes notification](README.d/news-and-changes-notification.svg)
+![news and changes notification](README.d/notification-news-and-changes.avif)
Adding a script
---------------
@@ -137,7 +181,7 @@ a comma separated list of script names.
$ScriptInstallUpdate check-certificates,check-routeros-update;
-![screenshot: install scripts](README.d/09-install-scripts.avif)
+![screenshot: install scripts](README.d/10-install-scripts.avif)
Scheduler and events
--------------------
@@ -149,7 +193,7 @@ miss an update.
/system/scheduler/add name="check-routeros-update" interval=1h on-event="/system/script/run check-routeros-update;";
-![screenshot: schedule script](README.d/10-schedule-script.avif)
+![screenshot: schedule script](README.d/11-schedule-script.avif)
Some events can run a script. If you want your DHCP hostnames to be available
in DNS use `dhcp-to-dns` with the events from dhcp server. For a regular
@@ -159,7 +203,7 @@ cleanup add a scheduler entry.
/ip/dhcp-server/set lease-script=lease-script [ find ];
/system/scheduler/add name="dhcp-to-dns" interval=5m on-event="/system/script/run dhcp-to-dns;";
-![screenshot: setup lease script](README.d/11-setup-lease-script.avif)
+![screenshot: setup lease script](README.d/12-setup-lease-script.avif)
There's much more to explore... Have fun!
@@ -183,9 +227,10 @@ Available scripts
* [Comment DHCP leases with info from access list](doc/dhcp-lease-comment.md)
* [Create DNS records for DHCP leases](doc/dhcp-to-dns.md)
* [Automatically upgrade firmware and reboot](doc/firmware-upgrade-reboot.md)
+* [Download, import and update firewall address-lists](doc/fw-addr-lists.md)
* [Wait for global functions und modules](doc/global-wait.md)
* [Send GPS position to server](doc/gps-track.md)
-* [Use WPA2 network with hotspot credentials](doc/hotspot-to-wpa.md)
+* [Use WPA network with hotspot credentials](doc/hotspot-to-wpa.md)
* [Create DNS records for IPSec peers](doc/ipsec-to-dns.md)
* [Update configuration on IPv6 prefix change](doc/ipv6-update.md)
* [Manage IP addresses with bridge status](doc/ip-addr-bridge.md)
@@ -200,8 +245,8 @@ Available scripts
* [Run scripts on ppp connection](doc/ppp-on-up.md)
* [Act on received SMS](doc/sms-action.md)
* [Forward received SMS](doc/sms-forward.md)
-* [Import SSH keys](doc/ssh-keys-import.md)
* [Play Super Mario theme](doc/super-mario-theme.md)
+* [Chat with your router and send commands via Telegram bot](doc/telegram-chat.md)
* [Install LTE firmware upgrade](doc/unattended-lte-firmware-upgrade.md)
* [Update GRE configuration with dynamic addresses](doc/update-gre-address.md)
* [Update tunnelbroker configuration](doc/update-tunnelbroker.md)
@@ -215,8 +260,10 @@ Available modules
* [IP address calculation](doc/mod/ipcalc.md)
* [Send notifications via e-mail](doc/mod/notification-email.md)
* [Send notifications via Matrix](doc/mod/notification-matrix.md)
+* [Send notifications via Ntfy](doc/mod/notification-ntfy.md)
* [Send notifications via Telegram](doc/mod/notification-telegram.md)
* [Download script and run it once](doc/mod/scriptrunonce.md)
+* [Import ssh keys for public key authentication](doc/mod/ssh-keys-import.md)
Installing custom scripts & modules
-----------------------------------
@@ -227,12 +274,9 @@ 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/plain/README.d/";
+ $ScriptInstallUpdate hello-world "base-url=https://git.eworm.de/cgit/routeros-scripts-custom/plain/";
-![screenshot: install custom script](README.d/12-install-custom-script.avif)
-
-(Yes, the example url still belongs to the repository for easy
-handling - but the url can be what ever you use.)
+![screenshot: install custom script](README.d/13-install-custom-script.avif)
For a script to be considered valid it has to begin with a *magic token*.
Have a look at [any script](README.d/hello-world.rsc) and copy the first line
@@ -241,6 +285,26 @@ without modification.
Starting a script's name with `mod/` makes it a module and it is run
automatically by `global-functions`.
+### Linked custom scripts & modules
+
+> ⚠️ **Warning**: These links are being provided for your convenience only;
+> they do not constitute an endorsement or an approval by me. I bear no
+> responsibility for the accuracy, legality or content of the external site
+> or for that of subsequent links. Contact the external site for answers to
+> questions regarding its content.
+
+* [Hello World](https://git.eworm.de/cgit/routeros-scripts-custom/about/doc/hello-world.md)
+ (This is a demo script to show how the linking to external documentation
+ will be done.)
+
+> ℹ️ **Info**: You have your own set of scripts and/or modules and want these
+> to be listed here? There should be a general info page that links here,
+> and documentation for each script. You can start by cloning my
+> [Custom RouterOS-Scripts](https://git.eworm.de/cgit/routeros-scripts-custom/)
+> (or fork on [GitHub](https://github.com/eworm-de/routeros-scripts-custom)
+> or [GitLab](https://gitlab.com/eworm-de/routeros-scripts-custom)) and make
+> your changes. Then please [get in contact](#patches-issues-and-whishlist)...
+
Removing a script
-----------------
@@ -249,7 +313,7 @@ configuration...
/system/script/remove to-be-removed;
-![screenshot: remove script](README.d/13-remove-script.avif)
+![screenshot: remove script](README.d/14-remove-script.avif)
Possibly a scheduler and other configuration has to be removed as well.
@@ -258,7 +322,7 @@ Contact
We have a Telegram Group [RouterOS-Scripts](https://t.me/routeros_scripts)!
-![RouterOS Scripts Telegram Group](README.d/telegram-group.avif)
+[![RouterOS Scripts Telegram Group](README.d/telegram-group.avif)](https://t.me/routeros_scripts)
Get help, give feedback or just chat - but do not expect free professional
support!
@@ -271,7 +335,9 @@ Thanks a lot for [past contributions](CONTRIBUTIONS.md)! ❤️
### Patches, issues and whishlist
Feel free to contact me via e-mail or open an
-[issue at github](https://github.com/eworm-de/routeros-scripts/issues).
+[issue](https://github.com/eworm-de/routeros-scripts/issues) or
+[pull request](https://github.com/eworm-de/routeros-scripts/pulls)
+at github.
### Donate
@@ -280,7 +346,7 @@ for you. If you like the scripts and think this is of value for you or your
business please consider to
[donate with PayPal](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J).
-[![donate with PayPal](https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)
+[![donate with PayPal](https://img.shields.io/badge/Like_it%3F-Donate!-orange?logo=githubsponsors&logoColor=orange&style=for-the-badge)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)
Thanks a lot for your support!
@@ -300,6 +366,8 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Upstream
--------
+![upstream](README.d/upstream.png)
+
URL:
[GitHub.com](https://github.com/eworm-de/routeros-scripts#routeros-scripts)
@@ -308,4 +376,4 @@ Mirror:
[GitLab.com](https://gitlab.com/eworm-de/routeros-scripts#routeros-scripts)
---
-[▲ Go back to top](#top)
+[⬆️ Go back to top](#top)
diff --git a/accesslist-duplicates.capsman b/accesslist-duplicates.capsman
deleted file mode 100644
index 848ca52..0000000
--- a/accesslist-duplicates.capsman
+++ /dev/null
@@ -1,42 +0,0 @@
-#!rsc by RouterOS
-# RouterOS script: accesslist-duplicates.capsman
-# Copyright (c) 2018-2022 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);
-}
diff --git a/accesslist-duplicates.capsman.rsc b/accesslist-duplicates.capsman.rsc
new file mode 100644
index 0000000..2ce8302
--- /dev/null
+++ b/accesslist-duplicates.capsman.rsc
@@ -0,0 +1,34 @@
+#!rsc by RouterOS
+# RouterOS script: accesslist-duplicates.capsman
+# Copyright (c) 2018-2024 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# requires RouterOS, version=7.12
+#
+# 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!
+
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:do {
+ :local ScriptName [ :jobname ];
+
+ :local Seen ({});
+
+ :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 ];
+ :if ($Seen->$Mac = 1) do={
+ /caps-man/access-list/print where mac-address=$Mac;
+ :local Remove [ :tonum [ /terminal/ask prompt="\nNumeric id to remove, any key to skip!" ] ];
+
+ :if ([ :typeof $Remove ] = "num") do={
+ :put ("Removing numeric id " . $Remove . "...\n");
+ /caps-man/access-list/remove $Remove;
+ }
+ }
+ :set ($Seen->$Mac) 1;
+ }
+} on-error={ }
diff --git a/accesslist-duplicates.local b/accesslist-duplicates.local
deleted file mode 100644
index 67f16f3..0000000
--- a/accesslist-duplicates.local
+++ /dev/null
@@ -1,42 +0,0 @@
-#!rsc by RouterOS
-# RouterOS script: accesslist-duplicates.local
-# Copyright (c) 2018-2022 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);
-}
diff --git a/accesslist-duplicates.local.rsc b/accesslist-duplicates.local.rsc
new file mode 100644
index 0000000..51ef6f3
--- /dev/null
+++ b/accesslist-duplicates.local.rsc
@@ -0,0 +1,34 @@
+#!rsc by RouterOS
+# RouterOS script: accesslist-duplicates.local
+# Copyright (c) 2018-2024 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# requires RouterOS, version=7.12
+#
+# 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!
+
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:do {
+ :local ScriptName [ :jobname ];
+
+ :local Seen ({});
+
+ :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 ];
+ :if ($Seen->$Mac = 1) do={
+ /interface/wireless/access-list/print where mac-address=$Mac;
+ :local Remove [ :tonum [ /terminal/ask prompt="\nNumeric id to remove, any key to skip!" ] ];
+
+ :if ([ :typeof $Remove ] = "num") do={
+ :put ("Removing numeric id " . $Remove . "...\n");
+ /interface/wireless/access-list/remove $Remove;
+ }
+ }
+ :set ($Seen->$Mac) 1;
+ }
+} on-error={ }
diff --git a/accesslist-duplicates.template b/accesslist-duplicates.template
deleted file mode 100644
index 8676551..0000000
--- a/accesslist-duplicates.template
+++ /dev/null
@@ -1,43 +0,0 @@
-#!rsc by RouterOS
-# RouterOS script: accesslist-duplicates%TEMPL%
-# Copyright (c) 2018-2022 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
-#
-# !! This is just a template! Replace '%PATH%' with 'caps-man'
-# !! or 'interface wireless'!
-
-:local 0 "accesslist-duplicates%TEMPL%";
-:global GlobalFunctionsReady;
-:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
-
-:global Read;
-
-:local Seen ({});
-:local Shown ({});
-
-:foreach AccList in=[ /%PATH%/access-list/find where mac-address!="00:00:00:00:00:00" ] do={
- :local Mac [ /%PATH%/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={
- /%PATH%/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");
- /%PATH%/access-list/remove $Remove;
- }
- }
- }
- }
- :set Seen ($Seen, $Mac);
-}
diff --git a/accesslist-duplicates.template.rsc b/accesslist-duplicates.template.rsc
new file mode 100644
index 0000000..770fb30
--- /dev/null
+++ b/accesslist-duplicates.template.rsc
@@ -0,0 +1,43 @@
+#!rsc by RouterOS
+# RouterOS script: accesslist-duplicates%TEMPL%
+# Copyright (c) 2018-2024 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# requires RouterOS, version=7.12
+#
+# print duplicate antries in wireless access list
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/accesslist-duplicates.md
+#
+# !! This is just a template to generate the real script!
+# !! Pattern '%TEMPL%' is replaced, paths are filtered.
+
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:do {
+ :local ScriptName [ :jobname ];
+
+ :local Seen ({});
+
+ :foreach AccList in=[ /caps-man/access-list/find where mac-address!="00:00:00:00:00:00" ] do={
+ :foreach AccList in=[ /interface/wifi/access-list/find where mac-address!="00:00:00:00:00:00" ] do={
+ :foreach AccList in=[ /interface/wireless/access-list/find where mac-address!="00:00:00:00:00:00" ] do={
+ :local Mac [ /caps-man/access-list/get $AccList mac-address ];
+ :local Mac [ /interface/wifi/access-list/get $AccList mac-address ];
+ :local Mac [ /interface/wireless/access-list/get $AccList mac-address ];
+ :if ($Seen->$Mac = 1) do={
+ /caps-man/access-list/print where mac-address=$Mac;
+ /interface/wifi/access-list/print where mac-address=$Mac;
+ /interface/wireless/access-list/print where mac-address=$Mac;
+ :local Remove [ :tonum [ /terminal/ask prompt="\nNumeric id to remove, any key to skip!" ] ];
+
+ :if ([ :typeof $Remove ] = "num") do={
+ :put ("Removing numeric id " . $Remove . "...\n");
+ /caps-man/access-list/remove $Remove;
+ /interface/wifi/access-list/remove $Remove;
+ /interface/wireless/access-list/remove $Remove;
+ }
+ }
+ :set ($Seen->$Mac) 1;
+ }
+} on-error={ }
diff --git a/accesslist-duplicates.wifi.rsc b/accesslist-duplicates.wifi.rsc
new file mode 100644
index 0000000..65f8aaa
--- /dev/null
+++ b/accesslist-duplicates.wifi.rsc
@@ -0,0 +1,34 @@
+#!rsc by RouterOS
+# RouterOS script: accesslist-duplicates.wifi
+# Copyright (c) 2018-2024 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# requires RouterOS, version=7.12
+#
+# 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!
+
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:do {
+ :local ScriptName [ :jobname ];
+
+ :local Seen ({});
+
+ :foreach AccList in=[ /interface/wifi/access-list/find where mac-address!="00:00:00:00:00:00" ] do={
+ :local Mac [ /interface/wifi/access-list/get $AccList mac-address ];
+ :if ($Seen->$Mac = 1) do={
+ /interface/wifi/access-list/print where mac-address=$Mac;
+ :local Remove [ :tonum [ /terminal/ask prompt="\nNumeric id to remove, any key to skip!" ] ];
+
+ :if ([ :typeof $Remove ] = "num") do={
+ :put ("Removing numeric id " . $Remove . "...\n");
+ /interface/wifi/access-list/remove $Remove;
+ }
+ }
+ :set ($Seen->$Mac) 1;
+ }
+} on-error={ }
diff --git a/backup-cloud b/backup-cloud
deleted file mode 100644
index 38aed1f..0000000
--- a/backup-cloud
+++ /dev/null
@@ -1,58 +0,0 @@
-#!rsc by RouterOS
-# RouterOS script: backup-cloud
-# Copyright (c) 2013-2022 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 "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;
-}
diff --git a/backup-cloud.rsc b/backup-cloud.rsc
new file mode 100644
index 0000000..cccb41b
--- /dev/null
+++ b/backup-cloud.rsc
@@ -0,0 +1,85 @@
+#!rsc by RouterOS
+# RouterOS script: backup-cloud
+# Copyright (c) 2013-2024 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# provides: backup-script, order=40
+# requires RouterOS, version=7.12
+#
+# upload backup to MikroTik cloud
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/backup-cloud.md
+
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:do {
+ :local ScriptName [ :jobname ];
+
+ :global BackupRandomDelay;
+ :global Identity;
+ :global PackagesUpdateBackupFailure;
+
+ :global DeviceInfo;
+ :global FormatLine;
+ :global HumanReadableNum;
+ :global LogPrint;
+ :global MkDir;
+ :global RandomDelay;
+ :global ScriptFromTerminal;
+ :global ScriptLock;
+ :global SendNotification2;
+ :global SymbolForNotification;
+ :global WaitForFile;
+ :global WaitFullyConnected;
+
+ :if ([ $ScriptLock $ScriptName ] = false) do={
+ :set PackagesUpdateBackupFailure true;
+ :error false;
+ }
+ $WaitFullyConnected;
+
+ :if ([ $ScriptFromTerminal $ScriptName ] = false && $BackupRandomDelay > 0) do={
+ $RandomDelay $BackupRandomDelay;
+ }
+
+ :if ([ $MkDir ("tmpfs/backup-cloud") ] = false) do={
+ $LogPrint error $ScriptName ("Failed creating directory!");
+ :error false;
+ }
+
+ :execute {
+ :global BackupPassword;
+ # we are not interested in output, but print is
+ # required to fetch information from cloud
+ /system/backup/cloud/print as-value;
+ :delay 20ms;
+ :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;
+ }
+ /file/add name="tmpfs/backup-cloud/done";
+ } as-string;
+
+ :if ([ $WaitForFile "tmpfs/backup-cloud/done" ] = true) do={
+ :local Cloud [ /system/backup/cloud/get ([ find ]->0) ];
+
+ $SendNotification2 ({ origin=$ScriptName; \
+ subject=([ $SymbolForNotification "floppy-disk,cloud" ] . "Cloud backup"); \
+ message=("Uploaded backup for " . $Identity . " to cloud.\n\n" . \
+ [ $DeviceInfo ] . "\n\n" . \
+ [ $FormatLine "Name" ($Cloud->"name") ] . "\n" . \
+ [ $FormatLine "Size" ([ $HumanReadableNum ($Cloud->"size") 1024 ] . "iB") ] . "\n" . \
+ [ $FormatLine "Download key" ($Cloud->"secret-download-key") ]); silent=true });
+ } else={
+ $SendNotification2 ({ origin=$ScriptName; \
+ subject=([ $SymbolForNotification "floppy-disk,warning-sign" ] . "Cloud backup failed"); \
+ message=("Failed uploading backup for " . $Identity . " to cloud!\n\n" . [ $DeviceInfo ]) });
+ $LogPrint error $ScriptName ("Failed uploading backup for " . $Identity . " to cloud!");
+ :set PackagesUpdateBackupFailure true;
+ :error false;
+ }
+ /file/remove "tmpfs/backup-cloud";
+} on-error={ }
diff --git a/backup-email b/backup-email
deleted file mode 100644
index 7cdf55e..0000000
--- a/backup-email
+++ /dev/null
@@ -1,80 +0,0 @@
-#!rsc by RouterOS
-# RouterOS script: backup-email
-# Copyright (c) 2013-2022 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 Domain;
-:global Identity;
-
-:global CharacterReplace;
-:global DeviceInfo;
-:global LogPrintExit2;
-:global MkDir;
-:global RandomDelay;
-:global ScriptFromTerminal;
-:global SendEMail2;
-: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;
-}
-
-:if ([ $MkDir $0 ] = false) do={
- $LogPrintExit2 error $0 ("Failed creating directory!") true;
-}
-
-# filename based on identity
-:local FileName [ $CharacterReplace ($Identity . "." . $Domain) "." "_" ];
-:local FilePath ($0 . "/" . $FileName);
-:local BackupFile "none";
-:local ConfigFile "none";
-:local Attach ({});
-
-# 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 ConfigFile ($FileName . ".rsc");
- :set Attach ($Attach, ($FilePath . ".rsc"));
-}
-
-# 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" . \
- "Config file: " . $ConfigFile); \
- attach=$Attach; remove-attach=true });
diff --git a/backup-email.rsc b/backup-email.rsc
new file mode 100644
index 0000000..64ca69c
--- /dev/null
+++ b/backup-email.rsc
@@ -0,0 +1,124 @@
+#!rsc by RouterOS
+# RouterOS script: backup-email
+# Copyright (c) 2013-2024 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# provides: backup-script, order=20
+# requires RouterOS, version=7.12
+#
+# create and email backup and config file
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/backup-email.md
+
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:do {
+ :local ScriptName [ :jobname ];
+
+ :global BackupPassword;
+ :global BackupRandomDelay;
+ :global BackupSendBinary;
+ :global BackupSendExport;
+ :global BackupSendGlobalConfig;
+ :global Domain;
+ :global Identity;
+ :global PackagesUpdateBackupFailure;
+
+ :global CleanName;
+ :global DeviceInfo;
+ :global FormatLine;
+ :global LogPrint;
+ :global MkDir;
+ :global RandomDelay;
+ :global ScriptFromTerminal;
+ :global ScriptLock;
+ :global SendEMail2;
+ :global SymbolForNotification;
+ :global WaitForFile;
+ :global WaitFullyConnected;
+
+ :if ([ :typeof $SendEMail2 ] = "nothing") do={
+ $LogPrint error $ScriptName ("The module for sending notifications via e-mail is not installed.");
+ :error false;
+ }
+
+ :if ($BackupSendBinary != true && \
+ $BackupSendExport != true) do={
+ $LogPrint error $ScriptName ("Configured to send neither backup nor config export.");
+ :error false;
+ }
+
+ :if ([ $ScriptLock $ScriptName ] = false) do={
+ :set PackagesUpdateBackupFailure true;
+ :error false;
+ }
+ $WaitFullyConnected;
+
+ :if ([ $ScriptFromTerminal $ScriptName ] = false && $BackupRandomDelay > 0) do={
+ $RandomDelay $BackupRandomDelay;
+ }
+
+ # filename based on identity
+ :local DirName ("tmpfs/" . $ScriptName);
+ :local FileName [ $CleanName ($Identity . "." . $Domain) ];
+ :local FilePath ($DirName . "/" . $FileName);
+ :local BackupFile "none";
+ :local ExportFile "none";
+ :local ConfigFile "none";
+ :local Attach ({});
+
+ :if ([ $MkDir $DirName ] = false) do={
+ $LogPrint error $ScriptName ("Failed creating directory!");
+ :error false;
+ }
+
+ # 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={
+ # Do *NOT* use '/file/add ...' here, as it is limited to 4095 bytes!
+ :execute script={ :put [ /system/script/get global-config-overlay source ]; } \
+ file=($FilePath . ".conf\00");
+ $WaitForFile ($FilePath . ".conf");
+ :set ConfigFile ($FileName . ".conf");
+ :set Attach ($Attach, ($FilePath . ".conf"));
+ }
+
+ # send email with status and files
+ $SendEMail2 ({ origin=$ScriptName; \
+ subject=([ $SymbolForNotification "floppy-disk,incoming-envelope" ] . \
+ "Backup & Config"); \
+ message=("See attached files for backup and config export for " . \
+ $Identity . ".\n\n" . \
+ [ $DeviceInfo ] . "\n\n" . \
+ [ $FormatLine "Backup file" $BackupFile ] . "\n" . \
+ [ $FormatLine "Export file" $ExportFile ] . "\n" . \
+ [ $FormatLine "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={
+ $LogPrint warning $ScriptName ("Files are still available, sending e-mail failed.");
+ :set PackagesUpdateBackupFailure true;
+ :error false;
+ }
+ :delay 1s;
+ :set I ($I + 1);
+ }
+} on-error={ }
diff --git a/backup-partition b/backup-partition
deleted file mode 100644
index a8aecac..0000000
--- a/backup-partition
+++ /dev/null
@@ -1,36 +0,0 @@
-#!rsc by RouterOS
-# RouterOS script: backup-partition
-# Copyright (c) 2022 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;
-}
diff --git a/backup-partition.rsc b/backup-partition.rsc
new file mode 100644
index 0000000..503d382
--- /dev/null
+++ b/backup-partition.rsc
@@ -0,0 +1,57 @@
+#!rsc by RouterOS
+# RouterOS script: backup-partition
+# Copyright (c) 2022-2024 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# provides: backup-script, order=70
+# requires RouterOS, version=7.12
+#
+# save configuration to fallback partition
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/backup-partition.md
+
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:do {
+ :local ScriptName [ :jobname ];
+
+ :global PackagesUpdateBackupFailure;
+
+ :global LogPrint;
+ :global ScriptLock;
+
+ :if ([ $ScriptLock $ScriptName ] = false) do={
+ :set PackagesUpdateBackupFailure true;
+ :error false;
+ }
+
+ :if ([ :len [ /partitions/find ] ] < 2) do={
+ $LogPrint error $ScriptName ("Device does not have a fallback partition.");
+ :set PackagesUpdateBackupFailure true;
+ :error false;
+ }
+
+ :local ActiveRunning [ /partitions/find where active running ];
+
+ :if ([ :len $ActiveRunning ] < 1) do={
+ $LogPrint error $ScriptName ("Device is not running from active partition.");
+ :set PackagesUpdateBackupFailure true;
+ :error false;
+ }
+
+ :local FallbackTo [ /partitions/get $ActiveRunning fallback-to ];
+
+ :do {
+ /system/scheduler/add start-time=startup name="running-from-backup-partition" \
+ on-event=(":log warning (\"Running from partition '\" . " . \
+ "[ /partitions/get [ find where running ] name ] . \"'!\")");
+ /partitions/save-config-to $FallbackTo;
+ /system/scheduler/remove "running-from-backup-partition";
+ $LogPrint info $ScriptName ("Saved configuration to partition '" . $FallbackTo . "'.");
+ } on-error={
+ /system/scheduler/remove [ find where name="running-from-backup-partition" ];
+ $LogPrint error $ScriptName ("Failed saving configuration to partition '" . $FallbackTo . "'!");
+ :set PackagesUpdateBackupFailure true;
+ :error false;
+ }
+} on-error={ }
diff --git a/backup-upload b/backup-upload
deleted file mode 100644
index 8ed4149..0000000
--- a/backup-upload
+++ /dev/null
@@ -1,106 +0,0 @@
-#!rsc by RouterOS
-# RouterOS script: backup-upload
-# Copyright (c) 2013-2022 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 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;
-}
-
-:if ([ $MkDir $0 ] = false) do={
- $LogPrintExit2 error $0 ("Failed creating directory!") true;
-}
-
-# filename based on identity
-:local FileName [ $CharacterReplace ($Identity . "." . $Domain) "." "_" ];
-:local FilePath ($0 . "/" . $FileName);
-:local BackupFile "none";
-:local ConfigFile "none";
-:local Failed 0;
-
-# 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 ConfigFile ($FileName . ".rsc");
- } on-error={
- $LogPrintExit2 error $0 ("Uploading configuration export failed!") false;
- :set ConfigFile "failed";
- :set Failed 1;
- }
-
- /file/remove ($FilePath . ".rsc");
-}
-
-$SendNotification2 ({ origin=$0; \
- subject=[ $IfThenElse ($Failed > 0) \
- ([ $SymbolForNotification "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" . \
- "Config file: " . $ConfigFile); silent=true });
-
-:if ($Failed = 1) do={
- :error "An error occured!";
-}
diff --git a/backup-upload.rsc b/backup-upload.rsc
new file mode 100644
index 0000000..ef5b7c7
--- /dev/null
+++ b/backup-upload.rsc
@@ -0,0 +1,161 @@
+#!rsc by RouterOS
+# RouterOS script: backup-upload
+# Copyright (c) 2013-2024 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# provides: backup-script, order=50
+# requires RouterOS, version=7.12
+#
+# create and upload backup and config file
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/backup-upload.md
+
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:do {
+ :local ScriptName [ :jobname ];
+
+ :global BackupPassword;
+ :global BackupRandomDelay;
+ :global BackupSendBinary;
+ :global BackupSendExport;
+ :global BackupSendGlobalConfig;
+ :global BackupUploadPass;
+ :global BackupUploadUrl;
+ :global BackupUploadUser;
+ :global Domain;
+ :global Identity;
+ :global PackagesUpdateBackupFailure;
+
+ :global CleanName;
+ :global DeviceInfo;
+ :global IfThenElse;
+ :global LogPrint;
+ :global MkDir;
+ :global RandomDelay;
+ :global ScriptFromTerminal;
+ :global ScriptLock;
+ :global SendNotification2;
+ :global SymbolForNotification;
+ :global WaitForFile;
+ :global WaitFullyConnected;
+
+ :if ($BackupSendBinary != true && \
+ $BackupSendExport != true) do={
+ $LogPrint error $ScriptName ("Configured to send neither backup nor config export.");
+ :error false;
+ }
+
+ :if ([ $ScriptLock $ScriptName ] = false) do={
+ :set PackagesUpdateBackupFailure true;
+ :error false;
+ }
+ $WaitFullyConnected;
+
+ :if ([ $ScriptFromTerminal $ScriptName ] = false && $BackupRandomDelay > 0) do={
+ $RandomDelay $BackupRandomDelay;
+ }
+
+ # filename based on identity
+ :local DirName ("tmpfs/" . $ScriptName);
+ :local FileName [ $CleanName ($Identity . "." . $Domain) ];
+ :local FilePath ($DirName . "/" . $FileName);
+ :local BackupFile "none";
+ :local ExportFile "none";
+ :local ConfigFile "none";
+ :local Failed 0;
+
+ :if ([ $MkDir $DirName ] = false) do={
+ $LogPrint error $ScriptName ("Failed creating directory!");
+ :error false;
+ }
+
+ # 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 [ /file/get ($FilePath . ".backup") ];
+ :set ($BackupFile->"name") ($FileName . ".backup");
+ } on-error={
+ $LogPrint error $ScriptName ("Uploading backup file failed!");
+ :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 [ /file/get ($FilePath . ".rsc") ];
+ :set ($ExportFile->"name") ($FileName . ".rsc");
+ } on-error={
+ $LogPrint error $ScriptName ("Uploading configuration export failed!");
+ :set ExportFile "failed";
+ :set Failed 1;
+ }
+
+ /file/remove ($FilePath . ".rsc");
+ }
+
+ # global-config-overlay
+ :if ($BackupSendGlobalConfig = true) do={
+ # Do *NOT* use '/file/add ...' here, as it is limited to 4095 bytes!
+ :execute script={ :put [ /system/script/get global-config-overlay source ]; } \
+ file=($FilePath . ".conf\00");
+ $WaitForFile ($FilePath . ".conf");
+
+ :do {
+ /tool/fetch upload=yes url=($BackupUploadUrl . "/" . $FileName . ".conf") \
+ user=$BackupUploadUser password=$BackupUploadPass src-path=($FilePath . ".conf");
+ :set ConfigFile [ /file/get ($FilePath . ".conf") ];
+ :set ($ConfigFile->"name") ($FileName . ".conf");
+ } on-error={
+ $LogPrint error $ScriptName ("Uploading global-config-overlay failed!");
+ :set ConfigFile "failed";
+ :set Failed 1;
+ }
+
+ /file/remove ($FilePath . ".conf");
+ }
+
+ :local FileInfo do={
+ :local Name $1;
+ :local File $2;
+
+ :global FormatLine;
+ :global HumanReadableNum;
+ :global IfThenElse;
+
+ :return \
+ [ $IfThenElse ([ :typeof $File ] = "array") \
+ ($Name . ":\n" . [ $FormatLine " name" ($File->"name") ] . "\n" . \
+ [ $FormatLine " size" ([ $HumanReadableNum ($File->"size") 1024 ] . "iB") ]) \
+ [ $FormatLine $Name $File ] ];
+ }
+
+ $SendNotification2 ({ origin=$ScriptName; \
+ subject=[ $IfThenElse ($Failed > 0) \
+ ([ $SymbolForNotification "floppy-disk,warning-sign" ] . "Backup & Config upload with failure") \
+ ([ $SymbolForNotification "floppy-disk,arrow-up" ] . "Backup & Config upload") ]; \
+ message=("Backup and config export upload for " . $Identity . ".\n\n" . \
+ [ $DeviceInfo ] . "\n\n" . \
+ [ $FileInfo "Backup file" $BackupFile ] . "\n" . \
+ [ $FileInfo "Export file" $ExportFile ] . "\n" . \
+ [ $FileInfo "Config file" $ConfigFile ]); silent=true });
+
+ :if ($Failed = 1) do={
+ :set PackagesUpdateBackupFailure true;
+ :error false;
+ }
+} on-error={ }
diff --git a/capsman-download-packages b/capsman-download-packages
deleted file mode 100644
index b745f14..0000000
--- a/capsman-download-packages
+++ /dev/null
@@ -1,86 +0,0 @@
-#!rsc by RouterOS
-# RouterOS script: capsman-download-packages
-# Copyright (c) 2018-2022 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 ];
- }
-}
diff --git a/capsman-download-packages.capsman.rsc b/capsman-download-packages.capsman.rsc
new file mode 100644
index 0000000..a3bd4a5
--- /dev/null
+++ b/capsman-download-packages.capsman.rsc
@@ -0,0 +1,83 @@
+#!rsc by RouterOS
+# RouterOS script: capsman-download-packages.capsman
+# Copyright (c) 2018-2024 Christian Hesse <mail@eworm.de>
+# Michael Gisbers <michael@gisbers.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# requires RouterOS, version=7.12
+#
+# download and cleanup packages for CAP installation from CAPsMAN
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/capsman-download-packages.md
+#
+# !! Do not edit this file, it is generated from template!
+
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:do {
+ :local ScriptName [ :jobname ];
+
+ :global CleanFilePath;
+ :global DownloadPackage;
+ :global LogPrint;
+ :global MkDir;
+ :global ScriptLock;
+ :global WaitFullyConnected;
+
+ :if ([ $ScriptLock $ScriptName ] = false) do={
+ :error false;
+ }
+ $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={
+ $LogPrint warning $ScriptName ("The CAPsMAN package path is not defined, can not download packages.");
+ :error false;
+ }
+
+ :if ([ :len [ /file/find where name=$PackagePath type="directory" ] ] = 0) do={
+ :if ([ $MkDir $PackagePath ] = false) do={
+ $LogPrint warning $ScriptName ("Creating directory at CAPsMAN package path (" . \
+ $PackagePath . ") failed!");
+ :error false;
+ }
+ $LogPrint info $ScriptName ("Created directory at CAPsMAN package path (" . $PackagePath . \
+ "). Please place your packages!");
+ }
+
+ :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 [ /file/find where type=package name~("^" . $PackagePath) ] ] = 0) do={
+ $LogPrint info $ScriptName ("No packages available, downloading default set.");
+ :foreach Arch in={ "arm"; "mipsbe" } do={
+ :foreach Package in={ "routeros"; "wireless" } do={
+ :if ([ $DownloadPackage $Package $InstalledVersion $Arch $PackagePath ] = true) do={
+ :set Updated true;
+ }
+ }
+ }
+ }
+
+ :if ($Updated = true) do={
+ :local Script ([ /system/script/find where source~"\n# provides: capsman-rolling-upgrade\n" ]->0);
+ :if ([ :len $Script ] > 0) do={
+ /system/script/run $Script;
+ } else={
+ /caps-man/remote-cap/upgrade [ find where version!=$InstalledVersion ];
+ }
+ }
+} on-error={ }
diff --git a/capsman-download-packages.template.rsc b/capsman-download-packages.template.rsc
new file mode 100644
index 0000000..cad3bcb
--- /dev/null
+++ b/capsman-download-packages.template.rsc
@@ -0,0 +1,94 @@
+#!rsc by RouterOS
+# RouterOS script: capsman-download-packages%TEMPL%
+# Copyright (c) 2018-2024 Christian Hesse <mail@eworm.de>
+# Michael Gisbers <michael@gisbers.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# requires RouterOS, version=7.12
+#
+# download and cleanup packages for CAP installation from CAPsMAN
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/capsman-download-packages.md
+#
+# !! This is just a template to generate the real script!
+# !! Pattern '%TEMPL%' is replaced, paths are filtered.
+
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:do {
+ :local ScriptName [ :jobname ];
+
+ :global CleanFilePath;
+ :global DownloadPackage;
+ :global LogPrint;
+ :global MkDir;
+ :global ScriptLock;
+ :global WaitFullyConnected;
+
+ :if ([ $ScriptLock $ScriptName ] = false) do={
+ :error false;
+ }
+ $WaitFullyConnected;
+
+ :local PackagePath [ $CleanFilePath [ /caps-man/manager/get package-path ] ];
+ :local PackagePath [ $CleanFilePath [ /interface/wifi/capsman/get package-path ] ];
+ :local InstalledVersion [ /system/package/update/get installed-version ];
+ :local Updated false;
+
+ :if ([ :len $PackagePath ] = 0) do={
+ $LogPrint warning $ScriptName ("The CAPsMAN package path is not defined, can not download packages.");
+ :error false;
+ }
+
+ :if ([ :len [ /file/find where name=$PackagePath type="directory" ] ] = 0) do={
+ :if ([ $MkDir $PackagePath ] = false) do={
+ $LogPrint warning $ScriptName ("Creating directory at CAPsMAN package path (" . \
+ $PackagePath . ") failed!");
+ :error false;
+ }
+ $LogPrint info $ScriptName ("Created directory at CAPsMAN package path (" . $PackagePath . \
+ "). Please place your packages!");
+ }
+
+ :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 [ /file/find where type=package name~("^" . $PackagePath) ] ] = 0) do={
+ $LogPrint info $ScriptName ("No packages available, downloading default set.");
+# NOT /interface/wifi/ #
+ :foreach Arch in={ "arm"; "mipsbe" } do={
+ :foreach Package in={ "routeros"; "wireless" } do={
+# NOT /interface/wifi/ #
+# NOT /caps-man/ #
+ :foreach Arch in={ "arm"; "arm64" } do={
+ :local Packages { "arm"={ "routeros"; "wifi-qcom"; "wifi-qcom-ac" };
+ "arm64"={ "routeros"; "wifi-qcom" } };
+ :foreach Package in=($Packages->$Arch) do={
+# NOT /caps-man/ #
+ :if ([ $DownloadPackage $Package $InstalledVersion $Arch $PackagePath ] = true) do={
+ :set Updated true;
+ }
+ }
+ }
+ }
+
+ :if ($Updated = true) do={
+ :local Script ([ /system/script/find where source~"\n# provides: capsman-rolling-upgrade\n" ]->0);
+ :if ([ :len $Script ] > 0) do={
+ /system/script/run $Script;
+ } else={
+ /caps-man/remote-cap/upgrade [ find where version!=$InstalledVersion ];
+ /interface/wifi/capsman/remote-cap/upgrade [ find where version!=$InstalledVersion ];
+ }
+ }
+} on-error={ }
diff --git a/capsman-download-packages.wifi.rsc b/capsman-download-packages.wifi.rsc
new file mode 100644
index 0000000..909688f
--- /dev/null
+++ b/capsman-download-packages.wifi.rsc
@@ -0,0 +1,85 @@
+#!rsc by RouterOS
+# RouterOS script: capsman-download-packages.wifi
+# Copyright (c) 2018-2024 Christian Hesse <mail@eworm.de>
+# Michael Gisbers <michael@gisbers.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# requires RouterOS, version=7.12
+#
+# download and cleanup packages for CAP installation from CAPsMAN
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/capsman-download-packages.md
+#
+# !! Do not edit this file, it is generated from template!
+
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:do {
+ :local ScriptName [ :jobname ];
+
+ :global CleanFilePath;
+ :global DownloadPackage;
+ :global LogPrint;
+ :global MkDir;
+ :global ScriptLock;
+ :global WaitFullyConnected;
+
+ :if ([ $ScriptLock $ScriptName ] = false) do={
+ :error false;
+ }
+ $WaitFullyConnected;
+
+ :local PackagePath [ $CleanFilePath [ /interface/wifi/capsman/get package-path ] ];
+ :local InstalledVersion [ /system/package/update/get installed-version ];
+ :local Updated false;
+
+ :if ([ :len $PackagePath ] = 0) do={
+ $LogPrint warning $ScriptName ("The CAPsMAN package path is not defined, can not download packages.");
+ :error false;
+ }
+
+ :if ([ :len [ /file/find where name=$PackagePath type="directory" ] ] = 0) do={
+ :if ([ $MkDir $PackagePath ] = false) do={
+ $LogPrint warning $ScriptName ("Creating directory at CAPsMAN package path (" . \
+ $PackagePath . ") failed!");
+ :error false;
+ }
+ $LogPrint info $ScriptName ("Created directory at CAPsMAN package path (" . $PackagePath . \
+ "). Please place your packages!");
+ }
+
+ :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 [ /file/find where type=package name~("^" . $PackagePath) ] ] = 0) do={
+ $LogPrint info $ScriptName ("No packages available, downloading default set.");
+ :foreach Arch in={ "arm"; "arm64" } do={
+ :local Packages { "arm"={ "routeros"; "wifi-qcom"; "wifi-qcom-ac" };
+ "arm64"={ "routeros"; "wifi-qcom" } };
+ :foreach Package in=($Packages->$Arch) do={
+ :if ([ $DownloadPackage $Package $InstalledVersion $Arch $PackagePath ] = true) do={
+ :set Updated true;
+ }
+ }
+ }
+ }
+
+ :if ($Updated = true) do={
+ :local Script ([ /system/script/find where source~"\n# provides: capsman-rolling-upgrade\n" ]->0);
+ :if ([ :len $Script ] > 0) do={
+ /system/script/run $Script;
+ } else={
+ /interface/wifi/capsman/remote-cap/upgrade [ find where version!=$InstalledVersion ];
+ }
+ }
+} on-error={ }
diff --git a/capsman-rolling-upgrade b/capsman-rolling-upgrade
deleted file mode 100644
index e6b1c51..0000000
--- a/capsman-rolling-upgrade
+++ /dev/null
@@ -1,36 +0,0 @@
-#!rsc by RouterOS
-# RouterOS script: capsman-rolling-upgrade
-# Copyright (c) 2018-2022 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");
- }
-}
diff --git a/capsman-rolling-upgrade.capsman.rsc b/capsman-rolling-upgrade.capsman.rsc
new file mode 100644
index 0000000..11bfd69
--- /dev/null
+++ b/capsman-rolling-upgrade.capsman.rsc
@@ -0,0 +1,46 @@
+#!rsc by RouterOS
+# RouterOS script: capsman-rolling-upgrade.capsman
+# Copyright (c) 2018-2024 Christian Hesse <mail@eworm.de>
+# Michael Gisbers <michael@gisbers.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# provides: capsman-rolling-upgrade
+# requires RouterOS, version=7.12
+#
+# upgrade CAPs one after another
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/capsman-rolling-upgrade.md
+#
+# !! Do not edit this file, it is generated from template!
+
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:do {
+ :local ScriptName [ :jobname ];
+
+ :global LogPrint;
+ :global ScriptLock;
+
+ :if ([ $ScriptLock $ScriptName ] = false) do={
+ :error false;
+ }
+
+ :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={
+ $LogPrint info $ScriptName ("Starting upgrade for " . $RemoteCapVal->"name" . \
+ " (" . $RemoteCapVal->"identity" . ")...");
+ /caps-man/remote-cap/upgrade $RemoteCap;
+ } else={
+ $LogPrint warning $ScriptName ("Remote CAP vanished, skipping upgrade.");
+ }
+ :delay ($Delay . "s");
+ }
+ }
+} on-error={ }
diff --git a/capsman-rolling-upgrade.template.rsc b/capsman-rolling-upgrade.template.rsc
new file mode 100644
index 0000000..e0effd4
--- /dev/null
+++ b/capsman-rolling-upgrade.template.rsc
@@ -0,0 +1,54 @@
+#!rsc by RouterOS
+# RouterOS script: capsman-rolling-upgrade%TEMPL%
+# Copyright (c) 2018-2024 Christian Hesse <mail@eworm.de>
+# Michael Gisbers <michael@gisbers.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# provides: capsman-rolling-upgrade
+# requires RouterOS, version=7.12
+#
+# upgrade CAPs one after another
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/capsman-rolling-upgrade.md
+#
+# !! This is just a template to generate the real script!
+# !! Pattern '%TEMPL%' is replaced, paths are filtered.
+
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:do {
+ :local ScriptName [ :jobname ];
+
+ :global LogPrint;
+ :global ScriptLock;
+
+ :if ([ $ScriptLock $ScriptName ] = false) do={
+ :error false;
+ }
+
+ :local InstalledVersion [ /system/package/update/get installed-version ];
+
+ :local RemoteCapCount [ :len [ /caps-man/remote-cap/find ] ];
+ :local RemoteCapCount [ :len [ /interface/wifi/capsman/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={
+ :foreach RemoteCap in=[ /interface/wifi/capsman/remote-cap/find where version!=$InstalledVersion ] do={
+ :local RemoteCapVal [ /caps-man/remote-cap/get $RemoteCap ];
+ :local RemoteCapVal [ /interface/wifi/capsman/remote-cap/get $RemoteCap ];
+ :if ([ :len $RemoteCapVal ] > 1) do={
+# NOT /caps-man/ #
+ :set ($RemoteCapVal->"name") ($RemoteCapVal->"common-name");
+# NOT /caps-man/ #
+ $LogPrint info $ScriptName ("Starting upgrade for " . $RemoteCapVal->"name" . \
+ " (" . $RemoteCapVal->"identity" . ")...");
+ /caps-man/remote-cap/upgrade $RemoteCap;
+ /interface/wifi/capsman/remote-cap/upgrade $RemoteCap;
+ } else={
+ $LogPrint warning $ScriptName ("Remote CAP vanished, skipping upgrade.");
+ }
+ :delay ($Delay . "s");
+ }
+ }
+} on-error={ }
diff --git a/capsman-rolling-upgrade.wifi.rsc b/capsman-rolling-upgrade.wifi.rsc
new file mode 100644
index 0000000..8ec6f26
--- /dev/null
+++ b/capsman-rolling-upgrade.wifi.rsc
@@ -0,0 +1,47 @@
+#!rsc by RouterOS
+# RouterOS script: capsman-rolling-upgrade.wifi
+# Copyright (c) 2018-2024 Christian Hesse <mail@eworm.de>
+# Michael Gisbers <michael@gisbers.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# provides: capsman-rolling-upgrade
+# requires RouterOS, version=7.12
+#
+# upgrade CAPs one after another
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/capsman-rolling-upgrade.md
+#
+# !! Do not edit this file, it is generated from template!
+
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:do {
+ :local ScriptName [ :jobname ];
+
+ :global LogPrint;
+ :global ScriptLock;
+
+ :if ([ $ScriptLock $ScriptName ] = false) do={
+ :error false;
+ }
+
+ :local InstalledVersion [ /system/package/update/get installed-version ];
+
+ :local RemoteCapCount [ :len [ /interface/wifi/capsman/remote-cap/find ] ];
+ :if ($RemoteCapCount > 0) do={
+ :local Delay (600 / $RemoteCapCount);
+ :if ($Delay > 120) do={ :set Delay 120; }
+ :foreach RemoteCap in=[ /interface/wifi/capsman/remote-cap/find where version!=$InstalledVersion ] do={
+ :local RemoteCapVal [ /interface/wifi/capsman/remote-cap/get $RemoteCap ];
+ :if ([ :len $RemoteCapVal ] > 1) do={
+ :set ($RemoteCapVal->"name") ($RemoteCapVal->"common-name");
+ $LogPrint info $ScriptName ("Starting upgrade for " . $RemoteCapVal->"name" . \
+ " (" . $RemoteCapVal->"identity" . ")...");
+ /interface/wifi/capsman/remote-cap/upgrade $RemoteCap;
+ } else={
+ $LogPrint warning $ScriptName ("Remote CAP vanished, skipping upgrade.");
+ }
+ :delay ($Delay . "s");
+ }
+ }
+} on-error={ }
diff --git a/certificate-renew-issued b/certificate-renew-issued
deleted file mode 100644
index 29aaa93..0000000
--- a/certificate-renew-issued
+++ /dev/null
@@ -1,38 +0,0 @@
-#!rsc by RouterOS
-# RouterOS script: certificate-renew-issued
-# Copyright (c) 2019-2022 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;
- }
-}
diff --git a/certificate-renew-issued.rsc b/certificate-renew-issued.rsc
new file mode 100644
index 0000000..45805c5
--- /dev/null
+++ b/certificate-renew-issued.rsc
@@ -0,0 +1,48 @@
+#!rsc by RouterOS
+# RouterOS script: certificate-renew-issued
+# Copyright (c) 2019-2024 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# requires RouterOS, version=7.12
+#
+# renew locally issued certificates
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/certificate-renew-issued.md
+
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:do {
+ :local ScriptName [ :jobname ];
+
+ :global CertIssuedExportPass;
+
+ :global LogPrint;
+ :global MkDir;
+ :global ScriptLock;
+
+ :if ([ $ScriptLock $ScriptName ] = false) do={
+ :error false;
+ }
+
+ :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"));
+ $LogPrint info $ScriptName ("Issued a new certificate for \"" . $CertVal->"common-name" . \
+ "\", exported to \"cert-issued/" . $CertVal->"common-name" . ".p12\".");
+ } else={
+ $LogPrint warning $ScriptName ("Failed creating directory, not exporting certificate.");
+ }
+ } else={
+ $LogPrint info $ScriptName ("Issued a new certificate for \"" . $CertVal->"common-name" . "\".");
+ }
+ }
+} on-error={ }
diff --git a/certs/Cloudflare-Inc-ECC-CA-3.pem b/certs/Cloudflare-Inc-ECC-CA-3.pem
new file mode 100644
index 0000000..fa91603
--- /dev/null
+++ b/certs/Cloudflare-Inc-ECC-CA-3.pem
@@ -0,0 +1,163 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 0a:37:87:64:5e:5f:b4:8c:22:4e:fd:1b:ed:14:0c:3c
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C = IE, O = Baltimore, OU = CyberTrust, CN = Baltimore CyberTrust Root
+ Validity
+ Not Before: Jan 27 12:48:08 2020 GMT
+ Not After : Dec 31 23:59:59 2024 GMT
+ Subject: C = US, O = "Cloudflare, Inc.", CN = Cloudflare Inc ECC CA-3
+ Subject Public Key Info:
+ Public Key Algorithm: id-ecPublicKey
+ Public-Key: (256 bit)
+ pub:
+ 04:b9:ad:4d:66:99:14:0b:46:ec:1f:81:d1:2a:50:
+ 1e:9d:03:15:2f:34:12:7d:2d:96:b8:88:38:9b:85:
+ 5f:8f:bf:bb:4d:ef:61:46:c4:c9:73:d4:24:4f:e0:
+ ee:1c:ce:6c:b3:51:71:2f:6a:ee:4c:05:09:77:d3:
+ 72:62:a4:9b:d7
+ ASN1 OID: prime256v1
+ NIST CURVE: P-256
+ X509v3 extensions:
+ X509v3 Subject Key Identifier:
+ A5:CE:37:EA:EB:B0:75:0E:94:67:88:B4:45:FA:D9:24:10:87:96:1F
+ X509v3 Authority Key Identifier:
+ E5:9D:59:30:82:47:58:CC:AC:FA:08:54:36:86:7B:3A:B5:04:4D:F0
+ X509v3 Key Usage: critical
+ Digital Signature, Certificate Sign, CRL Sign
+ X509v3 Extended Key Usage:
+ TLS Web Server Authentication, TLS Web Client Authentication
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ Authority Information Access:
+ OCSP - URI:http://ocsp.digicert.com
+ X509v3 CRL Distribution Points:
+ Full Name:
+ URI:http://crl3.digicert.com/Omniroot2025.crl
+ X509v3 Certificate Policies:
+ Policy: 2.16.840.1.114412.1.1
+ CPS: https://www.digicert.com/CPS
+ Policy: 2.16.840.1.114412.1.2
+ Policy: 2.23.140.1.2.1
+ Policy: 2.23.140.1.2.2
+ Policy: 2.23.140.1.2.3
+ Signature Algorithm: sha256WithRSAEncryption
+ Signature Value:
+ 05:24:1d:dd:1b:b0:2a:eb:98:d6:85:e3:39:4d:5e:6b:57:9d:
+ 82:57:fc:eb:e8:31:a2:57:90:65:05:be:16:44:38:5a:77:02:
+ b9:cf:10:42:c6:e1:92:a4:e3:45:27:f8:00:47:2c:68:a8:56:
+ 99:53:54:8f:ad:9e:40:c1:d0:0f:b6:d7:0d:0b:38:48:6c:50:
+ 2c:49:90:06:5b:64:1d:8b:cc:48:30:2e:de:08:e2:9b:49:22:
+ c0:92:0c:11:5e:96:92:94:d5:fc:20:dc:56:6c:e5:92:93:bf:
+ 7a:1c:c0:37:e3:85:49:15:fa:2b:e1:74:39:18:0f:b7:da:f3:
+ a2:57:58:60:4f:cc:8e:94:00:fc:46:7b:34:31:3e:4d:47:82:
+ 81:3a:cb:f4:89:5d:0e:ef:4d:0d:6e:9c:1b:82:24:dd:32:25:
+ 5d:11:78:51:10:3d:a0:35:23:04:2f:65:6f:9c:c1:d1:43:d7:
+ d0:1e:f3:31:67:59:27:dd:6b:d2:75:09:93:11:24:24:14:cf:
+ 29:be:e6:23:c3:b8:8f:72:3f:e9:07:c8:24:44:53:7a:b3:b9:
+ 61:65:a1:4c:0e:c6:48:00:c9:75:63:05:87:70:45:52:83:d3:
+ 95:9d:45:ea:f0:e8:31:1d:7e:09:1f:0a:fe:3e:dd:aa:3c:5e:
+ 74:d2:ac:b1
+-----BEGIN CERTIFICATE-----
+MIIDzTCCArWgAwIBAgIQCjeHZF5ftIwiTv0b7RQMPDANBgkqhkiG9w0BAQsFADBa
+MQswCQYDVQQGEwJJRTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJl
+clRydXN0MSIwIAYDVQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTIw
+MDEyNzEyNDgwOFoXDTI0MTIzMTIzNTk1OVowSjELMAkGA1UEBhMCVVMxGTAXBgNV
+BAoTEENsb3VkZmxhcmUsIEluYy4xIDAeBgNVBAMTF0Nsb3VkZmxhcmUgSW5jIEVD
+QyBDQS0zMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEua1NZpkUC0bsH4HRKlAe
+nQMVLzQSfS2WuIg4m4Vfj7+7Te9hRsTJc9QkT+DuHM5ss1FxL2ruTAUJd9NyYqSb
+16OCAWgwggFkMB0GA1UdDgQWBBSlzjfq67B1DpRniLRF+tkkEIeWHzAfBgNVHSME
+GDAWgBTlnVkwgkdYzKz6CFQ2hns6tQRN8DAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0l
+BBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMBIGA1UdEwEB/wQIMAYBAf8CAQAwNAYI
+KwYBBQUHAQEEKDAmMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5j
+b20wOgYDVR0fBDMwMTAvoC2gK4YpaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL09t
+bmlyb290MjAyNS5jcmwwbQYDVR0gBGYwZDA3BglghkgBhv1sAQEwKjAoBggrBgEF
+BQcCARYcaHR0cHM6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzALBglghkgBhv1sAQIw
+CAYGZ4EMAQIBMAgGBmeBDAECAjAIBgZngQwBAgMwDQYJKoZIhvcNAQELBQADggEB
+AAUkHd0bsCrrmNaF4zlNXmtXnYJX/OvoMaJXkGUFvhZEOFp3ArnPEELG4ZKk40Un
++ABHLGioVplTVI+tnkDB0A+21w0LOEhsUCxJkAZbZB2LzEgwLt4I4ptJIsCSDBFe
+lpKU1fwg3FZs5ZKTv3ocwDfjhUkV+ivhdDkYD7fa86JXWGBPzI6UAPxGezQxPk1H
+goE6y/SJXQ7vTQ1unBuCJN0yJV0ReFEQPaA1IwQvZW+cwdFD19Ae8zFnWSfda9J1
+CZMRJCQUzym+5iPDuI9yP+kHyCREU3qzuWFloUwOxkgAyXVjBYdwRVKD05WdRerw
+6DEdfgkfCv4+3ao8XnTSrLE=
+-----END CERTIFICATE-----
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 33554617 (0x20000b9)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C = IE, O = Baltimore, OU = CyberTrust, CN = Baltimore CyberTrust Root
+ Validity
+ Not Before: May 12 18:46:00 2000 GMT
+ Not After : May 12 23:59:00 2025 GMT
+ Subject: C = IE, O = Baltimore, OU = CyberTrust, CN = Baltimore CyberTrust Root
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:a3:04:bb:22:ab:98:3d:57:e8:26:72:9a:b5:79:
+ d4:29:e2:e1:e8:95:80:b1:b0:e3:5b:8e:2b:29:9a:
+ 64:df:a1:5d:ed:b0:09:05:6d:db:28:2e:ce:62:a2:
+ 62:fe:b4:88:da:12:eb:38:eb:21:9d:c0:41:2b:01:
+ 52:7b:88:77:d3:1c:8f:c7:ba:b9:88:b5:6a:09:e7:
+ 73:e8:11:40:a7:d1:cc:ca:62:8d:2d:e5:8f:0b:a6:
+ 50:d2:a8:50:c3:28:ea:f5:ab:25:87:8a:9a:96:1c:
+ a9:67:b8:3f:0c:d5:f7:f9:52:13:2f:c2:1b:d5:70:
+ 70:f0:8f:c0:12:ca:06:cb:9a:e1:d9:ca:33:7a:77:
+ d6:f8:ec:b9:f1:68:44:42:48:13:d2:c0:c2:a4:ae:
+ 5e:60:fe:b6:a6:05:fc:b4:dd:07:59:02:d4:59:18:
+ 98:63:f5:a5:63:e0:90:0c:7d:5d:b2:06:7a:f3:85:
+ ea:eb:d4:03:ae:5e:84:3e:5f:ff:15:ed:69:bc:f9:
+ 39:36:72:75:cf:77:52:4d:f3:c9:90:2c:b9:3d:e5:
+ c9:23:53:3f:1f:24:98:21:5c:07:99:29:bd:c6:3a:
+ ec:e7:6e:86:3a:6b:97:74:63:33:bd:68:18:31:f0:
+ 78:8d:76:bf:fc:9e:8e:5d:2a:86:a7:4d:90:dc:27:
+ 1a:39
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Subject Key Identifier:
+ E5:9D:59:30:82:47:58:CC:AC:FA:08:54:36:86:7B:3A:B5:04:4D:F0
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:3
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ Signature Algorithm: sha1WithRSAEncryption
+ Signature Value:
+ 85:0c:5d:8e:e4:6f:51:68:42:05:a0:dd:bb:4f:27:25:84:03:
+ bd:f7:64:fd:2d:d7:30:e3:a4:10:17:eb:da:29:29:b6:79:3f:
+ 76:f6:19:13:23:b8:10:0a:f9:58:a4:d4:61:70:bd:04:61:6a:
+ 12:8a:17:d5:0a:bd:c5:bc:30:7c:d6:e9:0c:25:8d:86:40:4f:
+ ec:cc:a3:7e:38:c6:37:11:4f:ed:dd:68:31:8e:4c:d2:b3:01:
+ 74:ee:be:75:5e:07:48:1a:7f:70:ff:16:5c:84:c0:79:85:b8:
+ 05:fd:7f:be:65:11:a3:0f:c0:02:b4:f8:52:37:39:04:d5:a9:
+ 31:7a:18:bf:a0:2a:f4:12:99:f7:a3:45:82:e3:3c:5e:f5:9d:
+ 9e:b5:c8:9e:7c:2e:c8:a4:9e:4e:08:14:4b:6d:fd:70:6d:6b:
+ 1a:63:bd:64:e6:1f:b7:ce:f0:f2:9f:2e:bb:1b:b7:f2:50:88:
+ 73:92:c2:e2:e3:16:8d:9a:32:02:ab:8e:18:dd:e9:10:11:ee:
+ 7e:35:ab:90:af:3e:30:94:7a:d0:33:3d:a7:65:0f:f5:fc:8e:
+ 9e:62:cf:47:44:2c:01:5d:bb:1d:b5:32:d2:47:d2:38:2e:d0:
+ fe:81:dc:32:6a:1e:b5:ee:3c:d5:fc:e7:81:1d:19:c3:24:42:
+ ea:63:39:a9
+-----BEGIN CERTIFICATE-----
+MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ
+RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD
+VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoX
+DTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9y
+ZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVy
+VHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKr
+mD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjr
+IZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeK
+mpYcqWe4PwzV9/lSEy/CG9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSu
+XmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZy
+dc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/ye
+jl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1
+BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3
+DQEBBQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT92
+9hkTI7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3Wgx
+jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0
+Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67G7fyUIhz
+ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS
+R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp
+-----END CERTIFICATE-----
diff --git a/certs/DigiCert-Global-G2-TLS-RSA-SHA256-2020-CA1.pem b/certs/DigiCert-Global-G2-TLS-RSA-SHA256-2020-CA1.pem
new file mode 100644
index 0000000..12084ee
--- /dev/null
+++ b/certs/DigiCert-Global-G2-TLS-RSA-SHA256-2020-CA1.pem
@@ -0,0 +1,182 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 0c:f5:bd:06:2b:56:02:f4:7a:b8:50:2c:23:cc:f0:66
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert Global Root G2
+ Validity
+ Not Before: Mar 30 00:00:00 2021 GMT
+ Not After : Mar 29 23:59:59 2031 GMT
+ Subject: C=US, O=DigiCert Inc, CN=DigiCert Global G2 TLS RSA SHA256 2020 CA1
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:cc:f7:10:62:4f:a6:bb:63:6f:ed:90:52:56:c5:
+ 6d:27:7b:7a:12:56:8a:f1:f4:f9:d6:e7:e1:8f:bd:
+ 95:ab:f2:60:41:15:70:db:12:00:fa:27:0a:b5:57:
+ 38:5b:7d:b2:51:93:71:95:0e:6a:41:94:5b:35:1b:
+ fa:7b:fa:bb:c5:be:24:30:fe:56:ef:c4:f3:7d:97:
+ e3:14:f5:14:4d:cb:a7:10:f2:16:ea:ab:22:f0:31:
+ 22:11:61:69:90:26:ba:78:d9:97:1f:e3:7d:66:ab:
+ 75:44:95:73:c8:ac:ff:ef:5d:0a:8a:59:43:e1:ac:
+ b2:3a:0f:f3:48:fc:d7:6b:37:c1:63:dc:de:46:d6:
+ db:45:fe:7d:23:fd:90:e8:51:07:1e:51:a3:5f:ed:
+ 49:46:54:7f:2c:88:c5:f4:13:9c:97:15:3c:03:e8:
+ a1:39:dc:69:0c:32:c1:af:16:57:4c:94:47:42:7c:
+ a2:c8:9c:7d:e6:d4:4d:54:af:42:99:a8:c1:04:c2:
+ 77:9c:d6:48:e4:ce:11:e0:2a:80:99:f0:43:70:cf:
+ 3f:76:6b:d1:4c:49:ab:24:5e:c2:0d:82:fd:46:a8:
+ ab:6c:93:cc:62:52:42:75:92:f8:9a:fa:5e:5e:b2:
+ b0:61:e5:1f:1f:b9:7f:09:98:e8:3d:fa:83:7f:47:
+ 69:a1
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Subject Key Identifier:
+ 74:85:80:C0:66:C7:DF:37:DE:CF:BD:29:37:AA:03:1D:BE:ED:CD:17
+ X509v3 Authority Key Identifier:
+ 4E:22:54:20:18:95:E6:E3:6E:E6:0F:FA:FA:B9:12:ED:06:17:8F:39
+ X509v3 Key Usage: critical
+ Digital Signature, Certificate Sign, CRL Sign
+ X509v3 Extended Key Usage:
+ TLS Web Server Authentication, TLS Web Client Authentication
+ Authority Information Access:
+ OCSP - URI:http://ocsp.digicert.com
+ CA Issuers - URI:http://cacerts.digicert.com/DigiCertGlobalRootG2.crt
+ X509v3 CRL Distribution Points:
+ Full Name:
+ URI:http://crl3.digicert.com/DigiCertGlobalRootG2.crl
+ X509v3 Certificate Policies:
+ Policy: 2.16.840.1.114412.2.1
+ Policy: 2.23.140.1.1
+ Policy: 2.23.140.1.2.1
+ Policy: 2.23.140.1.2.2
+ Policy: 2.23.140.1.2.3
+ Signature Algorithm: sha256WithRSAEncryption
+ Signature Value:
+ 90:f1:70:cb:28:97:69:97:7c:74:fd:c0:fa:26:7b:53:ab:ad:
+ cd:65:fd:ba:9c:06:9c:8a:d7:5a:43:87:ed:4d:4c:56:5f:ad:
+ c1:c5:b5:05:20:2e:59:d1:ff:4a:f5:a0:2a:d8:b0:95:ad:c9:
+ 2e:4a:3b:d7:a7:f6:6f:88:29:fc:30:3f:24:84:bb:c3:b7:7b:
+ 93:07:2c:af:87:6b:76:33:ed:00:55:52:b2:59:9e:e4:b9:d0:
+ f3:df:e7:0f:fe:dd:f8:c4:b9:10:72:81:09:04:5f:cf:97:9e:
+ 2e:32:75:8e:cf:9a:58:d2:57:31:7e:37:01:81:b2:66:6d:29:
+ 1a:b1:66:09:6d:d1:6e:90:f4:b9:fa:2f:01:14:c5:5c:56:64:
+ 01:d9:7d:87:a8:38:53:9f:8b:5d:46:6d:5c:c6:27:84:81:d4:
+ 7e:8c:8c:a3:9b:52:e7:c6:88:ec:37:7c:2a:fb:f0:55:5a:38:
+ 72:10:d8:00:13:cf:4c:73:db:aa:37:35:a8:29:81:69:9c:76:
+ bc:de:18:7b:90:d4:ca:cf:ef:67:03:fd:04:5a:21:16:b1:ff:
+ ea:3f:df:dc:82:f5:eb:f4:59:92:23:0d:24:2a:95:25:4c:ca:
+ a1:91:e6:d4:b7:ac:87:74:b3:f1:6d:a3:99:db:f9:d5:bd:84:
+ 40:9f:07:98
+-----BEGIN CERTIFICATE-----
+MIIEyDCCA7CgAwIBAgIQDPW9BitWAvR6uFAsI8zwZjANBgkqhkiG9w0BAQsFADBh
+MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
+d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH
+MjAeFw0yMTAzMzAwMDAwMDBaFw0zMTAzMjkyMzU5NTlaMFkxCzAJBgNVBAYTAlVT
+MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxMzAxBgNVBAMTKkRpZ2lDZXJ0IEdsb2Jh
+bCBHMiBUTFMgUlNBIFNIQTI1NiAyMDIwIENBMTCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBAMz3EGJPprtjb+2QUlbFbSd7ehJWivH0+dbn4Y+9lavyYEEV
+cNsSAPonCrVXOFt9slGTcZUOakGUWzUb+nv6u8W+JDD+Vu/E832X4xT1FE3LpxDy
+FuqrIvAxIhFhaZAmunjZlx/jfWardUSVc8is/+9dCopZQ+GssjoP80j812s3wWPc
+3kbW20X+fSP9kOhRBx5Ro1/tSUZUfyyIxfQTnJcVPAPooTncaQwywa8WV0yUR0J8
+osicfebUTVSvQpmowQTCd5zWSOTOEeAqgJnwQ3DPP3Zr0UxJqyRewg2C/Uaoq2yT
+zGJSQnWS+Jr6Xl6ysGHlHx+5fwmY6D36g39HaaECAwEAAaOCAYIwggF+MBIGA1Ud
+EwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFHSFgMBmx9833s+9KTeqAx2+7c0XMB8G
+A1UdIwQYMBaAFE4iVCAYlebjbuYP+vq5Eu0GF485MA4GA1UdDwEB/wQEAwIBhjAd
+BgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwdgYIKwYBBQUHAQEEajBoMCQG
+CCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQAYIKwYBBQUHMAKG
+NGh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEdsb2JhbFJvb3RH
+Mi5jcnQwQgYDVR0fBDswOTA3oDWgM4YxaHR0cDovL2NybDMuZGlnaWNlcnQuY29t
+L0RpZ2lDZXJ0R2xvYmFsUm9vdEcyLmNybDA9BgNVHSAENjA0MAsGCWCGSAGG/WwC
+ATAHBgVngQwBATAIBgZngQwBAgEwCAYGZ4EMAQICMAgGBmeBDAECAzANBgkqhkiG
+9w0BAQsFAAOCAQEAkPFwyyiXaZd8dP3A+iZ7U6utzWX9upwGnIrXWkOH7U1MVl+t
+wcW1BSAuWdH/SvWgKtiwla3JLko716f2b4gp/DA/JIS7w7d7kwcsr4drdjPtAFVS
+slme5LnQ89/nD/7d+MS5EHKBCQRfz5eeLjJ1js+aWNJXMX43AYGyZm0pGrFmCW3R
+bpD0ufovARTFXFZkAdl9h6g4U5+LXUZtXMYnhIHUfoyMo5tS58aI7Dd8KvvwVVo4
+chDYABPPTHPbqjc1qCmBaZx2vN4Ye5DUys/vZwP9BFohFrH/6j/f3IL16/RZkiMN
+JCqVJUzKoZHm1Lesh3Sz8W2jmdv51b2EQJ8HmA==
+-----END CERTIFICATE-----
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 03:3a:f1:e6:a7:11:a9:a0:bb:28:64:b1:1d:09:fa:e5
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert Global Root G2
+ Validity
+ Not Before: Aug 1 12:00:00 2013 GMT
+ Not After : Jan 15 12:00:00 2038 GMT
+ Subject: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert Global Root G2
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:bb:37:cd:34:dc:7b:6b:c9:b2:68:90:ad:4a:75:
+ ff:46:ba:21:0a:08:8d:f5:19:54:c9:fb:88:db:f3:
+ ae:f2:3a:89:91:3c:7a:e6:ab:06:1a:6b:cf:ac:2d:
+ e8:5e:09:24:44:ba:62:9a:7e:d6:a3:a8:7e:e0:54:
+ 75:20:05:ac:50:b7:9c:63:1a:6c:30:dc:da:1f:19:
+ b1:d7:1e:de:fd:d7:e0:cb:94:83:37:ae:ec:1f:43:
+ 4e:dd:7b:2c:d2:bd:2e:a5:2f:e4:a9:b8:ad:3a:d4:
+ 99:a4:b6:25:e9:9b:6b:00:60:92:60:ff:4f:21:49:
+ 18:f7:67:90:ab:61:06:9c:8f:f2:ba:e9:b4:e9:92:
+ 32:6b:b5:f3:57:e8:5d:1b:cd:8c:1d:ab:95:04:95:
+ 49:f3:35:2d:96:e3:49:6d:dd:77:e3:fb:49:4b:b4:
+ ac:55:07:a9:8f:95:b3:b4:23:bb:4c:6d:45:f0:f6:
+ a9:b2:95:30:b4:fd:4c:55:8c:27:4a:57:14:7c:82:
+ 9d:cd:73:92:d3:16:4a:06:0c:8c:50:d1:8f:1e:09:
+ be:17:a1:e6:21:ca:fd:83:e5:10:bc:83:a5:0a:c4:
+ 67:28:f6:73:14:14:3d:46:76:c3:87:14:89:21:34:
+ 4d:af:0f:45:0c:a6:49:a1:ba:bb:9c:c5:b1:33:83:
+ 29:85
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ X509v3 Key Usage: critical
+ Digital Signature, Certificate Sign, CRL Sign
+ X509v3 Subject Key Identifier:
+ 4E:22:54:20:18:95:E6:E3:6E:E6:0F:FA:FA:B9:12:ED:06:17:8F:39
+ Signature Algorithm: sha256WithRSAEncryption
+ Signature Value:
+ 60:67:28:94:6f:0e:48:63:eb:31:dd:ea:67:18:d5:89:7d:3c:
+ c5:8b:4a:7f:e9:be:db:2b:17:df:b0:5f:73:77:2a:32:13:39:
+ 81:67:42:84:23:f2:45:67:35:ec:88:bf:f8:8f:b0:61:0c:34:
+ a4:ae:20:4c:84:c6:db:f8:35:e1:76:d9:df:a6:42:bb:c7:44:
+ 08:86:7f:36:74:24:5a:da:6c:0d:14:59:35:bd:f2:49:dd:b6:
+ 1f:c9:b3:0d:47:2a:3d:99:2f:bb:5c:bb:b5:d4:20:e1:99:5f:
+ 53:46:15:db:68:9b:f0:f3:30:d5:3e:31:e2:8d:84:9e:e3:8a:
+ da:da:96:3e:35:13:a5:5f:f0:f9:70:50:70:47:41:11:57:19:
+ 4e:c0:8f:ae:06:c4:95:13:17:2f:1b:25:9f:75:f2:b1:8e:99:
+ a1:6f:13:b1:41:71:fe:88:2a:c8:4f:10:20:55:d7:f3:14:45:
+ e5:e0:44:f4:ea:87:95:32:93:0e:fe:53:46:fa:2c:9d:ff:8b:
+ 22:b9:4b:d9:09:45:a4:de:a4:b8:9a:58:dd:1b:7d:52:9f:8e:
+ 59:43:88:81:a4:9e:26:d5:6f:ad:dd:0d:c6:37:7d:ed:03:92:
+ 1b:e5:77:5f:76:ee:3c:8d:c4:5d:56:5b:a2:d9:66:6e:b3:35:
+ 37:e5:32:b6
+-----BEGIN CERTIFICATE-----
+MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBh
+MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
+d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH
+MjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVT
+MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
+b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkqhkiG
+9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI
+2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx
+1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQ
+q2EGnI/yuum06ZIya7XzV+hdG82MHauVBJVJ8zUtluNJbd134/tJS7SsVQepj5Wz
+tCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQ
+vIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQABo0IwQDAP
+BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV
+5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY
+1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4
+NeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NG
+Fdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ91
+8rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTe
+pLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl
+MrY=
+-----END CERTIFICATE-----
diff --git a/certs/DigiCert TLS Hybrid ECC SHA384 2020 CA1.pem b/certs/DigiCert-TLS-Hybrid-ECC-SHA384-2020-CA1.pem
index 446f56f..446f56f 100644
--- a/certs/DigiCert TLS Hybrid ECC SHA384 2020 CA1.pem
+++ b/certs/DigiCert-TLS-Hybrid-ECC-SHA384-2020-CA1.pem
diff --git a/certs/E1.pem b/certs/E1.pem
index 4c3c212..a62fc03 100644
--- a/certs/E1.pem
+++ b/certs/E1.pem
@@ -122,122 +122,3 @@ zj0EAwMDaAAwZQIwe3lORlCEwkSHRhtFcP9Ymd70/aTSVaYgLXTWNLxBo1BfASdW
tL4ndQavEi51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5U6VR5CmD1/iQMVtCnwr1
/q4AaOeMSQ+2b1tbFfLn
-----END CERTIFICATE-----
-Certificate:
- Data:
- Version: 3 (0x2)
- Serial Number:
- 82:10:cf:b0:d2:40:e3:59:44:63:e0:bb:63:82:8b:00
- Signature Algorithm: sha256WithRSAEncryption
- Issuer: C = US, O = Internet Security Research Group, CN = ISRG Root X1
- Validity
- Not Before: Jun 4 11:04:38 2015 GMT
- Not After : Jun 4 11:04:38 2035 GMT
- Subject: C = US, O = Internet Security Research Group, CN = ISRG Root X1
- Subject Public Key Info:
- Public Key Algorithm: rsaEncryption
- RSA Public-Key: (4096 bit)
- Modulus:
- 00:ad:e8:24:73:f4:14:37:f3:9b:9e:2b:57:28:1c:
- 87:be:dc:b7:df:38:90:8c:6e:3c:e6:57:a0:78:f7:
- 75:c2:a2:fe:f5:6a:6e:f6:00:4f:28:db:de:68:86:
- 6c:44:93:b6:b1:63:fd:14:12:6b:bf:1f:d2:ea:31:
- 9b:21:7e:d1:33:3c:ba:48:f5:dd:79:df:b3:b8:ff:
- 12:f1:21:9a:4b:c1:8a:86:71:69:4a:66:66:6c:8f:
- 7e:3c:70:bf:ad:29:22:06:f3:e4:c0:e6:80:ae:e2:
- 4b:8f:b7:99:7e:94:03:9f:d3:47:97:7c:99:48:23:
- 53:e8:38:ae:4f:0a:6f:83:2e:d1:49:57:8c:80:74:
- b6:da:2f:d0:38:8d:7b:03:70:21:1b:75:f2:30:3c:
- fa:8f:ae:dd:da:63:ab:eb:16:4f:c2:8e:11:4b:7e:
- cf:0b:e8:ff:b5:77:2e:f4:b2:7b:4a:e0:4c:12:25:
- 0c:70:8d:03:29:a0:e1:53:24:ec:13:d9:ee:19:bf:
- 10:b3:4a:8c:3f:89:a3:61:51:de:ac:87:07:94:f4:
- 63:71:ec:2e:e2:6f:5b:98:81:e1:89:5c:34:79:6c:
- 76:ef:3b:90:62:79:e6:db:a4:9a:2f:26:c5:d0:10:
- e1:0e:de:d9:10:8e:16:fb:b7:f7:a8:f7:c7:e5:02:
- 07:98:8f:36:08:95:e7:e2:37:96:0d:36:75:9e:fb:
- 0e:72:b1:1d:9b:bc:03:f9:49:05:d8:81:dd:05:b4:
- 2a:d6:41:e9:ac:01:76:95:0a:0f:d8:df:d5:bd:12:
- 1f:35:2f:28:17:6c:d2:98:c1:a8:09:64:77:6e:47:
- 37:ba:ce:ac:59:5e:68:9d:7f:72:d6:89:c5:06:41:
- 29:3e:59:3e:dd:26:f5:24:c9:11:a7:5a:a3:4c:40:
- 1f:46:a1:99:b5:a7:3a:51:6e:86:3b:9e:7d:72:a7:
- 12:05:78:59:ed:3e:51:78:15:0b:03:8f:8d:d0:2f:
- 05:b2:3e:7b:4a:1c:4b:73:05:12:fc:c6:ea:e0:50:
- 13:7c:43:93:74:b3:ca:74:e7:8e:1f:01:08:d0:30:
- d4:5b:71:36:b4:07:ba:c1:30:30:5c:48:b7:82:3b:
- 98:a6:7d:60:8a:a2:a3:29:82:cc:ba:bd:83:04:1b:
- a2:83:03:41:a1:d6:05:f1:1b:c2:b6:f0:a8:7c:86:
- 3b:46:a8:48:2a:88:dc:76:9a:76:bf:1f:6a:a5:3d:
- 19:8f:eb:38:f3:64:de:c8:2b:0d:0a:28:ff:f7:db:
- e2:15:42:d4:22:d0:27:5d:e1:79:fe:18:e7:70:88:
- ad:4e:e6:d9:8b:3a:c6:dd:27:51:6e:ff:bc:64:f5:
- 33:43:4f
- Exponent: 65537 (0x10001)
- X509v3 extensions:
- X509v3 Key Usage: critical
- Certificate Sign, CRL Sign
- X509v3 Basic Constraints: critical
- CA:TRUE
- X509v3 Subject Key Identifier:
- 79:B4:59:E6:7B:B6:E5:E4:01:73:80:08:88:C8:1A:58:F6:E9:9B:6E
- Signature Algorithm: sha256WithRSAEncryption
- 55:1f:58:a9:bc:b2:a8:50:d0:0c:b1:d8:1a:69:20:27:29:08:
- ac:61:75:5c:8a:6e:f8:82:e5:69:2f:d5:f6:56:4b:b9:b8:73:
- 10:59:d3:21:97:7e:e7:4c:71:fb:b2:d2:60:ad:39:a8:0b:ea:
- 17:21:56:85:f1:50:0e:59:eb:ce:e0:59:e9:ba:c9:15:ef:86:
- 9d:8f:84:80:f6:e4:e9:91:90:dc:17:9b:62:1b:45:f0:66:95:
- d2:7c:6f:c2:ea:3b:ef:1f:cf:cb:d6:ae:27:f1:a9:b0:c8:ae:
- fd:7d:7e:9a:fa:22:04:eb:ff:d9:7f:ea:91:2b:22:b1:17:0e:
- 8f:f2:8a:34:5b:58:d8:fc:01:c9:54:b9:b8:26:cc:8a:88:33:
- 89:4c:2d:84:3c:82:df:ee:96:57:05:ba:2c:bb:f7:c4:b7:c7:
- 4e:3b:82:be:31:c8:22:73:73:92:d1:c2:80:a4:39:39:10:33:
- 23:82:4c:3c:9f:86:b2:55:98:1d:be:29:86:8c:22:9b:9e:e2:
- 6b:3b:57:3a:82:70:4d:dc:09:c7:89:cb:0a:07:4d:6c:e8:5d:
- 8e:c9:ef:ce:ab:c7:bb:b5:2b:4e:45:d6:4a:d0:26:cc:e5:72:
- ca:08:6a:a5:95:e3:15:a1:f7:a4:ed:c9:2c:5f:a5:fb:ff:ac:
- 28:02:2e:be:d7:7b:bb:e3:71:7b:90:16:d3:07:5e:46:53:7c:
- 37:07:42:8c:d3:c4:96:9c:d5:99:b5:2a:e0:95:1a:80:48:ae:
- 4c:39:07:ce:cc:47:a4:52:95:2b:ba:b8:fb:ad:d2:33:53:7d:
- e5:1d:4d:6d:d5:a1:b1:c7:42:6f:e6:40:27:35:5c:a3:28:b7:
- 07:8d:e7:8d:33:90:e7:23:9f:fb:50:9c:79:6c:46:d5:b4:15:
- b3:96:6e:7e:9b:0c:96:3a:b8:52:2d:3f:d6:5b:e1:fb:08:c2:
- 84:fe:24:a8:a3:89:da:ac:6a:e1:18:2a:b1:a8:43:61:5b:d3:
- 1f:dc:3b:8d:76:f2:2d:e8:8d:75:df:17:33:6c:3d:53:fb:7b:
- cb:41:5f:ff:dc:a2:d0:61:38:e1:96:b8:ac:5d:8b:37:d7:75:
- d5:33:c0:99:11:ae:9d:41:c1:72:75:84:be:02:41:42:5f:67:
- 24:48:94:d1:9b:27:be:07:3f:b9:b8:4f:81:74:51:e1:7a:b7:
- ed:9d:23:e2:be:e0:d5:28:04:13:3c:31:03:9e:dd:7a:6c:8f:
- c6:07:18:c6:7f:de:47:8e:3f:28:9e:04:06:cf:a5:54:34:77:
- bd:ec:89:9b:e9:17:43:df:5b:db:5f:fe:8e:1e:57:a2:cd:40:
- 9d:7e:62:22:da:de:18:27
------BEGIN CERTIFICATE-----
-MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw
-TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
-cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4
-WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu
-ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY
-MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc
-h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+
-0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U
-A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW
-T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH
-B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC
-B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv
-KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn
-OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn
-jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw
-qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI
-rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
-HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq
-hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL
-ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ
-3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK
-NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5
-ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur
-TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC
-jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc
-oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq
-4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA
-mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d
-emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=
------END CERTIFICATE-----
diff --git a/certs/GTS CA 1C3.pem b/certs/GTS-CA-1C3.pem
index a8432d2..a8432d2 100644
--- a/certs/GTS CA 1C3.pem
+++ b/certs/GTS-CA-1C3.pem
diff --git a/certs/GTS-CA-1P5.pem b/certs/GTS-CA-1P5.pem
new file mode 100644
index 0000000..5be738d
--- /dev/null
+++ b/certs/GTS-CA-1P5.pem
@@ -0,0 +1,238 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 02:03:bc:50:a3:27:53:f0:91:80:22:ed:f1
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=US, O=Google Trust Services LLC, CN=GTS Root R1
+ Validity
+ Not Before: Aug 13 00:00:42 2020 GMT
+ Not After : Sep 30 00:00:42 2027 GMT
+ Subject: C=US, O=Google Trust Services LLC, CN=GTS CA 1P5
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:b3:82:f0:24:8c:bf:2d:87:af:b2:d9:a7:ae:fa:
+ ca:ba:44:d6:5b:3e:fe:b2:f7:b2:65:16:dc:de:10:
+ e8:4f:2d:10:58:5a:28:86:87:a1:ee:6a:b3:a0:d9:
+ 75:4f:7f:a1:52:01:8b:55:a8:4a:5b:06:48:c8:36:
+ 12:25:ab:89:f9:f2:23:5f:9d:60:65:f9:5c:da:be:
+ 3a:e8:5c:6d:7d:9c:d0:84:18:85:30:cd:4e:9b:ec:
+ 3c:d8:b3:e1:96:d4:f3:c5:0b:65:db:8f:b0:74:cb:
+ f6:1e:f3:78:f1:ac:95:c5:dd:73:c3:31:88:81:af:
+ 74:aa:6f:fd:0c:e3:05:95:f0:c5:10:4f:65:63:fa:
+ a0:af:c6:18:3d:c5:a1:df:97:79:d7:05:89:b3:30:
+ b0:74:ae:3d:92:10:6b:8c:15:77:dd:0b:04:57:fb:
+ 81:03:dd:ea:22:34:d5:e5:56:b2:f0:c4:8d:41:b1:
+ c3:02:db:62:ec:80:d0:ff:76:d4:86:e4:04:1a:b6:
+ b6:0c:2b:62:71:7d:d9:af:d9:f1:5e:fa:c0:1e:ca:
+ a0:19:5c:55:f0:80:d1:2a:0c:07:86:90:9f:35:e3:
+ 28:2b:5b:ef:23:c8:a3:1d:a4:a3:3a:ee:fe:83:dc:
+ 82:4c:25:b0:4d:c5:51:ad:9e:9b:d3:5b:84:c2:1a:
+ 5a:e9
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Key Usage: critical
+ Digital Signature, Certificate Sign, CRL Sign
+ X509v3 Extended Key Usage:
+ TLS Web Server Authentication, TLS Web Client Authentication
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Subject Key Identifier:
+ D5:FC:9E:0D:DF:1E:CA:DD:08:97:97:6E:2B:C5:5F:C5:2B:F5:EC:B8
+ X509v3 Authority Key Identifier:
+ E4:AF:2B:26:71:1A:2B:48:27:85:2F:52:66:2C:EF:F0:89:13:71:3E
+ Authority Information Access:
+ OCSP - URI:http://ocsp.pki.goog/gtsr1
+ CA Issuers - URI:http://pki.goog/repo/certs/gtsr1.der
+ X509v3 CRL Distribution Points:
+ Full Name:
+ URI:http://crl.pki.goog/gtsr1/gtsr1.crl
+ X509v3 Certificate Policies:
+ Policy: 1.3.6.1.4.1.11129.2.5.3
+ CPS: https://pki.goog/repository/
+ Policy: 2.23.140.1.2.1
+ Signature Algorithm: sha256WithRSAEncryption
+ Signature Value:
+ 6c:63:27:ee:23:df:e5:52:68:4d:81:66:91:85:df:7d:65:e5:
+ 5b:37:31:08:26:b2:07:5d:9a:be:b1:ca:01:b9:ad:bf:9d:77:
+ f6:51:1d:d7:98:c5:0b:49:a1:7b:a1:d7:d3:68:e5:44:0f:8b:
+ ba:36:dd:42:82:77:d2:8d:dd:f5:3f:fb:eb:c8:07:98:93:ee:
+ 5a:d0:b5:3d:de:4b:1c:2d:8c:4d:ec:7e:8c:7b:fe:4e:40:fd:
+ f0:b4:b3:59:02:10:51:5c:e3:c0:2b:fd:b7:06:48:51:7e:09:
+ 5e:3f:0f:dc:a7:fe:97:e7:79:c5:0e:44:89:78:c5:69:59:29:
+ a0:9a:3a:48:36:29:a6:94:93:55:2d:b8:47:b5:e9:96:b5:9f:
+ 07:cd:a6:ab:3e:32:8a:c0:86:83:c5:c1:41:c8:9f:2f:35:8e:
+ 0d:c0:07:7a:e1:ac:c9:65:b5:cb:8a:a7:dd:71:d8:61:65:39:
+ 84:ac:32:3e:f7:7a:36:f1:56:9f:57:a9:41:6d:5a:90:a7:db:
+ 3a:ea:75:80:0c:63:0b:69:74:6f:07:4c:15:f3:37:28:a5:19:
+ a4:6e:f5:f6:20:cd:63:b2:7e:c4:2b:09:75:89:da:d1:3c:2e:
+ 72:4f:36:1a:a1:9e:44:d0:cd:9b:a6:23:08:3f:97:a1:a7:9e:
+ 5a:a5:f7:09:94:ad:5d:76:5d:28:56:d1:1a:66:51:51:07:7b:
+ de:3d:b0:c8:ef:30:7a:24:2d:be:b8:b3:86:f6:4b:f7:f0:b5:
+ 4f:ff:ce:c6:f9:f6:3f:2a:27:08:0f:09:3e:23:5a:c7:e3:42:
+ 2d:7a:36:e4:3d:98:96:60:39:98:ea:d1:db:63:2a:eb:78:09:
+ b1:4e:21:b3:8e:b7:ce:3e:92:f1:95:5c:a4:39:d0:c0:2b:c8:
+ 53:15:f5:d2:2f:82:cd:06:74:67:99:90:77:37:0a:97:2d:c5:
+ 1c:1e:f4:d0:5b:e9:15:e3:ea:02:09:c8:13:d7:13:70:65:bf:
+ fb:88:9b:5a:25:be:77:09:e1:a7:6a:4e:11:75:b9:1e:4d:f1:
+ 00:1b:6a:66:79:8e:c3:6e:d8:6d:a2:22:a2:6d:05:fb:2c:f2:
+ f1:50:e5:a0:d1:d8:9f:35:7d:fc:70:ab:59:2a:02:f1:be:b0:
+ d3:f1:f8:cd:12:b9:6a:25:90:5b:e3:85:20:e6:f5:da:cb:40:
+ 1c:19:34:20:03:61:77:ba:7f:48:0f:49:0b:29:eb:e7:61:64:
+ c7:63:d1:47:eb:1c:e1:ee:94:46:ef:39:73:cc:ee:4f:2b:8d:
+ dc:fb:58:a7:b3:65:20:99:95:b9:fb:55:6f:d7:96:6e:94:3d:
+ f4:7a:92:8e:63:1d:df:6d
+-----BEGIN CERTIFICATE-----
+MIIFjDCCA3SgAwIBAgINAgO8UKMnU/CRgCLt8TANBgkqhkiG9w0BAQsFADBHMQsw
+CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU
+MBIGA1UEAxMLR1RTIFJvb3QgUjEwHhcNMjAwODEzMDAwMDQyWhcNMjcwOTMwMDAw
+MDQyWjBGMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp
+Y2VzIExMQzETMBEGA1UEAxMKR1RTIENBIDFQNTCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBALOC8CSMvy2Hr7LZp676yrpE1ls+/rL3smUW3N4Q6E8tEFha
+KIaHoe5qs6DZdU9/oVIBi1WoSlsGSMg2EiWrifnyI1+dYGX5XNq+OuhcbX2c0IQY
+hTDNTpvsPNiz4ZbU88ULZduPsHTL9h7zePGslcXdc8MxiIGvdKpv/QzjBZXwxRBP
+ZWP6oK/GGD3Fod+XedcFibMwsHSuPZIQa4wVd90LBFf7gQPd6iI01eVWsvDEjUGx
+wwLbYuyA0P921IbkBBq2tgwrYnF92a/Z8V76wB7KoBlcVfCA0SoMB4aQnzXjKCtb
+7yPIox2kozru/oPcgkwlsE3FUa2em9NbhMIaWukCAwEAAaOCAXYwggFyMA4GA1Ud
+DwEB/wQEAwIBhjAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwEgYDVR0T
+AQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU1fyeDd8eyt0Il5duK8VfxSv17LgwHwYD
+VR0jBBgwFoAU5K8rJnEaK0gnhS9SZizv8IkTcT4waAYIKwYBBQUHAQEEXDBaMCYG
+CCsGAQUFBzABhhpodHRwOi8vb2NzcC5wa2kuZ29vZy9ndHNyMTAwBggrBgEFBQcw
+AoYkaHR0cDovL3BraS5nb29nL3JlcG8vY2VydHMvZ3RzcjEuZGVyMDQGA1UdHwQt
+MCswKaAnoCWGI2h0dHA6Ly9jcmwucGtpLmdvb2cvZ3RzcjEvZ3RzcjEuY3JsME0G
+A1UdIARGMEQwOAYKKwYBBAHWeQIFAzAqMCgGCCsGAQUFBwIBFhxodHRwczovL3Br
+aS5nb29nL3JlcG9zaXRvcnkvMAgGBmeBDAECATANBgkqhkiG9w0BAQsFAAOCAgEA
+bGMn7iPf5VJoTYFmkYXffWXlWzcxCCayB12avrHKAbmtv5139lEd15jFC0mhe6HX
+02jlRA+LujbdQoJ30o3d9T/768gHmJPuWtC1Pd5LHC2MTex+jHv+TkD98LSzWQIQ
+UVzjwCv9twZIUX4JXj8P3Kf+l+d5xQ5EiXjFaVkpoJo6SDYpppSTVS24R7XplrWf
+B82mqz4yisCGg8XBQcifLzWODcAHeuGsyWW1y4qn3XHYYWU5hKwyPvd6NvFWn1ep
+QW1akKfbOup1gAxjC2l0bwdMFfM3KKUZpG719iDNY7J+xCsJdYna0Twuck82GqGe
+RNDNm6YjCD+XoaeeWqX3CZStXXZdKFbRGmZRUQd73j2wyO8weiQtvrizhvZL9/C1
+T//Oxvn2PyonCA8JPiNax+NCLXo25D2YlmA5mOrR22Mq63gJsU4hs463zj6S8ZVc
+pDnQwCvIUxX10i+CzQZ0Z5mQdzcKly3FHB700FvpFePqAgnIE9cTcGW/+4ibWiW+
+dwnhp2pOEXW5Hk3xABtqZnmOw27YbaIiom0F+yzy8VDloNHYnzV9/HCrWSoC8b6w
+0/H4zRK5aiWQW+OFIOb12stAHBk0IANhd7p/SA9JCynr52Fkx2PRR+sc4e6URu85
+c8zuTyuN3PtYp7NlIJmVuftVb9eWbpQ99HqSjmMd320=
+-----END CERTIFICATE-----
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 02:03:e5:93:6f:31:b0:13:49:88:6b:a2:17
+ Signature Algorithm: sha384WithRSAEncryption
+ Issuer: C=US, O=Google Trust Services LLC, CN=GTS Root R1
+ Validity
+ Not Before: Jun 22 00:00:00 2016 GMT
+ Not After : Jun 22 00:00:00 2036 GMT
+ Subject: C=US, O=Google Trust Services LLC, CN=GTS Root R1
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (4096 bit)
+ Modulus:
+ 00:b6:11:02:8b:1e:e3:a1:77:9b:3b:dc:bf:94:3e:
+ b7:95:a7:40:3c:a1:fd:82:f9:7d:32:06:82:71:f6:
+ f6:8c:7f:fb:e8:db:bc:6a:2e:97:97:a3:8c:4b:f9:
+ 2b:f6:b1:f9:ce:84:1d:b1:f9:c5:97:de:ef:b9:f2:
+ a3:e9:bc:12:89:5e:a7:aa:52:ab:f8:23:27:cb:a4:
+ b1:9c:63:db:d7:99:7e:f0:0a:5e:eb:68:a6:f4:c6:
+ 5a:47:0d:4d:10:33:e3:4e:b1:13:a3:c8:18:6c:4b:
+ ec:fc:09:90:df:9d:64:29:25:23:07:a1:b4:d2:3d:
+ 2e:60:e0:cf:d2:09:87:bb:cd:48:f0:4d:c2:c2:7a:
+ 88:8a:bb:ba:cf:59:19:d6:af:8f:b0:07:b0:9e:31:
+ f1:82:c1:c0:df:2e:a6:6d:6c:19:0e:b5:d8:7e:26:
+ 1a:45:03:3d:b0:79:a4:94:28:ad:0f:7f:26:e5:a8:
+ 08:fe:96:e8:3c:68:94:53:ee:83:3a:88:2b:15:96:
+ 09:b2:e0:7a:8c:2e:75:d6:9c:eb:a7:56:64:8f:96:
+ 4f:68:ae:3d:97:c2:84:8f:c0:bc:40:c0:0b:5c:bd:
+ f6:87:b3:35:6c:ac:18:50:7f:84:e0:4c:cd:92:d3:
+ 20:e9:33:bc:52:99:af:32:b5:29:b3:25:2a:b4:48:
+ f9:72:e1:ca:64:f7:e6:82:10:8d:e8:9d:c2:8a:88:
+ fa:38:66:8a:fc:63:f9:01:f9:78:fd:7b:5c:77:fa:
+ 76:87:fa:ec:df:b1:0e:79:95:57:b4:bd:26:ef:d6:
+ 01:d1:eb:16:0a:bb:8e:0b:b5:c5:c5:8a:55:ab:d3:
+ ac:ea:91:4b:29:cc:19:a4:32:25:4e:2a:f1:65:44:
+ d0:02:ce:aa:ce:49:b4:ea:9f:7c:83:b0:40:7b:e7:
+ 43:ab:a7:6c:a3:8f:7d:89:81:fa:4c:a5:ff:d5:8e:
+ c3:ce:4b:e0:b5:d8:b3:8e:45:cf:76:c0:ed:40:2b:
+ fd:53:0f:b0:a7:d5:3b:0d:b1:8a:a2:03:de:31:ad:
+ cc:77:ea:6f:7b:3e:d6:df:91:22:12:e6:be:fa:d8:
+ 32:fc:10:63:14:51:72:de:5d:d6:16:93:bd:29:68:
+ 33:ef:3a:66:ec:07:8a:26:df:13:d7:57:65:78:27:
+ de:5e:49:14:00:a2:00:7f:9a:a8:21:b6:a9:b1:95:
+ b0:a5:b9:0d:16:11:da:c7:6c:48:3c:40:e0:7e:0d:
+ 5a:cd:56:3c:d1:97:05:b9:cb:4b:ed:39:4b:9c:c4:
+ 3f:d2:55:13:6e:24:b0:d6:71:fa:f4:c1:ba:cc:ed:
+ 1b:f5:fe:81:41:d8:00:98:3d:3a:c8:ae:7a:98:37:
+ 18:05:95
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Key Usage: critical
+ Digital Signature, Certificate Sign, CRL Sign
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ X509v3 Subject Key Identifier:
+ E4:AF:2B:26:71:1A:2B:48:27:85:2F:52:66:2C:EF:F0:89:13:71:3E
+ Signature Algorithm: sha384WithRSAEncryption
+ Signature Value:
+ 9f:aa:42:26:db:0b:9b:be:ff:1e:96:92:2e:3e:a2:65:4a:6a:
+ 98:ba:22:cb:7d:c1:3a:d8:82:0a:06:c6:f6:a5:de:c0:4e:87:
+ 66:79:a1:f9:a6:58:9c:aa:f9:b5:e6:60:e7:e0:e8:b1:1e:42:
+ 41:33:0b:37:3d:ce:89:70:15:ca:b5:24:a8:cf:6b:b5:d2:40:
+ 21:98:cf:22:34:cf:3b:c5:22:84:e0:c5:0e:8a:7c:5d:88:e4:
+ 35:24:ce:9b:3e:1a:54:1e:6e:db:b2:87:a7:fc:f3:fa:81:55:
+ 14:62:0a:59:a9:22:05:31:3e:82:d6:ee:db:57:34:bc:33:95:
+ d3:17:1b:e8:27:a2:8b:7b:4e:26:1a:7a:5a:64:b6:d1:ac:37:
+ f1:fd:a0:f3:38:ec:72:f0:11:75:9d:cb:34:52:8d:e6:76:6b:
+ 17:c6:df:86:ab:27:8e:49:2b:75:66:81:10:21:a6:ea:3e:f4:
+ ae:25:ff:7c:15:de:ce:8c:25:3f:ca:62:70:0a:f7:2f:09:66:
+ 07:c8:3f:1c:fc:f0:db:45:30:df:62:88:c1:b5:0f:9d:c3:9f:
+ 4a:de:59:59:47:c5:87:22:36:e6:82:a7:ed:0a:b9:e2:07:a0:
+ 8d:7b:7a:4a:3c:71:d2:e2:03:a1:1f:32:07:dd:1b:e4:42:ce:
+ 0c:00:45:61:80:b5:0b:20:59:29:78:bd:f9:55:cb:63:c5:3c:
+ 4c:f4:b6:ff:db:6a:5f:31:6b:99:9e:2c:c1:6b:50:a4:d7:e6:
+ 18:14:bd:85:3f:67:ab:46:9f:a0:ff:42:a7:3a:7f:5c:cb:5d:
+ b0:70:1d:2b:34:f5:d4:76:09:0c:eb:78:4c:59:05:f3:33:42:
+ c3:61:15:10:1b:77:4d:ce:22:8c:d4:85:f2:45:7d:b7:53:ea:
+ ef:40:5a:94:0a:5c:20:5f:4e:40:5d:62:22:76:df:ff:ce:61:
+ bd:8c:23:78:d2:37:02:e0:8e:de:d1:11:37:89:f6:bf:ed:49:
+ 07:62:ae:92:ec:40:1a:af:14:09:d9:d0:4e:b2:a2:f7:be:ee:
+ ee:d8:ff:dc:1a:2d:de:b8:36:71:e2:fc:79:b7:94:25:d1:48:
+ 73:5b:a1:35:e7:b3:99:67:75:c1:19:3a:2b:47:4e:d3:42:8e:
+ fd:31:c8:16:66:da:d2:0c:3c:db:b3:8e:c9:a1:0d:80:0f:7b:
+ 16:77:14:bf:ff:db:09:94:b2:93:bc:20:58:15:e9:db:71:43:
+ f3:de:10:c3:00:dc:a8:2a:95:b6:c2:d6:3f:90:6b:76:db:6c:
+ fe:8c:bc:f2:70:35:0c:dc:99:19:35:dc:d7:c8:46:63:d5:36:
+ 71:ae:57:fb:b7:82:6d:dc
+-----BEGIN CERTIFICATE-----
+MIIFVzCCAz+gAwIBAgINAgPlk28xsBNJiGuiFzANBgkqhkiG9w0BAQwFADBHMQsw
+CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU
+MBIGA1UEAxMLR1RTIFJvb3QgUjEwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw
+MDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp
+Y2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwggIiMA0GCSqGSIb3DQEBAQUA
+A4ICDwAwggIKAoICAQC2EQKLHuOhd5s73L+UPreVp0A8of2C+X0yBoJx9vaMf/vo
+27xqLpeXo4xL+Sv2sfnOhB2x+cWX3u+58qPpvBKJXqeqUqv4IyfLpLGcY9vXmX7w
+Cl7raKb0xlpHDU0QM+NOsROjyBhsS+z8CZDfnWQpJSMHobTSPS5g4M/SCYe7zUjw
+TcLCeoiKu7rPWRnWr4+wB7CeMfGCwcDfLqZtbBkOtdh+JhpFAz2weaSUKK0Pfybl
+qAj+lug8aJRT7oM6iCsVlgmy4HqMLnXWnOunVmSPlk9orj2XwoSPwLxAwAtcvfaH
+szVsrBhQf4TgTM2S0yDpM7xSma8ytSmzJSq0SPly4cpk9+aCEI3oncKKiPo4Zor8
+Y/kB+Xj9e1x3+naH+uzfsQ55lVe0vSbv1gHR6xYKu44LtcXFilWr06zqkUspzBmk
+MiVOKvFlRNACzqrOSbTqn3yDsEB750Orp2yjj32JgfpMpf/VjsPOS+C12LOORc92
+wO1AK/1TD7Cn1TsNsYqiA94xrcx36m97PtbfkSIS5r762DL8EGMUUXLeXdYWk70p
+aDPvOmbsB4om3xPXV2V4J95eSRQAogB/mqghtqmxlbCluQ0WEdrHbEg8QOB+DVrN
+VjzRlwW5y0vtOUucxD/SVRNuJLDWcfr0wbrM7Rv1/oFB2ACYPTrIrnqYNxgFlQID
+AQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E
+FgQU5K8rJnEaK0gnhS9SZizv8IkTcT4wDQYJKoZIhvcNAQEMBQADggIBAJ+qQibb
+C5u+/x6Wki4+omVKapi6Ist9wTrYggoGxval3sBOh2Z5ofmmWJyq+bXmYOfg6LEe
+QkEzCzc9zolwFcq1JKjPa7XSQCGYzyI0zzvFIoTgxQ6KfF2I5DUkzps+GlQebtuy
+h6f88/qBVRRiClmpIgUxPoLW7ttXNLwzldMXG+gnoot7TiYaelpkttGsN/H9oPM4
+7HLwEXWdyzRSjeZ2axfG34arJ45JK3VmgRAhpuo+9K4l/3wV3s6MJT/KYnAK9y8J
+ZgfIPxz88NtFMN9iiMG1D53Dn0reWVlHxYciNuaCp+0KueIHoI17eko8cdLiA6Ef
+MgfdG+RCzgwARWGAtQsgWSl4vflVy2PFPEz0tv/bal8xa5meLMFrUKTX5hgUvYU/
+Z6tGn6D/Qqc6f1zLXbBwHSs09dR2CQzreExZBfMzQsNhFRAbd03OIozUhfJFfbdT
+6u9AWpQKXCBfTkBdYiJ23//OYb2MI3jSNwLgjt7RETeJ9r/tSQdirpLsQBqvFAnZ
+0E6yove+7u7Y/9waLd64NnHi/Hm3lCXRSHNboTXns5lndcEZOitHTtNCjv0xyBZm
+2tIMPNuzjsmhDYAPexZ3FL//2wmUspO8IFgV6dtxQ/PeEMMA3KgqlbbC1j+Qa3bb
+bP6MvPJwNQzcmRk13NfIRmPVNnGuV/u3gm3c
+-----END CERTIFICATE-----
diff --git a/certs/GlobalSign-Atlas-R3-DV-TLS-CA-2022-Q3.pem b/certs/GlobalSign-Atlas-R3-DV-TLS-CA-2022-Q3.pem
new file mode 100644
index 0000000..b514c11
--- /dev/null
+++ b/certs/GlobalSign-Atlas-R3-DV-TLS-CA-2022-Q3.pem
@@ -0,0 +1,177 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 7c:2a:0c:21:3f:c6:55:53:45:c9:1f:19:1f:b8:4e:fa
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: OU = GlobalSign Root CA - R3, O = GlobalSign, CN = GlobalSign
+ Validity
+ Not Before: Apr 20 12:00:00 2022 GMT
+ Not After : Apr 20 00:00:00 2025 GMT
+ Subject: C = BE, O = GlobalSign nv-sa, CN = GlobalSign Atlas R3 DV TLS CA 2022 Q3
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:b8:a8:7a:66:3c:4e:66:9c:ce:37:a5:54:35:4d:
+ 36:c7:99:d3:a8:27:36:f2:2f:c6:d5:18:3e:e9:09:
+ dd:05:d6:d7:2c:34:32:7c:08:63:49:d1:10:37:e5:
+ 78:5d:11:62:ce:6d:fb:2f:3f:37:94:db:8f:7b:30:
+ e9:5e:2c:d9:55:3f:b2:db:b9:a0:b5:60:37:8b:a4:
+ 06:32:35:50:a4:09:af:0a:45:ff:a8:1f:9b:65:8e:
+ dd:4a:e0:40:a1:e3:63:37:58:90:dd:75:3b:fc:0e:
+ 1c:82:40:98:bd:70:b1:c1:48:14:14:3c:04:4b:69:
+ dd:d4:9c:01:a6:e9:21:e3:82:0a:fe:e4:aa:bf:34:
+ a0:8c:cb:c9:79:6e:3e:5c:6a:52:9e:c4:ed:2b:c5:
+ 69:fe:50:3c:93:9d:b5:ff:2d:28:a8:6c:06:6c:9d:
+ c5:af:b2:59:fb:59:77:0d:74:7a:88:84:a4:d4:1d:
+ d4:ba:20:06:cc:b5:1e:48:4e:74:21:15:86:75:c0:
+ cc:5a:d1:05:cf:57:16:7a:13:17:ec:c2:4a:ae:d5:
+ 1e:72:aa:22:5a:8c:9c:82:32:c4:10:e6:42:6e:21:
+ 86:68:7c:80:23:30:35:d3:bd:b0:5e:0a:29:2b:f0:
+ 14:b1:18:37:d9:59:25:c3:e7:38:d9:e9:d4:2d:36:
+ 35:65
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Key Usage: critical
+ Digital Signature, Certificate Sign, CRL Sign
+ X509v3 Extended Key Usage:
+ TLS Web Server Authentication, TLS Web Client Authentication
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Subject Key Identifier:
+ FA:91:39:63:9A:FB:AD:10:24:E5:BE:B5:B9:DA:AB:D9:C4:46:69:AB
+ X509v3 Authority Key Identifier:
+ 8F:F0:4B:7F:A8:2E:45:24:AE:4D:50:FA:63:9A:8B:DE:E2:DD:1B:BC
+ Authority Information Access:
+ OCSP - URI:http://ocsp2.globalsign.com/rootr3
+ CA Issuers - URI:http://secure.globalsign.com/cacert/root-r3.crt
+ X509v3 CRL Distribution Points:
+ Full Name:
+ URI:http://crl.globalsign.com/root-r3.crl
+ X509v3 Certificate Policies:
+ Policy: 2.23.140.1.2.1
+ Policy: 1.3.6.1.4.1.4146.10.1.3
+ Signature Algorithm: sha256WithRSAEncryption
+ Signature Value:
+ 14:33:2c:79:e5:3f:82:c6:70:3f:da:59:38:a7:bb:a2:76:ac:
+ 61:18:05:68:57:d9:0d:fb:8a:46:bc:f1:a8:e8:0c:70:02:1d:
+ c6:2f:97:ed:36:3e:9e:52:86:2f:5c:62:d8:d5:47:43:9a:73:
+ d1:2b:25:87:9f:44:b4:14:eb:26:bc:21:47:74:20:bd:9f:a4:
+ bf:b3:80:1d:4d:35:7d:cd:b9:b5:da:55:f2:90:50:c8:b2:17:
+ 4e:0e:b4:61:88:29:5f:44:5d:03:7f:57:91:81:d0:eb:30:ae:
+ d5:2a:ec:82:20:ce:4e:d2:b0:8b:95:02:61:73:d8:69:34:f4:
+ ad:63:0e:5c:e4:20:1f:a9:7d:ed:8e:e5:1c:04:bb:22:9f:c7:
+ a9:22:ca:99:3d:02:a7:67:e8:06:2d:fa:04:6b:bb:49:d2:6c:
+ 99:57:63:6c:2d:c2:61:78:e1:20:b1:fb:f6:bf:e1:82:39:39:
+ 3c:7b:ef:7d:1a:95:4a:b2:72:da:55:90:ae:ed:dd:e2:70:90:
+ 7c:1a:ee:b5:32:5a:5d:cf:d6:fa:45:f2:9e:01:0c:31:2f:89:
+ 84:fe:31:60:0f:fd:ee:a6:5b:84:d5:c7:18:e6:a4:f9:40:30:
+ 29:18:1e:fe:fc:41:b5:b9:29:05:75:8b:62:1a:5b:22:2e:bf:
+ e4:59:6c:b0
+-----BEGIN CERTIFICATE-----
+MIIEjzCCA3egAwIBAgIQfCoMIT/GVVNFyR8ZH7hO+jANBgkqhkiG9w0BAQsFADBM
+MSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEGA1UEChMKR2xv
+YmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjAeFw0yMjA0MjAxMjAwMDBaFw0y
+NTA0MjAwMDAwMDBaMFgxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWdu
+IG52LXNhMS4wLAYDVQQDEyVHbG9iYWxTaWduIEF0bGFzIFIzIERWIFRMUyBDQSAy
+MDIyIFEzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuKh6ZjxOZpzO
+N6VUNU02x5nTqCc28i/G1Rg+6QndBdbXLDQyfAhjSdEQN+V4XRFizm37Lz83lNuP
+ezDpXizZVT+y27mgtWA3i6QGMjVQpAmvCkX/qB+bZY7dSuBAoeNjN1iQ3XU7/A4c
+gkCYvXCxwUgUFDwES2nd1JwBpukh44IK/uSqvzSgjMvJeW4+XGpSnsTtK8Vp/lA8
+k521/y0oqGwGbJ3Fr7JZ+1l3DXR6iISk1B3UuiAGzLUeSE50IRWGdcDMWtEFz1cW
+ehMX7MJKrtUecqoiWoycgjLEEOZCbiGGaHyAIzA1072wXgopK/AUsRg32Vklw+c4
+2enULTY1ZQIDAQABo4IBXzCCAVswDgYDVR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQG
+CCsGAQUFBwMBBggrBgEFBQcDAjASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQW
+BBT6kTljmvutECTlvrW52qvZxEZpqzAfBgNVHSMEGDAWgBSP8Et/qC5FJK5NUPpj
+move4t0bvDB7BggrBgEFBQcBAQRvMG0wLgYIKwYBBQUHMAGGImh0dHA6Ly9vY3Nw
+Mi5nbG9iYWxzaWduLmNvbS9yb290cjMwOwYIKwYBBQUHMAKGL2h0dHA6Ly9zZWN1
+cmUuZ2xvYmFsc2lnbi5jb20vY2FjZXJ0L3Jvb3QtcjMuY3J0MDYGA1UdHwQvMC0w
+K6ApoCeGJWh0dHA6Ly9jcmwuZ2xvYmFsc2lnbi5jb20vcm9vdC1yMy5jcmwwIQYD
+VR0gBBowGDAIBgZngQwBAgEwDAYKKwYBBAGgMgoBAzANBgkqhkiG9w0BAQsFAAOC
+AQEAFDMseeU/gsZwP9pZOKe7onasYRgFaFfZDfuKRrzxqOgMcAIdxi+X7TY+nlKG
+L1xi2NVHQ5pz0Sslh59EtBTrJrwhR3QgvZ+kv7OAHU01fc25tdpV8pBQyLIXTg60
+YYgpX0RdA39XkYHQ6zCu1SrsgiDOTtKwi5UCYXPYaTT0rWMOXOQgH6l97Y7lHAS7
+Ip/HqSLKmT0Cp2foBi36BGu7SdJsmVdjbC3CYXjhILH79r/hgjk5PHvvfRqVSrJy
+2lWQru3d4nCQfBrutTJaXc/W+kXyngEMMS+JhP4xYA/97qZbhNXHGOak+UAwKRge
+/vxBtbkpBXWLYhpbIi6/5FlssA==
+-----END CERTIFICATE-----
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 04:00:00:00:00:01:21:58:53:08:a2
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: OU = GlobalSign Root CA - R3, O = GlobalSign, CN = GlobalSign
+ Validity
+ Not Before: Mar 18 10:00:00 2009 GMT
+ Not After : Mar 18 10:00:00 2029 GMT
+ Subject: OU = GlobalSign Root CA - R3, O = GlobalSign, CN = GlobalSign
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:cc:25:76:90:79:06:78:22:16:f5:c0:83:b6:84:
+ ca:28:9e:fd:05:76:11:c5:ad:88:72:fc:46:02:43:
+ c7:b2:8a:9d:04:5f:24:cb:2e:4b:e1:60:82:46:e1:
+ 52:ab:0c:81:47:70:6c:dd:64:d1:eb:f5:2c:a3:0f:
+ 82:3d:0c:2b:ae:97:d7:b6:14:86:10:79:bb:3b:13:
+ 80:77:8c:08:e1:49:d2:6a:62:2f:1f:5e:fa:96:68:
+ df:89:27:95:38:9f:06:d7:3e:c9:cb:26:59:0d:73:
+ de:b0:c8:e9:26:0e:83:15:c6:ef:5b:8b:d2:04:60:
+ ca:49:a6:28:f6:69:3b:f6:cb:c8:28:91:e5:9d:8a:
+ 61:57:37:ac:74:14:dc:74:e0:3a:ee:72:2f:2e:9c:
+ fb:d0:bb:bf:f5:3d:00:e1:06:33:e8:82:2b:ae:53:
+ a6:3a:16:73:8c:dd:41:0e:20:3a:c0:b4:a7:a1:e9:
+ b2:4f:90:2e:32:60:e9:57:cb:b9:04:92:68:68:e5:
+ 38:26:60:75:b2:9f:77:ff:91:14:ef:ae:20:49:fc:
+ ad:40:15:48:d1:02:31:61:19:5e:b8:97:ef:ad:77:
+ b7:64:9a:7a:bf:5f:c1:13:ef:9b:62:fb:0d:6c:e0:
+ 54:69:16:a9:03:da:6e:e9:83:93:71:76:c6:69:85:
+ 82:17
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ X509v3 Subject Key Identifier:
+ 8F:F0:4B:7F:A8:2E:45:24:AE:4D:50:FA:63:9A:8B:DE:E2:DD:1B:BC
+ Signature Algorithm: sha256WithRSAEncryption
+ Signature Value:
+ 4b:40:db:c0:50:aa:fe:c8:0c:ef:f7:96:54:45:49:bb:96:00:
+ 09:41:ac:b3:13:86:86:28:07:33:ca:6b:e6:74:b9:ba:00:2d:
+ ae:a4:0a:d3:f5:f1:f1:0f:8a:bf:73:67:4a:83:c7:44:7b:78:
+ e0:af:6e:6c:6f:03:29:8e:33:39:45:c3:8e:e4:b9:57:6c:aa:
+ fc:12:96:ec:53:c6:2d:e4:24:6c:b9:94:63:fb:dc:53:68:67:
+ 56:3e:83:b8:cf:35:21:c3:c9:68:fe:ce:da:c2:53:aa:cc:90:
+ 8a:e9:f0:5d:46:8c:95:dd:7a:58:28:1a:2f:1d:de:cd:00:37:
+ 41:8f:ed:44:6d:d7:53:28:97:7e:f3:67:04:1e:15:d7:8a:96:
+ b4:d3:de:4c:27:a4:4c:1b:73:73:76:f4:17:99:c2:1f:7a:0e:
+ e3:2d:08:ad:0a:1c:2c:ff:3c:ab:55:0e:0f:91:7e:36:eb:c3:
+ 57:49:be:e1:2e:2d:7c:60:8b:c3:41:51:13:23:9d:ce:f7:32:
+ 6b:94:01:a8:99:e7:2c:33:1f:3a:3b:25:d2:86:40:ce:3b:2c:
+ 86:78:c9:61:2f:14:ba:ee:db:55:6f:df:84:ee:05:09:4d:bd:
+ 28:d8:72:ce:d3:62:50:65:1e:eb:92:97:83:31:d9:b3:b5:ca:
+ 47:58:3f:5f
+-----BEGIN CERTIFICATE-----
+MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4G
+A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNp
+Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4
+MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEG
+A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI
+hvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWtiHL8
+RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsT
+gHeMCOFJ0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmm
+KPZpO/bLyCiR5Z2KYVc3rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zd
+QQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjlOCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZ
+XriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2xmmFghcCAwEAAaNCMEAw
+DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI/wS3+o
+LkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZU
+RUm7lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMp
+jjM5RcOO5LlXbKr8EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK
+6fBdRoyV3XpYKBovHd7NADdBj+1EbddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQX
+mcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18YIvDQVETI53O9zJrlAGomecs
+Mx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7rkpeDMdmztcpH
+WD9f
+-----END CERTIFICATE-----
diff --git a/certs/Go Daddy Secure Certificate Authority - G2.pem b/certs/Go-Daddy-Secure-Certificate-Authority-G2.pem
index 4faba90..4faba90 100644
--- a/certs/Go Daddy Secure Certificate Authority - G2.pem
+++ b/certs/Go-Daddy-Secure-Certificate-Authority-G2.pem
diff --git a/certs/Starfield Secure Certificate Authority - G2.pem b/certs/Starfield-Secure-Certificate-Authority-G2.pem
index 7772e6b..7772e6b 100644
--- a/certs/Starfield Secure Certificate Authority - G2.pem
+++ b/certs/Starfield-Secure-Certificate-Authority-G2.pem
diff --git a/check-certificates b/check-certificates
deleted file mode 100644
index 99da7d5..0000000
--- a/check-certificates
+++ /dev/null
@@ -1,127 +0,0 @@
-#!rsc by RouterOS
-# RouterOS script: check-certificates
-# Copyright (c) 2013-2022 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 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;
- :foreach PassPhrase in=$CertRenewPass do={
- /certificate/import file-name=$CertFileName passphrase=$PassPhrase as-value;
- }
- /file/remove [ find where name=$CertFileName ];
-
- :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<2w !(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;
- }
-}
diff --git a/check-certificates.rsc b/check-certificates.rsc
new file mode 100644
index 0000000..20e2902
--- /dev/null
+++ b/check-certificates.rsc
@@ -0,0 +1,222 @@
+#!rsc by RouterOS
+# RouterOS script: check-certificates
+# Copyright (c) 2013-2024 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# requires RouterOS, version=7.12
+#
+# check for certificate validity
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/check-certificates.md
+
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:do {
+ :local ScriptName [ :jobname ];
+
+ :global CertRenewTime;
+ :global CertRenewUrl;
+ :global CertWarnTime;
+ :global Identity;
+
+ :global CertificateAvailable
+ :global EscapeForRegEx;
+ :global IfThenElse;
+ :global LogPrint;
+ :global ParseKeyValueStore;
+ :global ScriptLock;
+ :global SendNotification2;
+ :global SymbolForNotification;
+ :global UrlEncode;
+ :global WaitFullyConnected;
+
+ :local CheckCertificatesDownloadImport do={
+ :local ScriptName [ :tostr $1 ];
+ :local Name [ :tostr $2 ];
+
+ :global CertRenewUrl;
+ :global CertRenewPass;
+
+ :global CertificateNameByCN;
+ :global EscapeForRegEx;
+ :global FetchUserAgentStr;
+ :global LogPrint;
+ :global UrlEncode;
+ :global WaitForFile;
+
+ :local Return false;
+
+ :foreach Type in={ ".pem"; ".p12" } do={
+ :local CertFileName ([ $UrlEncode $Name ] . $Type);
+ :do {
+ /tool/fetch check-certificate=yes-without-crl http-header-field=({ [ $FetchUserAgentStr $ScriptName ] }) \
+ ($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={
+ $LogPrint warning $ScriptName ("Decryption failed for certificate file '" . $CertFileName . "'.");
+ }
+
+ :foreach CertInChain in=[ /certificate/find where name~("^" . [ $EscapeForRegEx $CertFileName ] . "_[0-9]+\$") \
+ common-name!=$Name !(subject-alt-name~("(^|\\W)(DNS|IP):" . [ $EscapeForRegEx $Name ] . "(\\W|\$)")) !(common-name=[]) ] do={
+ $CertificateNameByCN [ /certificate/get $CertInChain common-name ];
+ }
+
+ :set Return true;
+ } on-error={
+ $LogPrint debug $ScriptName ("Could not download certificate file '" . $CertFileName . "'.");
+ }
+ }
+
+ :return $Return;
+ }
+
+ :local FormatInfo do={
+ :local Cert $1;
+
+ :global FormatLine;
+ :global FormatMultiLines;
+ :global IfThenElse;
+
+ :local FormatExpire do={
+ :global CharacterReplace;
+ :return [ $CharacterReplace [ $CharacterReplace [ :tostr $1 ] "w" "w " ] "d" "d " ];
+ }
+
+ :local FormatCertChain do={
+ :local Cert $1;
+
+ :global EitherOr;
+ :global ParseKeyValueStore;
+
+ :local CertVal [ /certificate/get $Cert ];
+
+ :if ([ :typeof ($CertVal->"issuer") ] = "nothing") do={
+ :return "self-signed";
+ }
+
+ :local Return "";
+ :for I from=0 to=5 do={
+ :set Return ($Return . [ $EitherOr ([ $ParseKeyValueStore ($CertVal->"issuer") ]->"CN") \
+ ([ $ParseKeyValueStore (($CertVal->"issuer")->0) ]->"CN") ]);
+ :set CertVal [ /certificate/get [ find where skid=($CertVal->"akid") ] ];
+ :if (($CertVal->"akid") = "" || ($CertVal->"akid") = ($CertVal->"skid")) do={
+ :return $Return;
+ }
+ :set Return ($Return . " -> ");
+ }
+ :return ($Return . "...");
+ }
+
+ :local CertVal [ /certificate/get $Cert ];
+
+ :return ( \
+ [ $FormatLine "Name" ($CertVal->"name") ] . "\n" . \
+ [ $IfThenElse ([ :len ($CertVal->"common-name") ] > 0) ([ $FormatLine "CommonName" ($CertVal->"common-name") ] . "\n") ] . \
+ [ $IfThenElse ([ :len ($CertVal->"subject-alt-name") ] > 0) ([ $FormatMultiLines "SubjectAltNames" ($CertVal->"subject-alt-name") ] . "\n") ] . \
+ [ $FormatLine "Private key" [ $IfThenElse (($CertVal->"private-key") = true) "available" "missing" ] ] . "\n" . \
+ [ $FormatLine "Fingerprint" ($CertVal->"fingerprint") ] . "\n" . \
+ [ $IfThenElse ([ :len ($CertVal->"ca") ] > 0) [ $FormatLine "Issuer" ($CertVal->"ca") ] [ $FormatLine "Issuer chain" [ $FormatCertChain $Cert ] ] ] . "\n" . \
+ "Validity:\n" . \
+ [ $FormatLine " from" ($CertVal->"invalid-before") ] . "\n" . \
+ [ $FormatLine " to" ($CertVal->"invalid-after") ] . "\n" . \
+ [ $FormatLine "Expires in" [ $IfThenElse (($CertVal->"expired") = true) "expired" [ $FormatExpire ($CertVal->"expires-after") ] ] ]);
+ }
+
+ :if ([ $ScriptLock $ScriptName ] = false) do={
+ :error false;
+ }
+ $WaitFullyConnected;
+
+ :foreach Cert in=[ /certificate/find where !revoked !ca !scep-url expires-after<$CertRenewTime ] do={
+ :local CertVal [ /certificate/get $Cert ];
+ :local CertNew;
+ :local LastName;
+
+ :do {
+ :if ([ :len $CertRenewUrl ] = 0) do={
+ $LogPrint info $ScriptName ("No CertRenewUrl given.");
+ :error false;
+ }
+ $LogPrint info $ScriptName ("Attempting to renew certificate '" . ($CertVal->"name") . "'.");
+
+ :local ImportSuccess false;
+ :set LastName ($CertVal->"common-name");
+ :set ImportSuccess [ $CheckCertificatesDownloadImport $ScriptName $LastName ];
+ :foreach SAN in=($CertVal->"subject-alt-name") do={
+ :if ($ImportSuccess = false) do={
+ :set LastName [ :pick $SAN ([ :find $SAN ":" ] + 1) [ :len $SAN ] ];
+ :set ImportSuccess [ $CheckCertificatesDownloadImport $ScriptName $LastName ];
+ }
+ }
+ :if ($ImportSuccess = false) do={ :error false; }
+
+ :if ([ :len ($CertVal->"fingerprint") ] > 0 && $CertVal->"fingerprint" != [ /certificate/get $Cert fingerprint ]) do={
+ $LogPrint debug $ScriptName ("Certificate '" . $CertVal->"name" . "' was updated in place.");
+ :set CertVal [ /certificate/get $Cert ];
+ } else={
+ $LogPrint debug $ScriptName ("Certificate '" . $CertVal->"name" . "' was not updated, but replaced.");
+
+ :set CertNew [ /certificate/find where name~("^" . [ $EscapeForRegEx [ $UrlEncode $LastName ] ] . "\\.(p12|pem)_[0-9]+\$") \
+ (common-name=($CertVal->"common-name") or subject-alt-name~("(^|\\W)(DNS|IP):" . [ $EscapeForRegEx $LastName ] . "(\\W|\$)")) \
+ fingerprint!=[ :tostr ($CertVal->"fingerprint") ] expires-after>$CertRenewTime ];
+ :local CertNewVal [ /certificate/get $CertNew ];
+
+ :if ([ $CertificateAvailable ([ $ParseKeyValueStore ($CertNewVal->"issuer") ]->"CN") ] = false) do={
+ $LogPrint warning $ScriptName ("The certificate chain is not available!");
+ }
+
+ :if (($CertVal->"private-key") = true && ($CertVal->"private-key") != ($CertNewVal->"private-key")) do={
+ /certificate/remove $CertNew;
+ $LogPrint warning $ScriptName ("Old certificate '" . ($CertVal->"name") . "' has a private key, new certificate does not. Aborting renew.");
+ :error false;
+ }
+
+ /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");
+ :set CertNewVal;
+ :set CertVal [ /certificate/get $CertNew ];
+ }
+
+ $SendNotification2 ({ origin=$ScriptName; silent=true; \
+ subject=([ $SymbolForNotification "lock-with-ink-pen" ] . "Certificate renewed: " . ($CertVal->"name")); \
+ message=("A certificate on " . $Identity . " has been renewed.\n\n" . [ $FormatInfo $CertNew ]) });
+ $LogPrint info $ScriptName ("The certificate '" . ($CertVal->"name") . "' has been renewed.");
+ } on-error={
+ $LogPrint debug $ScriptName ("Could not renew certificate '" . ($CertVal->"name") . "'.");
+ }
+ }
+
+ :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={
+ $LogPrint debug $ScriptName ("Certificate '" . ($CertVal->"name") . "' is handled by SCEP, skipping.");
+ } else={
+ :local State [ $IfThenElse (($CertVal->"expired") = true) "expired" "is about to expire" ];
+
+ $SendNotification2 ({ origin=$ScriptName; \
+ subject=([ $SymbolForNotification "warning-sign" ] . "Certificate warning: " . ($CertVal->"name")); \
+ message=("A certificate on " . $Identity . " " . $State . ".\n\n" . [ $FormatInfo $Cert ]) });
+ $LogPrint info $ScriptName ("The certificate '" . ($CertVal->"name") . "' " . $State . \
+ ", it is invalid after " . ($CertVal->"invalid-after") . ".");
+ }
+ }
+} on-error={ }
diff --git a/check-health b/check-health
deleted file mode 100644
index 5da7d2a..0000000
--- a/check-health
+++ /dev/null
@@ -1,131 +0,0 @@
-#!rsc by RouterOS
-# RouterOS script: check-health
-# Copyright (c) 2019-2022 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 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);
-}
-
-:if ([ :len [ /system/health/find ] ] = 0) do={
- $LogPrintExit2 error $0 ("Your device does not provide any health values.") true;
-}
-
-:if ([ :typeof $CheckHealthLast ] != "array") do={
- :set CheckHealthLast ({});
-}
-:if ([ :typeof $CheckHealthTemperatureNotified ] != "array") do={
- :set CheckHealthTemperatureNotified ({});
-}
-
-$ScriptLock $0;
-
-: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;
-}
diff --git a/check-health.rsc b/check-health.rsc
new file mode 100644
index 0000000..2a97ad6
--- /dev/null
+++ b/check-health.rsc
@@ -0,0 +1,178 @@
+#!rsc by RouterOS
+# RouterOS script: check-health
+# Copyright (c) 2019-2024 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# requires RouterOS, version=7.12
+#
+# check for RouterOS health state
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/check-health.md
+
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:do {
+ :local ScriptName [ :jobname ];
+
+ :global CheckHealthCPUUtilization;
+ :global CheckHealthCPUUtilizationNotified;
+ :global CheckHealthLast;
+ :global CheckHealthRAMUtilizationNotified;
+ :global CheckHealthTemperature;
+ :global CheckHealthTemperatureDeviation;
+ :global CheckHealthTemperatureNotified;
+ :global CheckHealthVoltageLow;
+ :global CheckHealthVoltagePercent;
+ :global Identity;
+
+ :global FormatLine;
+ :global HumanReadableNum;
+ :global IfThenElse;
+ :global LogPrint;
+ :global ScriptLock;
+ :global SendNotification2;
+ :global SymbolForNotification;
+
+ :local TempToNum do={
+ :global CharacterReplace;
+ :local T [ :toarray [ $CharacterReplace $1 "." "," ] ];
+ :return ($T->0 * 10 + $T->1);
+ }
+
+ :if ([ $ScriptLock $ScriptName ] = false) do={
+ :error false;
+ }
+
+ :local Resource [ /system/resource/get ];
+
+ :set CheckHealthCPUUtilization (($CheckHealthCPUUtilization * 4 + ($Resource->"cpu-load") * 10) / 5);
+ :if ($CheckHealthCPUUtilization > 750 && $CheckHealthCPUUtilizationNotified != true) do={
+ $SendNotification2 ({ origin=$ScriptName; \
+ 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=$ScriptName; \
+ subject=([ $SymbolForNotification "abacus,chart-decreasing" ] . "Health recovery: CPU utilization"); \
+ message=("The average CPU utilization on " . $Identity . " decreased to " . ($CheckHealthCPUUtilization / 10) . "%.") });
+ :set CheckHealthCPUUtilizationNotified false;
+ }
+
+ :local CheckHealthRAMUtilization (($Resource->"total-memory" - $Resource->"free-memory") * 100 / $Resource->"total-memory");
+ :if ($CheckHealthRAMUtilization >=80 && $CheckHealthRAMUtilizationNotified != true) do={
+ $SendNotification2 ({ origin=$ScriptName; \
+ subject=([ $SymbolForNotification "card-file-box,chart-increasing" ] . "Health warning: RAM utilization"); \
+ message=("The RAM utilization on " . $Identity . " is at " . $CheckHealthRAMUtilization . "%!\n\n" . \
+ [ $FormatLine "total" ([ $HumanReadableNum ($Resource->"total-memory") 1024 ] . "iB") 8 ] . "\n" . \
+ [ $FormatLine "used" ([ $HumanReadableNum ($Resource->"total-memory" - $Resource->"free-memory") 1024 ] . "iB") 8 ] . "\n" . \
+ [ $FormatLine "free" ([ $HumanReadableNum ($Resource->"free-memory") 1024 ] . "iB") 8 ]) });
+ :set CheckHealthRAMUtilizationNotified true;
+ }
+ :if ($CheckHealthRAMUtilization < 70 && $CheckHealthRAMUtilizationNotified = true) do={
+ $SendNotification2 ({ origin=$ScriptName; \
+ subject=([ $SymbolForNotification "card-file-box,chart-decreasing" ] . "Health recovery: RAM utilization"); \
+ message=("The RAM utilization on " . $Identity . " decreased to " . $CheckHealthRAMUtilization . "%.") });
+ :set CheckHealthRAMUtilizationNotified false;
+ }
+
+ :if ([ :len [ /system/health/find ] ] = 0) do={
+ $LogPrint debug $ScriptName ("Your device does not provide any health values.");
+ :error 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=$ScriptName; \
+ 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" . \
+ [ $FormatLine "old value" ($CheckHealthLast->$Name . " V") 12 ] . "\n" . \
+ [ $FormatLine "new value" ($Value . " V") 12 ]) });
+ } else={
+ :if ($NumCurr <= $CheckHealthVoltageLow && $NumLast > $CheckHealthVoltageLow) do={
+ $SendNotification2 ({ origin=$ScriptName; \
+ 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=$ScriptName; \
+ 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=$ScriptName; \
+ 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=$ScriptName; \
+ 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={
+ $LogPrint info $ScriptName ("No threshold given for " . $Name . ", assuming 50C.");
+ :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=$ScriptName; \
+ 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=$ScriptName; \
+ 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;
+ }
+} on-error={ }
diff --git a/check-lte-firmware-upgrade b/check-lte-firmware-upgrade
deleted file mode 100644
index 62943ee..0000000
--- a/check-lte-firmware-upgrade
+++ /dev/null
@@ -1,82 +0,0 @@
-#!rsc by RouterOS
-# RouterOS script: check-lte-firmware-upgrade
-# Copyright (c) 2018-2022 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;
-}
diff --git a/check-lte-firmware-upgrade.rsc b/check-lte-firmware-upgrade.rsc
new file mode 100644
index 0000000..e7f06f3
--- /dev/null
+++ b/check-lte-firmware-upgrade.rsc
@@ -0,0 +1,103 @@
+#!rsc by RouterOS
+# RouterOS script: check-lte-firmware-upgrade
+# Copyright (c) 2018-2024 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# requires RouterOS, version=7.12
+#
+# check for LTE firmware upgrade, send notification
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/check-lte-firmware-upgrade.md
+
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:do {
+ :local ScriptName [ :jobname ];
+
+ :global SentLteFirmwareUpgradeNotification;
+
+ :global ScriptLock;
+
+ :if ([ $ScriptLock $ScriptName ] = false) do={
+ :error false;
+ }
+
+ :if ([ :typeof $SentLteFirmwareUpgradeNotification ] != "array") do={
+ :global SentLteFirmwareUpgradeNotification ({});
+ }
+
+ :local CheckInterface do={
+ :local ScriptName $1;
+ :local Interface $2;
+
+ :global Identity;
+ :global SentLteFirmwareUpgradeNotification;
+
+ :global FormatLine;
+ :global IfThenElse;
+ :global LogPrint;
+ :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={
+ $LogPrint debug $ScriptName ("Could not get latest LTE firmware version for interface " . \
+ $IntName . ".");
+ :return false;
+ }
+
+ :if ([ :len ($Firmware->"latest") ] = 0) do={
+ $LogPrint info $ScriptName ("An empty string is not a valid version.");
+ :return false;
+ }
+
+ :if (($Firmware->"installed") = ($Firmware->"latest")) do={
+ :if ([ $ScriptFromTerminal $ScriptName ] = true) do={
+ $LogPrint info $ScriptName ("No firmware upgrade available for LTE interface " . $IntName . ".");
+ }
+ :return true;
+ }
+
+ :if ([ $ScriptFromTerminal $ScriptName ] = 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;
+ $LogPrint info $ScriptName ("Scheduled lte firmware upgrade for interface " . $IntName . "...");
+ :return true;
+ } else={
+ :put "Canceled...";
+ }
+ }
+
+ :if (($SentLteFirmwareUpgradeNotification->$IntName) = ($Firmware->"latest")) do={
+ $LogPrint debug $ScriptName ("Already sent the LTE firmware upgrade notification for version " . \
+ ($Firmware->"latest") . ".");
+ :return false;
+ }
+
+ $LogPrint info $ScriptName ("A new firmware version " . ($Firmware->"latest") . " is available for " . \
+ "LTE interface " . $IntName . ".");
+ $SendNotification2 ({ origin=$ScriptName; \
+ subject=([ $SymbolForNotification "sparkles" ] . "LTE firmware upgrade"); \
+ message=("A new firmware version " . ($Firmware->"latest") . " is available for " . \
+ "LTE interface " . $IntName . " on " . $Identity . ".\n\n" . \
+ [ $IfThenElse ([ :len ($Info->"manufacturer") ] > 0) ([ $FormatLine "Manufacturer" ($Info->"manufacturer") ] . "\n") ] . \
+ [ $IfThenElse ([ :len ($Info->"model") ] > 0) ([ $FormatLine "Model" ($Info->"model") ] . "\n") ] . \
+ [ $IfThenElse ([ :len ($Info->"revision") ] > 0) ([ $FormatLine "Revision" ($Info->"revision") ] . "\n") ] . \
+ "Firmware version:\n" . \
+ [ $FormatLine " Installed" ($Firmware->"installed") ] . "\n" . \
+ [ $FormatLine " Available" ($Firmware->"latest") ]); silent=true });
+ :set ($SentLteFirmwareUpgradeNotification->$IntName) ($Firmware->"latest");
+ }
+
+ :foreach Interface in=[ /interface/lte/find ] do={
+ $CheckInterface $ScriptName $Interface;
+ }
+} on-error={ }
diff --git a/check-routeros-update b/check-routeros-update
deleted file mode 100644
index 9e1de8e..0000000
--- a/check-routeros-update
+++ /dev/null
@@ -1,143 +0,0 @@
-#!rsc by RouterOS
-# RouterOS script: check-routeros-update
-# Copyright (c) 2013-2022 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 SafeUpdateNeighbor;
-:global SafeUpdateOnCap;
-: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 ([ /interface/wireless/cap/get enabled ] = true && \
- [ /caps-man/manager/get enabled ] = false && \
- $SafeUpdateOnCap != true) do={
- $LogPrintExit2 error $0 ("System is managed by CAPsMAN, not checking for RouterOS version.") true;
-}
-
-: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 ($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");
-}
diff --git a/check-routeros-update.rsc b/check-routeros-update.rsc
new file mode 100644
index 0000000..c9ab93b
--- /dev/null
+++ b/check-routeros-update.rsc
@@ -0,0 +1,166 @@
+#!rsc by RouterOS
+# RouterOS script: check-routeros-update
+# Copyright (c) 2013-2024 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# requires RouterOS, version=7.12
+#
+# check for RouterOS update, send notification and/or install
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/check-routeros-update.md
+
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:do {
+ :local ScriptName [ :jobname ];
+
+ :global Identity;
+ :global SafeUpdateAll;
+ :global SafeUpdateNeighbor;
+ :global SafeUpdateNeighborIdentity;
+ :global SafeUpdatePatch;
+ :global SafeUpdateUrl;
+ :global SentRouterosUpdateNotification;
+
+ :global DeviceInfo;
+ :global EscapeForRegEx;
+ :global FetchUserAgentStr;
+ :global LogPrint;
+ :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.";
+ }
+
+ :if ([ $ScriptLock $ScriptName ] = false) do={
+ :error false;
+ }
+ $WaitFullyConnected;
+
+ :if ([ :len [ /system/scheduler/find where name="_RebootForUpdate" ] ] > 0) do={
+ :error "A reboot for update is already scheduled.";
+ }
+
+ $LogPrint debug $ScriptName ("Checking for updates...");
+ /system/package/update/check-for-updates without-paging as-value;
+ :local Update [ /system/package/update/get ];
+
+ :if ([ $ScriptFromTerminal $ScriptName ] = true && ($Update->"installed-version") = ($Update->"latest-version")) do={
+ $LogPrint info $ScriptName ("System is already up to date.");
+ :error 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={
+ $LogPrint info $ScriptName ("The version '" . ($Update->"latest-version") . "' is not a valid version.");
+ :error false;
+ }
+
+ :if ($NumInstalled < $NumLatest) do={
+ :if ($SafeUpdateAll ~ "^YES,? ?PLEASE!?\$") do={
+ $LogPrint info $ScriptName ("Installing ALL versions automatically, including " . \
+ $Update->"latest-version" . "...");
+ $SendNotification2 ({ origin=$ScriptName; \
+ subject=([ $SymbolForNotification "sparkles" ] . "RouterOS update: " . $Update->"latest-version"); \
+ 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={
+ $LogPrint info $ScriptName ("Version " . $Update->"latest-version" . " is a patch release, updating...");
+ $SendNotification2 ({ origin=$ScriptName; \
+ subject=([ $SymbolForNotification "sparkles" ] . "RouterOS update: " . $Update->"latest-version"); \
+ message=("Version " . $Update->"latest-version" . " is a patch update for " . $Update->"channel" . \
+ ", updating on " . $Identity . "..."); link=$Link; silent=true });
+ $DoUpdate;
+ }
+
+ :if ($SafeUpdateNeighbor = true) do={
+ :local Neighbors [ /ip/neighbor/find where platform="MikroTik" identity~$SafeUpdateNeighborIdentity \
+ version~("^" . [ $EscapeForRegEx ($Update->"latest-version") ] . "\\b") ];
+ :if ([ :len $Neighbors ] > 0) do={
+ :local Neighbor [ /ip/neighbor/get ($Neighbors->0) identity ];
+ $LogPrint info $ScriptName ("Seen a neighbor (" . $Neighbor . ") running version " . \
+ $Update->"latest-version" . " from " . $Update->"channel" . ", updating...");
+ $SendNotification2 ({ origin=$ScriptName; \
+ subject=([ $SymbolForNotification "sparkles" ] . "RouterOS update: " . $Update->"latest-version"); \
+ message=("Seen a neighbor (" . $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") http-header-field=({ [ $FetchUserAgentStr $ScriptName ] }) \
+ output=user as-value ];
+ } on-error={
+ $LogPrint warning $ScriptName ("Failed receiving safe version for " . $Update->"channel" . ".");
+ }
+ :if ($Result->"status" = "finished" && $Result->"data" = $Update->"latest-version") do={
+ $LogPrint info $ScriptName ("Version " . $Update->"latest-version" . " is considered safe, updating...");
+ $SendNotification2 ({ origin=$ScriptName; \
+ subject=([ $SymbolForNotification "sparkles" ] . "RouterOS update: " . $Update->"latest-version"); \
+ message=("Version " . $Update->"latest-version" . " is considered safe for " . $Update->"channel" . \
+ ", updating on " . $Identity . "..."); link=$Link; silent=true });
+ $DoUpdate;
+ }
+ }
+
+ :if ([ $ScriptFromTerminal $ScriptName ] = 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={
+ $LogPrint info $ScriptName ("Already sent the RouterOS update notification for version " . \
+ $Update->"latest-version" . ".");
+ :error true;
+ }
+
+ $SendNotification2 ({ origin=$ScriptName; \
+ subject=([ $SymbolForNotification "sparkles" ] . "RouterOS update: " . $Update->"latest-version"); \
+ 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={
+ $LogPrint info $ScriptName ("Already sent the RouterOS downgrade notification for version " . \
+ $Update->"latest-version" . ".");
+ :error true;
+ }
+
+ $SendNotification2 ({ origin=$ScriptName; \
+ subject=([ $SymbolForNotification "warning-sign" ] . "RouterOS version: " . $Update->"latest-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 });
+ $LogPrint info $ScriptName ("A different RouterOS version " . ($Update->"latest-version") . \
+ " is available for downgrade.");
+ :set SentRouterosUpdateNotification ($Update->"latest-version");
+ }
+} on-error={ }
diff --git a/collect-wireless-mac.capsman b/collect-wireless-mac.capsman
deleted file mode 100644
index 9e502e3..0000000
--- a/collect-wireless-mac.capsman
+++ /dev/null
@@ -1,85 +0,0 @@
-#!rsc by RouterOS
-# RouterOS script: collect-wireless-mac.capsman
-# Copyright (c) 2013-2022 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;
- }
-}
diff --git a/collect-wireless-mac.capsman.rsc b/collect-wireless-mac.capsman.rsc
new file mode 100644
index 0000000..dcb303c
--- /dev/null
+++ b/collect-wireless-mac.capsman.rsc
@@ -0,0 +1,96 @@
+#!rsc by RouterOS
+# RouterOS script: collect-wireless-mac.capsman
+# Copyright (c) 2013-2024 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# provides: lease-script, order=40
+# requires RouterOS, version=7.12
+#
+# collect wireless mac adresses in access list
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/collect-wireless-mac.md
+#
+# !! Do not edit this file, it is generated from template!
+
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:do {
+ :local ScriptName [ :jobname ];
+
+ :global Identity;
+
+ :global EitherOr;
+ :global FormatLine;
+ :global FormatMultiLines;
+ :global GetMacVendor;
+ :global LogPrint;
+ :global ScriptLock;
+ :global SendNotification2;
+ :global SymbolForNotification;
+
+ :if ([ $ScriptLock $ScriptName 10 ] = false) do={
+ :error false;
+ }
+
+ :if ([ :len [ /caps-man/access-list/find where comment="--- collected above ---" disabled ] ] = 0) do={
+ /caps-man/access-list/add comment="--- collected above ---" disabled=yes;
+ $LogPrint warning $ScriptName ("Added disabled access-list entry with comment '--- collected above ---'.");
+ }
+ :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={
+ $LogPrint debug $ScriptName ("Device already gone... Ignoring.");
+ }
+
+ :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={
+ $LogPrint debug $ScriptName ("MAC address " . $RegVal->"mac-address" . " already known: " . \
+ [ /caps-man/access-list/get $AccessList comment ]);
+ }
+
+ :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 active-mac-address=($RegVal->"mac-address") dynamic=yes status=bound ]->0);
+ :if ([ :len $Lease ] > 0) do={
+ :set Address [ /ip/dhcp-server/lease/get $Lease active-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 ] });
+ :foreach CName in=[ /ip/dns/static/find where type=CNAME cname=($DnsName->0) ] do={
+ :set DnsName ($DnsName, [ /ip/dns/static/get $CName 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");
+ $LogPrint info $ScriptName $Message;
+ /caps-man/access-list/add place-before=$PlaceBefore comment=$Message mac-address=($RegVal->"mac-address") disabled=yes;
+ $SendNotification2 ({ origin=$ScriptName; \
+ 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" . \
+ [ $FormatLine "Controller" $Identity ] . "\n" . \
+ [ $FormatLine "Interface" ($RegVal->"interface") ] . "\n" . \
+ [ $FormatLine "SSID" ($RegVal->"ssid") ] . "\n" . \
+ [ $FormatLine "MAC" ($RegVal->"mac-address") ] . "\n" . \
+ [ $FormatLine "Vendor" $Vendor ] . "\n" . \
+ [ $FormatLine "Hostname" $HostName ] . "\n" . \
+ [ $FormatLine "Address" $Address ] . "\n" . \
+ [ $FormatMultiLines "DNS name" $DnsName ] . "\n" . \
+ [ $FormatLine "Date" $DateTime ]) });
+ }
+ } else={
+ $LogPrint debug $ScriptName ("No mac address available... Ignoring.");
+ }
+ }
+} on-error={ }
diff --git a/collect-wireless-mac.local b/collect-wireless-mac.local
deleted file mode 100644
index a6dbd24..0000000
--- a/collect-wireless-mac.local
+++ /dev/null
@@ -1,86 +0,0 @@
-#!rsc by RouterOS
-# RouterOS script: collect-wireless-mac.local
-# Copyright (c) 2013-2022 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;
- }
-}
diff --git a/collect-wireless-mac.local.rsc b/collect-wireless-mac.local.rsc
new file mode 100644
index 0000000..7c1122c
--- /dev/null
+++ b/collect-wireless-mac.local.rsc
@@ -0,0 +1,97 @@
+#!rsc by RouterOS
+# RouterOS script: collect-wireless-mac.local
+# Copyright (c) 2013-2024 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# provides: lease-script, order=40
+# requires RouterOS, version=7.12
+#
+# collect wireless mac adresses in access list
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/collect-wireless-mac.md
+#
+# !! Do not edit this file, it is generated from template!
+
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:do {
+ :local ScriptName [ :jobname ];
+
+ :global Identity;
+
+ :global EitherOr;
+ :global FormatLine;
+ :global FormatMultiLines;
+ :global GetMacVendor;
+ :global LogPrint;
+ :global ScriptLock;
+ :global SendNotification2;
+ :global SymbolForNotification;
+
+ :if ([ $ScriptLock $ScriptName 10 ] = false) do={
+ :error false;
+ }
+
+ :if ([ :len [ /interface/wireless/access-list/find where comment="--- collected above ---" disabled ] ] = 0) do={
+ /interface/wireless/access-list/add comment="--- collected above ---" disabled=yes;
+ $LogPrint warning $ScriptName ("Added disabled access-list entry with comment '--- collected above ---'.");
+ }
+ :local PlaceBefore ([ /interface/wireless/access-list/find where comment="--- collected above ---" disabled ]->0);
+
+ :foreach Reg in=[ /interface/wireless/registration-table/find where ap=no ] do={
+ :local RegVal;
+ :do {
+ :set RegVal [ /interface/wireless/registration-table/get $Reg ];
+ } on-error={
+ $LogPrint debug $ScriptName ("Device already gone... Ignoring.");
+ }
+
+ :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={
+ $LogPrint debug $ScriptName ("MAC address " . $RegVal->"mac-address" . " already known: " . \
+ [ /interface/wireless/access-list/get $AccessList comment ]);
+ }
+
+ :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 active-mac-address=($RegVal->"mac-address") dynamic=yes status=bound ]->0);
+ :if ([ :len $Lease ] > 0) do={
+ :set Address [ /ip/dhcp-server/lease/get $Lease active-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 ] });
+ :foreach CName in=[ /ip/dns/static/find where type=CNAME cname=($DnsName->0) ] do={
+ :set DnsName ($DnsName, [ /ip/dns/static/get $CName 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");
+ $LogPrint info $ScriptName $Message;
+ /interface/wireless/access-list/add place-before=$PlaceBefore comment=$Message mac-address=($RegVal->"mac-address") disabled=yes;
+ $SendNotification2 ({ origin=$ScriptName; \
+ 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" . \
+ [ $FormatLine "Controller" $Identity ] . "\n" . \
+ [ $FormatLine "Interface" ($RegVal->"interface") ] . "\n" . \
+ [ $FormatLine "SSID" ($RegVal->"ssid") ] . "\n" . \
+ [ $FormatLine "MAC" ($RegVal->"mac-address") ] . "\n" . \
+ [ $FormatLine "Vendor" $Vendor ] . "\n" . \
+ [ $FormatLine "Hostname" $HostName ] . "\n" . \
+ [ $FormatLine "Address" $Address ] . "\n" . \
+ [ $FormatMultiLines "DNS name" $DnsName ] . "\n" . \
+ [ $FormatLine "Date" $DateTime ]) });
+ }
+ } else={
+ $LogPrint debug $ScriptName ("No mac address available... Ignoring.");
+ }
+ }
+} on-error={ }
diff --git a/collect-wireless-mac.template b/collect-wireless-mac.template
deleted file mode 100644
index 42a3d0a..0000000
--- a/collect-wireless-mac.template
+++ /dev/null
@@ -1,87 +0,0 @@
-#!rsc by RouterOS
-# RouterOS script: collect-wireless-mac%TEMPL%
-# Copyright (c) 2013-2022 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
-#
-# !! This is just a template! Replace '%PATH%' with 'caps-man'
-# !! or 'interface wireless'!
-
-:local 0 "collect-wireless-mac%TEMPL%";
-: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 [ /%PATH%/access-list/find where comment="--- collected above ---" disabled ] ] = 0) do={
- /%PATH%/access-list/add comment="--- collected above ---" disabled=yes;
- $LogPrintExit2 warning $0 ("Added disabled access-list entry with comment '--- collected above ---'.") false;
-}
-:local PlaceBefore ([ /%PATH%/access-list/find where comment="--- collected above ---" disabled ]->0);
-
-:foreach Reg in=[ /%PATH%/registration-table/find ] do={
- :local RegVal;
- :do {
- :set RegVal [ /%PATH%/registration-table/get $Reg ];
- } on-error={
- $LogPrintExit2 debug $0 ("Device already gone... Ignoring.") false;
- }
-
- :if ([ :len ($RegVal->"mac-address") ] > 0) do={
- :local AccessList ([ /%PATH%/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: " . \
- [ /%PATH%/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;
- /%PATH%/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;
- }
-}
diff --git a/collect-wireless-mac.template.rsc b/collect-wireless-mac.template.rsc
new file mode 100644
index 0000000..b8c5ff8
--- /dev/null
+++ b/collect-wireless-mac.template.rsc
@@ -0,0 +1,114 @@
+#!rsc by RouterOS
+# RouterOS script: collect-wireless-mac%TEMPL%
+# Copyright (c) 2013-2024 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# provides: lease-script, order=40
+# requires RouterOS, version=7.12
+#
+# collect wireless mac adresses in access list
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/collect-wireless-mac.md
+#
+# !! This is just a template to generate the real script!
+# !! Pattern '%TEMPL%' is replaced, paths are filtered.
+
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:do {
+ :local ScriptName [ :jobname ];
+
+ :global Identity;
+
+ :global EitherOr;
+ :global FormatLine;
+ :global FormatMultiLines;
+ :global GetMacVendor;
+ :global LogPrint;
+ :global ScriptLock;
+ :global SendNotification2;
+ :global SymbolForNotification;
+
+ :if ([ $ScriptLock $ScriptName 10 ] = false) do={
+ :error false;
+ }
+
+ :if ([ :len [ /caps-man/access-list/find where comment="--- collected above ---" disabled ] ] = 0) do={
+ :if ([ :len [ /interface/wifi/access-list/find where comment="--- collected above ---" disabled ] ] = 0) do={
+ :if ([ :len [ /interface/wireless/access-list/find where comment="--- collected above ---" disabled ] ] = 0) do={
+ /caps-man/access-list/add comment="--- collected above ---" disabled=yes;
+ /interface/wifi/access-list/add comment="--- collected above ---" disabled=yes;
+ /interface/wireless/access-list/add comment="--- collected above ---" disabled=yes;
+ $LogPrint warning $ScriptName ("Added disabled access-list entry with comment '--- collected above ---'.");
+ }
+ :local PlaceBefore ([ /caps-man/access-list/find where comment="--- collected above ---" disabled ]->0);
+ :local PlaceBefore ([ /interface/wifi/access-list/find where comment="--- collected above ---" disabled ]->0);
+ :local PlaceBefore ([ /interface/wireless/access-list/find where comment="--- collected above ---" disabled ]->0);
+
+ :foreach Reg in=[ /caps-man/registration-table/find ] do={
+ :foreach Reg in=[ /interface/wifi/registration-table/find ] do={
+ :foreach Reg in=[ /interface/wireless/registration-table/find where ap=no ] do={
+ :local RegVal;
+ :do {
+ :set RegVal [ /caps-man/registration-table/get $Reg ];
+ :set RegVal [ /interface/wifi/registration-table/get $Reg ];
+ :set RegVal [ /interface/wireless/registration-table/get $Reg ];
+ } on-error={
+ $LogPrint debug $ScriptName ("Device already gone... Ignoring.");
+ }
+
+ :if ([ :len ($RegVal->"mac-address") ] > 0) do={
+ :local AccessList ([ /caps-man/access-list/find where mac-address=($RegVal->"mac-address") ]->0);
+ :local AccessList ([ /interface/wifi/access-list/find where mac-address=($RegVal->"mac-address") ]->0);
+ :local AccessList ([ /interface/wireless/access-list/find where mac-address=($RegVal->"mac-address") ]->0);
+ :if ([ :len $AccessList ] > 0) do={
+ $LogPrint debug $ScriptName ("MAC address " . $RegVal->"mac-address" . " already known: " . \
+ [ /caps-man/access-list/get $AccessList comment ]);
+ [ /interface/wifi/access-list/get $AccessList comment ]);
+ [ /interface/wireless/access-list/get $AccessList comment ]);
+ }
+
+ :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 active-mac-address=($RegVal->"mac-address") dynamic=yes status=bound ]->0);
+ :if ([ :len $Lease ] > 0) do={
+ :set Address [ /ip/dhcp-server/lease/get $Lease active-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 ] });
+ :foreach CName in=[ /ip/dns/static/find where type=CNAME cname=($DnsName->0) ] do={
+ :set DnsName ($DnsName, [ /ip/dns/static/get $CName 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");
+ $LogPrint info $ScriptName $Message;
+ /caps-man/access-list/add place-before=$PlaceBefore comment=$Message mac-address=($RegVal->"mac-address") disabled=yes;
+ /interface/wifi/access-list/add place-before=$PlaceBefore comment=$Message mac-address=($RegVal->"mac-address") disabled=yes;
+ /interface/wireless/access-list/add place-before=$PlaceBefore comment=$Message mac-address=($RegVal->"mac-address") disabled=yes;
+ $SendNotification2 ({ origin=$ScriptName; \
+ 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" . \
+ [ $FormatLine "Controller" $Identity ] . "\n" . \
+ [ $FormatLine "Interface" ($RegVal->"interface") ] . "\n" . \
+ [ $FormatLine "SSID" ($RegVal->"ssid") ] . "\n" . \
+ [ $FormatLine "MAC" ($RegVal->"mac-address") ] . "\n" . \
+ [ $FormatLine "Vendor" $Vendor ] . "\n" . \
+ [ $FormatLine "Hostname" $HostName ] . "\n" . \
+ [ $FormatLine "Address" $Address ] . "\n" . \
+ [ $FormatMultiLines "DNS name" $DnsName ] . "\n" . \
+ [ $FormatLine "Date" $DateTime ]) });
+ }
+ } else={
+ $LogPrint debug $ScriptName ("No mac address available... Ignoring.");
+ }
+ }
+} on-error={ }
diff --git a/collect-wireless-mac.wifi.rsc b/collect-wireless-mac.wifi.rsc
new file mode 100644
index 0000000..b8ad939
--- /dev/null
+++ b/collect-wireless-mac.wifi.rsc
@@ -0,0 +1,96 @@
+#!rsc by RouterOS
+# RouterOS script: collect-wireless-mac.wifi
+# Copyright (c) 2013-2024 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# provides: lease-script, order=40
+# requires RouterOS, version=7.12
+#
+# collect wireless mac adresses in access list
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/collect-wireless-mac.md
+#
+# !! Do not edit this file, it is generated from template!
+
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:do {
+ :local ScriptName [ :jobname ];
+
+ :global Identity;
+
+ :global EitherOr;
+ :global FormatLine;
+ :global FormatMultiLines;
+ :global GetMacVendor;
+ :global LogPrint;
+ :global ScriptLock;
+ :global SendNotification2;
+ :global SymbolForNotification;
+
+ :if ([ $ScriptLock $ScriptName 10 ] = false) do={
+ :error false;
+ }
+
+ :if ([ :len [ /interface/wifi/access-list/find where comment="--- collected above ---" disabled ] ] = 0) do={
+ /interface/wifi/access-list/add comment="--- collected above ---" disabled=yes;
+ $LogPrint warning $ScriptName ("Added disabled access-list entry with comment '--- collected above ---'.");
+ }
+ :local PlaceBefore ([ /interface/wifi/access-list/find where comment="--- collected above ---" disabled ]->0);
+
+ :foreach Reg in=[ /interface/wifi/registration-table/find ] do={
+ :local RegVal;
+ :do {
+ :set RegVal [ /interface/wifi/registration-table/get $Reg ];
+ } on-error={
+ $LogPrint debug $ScriptName ("Device already gone... Ignoring.");
+ }
+
+ :if ([ :len ($RegVal->"mac-address") ] > 0) do={
+ :local AccessList ([ /interface/wifi/access-list/find where mac-address=($RegVal->"mac-address") ]->0);
+ :if ([ :len $AccessList ] > 0) do={
+ $LogPrint debug $ScriptName ("MAC address " . $RegVal->"mac-address" . " already known: " . \
+ [ /interface/wifi/access-list/get $AccessList comment ]);
+ }
+
+ :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 active-mac-address=($RegVal->"mac-address") dynamic=yes status=bound ]->0);
+ :if ([ :len $Lease ] > 0) do={
+ :set Address [ /ip/dhcp-server/lease/get $Lease active-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 ] });
+ :foreach CName in=[ /ip/dns/static/find where type=CNAME cname=($DnsName->0) ] do={
+ :set DnsName ($DnsName, [ /ip/dns/static/get $CName 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");
+ $LogPrint info $ScriptName $Message;
+ /interface/wifi/access-list/add place-before=$PlaceBefore comment=$Message mac-address=($RegVal->"mac-address") disabled=yes;
+ $SendNotification2 ({ origin=$ScriptName; \
+ 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" . \
+ [ $FormatLine "Controller" $Identity ] . "\n" . \
+ [ $FormatLine "Interface" ($RegVal->"interface") ] . "\n" . \
+ [ $FormatLine "SSID" ($RegVal->"ssid") ] . "\n" . \
+ [ $FormatLine "MAC" ($RegVal->"mac-address") ] . "\n" . \
+ [ $FormatLine "Vendor" $Vendor ] . "\n" . \
+ [ $FormatLine "Hostname" $HostName ] . "\n" . \
+ [ $FormatLine "Address" $Address ] . "\n" . \
+ [ $FormatMultiLines "DNS name" $DnsName ] . "\n" . \
+ [ $FormatLine "Date" $DateTime ]) });
+ }
+ } else={
+ $LogPrint debug $ScriptName ("No mac address available... Ignoring.");
+ }
+ }
+} on-error={ }
diff --git a/contrib/logo-color.d/browser-01.avif b/contrib/logo-color.d/browser-01.avif
new file mode 100644
index 0000000..3dc0a1f
--- /dev/null
+++ b/contrib/logo-color.d/browser-01.avif
Binary files differ
diff --git a/contrib/logo-color.d/browser-02.avif b/contrib/logo-color.d/browser-02.avif
new file mode 100644
index 0000000..1867fbe
--- /dev/null
+++ b/contrib/logo-color.d/browser-02.avif
Binary files differ
diff --git a/contrib/logo-color.d/browser-03.avif b/contrib/logo-color.d/browser-03.avif
new file mode 100644
index 0000000..dc24bbb
--- /dev/null
+++ b/contrib/logo-color.d/browser-03.avif
Binary files differ
diff --git a/contrib/logo-color.d/script.js b/contrib/logo-color.d/script.js
new file mode 100644
index 0000000..82cc204
--- /dev/null
+++ b/contrib/logo-color.d/script.js
@@ -0,0 +1,12 @@
+function invertHex(hex) {
+ return (Number("0x1" + hex) ^ 0xffffff).toString(16).substr(1);
+}
+
+function color() {
+ var svg = document.querySelector(".logo").getSVGDocument();
+ svg.getElementById("dark-1").setAttribute("stop-color", document.getElementById("color1").value);
+ svg.getElementById("dark-2").setAttribute("stop-color", document.getElementById("color2").value);
+ var background = document.getElementById("color3").value;
+ svg.getElementById("background").setAttribute("fill", background);
+ svg.getElementById("hexagon").setAttribute("stroke", "#" + invertHex(background.substring(1)));
+}
diff --git a/contrib/logo-color.d/style.css b/contrib/logo-color.d/style.css
new file mode 100644
index 0000000..eb2ec6a
--- /dev/null
+++ b/contrib/logo-color.d/style.css
@@ -0,0 +1,5 @@
+body {
+ font-family: fira-sans, sans-serif;
+ font-size: 10pt;
+ background-color: transparent;
+}
diff --git a/contrib/logo-color.html b/contrib/logo-color.html
new file mode 100644
index 0000000..17942ce
--- /dev/null
+++ b/contrib/logo-color.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<title>RouterOS-Scripts Logo Color Changer</title>
+<link rel="stylesheet" type="text/css" href="logo-color.d/style.css">
+<script src="logo-color.d/script.js"></script>
+</head>
+<body>
+
+<h1>RouterOS-Scripts Logo Color Changer</h1>
+
+<p>You want the logo for your own notifications? But you joined the
+<a href="https://t.me/routeros_scripts">Telegram Group</a> and want
+something that differentiates? Color it!</p>
+
+<embed class="logo" src="../logo.svg" width="192" height="192" type="image/svg+xml">
+
+<p>Select the colors here:
+<input id="color1" type="color" value="#222222" onchange="color();">
+<input id="color2" type="color" value="#444444" onchange="color();">
+<input id="color3" type="color" value="#ffffff" onchange="color();"></p>
+
+<p>Then right-click, click "<i>Take Screenshot</i>" and finally select the
+logo and download it.</p>
+
+<p><img src="logo-color.d/browser-01.avif" width=533 height=482 alt="Screenshot Browser 01">
+<img src="logo-color.d/browser-02.avif" width=533 height=482 alt="Screenshot Browser 02">
+<img src="logo-color.d/browser-03.avif" width=533 height=482 alt="Screenshot Browser 03"></p>
+
+<p>(This example is with
+<a href="https://www.mozilla.org/de/firefox/new/">Firefox</a>. The workflow
+for other browsers may differ.)</p>
+
+<p>See how to
+<a href="../../about/doc/mod/notification-telegram.md#set-a-profile-photo">Set
+a profile photo</a> for your Telegram bot.</p>
+
+</body>
+</html>
diff --git a/contrib/notification.d/script.js b/contrib/notification.d/script.js
new file mode 100644
index 0000000..91741fd
--- /dev/null
+++ b/contrib/notification.d/script.js
@@ -0,0 +1,6 @@
+function visible(cb, element) {
+ document.getElementById(element).style.display = cb.checked ? "block" : "none";
+}
+function update(cb, element) {
+ document.getElementById(element).innerHTML = cb.value;
+}
diff --git a/contrib/notification.d/style.css b/contrib/notification.d/style.css
new file mode 100644
index 0000000..648ea23
--- /dev/null
+++ b/contrib/notification.d/style.css
@@ -0,0 +1,36 @@
+body {
+ font-family: fira-sans, sans-serif;
+ font-size: 10pt;
+ background-color: transparent;
+}
+div.notification {
+ position: relative;
+ float: right;
+ width: 600px;
+ border: 3px outset #6c5d53;
+ /* border-radius: 5px; */
+ padding: 10px;
+ background-color: #e6e6e6;
+}
+div.content {
+ padding-left: 60px;
+}
+img.logo {
+ float: left;
+ border-radius: 50%;
+}
+p.heading {
+ margin: 0px;
+ font-weight: bold;
+ text-decoration: underline;
+}
+p.hint {
+ display: none;
+}
+pre {
+ font-family: fira-mono, monospace;
+ white-space: pre-wrap;
+}
+span.link {
+ color: #863600;
+}
diff --git a/contrib/notification.html b/contrib/notification.html
new file mode 100644
index 0000000..7875036
--- /dev/null
+++ b/contrib/notification.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<title>RouterOS-Scripts Notification Generator</title>
+<link rel="stylesheet" type="text/css" href="notification.d/style.css">
+<script src="notification.d/script.js"></script>
+</head>
+<body>
+
+<h1>RouterOS-Scripts Notification Generator</h1>
+
+<div class="notification">
+ <img src="../logo.svg" alt="logo" class="logo" width=48 height=48>
+ <div class="content">
+ <p id="heading" class="heading">[<span id="hostname">MikroTik</span>] <span id="subject">ℹ️ Subject</span></p>
+ <pre id="message">Message</pre>
+ <p id="link" class="hint">🔗 <span id="link-text" class="link">https://eworm.de/</span></p>
+ <p id="queued" class="hint">⏰ This message was queued since <span id="queued-since">oct/18/2022 18:30:48</span> and may be obsolete.</p>
+ <p id="cut" class="hint">✂️ The message was too long and has been truncated, cut off <span id="cut-percent">13</span>%!</p>
+ </div>
+</div>
+
+<p>Hostname: <input type="text" value="MikroTik" onchange="update(this, 'hostname')"></p>
+<p>Subject: <input type="text" size=50 value="ℹ️ Subject" onchange="update(this, 'subject')"></p>
+<p>Message: <textarea id="w3review" name="w3review" rows="4" cols="50" onchange="update(this, 'message')">Message</textarea></p>
+<p><input type="checkbox" onclick="visible(this, 'link')"> Show link: <input type="text" value="https://eworm.de/" onchange="update(this, 'link-text')"></p>
+<p><input type="checkbox" onclick="visible(this, 'queued')"> Queued since <input type="text" value="oct/18/2022 18:30:48" onchange="update(this, 'queued-since')"></p>
+<p><input type="checkbox" onclick="visible(this, 'cut')"> Cut-off with <input type="number" min=1 max=99 value=13 onchange="update(this, 'cut-percent')"> percent</p>
+
+<p>Then right-click, click "<i>Take Screenshot</i>" and finally select the
+notification and download it.</p>
+
+</body>
+</html>
diff --git a/daily-psk.capsman b/daily-psk.capsman
deleted file mode 100644
index 6e89ff2..0000000
--- a/daily-psk.capsman
+++ /dev/null
@@ -1,94 +0,0 @@
-#!rsc by RouterOS
-# RouterOS script: daily-psk.capsman
-# Copyright (c) 2013-2022 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 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 Ssid [ /caps-man/access-list/get $AccList ssid-regexp ];
- :local Configuration [ /caps-man/configuration/get ([ find where ssid=$Ssid ]->0) name ];
- :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/interface/find where configuration=$Configuration ] ] > 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 ("https://www.eworm.de/cgi-bin/cqrlogo-wifi.cgi" . \
- "?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 });
- }
- }
- }
-}
diff --git a/daily-psk.capsman.rsc b/daily-psk.capsman.rsc
new file mode 100644
index 0000000..43651d0
--- /dev/null
+++ b/daily-psk.capsman.rsc
@@ -0,0 +1,92 @@
+#!rsc by RouterOS
+# RouterOS script: daily-psk.capsman
+# Copyright (c) 2013-2024 Christian Hesse <mail@eworm.de>
+# Michael Gisbers <michael@gisbers.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# requires RouterOS, version=7.12
+#
+# 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!
+
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:do {
+ :local ScriptName [ :jobname ];
+
+ :global DailyPskMatchComment;
+ :global DailyPskQrCodeUrl;
+ :global Identity;
+
+ :global FormatLine;
+ :global LogPrint;
+ :global ScriptLock;
+ :global SendNotification2;
+ :global SymbolForNotification;
+ :global UrlEncode;
+ :global WaitForFile;
+ :global WaitFullyConnected;
+
+ :if ([ $ScriptLock $ScriptName ] = false) do={
+ :error false;
+ }
+ $WaitFullyConnected;
+
+ # return pseudo-random string for PSK
+ :local GeneratePSK do={
+ :local Date [ :tostr $1 ];
+
+ :global DailyPskSecrets;
+
+ :global ParseDate;
+
+ :set Date [ $ParseDate $Date ];
+
+ :local A ((14 - ($Date->"month")) / 12);
+ :local B (($Date->"year") - $A);
+ :local C (($Date->"month") + 12 * $A - 2);
+ :local WeekDay (7000 + ($Date->"day") + $B + ($B / 4) - ($B / 100) + ($B / 400) + ((31 * $C) / 12));
+ :set WeekDay ($WeekDay - (($WeekDay / 7) * 7));
+
+ :return (($DailyPskSecrets->0->(($Date->"day") - 1)) . \
+ ($DailyPskSecrets->1->(($Date->"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={
+ $LogPrint info $ScriptName ("Updating daily PSK for " . $Ssid . " to " . $NewPsk . " (was " . $OldPsk . ")");
+ /caps-man/access-list/set $AccList private-passphrase=$NewPsk;
+
+ :if ([ :len [ /caps-man/actual-interface-configuration/find where configuration.ssid=$Ssid !disabled ] ] > 0) do={
+ :if ($Seen->$Ssid = 1) do={
+ $LogPrint debug $ScriptName ("Already sent a mail for SSID " . $Ssid . ", skipping.");
+ } else={
+ :local Link ($DailyPskQrCodeUrl . \
+ "?scale=8&level=1&ssid=" . [ $UrlEncode $Ssid ] . "&pass=" . [ $UrlEncode $NewPsk ]);
+ $SendNotification2 ({ origin=$ScriptName; \
+ subject=([ $SymbolForNotification "calendar" ] . "daily PSK " . $Ssid); \
+ message=("This is the daily PSK on " . $Identity . ":\n\n" . \
+ [ $FormatLine "SSID" $Ssid ] . "\n" . \
+ [ $FormatLine "PSK" $NewPsk ] . "\n" . \
+ [ $FormatLine "Date" $Date ] . "\n\n" . \
+ "A client device specific rule must not exist!"); link=$Link });
+ :set ($Seen->$Ssid) 1;
+ }
+ }
+ }
+ }
+} on-error={ }
diff --git a/daily-psk.local b/daily-psk.local
deleted file mode 100644
index 3876430..0000000
--- a/daily-psk.local
+++ /dev/null
@@ -1,94 +0,0 @@
-#!rsc by RouterOS
-# RouterOS script: daily-psk.local
-# Copyright (c) 2013-2022 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 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 ("https://www.eworm.de/cgi-bin/cqrlogo-wifi.cgi" . \
- "?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 });
- }
- }
- }
-}
diff --git a/daily-psk.local.rsc b/daily-psk.local.rsc
new file mode 100644
index 0000000..2dbc61b
--- /dev/null
+++ b/daily-psk.local.rsc
@@ -0,0 +1,91 @@
+#!rsc by RouterOS
+# RouterOS script: daily-psk.local
+# Copyright (c) 2013-2024 Christian Hesse <mail@eworm.de>
+# Michael Gisbers <michael@gisbers.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# requires RouterOS, version=7.12
+#
+# 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!
+
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:do {
+ :local ScriptName [ :jobname ];
+
+ :global DailyPskMatchComment;
+ :global DailyPskQrCodeUrl;
+ :global Identity;
+
+ :global FormatLine;
+ :global LogPrint;
+ :global ScriptLock;
+ :global SendNotification2;
+ :global SymbolForNotification;
+ :global UrlEncode;
+ :global WaitForFile;
+ :global WaitFullyConnected;
+
+ :if ([ $ScriptLock $ScriptName ] = false) do={
+ :error false;
+ }
+ $WaitFullyConnected;
+
+ # return pseudo-random string for PSK
+ :local GeneratePSK do={
+ :local Date [ :tostr $1 ];
+
+ :global DailyPskSecrets;
+
+ :global ParseDate;
+
+ :set Date [ $ParseDate $Date ];
+
+ :local A ((14 - ($Date->"month")) / 12);
+ :local B (($Date->"year") - $A);
+ :local C (($Date->"month") + 12 * $A - 2);
+ :local WeekDay (7000 + ($Date->"day") + $B + ($B / 4) - ($B / 100) + ($B / 400) + ((31 * $C) / 12));
+ :set WeekDay ($WeekDay - (($WeekDay / 7) * 7));
+
+ :return (($DailyPskSecrets->0->(($Date->"day") - 1)) . \
+ ($DailyPskSecrets->1->(($Date->"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={
+ $LogPrint info $ScriptName ("Updating daily PSK for " . $Ssid . " to " . $NewPsk . " (was " . $OldPsk . ")");
+ /interface/wireless/access-list/set $AccList private-pre-shared-key=$NewPsk;
+
+ :if ([ :len [ /interface/wireless/find where name=$IntName !disabled ] ] = 1) do={
+ :if ($Seen->$Ssid = 1) do={
+ $LogPrint debug $ScriptName ("Already sent a mail for SSID " . $Ssid . ", skipping.");
+ } else={
+ :local Link ($DailyPskQrCodeUrl . \
+ "?scale=8&level=1&ssid=" . [ $UrlEncode $Ssid ] . "&pass=" . [ $UrlEncode $NewPsk ]);
+ $SendNotification2 ({ origin=$ScriptName; \
+ subject=([ $SymbolForNotification "calendar" ] . "daily PSK " . $Ssid); \
+ message=("This is the daily PSK on " . $Identity . ":\n\n" . \
+ [ $FormatLine "SSID" $Ssid ] . "\n" . \
+ [ $FormatLine "PSK" $NewPsk ] . "\n" . \
+ [ $FormatLine "Date" $Date ] . "\n\n" . \
+ "A client device specific rule must not exist!"); link=$Link });
+ :set ($Seen->$Ssid) 1;
+ }
+ }
+ }
+ }
+} on-error={ }
diff --git a/daily-psk.template b/daily-psk.template
deleted file mode 100644
index af05cd7..0000000
--- a/daily-psk.template
+++ /dev/null
@@ -1,100 +0,0 @@
-#!rsc by RouterOS
-# RouterOS script: daily-psk%TEMPL%
-# Copyright (c) 2013-2022 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
-#
-# !! This is just a template! Replace '%PATH%' with 'caps-man'
-# !! or 'interface wireless'!
-
-:local 0 "daily-psk%TEMPL%";
-:global GlobalFunctionsReady;
-:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
-
-:global DailyPskMatchComment;
-: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=[ /%PATH%/access-list/find where comment~$DailyPskMatchComment ] do={
- :local IntName [ /interface/wireless/access-list/get $AccList interface ];
- :local Ssid [ /interface/wireless/get $IntName ssid ];
- :local Ssid [ /caps-man/access-list/get $AccList ssid-regexp ];
- :local Configuration [ /caps-man/configuration/get ([ find where ssid=$Ssid ]->0) name ];
- :local OldPsk [ /interface/wireless/access-list/get $AccList private-pre-shared-key ];
- :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;
- /interface/wireless/access-list/set $AccList private-pre-shared-key=$NewPsk;
- /caps-man/access-list/set $AccList private-passphrase=$NewPsk;
-
- :if ([ :len [ /interface/wireless/find where name=$IntName !disabled ] ] = 1) do={
- :if ([ :len [ /caps-man/interface/find where configuration=$Configuration ] ] > 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 ("https://www.eworm.de/cgi-bin/cqrlogo-wifi.cgi" . \
- "?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 });
- }
- }
- }
-}
diff --git a/daily-psk.template.rsc b/daily-psk.template.rsc
new file mode 100644
index 0000000..e190ffb
--- /dev/null
+++ b/daily-psk.template.rsc
@@ -0,0 +1,107 @@
+#!rsc by RouterOS
+# RouterOS script: daily-psk%TEMPL%
+# Copyright (c) 2013-2024 Christian Hesse <mail@eworm.de>
+# Michael Gisbers <michael@gisbers.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# requires RouterOS, version=7.12
+#
+# update daily PSK (pre shared key)
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/daily-psk.md
+#
+# !! This is just a template to generate the real script!
+# !! Pattern '%TEMPL%' is replaced, paths are filtered.
+
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:do {
+ :local ScriptName [ :jobname ];
+
+ :global DailyPskMatchComment;
+ :global DailyPskQrCodeUrl;
+ :global Identity;
+
+ :global FormatLine;
+ :global LogPrint;
+ :global ScriptLock;
+ :global SendNotification2;
+ :global SymbolForNotification;
+ :global UrlEncode;
+ :global WaitForFile;
+ :global WaitFullyConnected;
+
+ :if ([ $ScriptLock $ScriptName ] = false) do={
+ :error false;
+ }
+ $WaitFullyConnected;
+
+ # return pseudo-random string for PSK
+ :local GeneratePSK do={
+ :local Date [ :tostr $1 ];
+
+ :global DailyPskSecrets;
+
+ :global ParseDate;
+
+ :set Date [ $ParseDate $Date ];
+
+ :local A ((14 - ($Date->"month")) / 12);
+ :local B (($Date->"year") - $A);
+ :local C (($Date->"month") + 12 * $A - 2);
+ :local WeekDay (7000 + ($Date->"day") + $B + ($B / 4) - ($B / 100) + ($B / 400) + ((31 * $C) / 12));
+ :set WeekDay ($WeekDay - (($WeekDay / 7) * 7));
+
+ :return (($DailyPskSecrets->0->(($Date->"day") - 1)) . \
+ ($DailyPskSecrets->1->(($Date->"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={
+ :foreach AccList in=[ /interface/wifi/access-list/find where comment~$DailyPskMatchComment ] do={
+ :foreach AccList in=[ /interface/wireless/access-list/find where comment~$DailyPskMatchComment ] do={
+ :local SsidRegExp [ /caps-man/access-list/get $AccList ssid-regexp ];
+ :local SsidRegExp [ /interface/wifi/access-list/get $AccList ssid-regexp ];
+ :local Configuration ([ /caps-man/configuration/find where ssid~$SsidRegExp ]->0);
+ :local Configuration ([ /interface/wifi/configuration/find where ssid~$SsidRegExp ]->0);
+ :local Ssid [ /caps-man/configuration/get $Configuration ssid ];
+ :local Ssid [ /interface/wifi/configuration/get $Configuration ssid ];
+ :local OldPsk [ /caps-man/access-list/get $AccList private-passphrase ];
+ :local OldPsk [ /interface/wifi/access-list/get $AccList passphrase ];
+ # /caps-man/ /interface/wifi/ above - /interface/wireless/ below
+ :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={
+ $LogPrint info $ScriptName ("Updating daily PSK for " . $Ssid . " to " . $NewPsk . " (was " . $OldPsk . ")");
+ /caps-man/access-list/set $AccList private-passphrase=$NewPsk;
+ /interface/wifi/access-list/set $AccList passphrase=$NewPsk;
+ /interface/wireless/access-list/set $AccList private-pre-shared-key=$NewPsk;
+
+ :if ([ :len [ /caps-man/actual-interface-configuration/find where configuration.ssid=$Ssid !disabled ] ] > 0) do={
+ :if ([ :len [ /interface/wifi/actual-configuration/find where configuration.ssid=$Ssid ] ] > 0) do={
+ :if ([ :len [ /interface/wireless/find where name=$IntName !disabled ] ] = 1) do={
+ :if ($Seen->$Ssid = 1) do={
+ $LogPrint debug $ScriptName ("Already sent a mail for SSID " . $Ssid . ", skipping.");
+ } else={
+ :local Link ($DailyPskQrCodeUrl . \
+ "?scale=8&level=1&ssid=" . [ $UrlEncode $Ssid ] . "&pass=" . [ $UrlEncode $NewPsk ]);
+ $SendNotification2 ({ origin=$ScriptName; \
+ subject=([ $SymbolForNotification "calendar" ] . "daily PSK " . $Ssid); \
+ message=("This is the daily PSK on " . $Identity . ":\n\n" . \
+ [ $FormatLine "SSID" $Ssid ] . "\n" . \
+ [ $FormatLine "PSK" $NewPsk ] . "\n" . \
+ [ $FormatLine "Date" $Date ] . "\n\n" . \
+ "A client device specific rule must not exist!"); link=$Link });
+ :set ($Seen->$Ssid) 1;
+ }
+ }
+ }
+ }
+} on-error={ }
diff --git a/daily-psk.wifi.rsc b/daily-psk.wifi.rsc
new file mode 100644
index 0000000..ee3e1b0
--- /dev/null
+++ b/daily-psk.wifi.rsc
@@ -0,0 +1,92 @@
+#!rsc by RouterOS
+# RouterOS script: daily-psk.wifi
+# Copyright (c) 2013-2024 Christian Hesse <mail@eworm.de>
+# Michael Gisbers <michael@gisbers.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# requires RouterOS, version=7.12
+#
+# 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!
+
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:do {
+ :local ScriptName [ :jobname ];
+
+ :global DailyPskMatchComment;
+ :global DailyPskQrCodeUrl;
+ :global Identity;
+
+ :global FormatLine;
+ :global LogPrint;
+ :global ScriptLock;
+ :global SendNotification2;
+ :global SymbolForNotification;
+ :global UrlEncode;
+ :global WaitForFile;
+ :global WaitFullyConnected;
+
+ :if ([ $ScriptLock $ScriptName ] = false) do={
+ :error false;
+ }
+ $WaitFullyConnected;
+
+ # return pseudo-random string for PSK
+ :local GeneratePSK do={
+ :local Date [ :tostr $1 ];
+
+ :global DailyPskSecrets;
+
+ :global ParseDate;
+
+ :set Date [ $ParseDate $Date ];
+
+ :local A ((14 - ($Date->"month")) / 12);
+ :local B (($Date->"year") - $A);
+ :local C (($Date->"month") + 12 * $A - 2);
+ :local WeekDay (7000 + ($Date->"day") + $B + ($B / 4) - ($B / 100) + ($B / 400) + ((31 * $C) / 12));
+ :set WeekDay ($WeekDay - (($WeekDay / 7) * 7));
+
+ :return (($DailyPskSecrets->0->(($Date->"day") - 1)) . \
+ ($DailyPskSecrets->1->(($Date->"month") - 1)) . \
+ ($DailyPskSecrets->2->$WeekDay));
+ }
+
+ :local Seen ({});
+ :local Date [ /system/clock/get date ];
+ :local NewPsk [ $GeneratePSK $Date ];
+
+ :foreach AccList in=[ /interface/wifi/access-list/find where comment~$DailyPskMatchComment ] do={
+ :local SsidRegExp [ /interface/wifi/access-list/get $AccList ssid-regexp ];
+ :local Configuration ([ /interface/wifi/configuration/find where ssid~$SsidRegExp ]->0);
+ :local Ssid [ /interface/wifi/configuration/get $Configuration ssid ];
+ :local OldPsk [ /interface/wifi/access-list/get $AccList passphrase ];
+ :local Skip 0;
+
+ :if ($NewPsk != $OldPsk) do={
+ $LogPrint info $ScriptName ("Updating daily PSK for " . $Ssid . " to " . $NewPsk . " (was " . $OldPsk . ")");
+ /interface/wifi/access-list/set $AccList passphrase=$NewPsk;
+
+ :if ([ :len [ /interface/wifi/actual-configuration/find where configuration.ssid=$Ssid ] ] > 0) do={
+ :if ($Seen->$Ssid = 1) do={
+ $LogPrint debug $ScriptName ("Already sent a mail for SSID " . $Ssid . ", skipping.");
+ } else={
+ :local Link ($DailyPskQrCodeUrl . \
+ "?scale=8&level=1&ssid=" . [ $UrlEncode $Ssid ] . "&pass=" . [ $UrlEncode $NewPsk ]);
+ $SendNotification2 ({ origin=$ScriptName; \
+ subject=([ $SymbolForNotification "calendar" ] . "daily PSK " . $Ssid); \
+ message=("This is the daily PSK on " . $Identity . ":\n\n" . \
+ [ $FormatLine "SSID" $Ssid ] . "\n" . \
+ [ $FormatLine "PSK" $NewPsk ] . "\n" . \
+ [ $FormatLine "Date" $Date ] . "\n\n" . \
+ "A client device specific rule must not exist!"); link=$Link });
+ :set ($Seen->$Ssid) 1;
+ }
+ }
+ }
+ }
+} on-error={ }
diff --git a/dhcp-lease-comment.capsman b/dhcp-lease-comment.capsman
deleted file mode 100644
index fa34539..0000000
--- a/dhcp-lease-comment.capsman
+++ /dev/null
@@ -1,30 +0,0 @@
-#!rsc by RouterOS
-# RouterOS script: dhcp-lease-comment.capsman
-# Copyright (c) 2013-2022 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;
- }
-}
diff --git a/dhcp-lease-comment.capsman.rsc b/dhcp-lease-comment.capsman.rsc
new file mode 100644
index 0000000..4ac228b
--- /dev/null
+++ b/dhcp-lease-comment.capsman.rsc
@@ -0,0 +1,39 @@
+#!rsc by RouterOS
+# RouterOS script: dhcp-lease-comment.capsman
+# Copyright (c) 2013-2024 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# provides: lease-script, order=60
+# requires RouterOS, version=7.12
+#
+# 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!
+
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:do {
+ :local ScriptName [ :jobname ];
+
+ :global LogPrint;
+ :global ScriptLock;
+
+ :if ([ $ScriptLock $ScriptName ] = false) do={
+ :error false;
+ }
+
+ :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->"active-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={
+ $LogPrint info $ScriptName ("Updating comment for DHCP lease " . $LeaseVal->"active-mac-address" . ": " . $NewComment);
+ /ip/dhcp-server/lease/set comment=$NewComment $Lease;
+ }
+ }
+} on-error={ }
diff --git a/dhcp-lease-comment.local b/dhcp-lease-comment.local
deleted file mode 100644
index 71e6d5e..0000000
--- a/dhcp-lease-comment.local
+++ /dev/null
@@ -1,30 +0,0 @@
-#!rsc by RouterOS
-# RouterOS script: dhcp-lease-comment.local
-# Copyright (c) 2013-2022 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;
- }
-}
diff --git a/dhcp-lease-comment.local.rsc b/dhcp-lease-comment.local.rsc
new file mode 100644
index 0000000..a49f74f
--- /dev/null
+++ b/dhcp-lease-comment.local.rsc
@@ -0,0 +1,39 @@
+#!rsc by RouterOS
+# RouterOS script: dhcp-lease-comment.local
+# Copyright (c) 2013-2024 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# provides: lease-script, order=60
+# requires RouterOS, version=7.12
+#
+# 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!
+
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:do {
+ :local ScriptName [ :jobname ];
+
+ :global LogPrint;
+ :global ScriptLock;
+
+ :if ([ $ScriptLock $ScriptName ] = false) do={
+ :error false;
+ }
+
+ :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->"active-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={
+ $LogPrint info $ScriptName ("Updating comment for DHCP lease " . $LeaseVal->"active-mac-address" . ": " . $NewComment);
+ /ip/dhcp-server/lease/set comment=$NewComment $Lease;
+ }
+ }
+} on-error={ }
diff --git a/dhcp-lease-comment.template b/dhcp-lease-comment.template
deleted file mode 100644
index a31812b..0000000
--- a/dhcp-lease-comment.template
+++ /dev/null
@@ -1,31 +0,0 @@
-#!rsc by RouterOS
-# RouterOS script: dhcp-lease-comment%TEMPL%
-# Copyright (c) 2013-2022 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
-#
-# !! This is just a template! Replace '%PATH%' with 'caps-man'
-# !! or 'interface wireless'!
-
-:local 0 "dhcp-lease-comment%TEMPL%";
-: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 ([ /%PATH%/access-list/find where mac-address=($LeaseVal->"mac-address") ]->0);
- :if ([ :len $AccessList ] > 0) do={
- :set NewComment [ /%PATH%/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;
- }
-}
diff --git a/dhcp-lease-comment.template.rsc b/dhcp-lease-comment.template.rsc
new file mode 100644
index 0000000..0f0975b
--- /dev/null
+++ b/dhcp-lease-comment.template.rsc
@@ -0,0 +1,44 @@
+#!rsc by RouterOS
+# RouterOS script: dhcp-lease-comment%TEMPL%
+# Copyright (c) 2013-2024 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# provides: lease-script, order=60
+# requires RouterOS, version=7.12
+#
+# update dhcp-server lease comment with infos from access-list
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/dhcp-lease-comment.md
+#
+# !! This is just a template to generate the real script!
+# !! Pattern '%TEMPL%' is replaced, paths are filtered.
+
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:do {
+ :local ScriptName [ :jobname ];
+
+ :global LogPrint;
+ :global ScriptLock;
+
+ :if ([ $ScriptLock $ScriptName ] = false) do={
+ :error false;
+ }
+
+ :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->"active-mac-address") ]->0);
+ :local AccessList ([ /interface/wifi/access-list/find where mac-address=($LeaseVal->"active-mac-address") ]->0);
+ :local AccessList ([ /interface/wireless/access-list/find where mac-address=($LeaseVal->"active-mac-address") ]->0);
+ :if ([ :len $AccessList ] > 0) do={
+ :set NewComment [ /caps-man/access-list/get $AccessList comment ];
+ :set NewComment [ /interface/wifi/access-list/get $AccessList comment ];
+ :set NewComment [ /interface/wireless/access-list/get $AccessList comment ];
+ }
+ :if ([ :len $NewComment ] != 0 && $LeaseVal->"comment" != $NewComment) do={
+ $LogPrint info $ScriptName ("Updating comment for DHCP lease " . $LeaseVal->"active-mac-address" . ": " . $NewComment);
+ /ip/dhcp-server/lease/set comment=$NewComment $Lease;
+ }
+ }
+} on-error={ }
diff --git a/dhcp-lease-comment.wifi.rsc b/dhcp-lease-comment.wifi.rsc
new file mode 100644
index 0000000..c9c091b
--- /dev/null
+++ b/dhcp-lease-comment.wifi.rsc
@@ -0,0 +1,39 @@
+#!rsc by RouterOS
+# RouterOS script: dhcp-lease-comment.wifi
+# Copyright (c) 2013-2024 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# provides: lease-script, order=60
+# requires RouterOS, version=7.12
+#
+# 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!
+
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:do {
+ :local ScriptName [ :jobname ];
+
+ :global LogPrint;
+ :global ScriptLock;
+
+ :if ([ $ScriptLock $ScriptName ] = false) do={
+ :error false;
+ }
+
+ :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/wifi/access-list/find where mac-address=($LeaseVal->"active-mac-address") ]->0);
+ :if ([ :len $AccessList ] > 0) do={
+ :set NewComment [ /interface/wifi/access-list/get $AccessList comment ];
+ }
+ :if ([ :len $NewComment ] != 0 && $LeaseVal->"comment" != $NewComment) do={
+ $LogPrint info $ScriptName ("Updating comment for DHCP lease " . $LeaseVal->"active-mac-address" . ": " . $NewComment);
+ /ip/dhcp-server/lease/set comment=$NewComment $Lease;
+ }
+ }
+} on-error={ }
diff --git a/dhcp-to-dns b/dhcp-to-dns
deleted file mode 100644
index 368d68f..0000000
--- a/dhcp-to-dns
+++ /dev/null
@@ -1,97 +0,0 @@
-#!rsc by RouterOS
-# RouterOS script: dhcp-to-dns
-# Copyright (c) 2013-2022 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;
- }
-}
diff --git a/dhcp-to-dns.rsc b/dhcp-to-dns.rsc
new file mode 100644
index 0000000..5b6e64a
--- /dev/null
+++ b/dhcp-to-dns.rsc
@@ -0,0 +1,126 @@
+#!rsc by RouterOS
+# RouterOS script: dhcp-to-dns
+# Copyright (c) 2013-2024 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# provides: lease-script, order=20
+# requires RouterOS, version=7.12
+#
+# check DHCP leases and add/remove/update DNS entries
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/dhcp-to-dns.md
+
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:do {
+ :local ScriptName [ :jobname ];
+
+ :global Domain;
+ :global Identity;
+
+ :global CleanName;
+ :global EitherOr;
+ :global IfThenElse;
+ :global LogPrint;
+ :global LogPrintOnce;
+ :global ParseKeyValueStore;
+ :global ScriptLock;
+
+ :if ([ $ScriptLock $ScriptName 10 ] = false) do={
+ :error false;
+ }
+
+ :local Ttl 5m;
+ :local CommentPrefix ("managed by " . $ScriptName);
+ :local CommentString ("--- " . $ScriptName . " above ---");
+
+ :if ([ :len [ /ip/dns/static/find where (name=$CommentString or (comment=$CommentString and name=-)) type=NXDOMAIN disabled ] ] = 0) do={
+ /ip/dns/static/add name=$CommentString type=NXDOMAIN disabled=yes;
+ $LogPrint warning $ScriptName ("Added disabled static dns record with name '" . $CommentString . "'.");
+ }
+ :local PlaceBefore ([ /ip/dns/static/find where (name=$CommentString or (comment=$CommentString and name=-)) type=NXDOMAIN disabled ]->0);
+
+ :foreach DnsRecord in=[ /ip/dns/static/find where comment~("^" . $CommentPrefix . "\\b") (!type or type=A) ] do={
+ :local DnsRecordVal [ /ip/dns/static/get $DnsRecord ];
+ :local DnsRecordInfo [ $ParseKeyValueStore ($DnsRecordVal->"comment") ];
+ :local MacInServer ($DnsRecordInfo->"macaddress" . " in " . $DnsRecordInfo->"server");
+
+ :if ([ :len [ /ip/dhcp-server/lease/find where active-mac-address=($DnsRecordInfo->"macaddress") \
+ active-address=($DnsRecordVal->"address") server=($DnsRecordInfo->"server") status=bound ] ] > 0) do={
+ $LogPrint debug $ScriptName ("Lease for " . $MacInServer . " (" . $DnsRecordVal->"name" . ") still exists. Not deleting record.");
+ } else={
+ :local Found false;
+ $LogPrint info $ScriptName ("Lease expired for " . $MacInServer . ", deleting record (" . $DnsRecordVal->"name" . ").");
+ /ip/dns/static/remove $DnsRecord;
+ /ip/dns/static/remove [ find where type=CNAME comment=($DnsRecordVal->"comment") ];
+ }
+ }
+
+ :foreach Lease in=[ /ip/dhcp-server/lease/find where status=bound ] do={
+ :local LeaseVal;
+ :do {
+ :set LeaseVal [ /ip/dhcp-server/lease/get $Lease ];
+ :if ([ :len [ /ip/dhcp-server/lease/find where active-mac-address=($LeaseVal->"active-mac-address") status=bound ] ] > 1) do={
+ $LogPrintOnce info $ScriptName ("Multiple bound leases found for mac-address " . ($LeaseVal->"active-mac-address") . "!");
+ }
+ } on-error={
+ $LogPrint debug $ScriptName ("A lease just vanished, ignoring.");
+ }
+
+ :if ([ :len ($LeaseVal->"active-address") ] > 0) do={
+ :local Comment ($CommentPrefix . ", macaddress=" . $LeaseVal->"active-mac-address" . ", server=" . $LeaseVal->"server");
+ :local MacDash [ $CleanName ($LeaseVal->"active-mac-address") ];
+ :local HostName [ $CleanName [ $EitherOr ([ $ParseKeyValueStore ($LeaseVal->"comment") ]->"hostname") ($LeaseVal->"host-name") ] ];
+ :local Network [ /ip/dhcp-server/network/find where ($LeaseVal->"active-address") in address ];
+ :local NetworkVal;
+ :if ([ :len $Network ] > 0) do={
+ :set NetworkVal [ /ip/dhcp-server/network/get ($Network->0) ];
+ }
+ :local NetworkInfo [ $ParseKeyValueStore ($NetworkVal->"comment") ];
+ :local NetDomain ([ $IfThenElse ([ :len ($NetworkInfo->"name-extra") ] > 0) ($NetworkInfo->"name-extra" . ".") ] . \
+ [ $EitherOr [ $EitherOr ($NetworkInfo->"domain") ($NetworkVal->"domain") ] $Domain ]);
+ :local FullA ($MacDash . "." . $NetDomain);
+ :local FullCN ($HostName . "." . $NetDomain);
+ :local MacInServer ($LeaseVal->"active-mac-address" . " in " . $LeaseVal->"server");
+
+ :local DnsRecord [ /ip/dns/static/find where comment=$Comment (!type or type=A) ];
+ :if ([ :len $DnsRecord ] > 0) do={
+ :local DnsRecordVal [ /ip/dns/static/get $DnsRecord ];
+
+ :if ($DnsRecordVal->"address" = $LeaseVal->"active-address" && $DnsRecordVal->"name" = $FullA) do={
+ $LogPrint debug $ScriptName ("The A record for " . $MacInServer . " (" . $FullA . ") does not need updating.");
+ } else={
+ $LogPrint info $ScriptName ("Updating A record for " . $MacInServer . " (" . $FullA . " -> " . $LeaseVal->"active-address" . ").");
+ /ip/dns/static/set address=($LeaseVal->"active-address") name=$FullA $DnsRecord;
+ }
+
+ :local CName [ /ip/dns/static/find where comment=$Comment type=CNAME ];
+ :if ([ :len $CName ] > 0) do={
+ :local CNameVal [ /ip/dns/static/get $CName ];
+ :if ($CNameVal->"name" != $FullCN || $CNameVal->"cname" != $FullA) do={
+ $LogPrint info $ScriptName ("Deleting CNAME record with wrong data for " . $MacInServer . ".");
+ /ip/dns/static/remove $CName;
+ }
+ }
+ :if ([ :len $HostName ] > 0 && [ :len [ /ip/dns/static/find where name=$FullCN type=CNAME ] ] = 0) do={
+ $LogPrint info $ScriptName ("Adding CNAME record for " . $MacInServer . " (" . $FullCN . " -> " . $FullA . ").");
+ /ip/dns/static/add name=$FullCN type=CNAME cname=$FullA ttl=$Ttl comment=$Comment place-before=$PlaceBefore;
+ }
+
+ } else={
+ $LogPrint info $ScriptName ("Adding A record for " . $MacInServer . " (" . $FullA . " -> " . $LeaseVal->"active-address" . ").");
+ /ip/dns/static/add name=$FullA type=A address=($LeaseVal->"active-address") ttl=$Ttl comment=$Comment place-before=$PlaceBefore;
+ :if ([ :len $HostName ] > 0 && [ :len [ /ip/dns/static/find where name=$FullCN type=CNAME ] ] = 0) do={
+ $LogPrint info $ScriptName ("Adding CNAME record for " . $MacInServer . " (" . $FullCN . " -> " . $FullA . ").");
+ /ip/dns/static/add name=$FullCN type=CNAME cname=$FullA ttl=$Ttl comment=$Comment place-before=$PlaceBefore;
+ }
+ }
+
+ :if ([ :len [ /ip/dns/static/find where name=$FullA (!type or type=A) ] ] > 1) do={
+ $LogPrintOnce warning $ScriptName ("The name '" . $FullA . "' appeared in more than one A record!");
+ }
+ } else={
+ $LogPrint debug $ScriptName ("No address available... Ignoring.");
+ }
+ }
+} on-error={ }
diff --git a/doc/accesslist-duplicates.md b/doc/accesslist-duplicates.md
index 50720b1..109bebf 100644
--- a/doc/accesslist-duplicates.md
+++ b/doc/accesslist-duplicates.md
@@ -1,7 +1,14 @@
Find and remove access list duplicates
======================================
-[◀ Go back to main README](../README.md)
+[![GitHub stars](https://img.shields.io/github/stars/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=red)](https://github.com/eworm-de/routeros-scripts/stargazers)
+[![GitHub forks](https://img.shields.io/github/forks/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=green)](https://github.com/eworm-de/routeros-scripts/network)
+[![GitHub watchers](https://img.shields.io/github/watchers/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=blue)](https://github.com/eworm-de/routeros-scripts/watchers)
+[![required RouterOS version](https://img.shields.io/badge/RouterOS-7.12-yellow?style=flat)](https://mikrotik.com/download/changelogs/)
+[![Telegram group @routeros_scripts](https://img.shields.io/badge/Telegram-%40routeros__scripts-%2326A5E4?logo=telegram&style=flat)](https://t.me/routeros_scripts)
+[![donate with PayPal](https://img.shields.io/badge/Like_it%3F-Donate!-orange?logo=githubsponsors&logoColor=orange&style=flat)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)
+
+[⬅️ Go back to main README](../README.md)
> ℹ️️ **Info**: This script can not be used on its own but requires the base
> installation. See [main README](../README.md) for details.
@@ -15,14 +22,19 @@ entries in wireless access list.
Requirements and installation
-----------------------------
-Depending on whether you use CAPsMAN (`/caps-man`) or local wireless
-interface (`/interface/wireless`) you need to install a different script.
+Depending on whether you use `wifi` package (`/interface/wifi`), legacy
+wifi with CAPsMAN (`/caps-man`) or local wireless interface
+(`/interface/wireless`) you need to install a different script.
+
+For `wifi`:
+
+ $ScriptInstallUpdate accesslist-duplicates.wifi;
-For CAPsMAN:
+For legacy CAPsMAN:
$ScriptInstallUpdate accesslist-duplicates.capsman;
-For local interface:
+For legacy local interface:
$ScriptInstallUpdate accesslist-duplicates.local;
@@ -31,7 +43,7 @@ Usage and invocation
Run this script from a terminal:
- /system/script/run accesslist-duplicates.local;
+ /system/script/run accesslist-duplicates.wifi;
![screenshot: example](accesslist-duplicates.d/01-example.avif)
@@ -41,5 +53,5 @@ See also
* [Collect MAC addresses in wireless access list](collect-wireless-mac.md)
---
-[◀ Go back to main README](../README.md)
-[▲ Go back to top](#top)
+[⬅️ Go back to main README](../README.md)
+[⬆️ Go back to top](#top)
diff --git a/doc/backup-cloud.d/notification.avif b/doc/backup-cloud.d/notification.avif
new file mode 100644
index 0000000..e533908
--- /dev/null
+++ b/doc/backup-cloud.d/notification.avif
Binary files differ
diff --git a/doc/backup-cloud.d/notification.svg b/doc/backup-cloud.d/notification.svg
deleted file mode 100644
index a0eed63..0000000
--- a/doc/backup-cloud.d/notification.svg
+++ /dev/null
@@ -1,46 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<svg id="svg8" width="403.78" height="273.78" version="1.1" viewBox="0 0 106.83 72.437" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
- <metadata id="metadata5">
- <rdf:RDF>
- <cc:Work rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <g id="layer1" transform="translate(16.375 11.083)">
- <rect id="rect857" x="-15.875" y="-10.583" width="105.83" height="71.437" rx="1.3229" fill="#e6e6e6" stroke="#6c5d53" stroke-linecap="round" stroke-linejoin="round"/>
- <g id="g884" transform="matrix(.5 0 0 .5 -12.406 -7.1146)" stroke-width="2">
- <path id="path899" d="m23.177 23.177c-2.9635 2.9635-17.991 2.9635-20.955 0-2.9635-2.9635-2.9635-17.991 0-20.955 2.9635-2.9635 17.991-2.9635 20.955 0 2.9635 2.9635 2.9635 17.991 0 20.955z" fill="#fff"/>
- <g id="g871">
- <g id="text837" stroke-width=".52916px" aria-label="#!rsc">
- <path id="path851" d="m7.4832 16.646v-1.0239h-0.54606l0.16577-1.2774h-1.2774l-0.16577 1.2774h-0.97511l0.16577-1.2774h-1.2774l-0.16577 1.2774h-0.70208v1.0239h0.56556l-0.24378 1.8722h-0.68257v1.0239h0.54606l-0.18527 1.3944h1.2774l0.18527-1.3944h0.97511l-0.18527 1.3944h1.2774l0.18527-1.3944h0.70208v-1.0239h-0.56556l0.24378-1.8722zm-2.2037 1.8722h-0.97511l0.24378-1.8722h0.97511z"/>
- <path id="path853" d="m9.6187 14.179h-1.6382l0.19502 4.271h1.2481zm-0.81909 5.1583c-0.48755 0-0.8776 0.39979-0.8776 0.8776 0 0.48755 0.39004 0.88735 0.8776 0.88735 0.4973 0 0.88735-0.39979 0.88735-0.88735 0-0.4778-0.39004-0.8776-0.88735-0.8776z"/>
- <path id="path855" d="m13.373 15.612c-0.59482 0-1.1019 0.42905-1.3359 1.1506l-0.13652-1.0044h-1.3359v5.1778h1.5407v-2.6035c0.17552-0.77033 0.4388-1.2286 1.0726-1.2286 0.16577 0 0.30228 0.02925 0.46805 0.06826l0.24378-1.4919c-0.17552-0.04875-0.32178-0.06826-0.51681-0.06826z"/>
- <path id="path857" d="m16.181 15.592c-1.3066 0-2.116 0.69233-2.116 1.5797 0 0.79959 0.50706 1.3261 1.5309 1.6187 0.9361 0.26328 1.0921 0.37054 1.0921 0.72158 0 0.31203-0.28278 0.48755-0.75083 0.48755-0.50706 0-0.98486-0.20477-1.3749-0.50706l-0.75083 0.83859c0.50706 0.4583 1.2676 0.77033 2.1647 0.77033 1.2871 0 2.3012-0.63382 2.3012-1.7064 0-0.92635-0.57531-1.3554-1.5992-1.6479-0.92635-0.27303-1.0629-0.39004-1.0629-0.66307 0-0.23402 0.20477-0.39004 0.62407-0.39004 0.44855 0 0.8776 0.14627 1.2774 0.39979l0.56556-0.86784c-0.4778-0.39004-1.1506-0.63382-1.9015-0.63382z"/>
- <path id="path859" d="m21.281 15.592c-1.5504 0-2.5353 1.1506-2.5353 2.7986 0 1.6382 0.97511 2.7108 2.5645 2.7108 0.71183 0 1.2676-0.23403 1.7454-0.61432l-0.67282-0.9556c-0.37054 0.23403-0.62407 0.35104-0.99461 0.35104-0.61432 0-1.0239-0.35104-1.0239-1.5017 0-1.1604 0.38029-1.6089 1.0434-1.6089 0.35104 0 0.65332 0.11701 0.98486 0.37054l0.66307-0.9166c-0.4973-0.4193-1.0531-0.63382-1.7747-0.63382z"/>
- </g>
- <g id="g1542" transform="matrix(2 0 0 2 -.41134 3.175)" fill="#676867" fill-rule="evenodd" stroke-width=".26458">
- <path id="path943" d="m4.9596-1.0196c0.40797 2.8312 1.9272 4.5499 5.0239 4.691-2.918 1.1164-5.9253-1.5076-5.0239-4.691"/>
- <path id="path945" d="m3.3407-0.52096c0.034969-0.00777 0.038854 0.015542 0.041445 0.041445 0.098431 1.8054 0.85998 3.1744 1.8689 4.1108 1.0089 0.93639 2.3636 1.6941 4.274 1.6604-3.5772 1.4247-7.337-1.9984-6.1856-5.8126"/>
- </g>
- </g>
- </g>
- <text id="text4811" transform="matrix(.26458 0 0 .26458 -44.95 -6.6039)" x="-248.88142" fill="#000000" font-family="'Fira Mono', 'Roboto Mono', monospace" font-size="12px" letter-spacing="0px" stroke-width="1px" word-spacing="0px" style="font-variant-caps:normal;font-variant-east-asian:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:15px;shape-inside:url(#rect4813);white-space:pre" xml:space="preserve"><tspan id="tspan13469" x="180" y="10.85">[MikroTik] 💾☁ Cloud backup
-</tspan><tspan id="tspan13471" x="180" y="26.082813">
-</tspan><tspan id="tspan13473" x="180" y="41.082813">Uploaded backup for MikroTik to cloud.
-</tspan><tspan id="tspan13475" x="180" y="56.082811">
-</tspan><tspan id="tspan13477" x="180" y="71.082811">Hostname: MikroTik
-</tspan><tspan id="tspan13479" x="180" y="86.082811">Board name: CHR
-</tspan><tspan id="tspan13481" x="180" y="101.08281">Architecture: x86_64
-</tspan><tspan id="tspan13483" x="180" y="116.08281">RouterOS:
-</tspan><tspan id="tspan13485" x="180" y="131.08281"> Channel: stable
-</tspan><tspan id="tspan13487" x="180" y="146.08282"> Installed: 7.4.1
-</tspan><tspan id="tspan13489" x="180" y="161.08282">RouterOS-Scripts:
-</tspan><tspan id="tspan13491" x="180" y="176.08282"> Version: 83
-</tspan><tspan id="tspan13493" x="180" y="191.08282">
-</tspan><tspan id="tspan13495" x="180" y="206.08282">Name: cloud-20220224-092419
-</tspan><tspan id="tspan13497" x="180" y="221.08282">Size: 370767 B (362 KiB)
-</tspan><tspan id="tspan13499" x="180" y="236.08282">Download key: LLDBfPcWXxmSetWilqeJX5V</tspan></text>
- </g>
-</svg>
diff --git a/doc/backup-cloud.md b/doc/backup-cloud.md
index 130e3f6..03d5953 100644
--- a/doc/backup-cloud.md
+++ b/doc/backup-cloud.md
@@ -1,7 +1,14 @@
Upload backup to Mikrotik cloud
===============================
-[◀ Go back to main README](../README.md)
+[![GitHub stars](https://img.shields.io/github/stars/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=red)](https://github.com/eworm-de/routeros-scripts/stargazers)
+[![GitHub forks](https://img.shields.io/github/forks/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=green)](https://github.com/eworm-de/routeros-scripts/network)
+[![GitHub watchers](https://img.shields.io/github/watchers/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=blue)](https://github.com/eworm-de/routeros-scripts/watchers)
+[![required RouterOS version](https://img.shields.io/badge/RouterOS-7.12-yellow?style=flat)](https://mikrotik.com/download/changelogs/)
+[![Telegram group @routeros_scripts](https://img.shields.io/badge/Telegram-%40routeros__scripts-%2326A5E4?logo=telegram&style=flat)](https://t.me/routeros_scripts)
+[![donate with PayPal](https://img.shields.io/badge/Like_it%3F-Donate!-orange?logo=githubsponsors&logoColor=orange&style=flat)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)
+
+[⬅️ Go back to main README](../README.md)
> ℹ️ **Info**: This script can not be used on its own but requires the base
> installation. See [main README](../README.md) for details.
@@ -12,14 +19,14 @@ Description
This script uploads
[binary backup to Mikrotik cloud](https://wiki.mikrotik.com/wiki/Manual:IP/Cloud#Backup).
-> ⚠️ **Warning**: The used command can hit errors that a script can not handle.
-> This may result in script termination (where no notification is sent) or
-> malfunction of fetch command (where all up- and downloads break) for some
-> time. Failed notifications are queued then.
+> ⚠️ **Warning**: The used command can hit errors that a script can with
+> workaround only. A notification *should* be sent anyway. But it can result
+> in malfunction of fetch command (where all up- and downloads break) for
+> some time. Failed notifications are queued then.
### Sample notification
-![backup-cloud notification](backup-cloud.d/notification.svg)
+![backup-cloud notification](backup-cloud.d/notification.avif)
Requirements and installation
-----------------------------
@@ -36,9 +43,14 @@ The configuration goes to `global-config-overlay`, these are the parameters:
* `BackupPassword`: password to encrypt the backup with
* `BackupRandomDelay`: delay up to amount of seconds when run from scheduler
+> ℹ️ **Info**: Copy relevant configuration from
+> [`global-config`](../global-config.rsc) (the one without `-overlay`) to
+> your local `global-config-overlay` and modify it to your specific needs.
+
Also notification settings are required for
[e-mail](mod/notification-email.md),
-[matrix](mod/notification-matrix.md) and/or
+[matrix](mod/notification-matrix.md),
+[ntfy](mod/notification-ntfy.md) and/or
[telegram](mod/notification-telegram.md).
Usage and invocation
@@ -60,5 +72,5 @@ See also
* [Upload backup to server](backup-upload.md)
---
-[◀ Go back to main README](../README.md)
-[▲ Go back to top](#top)
+[⬅️ Go back to main README](../README.md)
+[⬆️ Go back to top](#top)
diff --git a/doc/backup-email.md b/doc/backup-email.md
index ab2b9b5..56b0540 100644
--- a/doc/backup-email.md
+++ b/doc/backup-email.md
@@ -1,7 +1,14 @@
Send backup via e-mail
======================
-[◀ Go back to main README](../README.md)
+[![GitHub stars](https://img.shields.io/github/stars/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=red)](https://github.com/eworm-de/routeros-scripts/stargazers)
+[![GitHub forks](https://img.shields.io/github/forks/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=green)](https://github.com/eworm-de/routeros-scripts/network)
+[![GitHub watchers](https://img.shields.io/github/watchers/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=blue)](https://github.com/eworm-de/routeros-scripts/watchers)
+[![required RouterOS version](https://img.shields.io/badge/RouterOS-7.12-yellow?style=flat)](https://mikrotik.com/download/changelogs/)
+[![Telegram group @routeros_scripts](https://img.shields.io/badge/Telegram-%40routeros__scripts-%2326A5E4?logo=telegram&style=flat)](https://t.me/routeros_scripts)
+[![donate with PayPal](https://img.shields.io/badge/Like_it%3F-Donate!-orange?logo=githubsponsors&logoColor=orange&style=flat)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)
+
+[⬅️ Go back to main README](../README.md)
> ℹ️ **Info**: This script can not be used on its own but requires the base
> installation. See [main README](../README.md) for details.
@@ -29,9 +36,14 @@ The configuration goes to `global-config-overlay`, these are the parameters:
* `BackupSendBinary`: whether to send binary backup
* `BackupSendExport`: whether to send configuration export
+* `BackupSendGlobalConfig`: whether to send `global-config-overlay`
* `BackupPassword`: password to encrypt the backup with
* `BackupRandomDelay`: delay up to amount of seconds when run from scheduler
+> ℹ️ **Info**: Copy relevant configuration from
+> [`global-config`](../global-config.rsc) (the one without `-overlay`) to
+> your local `global-config-overlay` and modify it to your specific needs.
+
Usage and invocation
--------------------
@@ -52,5 +64,5 @@ See also
* [Upload backup to server](backup-upload.md)
---
-[◀ Go back to main README](../README.md)
-[▲ Go back to top](#top)
+[⬅️ Go back to main README](../README.md)
+[⬆️ Go back to top](#top)
diff --git a/doc/backup-partition.md b/doc/backup-partition.md
index b502330..e2ca8e0 100644
--- a/doc/backup-partition.md
+++ b/doc/backup-partition.md
@@ -1,7 +1,14 @@
Save configuration to fallback partition
========================================
-[◀ Go back to main README](../README.md)
+[![GitHub stars](https://img.shields.io/github/stars/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=red)](https://github.com/eworm-de/routeros-scripts/stargazers)
+[![GitHub forks](https://img.shields.io/github/forks/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=green)](https://github.com/eworm-de/routeros-scripts/network)
+[![GitHub watchers](https://img.shields.io/github/watchers/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=blue)](https://github.com/eworm-de/routeros-scripts/watchers)
+[![required RouterOS version](https://img.shields.io/badge/RouterOS-7.12-yellow?style=flat)](https://mikrotik.com/download/changelogs/)
+[![Telegram group @routeros_scripts](https://img.shields.io/badge/Telegram-%40routeros__scripts-%2326A5E4?logo=telegram&style=flat)](https://t.me/routeros_scripts)
+[![donate with PayPal](https://img.shields.io/badge/Like_it%3F-Donate!-orange?logo=githubsponsors&logoColor=orange&style=flat)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)
+
+[⬅️ Go back to main README](../README.md)
> ℹ️ **Info**: This script can not be used on its own but requires the base
> installation. See [main README](../README.md) for details.
@@ -15,6 +22,14 @@ This script saves the current configuration to fallback
For this to work you need a device with sufficient flash storage that is
properly partitioned.
+To make you aware of a possible issue a scheduler logging a warning is
+added in the backup partition's configuration. You may want to use
+[log-forward](log-forward.md) to be notified.
+
+> ⚠️ **Warning**: Only the configuration is saved to backup partition.
+> Every now and then you should copy your installation over for a recent
+> RouterOS version!
+
Requirements and installation
-----------------------------
@@ -39,7 +54,8 @@ See also
* [Upload backup to Mikrotik cloud](backup-cloud.md)
* [Send backup via e-mail](backup-email.md)
* [Upload backup to server](backup-upload.md)
+* [Forward log messages via notification](log-forward.md)
---
-[◀ Go back to main README](../README.md)
-[▲ Go back to top](#top)
+[⬅️ Go back to main README](../README.md)
+[⬆️ Go back to top](#top)
diff --git a/doc/backup-upload.d/notification.avif b/doc/backup-upload.d/notification.avif
new file mode 100644
index 0000000..83cfb18
--- /dev/null
+++ b/doc/backup-upload.d/notification.avif
Binary files differ
diff --git a/doc/backup-upload.d/notification.svg b/doc/backup-upload.d/notification.svg
deleted file mode 100644
index 73b34bd..0000000
--- a/doc/backup-upload.d/notification.svg
+++ /dev/null
@@ -1,45 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<svg id="svg8" width="443.78" height="263.78" version="1.1" viewBox="0 0 117.42 69.792" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
- <metadata id="metadata5">
- <rdf:RDF>
- <cc:Work rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <g id="layer1" transform="translate(16.375 11.083)">
- <rect id="rect857" x="-15.875" y="-10.583" width="116.42" height="68.792" rx="1.4552" fill="#e6e6e6" stroke="#6c5d53" stroke-linecap="round" stroke-linejoin="round"/>
- <g id="g884" transform="matrix(.5 0 0 .5 -12.406 -7.1146)" stroke-width="2">
- <path id="path899" d="m23.177 23.177c-2.9635 2.9635-17.991 2.9635-20.955 0-2.9635-2.9635-2.9635-17.991 0-20.955 2.9635-2.9635 17.991-2.9635 20.955 0 2.9635 2.9635 2.9635 17.991 0 20.955z" fill="#fff"/>
- <g id="g871">
- <g id="text837" stroke-width=".52916px" aria-label="#!rsc">
- <path id="path851" d="m7.4832 16.646v-1.0239h-0.54606l0.16577-1.2774h-1.2774l-0.16577 1.2774h-0.97511l0.16577-1.2774h-1.2774l-0.16577 1.2774h-0.70208v1.0239h0.56556l-0.24378 1.8722h-0.68257v1.0239h0.54606l-0.18527 1.3944h1.2774l0.18527-1.3944h0.97511l-0.18527 1.3944h1.2774l0.18527-1.3944h0.70208v-1.0239h-0.56556l0.24378-1.8722zm-2.2037 1.8722h-0.97511l0.24378-1.8722h0.97511z"/>
- <path id="path853" d="m9.6187 14.179h-1.6382l0.19502 4.271h1.2481zm-0.81909 5.1583c-0.48755 0-0.8776 0.39979-0.8776 0.8776 0 0.48755 0.39004 0.88735 0.8776 0.88735 0.4973 0 0.88735-0.39979 0.88735-0.88735 0-0.4778-0.39004-0.8776-0.88735-0.8776z"/>
- <path id="path855" d="m13.373 15.612c-0.59482 0-1.1019 0.42905-1.3359 1.1506l-0.13652-1.0044h-1.3359v5.1778h1.5407v-2.6035c0.17552-0.77033 0.4388-1.2286 1.0726-1.2286 0.16577 0 0.30228 0.02925 0.46805 0.06826l0.24378-1.4919c-0.17552-0.04875-0.32178-0.06826-0.51681-0.06826z"/>
- <path id="path857" d="m16.181 15.592c-1.3066 0-2.116 0.69233-2.116 1.5797 0 0.79959 0.50706 1.3261 1.5309 1.6187 0.9361 0.26328 1.0921 0.37054 1.0921 0.72158 0 0.31203-0.28278 0.48755-0.75083 0.48755-0.50706 0-0.98486-0.20477-1.3749-0.50706l-0.75083 0.83859c0.50706 0.4583 1.2676 0.77033 2.1647 0.77033 1.2871 0 2.3012-0.63382 2.3012-1.7064 0-0.92635-0.57531-1.3554-1.5992-1.6479-0.92635-0.27303-1.0629-0.39004-1.0629-0.66307 0-0.23402 0.20477-0.39004 0.62407-0.39004 0.44855 0 0.8776 0.14627 1.2774 0.39979l0.56556-0.86784c-0.4778-0.39004-1.1506-0.63382-1.9015-0.63382z"/>
- <path id="path859" d="m21.281 15.592c-1.5504 0-2.5353 1.1506-2.5353 2.7986 0 1.6382 0.97511 2.7108 2.5645 2.7108 0.71183 0 1.2676-0.23403 1.7454-0.61432l-0.67282-0.9556c-0.37054 0.23403-0.62407 0.35104-0.99461 0.35104-0.61432 0-1.0239-0.35104-1.0239-1.5017 0-1.1604 0.38029-1.6089 1.0434-1.6089 0.35104 0 0.65332 0.11701 0.98486 0.37054l0.66307-0.9166c-0.4973-0.4193-1.0531-0.63382-1.7747-0.63382z"/>
- </g>
- <g id="g1542" transform="matrix(2 0 0 2 -.41134 3.175)" fill="#676867" fill-rule="evenodd" stroke-width=".26458">
- <path id="path943" d="m4.9596-1.0196c0.40797 2.8312 1.9272 4.5499 5.0239 4.691-2.918 1.1164-5.9253-1.5076-5.0239-4.691"/>
- <path id="path945" d="m3.3407-0.52096c0.034969-0.00777 0.038854 0.015542 0.041445 0.041445 0.098431 1.8054 0.85998 3.1744 1.8689 4.1108 1.0089 0.93639 2.3636 1.6941 4.274 1.6604-3.5772 1.4247-7.337-1.9984-6.1856-5.8126"/>
- </g>
- </g>
- </g>
- <text id="text4811" transform="matrix(.26458 0 0 .26458 -44.95 -6.55)" x="-248.88142" fill="#000000" font-family="'Fira Mono', 'Roboto Mono', monospace" font-size="12px" letter-spacing="0px" stroke-width="1px" word-spacing="0px" style="font-variant-caps:normal;font-variant-east-asian:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:15px;shape-inside:url(#rect4813);white-space:pre" xml:space="preserve"><tspan id="tspan10771" x="180" y="10.85">[MikroTik] 💾⬆ Backup &amp; Config upload
-</tspan><tspan id="tspan10773" x="180" y="25.85">
-</tspan><tspan id="tspan10775" x="180" y="40.85">Backup and config export upload for MikroTik.
-</tspan><tspan id="tspan10777" x="180" y="55.85">
-</tspan><tspan id="tspan10779" x="180" y="70.85">Hostname: MikroTik
-</tspan><tspan id="tspan10781" x="180" y="85.85">Board name: CHR
-</tspan><tspan id="tspan10783" x="180" y="100.85">Architecture: x86_64
-</tspan><tspan id="tspan10785" x="180" y="115.85">RouterOS:
-</tspan><tspan id="tspan10787" x="180" y="130.85"> Channel: stable
-</tspan><tspan id="tspan10789" x="180" y="145.85"> Installed: 7.4.1
-</tspan><tspan id="tspan10791" x="180" y="160.85">RouterOS-Scripts:
-</tspan><tspan id="tspan10793" x="180" y="175.85"> Version: 83
-</tspan><tspan id="tspan10795" x="180" y="190.85">
-</tspan><tspan id="tspan10797" x="180" y="205.85">Backup file: MikroTik_example_com.backup
-</tspan><tspan id="tspan10799" x="180" y="220.85">Config file: MikroTik_example_com.rsc</tspan></text>
- </g>
-</svg>
diff --git a/doc/backup-upload.md b/doc/backup-upload.md
index 34df1c6..953ac93 100644
--- a/doc/backup-upload.md
+++ b/doc/backup-upload.md
@@ -1,7 +1,14 @@
Upload backup to server
=======================
-[◀ Go back to main README](../README.md)
+[![GitHub stars](https://img.shields.io/github/stars/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=red)](https://github.com/eworm-de/routeros-scripts/stargazers)
+[![GitHub forks](https://img.shields.io/github/forks/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=green)](https://github.com/eworm-de/routeros-scripts/network)
+[![GitHub watchers](https://img.shields.io/github/watchers/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=blue)](https://github.com/eworm-de/routeros-scripts/watchers)
+[![required RouterOS version](https://img.shields.io/badge/RouterOS-7.12-yellow?style=flat)](https://mikrotik.com/download/changelogs/)
+[![Telegram group @routeros_scripts](https://img.shields.io/badge/Telegram-%40routeros__scripts-%2326A5E4?logo=telegram&style=flat)](https://t.me/routeros_scripts)
+[![donate with PayPal](https://img.shields.io/badge/Like_it%3F-Donate!-orange?logo=githubsponsors&logoColor=orange&style=flat)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)
+
+[⬅️ Go back to main README](../README.md)
> ℹ️ **Info**: This script can not be used on its own but requires the base
> installation. See [main README](../README.md) for details.
@@ -19,7 +26,7 @@ configuration export (`/export terse show-sensitive`) to external server.
### Sample notification
-![backup-upload notification](backup-upload.d/notification.svg)
+![backup-upload notification](backup-upload.d/notification.avif)
Requirements and installation
-----------------------------
@@ -35,15 +42,21 @@ The configuration goes to `global-config-overlay`, these are the parameters:
* `BackupSendBinary`: whether to send binary backup
* `BackupSendExport`: whether to send configuration export
+* `BackupSendGlobalConfig`: whether to send `global-config-overlay`
* `BackupPassword`: password to encrypt the backup with
* `BackupRandomDelay`: delay up to amount of seconds when run from scheduler
* `BackupUploadUrl`: url to upload to
* `BackupUploadUser`: username for server authentication
* `BackupUploadPass`: password for server authentication
+> ℹ️ **Info**: Copy relevant configuration from
+> [`global-config`](../global-config.rsc) (the one without `-overlay`) to
+> your local `global-config-overlay` and modify it to your specific needs.
+
Also notification settings are required for
[e-mail](mod/notification-email.md),
-[matrix](mod/notification-matrix.md) and/or
+[matrix](mod/notification-matrix.md),
+[ntfy](mod/notification-ntfy.md) and/or
[telegram](mod/notification-telegram.md).
### Issues with SFTP client
@@ -75,5 +88,5 @@ See also
* [Save configuration to fallback partition](doc/backup-partition.md)
---
-[◀ Go back to main README](../README.md)
-[▲ Go back to top](#top)
+[⬅️ Go back to main README](../README.md)
+[⬆️ Go back to top](#top)
diff --git a/doc/capsman-download-packages.md b/doc/capsman-download-packages.md
index 0fdd6cb..20fb007 100644
--- a/doc/capsman-download-packages.md
+++ b/doc/capsman-download-packages.md
@@ -1,7 +1,14 @@
Download packages for CAP upgrade from CAPsMAN
=============================================
-[◀ Go back to main README](../README.md)
+[![GitHub stars](https://img.shields.io/github/stars/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=red)](https://github.com/eworm-de/routeros-scripts/stargazers)
+[![GitHub forks](https://img.shields.io/github/forks/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=green)](https://github.com/eworm-de/routeros-scripts/network)
+[![GitHub watchers](https://img.shields.io/github/watchers/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=blue)](https://github.com/eworm-de/routeros-scripts/watchers)
+[![required RouterOS version](https://img.shields.io/badge/RouterOS-7.12-yellow?style=flat)](https://mikrotik.com/download/changelogs/)
+[![Telegram group @routeros_scripts](https://img.shields.io/badge/Telegram-%40routeros__scripts-%2326A5E4?logo=telegram&style=flat)](https://t.me/routeros_scripts)
+[![donate with PayPal](https://img.shields.io/badge/Like_it%3F-Donate!-orange?logo=githubsponsors&logoColor=orange&style=flat)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)
+
+[⬅️ Go back to main README](../README.md)
> ℹ️ **Info**: This script can not be used on its own but requires the base
> installation. See [main README](../README.md) for details.
@@ -18,24 +25,47 @@ This script automatically downloads these packages.
Requirements and installation
-----------------------------
-Just install the script on CAPsMAN device:
+Make sure you have the `package-path` set in your CAPsMAN configuration,
+as that is where packages are downloaded to and where the system expects
+them.
+
+Then just install the script on CAPsMAN device.
+Depending on whether you use `wifi` package (`/interface/wifi`) or legacy
+wifi with CAPsMAN (`/caps-man`) you need to install a different script.
+
+For `wifi`:
+
+ $ScriptInstallUpdate capsman-download-packages.wifi;
+
+For legacy CAPsMAN:
- $ScriptInstallUpdate capsman-download-packages;
+ $ScriptInstallUpdate capsman-download-packages.capsman;
-Optionally add a scheduler to run after startup:
+Optionally add a scheduler to run after startup. For `wifi`:
- /system/scheduler/add name=capsman-download-packages on-event="/system/script/run capsman-download-packages;" start-time=startup;
+ /system/scheduler/add name=capsman-download-packages on-event="/system/script/run capsman-download-packages.wifi;" start-time=startup;
+
+For legacy CAPsMAN:
+
+ /system/scheduler/add name=capsman-download-packages on-event="/system/script/run capsman-download-packages.capsman;" start-time=startup;
Packages available in local storage in older version are downloaded
-unconditionally. The script tries to download missing packages by guessing
-from system log.
+unconditionally.
+
+If no packages are found the script downloads a default set of packages:
+
+ * `wifi`: `routeros` and `wifi-qcom` for *arm* and *arm64*, `wifi-qcom-ac` for *arm*
+ * legacy CAPsMAN: `routeros` and `wireless` for *arm* and *mipsbe*
+
+> ℹ️ **Info**: If you have packages in the directory and things go wrong for
+> what ever unknown reason: Remove **all** packages and start over.
Usage and invocation
--------------------
Run the script manually:
- /system/script/run capsman-download-packages;
+ /system/script/run capsman-download-packages.wifi;
... or from scheduler.
@@ -49,5 +79,5 @@ See also
* [Run rolling CAP upgrades from CAPsMAN](capsman-rolling-upgrade.md)
---
-[◀ Go back to main README](../README.md)
-[▲ Go back to top](#top)
+[⬅️ Go back to main README](../README.md)
+[⬆️ Go back to top](#top)
diff --git a/doc/capsman-rolling-upgrade.md b/doc/capsman-rolling-upgrade.md
index 94a2a79..8362794 100644
--- a/doc/capsman-rolling-upgrade.md
+++ b/doc/capsman-rolling-upgrade.md
@@ -1,7 +1,14 @@
Run rolling CAP upgrades from CAPsMAN
=====================================
-[◀ Go back to main README](../README.md)
+[![GitHub stars](https://img.shields.io/github/stars/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=red)](https://github.com/eworm-de/routeros-scripts/stargazers)
+[![GitHub forks](https://img.shields.io/github/forks/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=green)](https://github.com/eworm-de/routeros-scripts/network)
+[![GitHub watchers](https://img.shields.io/github/watchers/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=blue)](https://github.com/eworm-de/routeros-scripts/watchers)
+[![required RouterOS version](https://img.shields.io/badge/RouterOS-7.12-yellow?style=flat)](https://mikrotik.com/download/changelogs/)
+[![Telegram group @routeros_scripts](https://img.shields.io/badge/Telegram-%40routeros__scripts-%2326A5E4?logo=telegram&style=flat)](https://t.me/routeros_scripts)
+[![donate with PayPal](https://img.shields.io/badge/Like_it%3F-Donate!-orange?logo=githubsponsors&logoColor=orange&style=flat)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)
+
+[⬅️ Go back to main README](../README.md)
> ℹ️ **Info**: This script can not be used on its own but requires the base
> installation. See [main README](../README.md) for details.
@@ -20,9 +27,17 @@ parallel.
Requirements and installation
-----------------------------
-Just install the script:
+Just install the script on CAPsMAN device.
+Depending on whether you use `wifi` package (`/interface/wifi`) or legacy
+wifi with CAPsMAN (`/caps-man`) you need to install a different script.
+
+For `wifi`:
+
+ $ScriptInstallUpdate capsman-rolling-upgrade.wifi;
+
+For legacy CAPsMAN:
- $ScriptInstallUpdate capsman-rolling-upgrade;
+ $ScriptInstallUpdate capsman-rolling-upgrade.capsman;
Usage and invocation
--------------------
@@ -33,7 +48,7 @@ that script when required.
Alternatively run it manually:
- /system/script/run capsman-rolling-upgrade;
+ /system/script/run capsman-rolling-upgrade.wifi;
See also
--------
@@ -41,5 +56,5 @@ See also
* [Download packages for CAP upgrade from CAPsMAN](capsman-download-packages.md)
---
-[◀ Go back to main README](../README.md)
-[▲ Go back to top](#top)
+[⬅️ Go back to main README](../README.md)
+[⬆️ Go back to top](#top)
diff --git a/doc/certificate-renew-issued.md b/doc/certificate-renew-issued.md
index d8201a7..2df8be3 100644
--- a/doc/certificate-renew-issued.md
+++ b/doc/certificate-renew-issued.md
@@ -1,7 +1,14 @@
Renew locally issued certificates
=================================
-[◀ Go back to main README](../README.md)
+[![GitHub stars](https://img.shields.io/github/stars/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=red)](https://github.com/eworm-de/routeros-scripts/stargazers)
+[![GitHub forks](https://img.shields.io/github/forks/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=green)](https://github.com/eworm-de/routeros-scripts/network)
+[![GitHub watchers](https://img.shields.io/github/watchers/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=blue)](https://github.com/eworm-de/routeros-scripts/watchers)
+[![required RouterOS version](https://img.shields.io/badge/RouterOS-7.12-yellow?style=flat)](https://mikrotik.com/download/changelogs/)
+[![Telegram group @routeros_scripts](https://img.shields.io/badge/Telegram-%40routeros__scripts-%2326A5E4?logo=telegram&style=flat)](https://t.me/routeros_scripts)
+[![donate with PayPal](https://img.shields.io/badge/Like_it%3F-Donate!-orange?logo=githubsponsors&logoColor=orange&style=flat)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)
+
+[⬅️ Go back to main README](../README.md)
> ℹ️ **Info**: This script can not be used on its own but requires the base
> installation. See [main README](../README.md) for details.
@@ -28,6 +35,10 @@ parameter:
* `CertRenewPass`: an array holding individual passphrases for certificates
+> ℹ️ **Info**: Copy relevant configuration from
+> [`global-config`](../global-config.rsc) (the one without `-overlay`) to
+> your local `global-config-overlay` and modify it to your specific needs.
+
Usage and invocation
--------------------
@@ -46,5 +57,5 @@ See also
* [Renew certificates and notify on expiration](check-certificates.md)
---
-[◀ Go back to main README](../README.md)
-[▲ Go back to top](#top)
+[⬅️ Go back to main README](../README.md)
+[⬆️ Go back to top](#top)
diff --git a/doc/check-certificates.d/notification.avif b/doc/check-certificates.d/notification.avif
new file mode 100644
index 0000000..7c250da
--- /dev/null
+++ b/doc/check-certificates.d/notification.avif
Binary files differ
diff --git a/doc/check-certificates.d/notification.svg b/doc/check-certificates.d/notification.svg
deleted file mode 100644
index e1d8baa..0000000
--- a/doc/check-certificates.d/notification.svg
+++ /dev/null
@@ -1,41 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<svg id="svg8" width="683.78" height="203.78" version="1.1" viewBox="0 0 180.92 53.917" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
- <metadata id="metadata5">
- <rdf:RDF>
- <cc:Work rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <g id="layer1" transform="translate(16.375 11.083)">
- <rect id="rect857" x="-15.875" y="-10.583" width="179.92" height="52.917" rx="2.249" fill="#e6e6e6" stroke="#6c5d53" stroke-linecap="round" stroke-linejoin="round"/>
- <g id="g884" transform="matrix(.5 0 0 .5 -12.406 -7.1146)" stroke-width="2">
- <path id="path899" d="m23.177 23.177c-2.9635 2.9635-17.991 2.9635-20.955 0-2.9635-2.9635-2.9635-17.991 0-20.955 2.9635-2.9635 17.991-2.9635 20.955 0 2.9635 2.9635 2.9635 17.991 0 20.955z" fill="#fff"/>
- <g id="g871">
- <g id="text837" stroke-width=".52916px" aria-label="#!rsc">
- <path id="path851" d="m7.4832 16.646v-1.0239h-0.54606l0.16577-1.2774h-1.2774l-0.16577 1.2774h-0.97511l0.16577-1.2774h-1.2774l-0.16577 1.2774h-0.70208v1.0239h0.56556l-0.24378 1.8722h-0.68257v1.0239h0.54606l-0.18527 1.3944h1.2774l0.18527-1.3944h0.97511l-0.18527 1.3944h1.2774l0.18527-1.3944h0.70208v-1.0239h-0.56556l0.24378-1.8722zm-2.2037 1.8722h-0.97511l0.24378-1.8722h0.97511z"/>
- <path id="path853" d="m9.6187 14.179h-1.6382l0.19502 4.271h1.2481zm-0.81909 5.1583c-0.48755 0-0.8776 0.39979-0.8776 0.8776 0 0.48755 0.39004 0.88735 0.8776 0.88735 0.4973 0 0.88735-0.39979 0.88735-0.88735 0-0.4778-0.39004-0.8776-0.88735-0.8776z"/>
- <path id="path855" d="m13.373 15.612c-0.59482 0-1.1019 0.42905-1.3359 1.1506l-0.13652-1.0044h-1.3359v5.1778h1.5407v-2.6035c0.17552-0.77033 0.4388-1.2286 1.0726-1.2286 0.16577 0 0.30228 0.02925 0.46805 0.06826l0.24378-1.4919c-0.17552-0.04875-0.32178-0.06826-0.51681-0.06826z"/>
- <path id="path857" d="m16.181 15.592c-1.3066 0-2.116 0.69233-2.116 1.5797 0 0.79959 0.50706 1.3261 1.5309 1.6187 0.9361 0.26328 1.0921 0.37054 1.0921 0.72158 0 0.31203-0.28278 0.48755-0.75083 0.48755-0.50706 0-0.98486-0.20477-1.3749-0.50706l-0.75083 0.83859c0.50706 0.4583 1.2676 0.77033 2.1647 0.77033 1.2871 0 2.3012-0.63382 2.3012-1.7064 0-0.92635-0.57531-1.3554-1.5992-1.6479-0.92635-0.27303-1.0629-0.39004-1.0629-0.66307 0-0.23402 0.20477-0.39004 0.62407-0.39004 0.44855 0 0.8776 0.14627 1.2774 0.39979l0.56556-0.86784c-0.4778-0.39004-1.1506-0.63382-1.9015-0.63382z"/>
- <path id="path859" d="m21.281 15.592c-1.5504 0-2.5353 1.1506-2.5353 2.7986 0 1.6382 0.97511 2.7108 2.5645 2.7108 0.71183 0 1.2676-0.23403 1.7454-0.61432l-0.67282-0.9556c-0.37054 0.23403-0.62407 0.35104-0.99461 0.35104-0.61432 0-1.0239-0.35104-1.0239-1.5017 0-1.1604 0.38029-1.6089 1.0434-1.6089 0.35104 0 0.65332 0.11701 0.98486 0.37054l0.66307-0.9166c-0.4973-0.4193-1.0531-0.63382-1.7747-0.63382z"/>
- </g>
- <g id="g1542" transform="matrix(2 0 0 2 -.41134 3.175)" fill="#676867" fill-rule="evenodd" stroke-width=".26458">
- <path id="path943" d="m4.9596-1.0196c0.40797 2.8312 1.9272 4.5499 5.0239 4.691-2.918 1.1164-5.9253-1.5076-5.0239-4.691"/>
- <path id="path945" d="m3.3407-0.52096c0.034969-0.00777 0.038854 0.015542 0.041445 0.041445 0.098431 1.8054 0.85998 3.1744 1.8689 4.1108 1.0089 0.93639 2.3636 1.6941 4.274 1.6604-3.5772 1.4247-7.337-1.9984-6.1856-5.8126"/>
- </g>
- </g>
- </g>
- <text id="text4811" transform="matrix(.26458 0 0 .26458 -45.014 -6.6039)" x="-248.88142" fill="#000000" font-family="'Fira Mono', 'Roboto Mono', monospace" font-size="12px" letter-spacing="0px" stroke-width="1px" word-spacing="0px" style="font-variant-caps:normal;font-variant-east-asian:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:15px;shape-inside:url(#rect4813);white-space:pre" xml:space="preserve"><tspan id="tspan2179" x="180" y="10.85">[MikroTik] 🔏 Certificate renewed
-</tspan><tspan id="tspan2181" x="180" y="25.85">
-</tspan><tspan id="tspan2183" x="180" y="40.85">A certificate on MikroTik has been renewed.
-</tspan><tspan id="tspan2185" x="180" y="55.85">
-</tspan><tspan id="tspan2187" x="180" y="70.85">Name: example.com
-</tspan><tspan id="tspan2189" x="180" y="85.85">CommonName: example.com
-</tspan><tspan id="tspan2191" x="180" y="100.85">Private key: available
-</tspan><tspan id="tspan2193" x="180" y="115.85">Fingerprint: cc54cdd01fcd7698ecb71213874be776906eb33d26cd57754d168632f14c4c8b
-</tspan><tspan id="tspan2195" x="180" y="130.85">Issuer: R3
-</tspan><tspan id="tspan2197" x="180" y="145.85">Validity: may/22/2021 22:29:34 to aug/20/2021 22:29:34
-</tspan><tspan id="tspan2199" x="180" y="160.85">Expires in: 11w 5d 08:18:06</tspan></text>
- </g>
-</svg>
diff --git a/doc/check-certificates.md b/doc/check-certificates.md
index 5198e52..62b9ceb 100644
--- a/doc/check-certificates.md
+++ b/doc/check-certificates.md
@@ -1,7 +1,14 @@
Renew certificates and notify on expiration
===========================================
-[◀ Go back to main README](../README.md)
+[![GitHub stars](https://img.shields.io/github/stars/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=red)](https://github.com/eworm-de/routeros-scripts/stargazers)
+[![GitHub forks](https://img.shields.io/github/forks/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=green)](https://github.com/eworm-de/routeros-scripts/network)
+[![GitHub watchers](https://img.shields.io/github/watchers/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=blue)](https://github.com/eworm-de/routeros-scripts/watchers)
+[![required RouterOS version](https://img.shields.io/badge/RouterOS-7.12-yellow?style=flat)](https://mikrotik.com/download/changelogs/)
+[![Telegram group @routeros_scripts](https://img.shields.io/badge/Telegram-%40routeros__scripts-%2326A5E4?logo=telegram&style=flat)](https://t.me/routeros_scripts)
+[![donate with PayPal](https://img.shields.io/badge/Like_it%3F-Donate!-orange?logo=githubsponsors&logoColor=orange&style=flat)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)
+
+[⬅️ Go back to main README](../README.md)
> ℹ️ **Info**: This script can not be used on its own but requires the base
> installation. See [main README](../README.md) for details.
@@ -14,7 +21,7 @@ certificates that are still about to expire.
### Sample notification
-![check-certificates notification](check-certificates.d/notification.svg)
+![check-certificates notification](check-certificates.d/notification.avif)
Requirements and installation
-----------------------------
@@ -30,14 +37,22 @@ For automatic download and renewal of certificates you need configuration
in `global-config-overlay`, these are the parameters:
* `CertRenewPass`: an array of passphrases to try
+* `CertRenewTime`: on what remaining time to try a renew
* `CertRenewUrl`: the url to download certificates from
+* `CertWarnTime`: on what remaining time to warn via notification
+
+> ℹ️ **Info**: Copy relevant configuration from
+> [`global-config`](../global-config.rsc) (the one without `-overlay`) to
+> your local `global-config-overlay` and modify it to your specific needs.
-Certificates on the web server should be named `CN.pem` (`PEM` format) or
-`CN.p12` (`PKCS#12` format).
+Certificates on the web server should be named by their common name, like
+`CN.pem` (`PEM` format) or`CN.p12` (`PKCS#12` format). Alternatively any
+subject alternative name (aka *Subject Alt Name* or *SAN*) can be used.
Also notification settings are required for
[e-mail](mod/notification-email.md),
-[matrix](mod/notification-matrix.md) and/or
+[matrix](mod/notification-matrix.md),
+[ntfy](mod/notification-ntfy.md) and/or
[telegram](mod/notification-telegram.md).
Usage and invocation
@@ -51,9 +66,26 @@ Just run the script:
/system/scheduler/add interval=1d name=check-certificates on-event="/system/script/run check-certificates;" start-time=startup;
-Alternatively running on startup may be desired:
- /system/scheduler/add name=check-certificates-startup on-event="/system/script/run check-certificates;" start-time=startup;
+Tips & Tricks
+-------------
+
+### Schedule at startup
+
+The script checks for full connectivity before acting, so scheduling at
+startup is perfectly valid:
+
+ /system/scheduler/add name=check-certificates@startup on-event="/system/script/run check-certificates;" start-time=startup;
+
+### Initial import
+
+Given you have a certificate on you server, you can use `check-certificates`
+for the initial import. Just create a *dummy* certificate with short lifetime
+that matches criteria to be renewed:
+
+ /certificate/add name=example.com common-name=example.com days-valid=1;
+ /certificate/sign example.com;
+ /system/script/run check-certificates;
See also
--------
@@ -61,5 +93,5 @@ See also
* [Renew locally issued certificates](certificate-renew-issued.md)
---
-[◀ Go back to main README](../README.md)
-[▲ Go back to top](#top)
+[⬅️ Go back to main README](../README.md)
+[⬆️ Go back to top](#top)
diff --git a/doc/check-health.d/notification-01-cpu-utilization-high.avif b/doc/check-health.d/notification-01-cpu-utilization-high.avif
new file mode 100644
index 0000000..326e7fe
--- /dev/null
+++ b/doc/check-health.d/notification-01-cpu-utilization-high.avif
Binary files differ
diff --git a/doc/check-health.d/notification-01-voltage.svg b/doc/check-health.d/notification-01-voltage.svg
deleted file mode 100644
index 4c9caad..0000000
--- a/doc/check-health.d/notification-01-voltage.svg
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<svg id="svg8" width="443.78" height="123.78" version="1.1" viewBox="0 0 117.42 32.75" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
- <metadata id="metadata5">
- <rdf:RDF>
- <cc:Work rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <g id="layer1" transform="translate(16.375 11.083)">
- <rect id="rect857" x="-15.875" y="-10.583" width="116.42" height="31.75" rx="1.4552" fill="#e6e6e6" stroke="#6c5d53" stroke-linecap="round" stroke-linejoin="round"/>
- <g id="g884" transform="matrix(.5 0 0 .5 -12.406 -7.1146)" stroke-width="2">
- <path id="path899" d="m23.177 23.177c-2.9635 2.9635-17.991 2.9635-20.955 0-2.9635-2.9635-2.9635-17.991 0-20.955 2.9635-2.9635 17.991-2.9635 20.955 0 2.9635 2.9635 2.9635 17.991 0 20.955z" fill="#fff"/>
- <g id="g871">
- <g id="text837" stroke-width=".52916px" aria-label="#!rsc">
- <path id="path851" d="m7.4832 16.646v-1.0239h-0.54606l0.16577-1.2774h-1.2774l-0.16577 1.2774h-0.97511l0.16577-1.2774h-1.2774l-0.16577 1.2774h-0.70208v1.0239h0.56556l-0.24378 1.8722h-0.68257v1.0239h0.54606l-0.18527 1.3944h1.2774l0.18527-1.3944h0.97511l-0.18527 1.3944h1.2774l0.18527-1.3944h0.70208v-1.0239h-0.56556l0.24378-1.8722zm-2.2037 1.8722h-0.97511l0.24378-1.8722h0.97511z"/>
- <path id="path853" d="m9.6187 14.179h-1.6382l0.19502 4.271h1.2481zm-0.81909 5.1583c-0.48755 0-0.8776 0.39979-0.8776 0.8776 0 0.48755 0.39004 0.88735 0.8776 0.88735 0.4973 0 0.88735-0.39979 0.88735-0.88735 0-0.4778-0.39004-0.8776-0.88735-0.8776z"/>
- <path id="path855" d="m13.373 15.612c-0.59482 0-1.1019 0.42905-1.3359 1.1506l-0.13652-1.0044h-1.3359v5.1778h1.5407v-2.6035c0.17552-0.77033 0.4388-1.2286 1.0726-1.2286 0.16577 0 0.30228 0.02925 0.46805 0.06826l0.24378-1.4919c-0.17552-0.04875-0.32178-0.06826-0.51681-0.06826z"/>
- <path id="path857" d="m16.181 15.592c-1.3066 0-2.116 0.69233-2.116 1.5797 0 0.79959 0.50706 1.3261 1.5309 1.6187 0.9361 0.26328 1.0921 0.37054 1.0921 0.72158 0 0.31203-0.28278 0.48755-0.75083 0.48755-0.50706 0-0.98486-0.20477-1.3749-0.50706l-0.75083 0.83859c0.50706 0.4583 1.2676 0.77033 2.1647 0.77033 1.2871 0 2.3012-0.63382 2.3012-1.7064 0-0.92635-0.57531-1.3554-1.5992-1.6479-0.92635-0.27303-1.0629-0.39004-1.0629-0.66307 0-0.23402 0.20477-0.39004 0.62407-0.39004 0.44855 0 0.8776 0.14627 1.2774 0.39979l0.56556-0.86784c-0.4778-0.39004-1.1506-0.63382-1.9015-0.63382z"/>
- <path id="path859" d="m21.281 15.592c-1.5504 0-2.5353 1.1506-2.5353 2.7986 0 1.6382 0.97511 2.7108 2.5645 2.7108 0.71183 0 1.2676-0.23403 1.7454-0.61432l-0.67282-0.9556c-0.37054 0.23403-0.62407 0.35104-0.99461 0.35104-0.61432 0-1.0239-0.35104-1.0239-1.5017 0-1.1604 0.38029-1.6089 1.0434-1.6089 0.35104 0 0.65332 0.11701 0.98486 0.37054l0.66307-0.9166c-0.4973-0.4193-1.0531-0.63382-1.7747-0.63382z"/>
- </g>
- <g id="g1542" transform="matrix(2 0 0 2 -.41134 3.175)" fill="#676867" fill-rule="evenodd" stroke-width=".26458">
- <path id="path943" d="m4.9596-1.0196c0.40797 2.8312 1.9272 4.5499 5.0239 4.691-2.918 1.1164-5.9253-1.5076-5.0239-4.691"/>
- <path id="path945" d="m3.3407-0.52096c0.034969-0.00777 0.038854 0.015542 0.041445 0.041445 0.098431 1.8054 0.85998 3.1744 1.8689 4.1108 1.0089 0.93639 2.3636 1.6941 4.274 1.6604-3.5772 1.4247-7.337-1.9984-6.1856-5.8126"/>
- </g>
- </g>
- </g>
- <text id="text4811" transform="matrix(.26458 0 0 .26458 -45.077 -6.6039)" x="-248.88142" fill="#000000" font-family="'Fira Mono', 'Roboto Mono', monospace" font-size="12px" letter-spacing="0px" stroke-width="1px" word-spacing="0px" style="font-variant-caps:normal;font-variant-east-asian:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:15px;shape-inside:url(#rect4813);white-space:pre" xml:space="preserve"><tspan id="tspan13643" x="180" y="10.85">[MikroTik] ⚡📉 Health warning: voltage
-</tspan><tspan id="tspan13645" x="180" y="25.85">
-</tspan><tspan id="tspan13647" x="180" y="40.85">The voltage on MikroTik jumped more than 10%.
-</tspan><tspan id="tspan13649" x="180" y="55.85">
-</tspan><tspan id="tspan13651" x="180" y="70.85">old value: 16.2V
-</tspan><tspan id="tspan13653" x="180" y="85.85">new value: 12.4V</tspan></text>
- </g>
-</svg>
diff --git a/doc/check-health.d/notification-02-cpu-utilization-ok.avif b/doc/check-health.d/notification-02-cpu-utilization-ok.avif
new file mode 100644
index 0000000..811ccd7
--- /dev/null
+++ b/doc/check-health.d/notification-02-cpu-utilization-ok.avif
Binary files differ
diff --git a/doc/check-health.d/notification-02-temperature-high.svg b/doc/check-health.d/notification-02-temperature-high.svg
deleted file mode 100644
index 0ab9488..0000000
--- a/doc/check-health.d/notification-02-temperature-high.svg
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<svg id="svg8" width="503.78" height="83.78" version="1.1" viewBox="0 0 133.29 22.167" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
- <metadata id="metadata5">
- <rdf:RDF>
- <cc:Work rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <g id="layer1" transform="translate(16.375 11.083)">
- <rect id="rect857" x="-15.875" y="-10.583" width="132.29" height="21.167" rx="1.6536" fill="#e6e6e6" stroke="#6c5d53" stroke-linecap="round" stroke-linejoin="round"/>
- <g id="g884" transform="matrix(.5 0 0 .5 -12.406 -7.1146)" stroke-width="2">
- <path id="path899" d="m23.177 23.177c-2.9635 2.9635-17.991 2.9635-20.955 0-2.9635-2.9635-2.9635-17.991 0-20.955 2.9635-2.9635 17.991-2.9635 20.955 0 2.9635 2.9635 2.9635 17.991 0 20.955z" fill="#fff"/>
- <g id="g871">
- <g id="text837" stroke-width=".52916px" aria-label="#!rsc">
- <path id="path851" d="m7.4832 16.646v-1.0239h-0.54606l0.16577-1.2774h-1.2774l-0.16577 1.2774h-0.97511l0.16577-1.2774h-1.2774l-0.16577 1.2774h-0.70208v1.0239h0.56556l-0.24378 1.8722h-0.68257v1.0239h0.54606l-0.18527 1.3944h1.2774l0.18527-1.3944h0.97511l-0.18527 1.3944h1.2774l0.18527-1.3944h0.70208v-1.0239h-0.56556l0.24378-1.8722zm-2.2037 1.8722h-0.97511l0.24378-1.8722h0.97511z"/>
- <path id="path853" d="m9.6187 14.179h-1.6382l0.19502 4.271h1.2481zm-0.81909 5.1583c-0.48755 0-0.8776 0.39979-0.8776 0.8776 0 0.48755 0.39004 0.88735 0.8776 0.88735 0.4973 0 0.88735-0.39979 0.88735-0.88735 0-0.4778-0.39004-0.8776-0.88735-0.8776z"/>
- <path id="path855" d="m13.373 15.612c-0.59482 0-1.1019 0.42905-1.3359 1.1506l-0.13652-1.0044h-1.3359v5.1778h1.5407v-2.6035c0.17552-0.77033 0.4388-1.2286 1.0726-1.2286 0.16577 0 0.30228 0.02925 0.46805 0.06826l0.24378-1.4919c-0.17552-0.04875-0.32178-0.06826-0.51681-0.06826z"/>
- <path id="path857" d="m16.181 15.592c-1.3066 0-2.116 0.69233-2.116 1.5797 0 0.79959 0.50706 1.3261 1.5309 1.6187 0.9361 0.26328 1.0921 0.37054 1.0921 0.72158 0 0.31203-0.28278 0.48755-0.75083 0.48755-0.50706 0-0.98486-0.20477-1.3749-0.50706l-0.75083 0.83859c0.50706 0.4583 1.2676 0.77033 2.1647 0.77033 1.2871 0 2.3012-0.63382 2.3012-1.7064 0-0.92635-0.57531-1.3554-1.5992-1.6479-0.92635-0.27303-1.0629-0.39004-1.0629-0.66307 0-0.23402 0.20477-0.39004 0.62407-0.39004 0.44855 0 0.8776 0.14627 1.2774 0.39979l0.56556-0.86784c-0.4778-0.39004-1.1506-0.63382-1.9015-0.63382z"/>
- <path id="path859" d="m21.281 15.592c-1.5504 0-2.5353 1.1506-2.5353 2.7986 0 1.6382 0.97511 2.7108 2.5645 2.7108 0.71183 0 1.2676-0.23403 1.7454-0.61432l-0.67282-0.9556c-0.37054 0.23403-0.62407 0.35104-0.99461 0.35104-0.61432 0-1.0239-0.35104-1.0239-1.5017 0-1.1604 0.38029-1.6089 1.0434-1.6089 0.35104 0 0.65332 0.11701 0.98486 0.37054l0.66307-0.9166c-0.4973-0.4193-1.0531-0.63382-1.7747-0.63382z"/>
- </g>
- <g id="g1542" transform="matrix(2 0 0 2 -.41134 3.175)" fill="#676867" fill-rule="evenodd" stroke-width=".26458">
- <path id="path943" d="m4.9596-1.0196c0.40797 2.8312 1.9272 4.5499 5.0239 4.691-2.918 1.1164-5.9253-1.5076-5.0239-4.691"/>
- <path id="path945" d="m3.3407-0.52096c0.034969-0.00777 0.038854 0.015542 0.041445 0.041445 0.098431 1.8054 0.85998 3.1744 1.8689 4.1108 1.0089 0.93639 2.3636 1.6941 4.274 1.6604-3.5772 1.4247-7.337-1.9984-6.1856-5.8126"/>
- </g>
- </g>
- </g>
- <text id="text4811" transform="matrix(.26458 0 0 .26458 -45.077 -6.6039)" x="-248.88142" fill="#000000" font-family="'Fira Mono', 'Roboto Mono', monospace" font-size="12px" letter-spacing="0px" stroke-width="1px" word-spacing="0px" style="font-variant-caps:normal;font-variant-east-asian:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:15px;shape-inside:url(#rect4813);white-space:pre" xml:space="preserve"><tspan id="tspan13709" x="180" y="10.85">[MikroTik] 🔥 Health warning: temperature
-</tspan><tspan id="tspan13711" x="180" y="25.85">
-</tspan><tspan id="tspan13713" x="180" y="40.85">The temperature on MikroTik is above threshold: 51°C</tspan></text>
- </g>
-</svg>
diff --git a/doc/check-health.d/notification-03-ram-utilization-high.avif b/doc/check-health.d/notification-03-ram-utilization-high.avif
new file mode 100644
index 0000000..59155c5
--- /dev/null
+++ b/doc/check-health.d/notification-03-ram-utilization-high.avif
Binary files differ
diff --git a/doc/check-health.d/notification-03-temperature-ok.svg b/doc/check-health.d/notification-03-temperature-ok.svg
deleted file mode 100644
index 180d13f..0000000
--- a/doc/check-health.d/notification-03-temperature-ok.svg
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<svg id="svg8" width="533.78" height="83.78" version="1.1" viewBox="0 0 141.23 22.167" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
- <metadata id="metadata5">
- <rdf:RDF>
- <cc:Work rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <g id="layer1" transform="translate(16.375 11.083)">
- <rect id="rect857" x="-15.875" y="-10.583" width="140.23" height="21.167" rx="1.7529" fill="#e6e6e6" stroke="#6c5d53" stroke-linecap="round" stroke-linejoin="round"/>
- <g id="g884" transform="matrix(.5 0 0 .5 -12.406 -7.1146)" stroke-width="2">
- <path id="path899" d="m23.177 23.177c-2.9635 2.9635-17.991 2.9635-20.955 0-2.9635-2.9635-2.9635-17.991 0-20.955 2.9635-2.9635 17.991-2.9635 20.955 0 2.9635 2.9635 2.9635 17.991 0 20.955z" fill="#fff"/>
- <g id="g871">
- <g id="text837" stroke-width=".52916px" aria-label="#!rsc">
- <path id="path851" d="m7.4832 16.646v-1.0239h-0.54606l0.16577-1.2774h-1.2774l-0.16577 1.2774h-0.97511l0.16577-1.2774h-1.2774l-0.16577 1.2774h-0.70208v1.0239h0.56556l-0.24378 1.8722h-0.68257v1.0239h0.54606l-0.18527 1.3944h1.2774l0.18527-1.3944h0.97511l-0.18527 1.3944h1.2774l0.18527-1.3944h0.70208v-1.0239h-0.56556l0.24378-1.8722zm-2.2037 1.8722h-0.97511l0.24378-1.8722h0.97511z"/>
- <path id="path853" d="m9.6187 14.179h-1.6382l0.19502 4.271h1.2481zm-0.81909 5.1583c-0.48755 0-0.8776 0.39979-0.8776 0.8776 0 0.48755 0.39004 0.88735 0.8776 0.88735 0.4973 0 0.88735-0.39979 0.88735-0.88735 0-0.4778-0.39004-0.8776-0.88735-0.8776z"/>
- <path id="path855" d="m13.373 15.612c-0.59482 0-1.1019 0.42905-1.3359 1.1506l-0.13652-1.0044h-1.3359v5.1778h1.5407v-2.6035c0.17552-0.77033 0.4388-1.2286 1.0726-1.2286 0.16577 0 0.30228 0.02925 0.46805 0.06826l0.24378-1.4919c-0.17552-0.04875-0.32178-0.06826-0.51681-0.06826z"/>
- <path id="path857" d="m16.181 15.592c-1.3066 0-2.116 0.69233-2.116 1.5797 0 0.79959 0.50706 1.3261 1.5309 1.6187 0.9361 0.26328 1.0921 0.37054 1.0921 0.72158 0 0.31203-0.28278 0.48755-0.75083 0.48755-0.50706 0-0.98486-0.20477-1.3749-0.50706l-0.75083 0.83859c0.50706 0.4583 1.2676 0.77033 2.1647 0.77033 1.2871 0 2.3012-0.63382 2.3012-1.7064 0-0.92635-0.57531-1.3554-1.5992-1.6479-0.92635-0.27303-1.0629-0.39004-1.0629-0.66307 0-0.23402 0.20477-0.39004 0.62407-0.39004 0.44855 0 0.8776 0.14627 1.2774 0.39979l0.56556-0.86784c-0.4778-0.39004-1.1506-0.63382-1.9015-0.63382z"/>
- <path id="path859" d="m21.281 15.592c-1.5504 0-2.5353 1.1506-2.5353 2.7986 0 1.6382 0.97511 2.7108 2.5645 2.7108 0.71183 0 1.2676-0.23403 1.7454-0.61432l-0.67282-0.9556c-0.37054 0.23403-0.62407 0.35104-0.99461 0.35104-0.61432 0-1.0239-0.35104-1.0239-1.5017 0-1.1604 0.38029-1.6089 1.0434-1.6089 0.35104 0 0.65332 0.11701 0.98486 0.37054l0.66307-0.9166c-0.4973-0.4193-1.0531-0.63382-1.7747-0.63382z"/>
- </g>
- <g id="g1542" transform="matrix(2 0 0 2 -.41134 3.175)" fill="#676867" fill-rule="evenodd" stroke-width=".26458">
- <path id="path943" d="m4.9596-1.0196c0.40797 2.8312 1.9272 4.5499 5.0239 4.691-2.918 1.1164-5.9253-1.5076-5.0239-4.691"/>
- <path id="path945" d="m3.3407-0.52096c0.034969-0.00777 0.038854 0.015542 0.041445 0.041445 0.098431 1.8054 0.85998 3.1744 1.8689 4.1108 1.0089 0.93639 2.3636 1.6941 4.274 1.6604-3.5772 1.4247-7.337-1.9984-6.1856-5.8126"/>
- </g>
- </g>
- </g>
- <text id="text4811" transform="matrix(.26458 0 0 .26458 -45.077 -6.6039)" x="-248.88142" fill="#000000" font-family="'Fira Mono', 'Roboto Mono', monospace" font-size="12px" letter-spacing="0px" stroke-width="1px" word-spacing="0px" style="font-variant-caps:normal;font-variant-east-asian:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:15px;shape-inside:url(#rect4813);white-space:pre" xml:space="preserve"><tspan id="tspan13679" x="180" y="10.85">[MikroTik] ✅ Health recovery: temperature
-</tspan><tspan id="tspan13681" x="180" y="25.85">
-</tspan><tspan id="tspan13683" x="180" y="40.85">The temperature on MikroTik dropped below threshold: 48°C</tspan></text>
- </g>
-</svg>
diff --git a/doc/check-health.d/notification-04-psu-fail.svg b/doc/check-health.d/notification-04-psu-fail.svg
deleted file mode 100644
index f7efe4b..0000000
--- a/doc/check-health.d/notification-04-psu-fail.svg
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<svg id="svg8" width="513.78" height="83.78" version="1.1" viewBox="0 0 135.94 22.167" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
- <metadata id="metadata5">
- <rdf:RDF>
- <cc:Work rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <g id="layer1" transform="translate(16.375 11.083)">
- <rect id="rect857" x="-15.875" y="-10.583" width="134.94" height="21.167" rx="1.6867" fill="#e6e6e6" stroke="#6c5d53" stroke-linecap="round" stroke-linejoin="round"/>
- <g id="g884" transform="matrix(.5 0 0 .5 -12.406 -7.1146)" stroke-width="2">
- <path id="path899" d="m23.177 23.177c-2.9635 2.9635-17.991 2.9635-20.955 0-2.9635-2.9635-2.9635-17.991 0-20.955 2.9635-2.9635 17.991-2.9635 20.955 0 2.9635 2.9635 2.9635 17.991 0 20.955z" fill="#fff"/>
- <g id="g871">
- <g id="text837" stroke-width=".52916px" aria-label="#!rsc">
- <path id="path851" d="m7.4832 16.646v-1.0239h-0.54606l0.16577-1.2774h-1.2774l-0.16577 1.2774h-0.97511l0.16577-1.2774h-1.2774l-0.16577 1.2774h-0.70208v1.0239h0.56556l-0.24378 1.8722h-0.68257v1.0239h0.54606l-0.18527 1.3944h1.2774l0.18527-1.3944h0.97511l-0.18527 1.3944h1.2774l0.18527-1.3944h0.70208v-1.0239h-0.56556l0.24378-1.8722zm-2.2037 1.8722h-0.97511l0.24378-1.8722h0.97511z"/>
- <path id="path853" d="m9.6187 14.179h-1.6382l0.19502 4.271h1.2481zm-0.81909 5.1583c-0.48755 0-0.8776 0.39979-0.8776 0.8776 0 0.48755 0.39004 0.88735 0.8776 0.88735 0.4973 0 0.88735-0.39979 0.88735-0.88735 0-0.4778-0.39004-0.8776-0.88735-0.8776z"/>
- <path id="path855" d="m13.373 15.612c-0.59482 0-1.1019 0.42905-1.3359 1.1506l-0.13652-1.0044h-1.3359v5.1778h1.5407v-2.6035c0.17552-0.77033 0.4388-1.2286 1.0726-1.2286 0.16577 0 0.30228 0.02925 0.46805 0.06826l0.24378-1.4919c-0.17552-0.04875-0.32178-0.06826-0.51681-0.06826z"/>
- <path id="path857" d="m16.181 15.592c-1.3066 0-2.116 0.69233-2.116 1.5797 0 0.79959 0.50706 1.3261 1.5309 1.6187 0.9361 0.26328 1.0921 0.37054 1.0921 0.72158 0 0.31203-0.28278 0.48755-0.75083 0.48755-0.50706 0-0.98486-0.20477-1.3749-0.50706l-0.75083 0.83859c0.50706 0.4583 1.2676 0.77033 2.1647 0.77033 1.2871 0 2.3012-0.63382 2.3012-1.7064 0-0.92635-0.57531-1.3554-1.5992-1.6479-0.92635-0.27303-1.0629-0.39004-1.0629-0.66307 0-0.23402 0.20477-0.39004 0.62407-0.39004 0.44855 0 0.8776 0.14627 1.2774 0.39979l0.56556-0.86784c-0.4778-0.39004-1.1506-0.63382-1.9015-0.63382z"/>
- <path id="path859" d="m21.281 15.592c-1.5504 0-2.5353 1.1506-2.5353 2.7986 0 1.6382 0.97511 2.7108 2.5645 2.7108 0.71183 0 1.2676-0.23403 1.7454-0.61432l-0.67282-0.9556c-0.37054 0.23403-0.62407 0.35104-0.99461 0.35104-0.61432 0-1.0239-0.35104-1.0239-1.5017 0-1.1604 0.38029-1.6089 1.0434-1.6089 0.35104 0 0.65332 0.11701 0.98486 0.37054l0.66307-0.9166c-0.4973-0.4193-1.0531-0.63382-1.7747-0.63382z"/>
- </g>
- <g id="g1542" transform="matrix(2 0 0 2 -.41134 3.175)" fill="#676867" fill-rule="evenodd" stroke-width=".26458">
- <path id="path943" d="m4.9596-1.0196c0.40797 2.8312 1.9272 4.5499 5.0239 4.691-2.918 1.1164-5.9253-1.5076-5.0239-4.691"/>
- <path id="path945" d="m3.3407-0.52096c0.034969-0.00777 0.038854 0.015542 0.041445 0.041445 0.098431 1.8054 0.85998 3.1744 1.8689 4.1108 1.0089 0.93639 2.3636 1.6941 4.274 1.6604-3.5772 1.4247-7.337-1.9984-6.1856-5.8126"/>
- </g>
- </g>
- </g>
- <text id="text4811" transform="matrix(.26458 0 0 .26458 -45.077 -6.6039)" x="-248.88142" fill="#000000" font-family="'Fira Mono', 'Roboto Mono', monospace" font-size="12px" letter-spacing="0px" stroke-width="1px" word-spacing="0px" style="font-variant-caps:normal;font-variant-east-asian:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:15px;shape-inside:url(#rect4813);white-space:pre" xml:space="preserve"><tspan id="tspan2171" x="180" y="10.85">[MikroTik] ❌ Health warning: psu1-state
-</tspan><tspan id="tspan2173" x="180" y="25.85">
-</tspan><tspan id="tspan2175" x="180" y="40.85">The power supply unit 'psu1-state' on MikroTik failed!</tspan></text>
- </g>
-</svg>
diff --git a/doc/check-health.d/notification-04-ram-utilization-ok.avif b/doc/check-health.d/notification-04-ram-utilization-ok.avif
new file mode 100644
index 0000000..d995b9a
--- /dev/null
+++ b/doc/check-health.d/notification-04-ram-utilization-ok.avif
Binary files differ
diff --git a/doc/check-health.d/notification-05-psu-ok.svg b/doc/check-health.d/notification-05-psu-ok.svg
deleted file mode 100644
index 0a3f05c..0000000
--- a/doc/check-health.d/notification-05-psu-ok.svg
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<svg id="svg8" width="533.78" height="83.78" version="1.1" viewBox="0 0 141.23 22.167" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
- <metadata id="metadata5">
- <rdf:RDF>
- <cc:Work rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <g id="layer1" transform="translate(16.375 11.083)">
- <rect id="rect857" x="-15.875" y="-10.583" width="140.23" height="21.167" rx="1.7529" fill="#e6e6e6" stroke="#6c5d53" stroke-linecap="round" stroke-linejoin="round"/>
- <g id="g884" transform="matrix(.5 0 0 .5 -12.406 -7.1146)" stroke-width="2">
- <path id="path899" d="m23.177 23.177c-2.9635 2.9635-17.991 2.9635-20.955 0-2.9635-2.9635-2.9635-17.991 0-20.955 2.9635-2.9635 17.991-2.9635 20.955 0 2.9635 2.9635 2.9635 17.991 0 20.955z" fill="#fff"/>
- <g id="g871">
- <g id="text837" stroke-width=".52916px" aria-label="#!rsc">
- <path id="path851" d="m7.4832 16.646v-1.0239h-0.54606l0.16577-1.2774h-1.2774l-0.16577 1.2774h-0.97511l0.16577-1.2774h-1.2774l-0.16577 1.2774h-0.70208v1.0239h0.56556l-0.24378 1.8722h-0.68257v1.0239h0.54606l-0.18527 1.3944h1.2774l0.18527-1.3944h0.97511l-0.18527 1.3944h1.2774l0.18527-1.3944h0.70208v-1.0239h-0.56556l0.24378-1.8722zm-2.2037 1.8722h-0.97511l0.24378-1.8722h0.97511z"/>
- <path id="path853" d="m9.6187 14.179h-1.6382l0.19502 4.271h1.2481zm-0.81909 5.1583c-0.48755 0-0.8776 0.39979-0.8776 0.8776 0 0.48755 0.39004 0.88735 0.8776 0.88735 0.4973 0 0.88735-0.39979 0.88735-0.88735 0-0.4778-0.39004-0.8776-0.88735-0.8776z"/>
- <path id="path855" d="m13.373 15.612c-0.59482 0-1.1019 0.42905-1.3359 1.1506l-0.13652-1.0044h-1.3359v5.1778h1.5407v-2.6035c0.17552-0.77033 0.4388-1.2286 1.0726-1.2286 0.16577 0 0.30228 0.02925 0.46805 0.06826l0.24378-1.4919c-0.17552-0.04875-0.32178-0.06826-0.51681-0.06826z"/>
- <path id="path857" d="m16.181 15.592c-1.3066 0-2.116 0.69233-2.116 1.5797 0 0.79959 0.50706 1.3261 1.5309 1.6187 0.9361 0.26328 1.0921 0.37054 1.0921 0.72158 0 0.31203-0.28278 0.48755-0.75083 0.48755-0.50706 0-0.98486-0.20477-1.3749-0.50706l-0.75083 0.83859c0.50706 0.4583 1.2676 0.77033 2.1647 0.77033 1.2871 0 2.3012-0.63382 2.3012-1.7064 0-0.92635-0.57531-1.3554-1.5992-1.6479-0.92635-0.27303-1.0629-0.39004-1.0629-0.66307 0-0.23402 0.20477-0.39004 0.62407-0.39004 0.44855 0 0.8776 0.14627 1.2774 0.39979l0.56556-0.86784c-0.4778-0.39004-1.1506-0.63382-1.9015-0.63382z"/>
- <path id="path859" d="m21.281 15.592c-1.5504 0-2.5353 1.1506-2.5353 2.7986 0 1.6382 0.97511 2.7108 2.5645 2.7108 0.71183 0 1.2676-0.23403 1.7454-0.61432l-0.67282-0.9556c-0.37054 0.23403-0.62407 0.35104-0.99461 0.35104-0.61432 0-1.0239-0.35104-1.0239-1.5017 0-1.1604 0.38029-1.6089 1.0434-1.6089 0.35104 0 0.65332 0.11701 0.98486 0.37054l0.66307-0.9166c-0.4973-0.4193-1.0531-0.63382-1.7747-0.63382z"/>
- </g>
- <g id="g1542" transform="matrix(2 0 0 2 -.41134 3.175)" fill="#676867" fill-rule="evenodd" stroke-width=".26458">
- <path id="path943" d="m4.9596-1.0196c0.40797 2.8312 1.9272 4.5499 5.0239 4.691-2.918 1.1164-5.9253-1.5076-5.0239-4.691"/>
- <path id="path945" d="m3.3407-0.52096c0.034969-0.00777 0.038854 0.015542 0.041445 0.041445 0.098431 1.8054 0.85998 3.1744 1.8689 4.1108 1.0089 0.93639 2.3636 1.6941 4.274 1.6604-3.5772 1.4247-7.337-1.9984-6.1856-5.8126"/>
- </g>
- </g>
- </g>
- <text id="text4811" transform="matrix(.26458 0 0 .26458 -45.077 -6.6039)" x="-248.88142" fill="#000000" font-family="'Fira Mono', 'Roboto Mono', monospace" font-size="12px" letter-spacing="0px" stroke-width="1px" word-spacing="0px" style="font-variant-caps:normal;font-variant-east-asian:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:15px;shape-inside:url(#rect4813);white-space:pre" xml:space="preserve"><tspan id="tspan13739" x="180" y="10.85">[MikroTik] ✅ Health recovery: psu1-state
-</tspan><tspan id="tspan13741" x="180" y="25.85">
-</tspan><tspan id="tspan13743" x="180" y="40.85">The power supply unit 'psu1-state' on MikroTik recovered!</tspan></text>
- </g>
-</svg>
diff --git a/doc/check-health.d/notification-05-voltage.avif b/doc/check-health.d/notification-05-voltage.avif
new file mode 100644
index 0000000..17a385b
--- /dev/null
+++ b/doc/check-health.d/notification-05-voltage.avif
Binary files differ
diff --git a/doc/check-health.d/notification-06-temperature-high.avif b/doc/check-health.d/notification-06-temperature-high.avif
new file mode 100644
index 0000000..60d3802
--- /dev/null
+++ b/doc/check-health.d/notification-06-temperature-high.avif
Binary files differ
diff --git a/doc/check-health.d/notification-07-temperature-ok.avif b/doc/check-health.d/notification-07-temperature-ok.avif
new file mode 100644
index 0000000..4afed02
--- /dev/null
+++ b/doc/check-health.d/notification-07-temperature-ok.avif
Binary files differ
diff --git a/doc/check-health.d/notification-08-psu-fail.avif b/doc/check-health.d/notification-08-psu-fail.avif
new file mode 100644
index 0000000..ad049ac
--- /dev/null
+++ b/doc/check-health.d/notification-08-psu-fail.avif
Binary files differ
diff --git a/doc/check-health.d/notification-09-psu-ok.avif b/doc/check-health.d/notification-09-psu-ok.avif
new file mode 100644
index 0000000..26f5a74
--- /dev/null
+++ b/doc/check-health.d/notification-09-psu-ok.avif
Binary files differ
diff --git a/doc/check-health.md b/doc/check-health.md
index 9ee16bb..ee52b61 100644
--- a/doc/check-health.md
+++ b/doc/check-health.md
@@ -1,7 +1,14 @@
Notify about health state
=========================
-[◀ Go back to main README](../README.md)
+[![GitHub stars](https://img.shields.io/github/stars/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=red)](https://github.com/eworm-de/routeros-scripts/stargazers)
+[![GitHub forks](https://img.shields.io/github/forks/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=green)](https://github.com/eworm-de/routeros-scripts/network)
+[![GitHub watchers](https://img.shields.io/github/watchers/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=blue)](https://github.com/eworm-de/routeros-scripts/watchers)
+[![required RouterOS version](https://img.shields.io/badge/RouterOS-7.12-yellow?style=flat)](https://mikrotik.com/download/changelogs/)
+[![Telegram group @routeros_scripts](https://img.shields.io/badge/Telegram-%40routeros__scripts-%2326A5E4?logo=telegram&style=flat)](https://t.me/routeros_scripts)
+[![donate with PayPal](https://img.shields.io/badge/Like_it%3F-Donate!-orange?logo=githubsponsors&logoColor=orange&style=flat)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)
+
+[⬅️ Go back to main README](../README.md)
> ℹ️ **Info**: This script can not be used on its own but requires the base
> installation. See [main README](../README.md) for details.
@@ -12,32 +19,46 @@ Description
This script is run from scheduler periodically, sending notification on
health related events:
-* voltage jumps up or down more than configured threshold or drops below limit
+* high CPU utilization
+* high RAM utilization (low available RAM)
+* voltage jumps up or down more than configured threshold
+* voltage drops below hard lower limit
* power supply failed or recovered
* temperature is above or below threshold
Note that bad initial state will not trigger an event.
-Only sensors available in hardware can be checked. See what your
-hardware supports:
+Monitoring CPU and RAM utilization (available processing and memory
+resources) works on all devices. Other than that only sensors available
+in hardware can be checked. See what your hardware supports:
/system/health/print;
### Sample notifications
+#### CPU utilization
+
+![check-health notification cpu utilization high](check-health.d/notification-01-cpu-utilization-high.avif)
+![check-health notification cpu utilization ok](check-health.d/notification-02-cpu-utilization-ok.avif)
+
+#### RAM utilization (low available RAM)
+
+![check-health notification ram utilization high](check-health.d/notification-03-ram-utilization-high.avif)
+![check-health notification ram utilization ok](check-health.d/notification-04-ram-utilization-ok.avif)
+
#### Voltage
-![check-health notification voltage](check-health.d/notification-01-voltage.svg)
+![check-health notification voltage](check-health.d/notification-05-voltage.avif)
#### Temperature
-![check-health notification](check-health.d/notification-02-temperature-high.svg)
-![check-health notification](check-health.d/notification-03-temperature-ok.svg)
+![check-health notification temperature high](check-health.d/notification-06-temperature-high.avif)
+![check-health notification temperature ok](check-health.d/notification-07-temperature-ok.avif)
#### PSU state
-![check-health notification](check-health.d/notification-04-psu-fail.svg)
-![check-health notification](check-health.d/notification-05-psu-ok.svg)
+![check-health notification psu fail](check-health.d/notification-08-psu-fail.avif)
+![check-health notification psu ok](check-health.d/notification-09-psu-ok.avif)
Requirements and installation
-----------------------------
@@ -45,7 +66,11 @@ Requirements and installation
Just install the script and create a scheduler:
$ScriptInstallUpdate check-health;
- /system/scheduler/add interval=1m name=check-health on-event="/system/script/run check-health;" start-time=startup;
+ /system/scheduler/add interval=53s name=check-health on-event="/system/script/run check-health;" start-time=startup;
+
+> ℹ️ **Info**: Running lots of scripts simultaneously can tamper the
+> precision of cpu utilization, escpecially on devices with limited
+> resources. Thus an unusual interval is used here.
Configuration
-------------
@@ -56,11 +81,16 @@ The configuration goes to `global-config-overlay`, these are the parameters:
* `CheckHealthVoltageLow`: value (in volt*10) giving a hard lower limit
* `CheckHealthVoltagePercent`: percentage value to trigger voltage jumps
+> ℹ️ **Info**: Copy relevant configuration from
+> [`global-config`](../global-config.rsc) (the one without `-overlay`) to
+> your local `global-config-overlay` and modify it to your specific needs.
+
Also notification settings are required for
[e-mail](mod/notification-email.md),
-[matrix](mod/notification-matrix.md) and/or
+[matrix](mod/notification-matrix.md),
+[ntfy](mod/notification-ntfy.md) and/or
[telegram](mod/notification-telegram.md).
---
-[◀ Go back to main README](../README.md)
-[▲ Go back to top](#top)
+[⬅️ Go back to main README](../README.md)
+[⬆️ Go back to top](#top)
diff --git a/doc/check-lte-firmware-upgrade.d/notification.avif b/doc/check-lte-firmware-upgrade.d/notification.avif
new file mode 100644
index 0000000..c440da5
--- /dev/null
+++ b/doc/check-lte-firmware-upgrade.d/notification.avif
Binary files differ
diff --git a/doc/check-lte-firmware-upgrade.d/notification.svg b/doc/check-lte-firmware-upgrade.d/notification.svg
deleted file mode 100644
index 5992462..0000000
--- a/doc/check-lte-firmware-upgrade.d/notification.svg
+++ /dev/null
@@ -1,38 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<svg id="svg8" width="483.78" height="153.78" version="1.1" viewBox="0 0 128 40.687" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
- <metadata id="metadata5">
- <rdf:RDF>
- <cc:Work rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <g id="layer1" transform="translate(16.375 11.083)">
- <rect id="rect857" x="-15.875" y="-10.583" width="127" height="39.687" rx="1.5875" fill="#e6e6e6" stroke="#6c5d53" stroke-linecap="round" stroke-linejoin="round"/>
- <g id="g884" transform="matrix(.5 0 0 .5 -12.406 -7.1146)" stroke-width="2">
- <path id="path899" d="m23.177 23.177c-2.9635 2.9635-17.991 2.9635-20.955 0-2.9635-2.9635-2.9635-17.991 0-20.955 2.9635-2.9635 17.991-2.9635 20.955 0 2.9635 2.9635 2.9635 17.991 0 20.955z" fill="#fff"/>
- <g id="g871">
- <g id="text837" stroke-width=".52916px" aria-label="#!rsc">
- <path id="path851" d="m7.4832 16.646v-1.0239h-0.54606l0.16577-1.2774h-1.2774l-0.16577 1.2774h-0.97511l0.16577-1.2774h-1.2774l-0.16577 1.2774h-0.70208v1.0239h0.56556l-0.24378 1.8722h-0.68257v1.0239h0.54606l-0.18527 1.3944h1.2774l0.18527-1.3944h0.97511l-0.18527 1.3944h1.2774l0.18527-1.3944h0.70208v-1.0239h-0.56556l0.24378-1.8722zm-2.2037 1.8722h-0.97511l0.24378-1.8722h0.97511z"/>
- <path id="path853" d="m9.6187 14.179h-1.6382l0.19502 4.271h1.2481zm-0.81909 5.1583c-0.48755 0-0.8776 0.39979-0.8776 0.8776 0 0.48755 0.39004 0.88735 0.8776 0.88735 0.4973 0 0.88735-0.39979 0.88735-0.88735 0-0.4778-0.39004-0.8776-0.88735-0.8776z"/>
- <path id="path855" d="m13.373 15.612c-0.59482 0-1.1019 0.42905-1.3359 1.1506l-0.13652-1.0044h-1.3359v5.1778h1.5407v-2.6035c0.17552-0.77033 0.4388-1.2286 1.0726-1.2286 0.16577 0 0.30228 0.02925 0.46805 0.06826l0.24378-1.4919c-0.17552-0.04875-0.32178-0.06826-0.51681-0.06826z"/>
- <path id="path857" d="m16.181 15.592c-1.3066 0-2.116 0.69233-2.116 1.5797 0 0.79959 0.50706 1.3261 1.5309 1.6187 0.9361 0.26328 1.0921 0.37054 1.0921 0.72158 0 0.31203-0.28278 0.48755-0.75083 0.48755-0.50706 0-0.98486-0.20477-1.3749-0.50706l-0.75083 0.83859c0.50706 0.4583 1.2676 0.77033 2.1647 0.77033 1.2871 0 2.3012-0.63382 2.3012-1.7064 0-0.92635-0.57531-1.3554-1.5992-1.6479-0.92635-0.27303-1.0629-0.39004-1.0629-0.66307 0-0.23402 0.20477-0.39004 0.62407-0.39004 0.44855 0 0.8776 0.14627 1.2774 0.39979l0.56556-0.86784c-0.4778-0.39004-1.1506-0.63382-1.9015-0.63382z"/>
- <path id="path859" d="m21.281 15.592c-1.5504 0-2.5353 1.1506-2.5353 2.7986 0 1.6382 0.97511 2.7108 2.5645 2.7108 0.71183 0 1.2676-0.23403 1.7454-0.61432l-0.67282-0.9556c-0.37054 0.23403-0.62407 0.35104-0.99461 0.35104-0.61432 0-1.0239-0.35104-1.0239-1.5017 0-1.1604 0.38029-1.6089 1.0434-1.6089 0.35104 0 0.65332 0.11701 0.98486 0.37054l0.66307-0.9166c-0.4973-0.4193-1.0531-0.63382-1.7747-0.63382z"/>
- </g>
- <g id="g1542" transform="matrix(2 0 0 2 -.41134 3.175)" fill="#676867" fill-rule="evenodd" stroke-width=".26458">
- <path id="path943" d="m4.9596-1.0196c0.40797 2.8312 1.9272 4.5499 5.0239 4.691-2.918 1.1164-5.9253-1.5076-5.0239-4.691"/>
- <path id="path945" d="m3.3407-0.52096c0.034969-0.00777 0.038854 0.015542 0.041445 0.041445 0.098431 1.8054 0.85998 3.1744 1.8689 4.1108 1.0089 0.93639 2.3636 1.6941 4.274 1.6604-3.5772 1.4247-7.337-1.9984-6.1856-5.8126"/>
- </g>
- </g>
- </g>
- <text id="text4811" transform="matrix(.26458 0 0 .26458 -45.014 -6.6039)" x="-248.88142" fill="#000000" font-family="'Fira Mono', 'Roboto Mono', monospace" font-size="12px" letter-spacing="0px" stroke-width="1px" word-spacing="0px" style="font-variant-caps:normal;font-variant-east-asian:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:15px;shape-inside:url(#rect4813);white-space:pre" xml:space="preserve"><tspan id="tspan13600" x="180" y="10.85">[MikroTik] ✨ LTE firmware upgrade
-</tspan><tspan id="tspan13602" x="180" y="25.85">
-</tspan><tspan id="tspan13604" x="180" y="40.85">A new firmware version R11e-LTE6_V033 is available
-</tspan><tspan id="tspan13606" x="180" y="55.85">for LTE interface lte on MikroTik.
-</tspan><tspan id="tspan13608" x="180" y="70.85">
-</tspan><tspan id="tspan13610" x="180" y="85.85">Interface: MikroTik R11e-LTE6
-</tspan><tspan id="tspan13612" x="180" y="100.85">Installed: R11e-LTE6_V027
-</tspan><tspan id="tspan13614" x="180" y="115.85">Available: R11e-LTE6_V033</tspan></text>
- </g>
-</svg>
diff --git a/doc/check-lte-firmware-upgrade.md b/doc/check-lte-firmware-upgrade.md
index f3b3bfc..bec3177 100644
--- a/doc/check-lte-firmware-upgrade.md
+++ b/doc/check-lte-firmware-upgrade.md
@@ -1,7 +1,14 @@
Notify on LTE firmware upgrade
==============================
-[◀ Go back to main README](../README.md)
+[![GitHub stars](https://img.shields.io/github/stars/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=red)](https://github.com/eworm-de/routeros-scripts/stargazers)
+[![GitHub forks](https://img.shields.io/github/forks/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=green)](https://github.com/eworm-de/routeros-scripts/network)
+[![GitHub watchers](https://img.shields.io/github/watchers/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=blue)](https://github.com/eworm-de/routeros-scripts/watchers)
+[![required RouterOS version](https://img.shields.io/badge/RouterOS-7.12-yellow?style=flat)](https://mikrotik.com/download/changelogs/)
+[![Telegram group @routeros_scripts](https://img.shields.io/badge/Telegram-%40routeros__scripts-%2326A5E4?logo=telegram&style=flat)](https://t.me/routeros_scripts)
+[![donate with PayPal](https://img.shields.io/badge/Like_it%3F-Donate!-orange?logo=githubsponsors&logoColor=orange&style=flat)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)
+
+[⬅️ Go back to main README](../README.md)
> ℹ️ **Info**: This script can not be used on its own but requires the base
> installation. See [main README](../README.md) for details.
@@ -19,7 +26,7 @@ upgrades. Currently supported LTE hardware:
### Sample notification
-![check-lte-firmware-upgrade notification](check-lte-firmware-upgrade.d/notification.svg)
+![check-lte-firmware-upgrade notification](check-lte-firmware-upgrade.d/notification.avif)
Requirements and installation
-----------------------------
@@ -47,5 +54,5 @@ See also
* [Install LTE firmware upgrade](unattended-lte-firmware-upgrade.md)
---
-[◀ Go back to main README](../README.md)
-[▲ Go back to top](#top)
+[⬅️ Go back to main README](../README.md)
+[⬆️ Go back to top](#top)
diff --git a/doc/check-routeros-update.d/notification.avif b/doc/check-routeros-update.d/notification.avif
new file mode 100644
index 0000000..50317cf
--- /dev/null
+++ b/doc/check-routeros-update.d/notification.avif
Binary files differ
diff --git a/doc/check-routeros-update.d/notification.svg b/doc/check-routeros-update.d/notification.svg
deleted file mode 100644
index d4dbc43..0000000
--- a/doc/check-routeros-update.d/notification.svg
+++ /dev/null
@@ -1,45 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<svg id="svg8" width="523.78" height="253.78" version="1.1" viewBox="0 0 138.58 67.146" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
- <metadata id="metadata5">
- <rdf:RDF>
- <cc:Work rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <g id="layer1" transform="translate(16.375 11.083)">
- <rect id="rect857" x="-15.875" y="-10.583" width="137.58" height="66.146" rx="1.7198" fill="#e6e6e6" stroke="#6c5d53" stroke-linecap="round" stroke-linejoin="round"/>
- <g id="g884" transform="matrix(.5 0 0 .5 -12.406 -7.1146)" stroke-width="2">
- <path id="path899" d="m23.177 23.177c-2.9635 2.9635-17.991 2.9635-20.955 0-2.9635-2.9635-2.9635-17.991 0-20.955 2.9635-2.9635 17.991-2.9635 20.955 0 2.9635 2.9635 2.9635 17.991 0 20.955z" fill="#fff"/>
- <g id="g871">
- <g id="text837" stroke-width=".52916px" aria-label="#!rsc">
- <path id="path851" d="m7.4832 16.646v-1.0239h-0.54606l0.16577-1.2774h-1.2774l-0.16577 1.2774h-0.97511l0.16577-1.2774h-1.2774l-0.16577 1.2774h-0.70208v1.0239h0.56556l-0.24378 1.8722h-0.68257v1.0239h0.54606l-0.18527 1.3944h1.2774l0.18527-1.3944h0.97511l-0.18527 1.3944h1.2774l0.18527-1.3944h0.70208v-1.0239h-0.56556l0.24378-1.8722zm-2.2037 1.8722h-0.97511l0.24378-1.8722h0.97511z"/>
- <path id="path853" d="m9.6187 14.179h-1.6382l0.19502 4.271h1.2481zm-0.81909 5.1583c-0.48755 0-0.8776 0.39979-0.8776 0.8776 0 0.48755 0.39004 0.88735 0.8776 0.88735 0.4973 0 0.88735-0.39979 0.88735-0.88735 0-0.4778-0.39004-0.8776-0.88735-0.8776z"/>
- <path id="path855" d="m13.373 15.612c-0.59482 0-1.1019 0.42905-1.3359 1.1506l-0.13652-1.0044h-1.3359v5.1778h1.5407v-2.6035c0.17552-0.77033 0.4388-1.2286 1.0726-1.2286 0.16577 0 0.30228 0.02925 0.46805 0.06826l0.24378-1.4919c-0.17552-0.04875-0.32178-0.06826-0.51681-0.06826z"/>
- <path id="path857" d="m16.181 15.592c-1.3066 0-2.116 0.69233-2.116 1.5797 0 0.79959 0.50706 1.3261 1.5309 1.6187 0.9361 0.26328 1.0921 0.37054 1.0921 0.72158 0 0.31203-0.28278 0.48755-0.75083 0.48755-0.50706 0-0.98486-0.20477-1.3749-0.50706l-0.75083 0.83859c0.50706 0.4583 1.2676 0.77033 2.1647 0.77033 1.2871 0 2.3012-0.63382 2.3012-1.7064 0-0.92635-0.57531-1.3554-1.5992-1.6479-0.92635-0.27303-1.0629-0.39004-1.0629-0.66307 0-0.23402 0.20477-0.39004 0.62407-0.39004 0.44855 0 0.8776 0.14627 1.2774 0.39979l0.56556-0.86784c-0.4778-0.39004-1.1506-0.63382-1.9015-0.63382z"/>
- <path id="path859" d="m21.281 15.592c-1.5504 0-2.5353 1.1506-2.5353 2.7986 0 1.6382 0.97511 2.7108 2.5645 2.7108 0.71183 0 1.2676-0.23403 1.7454-0.61432l-0.67282-0.9556c-0.37054 0.23403-0.62407 0.35104-0.99461 0.35104-0.61432 0-1.0239-0.35104-1.0239-1.5017 0-1.1604 0.38029-1.6089 1.0434-1.6089 0.35104 0 0.65332 0.11701 0.98486 0.37054l0.66307-0.9166c-0.4973-0.4193-1.0531-0.63382-1.7747-0.63382z"/>
- </g>
- <g id="g1542" transform="matrix(2 0 0 2 -.41134 3.175)" fill="#676867" fill-rule="evenodd" stroke-width=".26458">
- <path id="path943" d="m4.9596-1.0196c0.40797 2.8312 1.9272 4.5499 5.0239 4.691-2.918 1.1164-5.9253-1.5076-5.0239-4.691"/>
- <path id="path945" d="m3.3407-0.52096c0.034969-0.00777 0.038854 0.015542 0.041445 0.041445 0.098431 1.8054 0.85998 3.1744 1.8689 4.1108 1.0089 0.93639 2.3636 1.6941 4.274 1.6604-3.5772 1.4247-7.337-1.9984-6.1856-5.8126"/>
- </g>
- </g>
- </g>
- <text id="text4811" transform="matrix(.26458 0 0 .26458 -44.95 -6.6039)" x="-248.88142" fill="#000000" font-family="'Fira Mono', 'Roboto Mono', monospace" font-size="12px" letter-spacing="0px" stroke-width="1px" word-spacing="0px" style="font-variant-caps:normal;font-variant-east-asian:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:15px;shape-inside:url(#rect4813);white-space:pre" xml:space="preserve"><tspan id="tspan13541" x="180" y="10.85">[MikroTik] ✨ RouterOS update
-</tspan><tspan id="tspan13543" x="180" y="25.85">
-</tspan><tspan id="tspan13545" x="180" y="40.85">A new RouterOS version 7.4.1 is available for MikroTik.
-</tspan><tspan id="tspan13547" x="180" y="55.85">
-</tspan><tspan id="tspan13549" x="180" y="70.85">Hostname: MikroTik
-</tspan><tspan id="tspan13551" x="180" y="85.85">Board name: CHR
-</tspan><tspan id="tspan13553" x="180" y="100.85">Architecture: x86_64
-</tspan><tspan id="tspan13555" x="180" y="115.85">RouterOS:
-</tspan><tspan id="tspan13557" x="180" y="130.85"> Channel: stable
-</tspan><tspan id="tspan13559" x="180" y="145.85"> Installed: 7.3.1
-</tspan><tspan id="tspan13561" x="180" y="160.85"> Available: 7.4.1
-</tspan><tspan id="tspan13563" x="180" y="175.85">RouterOS-Scripts:
-</tspan><tspan id="tspan13565" x="180" y="190.85"> Version: 83</tspan></text>
- <text id="text6580" transform="translate(-59.247 -209.41)" fill="#000000" font-family="sans-serif" font-size="3.175px" letter-spacing="0px" stroke-width="1px" word-spacing="0px" style="font-variant-caps:normal;font-variant-east-asian:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:3.96875px;shape-inside:url(#rect6582);white-space:pre" xml:space="preserve"><tspan id="tspan13567" x="61.890625" y="254.76135">
-</tspan><tspan id="tspan13569" x="61.890625" y="258.7301">🔗 https://mikrotik.com/download/changelogs/stable-release-tree</tspan></text>
- </g>
-</svg>
diff --git a/doc/check-routeros-update.md b/doc/check-routeros-update.md
index edffcbb..dbb2b89 100644
--- a/doc/check-routeros-update.md
+++ b/doc/check-routeros-update.md
@@ -1,7 +1,14 @@
Notify on RouterOS update
=========================
-[◀ Go back to main README](../README.md)
+[![GitHub stars](https://img.shields.io/github/stars/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=red)](https://github.com/eworm-de/routeros-scripts/stargazers)
+[![GitHub forks](https://img.shields.io/github/forks/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=green)](https://github.com/eworm-de/routeros-scripts/network)
+[![GitHub watchers](https://img.shields.io/github/watchers/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=blue)](https://github.com/eworm-de/routeros-scripts/watchers)
+[![required RouterOS version](https://img.shields.io/badge/RouterOS-7.12-yellow?style=flat)](https://mikrotik.com/download/changelogs/)
+[![Telegram group @routeros_scripts](https://img.shields.io/badge/Telegram-%40routeros__scripts-%2326A5E4?logo=telegram&style=flat)](https://t.me/routeros_scripts)
+[![donate with PayPal](https://img.shields.io/badge/Like_it%3F-Donate!-orange?logo=githubsponsors&logoColor=orange&style=flat)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)
+
+[⬅️ Go back to main README](../README.md)
> ℹ️ **Info**: This script can not be used on its own but requires the base
> installation. See [main README](../README.md) for details.
@@ -30,7 +37,7 @@ automatically is supported.
### Sample notification
-![check-routeros-update notification](check-routeros-update.d/notification.svg)
+![check-routeros-update notification](check-routeros-update.d/notification.avif)
Requirements and installation
-----------------------------
@@ -46,19 +53,28 @@ And add a scheduler for automatic update notification:
Configuration
-------------
-Configuration is required only if you want to control update process with
-safe versions from a web server. The configuration goes to
-`global-config-overlay`, this is the parameter:
+No extra configuration is required to receive notifications. Several
+mechanisms are availalbe to enable automatic installation of updates.
+The configuration goes to `global-config-overlay`, these are the parameters:
+
+* `SafeUpdateNeighbor`: install updates automatically if at least one other
+ device is seen in neighbor list with new version
+* `SafeUpdateNeighborIdentity`: regular expression to match identity for
+ trusted devices, leave empty to match all
+* `SafeUpdatePatch`: install patch updates (where just last digit changes)
+ automatically
+* `SafeUpdateUrl`: url on webserver to check for safe update, the channel
+ (`long-term`, `stable` or `testing`) is appended
+* `SafeUpdateAll`: install **all** updates automatically
-* `SafeUpdateNeighbor`: install updates automatically if seen in neighbor list
-* `SafeUpdateOnCap`: check for updates even if device is managed by CAPsMAN
-* `SafeUpdatePatch`: install patch updates automatically
-* `SafeUpdateUrl`: url to check for safe update, the channel (`long-term`,
-`stable` or `testing`) is appended
+> ℹ️ **Info**: Copy relevant configuration from
+> [`global-config`](../global-config.rsc) (the one without `-overlay`) to
+> your local `global-config-overlay` and modify it to your specific needs.
Also notification settings are required for
[e-mail](mod/notification-email.md),
-[matrix](mod/notification-matrix.md) and/or
+[matrix](mod/notification-matrix.md),
+[ntfy](mod/notification-ntfy.md) and/or
[telegram](mod/notification-telegram.md).
Usage and invocation
@@ -72,6 +88,14 @@ If an update is found you can install it right away.
Installing script [packages-update](packages-update.md) gives extra options.
+Tips & Tricks
+-------------
+
+The script checks for full connectivity before acting, so scheduling at
+startup is perfectly valid:
+
+ /system/scheduler/add name=check-routeros-update@startup on-event="/system/script/run check-routeros-update;" start-time=startup;
+
See also
--------
@@ -79,5 +103,5 @@ See also
* [Manage system update](packages-update.md)
---
-[◀ Go back to main README](../README.md)
-[▲ Go back to top](#top)
+[⬅️ Go back to main README](../README.md)
+[⬆️ Go back to top](#top)
diff --git a/doc/collect-wireless-mac.d/notification.avif b/doc/collect-wireless-mac.d/notification.avif
new file mode 100644
index 0000000..a2833f0
--- /dev/null
+++ b/doc/collect-wireless-mac.d/notification.avif
Binary files differ
diff --git a/doc/collect-wireless-mac.d/notification.svg b/doc/collect-wireless-mac.d/notification.svg
deleted file mode 100644
index aae8cc5..0000000
--- a/doc/collect-wireless-mac.d/notification.svg
+++ /dev/null
@@ -1,44 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<svg id="svg8" width="493.78" height="243.78" version="1.1" viewBox="0 0 130.65 64.5" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
- <metadata id="metadata5">
- <rdf:RDF>
- <cc:Work rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <g id="layer1" transform="translate(16.375 11.083)">
- <rect id="rect857" x="-15.875" y="-10.583" width="129.65" height="63.5" rx="1.6206" fill="#e6e6e6" stroke="#6c5d53" stroke-linecap="round" stroke-linejoin="round"/>
- <g id="g884" transform="matrix(.5 0 0 .5 -12.406 -7.1146)" stroke-width="2">
- <path id="path899" d="m23.177 23.177c-2.9635 2.9635-17.991 2.9635-20.955 0-2.9635-2.9635-2.9635-17.991 0-20.955 2.9635-2.9635 17.991-2.9635 20.955 0 2.9635 2.9635 2.9635 17.991 0 20.955z" fill="#fff"/>
- <g id="g871">
- <g id="text837" stroke-width=".52916px" aria-label="#!rsc">
- <path id="path851" d="m7.4832 16.646v-1.0239h-0.54606l0.16577-1.2774h-1.2774l-0.16577 1.2774h-0.97511l0.16577-1.2774h-1.2774l-0.16577 1.2774h-0.70208v1.0239h0.56556l-0.24378 1.8722h-0.68257v1.0239h0.54606l-0.18527 1.3944h1.2774l0.18527-1.3944h0.97511l-0.18527 1.3944h1.2774l0.18527-1.3944h0.70208v-1.0239h-0.56556l0.24378-1.8722zm-2.2037 1.8722h-0.97511l0.24378-1.8722h0.97511z"/>
- <path id="path853" d="m9.6187 14.179h-1.6382l0.19502 4.271h1.2481zm-0.81909 5.1583c-0.48755 0-0.8776 0.39979-0.8776 0.8776 0 0.48755 0.39004 0.88735 0.8776 0.88735 0.4973 0 0.88735-0.39979 0.88735-0.88735 0-0.4778-0.39004-0.8776-0.88735-0.8776z"/>
- <path id="path855" d="m13.373 15.612c-0.59482 0-1.1019 0.42905-1.3359 1.1506l-0.13652-1.0044h-1.3359v5.1778h1.5407v-2.6035c0.17552-0.77033 0.4388-1.2286 1.0726-1.2286 0.16577 0 0.30228 0.02925 0.46805 0.06826l0.24378-1.4919c-0.17552-0.04875-0.32178-0.06826-0.51681-0.06826z"/>
- <path id="path857" d="m16.181 15.592c-1.3066 0-2.116 0.69233-2.116 1.5797 0 0.79959 0.50706 1.3261 1.5309 1.6187 0.9361 0.26328 1.0921 0.37054 1.0921 0.72158 0 0.31203-0.28278 0.48755-0.75083 0.48755-0.50706 0-0.98486-0.20477-1.3749-0.50706l-0.75083 0.83859c0.50706 0.4583 1.2676 0.77033 2.1647 0.77033 1.2871 0 2.3012-0.63382 2.3012-1.7064 0-0.92635-0.57531-1.3554-1.5992-1.6479-0.92635-0.27303-1.0629-0.39004-1.0629-0.66307 0-0.23402 0.20477-0.39004 0.62407-0.39004 0.44855 0 0.8776 0.14627 1.2774 0.39979l0.56556-0.86784c-0.4778-0.39004-1.1506-0.63382-1.9015-0.63382z"/>
- <path id="path859" d="m21.281 15.592c-1.5504 0-2.5353 1.1506-2.5353 2.7986 0 1.6382 0.97511 2.7108 2.5645 2.7108 0.71183 0 1.2676-0.23403 1.7454-0.61432l-0.67282-0.9556c-0.37054 0.23403-0.62407 0.35104-0.99461 0.35104-0.61432 0-1.0239-0.35104-1.0239-1.5017 0-1.1604 0.38029-1.6089 1.0434-1.6089 0.35104 0 0.65332 0.11701 0.98486 0.37054l0.66307-0.9166c-0.4973-0.4193-1.0531-0.63382-1.7747-0.63382z"/>
- </g>
- <g id="g1542" transform="matrix(2 0 0 2 -.41134 3.175)" fill="#676867" fill-rule="evenodd" stroke-width=".26458">
- <path id="path943" d="m4.9596-1.0196c0.40797 2.8312 1.9272 4.5499 5.0239 4.691-2.918 1.1164-5.9253-1.5076-5.0239-4.691"/>
- <path id="path945" d="m3.3407-0.52096c0.034969-0.00777 0.038854 0.015542 0.041445 0.041445 0.098431 1.8054 0.85998 3.1744 1.8689 4.1108 1.0089 0.93639 2.3636 1.6941 4.274 1.6604-3.5772 1.4247-7.337-1.9984-6.1856-5.8126"/>
- </g>
- </g>
- </g>
- <text id="text4811" transform="matrix(.26458 0 0 .26458 -45.014 -6.6039)" x="-248.88142" fill="#000000" font-family="'Fira Mono', 'Roboto Mono', monospace" font-size="12px" letter-spacing="0px" stroke-width="1px" word-spacing="0px" style="font-variant-caps:normal;font-variant-east-asian:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:15px;shape-inside:url(#rect4813);white-space:pre" xml:space="preserve"><tspan id="tspan13404" x="180" y="10.85">[MikroTik] 📱 48:F1:7F:D0:E5:4E connected to Wifi
-</tspan><tspan id="tspan13406" x="180" y="25.85">
-</tspan><tspan id="tspan13408" x="180" y="40.85">A device with unknown MAC address connected to Wifi
-</tspan><tspan id="tspan13410" x="180" y="55.85">on MikroTik.
-</tspan><tspan id="tspan13412" x="180" y="70.85">
-</tspan><tspan id="tspan13414" x="180" y="85.85">Controller: MikroTik
-</tspan><tspan id="tspan13416" x="180" y="100.85">Interface: wl5-wifi
-</tspan><tspan id="tspan13418" x="180" y="115.85">SSID: Wifi
-</tspan><tspan id="tspan13420" x="180" y="130.85">MAC: 48:F1:7F:D0:E5:4E
-</tspan><tspan id="tspan13422" x="180" y="145.85">Vendor: Intel Corporate
-</tspan><tspan id="tspan13424" x="180" y="160.85">Hostname: host-523c8e0e
-</tspan><tspan id="tspan13426" x="180" y="175.85">Address: 192.168.20.254
-</tspan><tspan id="tspan13428" x="180" y="190.85">DNS name: host-523c8e0e.dhcp.MikroTik.example.com
-</tspan><tspan id="tspan13430" x="180" y="205.85">Date: jun/15/2021 09:21:56</tspan></text>
- </g>
-</svg>
diff --git a/doc/collect-wireless-mac.md b/doc/collect-wireless-mac.md
index e6ef990..b0a2298 100644
--- a/doc/collect-wireless-mac.md
+++ b/doc/collect-wireless-mac.md
@@ -1,7 +1,14 @@
Collect MAC addresses in wireless access list
=============================================
-[◀ Go back to main README](../README.md)
+[![GitHub stars](https://img.shields.io/github/stars/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=red)](https://github.com/eworm-de/routeros-scripts/stargazers)
+[![GitHub forks](https://img.shields.io/github/forks/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=green)](https://github.com/eworm-de/routeros-scripts/network)
+[![GitHub watchers](https://img.shields.io/github/watchers/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=blue)](https://github.com/eworm-de/routeros-scripts/watchers)
+[![required RouterOS version](https://img.shields.io/badge/RouterOS-7.12-yellow?style=flat)](https://mikrotik.com/download/changelogs/)
+[![Telegram group @routeros_scripts](https://img.shields.io/badge/Telegram-%40routeros__scripts-%2326A5E4?logo=telegram&style=flat)](https://t.me/routeros_scripts)
+[![donate with PayPal](https://img.shields.io/badge/Like_it%3F-Donate!-orange?logo=githubsponsors&logoColor=orange&style=flat)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)
+
+[⬅️ Go back to main README](../README.md)
> ℹ️ **Info**: This script can not be used on its own but requires the base
> installation. See [main README](../README.md) for details.
@@ -17,19 +24,24 @@ and modify it to your needs.
### Sample notification
-![collect-wireless-mac notification](collect-wireless-mac.d/notification.svg)
+![collect-wireless-mac notification](collect-wireless-mac.d/notification.avif)
Requirements and installation
-----------------------------
-Depending on whether you use CAPsMAN (`/caps-man`) or local wireless
-interface (`/interface/wireless`) you need to install a different script.
+Depending on whether you use `wifi` package (`/interface/wifi`), legacy
+wifi with CAPsMAN (`/caps-man`) or local wireless interface
+(`/interface/wireless`) you need to install a different script.
+
+For `wifi`:
+
+ $ScriptInstallUpdate collect-wireless-mac.wifi;
-For CAPsMAN:
+For legacy CAPsMAN:
$ScriptInstallUpdate collect-wireless-mac.capsman;
-For local interface:
+For legacy local interface:
$ScriptInstallUpdate collect-wireless-mac.local;
@@ -42,7 +54,8 @@ entries are to be added.
Also notification settings are required for
[e-mail](mod/notification-email.md),
-[matrix](mod/notification-matrix.md) and/or
+[matrix](mod/notification-matrix.md),
+[ntfy](mod/notification-ntfy.md) and/or
[telegram](mod/notification-telegram.md).
Usage and invocation
@@ -60,5 +73,5 @@ See also
* [Run other scripts on DHCP lease](lease-script.md)
---
-[◀ Go back to main README](../README.md)
-[▲ Go back to top](#top)
+[⬅️ Go back to main README](../README.md)
+[⬆️ Go back to top](#top)
diff --git a/doc/daily-psk.d/notification.avif b/doc/daily-psk.d/notification.avif
new file mode 100644
index 0000000..dd0b1b6
--- /dev/null
+++ b/doc/daily-psk.d/notification.avif
Binary files differ
diff --git a/doc/daily-psk.d/notification.svg b/doc/daily-psk.d/notification.svg
deleted file mode 100644
index 77cf955..0000000
--- a/doc/daily-psk.d/notification.svg
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<svg id="svg8" width="443.78" height="213.78" version="1.1" viewBox="0 0 117.42 56.562" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
- <metadata id="metadata5">
- <rdf:RDF>
- <cc:Work rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <g id="layer1" transform="translate(16.375 11.083)">
- <rect id="rect857" x="-15.875" y="-10.583" width="116.42" height="55.562" rx="1.4552" fill="#e6e6e6" stroke="#6c5d53" stroke-linecap="round" stroke-linejoin="round"/>
- <g id="g884" transform="matrix(.5 0 0 .5 -12.406 -7.1146)" stroke-width="2">
- <path id="path899" d="m23.177 23.177c-2.9635 2.9635-17.991 2.9635-20.955 0-2.9635-2.9635-2.9635-17.991 0-20.955 2.9635-2.9635 17.991-2.9635 20.955 0 2.9635 2.9635 2.9635 17.991 0 20.955z" fill="#fff"/>
- <g id="g871">
- <g id="text837" stroke-width=".52916px" aria-label="#!rsc">
- <path id="path851" d="m7.4832 16.646v-1.0239h-0.54606l0.16577-1.2774h-1.2774l-0.16577 1.2774h-0.97511l0.16577-1.2774h-1.2774l-0.16577 1.2774h-0.70208v1.0239h0.56556l-0.24378 1.8722h-0.68257v1.0239h0.54606l-0.18527 1.3944h1.2774l0.18527-1.3944h0.97511l-0.18527 1.3944h1.2774l0.18527-1.3944h0.70208v-1.0239h-0.56556l0.24378-1.8722zm-2.2037 1.8722h-0.97511l0.24378-1.8722h0.97511z"/>
- <path id="path853" d="m9.6187 14.179h-1.6382l0.19502 4.271h1.2481zm-0.81909 5.1583c-0.48755 0-0.8776 0.39979-0.8776 0.8776 0 0.48755 0.39004 0.88735 0.8776 0.88735 0.4973 0 0.88735-0.39979 0.88735-0.88735 0-0.4778-0.39004-0.8776-0.88735-0.8776z"/>
- <path id="path855" d="m13.373 15.612c-0.59482 0-1.1019 0.42905-1.3359 1.1506l-0.13652-1.0044h-1.3359v5.1778h1.5407v-2.6035c0.17552-0.77033 0.4388-1.2286 1.0726-1.2286 0.16577 0 0.30228 0.02925 0.46805 0.06826l0.24378-1.4919c-0.17552-0.04875-0.32178-0.06826-0.51681-0.06826z"/>
- <path id="path857" d="m16.181 15.592c-1.3066 0-2.116 0.69233-2.116 1.5797 0 0.79959 0.50706 1.3261 1.5309 1.6187 0.9361 0.26328 1.0921 0.37054 1.0921 0.72158 0 0.31203-0.28278 0.48755-0.75083 0.48755-0.50706 0-0.98486-0.20477-1.3749-0.50706l-0.75083 0.83859c0.50706 0.4583 1.2676 0.77033 2.1647 0.77033 1.2871 0 2.3012-0.63382 2.3012-1.7064 0-0.92635-0.57531-1.3554-1.5992-1.6479-0.92635-0.27303-1.0629-0.39004-1.0629-0.66307 0-0.23402 0.20477-0.39004 0.62407-0.39004 0.44855 0 0.8776 0.14627 1.2774 0.39979l0.56556-0.86784c-0.4778-0.39004-1.1506-0.63382-1.9015-0.63382z"/>
- <path id="path859" d="m21.281 15.592c-1.5504 0-2.5353 1.1506-2.5353 2.7986 0 1.6382 0.97511 2.7108 2.5645 2.7108 0.71183 0 1.2676-0.23403 1.7454-0.61432l-0.67282-0.9556c-0.37054 0.23403-0.62407 0.35104-0.99461 0.35104-0.61432 0-1.0239-0.35104-1.0239-1.5017 0-1.1604 0.38029-1.6089 1.0434-1.6089 0.35104 0 0.65332 0.11701 0.98486 0.37054l0.66307-0.9166c-0.4973-0.4193-1.0531-0.63382-1.7747-0.63382z"/>
- </g>
- <g id="g1542" transform="matrix(2 0 0 2 -.41134 3.175)" fill="#676867" fill-rule="evenodd" stroke-width=".26458">
- <path id="path943" d="m4.9596-1.0196c0.40797 2.8312 1.9272 4.5499 5.0239 4.691-2.918 1.1164-5.9253-1.5076-5.0239-4.691"/>
- <path id="path945" d="m3.3407-0.52096c0.034969-0.00777 0.038854 0.015542 0.041445 0.041445 0.098431 1.8054 0.85998 3.1744 1.8689 4.1108 1.0089 0.93639 2.3636 1.6941 4.274 1.6604-3.5772 1.4247-7.337-1.9984-6.1856-5.8126"/>
- </g>
- </g>
- </g>
- <text id="text4811" transform="matrix(.26458 0 0 .26458 -45.014 -6.6039)" x="-248.88142" fill="#000000" font-family="'Fira Mono', 'Roboto Mono', monospace" font-size="12px" letter-spacing="0px" stroke-width="1px" word-spacing="0px" style="font-variant-caps:normal;font-variant-east-asian:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:15px;shape-inside:url(#rect4813);white-space:pre" xml:space="preserve"><tspan id="tspan13351" x="180" y="10.85">[MikroTik] 📅 daily PSK Guest-Wifi
-</tspan><tspan id="tspan13353" x="180" y="25.85">
-</tspan><tspan id="tspan13355" x="180" y="40.85">This is the daily PSK on MikroTik:
-</tspan><tspan id="tspan13357" x="180" y="55.85">
-</tspan><tspan id="tspan13359" x="180" y="70.85">SSID: Guest-Wifi
-</tspan><tspan id="tspan13361" x="180" y="85.85">PSK: S3cr3tStr1ng
-</tspan><tspan id="tspan13363" x="180" y="100.85">Date: jun/17/2021
-</tspan><tspan id="tspan13365" x="180" y="115.85">
-</tspan><tspan id="tspan13367" x="180" y="130.85">A client device specific rule must not exist!</tspan></text>
- <text id="text8530" x="2.6538308" y="34.683964" fill="#000000" font-family="sans-serif" font-size="3.175px" letter-spacing="0px" stroke-width="1px" word-spacing="0px" style="font-variant-caps:normal;font-variant-east-asian:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:2.38125px" xml:space="preserve"><tspan id="tspan25760" x="2.6538308" y="34.683964" style="font-variant-caps:normal;font-variant-east-asian:normal;font-variant-ligatures:normal;font-variant-numeric:normal">🔗 https://www.eworm.de/cgi-bin/cqrlogo-wifi.cgi?</tspan><tspan id="tspan25762" x="2.6538308" y="39.528858" style="font-variant-caps:normal;font-variant-east-asian:normal;font-variant-ligatures:normal;font-variant-numeric:normal">scale=8&amp;level=1&amp;ssid=Guest-Wifi&amp;pass=S3cr3tStr1ng</tspan></text>
- </g>
-</svg>
diff --git a/doc/daily-psk.md b/doc/daily-psk.md
index 62c26ee..f723617 100644
--- a/doc/daily-psk.md
+++ b/doc/daily-psk.md
@@ -1,7 +1,14 @@
Use wireless network with daily psk
===================================
-[◀ Go back to main README](../README.md)
+[![GitHub stars](https://img.shields.io/github/stars/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=red)](https://github.com/eworm-de/routeros-scripts/stargazers)
+[![GitHub forks](https://img.shields.io/github/forks/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=green)](https://github.com/eworm-de/routeros-scripts/network)
+[![GitHub watchers](https://img.shields.io/github/watchers/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=blue)](https://github.com/eworm-de/routeros-scripts/watchers)
+[![required RouterOS version](https://img.shields.io/badge/RouterOS-7.12-yellow?style=flat)](https://mikrotik.com/download/changelogs/)
+[![Telegram group @routeros_scripts](https://img.shields.io/badge/Telegram-%40routeros__scripts-%2326A5E4?logo=telegram&style=flat)](https://t.me/routeros_scripts)
+[![donate with PayPal](https://img.shields.io/badge/Like_it%3F-Donate!-orange?logo=githubsponsors&logoColor=orange&style=flat)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)
+
+[⬅️ Go back to main README](../README.md)
> ℹ️ **Info**: This script can not be used on its own but requires the base
> installation. See [main README](../README.md) for details.
@@ -14,28 +21,35 @@ passphrase to a pseudo-random string daily.
### Sample notification
-![daily-psk notification](daily-psk.d/notification.svg)
+![daily-psk notification](daily-psk.d/notification.avif)
Requirements and installation
-----------------------------
Just install this script.
-Depending on whether you use CAPsMAN (`/caps-man`) or local wireless
-interface (`/interface/wireless`) you need to install a different script.
+Depending on whether you use `wifi` package (`/interface/wifi`), legacy
+wifi with CAPsMAN (`/caps-man`) or local wireless interface
+(`/interface/wireless`) you need to install a different script and add
+schedulers to run the script:
-For CAPsMAN:
+For `wifi`:
- $ScriptInstallUpdate daily-psk.capsman;
+ $ScriptInstallUpdate daily-psk.wifi;
+ /system/scheduler/add interval=1d name=daily-psk on-event="/system/script/run daily-psk.wifi;" start-time=03:00:00;
+ /system/scheduler/add name=daily-psk@startup on-event="/system/script/run daily-psk.wifi;" start-time=startup;
-For local interface:
+For legacy CAPsMAN:
- $ScriptInstallUpdate daily-psk.local;
+ $ScriptInstallUpdate daily-psk.capsman;
+ /system/scheduler/add interval=1d name=daily-psk on-event="/system/script/run daily-psk.capsman;" start-time=03:00:00;
+ /system/scheduler/add name=daily-psk@startup on-event="/system/script/run daily-psk.capsman;" start-time=startup;
-And add schedulers to run the script:
+For legacy local interface:
- /system/scheduler/add interval=1d name=daily-psk-nightly on-event="/system/script/run daily-psk.local;" start-date=may/23/2018 start-time=03:00:00;
- /system/scheduler/add name=daily-psk-startup on-event="/system/script/run daily-psk.local;" start-time=startup;
+ $ScriptInstallUpdate daily-psk.local;
+ /system/scheduler/add interval=1d name=daily-psk on-event="/system/script/run daily-psk.local;" start-time=03:00:00;
+ /system/scheduler/add name=daily-psk@startup on-event="/system/script/run daily-psk.local;" start-time=startup;
These will update the passphrase on boot and nightly at 3:00.
@@ -47,15 +61,28 @@ The configuration goes to `global-config-overlay`, these are the parameters:
* `DailyPskMatchComment`: pattern to match the wireless access list comment
* `DailyPskSecrets`: an array with pseudo random strings
-Then add an access list entry:
+> ℹ️ **Info**: Copy relevant configuration from
+> [`global-config`](../global-config.rsc) (the one without `-overlay`) to
+> your local `global-config-overlay` and modify it to your specific needs.
+
+Then add an access list entry. For `wifi`:
+
+ /interface/wifi/access-list/add comment="Daily PSK" ssid-regexp="-guest\$" passphrase="ToBeChangedDaily";
+
+For legacy CAPsMAN:
+
+ /caps-man/access-list/add comment="Daily PSK" ssid-regexp="-guest\$" private-passphrase="ToBeChangedDaily";
+
+For legacy local interface:
/interface/wireless/access-list/add comment="Daily PSK" interface=wl-daily private-pre-shared-key="ToBeChangedDaily";
Also notification settings are required for
[e-mail](mod/notification-email.md),
-[matrix](mod/notification-matrix.md) and/or
+[trix](mod/notification-matrix.md),
+[ntfy](mod/notification-ntfy.md) and/or
[telegram](mod/notification-telegram.md).
---
-[◀ Go back to main README](../README.md)
-[▲ Go back to top](#top)
+[⬅️ Go back to main README](../README.md)
+[⬆️ Go back to top](#top)
diff --git a/doc/dhcp-lease-comment.md b/doc/dhcp-lease-comment.md
index bb9222e..4831b8c 100644
--- a/doc/dhcp-lease-comment.md
+++ b/doc/dhcp-lease-comment.md
@@ -1,7 +1,14 @@
Comment DHCP leases with info from access list
==============================================
-[◀ Go back to main README](../README.md)
+[![GitHub stars](https://img.shields.io/github/stars/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=red)](https://github.com/eworm-de/routeros-scripts/stargazers)
+[![GitHub forks](https://img.shields.io/github/forks/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=green)](https://github.com/eworm-de/routeros-scripts/network)
+[![GitHub watchers](https://img.shields.io/github/watchers/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=blue)](https://github.com/eworm-de/routeros-scripts/watchers)
+[![required RouterOS version](https://img.shields.io/badge/RouterOS-7.12-yellow?style=flat)](https://mikrotik.com/download/changelogs/)
+[![Telegram group @routeros_scripts](https://img.shields.io/badge/Telegram-%40routeros__scripts-%2326A5E4?logo=telegram&style=flat)](https://t.me/routeros_scripts)
+[![donate with PayPal](https://img.shields.io/badge/Like_it%3F-Donate!-orange?logo=githubsponsors&logoColor=orange&style=flat)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)
+
+[⬅️ Go back to main README](../README.md)
> ℹ️ **Info**: This script can not be used on its own but requires the base
> installation. See [main README](../README.md) for details.
@@ -15,14 +22,19 @@ from wireless access list.
Requirements and installation
-----------------------------
-Depending on whether you use CAPsMAN (`/caps-man`) or local wireless
-interface (`/interface/wireless`) you need to install a different script.
+Depending on whether you use `wifi` package (`/interface/wifi`), legacy
+wifi with CAPsMAN (`/caps-man`) or local wireless interface
+(`/interface/wireless`) you need to install a different script.
+
+For `wifi`:
+
+ $ScriptInstallUpdate dhcp-lease-comment.wifi;
-For CAPsMAN:
+For legacy CAPsMAN:
$ScriptInstallUpdate dhcp-lease-comment.capsman;
-For local interface:
+For legacy local interface:
$ScriptInstallUpdate dhcp-lease-comment.local;
@@ -48,5 +60,5 @@ See also
* [Run other scripts on DHCP lease](lease-script.md)
---
-[◀ Go back to main README](../README.md)
-[▲ Go back to top](#top)
+[⬅️ Go back to main README](../README.md)
+[⬆️ Go back to top](#top)
diff --git a/doc/dhcp-to-dns.md b/doc/dhcp-to-dns.md
index bde7f12..e7f3b88 100644
--- a/doc/dhcp-to-dns.md
+++ b/doc/dhcp-to-dns.md
@@ -1,7 +1,14 @@
Create DNS records for DHCP leases
==================================
-[◀ Go back to main README](../README.md)
+[![GitHub stars](https://img.shields.io/github/stars/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=red)](https://github.com/eworm-de/routeros-scripts/stargazers)
+[![GitHub forks](https://img.shields.io/github/forks/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=green)](https://github.com/eworm-de/routeros-scripts/network)
+[![GitHub watchers](https://img.shields.io/github/watchers/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=blue)](https://github.com/eworm-de/routeros-scripts/watchers)
+[![required RouterOS version](https://img.shields.io/badge/RouterOS-7.12-yellow?style=flat)](https://mikrotik.com/download/changelogs/)
+[![Telegram group @routeros_scripts](https://img.shields.io/badge/Telegram-%40routeros__scripts-%2326A5E4?logo=telegram&style=flat)](https://t.me/routeros_scripts)
+[![donate with PayPal](https://img.shields.io/badge/Like_it%3F-Donate!-orange?logo=githubsponsors&logoColor=orange&style=flat)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)
+
+[⬅️ Go back to main README](../README.md)
> ℹ️ **Info**: This script can not be used on its own but requires the base
> installation. See [main README](../README.md) for details.
@@ -9,7 +16,9 @@ Create DNS records for DHCP leases
Description
-----------
-This script adds (and removes) dns records based on dhcp server leases.
+This script adds (and updates & removes) dns records based on dhcp server
+leases. An A record based on mac address is created for all bound lease,
+additionally a CNAME record is created from host name if available.
Requirements and installation
-----------------------------
@@ -32,12 +41,44 @@ On first run a disabled static dns record acting as marker (with comment
"`--- dhcp-to-dns above ---`") is added. Move this entry to define where new
entries are to be added.
-The configuration goes to `global-config-overlay`, these are the parameters:
+The configuration goes to dhcp server's network definition. The domain is
+used to form the dns name:
+
+ /ip/dhcp-server/network/add address=10.0.0.0/24 domain=example.com;
+
+A bound lease for mac address `00:11:22:33:44:55` with ip address
+`10.0.0.50` would result in an A record `00-11-22-33-44-55.example.com`
+pointing to the given ip address.
+
+Additional options can be given from comment, to add an extra level in
+dns name or define a different domain.
+
+ /ip/dhcp-server/network/add address=10.0.0.0/24 domain=example.com comment="domain=another-domain.com, name-extra=dhcp";
+
+This example would result in name `00-11-22-33-44-55.dhcp.another-domain.com`
+for the same lease.
+
+If no domain is found in dhcp server's network definition a fallback from
+`global-config-overlay` is used. This is the parameter:
* `Domain`: the domain used for dns records
-* `HostNameInZone`: whether or not to add the dhcp/dns server's hostname
-* `PrefixInZone`: whether or not to add prefix `dhcp`
-* `ServerNameInZone`: whether or not to add DHCP server name
+
+> ℹ️ **Info**: Copy relevant configuration from
+> [`global-config`](../global-config.rsc) (the one without `-overlay`) to
+> your local `global-config-overlay` and modify it to your specific needs.
+
+### Host name from DHCP lease comment
+
+Overwriting the host name from dhcp lease comment is supported, just add
+something like `hostname=new-hostname` in comment, and separate it by comma
+from other information if required:
+
+ /ip/dhcp-server/lease/add address=10.0.0.50 comment="my device, hostname=new-hostname" mac-address=00:11:22:33:44:55 server=dhcp;
+
+Note this information can be configured in wireless access list with
+[dhcp-lease-comment](dhcp-lease-comment.md), though it comes with a delay
+then due to script execution order. Decrease the scheduler interval to
+reduce the effect.
See also
--------
@@ -48,5 +89,5 @@ See also
* [Run other scripts on DHCP lease](lease-script.md)
---
-[◀ Go back to main README](../README.md)
-[▲ Go back to top](#top)
+[⬅️ Go back to main README](../README.md)
+[⬆️ Go back to top](#top)
diff --git a/doc/firmware-upgrade-reboot.md b/doc/firmware-upgrade-reboot.md
index 0215ce9..420dfe1 100644
--- a/doc/firmware-upgrade-reboot.md
+++ b/doc/firmware-upgrade-reboot.md
@@ -1,7 +1,14 @@
Automatically upgrade firmware and reboot
=========================================
-[◀ Go back to main README](../README.md)
+[![GitHub stars](https://img.shields.io/github/stars/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=red)](https://github.com/eworm-de/routeros-scripts/stargazers)
+[![GitHub forks](https://img.shields.io/github/forks/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=green)](https://github.com/eworm-de/routeros-scripts/network)
+[![GitHub watchers](https://img.shields.io/github/watchers/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=blue)](https://github.com/eworm-de/routeros-scripts/watchers)
+[![required RouterOS version](https://img.shields.io/badge/RouterOS-7.12-yellow?style=flat)](https://mikrotik.com/download/changelogs/)
+[![Telegram group @routeros_scripts](https://img.shields.io/badge/Telegram-%40routeros__scripts-%2326A5E4?logo=telegram&style=flat)](https://t.me/routeros_scripts)
+[![donate with PayPal](https://img.shields.io/badge/Like_it%3F-Donate!-orange?logo=githubsponsors&logoColor=orange&style=flat)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)
+
+[⬅️ Go back to main README](../README.md)
> ℹ️ **Info**: This script can not be used on its own but requires the base
> installation. See [main README](../README.md) for details.
@@ -32,5 +39,5 @@ See also
* [Manage system update](packages-update.md)
---
-[◀ Go back to main README](../README.md)
-[▲ Go back to top](#top)
+[⬅️ Go back to main README](../README.md)
+[⬆️ Go back to top](#top)
diff --git a/doc/fw-addr-lists.md b/doc/fw-addr-lists.md
new file mode 100644
index 0000000..70ca6e9
--- /dev/null
+++ b/doc/fw-addr-lists.md
@@ -0,0 +1,128 @@
+Download, import and update firewall address-lists
+==================================================
+
+[![GitHub stars](https://img.shields.io/github/stars/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=red)](https://github.com/eworm-de/routeros-scripts/stargazers)
+[![GitHub forks](https://img.shields.io/github/forks/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=green)](https://github.com/eworm-de/routeros-scripts/network)
+[![GitHub watchers](https://img.shields.io/github/watchers/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=blue)](https://github.com/eworm-de/routeros-scripts/watchers)
+[![required RouterOS version](https://img.shields.io/badge/RouterOS-7.12-yellow?style=flat)](https://mikrotik.com/download/changelogs/)
+[![Telegram group @routeros_scripts](https://img.shields.io/badge/Telegram-%40routeros__scripts-%2326A5E4?logo=telegram&style=flat)](https://t.me/routeros_scripts)
+[![donate with PayPal](https://img.shields.io/badge/Like_it%3F-Donate!-orange?logo=githubsponsors&logoColor=orange&style=flat)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)
+
+[⬅️ Go back to main README](../README.md)
+
+> ℹ️ **Info**: This script can not be used on its own but requires the base
+> installation. See [main README](../README.md) for details.
+
+Description
+-----------
+
+This script downloads, imports and updates firewall address-lists. Its main
+purpose is to block attacking ip addresses, spam hosts, command-and-control
+servers and similar malicious entities. The default configuration contains
+lists from [abuse.ch](https://abuse.ch/) and
+[dshield.org](https://dshield.org/), and
+lists from [spamhaus.org](https://spamhaus.org/) are prepared.
+
+The address-lists are updated in place, so after initial import you will not
+see situation when the lists are not populated.
+
+To mitigate man-in-the-middle attacks with altered lists the server's
+certificate is checked.
+
+Requirements and installation
+-----------------------------
+
+Just install the script:
+
+ $ScriptInstallUpdate fw-addr-lists;
+
+And add two schedulers, first one for initial import after startup, second
+one for subsequent updates:
+
+ /system/scheduler/add name="fw-addr-lists@startup" start-time=startup on-event="/system/script/run fw-addr-lists;";
+ /system/scheduler/add name="fw-addr-lists" start-time=startup interval=2h on-event="/system/script/run fw-addr-lists;";
+
+> ℹ️ **Info**: Modify the interval to your needs, but it is recommended to
+> use less than half of the configured timeout for expiration.
+
+Configuration
+-------------
+
+The configuration goes to `global-config-overlay`, these are the parameters:
+
+* `FwAddrLists`: a list of firewall address-lists to download and import
+* `FwAddrListTimeOut`: the timeout for expiration without renew
+
+> ℹ️ **Info**: Copy relevant configuration from
+> [`global-config`](../global-config.rsc) (the one without `-overlay`) to
+> your local `global-config-overlay` and modify it to your specific needs.
+
+Naming a certificate for a list makes the script verify the server
+certificate, so you should add that if possible. Some certificates are
+available in my repository and downloaded automatically. Import it manually
+(menu `/certificate/`) if missing.
+
+Create firewall rules to process the packets that are related to addresses
+from address-lists.
+
+### IPv4 rules
+
+This rejects the packets from and to IPv4 addresses listed in
+address-list `block`.
+
+ /ip/firewall/filter/add chain=input src-address-list=block action=reject reject-with=icmp-admin-prohibited;
+ /ip/firewall/filter/add chain=forward src-address-list=block action=reject reject-with=icmp-admin-prohibited;
+ /ip/firewall/filter/add chain=forward dst-address-list=block action=reject reject-with=icmp-admin-prohibited;
+ /ip/firewall/filter/add chain=output dst-address-list=block action=reject reject-with=icmp-admin-prohibited;
+
+You may want to have an address-list to allow specific addresses, as prepared
+with a list `allow`. In fact you can use any list name, just change the
+default ones or add your own - matching in configuration and firewall rules.
+
+ /ip/firewall/filter/add chain=input src-address-list=allow action=accept;
+ /ip/firewall/filter/add chain=forward src-address-list=allow action=accept;
+ /ip/firewall/filter/add chain=forward dst-address-list=allow action=accept;
+ /ip/firewall/filter/add chain=output dst-address-list=allow action=accept;
+
+Modify these for your needs, but **most important**: Move the rules up in
+chains and make sure they actually take effect as expected!
+
+Alternatively handle the packets in firewall's raw section if you prefer:
+
+ /ip/firewall/raw/add chain=prerouting src-address-list=block action=drop;
+ /ip/firewall/raw/add chain=prerouting dst-address-list=block action=drop;
+ /ip/firewall/raw/add chain=output dst-address-list=block action=drop;
+
+> ⚠️ **Warning**: Just again... The order of firewall rules is important. Make
+> sure they actually take effect as expected!
+
+### IPv6 rules
+
+These are the same rules, but for IPv6.
+
+Reject packets in address-list `block`:
+
+ /ipv6/firewall/filter/add chain=input src-address-list=block action=reject reject-with=icmp-admin-prohibited;
+ /ipv6/firewall/filter/add chain=forward src-address-list=block action=reject reject-with=icmp-admin-prohibited;
+ /ipv6/firewall/filter/add chain=forward dst-address-list=block action=reject reject-with=icmp-admin-prohibited;
+ /ipv6/firewall/filter/add chain=output dst-address-list=block action=reject reject-with=icmp-admin-prohibited;
+
+Allow packets in address-list `allow`:
+
+ /ipv6/firewall/filter/add chain=input src-address-list=allow action=accept;
+ /ipv6/firewall/filter/add chain=forward src-address-list=allow action=accept;
+ /ipv6/firewall/filter/add chain=forward dst-address-list=allow action=accept;
+ /ipv6/firewall/filter/add chain=output dst-address-list=allow action=accept;
+
+Drop packets in firewall's raw section:
+
+ /ipv6/firewall/raw/add chain=prerouting src-address-list=block action=drop;
+ /ipv6/firewall/raw/add chain=prerouting dst-address-list=block action=drop;
+ /ipv6/firewall/raw/add chain=output dst-address-list=block action=drop;
+
+> ⚠️ **Warning**: Just again... The order of firewall rules is important. Make
+> sure they actually take effect as expected!
+
+---
+[⬅️ Go back to main README](../README.md)
+[⬆️ Go back to top](#top)
diff --git a/doc/global-wait.md b/doc/global-wait.md
index ac3e5cc..a3fe6d6 100644
--- a/doc/global-wait.md
+++ b/doc/global-wait.md
@@ -1,7 +1,14 @@
Wait for global functions and modules
=====================================
-[◀ Go back to main README](../README.md)
+[![GitHub stars](https://img.shields.io/github/stars/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=red)](https://github.com/eworm-de/routeros-scripts/stargazers)
+[![GitHub forks](https://img.shields.io/github/forks/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=green)](https://github.com/eworm-de/routeros-scripts/network)
+[![GitHub watchers](https://img.shields.io/github/watchers/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=blue)](https://github.com/eworm-de/routeros-scripts/watchers)
+[![required RouterOS version](https://img.shields.io/badge/RouterOS-7.12-yellow?style=flat)](https://mikrotik.com/download/changelogs/)
+[![Telegram group @routeros_scripts](https://img.shields.io/badge/Telegram-%40routeros__scripts-%2326A5E4?logo=telegram&style=flat)](https://t.me/routeros_scripts)
+[![donate with PayPal](https://img.shields.io/badge/Like_it%3F-Donate!-orange?logo=githubsponsors&logoColor=orange&style=flat)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)
+
+[⬅️ Go back to main README](../README.md)
> ℹ️ **Info**: This script can not be used on its own but requires the base
> installation. See [main README](../README.md) for details.
@@ -36,5 +43,5 @@ See also
* [Manage VLANs on bridge ports](mod/bridge-port-vlan.md)
---
-[◀ Go back to main README](../README.md)
-[▲ Go back to top](#top)
+[⬅️ Go back to main README](../README.md)
+[⬆️ Go back to top](#top)
diff --git a/doc/gps-track.md b/doc/gps-track.md
index c7d28f6..03f79a7 100644
--- a/doc/gps-track.md
+++ b/doc/gps-track.md
@@ -1,7 +1,14 @@
Send GPS position to server
===========================
-[◀ Go back to main README](../README.md)
+[![GitHub stars](https://img.shields.io/github/stars/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=red)](https://github.com/eworm-de/routeros-scripts/stargazers)
+[![GitHub forks](https://img.shields.io/github/forks/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=green)](https://github.com/eworm-de/routeros-scripts/network)
+[![GitHub watchers](https://img.shields.io/github/watchers/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=blue)](https://github.com/eworm-de/routeros-scripts/watchers)
+[![required RouterOS version](https://img.shields.io/badge/RouterOS-7.12-yellow?style=flat)](https://mikrotik.com/download/changelogs/)
+[![Telegram group @routeros_scripts](https://img.shields.io/badge/Telegram-%40routeros__scripts-%2326A5E4?logo=telegram&style=flat)](https://t.me/routeros_scripts)
+[![donate with PayPal](https://img.shields.io/badge/Like_it%3F-Donate!-orange?logo=githubsponsors&logoColor=orange&style=flat)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)
+
+[⬅️ Go back to main README](../README.md)
> ℹ️ **Info**: This script can not be used on its own but requires the base
> installation. See [main README](../README.md) for details.
@@ -32,9 +39,13 @@ The configuration goes to `global-config-overlay`, the only parameter is:
* `GpsTrackUrl`: the url to send json data to
+> ℹ️ **Info**: Copy relevant configuration from
+> [`global-config`](../global-config.rsc) (the one without `-overlay`) to
+> your local `global-config-overlay` and modify it to your specific needs.
+
The configured coordinate format (see `/system/gps`) defines the format
sent to the server.
---
-[◀ Go back to main README](../README.md)
-[▲ Go back to top](#top)
+[⬅️ Go back to main README](../README.md)
+[⬆️ Go back to top](#top)
diff --git a/doc/hotspot-to-wpa.md b/doc/hotspot-to-wpa.md
index 211d4fb..6ce4421 100644
--- a/doc/hotspot-to-wpa.md
+++ b/doc/hotspot-to-wpa.md
@@ -1,7 +1,14 @@
-Use WPA2 network with hotspot credentials
-=========================================
+Use WPA network with hotspot credentials
+========================================
-[◀ Go back to main README](../README.md)
+[![GitHub stars](https://img.shields.io/github/stars/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=red)](https://github.com/eworm-de/routeros-scripts/stargazers)
+[![GitHub forks](https://img.shields.io/github/forks/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=green)](https://github.com/eworm-de/routeros-scripts/network)
+[![GitHub watchers](https://img.shields.io/github/watchers/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=blue)](https://github.com/eworm-de/routeros-scripts/watchers)
+[![required RouterOS version](https://img.shields.io/badge/RouterOS-7.12-yellow?style=flat)](https://mikrotik.com/download/changelogs/)
+[![Telegram group @routeros_scripts](https://img.shields.io/badge/Telegram-%40routeros__scripts-%2326A5E4?logo=telegram&style=flat)](https://t.me/routeros_scripts)
+[![donate with PayPal](https://img.shields.io/badge/Like_it%3F-Donate!-orange?logo=githubsponsors&logoColor=orange&style=flat)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)
+
+[⬅️ Go back to main README](../README.md)
> ℹ️ **Info**: This script can not be used on its own but requires the base
> installation. See [main README](../README.md) for details.
@@ -10,37 +17,56 @@ Description
-----------
RouterOS supports an unlimited number of MAC address specific passphrases
-for WPA2 encrypted wifi networks via access list. The idea of this script
-is to transfer hotspot credentials to MAC address specific WPA2 passphrase.
+for WPA encrypted wifi networks via access list. The idea of this script
+is to transfer hotspot credentials to MAC address specific WPA passphrase.
Requirements and installation
-----------------------------
-You need a properly configured hotspot on one (open) SSID and a WP2 enabled
+You need a properly configured hotspot on one (open) SSID and a WPA enabled
SSID with suffix "`-wpa`".
-Then install the script:
+Then install the script.
+Depending on whether you use `wifi` package (`/interface/wifi`)or legacy
+wifi with CAPsMAN (`/caps-man`) you need to install a different script and
+set it as `on-login` script in hotspot.
+
+For `wifi`:
- $ScriptInstallUpdate hotspot-to-wpa;
+ $ScriptInstallUpdate hotspot-to-wpa.wifi;
+ /ip/hotspot/user/profile/set on-login="hotspot-to-wpa.wifi" [ find ];
-Configure your hotspot to use this script as `on-login` script:
+For legacy CAPsMAN:
- /ip/hotspot/user/profile/set on-login=hotspot-to-wpa [ find ];
+ $ScriptInstallUpdate hotspot-to-wpa.capsman;
+ /ip/hotspot/user/profile/set on-login="hotspot-to-wpa.capsman" [ find ];
### Automatic cleanup
With just `hotspot-to-wpa` installed the mac addresses will last in the
-access list forever. Install the optional script for automatic cleanup:
+access list forever. Install the optional script for automatic cleanup
+and add a scheduler.
+
+For `wifi`:
+
+ $ScriptInstallUpdate hotspot-to-wpa-cleanup.wifi,lease-script;
+ /system/scheduler/add interval=1d name=hotspot-to-wpa-cleanup on-event="/system/script/run hotspot-to-wpa-cleanup.wifi;" start-time=startup;
- $ScriptInstallUpdate hotspot-to-wpa-cleanup,lease-script;
+For legacy CAPsMAN:
-Create a scheduler:
+ $ScriptInstallUpdate hotspot-to-wpa-cleanup.capsman,lease-script;
+ /system/scheduler/add interval=1d name=hotspot-to-wpa-cleanup on-event="/system/script/run hotspot-to-wpa-cleanup.capsman;" start-time=startup;
- /system/scheduler/add interval=1d name=hotspot-to-wpa-cleanup on-event="/system/script/run hotspot-to-wpa-cleanup;" start-time=startup;
+And add the lease script and matcher comment to your wpa interfaces' dhcp
+server. You can add more information to the comment, separated by comma. In
+this example the server is called `hotspot-to-wpa`.
-And add the lease script to your wpa interfaces' dhcp server:
+ /ip/dhcp-server/set lease-script=lease-script comment="hotspot-to-wpa=wpa" hotspot-to-wpa;
- /ip/dhcp-server/set lease-script=lease-script [ find where name~"wpa" ];
+You can specify the timeout after which a device is removed from leases and
+access-list. The default is four weeks.
+
+ /ip/dhcp-server/set lease-script=lease-script comment="hotspot-to-wpa=wpa, timeout=2w" hotspot-to-wpa;
Configuration
-------------
@@ -54,17 +80,25 @@ Create hotspot login credentials:
/ip/hotspot/user/add comment="Test User 1" name=user1 password=v3ry;
/ip/hotspot/user/add comment="Test User 2" name=user2 password=s3cr3t;
+This also works with authentication via radius, but is limited then:
+Additional information is not available, including the password.
+
Additionally templates can be created to give more options for access list:
* `action`: set to `reject` to ignore logins on that hotspot
-* `private-passphrase`: do **not** use passphrase from hotspot's user
- credentials, but given one - or unset (use default passphrase) with
- special word `ignore`
+* `passphrase` or `private-passphrase`: do **not** use passphrase from
+ hotspot's user credentials, but given one - or unset (use default
+ passphrase) with special word `ignore`
* `ssid-regexp`: set a different SSID regular expression to match
* `vlan-id`: connect device to specific VLAN
* `vlan-mode`: set the VLAN mode for device
-For a hotspot called `example` the template could look like this:
+For a hotspot called `example` the template could look like this.
+For `wifi`:
+
+ /interface/wifi/access-list/add comment="hotspot-to-wpa template example" disabled=yes passphrase="ignore" ssid-regexp="^example\$" vlan-id=10;
+
+For legacy CAPsMAN:
/caps-man/access-list/add comment="hotspot-to-wpa template example" disabled=yes private-passphrase="ignore" ssid-regexp="^example\$" vlan-id=10 vlan-mode=use-tag;
@@ -77,7 +111,7 @@ Usage and invocation
--------------------
Now let the users connect and login to the hotspot. After that the devices
-(identified by MAC address) can connect to the WPA2 network, using the
+(identified by MAC address) can connect to the WPA network, using the
passphrase from hotspot credentials.
See also
@@ -86,5 +120,5 @@ See also
* [Run other scripts on DHCP lease](lease-script.md)
---
-[◀ Go back to main README](../README.md)
-[▲ Go back to top](#top)
+[⬅️ Go back to main README](../README.md)
+[⬆️ Go back to top](#top)
diff --git a/doc/ip-addr-bridge.md b/doc/ip-addr-bridge.md
index f0b593e..8bb9811 100644
--- a/doc/ip-addr-bridge.md
+++ b/doc/ip-addr-bridge.md
@@ -1,7 +1,14 @@
Manage IP addresses with bridge status
======================================
-[◀ Go back to main README](../README.md)
+[![GitHub stars](https://img.shields.io/github/stars/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=red)](https://github.com/eworm-de/routeros-scripts/stargazers)
+[![GitHub forks](https://img.shields.io/github/forks/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=green)](https://github.com/eworm-de/routeros-scripts/network)
+[![GitHub watchers](https://img.shields.io/github/watchers/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=blue)](https://github.com/eworm-de/routeros-scripts/watchers)
+[![required RouterOS version](https://img.shields.io/badge/RouterOS-7.12-yellow?style=flat)](https://mikrotik.com/download/changelogs/)
+[![Telegram group @routeros_scripts](https://img.shields.io/badge/Telegram-%40routeros__scripts-%2326A5E4?logo=telegram&style=flat)](https://t.me/routeros_scripts)
+[![donate with PayPal](https://img.shields.io/badge/Like_it%3F-Donate!-orange?logo=githubsponsors&logoColor=orange&style=flat)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)
+
+[⬅️ Go back to main README](../README.md)
Description
-----------
@@ -28,5 +35,5 @@ Note that IP addresses on bridges without a single port (acting as loopback
interface) are ignored.
---
-[◀ Go back to main README](../README.md)
-[▲ Go back to top](#top)
+[⬅️ Go back to main README](../README.md)
+[⬆️ Go back to top](#top)
diff --git a/doc/ipsec-to-dns.md b/doc/ipsec-to-dns.md
index 87ad21a..ca5b86c 100644
--- a/doc/ipsec-to-dns.md
+++ b/doc/ipsec-to-dns.md
@@ -1,7 +1,14 @@
Create DNS records for IPSec peers
==================================
-[◀ Go back to main README](../README.md)
+[![GitHub stars](https://img.shields.io/github/stars/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=red)](https://github.com/eworm-de/routeros-scripts/stargazers)
+[![GitHub forks](https://img.shields.io/github/forks/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=green)](https://github.com/eworm-de/routeros-scripts/network)
+[![GitHub watchers](https://img.shields.io/github/watchers/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=blue)](https://github.com/eworm-de/routeros-scripts/watchers)
+[![required RouterOS version](https://img.shields.io/badge/RouterOS-7.12-yellow?style=flat)](https://mikrotik.com/download/changelogs/)
+[![Telegram group @routeros_scripts](https://img.shields.io/badge/Telegram-%40routeros__scripts-%2326A5E4?logo=telegram&style=flat)](https://t.me/routeros_scripts)
+[![donate with PayPal](https://img.shields.io/badge/Like_it%3F-Donate!-orange?logo=githubsponsors&logoColor=orange&style=flat)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)
+
+[⬅️ Go back to main README](../README.md)
> ℹ️ **Info**: This script can not be used on its own but requires the base
> installation. See [main README](../README.md) for details.
@@ -36,11 +43,15 @@ The configuration goes to `global-config-overlay`, these are the parameters:
* `HostNameInZone`: whether or not to add the ipsec/dns server's hostname
* `PrefixInZone`: whether or not to add prefix `ipsec`
+> ℹ️ **Info**: Copy relevant configuration from
+> [`global-config`](../global-config.rsc) (the one without `-overlay`) to
+> your local `global-config-overlay` and modify it to your specific needs.
+
See also
--------
* [Create DNS records for DHCP leases](dns-to-dhcp.md)
---
-[◀ Go back to main README](../README.md)
-[▲ Go back to top](#top)
+[⬅️ Go back to main README](../README.md)
+[⬆️ Go back to top](#top)
diff --git a/doc/ipv6-update.md b/doc/ipv6-update.md
index 7c5a943..a5661fc 100644
--- a/doc/ipv6-update.md
+++ b/doc/ipv6-update.md
@@ -1,7 +1,14 @@
Update configuration on IPv6 prefix change
==========================================
-[◀ Go back to main README](../README.md)
+[![GitHub stars](https://img.shields.io/github/stars/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=red)](https://github.com/eworm-de/routeros-scripts/stargazers)
+[![GitHub forks](https://img.shields.io/github/forks/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=green)](https://github.com/eworm-de/routeros-scripts/network)
+[![GitHub watchers](https://img.shields.io/github/watchers/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=blue)](https://github.com/eworm-de/routeros-scripts/watchers)
+[![required RouterOS version](https://img.shields.io/badge/RouterOS-7.12-yellow?style=flat)](https://mikrotik.com/download/changelogs/)
+[![Telegram group @routeros_scripts](https://img.shields.io/badge/Telegram-%40routeros__scripts-%2326A5E4?logo=telegram&style=flat)](https://t.me/routeros_scripts)
+[![donate with PayPal](https://img.shields.io/badge/Like_it%3F-Donate!-orange?logo=githubsponsors&logoColor=orange&style=flat)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)
+
+[⬅️ Go back to main README](../README.md)
> ℹ️ **Info**: This script can not be used on its own but requires the base
> installation. See [main README](../README.md) for details.
@@ -11,7 +18,7 @@ Description
With changing IPv6 prefix from ISP this script handles to update...
-* ipv6 firewall address-list
+* ipv6 firewall address-list (prefixes (`/64`) and host addresses (`/128`))
* dns records
Requirements and installation
@@ -52,6 +59,11 @@ has to be associated to an interface in comment:
/ipv6/firewall/address-list/add address=2003:cf:2f0f:de01::/64 comment="ipv6-pool-isp, interface=br-local" list=local;
+Updating address list entries with host addresses works as well, the new
+prefix is combinded with given suffix then:
+
+ /ipv6/firewall/address-list/add address=2003:cf:2f0f:de01:e3e0:f8fa:8cd6:dbe1/128 comment="ipv6-pool-isp, interface=br-local" list=hosts;
+
Static DNS records need a special comment to be updated. Again it has to
start with "`ipv6-pool-`" and actual pool name, followed by a comma,
"`interface=`" and the name of interface this address is connected to:
@@ -64,5 +76,5 @@ See also
* [Run scripts on ppp connection](ppp-on-up.md)
---
-[◀ Go back to main README](../README.md)
-[▲ Go back to top](#top)
+[⬅️ Go back to main README](../README.md)
+[⬆️ Go back to top](#top)
diff --git a/doc/lease-script.md b/doc/lease-script.md
index a435d43..f346621 100644
--- a/doc/lease-script.md
+++ b/doc/lease-script.md
@@ -1,7 +1,14 @@
Run other scripts on DHCP lease
===============================
-[◀ Go back to main README](../README.md)
+[![GitHub stars](https://img.shields.io/github/stars/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=red)](https://github.com/eworm-de/routeros-scripts/stargazers)
+[![GitHub forks](https://img.shields.io/github/forks/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=green)](https://github.com/eworm-de/routeros-scripts/network)
+[![GitHub watchers](https://img.shields.io/github/watchers/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=blue)](https://github.com/eworm-de/routeros-scripts/watchers)
+[![required RouterOS version](https://img.shields.io/badge/RouterOS-7.12-yellow?style=flat)](https://mikrotik.com/download/changelogs/)
+[![Telegram group @routeros_scripts](https://img.shields.io/badge/Telegram-%40routeros__scripts-%2326A5E4?logo=telegram&style=flat)](https://t.me/routeros_scripts)
+[![donate with PayPal](https://img.shields.io/badge/Like_it%3F-Donate!-orange?logo=githubsponsors&logoColor=orange&style=flat)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)
+
+[⬅️ Go back to main README](../README.md)
> ℹ️ **Info**: This script can not be used on its own but requires the base
> installation. See [main README](../README.md) for details.
@@ -40,8 +47,8 @@ See also
* [Collect MAC addresses in wireless access list](collect-wireless-mac.md)
* [Comment DHCP leases with info from access list](dhcp-lease-comment.md)
* [Create DNS records for DHCP leases](dhcp-to-dns.md)
-* [Use WPA2 network with hotspot credentials](hotspot-to-wpa.md)
+* [Use WPA network with hotspot credentials](hotspot-to-wpa.md)
---
-[◀ Go back to main README](../README.md)
-[▲ Go back to top](#top)
+[⬅️ Go back to main README](../README.md)
+[⬆️ Go back to top](#top)
diff --git a/doc/leds-mode.md b/doc/leds-mode.md
index 65f9f01..5dd5f63 100644
--- a/doc/leds-mode.md
+++ b/doc/leds-mode.md
@@ -1,7 +1,14 @@
Manage LEDs dark mode
=====================
-[◀ Go back to main README](../README.md)
+[![GitHub stars](https://img.shields.io/github/stars/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=red)](https://github.com/eworm-de/routeros-scripts/stargazers)
+[![GitHub forks](https://img.shields.io/github/forks/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=green)](https://github.com/eworm-de/routeros-scripts/network)
+[![GitHub watchers](https://img.shields.io/github/watchers/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=blue)](https://github.com/eworm-de/routeros-scripts/watchers)
+[![required RouterOS version](https://img.shields.io/badge/RouterOS-7.12-yellow?style=flat)](https://mikrotik.com/download/changelogs/)
+[![Telegram group @routeros_scripts](https://img.shields.io/badge/Telegram-%40routeros__scripts-%2326A5E4?logo=telegram&style=flat)](https://t.me/routeros_scripts)
+[![donate with PayPal](https://img.shields.io/badge/Like_it%3F-Donate!-orange?logo=githubsponsors&logoColor=orange&style=flat)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)
+
+[⬅️ Go back to main README](../README.md)
Description
-----------
@@ -46,5 +53,5 @@ See also
* [Mode button with multiple presses](mode-button.md)
---
-[◀ Go back to main README](../README.md)
-[▲ Go back to top](#top)
+[⬅️ Go back to main README](../README.md)
+[⬆️ Go back to top](#top)
diff --git a/doc/log-forward.d/notification.avif b/doc/log-forward.d/notification.avif
new file mode 100644
index 0000000..a0f9ab3
--- /dev/null
+++ b/doc/log-forward.d/notification.avif
Binary files differ
diff --git a/doc/log-forward.d/notification.svg b/doc/log-forward.d/notification.svg
deleted file mode 100644
index 9527411..0000000
--- a/doc/log-forward.d/notification.svg
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<svg id="svg8" width="573.78" height="203.78" version="1.1" viewBox="0 0 151.81 53.917" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
- <metadata id="metadata5">
- <rdf:RDF>
- <cc:Work rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <g id="layer1" transform="translate(16.375 11.083)">
- <rect id="rect857" x="-15.875" y="-10.583" width="150.81" height="52.917" rx="1.8852" fill="#e6e6e6" stroke="#6c5d53" stroke-linecap="round" stroke-linejoin="round"/>
- <g id="g884" transform="matrix(.5 0 0 .5 -12.406 -7.1146)" stroke-width="2">
- <path id="path899" d="m23.177 23.177c-2.9635 2.9635-17.991 2.9635-20.955 0-2.9635-2.9635-2.9635-17.991 0-20.955 2.9635-2.9635 17.991-2.9635 20.955 0 2.9635 2.9635 2.9635 17.991 0 20.955z" fill="#fff"/>
- <g id="g871">
- <g id="text837" stroke-width=".52916px" aria-label="#!rsc">
- <path id="path851" d="m7.4832 16.646v-1.0239h-0.54606l0.16577-1.2774h-1.2774l-0.16577 1.2774h-0.97511l0.16577-1.2774h-1.2774l-0.16577 1.2774h-0.70208v1.0239h0.56556l-0.24378 1.8722h-0.68257v1.0239h0.54606l-0.18527 1.3944h1.2774l0.18527-1.3944h0.97511l-0.18527 1.3944h1.2774l0.18527-1.3944h0.70208v-1.0239h-0.56556l0.24378-1.8722zm-2.2037 1.8722h-0.97511l0.24378-1.8722h0.97511z"/>
- <path id="path853" d="m9.6187 14.179h-1.6382l0.19502 4.271h1.2481zm-0.81909 5.1583c-0.48755 0-0.8776 0.39979-0.8776 0.8776 0 0.48755 0.39004 0.88735 0.8776 0.88735 0.4973 0 0.88735-0.39979 0.88735-0.88735 0-0.4778-0.39004-0.8776-0.88735-0.8776z"/>
- <path id="path855" d="m13.373 15.612c-0.59482 0-1.1019 0.42905-1.3359 1.1506l-0.13652-1.0044h-1.3359v5.1778h1.5407v-2.6035c0.17552-0.77033 0.4388-1.2286 1.0726-1.2286 0.16577 0 0.30228 0.02925 0.46805 0.06826l0.24378-1.4919c-0.17552-0.04875-0.32178-0.06826-0.51681-0.06826z"/>
- <path id="path857" d="m16.181 15.592c-1.3066 0-2.116 0.69233-2.116 1.5797 0 0.79959 0.50706 1.3261 1.5309 1.6187 0.9361 0.26328 1.0921 0.37054 1.0921 0.72158 0 0.31203-0.28278 0.48755-0.75083 0.48755-0.50706 0-0.98486-0.20477-1.3749-0.50706l-0.75083 0.83859c0.50706 0.4583 1.2676 0.77033 2.1647 0.77033 1.2871 0 2.3012-0.63382 2.3012-1.7064 0-0.92635-0.57531-1.3554-1.5992-1.6479-0.92635-0.27303-1.0629-0.39004-1.0629-0.66307 0-0.23402 0.20477-0.39004 0.62407-0.39004 0.44855 0 0.8776 0.14627 1.2774 0.39979l0.56556-0.86784c-0.4778-0.39004-1.1506-0.63382-1.9015-0.63382z"/>
- <path id="path859" d="m21.281 15.592c-1.5504 0-2.5353 1.1506-2.5353 2.7986 0 1.6382 0.97511 2.7108 2.5645 2.7108 0.71183 0 1.2676-0.23403 1.7454-0.61432l-0.67282-0.9556c-0.37054 0.23403-0.62407 0.35104-0.99461 0.35104-0.61432 0-1.0239-0.35104-1.0239-1.5017 0-1.1604 0.38029-1.6089 1.0434-1.6089 0.35104 0 0.65332 0.11701 0.98486 0.37054l0.66307-0.9166c-0.4973-0.4193-1.0531-0.63382-1.7747-0.63382z"/>
- </g>
- <g id="g1542" transform="matrix(2 0 0 2 -.41134 3.175)" fill="#676867" fill-rule="evenodd" stroke-width=".26458">
- <path id="path943" d="m4.9596-1.0196c0.40797 2.8312 1.9272 4.5499 5.0239 4.691-2.918 1.1164-5.9253-1.5076-5.0239-4.691"/>
- <path id="path945" d="m3.3407-0.52096c0.034969-0.00777 0.038854 0.015542 0.041445 0.041445 0.098431 1.8054 0.85998 3.1744 1.8689 4.1108 1.0089 0.93639 2.3636 1.6941 4.274 1.6604-3.5772 1.4247-7.337-1.9984-6.1856-5.8126"/>
- </g>
- </g>
- </g>
- <text id="text4811" transform="matrix(.26458 0 0 .26458 -44.95 -6.6039)" x="-248.88142" fill="#000000" font-family="'Fira Mono', 'Roboto Mono', monospace" font-size="12px" letter-spacing="0px" stroke-width="1px" word-spacing="0px" style="font-variant-caps:normal;font-variant-east-asian:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:15px;shape-inside:url(#rect4813);white-space:pre" xml:space="preserve"><tspan id="tspan2317" x="180" y="10.85">[MikroTik] ⚠ Log Forwarding
-</tspan><tspan id="tspan2319" x="180" y="26.082813">
-</tspan><tspan id="tspan2321" x="180" y="41.082813">The log on MikroTik contains these 3 messages after 6d23:55:18 </tspan><tspan id="tspan2323" x="180" y="56.082811">uptime.
-</tspan><tspan id="tspan2325" x="180" y="71.082811">
-</tspan><tspan id="tspan2327" x="180" y="86.082811"> ● 13:24:02 script;error backup-cloud: Failed uploading backup </tspan><tspan id="tspan2329" x="180" y="101.08281">for MikroTik to cloud!
-</tspan><tspan id="tspan2331" x="180" y="116.08281"> ● 13:24:17 system;info;account user admin logged in from </tspan><tspan id="tspan2333" x="180" y="131.08281">192.168.88.177 via ssh
-</tspan><tspan id="tspan2335" x="180" y="146.08282"> ● 13:24:57 system;info;account user admin logged out from </tspan><tspan id="tspan2337" x="180" y="161.08282">192.168.88.177 via ssh</tspan></text>
- </g>
-</svg>
diff --git a/doc/log-forward.md b/doc/log-forward.md
index 40a4135..4183d7b 100644
--- a/doc/log-forward.md
+++ b/doc/log-forward.md
@@ -1,7 +1,14 @@
Forward log messages via notification
=====================================
-[◀ Go back to main README](../README.md)
+[![GitHub stars](https://img.shields.io/github/stars/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=red)](https://github.com/eworm-de/routeros-scripts/stargazers)
+[![GitHub forks](https://img.shields.io/github/forks/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=green)](https://github.com/eworm-de/routeros-scripts/network)
+[![GitHub watchers](https://img.shields.io/github/watchers/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=blue)](https://github.com/eworm-de/routeros-scripts/watchers)
+[![required RouterOS version](https://img.shields.io/badge/RouterOS-7.12-yellow?style=flat)](https://mikrotik.com/download/changelogs/)
+[![Telegram group @routeros_scripts](https://img.shields.io/badge/Telegram-%40routeros__scripts-%2326A5E4?logo=telegram&style=flat)](https://t.me/routeros_scripts)
+[![donate with PayPal](https://img.shields.io/badge/Like_it%3F-Donate!-orange?logo=githubsponsors&logoColor=orange&style=flat)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)
+
+[⬅️ Go back to main README](../README.md)
> ℹ️ **Info**: This script can not be used on its own but requires the base
> installation. See [main README](../README.md) for details.
@@ -9,20 +16,28 @@ Forward log messages via notification
Description
-----------
-RouterOS supports sending log messages via e-mail or to a syslog server.
-This has some limitation, however:
+RouterOS itself supports sending log messages via e-mail or to a syslog
+server (see `/system/logging`). This has some limitation, however:
* does not work early after boot if network connectivity is not
- yet established
+ yet established, or breaks intermittently
* lots of messages generate a flood of mails
* Matrix and Telegram are not supported
-The script is intended to be run periodically. It collects log messages
-and forwards them via notification.
+The script works around the limitations, for example it does:
+
+* read from `/log`, including messages from early boot
+* skip multi-repeated messages
+* rate-limit itself to mitigate flooding
+* forward via notification (which includes *e-mail*, *Matrix* and *Telegram*
+ when installed and configured, see below)
+
+It is intended to be run periodically from scheduler, then collects new
+log messages and forwards them via notification.
### Sample notification
-![log-forward notification](log-forward.d/notification.svg)
+![log-forward notification](log-forward.d/notification.avif)
Requirements and installation
-----------------------------
@@ -46,11 +61,35 @@ The configuration goes to `global-config-overlay`, these are the parameters:
* `LogForwardIncludeMessage`: define message text to be forwarded (even if
filter matches)
+> ℹ️ **Info**: Copy relevant configuration from
+> [`global-config`](../global-config.rsc) (the one without `-overlay`) to
+> your local `global-config-overlay` and modify it to your specific needs.
+
+These patterns are matched as
+[regular expressions](https://wiki.mikrotik.com/wiki/Manual:Regular_Expressions).
+To forward **all** (ignoring severity) log messages with topics `account`
+(which includes user logins) and `dhcp` you need something like:
+
+ :global LogForwardInclude "(account|dhcp)";
+
Also notification settings are required for
[e-mail](mod/notification-email.md),
-[matrix](mod/notification-matrix.md) and/or
+[matrix](mod/notification-matrix.md),
+[ntfy](mod/notification-ntfy.md) and/or
[telegram](mod/notification-telegram.md).
+Tips & Tricks
+-------------
+
+### Notification on reboot
+
+You want to receive a notification on every device (re-)boot? Quite easy,
+just add:
+
+ :global LogForwardIncludeMessage "(^router rebooted)";
+
+This will match on every log message beginning with `router rebooted`.
+
---
-[◀ Go back to main README](../README.md)
-[▲ Go back to top](#top)
+[⬅️ Go back to main README](../README.md)
+[⬆️ Go back to top](#top)
diff --git a/doc/mod/bridge-port-to.md b/doc/mod/bridge-port-to.md
index f86b21d..838d1e0 100644
--- a/doc/mod/bridge-port-to.md
+++ b/doc/mod/bridge-port-to.md
@@ -1,7 +1,14 @@
Manage ports in bridge
======================
-[◀ Go back to main README](../../README.md)
+[![GitHub stars](https://img.shields.io/github/stars/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=red)](https://github.com/eworm-de/routeros-scripts/stargazers)
+[![GitHub forks](https://img.shields.io/github/forks/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=green)](https://github.com/eworm-de/routeros-scripts/network)
+[![GitHub watchers](https://img.shields.io/github/watchers/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=blue)](https://github.com/eworm-de/routeros-scripts/watchers)
+[![required RouterOS version](https://img.shields.io/badge/RouterOS-7.12-yellow?style=flat)](https://mikrotik.com/download/changelogs/)
+[![Telegram group @routeros_scripts](https://img.shields.io/badge/Telegram-%40routeros__scripts-%2326A5E4?logo=telegram&style=flat)](https://t.me/routeros_scripts)
+[![donate with PayPal](https://img.shields.io/badge/Like_it%3F-Donate!-orange?logo=githubsponsors&logoColor=orange&style=flat)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)
+
+[⬅️ Go back to main README](../../README.md)
> ℹ️️ **Info**: This module can not be used on its own but requires the base
> installation. See [main README](../../README.md) for details.
@@ -77,5 +84,5 @@ See also
* [Manage VLANs on bridge ports](bridge-port-vlan.md)
---
-[◀ Go back to main README](../../README.md)
-[▲ Go back to top](#top)
+[⬅️ Go back to main README](../../README.md)
+[⬆️ Go back to top](#top)
diff --git a/doc/mod/bridge-port-vlan.md b/doc/mod/bridge-port-vlan.md
index 0e6c28f..5660b9d 100644
--- a/doc/mod/bridge-port-vlan.md
+++ b/doc/mod/bridge-port-vlan.md
@@ -1,7 +1,14 @@
Manage VLANs on bridge ports
============================
-[◀ Go back to main README](../../README.md)
+[![GitHub stars](https://img.shields.io/github/stars/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=red)](https://github.com/eworm-de/routeros-scripts/stargazers)
+[![GitHub forks](https://img.shields.io/github/forks/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=green)](https://github.com/eworm-de/routeros-scripts/network)
+[![GitHub watchers](https://img.shields.io/github/watchers/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=blue)](https://github.com/eworm-de/routeros-scripts/watchers)
+[![required RouterOS version](https://img.shields.io/badge/RouterOS-7.12-yellow?style=flat)](https://mikrotik.com/download/changelogs/)
+[![Telegram group @routeros_scripts](https://img.shields.io/badge/Telegram-%40routeros__scripts-%2326A5E4?logo=telegram&style=flat)](https://t.me/routeros_scripts)
+[![donate with PayPal](https://img.shields.io/badge/Like_it%3F-Donate!-orange?logo=githubsponsors&logoColor=orange&style=flat)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)
+
+[⬅️ Go back to main README](../../README.md)
> ℹ️️ **Info**: This module can not be used on its own but requires the base
> installation. See [main README](../../README.md) for details.
@@ -81,5 +88,5 @@ See also
* [Manage ports in bridge](bridge-port-to.md)
---
-[◀ Go back to main README](../../README.md)
-[▲ Go back to top](#top)
+[⬅️ Go back to main README](../../README.md)
+[⬆️ Go back to top](#top)
diff --git a/doc/mod/inspectvar.md b/doc/mod/inspectvar.md
index 7782c8a..7ec74f2 100644
--- a/doc/mod/inspectvar.md
+++ b/doc/mod/inspectvar.md
@@ -1,7 +1,14 @@
Inspect variables
=================
-[◀ Go back to main README](../../README.md)
+[![GitHub stars](https://img.shields.io/github/stars/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=red)](https://github.com/eworm-de/routeros-scripts/stargazers)
+[![GitHub forks](https://img.shields.io/github/forks/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=green)](https://github.com/eworm-de/routeros-scripts/network)
+[![GitHub watchers](https://img.shields.io/github/watchers/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=blue)](https://github.com/eworm-de/routeros-scripts/watchers)
+[![required RouterOS version](https://img.shields.io/badge/RouterOS-7.12-yellow?style=flat)](https://mikrotik.com/download/changelogs/)
+[![Telegram group @routeros_scripts](https://img.shields.io/badge/Telegram-%40routeros__scripts-%2326A5E4?logo=telegram&style=flat)](https://t.me/routeros_scripts)
+[![donate with PayPal](https://img.shields.io/badge/Like_it%3F-Donate!-orange?logo=githubsponsors&logoColor=orange&style=flat)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)
+
+[⬅️ Go back to main README](../../README.md)
> ℹ️️ **Info**: This module can not be used on its own but requires the base
> installation. See [main README](../../README.md) for details.
@@ -29,5 +36,5 @@ Call the function `$InspectVar` with a variable as parameter:
![InspectVar](inspectvar.d/inspectvar.avif)
---
-[◀ Go back to main README](../../README.md)
-[▲ Go back to top](#top)
+[⬅️ Go back to main README](../../README.md)
+[⬆️ Go back to top](#top)
diff --git a/doc/mod/ipcalc.md b/doc/mod/ipcalc.md
index a3e7fc8..5b24952 100644
--- a/doc/mod/ipcalc.md
+++ b/doc/mod/ipcalc.md
@@ -1,7 +1,14 @@
IP address calculation
======================
-[◀ Go back to main README](../../README.md)
+[![GitHub stars](https://img.shields.io/github/stars/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=red)](https://github.com/eworm-de/routeros-scripts/stargazers)
+[![GitHub forks](https://img.shields.io/github/forks/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=green)](https://github.com/eworm-de/routeros-scripts/network)
+[![GitHub watchers](https://img.shields.io/github/watchers/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=blue)](https://github.com/eworm-de/routeros-scripts/watchers)
+[![required RouterOS version](https://img.shields.io/badge/RouterOS-7.12-yellow?style=flat)](https://mikrotik.com/download/changelogs/)
+[![Telegram group @routeros_scripts](https://img.shields.io/badge/Telegram-%40routeros__scripts-%2326A5E4?logo=telegram&style=flat)](https://t.me/routeros_scripts)
+[![donate with PayPal](https://img.shields.io/badge/Like_it%3F-Donate!-orange?logo=githubsponsors&logoColor=orange&style=flat)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)
+
+[⬅️ Go back to main README](../../README.md)
> ℹ️️ **Info**: This module can not be used on its own but requires the base
> installation. See [main README](../../README.md) for details.
@@ -49,5 +56,5 @@ the information in a named array.
![IPCalcReturn](ipcalc.d/ipcalcreturn.avif)
---
-[◀ Go back to main README](../../README.md)
-[▲ Go back to top](#top)
+[⬅️ Go back to main README](../../README.md)
+[⬆️ Go back to top](#top)
diff --git a/doc/mod/notification-email.md b/doc/mod/notification-email.md
index 4e0ba04..76816c1 100644
--- a/doc/mod/notification-email.md
+++ b/doc/mod/notification-email.md
@@ -1,7 +1,14 @@
Send notifications via e-mail
=============================
-[◀ Go back to main README](../../README.md)
+[![GitHub stars](https://img.shields.io/github/stars/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=red)](https://github.com/eworm-de/routeros-scripts/stargazers)
+[![GitHub forks](https://img.shields.io/github/forks/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=green)](https://github.com/eworm-de/routeros-scripts/network)
+[![GitHub watchers](https://img.shields.io/github/watchers/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=blue)](https://github.com/eworm-de/routeros-scripts/watchers)
+[![required RouterOS version](https://img.shields.io/badge/RouterOS-7.12-yellow?style=flat)](https://mikrotik.com/download/changelogs/)
+[![Telegram group @routeros_scripts](https://img.shields.io/badge/Telegram-%40routeros__scripts-%2326A5E4?logo=telegram&style=flat)](https://t.me/routeros_scripts)
+[![donate with PayPal](https://img.shields.io/badge/Like_it%3F-Donate!-orange?logo=githubsponsors&logoColor=orange&style=flat)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)
+
+[⬅️ Go back to main README](../../README.md)
> ℹ️️ **Info**: This module can not be used on its own but requires the base
> installation. See [main README](../../README.md) for details.
@@ -26,10 +33,16 @@ Configuration
Set up your device's
[e-mail settings](https://wiki.mikrotik.com/wiki/Manual:Tools/email).
+Also make sure the device has correct time configured, best is to set up
+the ntp client.
Then edit `global-config-overlay`, add `EmailGeneralTo` with a valid
recipient address. Finally reload the configuration.
+> ℹ️ **Info**: Copy relevant configuration from
+> [`global-config`](../../global-config.rsc) (the one without `-overlay`) to
+> your local `global-config-overlay` and modify it to your specific needs.
+
### Sending to several recipients
Sending notifications to several recipients is possible as well. Add
@@ -42,22 +55,34 @@ Usage and invocation
There's nothing special to do. Every script or function sending a notification
will now send it to your e-mail account.
-But of course you can send notifications directly or use a function in your
-own scripts. Give it a try:
+But of course you can use the function to send notifications directly. Give
+it a try:
- $SendEMail "Subject..." "Body..."
+ $SendEMail "Subject..." "Body...";
Alternatively this sends a notification with all available and configured
methods:
- $SendNotification "Subject..." "Body..."
+ $SendNotification "Subject..." "Body...";
+
+To use the functions in your own scripts you have to declare them first.
+Place this before you call them:
+
+ :global SendEMail;
+ :global SendNotification;
+
+In case there is a situation when the queue needs to be purged there is a
+function available:
+
+ $PurgeEMailQueue;
See also
--------
* [Send notifications via Matrix](notification-matrix.md)
+* [Send notifications via Ntfy](notification-ntfy.md)
* [Send notifications via Telegram](notification-telegram.md)
---
-[◀ Go back to main README](../../README.md)
-[▲ Go back to top](#top)
+[⬅️ Go back to main README](../../README.md)
+[⬆️ Go back to top](#top)
diff --git a/doc/mod/notification-matrix.d/01-authenticate.avif b/doc/mod/notification-matrix.d/01-authenticate.avif
new file mode 100644
index 0000000..1db516b
--- /dev/null
+++ b/doc/mod/notification-matrix.d/01-authenticate.avif
Binary files differ
diff --git a/doc/mod/notification-matrix.d/01-home-server.avif b/doc/mod/notification-matrix.d/01-home-server.avif
deleted file mode 100644
index 683c7b5..0000000
--- a/doc/mod/notification-matrix.d/01-home-server.avif
+++ /dev/null
Binary files differ
diff --git a/doc/mod/notification-matrix.d/02-access-token.avif b/doc/mod/notification-matrix.d/02-access-token.avif
deleted file mode 100644
index 54109a6..0000000
--- a/doc/mod/notification-matrix.d/02-access-token.avif
+++ /dev/null
Binary files differ
diff --git a/doc/mod/notification-matrix.d/02-join-room.avif b/doc/mod/notification-matrix.d/02-join-room.avif
new file mode 100644
index 0000000..edd6c81
--- /dev/null
+++ b/doc/mod/notification-matrix.d/02-join-room.avif
Binary files differ
diff --git a/doc/mod/notification-matrix.d/03-join-room.avif b/doc/mod/notification-matrix.d/03-join-room.avif
deleted file mode 100644
index 45974b8..0000000
--- a/doc/mod/notification-matrix.d/03-join-room.avif
+++ /dev/null
Binary files differ
diff --git a/doc/mod/notification-matrix.md b/doc/mod/notification-matrix.md
index b1f520e..c68b0aa 100644
--- a/doc/mod/notification-matrix.md
+++ b/doc/mod/notification-matrix.md
@@ -1,7 +1,14 @@
Send notifications via Matrix
=============================
-[◀ Go back to main README](../../README.md)
+[![GitHub stars](https://img.shields.io/github/stars/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=red)](https://github.com/eworm-de/routeros-scripts/stargazers)
+[![GitHub forks](https://img.shields.io/github/forks/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=green)](https://github.com/eworm-de/routeros-scripts/network)
+[![GitHub watchers](https://img.shields.io/github/watchers/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=blue)](https://github.com/eworm-de/routeros-scripts/watchers)
+[![required RouterOS version](https://img.shields.io/badge/RouterOS-7.12-yellow?style=flat)](https://mikrotik.com/download/changelogs/)
+[![Telegram group @routeros_scripts](https://img.shields.io/badge/Telegram-%40routeros__scripts-%2326A5E4?logo=telegram&style=flat)](https://t.me/routeros_scripts)
+[![donate with PayPal](https://img.shields.io/badge/Like_it%3F-Donate!-orange?logo=githubsponsors&logoColor=orange&style=flat)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)
+
+[⬅️ Go back to main README](../../README.md)
> ℹ️️ **Info**: This module can not be used on its own but requires the base
> installation. See [main README](../../README.md) for details.
@@ -21,8 +28,8 @@ Just install the module:
$ScriptInstallUpdate mod/notification-matrix;
Also install a Matrix client on at least one of your mobile and/or desktop
-devices. As there is no privilege separation you should create a dedicated
-notification account, in addition to your general user account.
+devices. Create and setup an account there, we will reference that as
+"*general account*" later.
Configuration
-------------
@@ -31,58 +38,57 @@ Edit `global-config-overlay`, add `MatrixHomeServer`, `MatrixAccessToken` and
`MatrixRoom` - see below on hints how to retrieve this information. Then
reload the configuration.
-### Home server
+> ℹ️ **Info**: Copy relevant configuration from
+> [`global-config`](../../global-config.rsc) (the one without `-overlay`) to
+> your local `global-config-overlay` and modify it to your specific needs.
-Matrix user accounts are identified by a unique user id in the form of
-`@localpart:domain`. The `domain` part is not necessarily your home server
-address, you have to resolve it with the procedure described in the
-[Matrix specification](https://spec.matrix.org/latest/client-server-api/#server-discovery).
+The Matrix server is connected via encrypted https, and certificate
+verification is applied. So make sure you have the certificate chain for
+your server in device's certificate store.
-Your best bet is to query the server at `domain` with the
-[well-known uri](https://spec.matrix.org/latest/client-server-api/#well-known-uri).
-For "*matrix.org*" this query is:
+> ℹ️ **Info**: The *matrix.org* server uses a Cloudflare certificate. You can
+> install that with: `$CertificateAvailable "Cloudflare Inc ECC CA-3"`
- /tool/fetch "https://matrix.org/.well-known/matrix/client" output=user;
+### From other device
-![home server](notification-matrix.d/01-home-server.avif)
+If you have setup your Matrix *notification account* before just reuse that.
+Copy the relevant configuration to the device to be configured.
-So the home server for "*matrix.org*" is "*matrix-client.matrix.org*".
-Please strip the protocol ("*https://*") for `MatrixHomeServer` if given.
+### Setup new account
-### Access token
+As there is no privilege separation you should create a dedicated account
+for use with these scripts, in addition to your *general account*.
+We will reference that as "*notification account*" in the following steps.
-After discovering the correct home server an access token has to be created.
-For this the login credentials (username and password) of the notification
-account must be sent to the home server via
-[client server api](https://matrix.org/docs/guides/client-server-api#login).
+#### Authenticate
-We use the home server discovered above, "*matrix-client.matrix.org*".
-The user is "*example*" and password is "*v3ry-s3cr3t*".
+Matrix user accounts are identified by a unique user id in the form of
+`@localpart:domain`. Use that and your password to generate an access token
+and write first part of the configuration:
- /tool/fetch "https://matrix-client.matrix.org/_matrix/client/r0/login" http-method=post http-data="{\"type\":\"m.login.password\", \"user\":\"example\", \"password\":\"v3ry-s3cr3t\"}" output=user;
+ $SetupMatrixAuthenticate "@example:matrix.org" "v3ry-s3cr3t";
-![access token](notification-matrix.d/02-access-token.avif)
+![authenticate](notification-matrix.d/01-authenticate.avif)
-The server replied with a JSON object containing the `access_token`, use that
-for `MatrixAccessToken`.
+#### Join Room
-### Room
+Every Matix chat is a room, so we have to create one. Do that with your
+*general account*, this makes sure your *general account* is the room owner.
+Then join the room and invite the *notification account* by its user id
+"*@example:matrix.org*".
+Look up the *room id* within the Matrix client, it should read like
+"*!WUcxpSjKyxSGelouhA:matrix.org*" (starting with an exclamation mark and
+ending with the domain).
-Every Matix chat is a room, so we have to create one. Do so with your general
-user, this makes sure your general user is the room owner. Then join the room
-and invite the notification user by its user id "*@example:matrix.org*". Look
-up the room id within the Matrix client, it should read like
-"*!WUcxpSjKyxSGelouhA:matrix.org*". Use that for `MatrixRoom`.
+Finally make the *notification account* join into the room by accepting
+the invite.
-Finally join the notification user to the room by accepting the invite. Again,
-this can be done with
-[client server api](https://matrix.org/docs/guides/client-server-api#joining-a-room-via-an-invite).
-Make sure to replace room id ("*!*" is escaped with "*%21*") and access token
-with your data.
+ $SetupMatrixJoinRoom "!WUcxpSjKyxSGelouhA:matrix.org";
- /tool/fetch "https://matrix-client.matrix.org/_matrix/client/r0/rooms/%21WUcxpSjKyxSGelouhA:matrix.org/join?access_token=yt_ZXdvcm0tdGVzdA_NNqUyvKHRhBLZmnzVVSK_0xu6yN" http-method=post http-data="" output=user;
+![join room](notification-matrix.d/02-join-room.avif)
-![join room](notification-matrix.d/03-join-room.avif)
+The settings have been appended to `global-config-overlay`. You may want to
+edit to move it to an appropriate place.
Usage and invocation
--------------------
@@ -90,22 +96,34 @@ Usage and invocation
There's nothing special to do. Every script or function sending a notification
will now send it to your Matrix account.
-But of course you can send notifications directly or use a function in your
-own scripts. Give it a try:
+But of course you can use the function to send notifications directly. Give
+it a try:
- $SendMatrix "Subject..." "Body..."
+ $SendMatrix "Subject..." "Body...";
Alternatively this sends a notification with all available and configured
methods:
- $SendNotification "Subject..." "Body..."
+ $SendNotification "Subject..." "Body...";
+
+To use the functions in your own scripts you have to declare them first.
+Place this before you call them:
+
+ :global SendMatrix;
+ :global SendNotification;
+
+In case there is a situation when the queue needs to be purged there is a
+function available:
+
+ $PurgeMatrixQueue;
See also
--------
* [Send notifications via e-mail](notification-email.md)
+* [Send notifications via Ntfy](notification-ntfy.md)
* [Send notifications via Telegram](notification-telegram.md)
---
-[◀ Go back to main README](../../README.md)
-[▲ Go back to top](#top)
+[⬅️ Go back to main README](../../README.md)
+[⬆️ Go back to top](#top)
diff --git a/doc/mod/notification-ntfy.md b/doc/mod/notification-ntfy.md
new file mode 100644
index 0000000..a3fdf88
--- /dev/null
+++ b/doc/mod/notification-ntfy.md
@@ -0,0 +1,86 @@
+Send notifications via Ntfy
+===========================
+
+[![GitHub stars](https://img.shields.io/github/stars/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=red)](https://github.com/eworm-de/routeros-scripts/stargazers)
+[![GitHub forks](https://img.shields.io/github/forks/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=green)](https://github.com/eworm-de/routeros-scripts/network)
+[![GitHub watchers](https://img.shields.io/github/watchers/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=blue)](https://github.com/eworm-de/routeros-scripts/watchers)
+[![required RouterOS version](https://img.shields.io/badge/RouterOS-7.12-yellow?style=flat)](https://mikrotik.com/download/changelogs/)
+[![Telegram group @routeros_scripts](https://img.shields.io/badge/Telegram-%40routeros__scripts-%2326A5E4?logo=telegram&style=flat)](https://t.me/routeros_scripts)
+[![donate with PayPal](https://img.shields.io/badge/Like_it%3F-Donate!-orange?logo=githubsponsors&logoColor=orange&style=flat)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)
+
+[⬅️ Go back to main README](../../README.md)
+
+> ℹ️️ **Info**: This module can not be used on its own but requires the base
+> installation. See [main README](../../README.md) for details.
+
+Description
+-----------
+
+This module adds support for sending notifications via
+[Ntfy](https://ntfy.sh/). A queue is used to make sure
+notifications are not lost on failure but sent later.
+
+Requirements and installation
+-----------------------------
+
+Just install the module:
+
+ $ScriptInstallUpdate mod/notification-ntfy;
+
+Also install the Ntfy app on your mobile device or use the
+[web app](https://ntfy.sh/app) in a browser of your choice.
+
+Configuration
+-------------
+
+Creating an account is not required. Just choose a topic and you are good
+to go.
+
+> ⚠️ **Warning**: If you use ntfy without sign-up, the topic is essentially
+> a password, so pick something that's not easily guessable.
+
+Edit `global-config-overlay`, add `NtfyServer` (leave it unchanged, unless
+you are self-hosting the service) and `NtfyTopic` with your choosen topic.
+Then reload the configuration.
+
+> ℹ️ **Info**: Copy relevant configuration from
+> [`global-config`](../../global-config.rsc) (the one without `-overlay`) to
+> your local `global-config-overlay` and modify it to your specific needs.
+
+Usage and invocation
+--------------------
+
+There's nothing special to do. Every script or function sending a notification
+will now send it to your Ntfy topic.
+
+But of course you can use the function to send notifications directly. Give
+it a try:
+
+ $SendNtfy "Subject..." "Body...";
+
+Alternatively this sends a notification with all available and configured
+methods:
+
+ $SendNotification "Subject..." "Body...";
+
+To use the functions in your own scripts you have to declare them first.
+Place this before you call them:
+
+ :global SendNtfy;
+ :global SendNotification;
+
+In case there is a situation when the queue needs to be purged there is a
+function available:
+
+ $PurgeNtfyQueue;
+
+See also
+--------
+
+* [Send notifications via e-mail](notification-email.md)
+* [Send notifications via Matrix](notification-matrix.md)
+* [Send notifications via Telegram](notification-telegram.md)
+
+---
+[⬅️ Go back to main README](../../README.md)
+[⬆️ Go back to top](#top)
diff --git a/doc/mod/notification-telegram.d/setuserpic.avif b/doc/mod/notification-telegram.d/setuserpic.avif
new file mode 100644
index 0000000..2017d20
--- /dev/null
+++ b/doc/mod/notification-telegram.d/setuserpic.avif
Binary files differ
diff --git a/doc/mod/notification-telegram.md b/doc/mod/notification-telegram.md
index 2b1abe9..cb326f0 100644
--- a/doc/mod/notification-telegram.md
+++ b/doc/mod/notification-telegram.md
@@ -1,7 +1,14 @@
Send notifications via Telegram
===============================
-[◀ Go back to main README](../../README.md)
+[![GitHub stars](https://img.shields.io/github/stars/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=red)](https://github.com/eworm-de/routeros-scripts/stargazers)
+[![GitHub forks](https://img.shields.io/github/forks/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=green)](https://github.com/eworm-de/routeros-scripts/network)
+[![GitHub watchers](https://img.shields.io/github/watchers/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=blue)](https://github.com/eworm-de/routeros-scripts/watchers)
+[![required RouterOS version](https://img.shields.io/badge/RouterOS-7.12-yellow?style=flat)](https://mikrotik.com/download/changelogs/)
+[![Telegram group @routeros_scripts](https://img.shields.io/badge/Telegram-%40routeros__scripts-%2326A5E4?logo=telegram&style=flat)](https://t.me/routeros_scripts)
+[![donate with PayPal](https://img.shields.io/badge/Like_it%3F-Donate!-orange?logo=githubsponsors&logoColor=orange&style=flat)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)
+
+[⬅️ Go back to main README](../../README.md)
> ℹ️️ **Info**: This module can not be used on its own but requires the base
> installation. See [main README](../../README.md) for details.
@@ -41,6 +48,10 @@ Finally edit `global-config-overlay`, add `TelegramTokenId` with the token
from *BotFather* and `TelegramChatId` with your id from *GetIDs Bot*. Then
reload the configuration.
+> ℹ️ **Info**: Copy relevant configuration from
+> [`global-config`](../../global-config.rsc) (the one without `-overlay`) to
+> your local `global-config-overlay` and modify it to your specific needs.
+
### Notifications to a group
Sending notifications to a group is possible as well. Add your bot and the
@@ -53,22 +64,49 @@ Usage and invocation
There's nothing special to do. Every script or function sending a notification
will now send it to your Telegram account.
-But of course you can send notifications directly or use a function in your
-own scripts. Give it a try:
+But of course you can use the function to send notifications directly. Give
+it a try:
- $SendTelegram "Subject..." "Body..."
+ $SendTelegram "Subject..." "Body...";
Alternatively this sends a notification with all available and configured
methods:
- $SendNotification "Subject..." "Body..."
+ $SendNotification "Subject..." "Body...";
+
+To use the functions in your own scripts you have to declare them first.
+Place this before you call them:
+
+ :global SendTelegram;
+ :global SendNotification;
+
+In case there is a situation when the queue needs to be purged there is a
+function available:
+
+ $PurgeTelegramQueue;
+
+Tips & Tricks
+-------------
+
+### Set a profile photo
+
+You can use a profile photo for your bot to make it recognizable. Open the
+chat with [BotFather](https://t.me/BotFather) and set it there.
+
+![set profile photo](notification-telegram.d/setuserpic.avif)
+
+Have a look at my
+[RouterOS-Scripts Logo Color Changer](https://git.eworm.de/cgit/routeros-scripts/plain/contrib/logo-color.html)
+to create a colored version of this scripts' logo.
See also
--------
+* [Chat with your router and send commands via Telegram bot](../telegram-chat.md)
* [Send notifications via e-mail](notification-email.md)
* [Send notifications via Matrix](notification-matrix.md)
+* [Send notifications via Ntfy](notification-ntfy.md)
---
-[◀ Go back to main README](../../README.md)
-[▲ Go back to top](#top)
+[⬅️ Go back to main README](../../README.md)
+[⬆️ Go back to top](#top)
diff --git a/doc/mod/scriptrunonce.md b/doc/mod/scriptrunonce.md
index 6efa1b7..6619efb 100644
--- a/doc/mod/scriptrunonce.md
+++ b/doc/mod/scriptrunonce.md
@@ -1,7 +1,14 @@
Download script and run it once
===============================
-[◀ Go back to main README](../../README.md)
+[![GitHub stars](https://img.shields.io/github/stars/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=red)](https://github.com/eworm-de/routeros-scripts/stargazers)
+[![GitHub forks](https://img.shields.io/github/forks/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=green)](https://github.com/eworm-de/routeros-scripts/network)
+[![GitHub watchers](https://img.shields.io/github/watchers/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=blue)](https://github.com/eworm-de/routeros-scripts/watchers)
+[![required RouterOS version](https://img.shields.io/badge/RouterOS-7.12-yellow?style=flat)](https://mikrotik.com/download/changelogs/)
+[![Telegram group @routeros_scripts](https://img.shields.io/badge/Telegram-%40routeros__scripts-%2326A5E4?logo=telegram&style=flat)](https://t.me/routeros_scripts)
+[![donate with PayPal](https://img.shields.io/badge/Like_it%3F-Donate!-orange?logo=githubsponsors&logoColor=orange&style=flat)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)
+
+[⬅️ Go back to main README](../../README.md)
> ℹ️️ **Info**: This module can not be used on its own but requires the base
> installation. See [main README](../../README.md) for details.
@@ -27,9 +34,13 @@ The optional configuration goes to `global-config-overlay`.
* `ScriptRunOnceBaseUrl`: base url, prepended to parameter
* `ScriptRunOnceUrlSuffix`: url suffix, appended to parameter
+> ℹ️ **Info**: Copy relevant configuration from
+> [`global-config`](../../global-config.rsc) (the one without `-overlay`) to
+> your local `global-config-overlay` and modify it to your specific needs.
+
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
--------------------
@@ -44,5 +55,5 @@ The function `$ScriptRunOnce` expects an URL (or name if
Giving multiple scripts is possible, separated by comma.
---
-[◀ Go back to main README](../../README.md)
-[▲ Go back to top](#top)
+[⬅️ Go back to main README](../../README.md)
+[⬆️ Go back to top](#top)
diff --git a/doc/mod/ssh-keys-import.md b/doc/mod/ssh-keys-import.md
new file mode 100644
index 0000000..3d81566
--- /dev/null
+++ b/doc/mod/ssh-keys-import.md
@@ -0,0 +1,73 @@
+Import ssh keys for public key authentication
+=============================================
+
+[![GitHub stars](https://img.shields.io/github/stars/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=red)](https://github.com/eworm-de/routeros-scripts/stargazers)
+[![GitHub forks](https://img.shields.io/github/forks/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=green)](https://github.com/eworm-de/routeros-scripts/network)
+[![GitHub watchers](https://img.shields.io/github/watchers/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=blue)](https://github.com/eworm-de/routeros-scripts/watchers)
+[![required RouterOS version](https://img.shields.io/badge/RouterOS-7.12-yellow?style=flat)](https://mikrotik.com/download/changelogs/)
+[![Telegram group @routeros_scripts](https://img.shields.io/badge/Telegram-%40routeros__scripts-%2326A5E4?logo=telegram&style=flat)](https://t.me/routeros_scripts)
+[![donate with PayPal](https://img.shields.io/badge/Like_it%3F-Donate!-orange?logo=githubsponsors&logoColor=orange&style=flat)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)
+
+[⬅️ Go back to main README](../../README.md)
+
+> ℹ️️ **Info**: This module can not be used on its own but requires the base
+> installation. See [main README](../../README.md) for details.
+
+Description
+-----------
+
+RouterOS supports ssh login with public key authentication. The functions
+in this module help importing the keys.
+
+Requirements and installation
+-----------------------------
+
+Just install the module:
+
+ $ScriptInstallUpdate mod/ssh-keys-import;
+
+Usage and invocation
+--------------------
+
+### Import single key from terminal
+
+Call the function `$SSHKeysImport` with key and user as parameter to
+import that key:
+
+ $SSHKeysImport "ssh-rsa AAAAB3Nza...QYZk8= user" admin;
+
+Starting with RouterOS *7.12beta1* support for keys of type `ed25519` has
+been added:
+
+ $SSHKeysImport "ssh-ed25519 AAAAC3Nza...ZVugJT user" admin;
+
+The third part of the key (`user` in this example) is inherited as
+`key-owner` in RouterOS. Also the `MD5` fingerprint is recorded, this helps
+to audit and verify the available keys.
+
+> ℹ️️ **Info**: Use `ssh-keygen` to show a fingerprint of an existing public
+> key file: `ssh-keygen -l -E md5 -f ~/.ssh/id_ed25519.pub`
+
+### Import several keys from file
+
+The functions `$SSHKeysImportFile` can read an `authorized_keys`-style file
+and import all the keys. The user given to the function can be overwritting
+from comments in the file. Create a file `keys.pub` with this content:
+
+```
+ssh-ed25519 AAAAC3Nza...3OcN8A user@client
+ssh-rsa AAAAB3Nza...ozyts= worker@station
+# user=example
+ssh-rsa AAAAB3Nza...GXQVk= person@host
+```
+
+Then import it with:
+
+ $SSHKeysImportFile keys.pub admin;
+
+This will import the first two keys for user `admin` (as given to function)
+and the third one for user `example` (as defined in comment).
+
+---
+[⬅️ Go back to main README](../../README.md)
+[⬆️ Go back to top](#top)
diff --git a/doc/mode-button.md b/doc/mode-button.md
index ef7754c..22ec215 100644
--- a/doc/mode-button.md
+++ b/doc/mode-button.md
@@ -1,7 +1,14 @@
Mode button with multiple presses
=================================
-[◀ Go back to main README](../README.md)
+[![GitHub stars](https://img.shields.io/github/stars/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=red)](https://github.com/eworm-de/routeros-scripts/stargazers)
+[![GitHub forks](https://img.shields.io/github/forks/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=green)](https://github.com/eworm-de/routeros-scripts/network)
+[![GitHub watchers](https://img.shields.io/github/watchers/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=blue)](https://github.com/eworm-de/routeros-scripts/watchers)
+[![required RouterOS version](https://img.shields.io/badge/RouterOS-7.12-yellow?style=flat)](https://mikrotik.com/download/changelogs/)
+[![Telegram group @routeros_scripts](https://img.shields.io/badge/Telegram-%40routeros__scripts-%2326A5E4?logo=telegram&style=flat)](https://t.me/routeros_scripts)
+[![donate with PayPal](https://img.shields.io/badge/Like_it%3F-Donate!-orange?logo=githubsponsors&logoColor=orange&style=flat)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)
+
+[⬅️ Go back to main README](../README.md)
> ℹ️ **Info**: This script can not be used on its own but requires the base
> installation. See [main README](../README.md) for details.
@@ -52,7 +59,11 @@ Configuration
The configuration goes to `global-config-overlay`, these are the parameters:
* `ModeButton`: an array with defined actions
-* `ModeButtonLED`: led to give visual feedback
+* `ModeButtonLED`: led to give visual feedback, `type` must be `on` or `off`
+
+> ℹ️ **Info**: Copy relevant configuration from
+> [`global-config`](../global-config.rsc) (the one without `-overlay`) to
+> your local `global-config-overlay` and modify it to your specific needs.
Usage and invocation
--------------------
@@ -60,5 +71,5 @@ Usage and invocation
Press the mode button. 😜
---
-[◀ Go back to main README](../README.md)
-[▲ Go back to top](#top)
+[⬅️ Go back to main README](../README.md)
+[⬆️ Go back to top](#top)
diff --git a/doc/netwatch-dns.md b/doc/netwatch-dns.md
index 65d0488..e00ccd0 100644
--- a/doc/netwatch-dns.md
+++ b/doc/netwatch-dns.md
@@ -1,7 +1,14 @@
Manage DNS and DoH servers from netwatch
========================================
-[◀ Go back to main README](../README.md)
+[![GitHub stars](https://img.shields.io/github/stars/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=red)](https://github.com/eworm-de/routeros-scripts/stargazers)
+[![GitHub forks](https://img.shields.io/github/forks/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=green)](https://github.com/eworm-de/routeros-scripts/network)
+[![GitHub watchers](https://img.shields.io/github/watchers/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=blue)](https://github.com/eworm-de/routeros-scripts/watchers)
+[![required RouterOS version](https://img.shields.io/badge/RouterOS-7.12-yellow?style=flat)](https://mikrotik.com/download/changelogs/)
+[![Telegram group @routeros_scripts](https://img.shields.io/badge/Telegram-%40routeros__scripts-%2326A5E4?logo=telegram&style=flat)](https://t.me/routeros_scripts)
+[![donate with PayPal](https://img.shields.io/badge/Like_it%3F-Donate!-orange?logo=githubsponsors&logoColor=orange&style=flat)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)
+
+[⬅️ Go back to main README](../README.md)
> ℹ️ **Info**: This script can not be used on its own but requires the base
> installation. See [main README](../README.md) for details.
@@ -43,10 +50,19 @@ Giving a specific query url for DoH is possible:
Note that using a name in DoH url may introduce a chicken-and-egg issue!
+Adding a static DNS record has the same result for the url, but always
+resolves to the same address.
+
+ /ip/dns/static/add name="dns.nextdns.io" address=199.247.16.158;
+ /tool/netwatch/add comment="doh" host=199.247.16.158;
+
+Be aware that you have to keep the ip address in sync with real world
+manually!
+
Importing a certificate automatically is possible, at least if available in
the repository (see `certs` sub directory).
- /tool/netwatch/add comment="doh, doh-cert=DigiCert TLS Hybrid ECC SHA384 2020 CA1" host=1.1.1.1;
+ /tool/netwatch/add comment="doh, doh-cert=DigiCert Global G2 TLS RSA SHA256 2020 CA1" host=1.1.1.1;
/tool/netwatch/add comment="doh, doh-cert=DigiCert TLS Hybrid ECC SHA384 2020 CA1" host=9.9.9.9;
/tool/netwatch/add comment="doh, doh-cert=GTS CA 1C3" host=8.8.8.8;
@@ -74,5 +90,5 @@ See also
* [Notify on host up and down](netwatch-notify.md)
---
-[◀ Go back to main README](../README.md)
-[▲ Go back to top](#top)
+[⬅️ Go back to main README](../README.md)
+[⬆️ Go back to top](#top)
diff --git a/doc/netwatch-notify.d/notification-01-down.avif b/doc/netwatch-notify.d/notification-01-down.avif
new file mode 100644
index 0000000..894fb23
--- /dev/null
+++ b/doc/netwatch-notify.d/notification-01-down.avif
Binary files differ
diff --git a/doc/netwatch-notify.d/notification-01-down.svg b/doc/netwatch-notify.d/notification-01-down.svg
deleted file mode 100644
index 13988a3..0000000
--- a/doc/netwatch-notify.d/notification-01-down.svg
+++ /dev/null
@@ -1,34 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<svg id="svg8" width="463.78" height="93.78" version="1.1" viewBox="0 0 122.71 24.812" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
- <metadata id="metadata5">
- <rdf:RDF>
- <cc:Work rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <g id="layer1" transform="translate(16.375 11.083)">
- <rect id="rect857" x="-15.875" y="-10.583" width="121.71" height="23.812" rx="1.5214" fill="#e6e6e6" stroke="#6c5d53" stroke-linecap="round" stroke-linejoin="round"/>
- <g id="g884" transform="matrix(.5 0 0 .5 -12.406 -7.1146)" stroke-width="2">
- <path id="path899" d="m23.177 23.177c-2.9635 2.9635-17.991 2.9635-20.955 0-2.9635-2.9635-2.9635-17.991 0-20.955 2.9635-2.9635 17.991-2.9635 20.955 0 2.9635 2.9635 2.9635 17.991 0 20.955z" fill="#fff"/>
- <g id="g871">
- <g id="text837" stroke-width=".52916px" aria-label="#!rsc">
- <path id="path851" d="m7.4832 16.646v-1.0239h-0.54606l0.16577-1.2774h-1.2774l-0.16577 1.2774h-0.97511l0.16577-1.2774h-1.2774l-0.16577 1.2774h-0.70208v1.0239h0.56556l-0.24378 1.8722h-0.68257v1.0239h0.54606l-0.18527 1.3944h1.2774l0.18527-1.3944h0.97511l-0.18527 1.3944h1.2774l0.18527-1.3944h0.70208v-1.0239h-0.56556l0.24378-1.8722zm-2.2037 1.8722h-0.97511l0.24378-1.8722h0.97511z"/>
- <path id="path853" d="m9.6187 14.179h-1.6382l0.19502 4.271h1.2481zm-0.81909 5.1583c-0.48755 0-0.8776 0.39979-0.8776 0.8776 0 0.48755 0.39004 0.88735 0.8776 0.88735 0.4973 0 0.88735-0.39979 0.88735-0.88735 0-0.4778-0.39004-0.8776-0.88735-0.8776z"/>
- <path id="path855" d="m13.373 15.612c-0.59482 0-1.1019 0.42905-1.3359 1.1506l-0.13652-1.0044h-1.3359v5.1778h1.5407v-2.6035c0.17552-0.77033 0.4388-1.2286 1.0726-1.2286 0.16577 0 0.30228 0.02925 0.46805 0.06826l0.24378-1.4919c-0.17552-0.04875-0.32178-0.06826-0.51681-0.06826z"/>
- <path id="path857" d="m16.181 15.592c-1.3066 0-2.116 0.69233-2.116 1.5797 0 0.79959 0.50706 1.3261 1.5309 1.6187 0.9361 0.26328 1.0921 0.37054 1.0921 0.72158 0 0.31203-0.28278 0.48755-0.75083 0.48755-0.50706 0-0.98486-0.20477-1.3749-0.50706l-0.75083 0.83859c0.50706 0.4583 1.2676 0.77033 2.1647 0.77033 1.2871 0 2.3012-0.63382 2.3012-1.7064 0-0.92635-0.57531-1.3554-1.5992-1.6479-0.92635-0.27303-1.0629-0.39004-1.0629-0.66307 0-0.23402 0.20477-0.39004 0.62407-0.39004 0.44855 0 0.8776 0.14627 1.2774 0.39979l0.56556-0.86784c-0.4778-0.39004-1.1506-0.63382-1.9015-0.63382z"/>
- <path id="path859" d="m21.281 15.592c-1.5504 0-2.5353 1.1506-2.5353 2.7986 0 1.6382 0.97511 2.7108 2.5645 2.7108 0.71183 0 1.2676-0.23403 1.7454-0.61432l-0.67282-0.9556c-0.37054 0.23403-0.62407 0.35104-0.99461 0.35104-0.61432 0-1.0239-0.35104-1.0239-1.5017 0-1.1604 0.38029-1.6089 1.0434-1.6089 0.35104 0 0.65332 0.11701 0.98486 0.37054l0.66307-0.9166c-0.4973-0.4193-1.0531-0.63382-1.7747-0.63382z"/>
- </g>
- <g id="g1542" transform="matrix(2 0 0 2 -.41134 3.175)" fill="#676867" fill-rule="evenodd" stroke-width=".26458">
- <path id="path943" d="m4.9596-1.0196c0.40797 2.8312 1.9272 4.5499 5.0239 4.691-2.918 1.1164-5.9253-1.5076-5.0239-4.691"/>
- <path id="path945" d="m3.3407-0.52096c0.034969-0.00777 0.038854 0.015542 0.041445 0.041445 0.098431 1.8054 0.85998 3.1744 1.8689 4.1108 1.0089 0.93639 2.3636 1.6941 4.274 1.6604-3.5772 1.4247-7.337-1.9984-6.1856-5.8126"/>
- </g>
- </g>
- </g>
- <text id="text4811" transform="matrix(.26458 0 0 .26458 -45.214 -6.6039)" x="-248.88142" fill="#000000" font-family="'Fira Mono', 'Roboto Mono', monospace" font-size="12px" letter-spacing="0px" stroke-width="1px" word-spacing="0px" style="font-variant-caps:normal;font-variant-east-asian:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:15px;shape-inside:url(#rect4813);white-space:pre" xml:space="preserve"><tspan id="tspan2277" x="180" y="10.85">[MikroTik] ❌ Netwatch Notify: example.com down
-</tspan><tspan id="tspan2279" x="180" y="25.85">
-</tspan><tspan id="tspan2281" x="180" y="40.85">The host 'example.com' (93.184.216.34) is down
-</tspan><tspan id="tspan2283" x="180" y="55.85">since jun/08/2021 06:55:03.</tspan></text>
- </g>
-</svg>
diff --git a/doc/netwatch-notify.d/notification-02-up.avif b/doc/netwatch-notify.d/notification-02-up.avif
new file mode 100644
index 0000000..9021a93
--- /dev/null
+++ b/doc/netwatch-notify.d/notification-02-up.avif
Binary files differ
diff --git a/doc/netwatch-notify.d/notification-02-up.svg b/doc/netwatch-notify.d/notification-02-up.svg
deleted file mode 100644
index 32dfbcc..0000000
--- a/doc/netwatch-notify.d/notification-02-up.svg
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<svg id="svg8" width="493.78" height="113.78" version="1.1" viewBox="0 0 130.65 30.104" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
- <metadata id="metadata5">
- <rdf:RDF>
- <cc:Work rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <g id="layer1" transform="translate(16.375 11.083)">
- <rect id="rect857" x="-15.875" y="-10.583" width="129.65" height="29.104" rx="1.6206" fill="#e6e6e6" stroke="#6c5d53" stroke-linecap="round" stroke-linejoin="round"/>
- <g id="g884" transform="matrix(.5 0 0 .5 -12.406 -7.1146)" stroke-width="2">
- <path id="path899" d="m23.177 23.177c-2.9635 2.9635-17.991 2.9635-20.955 0-2.9635-2.9635-2.9635-17.991 0-20.955 2.9635-2.9635 17.991-2.9635 20.955 0 2.9635 2.9635 2.9635 17.991 0 20.955z" fill="#fff"/>
- <g id="g871">
- <g id="text837" stroke-width=".52916px" aria-label="#!rsc">
- <path id="path851" d="m7.4832 16.646v-1.0239h-0.54606l0.16577-1.2774h-1.2774l-0.16577 1.2774h-0.97511l0.16577-1.2774h-1.2774l-0.16577 1.2774h-0.70208v1.0239h0.56556l-0.24378 1.8722h-0.68257v1.0239h0.54606l-0.18527 1.3944h1.2774l0.18527-1.3944h0.97511l-0.18527 1.3944h1.2774l0.18527-1.3944h0.70208v-1.0239h-0.56556l0.24378-1.8722zm-2.2037 1.8722h-0.97511l0.24378-1.8722h0.97511z"/>
- <path id="path853" d="m9.6187 14.179h-1.6382l0.19502 4.271h1.2481zm-0.81909 5.1583c-0.48755 0-0.8776 0.39979-0.8776 0.8776 0 0.48755 0.39004 0.88735 0.8776 0.88735 0.4973 0 0.88735-0.39979 0.88735-0.88735 0-0.4778-0.39004-0.8776-0.88735-0.8776z"/>
- <path id="path855" d="m13.373 15.612c-0.59482 0-1.1019 0.42905-1.3359 1.1506l-0.13652-1.0044h-1.3359v5.1778h1.5407v-2.6035c0.17552-0.77033 0.4388-1.2286 1.0726-1.2286 0.16577 0 0.30228 0.02925 0.46805 0.06826l0.24378-1.4919c-0.17552-0.04875-0.32178-0.06826-0.51681-0.06826z"/>
- <path id="path857" d="m16.181 15.592c-1.3066 0-2.116 0.69233-2.116 1.5797 0 0.79959 0.50706 1.3261 1.5309 1.6187 0.9361 0.26328 1.0921 0.37054 1.0921 0.72158 0 0.31203-0.28278 0.48755-0.75083 0.48755-0.50706 0-0.98486-0.20477-1.3749-0.50706l-0.75083 0.83859c0.50706 0.4583 1.2676 0.77033 2.1647 0.77033 1.2871 0 2.3012-0.63382 2.3012-1.7064 0-0.92635-0.57531-1.3554-1.5992-1.6479-0.92635-0.27303-1.0629-0.39004-1.0629-0.66307 0-0.23402 0.20477-0.39004 0.62407-0.39004 0.44855 0 0.8776 0.14627 1.2774 0.39979l0.56556-0.86784c-0.4778-0.39004-1.1506-0.63382-1.9015-0.63382z"/>
- <path id="path859" d="m21.281 15.592c-1.5504 0-2.5353 1.1506-2.5353 2.7986 0 1.6382 0.97511 2.7108 2.5645 2.7108 0.71183 0 1.2676-0.23403 1.7454-0.61432l-0.67282-0.9556c-0.37054 0.23403-0.62407 0.35104-0.99461 0.35104-0.61432 0-1.0239-0.35104-1.0239-1.5017 0-1.1604 0.38029-1.6089 1.0434-1.6089 0.35104 0 0.65332 0.11701 0.98486 0.37054l0.66307-0.9166c-0.4973-0.4193-1.0531-0.63382-1.7747-0.63382z"/>
- </g>
- <g id="g1542" transform="matrix(2 0 0 2 -.41134 3.175)" fill="#676867" fill-rule="evenodd" stroke-width=".26458">
- <path id="path943" d="m4.9596-1.0196c0.40797 2.8312 1.9272 4.5499 5.0239 4.691-2.918 1.1164-5.9253-1.5076-5.0239-4.691"/>
- <path id="path945" d="m3.3407-0.52096c0.034969-0.00777 0.038854 0.015542 0.041445 0.041445 0.098431 1.8054 0.85998 3.1744 1.8689 4.1108 1.0089 0.93639 2.3636 1.6941 4.274 1.6604-3.5772 1.4247-7.337-1.9984-6.1856-5.8126"/>
- </g>
- </g>
- </g>
- <text id="text4811" transform="matrix(.26458 0 0 .26458 -45.214 -6.6039)" x="-248.88142" fill="#000000" font-family="'Fira Mono', 'Roboto Mono', monospace" font-size="12px" letter-spacing="0px" stroke-width="1px" word-spacing="0px" style="font-variant-caps:normal;font-variant-east-asian:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:15px;shape-inside:url(#rect4813);white-space:pre" xml:space="preserve"><tspan id="tspan2242" x="180" y="10.85">[MikroTik] ✅ Netwatch Notify: example.com up
-</tspan><tspan id="tspan2244" x="180" y="25.85">
-</tspan><tspan id="tspan2246" x="180" y="40.85">The host 'example.com' (93.184.216.34) is up
-</tspan><tspan id="tspan2248" x="180" y="55.85">since jun/08/2021 07:01:00.
-</tspan><tspan id="tspan2250" x="180" y="70.85">It was down for 6 checks since jun/08/2021 06:55:03.</tspan></text>
- </g>
-</svg>
diff --git a/doc/netwatch-notify.md b/doc/netwatch-notify.md
index 032106a..806bb3a 100644
--- a/doc/netwatch-notify.md
+++ b/doc/netwatch-notify.md
@@ -1,7 +1,14 @@
Notify on host up and down
==========================
-[◀ Go back to main README](../README.md)
+[![GitHub stars](https://img.shields.io/github/stars/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=red)](https://github.com/eworm-de/routeros-scripts/stargazers)
+[![GitHub forks](https://img.shields.io/github/forks/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=green)](https://github.com/eworm-de/routeros-scripts/network)
+[![GitHub watchers](https://img.shields.io/github/watchers/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=blue)](https://github.com/eworm-de/routeros-scripts/watchers)
+[![required RouterOS version](https://img.shields.io/badge/RouterOS-7.12-yellow?style=flat)](https://mikrotik.com/download/changelogs/)
+[![Telegram group @routeros_scripts](https://img.shields.io/badge/Telegram-%40routeros__scripts-%2326A5E4?logo=telegram&style=flat)](https://t.me/routeros_scripts)
+[![donate with PayPal](https://img.shields.io/badge/Like_it%3F-Donate!-orange?logo=githubsponsors&logoColor=orange&style=flat)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)
+
+[⬅️ Go back to main README](../README.md)
> ℹ️ **Info**: This script can not be used on its own but requires the base
> installation. See [main README](../README.md) for details.
@@ -17,8 +24,8 @@ optional parent host is not down to avoid false alerts.
### Sample notifications
-![netwatch-notify notification down](netwatch-notify.d/notification-01-down.svg)
-![netwatch-notify notification up](netwatch-notify.d/notification-02-up.svg)
+![netwatch-notify notification down](netwatch-notify.d/notification-01-down.avif)
+![netwatch-notify notification up](netwatch-notify.d/notification-02-up.avif)
Requirements and installation
-----------------------------
@@ -38,6 +45,12 @@ The hosts to be checked have to be added to netwatch with specific comment:
/tool/netwatch/add comment="notify, name=example.com" host=[ :resolve "example.com" ];
+Also notification settings are required for
+[e-mail](mod/notification-email.md),
+[matrix](mod/notification-matrix.md),
+[ntfy](mod/notification-ntfy.md) and/or
+[telegram](mod/notification-telegram.md).
+
### Hooks
It is possible to run an up hook command (`up-hook`) or down hook command
@@ -50,6 +63,9 @@ Also there is a `pre-down-hook` that fires at two thirds of failed checks
required for the notification. The idea is to fix the issue before a
notification is sent.
+Getting the escaping right may be troublesome. Please consider adding a
+script in `/system/script`, then running that from hook.
+
### Count threshould
The count threshould (default is 5 checks) is configurable as well:
@@ -74,9 +90,9 @@ with a resolvable name:
/tool/netwatch/add comment="notify, name=example.com, resolve=example.com";
-But be warned: Dynamic updates will probably cause issues if the name has
-more than one record in dns - a high rate of configuration changes (and flash
-writes) at least.
+This supports multiple A or AAAA records for a name just fine, even a CNAME
+to those. An update happens only if no more record with the configured host
+address is found.
### No notification on host down
@@ -88,10 +104,19 @@ powered off, but accessibility is of interest.
Go and get your coffee ☕️ before sending the print job.
-Also notification settings are required for
-[e-mail](mod/notification-email.md),
-[matrix](mod/notification-matrix.md) and/or
-[telegram](mod/notification-telegram.md).
+### Add a note in notification
+
+For some extra information it is possible to add a text note. This is
+included verbatim into the notification.
+
+ /tool/netwatch/add comment="notify, name=example, note=Do not touch!" host=10.0.0.31;
+
+### Add a link in notification
+
+It is possible to add a link in notification, that is added below the
+formatted notification text.
+
+ /tool/netwatch/add comment="notify, name=example.com, resolve=example.com, link=https://example.com/";
Tips & Tricks
-------------
@@ -151,5 +176,5 @@ See also
* [Manage DNS and DoH servers from netwatch](netwatch-dns.md)
---
-[◀ Go back to main README](../README.md)
-[▲ Go back to top](#top)
+[⬅️ Go back to main README](../README.md)
+[⬆️ Go back to top](#top)
diff --git a/doc/ospf-to-leds.md b/doc/ospf-to-leds.md
index 5ab5c75..a7d4e9a 100644
--- a/doc/ospf-to-leds.md
+++ b/doc/ospf-to-leds.md
@@ -1,7 +1,14 @@
Visualize OSPF state via LEDs
=============================
-[◀ Go back to main README](../README.md)
+[![GitHub stars](https://img.shields.io/github/stars/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=red)](https://github.com/eworm-de/routeros-scripts/stargazers)
+[![GitHub forks](https://img.shields.io/github/forks/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=green)](https://github.com/eworm-de/routeros-scripts/network)
+[![GitHub watchers](https://img.shields.io/github/watchers/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=blue)](https://github.com/eworm-de/routeros-scripts/watchers)
+[![required RouterOS version](https://img.shields.io/badge/RouterOS-7.12-yellow?style=flat)](https://mikrotik.com/download/changelogs/)
+[![Telegram group @routeros_scripts](https://img.shields.io/badge/Telegram-%40routeros__scripts-%2326A5E4?logo=telegram&style=flat)](https://t.me/routeros_scripts)
+[![donate with PayPal](https://img.shields.io/badge/Like_it%3F-Donate!-orange?logo=githubsponsors&logoColor=orange&style=flat)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)
+
+[⬅️ Go back to main README](../README.md)
> ℹ️ **Info**: This script can not be used on its own but requires the base
> installation. See [main README](../README.md) for details.
@@ -33,5 +40,5 @@ instance `default` via LED `user-led` set this:
/routing/ospf/instance/set default comment="ospf-to-leds, leds=user-led";
---
-[◀ Go back to main README](../README.md)
-[▲ Go back to top](#top)
+[⬅️ Go back to main README](../README.md)
+[⬆️ Go back to top](#top)
diff --git a/doc/packages-update.md b/doc/packages-update.md
index 57f02d9..fae3896 100644
--- a/doc/packages-update.md
+++ b/doc/packages-update.md
@@ -1,7 +1,14 @@
Manage system update
====================
-[◀ Go back to main README](../README.md)
+[![GitHub stars](https://img.shields.io/github/stars/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=red)](https://github.com/eworm-de/routeros-scripts/stargazers)
+[![GitHub forks](https://img.shields.io/github/forks/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=green)](https://github.com/eworm-de/routeros-scripts/network)
+[![GitHub watchers](https://img.shields.io/github/watchers/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=blue)](https://github.com/eworm-de/routeros-scripts/watchers)
+[![required RouterOS version](https://img.shields.io/badge/RouterOS-7.13-yellow?style=flat)](https://mikrotik.com/download/changelogs/)
+[![Telegram group @routeros_scripts](https://img.shields.io/badge/Telegram-%40routeros__scripts-%2326A5E4?logo=telegram&style=flat)](https://t.me/routeros_scripts)
+[![donate with PayPal](https://img.shields.io/badge/Like_it%3F-Donate!-orange?logo=githubsponsors&logoColor=orange&style=flat)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)
+
+[⬅️ Go back to main README](../README.md)
> ℹ️ **Info**: This script can not be used on its own but requires the base
> installation. See [main README](../README.md) for details.
@@ -16,8 +23,12 @@ verification.
But it provides some extra functionality:
+* upload backup to Mikrotik cloud if [backup-cloud](backup-cloud.md) is
+ installed
* send backup via e-mail if [backup-email](backup-email.md) is installed
-* upload backup if [backup-upload](backup-upload.md) is installed
+* save configuration to fallback partition if
+ [backup-partition](backup-partition.md) is installed
+* upload backup to server if [backup-upload](backup-upload.md) is installed
* schedule reboot at night
Requirements and installation
@@ -30,6 +41,21 @@ Just install the script:
It is automatically run by [check-routeros-update](check-routeros-update.md)
if available.
+Configuration
+-------------
+
+The configuration goes to `global-config-overlay`, this is the only parameter:
+
+* `PackagesUpdateDeferReboot`: defer the reboot for night (between 3 AM
+ and 5 AM)
+
+By modifying the scheduler's `start-time` you can force the reboot at
+different time.
+
+> ℹ️ **Info**: Copy relevant configuration from
+> [`global-config`](../global-config.rsc) (the one without `-overlay`) to
+> your local `global-config-overlay` and modify it to your specific needs.
+
Usage and invocation
--------------------
@@ -40,13 +66,13 @@ Alternatively run it manually:
See also
--------
-* [Notify on RouterOS update](check-routeros-update.md)
* [Upload backup to Mikrotik cloud](backup-cloud.md)
* [Send backup via e-mail](backup-email.md)
-* [Save configuration to fallback partition](doc/backup-partition.md)
+* [Save configuration to fallback partition](backup-partition.md)
* [Upload backup to server](backup-upload.md)
+* [Notify on RouterOS update](check-routeros-update.md)
* [Automatically upgrade firmware and reboot](firmware-upgrade-reboot.md)
---
-[◀ Go back to main README](../README.md)
-[▲ Go back to top](#top)
+[⬅️ Go back to main README](../README.md)
+[⬆️ Go back to top](#top)
diff --git a/doc/ppp-on-up.md b/doc/ppp-on-up.md
index 7545c5e..418f05e 100644
--- a/doc/ppp-on-up.md
+++ b/doc/ppp-on-up.md
@@ -1,7 +1,14 @@
Run scripts on ppp connection
=============================
-[◀ Go back to main README](../README.md)
+[![GitHub stars](https://img.shields.io/github/stars/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=red)](https://github.com/eworm-de/routeros-scripts/stargazers)
+[![GitHub forks](https://img.shields.io/github/forks/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=green)](https://github.com/eworm-de/routeros-scripts/network)
+[![GitHub watchers](https://img.shields.io/github/watchers/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=blue)](https://github.com/eworm-de/routeros-scripts/watchers)
+[![required RouterOS version](https://img.shields.io/badge/RouterOS-7.12-yellow?style=flat)](https://mikrotik.com/download/changelogs/)
+[![Telegram group @routeros_scripts](https://img.shields.io/badge/Telegram-%40routeros__scripts-%2326A5E4?logo=telegram&style=flat)](https://t.me/routeros_scripts)
+[![donate with PayPal](https://img.shields.io/badge/Like_it%3F-Donate!-orange?logo=githubsponsors&logoColor=orange&style=flat)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)
+
+[⬅️ Go back to main README](../README.md)
> ℹ️ **Info**: This script can not be used on its own but requires the base
> installation. See [main README](../README.md) for details.
@@ -33,5 +40,5 @@ See also
* [Update tunnelbroker configuration](update-tunnelbroker.md)
---
-[◀ Go back to main README](../README.md)
-[▲ Go back to top](#top)
+[⬅️ Go back to main README](../README.md)
+[⬆️ Go back to top](#top)
diff --git a/doc/sms-action.md b/doc/sms-action.md
index d2e3252..18ca574 100644
--- a/doc/sms-action.md
+++ b/doc/sms-action.md
@@ -1,7 +1,14 @@
Act on received SMS
===================
-[◀ Go back to main README](../README.md)
+[![GitHub stars](https://img.shields.io/github/stars/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=red)](https://github.com/eworm-de/routeros-scripts/stargazers)
+[![GitHub forks](https://img.shields.io/github/forks/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=green)](https://github.com/eworm-de/routeros-scripts/network)
+[![GitHub watchers](https://img.shields.io/github/watchers/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=blue)](https://github.com/eworm-de/routeros-scripts/watchers)
+[![required RouterOS version](https://img.shields.io/badge/RouterOS-7.12-yellow?style=flat)](https://mikrotik.com/download/changelogs/)
+[![Telegram group @routeros_scripts](https://img.shields.io/badge/Telegram-%40routeros__scripts-%2326A5E4?logo=telegram&style=flat)](https://t.me/routeros_scripts)
+[![donate with PayPal](https://img.shields.io/badge/Like_it%3F-Donate!-orange?logo=githubsponsors&logoColor=orange&style=flat)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)
+
+[⬅️ Go back to main README](../README.md)
> ℹ️ **Info**: This script can not be used on its own but requires the base
> installation. See [main README](../README.md) for details.
@@ -28,6 +35,10 @@ The configuration goes to `global-config-overlay`, this is the only parameter:
* `SmsAction`: an array with pre-defined actions
+> ℹ️ **Info**: Copy relevant configuration from
+> [`global-config`](../global-config.rsc) (the one without `-overlay`) to
+> your local `global-config-overlay` and modify it to your specific needs.
+
Then enable SMS actions:
/tool/sms/set allowed-number=+491234567890 receive-enabled=yes secret=s3cr3t;
@@ -48,5 +59,5 @@ See also
* [Forward received SMS](sms-forward.md)
---
-[◀ Go back to main README](../README.md)
-[▲ Go back to top](#top)
+[⬅️ Go back to main README](../README.md)
+[⬆️ Go back to top](#top)
diff --git a/doc/sms-forward.d/notification.avif b/doc/sms-forward.d/notification.avif
new file mode 100644
index 0000000..01eb7ba
--- /dev/null
+++ b/doc/sms-forward.d/notification.avif
Binary files differ
diff --git a/doc/sms-forward.d/notification.svg b/doc/sms-forward.d/notification.svg
deleted file mode 100644
index 4b94850..0000000
--- a/doc/sms-forward.d/notification.svg
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<svg id="svg8" width="443.78" height="123.78" version="1.1" viewBox="0 0 117.42 32.75" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
- <metadata id="metadata5">
- <rdf:RDF>
- <cc:Work rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <g id="layer1" transform="translate(16.375 11.083)">
- <rect id="rect857" x="-15.875" y="-10.583" width="116.42" height="31.75" rx="1.4552" fill="#e6e6e6" stroke="#6c5d53" stroke-linecap="round" stroke-linejoin="round"/>
- <g id="g884" transform="matrix(.5 0 0 .5 -12.406 -7.1146)" stroke-width="2">
- <path id="path899" d="m23.177 23.177c-2.9635 2.9635-17.991 2.9635-20.955 0-2.9635-2.9635-2.9635-17.991 0-20.955 2.9635-2.9635 17.991-2.9635 20.955 0 2.9635 2.9635 2.9635 17.991 0 20.955z" fill="#fff"/>
- <g id="g871">
- <g id="text837" stroke-width=".52916px" aria-label="#!rsc">
- <path id="path851" d="m7.4832 16.646v-1.0239h-0.54606l0.16577-1.2774h-1.2774l-0.16577 1.2774h-0.97511l0.16577-1.2774h-1.2774l-0.16577 1.2774h-0.70208v1.0239h0.56556l-0.24378 1.8722h-0.68257v1.0239h0.54606l-0.18527 1.3944h1.2774l0.18527-1.3944h0.97511l-0.18527 1.3944h1.2774l0.18527-1.3944h0.70208v-1.0239h-0.56556l0.24378-1.8722zm-2.2037 1.8722h-0.97511l0.24378-1.8722h0.97511z"/>
- <path id="path853" d="m9.6187 14.179h-1.6382l0.19502 4.271h1.2481zm-0.81909 5.1583c-0.48755 0-0.8776 0.39979-0.8776 0.8776 0 0.48755 0.39004 0.88735 0.8776 0.88735 0.4973 0 0.88735-0.39979 0.88735-0.88735 0-0.4778-0.39004-0.8776-0.88735-0.8776z"/>
- <path id="path855" d="m13.373 15.612c-0.59482 0-1.1019 0.42905-1.3359 1.1506l-0.13652-1.0044h-1.3359v5.1778h1.5407v-2.6035c0.17552-0.77033 0.4388-1.2286 1.0726-1.2286 0.16577 0 0.30228 0.02925 0.46805 0.06826l0.24378-1.4919c-0.17552-0.04875-0.32178-0.06826-0.51681-0.06826z"/>
- <path id="path857" d="m16.181 15.592c-1.3066 0-2.116 0.69233-2.116 1.5797 0 0.79959 0.50706 1.3261 1.5309 1.6187 0.9361 0.26328 1.0921 0.37054 1.0921 0.72158 0 0.31203-0.28278 0.48755-0.75083 0.48755-0.50706 0-0.98486-0.20477-1.3749-0.50706l-0.75083 0.83859c0.50706 0.4583 1.2676 0.77033 2.1647 0.77033 1.2871 0 2.3012-0.63382 2.3012-1.7064 0-0.92635-0.57531-1.3554-1.5992-1.6479-0.92635-0.27303-1.0629-0.39004-1.0629-0.66307 0-0.23402 0.20477-0.39004 0.62407-0.39004 0.44855 0 0.8776 0.14627 1.2774 0.39979l0.56556-0.86784c-0.4778-0.39004-1.1506-0.63382-1.9015-0.63382z"/>
- <path id="path859" d="m21.281 15.592c-1.5504 0-2.5353 1.1506-2.5353 2.7986 0 1.6382 0.97511 2.7108 2.5645 2.7108 0.71183 0 1.2676-0.23403 1.7454-0.61432l-0.67282-0.9556c-0.37054 0.23403-0.62407 0.35104-0.99461 0.35104-0.61432 0-1.0239-0.35104-1.0239-1.5017 0-1.1604 0.38029-1.6089 1.0434-1.6089 0.35104 0 0.65332 0.11701 0.98486 0.37054l0.66307-0.9166c-0.4973-0.4193-1.0531-0.63382-1.7747-0.63382z"/>
- </g>
- <g id="g1542" transform="matrix(2 0 0 2 -.41134 3.175)" fill="#676867" fill-rule="evenodd" stroke-width=".26458">
- <path id="path943" d="m4.9596-1.0196c0.40797 2.8312 1.9272 4.5499 5.0239 4.691-2.918 1.1164-5.9253-1.5076-5.0239-4.691"/>
- <path id="path945" d="m3.3407-0.52096c0.034969-0.00777 0.038854 0.015542 0.041445 0.041445 0.098431 1.8054 0.85998 3.1744 1.8689 4.1108 1.0089 0.93639 2.3636 1.6941 4.274 1.6604-3.5772 1.4247-7.337-1.9984-6.1856-5.8126"/>
- </g>
- </g>
- </g>
- <text id="text4811" transform="matrix(.26458 0 0 .26458 -44.966 -6.6039)" x="-248.88142" fill="#000000" font-family="'Fira Mono', 'Roboto Mono', monospace" font-size="12px" letter-spacing="0px" stroke-width="1px" word-spacing="0px" style="font-variant-caps:normal;font-variant-east-asian:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:15px;shape-inside:url(#rect4813);white-space:pre" xml:space="preserve"><tspan id="tspan2204" x="180" y="10.85">[MikroTik] 📨 SMS Forwarding from 7277
-</tspan><tspan id="tspan2206" x="180" y="25.85">
-</tspan><tspan id="tspan2208" x="180" y="40.85">Received this message by MikroTik from 7277:
-</tspan><tspan id="tspan2210" x="180" y="55.85">
-</tspan><tspan id="tspan2212" x="180" y="70.85">On Jun/12/2021 13:44:10 GMT -0 type class-0:
-</tspan><tspan id="tspan2214" x="180" y="85.85">Welcome to our network!</tspan></text>
- </g>
-</svg>
diff --git a/doc/sms-forward.md b/doc/sms-forward.md
index f75b78f..2fe9486 100644
--- a/doc/sms-forward.md
+++ b/doc/sms-forward.md
@@ -1,7 +1,14 @@
Forward received SMS
====================
-[◀ Go back to main README](../README.md)
+[![GitHub stars](https://img.shields.io/github/stars/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=red)](https://github.com/eworm-de/routeros-scripts/stargazers)
+[![GitHub forks](https://img.shields.io/github/forks/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=green)](https://github.com/eworm-de/routeros-scripts/network)
+[![GitHub watchers](https://img.shields.io/github/watchers/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=blue)](https://github.com/eworm-de/routeros-scripts/watchers)
+[![required RouterOS version](https://img.shields.io/badge/RouterOS-7.12-yellow?style=flat)](https://mikrotik.com/download/changelogs/)
+[![Telegram group @routeros_scripts](https://img.shields.io/badge/Telegram-%40routeros__scripts-%2326A5E4?logo=telegram&style=flat)](https://t.me/routeros_scripts)
+[![donate with PayPal](https://img.shields.io/badge/Like_it%3F-Donate!-orange?logo=githubsponsors&logoColor=orange&style=flat)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)
+
+[⬅️ Go back to main README](../README.md)
> ℹ️ **Info**: This script can not be used on its own but requires the base
> installation. See [main README](../README.md) for details.
@@ -15,7 +22,7 @@ A broadband interface with SMS support is required.
### Sample notification
-![sms-forward notification](sms-forward.d/notification.svg)
+![sms-forward notification](sms-forward.d/notification.avif)
Requirements and installation
-----------------------------
@@ -31,13 +38,55 @@ Just install the script:
Configuration
-------------
+You have to enable receiving of SMS:
+
+ /tool/sms/set receive-enabled=yes;
+
+The configuration goes to `global-config-overlay`, this is the only parameter:
+
+* `SmsForwardHooks`: an array with pre-defined hooks, where each hook consists
+ of `match` (which is matched against the received message), `allowed-number`
+ (which is matched against the sending phone number or name) and `command`.
+ For `match` and `allowed-number` regular expressions are supported. Actual
+ phone number (`$Phone`) and message (`$Message`) are available for the hook.
+
+> ℹ️ **Info**: Copy relevant configuration from
+> [`global-config`](../global-config.rsc) (the one without `-overlay`) to
+> your local `global-config-overlay` and modify it to your specific needs.
+
Notification settings are required for
[e-mail](mod/notification-email.md),
-[matrix](mod/notification-matrix.md) and/or
+[matrix](mod/notification-matrix.md),
+[ntfy](mod/notification-ntfy.md) and/or
[telegram](mod/notification-telegram.md).
-Also you have to enable receiving of SMS:
- /tool/sms/set receive-enabled=yes;
+Tips & Tricks
+-------------
+
+### Take care of harmful commands!
+
+It is easy to fake the sending phone number! So make sure you do not rely on
+that number for potentially harmful commands. Add a shared secret to match
+into the text instead, for example: `reboot-53cr3t-5tr1n9` instead of just
+`reboot`.
+
+### Order new volume
+
+Most broadband providers include a volume limit for their data plans. The
+hook functionality can be used to order new volume automatically.
+
+Let's assume an imaginary provider **ABC** sends a message when the available
+volume is about to deplete. The message is sent from `ABC` and the text
+contains the string `80%`. New volume can be ordered by sending a SMS back to
+the phone number `1234` with the text `data-plan`.
+
+ :global SmsForwardHooks {
+ { match="80%";
+ allowed-number="ABC";
+ command="/tool/sms/send lte1 phone-number=1234 message=\"data-plan\";" };
+ };
+
+Adjust the values to your own needs.
See also
--------
@@ -45,5 +94,5 @@ See also
* [Act on received SMS](sms-action.md)
---
-[◀ Go back to main README](../README.md)
-[▲ Go back to top](#top)
+[⬅️ Go back to main README](../README.md)
+[⬆️ Go back to top](#top)
diff --git a/doc/ssh-keys-import.md b/doc/ssh-keys-import.md
index d83311f..d1325aa 100644
--- a/doc/ssh-keys-import.md
+++ b/doc/ssh-keys-import.md
@@ -1,33 +1,2 @@
-Import SSH keys
-===============
-
-[◀ Go back to main README](../README.md)
-
-Description
------------
-
-This script imports public SSH keys (files with extension "`pub`") into
-local store for user authentication.
-
-Requirements and installation
------------------------------
-
-Just install the script:
-
- $ScriptInstallUpdate ssh-keys-import;
-
-Usage and invocation
---------------------
-
-Copy files with extension "`pub`" containing public SSH keys for your device.
-Then run the script:
-
- /system/script/run ssh-keys-import;
-
-Starting with an `authorized_keys` file you can split it on a shell:
-
- grep -E '^ssh-rsa' authorized_keys | nl -nrz | while read num type key name; do echo $type $key $name > $num-$name.pub; done
-
----
-[◀ Go back to main README](../README.md)
-[▲ Go back to top](#top)
+This script has been replaced by a module. Please see
+[Import ssh keys for public key authentication](mod/ssh-keys-import.md).
diff --git a/doc/super-mario-theme.md b/doc/super-mario-theme.md
index 8142cda..ec59b39 100644
--- a/doc/super-mario-theme.md
+++ b/doc/super-mario-theme.md
@@ -1,7 +1,14 @@
Play Super Mario theme
======================
-[◀ Go back to main README](../README.md)
+[![GitHub stars](https://img.shields.io/github/stars/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=red)](https://github.com/eworm-de/routeros-scripts/stargazers)
+[![GitHub forks](https://img.shields.io/github/forks/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=green)](https://github.com/eworm-de/routeros-scripts/network)
+[![GitHub watchers](https://img.shields.io/github/watchers/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=blue)](https://github.com/eworm-de/routeros-scripts/watchers)
+[![required RouterOS version](https://img.shields.io/badge/RouterOS-7.12-yellow?style=flat)](https://mikrotik.com/download/changelogs/)
+[![Telegram group @routeros_scripts](https://img.shields.io/badge/Telegram-%40routeros__scripts-%2326A5E4?logo=telegram&style=flat)](https://t.me/routeros_scripts)
+[![donate with PayPal](https://img.shields.io/badge/Like_it%3F-Donate!-orange?logo=githubsponsors&logoColor=orange&style=flat)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)
+
+[⬅️ Go back to main README](../README.md)
Description
-----------
@@ -27,5 +34,5 @@ Just run the script to play:
For extra fun use it for dhcp lease script. :)
---
-[◀ Go back to main README](../README.md)
-[▲ Go back to top](#top)
+[⬅️ Go back to main README](../README.md)
+[⬆️ Go back to top](#top)
diff --git a/doc/telegram-chat.d/01-chat-specific.avif b/doc/telegram-chat.d/01-chat-specific.avif
new file mode 100644
index 0000000..ab75f78
--- /dev/null
+++ b/doc/telegram-chat.d/01-chat-specific.avif
Binary files differ
diff --git a/doc/telegram-chat.d/02-chat-all.avif b/doc/telegram-chat.d/02-chat-all.avif
new file mode 100644
index 0000000..ed1a389
--- /dev/null
+++ b/doc/telegram-chat.d/02-chat-all.avif
Binary files differ
diff --git a/doc/telegram-chat.d/03-reply.avif b/doc/telegram-chat.d/03-reply.avif
new file mode 100644
index 0000000..515853e
--- /dev/null
+++ b/doc/telegram-chat.d/03-reply.avif
Binary files differ
diff --git a/doc/telegram-chat.md b/doc/telegram-chat.md
new file mode 100644
index 0000000..2a4af99
--- /dev/null
+++ b/doc/telegram-chat.md
@@ -0,0 +1,153 @@
+Chat with your router and send commands via Telegram bot
+========================================================
+
+[![GitHub stars](https://img.shields.io/github/stars/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=red)](https://github.com/eworm-de/routeros-scripts/stargazers)
+[![GitHub forks](https://img.shields.io/github/forks/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=green)](https://github.com/eworm-de/routeros-scripts/network)
+[![GitHub watchers](https://img.shields.io/github/watchers/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=blue)](https://github.com/eworm-de/routeros-scripts/watchers)
+[![required RouterOS version](https://img.shields.io/badge/RouterOS-7.12-yellow?style=flat)](https://mikrotik.com/download/changelogs/)
+[![Telegram group @routeros_scripts](https://img.shields.io/badge/Telegram-%40routeros__scripts-%2326A5E4?logo=telegram&style=flat)](https://t.me/routeros_scripts)
+[![donate with PayPal](https://img.shields.io/badge/Like_it%3F-Donate!-orange?logo=githubsponsors&logoColor=orange&style=flat)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)
+
+[⬅️ Go back to main README](../README.md)
+
+> ℹ️ **Info**: This script can not be used on its own but requires the base
+> installation. See [main README](../README.md) for details.
+
+Description
+-----------
+
+This script makes your device poll a Telegram bot for new messages. With
+these messages you can send commands to your device and make it run them.
+The resulting output is send back to you.
+
+Requirements and installation
+-----------------------------
+
+Just install the script and the module for notifications via Telegram:
+
+ $ScriptInstallUpdate telegram-chat,mod/notification-telegram;
+
+Then create a schedule that runs the script periodically:
+
+ /system/scheduler/add start-time=startup interval=30s name=telegram-chat on-event="/system/script/run telegram-chat;";
+
+> ⚠️ **Warning**: Make sure to keep the interval in sync when installing
+> on several devices. Differing polling intervals will result in missed
+> messages.
+
+Configuration
+-------------
+
+Make sure to configure
+[notifications via telegram](mod/notification-telegram.md) first. The
+additional configuration goes to `global-config-overlay`, these are the
+parameters:
+
+* `TelegramChatIdsTrusted`: an array with trusted chat ids or user names
+* `TelegramChatGroups`: define the groups a device should belong to
+
+> ℹ️ **Info**: Copy relevant configuration from
+> [`global-config`](../global-config.rsc) (the one without `-overlay`) to
+> your local `global-config-overlay` and modify it to your specific needs.
+
+Usage and invocation
+--------------------
+
+### Activating device(s)
+
+This script is capable of chatting with multiple devices. By default a
+device is passive and not acting on messages. To activate it send a message
+containing `! identity` (exclamation mark, optional space and system's
+identity). To query all dynamic ip addresses form a device named "*MikroTik*"
+send `! MikroTik`, followed by `/ip/address/print where dynamic;`.
+
+![chat to specific device](telegram-chat.d/01-chat-specific.avif)
+
+Devices can be grouped to chat with them simultaneously. The default group
+"*all*" can be activated by sending `! @all`, which will make all devices
+act on your commands.
+
+![chat to all devices](telegram-chat.d/02-chat-all.avif)
+
+Send a single exclamation mark or non-existent identity to make all
+devices passive again.
+
+### Reply to message
+
+Let's assume you received a message from a device before, and want to send
+a command to that device. No need to activate it, you can just reply to
+that message.
+
+![reply to message](telegram-chat.d/03-reply.avif)
+
+Associated messages are cleared on device reboot.
+
+### Ask for devices
+
+Send a message with a single question mark (`?`) to query for devices
+currenty online. The answer can be used for command via reply then.
+
+Known limitations
+-----------------
+
+### Do not use numeric ids!
+
+Numeric ids are valid within a session only. Usually you can use something
+like this to print all ip addresses and remove the first one:
+
+ /ip/address/print;
+ /ip/address/remove 0;
+
+This will fail when sent in separate messages. Instead you should use basic
+scripting capabilities. Try to print what you want to act on...
+
+ /ip/address/print where interface=eth;
+
+... verify and finally remove it.
+
+ /ip/address/remove [ find where interface=eth ];
+
+What does work is using the persistent ids:
+
+ /ip/address/print show-ids;
+
+The output contains an id starting with asterisk that can be used:
+
+ /ip/address/remove *E;
+
+### Mind command runtime
+
+The command is run in background while the script waits for it - about
+20 seconds at maximum. A command exceeding that time continues to run in
+background, but the output in the message is missing or truncated then.
+
+If you still want a response you can work around this by making your code
+send information on its own. Something like this should do the job:
+
+ :global SendTelegram;
+ :delay 30s;
+ $SendTelegram "Command finished" "Your command finished...";
+
+### Output size
+
+RouterOS is limited in reading file content to a size of about four
+kilobytes. Reading larger files does just fail, and that is also the limit
+for command output.
+
+### Sending commands to a group
+
+Adding a bot to a group allows it to send messages to that group. To allow
+it to receive messages you have to make it an admin of that group! It is
+fine to deny all permissions, though.
+
+Also adding an admin to a group can cause the group id to change, so check
+that if notifications break suddenly.
+
+See also
+--------
+
+* [Send notifications via Telegram](mod/notification-telegram.md)
+
+---
+[⬅️ Go back to main README](../README.md)
+[⬆️ Go back to top](#top)
diff --git a/doc/unattended-lte-firmware-upgrade.md b/doc/unattended-lte-firmware-upgrade.md
index 63f2793..a6bf994 100644
--- a/doc/unattended-lte-firmware-upgrade.md
+++ b/doc/unattended-lte-firmware-upgrade.md
@@ -1,7 +1,14 @@
Install LTE firmware upgrade
============================
-[◀ Go back to main README](../README.md)
+[![GitHub stars](https://img.shields.io/github/stars/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=red)](https://github.com/eworm-de/routeros-scripts/stargazers)
+[![GitHub forks](https://img.shields.io/github/forks/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=green)](https://github.com/eworm-de/routeros-scripts/network)
+[![GitHub watchers](https://img.shields.io/github/watchers/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=blue)](https://github.com/eworm-de/routeros-scripts/watchers)
+[![required RouterOS version](https://img.shields.io/badge/RouterOS-7.12-yellow?style=flat)](https://mikrotik.com/download/changelogs/)
+[![Telegram group @routeros_scripts](https://img.shields.io/badge/Telegram-%40routeros__scripts-%2326A5E4?logo=telegram&style=flat)](https://t.me/routeros_scripts)
+[![donate with PayPal](https://img.shields.io/badge/Like_it%3F-Donate!-orange?logo=githubsponsors&logoColor=orange&style=flat)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)
+
+[⬅️ Go back to main README](../README.md)
Description
-----------
@@ -12,6 +19,7 @@ This script upgrades LTE firmware on compatible devices:
* R11e-LTE-US
* R11e-4G
* R11e-LTE6
+* ... and more - probably what ever Mikrotik builds into their devices
A temporary scheduler is created to be independent from terminal. Thus
starting the upgrade process over the broadband connection is supported.
@@ -42,5 +50,5 @@ See also
* [Notify on LTE firmware upgrade](check-lte-firmware-upgrade.md)
---
-[◀ Go back to main README](../README.md)
-[▲ Go back to top](#top)
+[⬅️ Go back to main README](../README.md)
+[⬆️ Go back to top](#top)
diff --git a/doc/update-gre-address.md b/doc/update-gre-address.md
index 7e87743..fba2a65 100644
--- a/doc/update-gre-address.md
+++ b/doc/update-gre-address.md
@@ -1,7 +1,14 @@
Update GRE configuration with dynamic addresses
===============================================
-[◀ Go back to main README](../README.md)
+[![GitHub stars](https://img.shields.io/github/stars/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=red)](https://github.com/eworm-de/routeros-scripts/stargazers)
+[![GitHub forks](https://img.shields.io/github/forks/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=green)](https://github.com/eworm-de/routeros-scripts/network)
+[![GitHub watchers](https://img.shields.io/github/watchers/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=blue)](https://github.com/eworm-de/routeros-scripts/watchers)
+[![required RouterOS version](https://img.shields.io/badge/RouterOS-7.12-yellow?style=flat)](https://mikrotik.com/download/changelogs/)
+[![Telegram group @routeros_scripts](https://img.shields.io/badge/Telegram-%40routeros__scripts-%2326A5E4?logo=telegram&style=flat)](https://t.me/routeros_scripts)
+[![donate with PayPal](https://img.shields.io/badge/Like_it%3F-Donate!-orange?logo=githubsponsors&logoColor=orange&style=flat)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)
+
+[⬅️ Go back to main README](../README.md)
> ℹ️ **Info**: This script can not be used on its own but requires the base
> installation. See [main README](../README.md) for details.
@@ -37,5 +44,5 @@ certificate CN into the comment:
/interface/gre/set comment="ikev2-client1" gre-client1;
---
-[◀ Go back to main README](../README.md)
-[▲ Go back to top](#top)
+[⬅️ Go back to main README](../README.md)
+[⬆️ Go back to top](#top)
diff --git a/doc/update-tunnelbroker.md b/doc/update-tunnelbroker.md
index bfe8e25..5aca581 100644
--- a/doc/update-tunnelbroker.md
+++ b/doc/update-tunnelbroker.md
@@ -1,7 +1,14 @@
Update tunnelbroker configuration
=================================
-[◀ Go back to main README](../README.md)
+[![GitHub stars](https://img.shields.io/github/stars/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=red)](https://github.com/eworm-de/routeros-scripts/stargazers)
+[![GitHub forks](https://img.shields.io/github/forks/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=green)](https://github.com/eworm-de/routeros-scripts/network)
+[![GitHub watchers](https://img.shields.io/github/watchers/eworm-de/routeros-scripts?logo=GitHub&style=flat&color=blue)](https://github.com/eworm-de/routeros-scripts/watchers)
+[![required RouterOS version](https://img.shields.io/badge/RouterOS-7.12-yellow?style=flat)](https://mikrotik.com/download/changelogs/)
+[![Telegram group @routeros_scripts](https://img.shields.io/badge/Telegram-%40routeros__scripts-%2326A5E4?logo=telegram&style=flat)](https://t.me/routeros_scripts)
+[![donate with PayPal](https://img.shields.io/badge/Like_it%3F-Donate!-orange?logo=githubsponsors&logoColor=orange&style=flat)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)
+
+[⬅️ Go back to main README](../README.md)
> ℹ️ **Info**: This script can not be used on its own but requires the base
> installation. See [main README](../README.md) for details.
@@ -28,11 +35,10 @@ Configuration
The configuration goes to interface's comment:
- /interface/6to4/set comment="tunnelbroker, user=user, pass=s3cr3t, id=12345" tunnelbroker;
-
-Also enabling dynamic DNS in Mikrotik cloud is required:
+ /interface/6to4/set comment="tunnelbroker, user=user, id=12345, pass=s3cr3t" tunnelbroker;
- /ip/cloud/set ddns-enabled=yes;
+You should know you user name from login. The `id` is the tunnel's numeric
+id, `pass` is the *update key* found on the tunnel's advanced tab.
See also
--------
@@ -40,5 +46,5 @@ See also
* [Run scripts on ppp connection](ppp-on-up.md)
---
-[◀ Go back to main README](../README.md)
-[▲ Go back to top](#top)
+[⬅️ Go back to main README](../README.md)
+[⬆️ Go back to top](#top)
diff --git a/firmware-upgrade-reboot b/firmware-upgrade-reboot
deleted file mode 100644
index 27bbe41..0000000
--- a/firmware-upgrade-reboot
+++ /dev/null
@@ -1,41 +0,0 @@
-#!rsc by RouterOS
-# RouterOS script: firmware-upgrade-reboot
-# Copyright (c) 2022 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 ("Firmware is already up to date.") 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;
diff --git a/firmware-upgrade-reboot.rsc b/firmware-upgrade-reboot.rsc
new file mode 100644
index 0000000..038f74e
--- /dev/null
+++ b/firmware-upgrade-reboot.rsc
@@ -0,0 +1,54 @@
+#!rsc by RouterOS
+# RouterOS script: firmware-upgrade-reboot
+# Copyright (c) 2022-2024 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# requires RouterOS, version=7.12
+#
+# install firmware upgrade, and reboot
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/firmware-upgrade-reboot.md
+
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:do {
+ :local ScriptName [ :jobname ];
+
+ :global LogPrint;
+ :global ScriptLock;
+ :global VersionToNum;
+
+ :if ([ $ScriptLock $ScriptName ] = false) do={
+ :error false;
+ }
+
+ :local RouterBoard [ /system/routerboard/get ];
+ :if ($RouterBoard->"current-firmware" = $RouterBoard->"upgrade-firmware") do={
+ $LogPrint info $ScriptName ("Current and upgrade firmware match with version " . \
+ $RouterBoard->"current-firmware" . ".");
+ :error true;
+ }
+ :if ([ $VersionToNum ($RouterBoard->"current-firmware") ] > [ $VersionToNum ($RouterBoard->"upgrade-firmware") ]) do={
+ $LogPrint info $ScriptName ("Different firmware version is available, but it is a downgrade. Ignoring.");
+ :error true;
+ }
+
+ :if ([ /system/routerboard/settings/get auto-upgrade ] = false) do={
+ $LogPrint info $ScriptName ("Firmware version " . $RouterBoard->"upgrade-firmware" . \
+ " is available, upgrading.");
+ /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;
+ }
+
+ $LogPrint info $ScriptName ("Firmware upgrade successful, rebooting.");
+ /system/reboot;
+} on-error={ }
diff --git a/fw-addr-lists.d/allow b/fw-addr-lists.d/allow
new file mode 100644
index 0000000..8b59ed7
--- /dev/null
+++ b/fw-addr-lists.d/allow
@@ -0,0 +1,3 @@
+# an ip address list for use with fw-addr-lists script
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/fw-addr-lists.md
+git.eworm.de
diff --git a/fw-addr-lists.d/block b/fw-addr-lists.d/block
new file mode 100644
index 0000000..5e9fef2
--- /dev/null
+++ b/fw-addr-lists.d/block
@@ -0,0 +1,5 @@
+# an ip address list for use with fw-addr-lists script
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/fw-addr-lists.md
+
+# example.net
+93.184.216.34
diff --git a/fw-addr-lists.d/mikrotik b/fw-addr-lists.d/mikrotik
new file mode 100644
index 0000000..3b31a94
--- /dev/null
+++ b/fw-addr-lists.d/mikrotik
@@ -0,0 +1,5 @@
+# AS51894 Mikrotikls SIA
+# https://bgp.he.net/AS51894
+159.148.147.0/24
+159.148.172.0/24
+2a02:610:7501::/48
diff --git a/fw-addr-lists.rsc b/fw-addr-lists.rsc
new file mode 100644
index 0000000..68775b4
--- /dev/null
+++ b/fw-addr-lists.rsc
@@ -0,0 +1,159 @@
+#!rsc by RouterOS
+# RouterOS script: fw-addr-lists
+# Copyright (c) 2023-2024 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# requires RouterOS, version=7.12
+#
+# download, import and update firewall address-lists
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/fw-addr-lists.md
+
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:do {
+ :local ScriptName [ :jobname ];
+
+ :global FwAddrLists;
+ :global FwAddrListTimeOut;
+
+ :global CertificateAvailable;
+ :global EitherOr;
+ :global FetchUserAgentStr;
+ :global LogPrint;
+ :global LogPrintOnce;
+ :global ScriptLock;
+ :global WaitFullyConnected;
+
+ :local FindDelim do={
+ :local ValidChars "0123456789.:/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-";
+ :for I from=0 to=[ :len $1 ] do={
+ :if ([ :typeof [ :find $ValidChars [ :pick ($1 . " ") $I ] ] ] != "num") do={
+ :return $I;
+ }
+ }
+ }
+
+ :if ([ $ScriptLock $ScriptName ] = false) do={
+ :error false;
+ }
+ $WaitFullyConnected;
+
+ :local ListComment ("managed by " . $ScriptName);
+
+ :foreach FwListName,FwList in=$FwAddrLists do={
+ :local CntAdd 0;
+ :local CntRenew 0;
+ :local CntRemove 0;
+ :local IPv4Addresses ({});
+ :local IPv6Addresses ({});
+ :local Failure false;
+
+ :foreach List in=$FwList do={
+ :local CheckCertificate "no";
+ :local Data false;
+ :local TimeOut [ $EitherOr [ :totime ($List->"timeout") ] $FwAddrListTimeOut ];
+
+ :if ([ :len ($List->"cert") ] > 0) do={
+ :set CheckCertificate "yes-without-crl";
+ :if ([ $CertificateAvailable ($List->"cert") ] = false) do={
+ $LogPrint warning $ScriptName ("Downloading required certificate failed, trying anyway.");
+ }
+ }
+
+ :for I from=1 to=5 do={
+ :if ($Data = false) do={
+ :do {
+ :set Data ([ /tool/fetch check-certificate=$CheckCertificate output=user \
+ http-header-field=({ [ $FetchUserAgentStr $ScriptName ] }) ($List->"url") as-value ]->"data");
+ } on-error={
+ :if ($I < 5) do={
+ $LogPrint debug $ScriptName ("Failed downloading, " . $I . ". try: " . $List->"url");
+ :delay (($I * $I) . "s");
+ }
+ }
+ }
+ }
+
+ :if ($Data = false) do={
+ :set Data "";
+ :set Failure true;
+ $LogPrint warning $ScriptName ("Failed downloading list from: " . $List->"url");
+ }
+
+ :if ([ :len $Data ] > 63000) do={
+ $LogPrintOnce warning $ScriptName ("The list is huge and may be truncated: " . $List->"url");
+ }
+
+ :while ([ :len $Data ] != 0) do={
+ :local Line [ :pick $Data 0 [ :find $Data "\n" ] ];
+ :local Address ([ :pick $Line 0 [ $FindDelim $Line ] ] . ($List->"cidr"));
+ :if ($Address ~ "^[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}(/[0-9]{1,2})?\$" || \
+ $Address ~ "^[\\.a-zA-Z0-9-]+\\.[a-zA-Z]{2,}\$") do={
+ :set ($IPv4Addresses->$Address) $TimeOut;
+ }
+ :if ($Address ~ "^[0-9a-zA-Z]*:[0-9a-zA-Z:\\.]+(/[0-9]{1,3})?\$" || \
+ $Address ~ "^[\\.a-zA-Z0-9-]+\\.[a-zA-Z]{2,}\$") do={
+ :set ($IPv6Addresses->$Address) $TimeOut;
+ }
+ :set Data [ :pick $Data ([ :len $Line ] + 1) [ :len $Data ] ];
+ }
+ }
+
+ :foreach Entry in=[ /ip/firewall/address-list/find where list=$FwListName comment=$ListComment ] do={
+ :local Address [ /ip/firewall/address-list/get $Entry address ];
+ :if ([ :typeof ($IPv4Addresses->$Address) ] = "time") do={
+ $LogPrint debug $ScriptName ("Renewing IPv4 address for " . ($IPv4Addresses->$Address) . ": " . $Address);
+ /ip/firewall/address-list/set $Entry timeout=($IPv4Addresses->$Address);
+ :set ($IPv4Addresses->$Address);
+ :set CntRenew ($CntRenew + 1);
+ } else={
+ :if ($Failure = false) do={
+ $LogPrint debug $ScriptName ("Removing IPv4 address: " . $Address);
+ /ip/firewall/address-list/remove $Entry;
+ :set CntRemove ($CntRemove + 1);
+ }
+ }
+ }
+
+ :foreach Entry in=[ /ipv6/firewall/address-list/find where list=$FwListName comment=$ListComment ] do={
+ :local Address [ /ipv6/firewall/address-list/get $Entry address ];
+ :if ([ :typeof ($IPv6Addresses->$Address) ] = "time") do={
+ $LogPrint debug $ScriptName ("Renewing IPv6 address for " . ($IPv6Addresses->$Address) . ": " . $Address);
+ /ipv6/firewall/address-list/set $Entry timeout=($IPv6Addresses->$Address);
+ :set ($IPv6Addresses->$Address);
+ :set CntRenew ($CntRenew + 1);
+ } else={
+ :if ($Failure = false) do={
+ $LogPrint debug $ScriptName ("Removing: " . $Address);
+ /ipv6/firewall/address-list/remove $Entry;
+ :set CntRemove ($CntRemove + 1);
+ }
+ }
+ }
+
+ :foreach Address,Timeout in=$IPv4Addresses do={
+ $LogPrint debug $ScriptName ("Adding IPv4 address for " . $Timeout . ": " . $Address);
+ :do {
+ /ip/firewall/address-list/add list=$FwListName comment=$ListComment address=$Address timeout=$Timeout;
+ :set ($IPv4Addresses->$Address);
+ :set CntAdd ($CntAdd + 1);
+ } on-error={
+ $LogPrint warning $ScriptName ("Failed to add IPv4 address " . $Address . " to list '" . $FwListName . "'.");
+ }
+ }
+
+ :foreach Address,Timeout in=$IPv6Addresses do={
+ $LogPrint debug $ScriptName ("Adding IPv6 address for " . $Timeout . ": " . $Address);
+ :do {
+ /ipv6/firewall/address-list/add list=$FwListName comment=$ListComment address=$Address timeout=$Timeout;
+ :set ($IPv6Addresses->$Address);
+ :set CntAdd ($CntAdd + 1);
+ } on-error={
+ $LogPrint warning $ScriptName ("Failed to add IPv6 address " . $Address . " to list '" . $FwListName . "'.");
+ }
+ }
+
+ $LogPrint info $ScriptName ("list: " . $FwListName . " -- added: " . $CntAdd . " - renewed: " . $CntRenew . " - removed: " . $CntRemove);
+ }
+} on-error={ }
diff --git a/global-config-overlay b/global-config-overlay
deleted file mode 100644
index 70d8570..0000000
--- a/global-config-overlay
+++ /dev/null
@@ -1,11 +0,0 @@
-# Overlay for global configuration by RouterOS Scripts
-# Copyright (c) 2013-2022 Christian Hesse <mail@eworm.de>
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
-#
-# global configuration, custom overlay
-# https://git.eworm.de/cgit/routeros-scripts/about/
-
-# Copy configuration from global-config, paste and modify it here.
-
-
-# End of global-config-overlay
diff --git a/global-config-overlay.rsc b/global-config-overlay.rsc
new file mode 100644
index 0000000..9ffd90c
--- /dev/null
+++ b/global-config-overlay.rsc
@@ -0,0 +1,12 @@
+# Overlay for global configuration by RouterOS Scripts
+# Copyright (c) 2013-2024 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# global configuration, custom overlay
+# https://git.eworm.de/cgit/routeros-scripts/about/#editing-configuration
+
+# Copy relevant configuration from global-config, paste and modify it here.
+# https://git.eworm.de/cgit/routeros-scripts/about/global-config.rsc
+
+
+# End of global-config-overlay
diff --git a/global-config.changes b/global-config.changes
deleted file mode 100644
index 4bd302f..0000000
--- a/global-config.changes
+++ /dev/null
@@ -1,112 +0,0 @@
-# News, changes and migration by RouterOS Scripts
-# Copyright (c) 2019-2022 Christian Hesse <mail@eworm.de>
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
-
-:global IfThenElse;
-:global RequiredRouterOS;
-
-# Changes for global-config to be added to notification on script updates
-:global GlobalConfigChanges {
- 1="Moved variables from 'global-config' to 'global-functions' for independence";
- 2="Variable names became CamelCase to work around scripting issues";
- 3="Variable for certificate renew passphrase became an array to support multiple passphrases";
- 4="Added option to ignore global-config changes";
- 5="Split off new script 'cloud-backup' from 'email-backup'";
- 6="Introduced script 'upload-backup' with new configuration parameters";
- 7="Introduced script 'check-health' with new configuration parameters";
- 8="Added donation hint and option to silence it";
- 9="Introduced configuration overlay 'global-config-overlay'";
- 10="Made health threshold for voltage configurable";
- 11="Introduced function '\$ScriptInstallUpdate' to install new and update existing scripts";
- 12="Removed '\$ScriptUpdatesConfigChangesIgnore', comment '\$GlobalConfigVersion' in 'global-config-overlay' to disable change notifications";
- 13="Configuration for script 'bridge-port-to-default' changed with new syntax in comment";
- 14="Dropped script 'script-updates', use '\$ScriptInstallUpdate' exclusively!";
- 15="New documentation is online! https://git.eworm.de/cgit/routeros-scripts/about/#available-scripts";
- 16="Happy with RouterOS Scripts and have a GitHub and/or GitLab account? Please star!";
- 17="Introduced script 'early-errors'";
- 18=("Added a simple IP calculation function, try: \$IPCalc " . [ /ip/address/get ([ find ]->0) address ]);
- 19="Commenting scripts with 'ignore', 'base-url=...' and 'url-suffix=...' is honored on update";
- 20="Added support for hooks to 'netwatch-notify'";
- 21="Added support for installing patch updates automatically by 'check-routeros-update'";
- 22="Dropped '\$ScriptUpdatesIgnore' from global configuration, auto-migrating to ignore flag in comment"
- 23="Added 'log-forward' with configurable filter, which replaces 'early-errors'";
- 24="Made symbols in notifications configurable.";
- 25="Added support for DHCP server name in DNS FQDN via '\$ServerNameInZone'";
- 26="Made check count threshold in 'netwatch-notify' configurable.";
- 27="Added queue for Telegram notifications to resend later on error.";
- 28="Made 'dhcp-to-dns' act on all bound leases, not just dynamic ones.";
- 29="Added filter on log message text for 'log-forward'.";
- 30="Implemented simple rate limit for 'log-forward' to prevent flooding.";
- 31="Switched Telegram notifications to fixed-width font, with opt-out.";
- 32="Merged mode (& reset) button scripts in single new script 'mode-button'.";
- 33="Added configurable deviation on health temperature recovery threshold against notification flooding.";
- 34="Introduced script 'ospf-to-leds' to visualize OSPF instance state via LEDs.";
- 35="Implemented visual feedback for 'mode-button' with configurable LED.";
- 36="Added support for installing updates automatically if seen in neighbor list.";
- 37="Implemented simple dependency model in 'netwatch-notify'.";
- 38="Imported new Let's Encrypt intermediate certificate 'R3'.";
- 39="Added support for interface specific address list entries in 'ipv6-update'.";
- 40="Made the certificate renewal time configurable.";
- 41="Implemented migration mechanism for script updates.";
- 42="Made severity in terminal output colorful, with opt-out.";
- 43="Added queue for e-mail notifications to resend later on error.";
- 44="Dropped script 'global-wait', all scripts wait on their own now.";
- 45="We have a Telegram Group! Come along and say hello: https://t.me/routeros_scripts";
- 46="Added configurable random delay in backup scripts to stretch execution and prevent resource congestion.";
- 47="Removed obsolete intermediate certificate 'Let's Encrypt Authority X3' from store.";
- 48="Added support for overriding e-mail and Telegram settings for every script.";
- 49="Dropped '\$EmailBackupTo' & '\$EmailBackupCc' from configuration, use settings override if required.";
- 50="Added support for dynamic address update in 'netwatch-notify'.";
- 51="Added 'ipsec-to-dns' to add DNS records for IPSec peers from mode-config.";
- 52="Updated Let's Encrypt trust chain to use root certificate 'ISRG Root X1'. Do not re-import the old chain!";
- 53="Added support to send notifications via Matrix.";
- 54="Support for Telegram notifications moved to a module. It is installed automatically if required.";
- 55="Added reverse logic in 'log-forward', so messages can be included even if filtered before.";
- 56="Added tags in all backup, lease and ppp-on-up scripts. These are used by 'packages-update', 'lease-script' and 'ppp-on-up' to find the scripts.";
- 57="Celebrating the 1.000th commit - Hooray!";
- 58="Added a cleanup script for 'hotspot-to-wpa' to purge old access list entries.";
- 59="Updating CAP with 'check-routeros-update' is now possible with opt-in.";
- 60="Implemented a pre-down hook in 'netwatch-notify' that fires at two thirds of failed checks.";
- 61="Finally removed old scripts.";
- 62="Added '\$ScriptRunOnce' to run a script from URL once without installation, intended to aid configuration management and the like.";
- 63="Moved optional functions '\$IPCalc' and '\$ScriptRunOnce' to modules.";
- 64="Implemented '\$InspectVar' in module to inspect variables.";
- 65="Added module to manage VLANs on bridge ports.";
- 66="Moved script 'bridge-port-to-default' to new module.";
- 67="Moved modules to directory with shorter name.";
- 68="Reintroduced 'global-wait' for functions in scheduler.";
- 69="Support hard lower limit for voltage in 'check-health'.";
- 70="MikroTik started pushing RouterOS v7. Changes are no longer required.";
- 71="MikroTik is pushing RouterOS v7 even more, in parallel branches. If you want to keep RouterOS v6 for some time see https://git.eworm.de/cgit/routeros-scripts/about/#requirements";
- 72="Introduced new script 'netwatch-dns' to manage DNS and DoH servers from netwatch.";
- 73="Renamed backup scripts ('cloud-backup' -> 'backup-cloud', 'email-backup' -> 'backup-email', 'upload-backup' -> 'backup-upload').";
- 74="Extended 'hotspot-to-wpa', it can now read additional configuration from templates and hotspot users.";
- 75=("Finally merged the RouterOS v7 code into the main branch. " . [ $IfThenElse ([ $RequiredRouterOS "global-config.changes" "7.0" false ] = true) \
- ("You may now drop '\$ScriptUpdatesUrlSuffix' from 'global-config-overlay'.") \
- ("Still running RouterOS v6, so last reminder to see https://git.eworm.de/cgit/routeros-scripts/about/#requirements") ]);
- 76="Added an option to suppress notifications on host down with 'netwatch-notify'.";
- 77="Introduced new script 'firmware-upgrade-reboot'. Handle with care!";
- 78="New documentation is online for notifications via Telegram & Matrix, variable inspection, ip address calculation and running scripts once.";
- 79="Introduced new script 'backup-partition' to save configuration to fallback partition.";
- 80="The 'routeros-v7' branch will now freeze, and vanish any time in future. You already switched to 'main' branch, well done!";
- 81="Dropped script 'rotate-ntp', as the limitation does no longer exist.";
- 82="Renamed the comment parameter 'hostname' to just 'name' for 'netwatch-notify'.";
- 83="Introduced new setting to disable news and change notifications, dropped version from configuration.";
- 84="Support for e-mail notifications moved to a module. It is installed automatically if required.";
- 85="Dropped 'netwatch-syslog', filtering in firewall is advised.";
-};
-
-# Migration steps to be applied on script updates
-:global GlobalConfigMigration {
- 41=":global SendNotification; \$SendNotification (\"Migration mechanism\") (\"Congratulations!\nSuccessfully tested the new migration mechanism.\");";
- 47="/certificate/remove [ find where fingerprint=\"731d3d9cfaa061487a1d71445a42f67df0afca2a6c2d2f98ff7b3ce112b1f568\" or fingerprint=\"25847d668eb4f04fdd40b12b6b0740c567da7d024308eb6c2c96fe41d9de218d\" ];";
- 52=":global CertificateDownload; :if ([ :len [ /certificate/find where fingerprint=\"67add1166b020ae61b8f5fc96813c04c2aa589960796865572a3c7e737613dfd\" or fingerprint=\"96bcec06264976f37460779acf28c5a7cfe8a3c0aae11a8ffcee05c0bddf08c6\" ] ] < 2) do={ \$CertificateDownload \"R3\"; }; /certificate/remove [ find where fingerprint=\"0687260331a72403d909f105e69bcf0d32e1bd2493ffc6d9206d11bcd6770739\" ];";
- 54=":global ScriptInstallUpdate; :global TelegramTokenId; :global TelegramChatId; :if ([ :len \$TelegramTokenId ] > 0 && [ :len \$TelegramChatId ] > 0) do={ \$ScriptInstallUpdate mod/notification-telegram; }";
- 61="/system/script/remove [ find where name~\"^(early-errors|mode-button-(event|scheduler)|script-updates)\\\$\" source~\"^#!rsc by RouterOS\\n\" ];";
- 66=":global ScriptInstallUpdate; :if ([ :len [ /system/script/find where name=\"bridge-port-to-default\" ] ] > 0) do={ /system/script/remove [ find where name~\"^bridge-port-to(-default|ggle)\\\$\" ]; \$ScriptInstallUpdate mod/bridge-port-to; }";
- 67=":global ScriptInstallUpdate; :global CharacterReplace; :foreach Script in=[ /system/script/find where name~\"^global-functions.d/\" ] do={ /system/script/set name=[ \$CharacterReplace [ /system/script/get \$Script name ] \"global-functions.d/\" \"mod/\" ] \$Script; }; \$ScriptInstallUpdate;";
- 73=":global ScriptInstallUpdate; :global CharacterReplace; :foreach Old,New in={ \"cloud-backup\"=\"backup-cloud\"; \"email-backup\"=\"backup-email\"; \"upload-backup\"=\"backup-upload\" } do={ /system/script/set name=\$New [ find where name=\$Old ]; :foreach Scheduler in=[ /system/scheduler/find where on-event~\$Old ] do={ /system/scheduler/set \$Scheduler name=[ \$CharacterReplace [ get \$Scheduler name ] \$Old \$New ] on-event=[ \$CharacterReplace [ get \$Scheduler on-event ] \$Old \$New ]; }; }; \$ScriptInstallUpdate;";
- 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; }";
-};
diff --git a/global-config b/global-config.rsc
index 6ed2a45..f393abb 100644
--- a/global-config
+++ b/global-config.rsc
@@ -1,6 +1,6 @@
#!rsc by RouterOS
# RouterOS script: global-config
-# Copyright (c) 2013-2022 Christian Hesse <mail@eworm.de>
+# Copyright (c) 2013-2024 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# global configuration
@@ -9,11 +9,12 @@
# Set this to 'true' to disable news and change notifications.
:global NoNewsAndChangesNotification false;
-# This is used for DNS and backup file.
+# Add extra text (or emojis) in notification tags.
+:global IdentityExtra "";
+
+# This is used in DNS scripts ('ipsec-to-dns' and fallback in 'dhcp-to-dns')
+# and backup scripts for file names.
: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:
@@ -32,8 +33,14 @@
:global TelegramChatId "";
#:global TelegramTokenId "123456:ABCDEF-GHI";
#:global TelegramChatId "12345678";
-# This is whether or not to send Telegram messages with fixed-width font.
-:global TelegramFixedWidthFont true;
+# 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)";
# You can send Matrix notifications. Configure these settings and
# install the module:
@@ -45,9 +52,15 @@
#: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:
+# You can send Ntfy notifications. Configure these settings and
+# install the module:
+# $ScriptInstallUpdate mod/notification-ntfy
+:global NtfyServer "ntfy.sh";
+:global NtfyTopic "";
+
+# It is possible to override e-mail, Telegram, Matrix and Ntfy 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";
@@ -61,6 +74,7 @@
# 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.
@@ -70,11 +84,38 @@
:global BackupUploadUser "mikrotik";
:global BackupUploadPass "v3ry-s3cr3t";
+# This defines the settings for firewall address-lists (fw-addr-lists).
+:global FwAddrLists {
+# "allow"={
+# { url="https://git.eworm.de/cgit/routeros-scripts/plain/fw-addr-lists.d/allow";
+# cert="E1"; timeout=1w };
+# };
+ "block"={
+# { url="https://git.eworm.de/cgit/routeros-scripts/plain/fw-addr-lists.d/block";
+# cert="E1" };
+ { url="https://feodotracker.abuse.ch/downloads/ipblocklist_recommended.txt";
+ cert="GlobalSign Atlas R3 DV TLS CA 2022 Q3" };
+ { url="https://sslbl.abuse.ch/blacklist/sslipblacklist.txt";
+ cert="GlobalSign Atlas R3 DV TLS CA 2022 Q3" };
+ { url="https://www.dshield.org/block.txt"; cidr="/24";
+ cert="R3" };
+# { url="https://www.spamhaus.org/drop/drop.txt";
+# cert="Cloudflare Inc ECC CA-3" };
+# { url="https://www.spamhaus.org/drop/edrop.txt";
+# cert="Cloudflare Inc ECC CA-3" };
+ };
+# "mikrotik"={
+# { url="https://git.eworm.de/cgit/routeros-scripts/plain/fw-addr-lists.d/mikrotik";
+# cert="E1"; timeout=1w };
+# };
+};
+:global FwAddrListTimeOut 1d;
+
# 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 LogForwardFilter "(debug|info|packet|raw)";
:global LogForwardFilterMessage [];
#:global LogForwardFilterMessage "message text";
#:global LogForwardFilterMessage "(message text|another text|...)";
@@ -93,8 +134,13 @@
:global SafeUpdatePatch false;
# Allow to install updates automatically if seen in neighbor list.
:global SafeUpdateNeighbor false;
-# Allow to install updates even if device is managed by CAPsMAN.
-:global SafeUpdateOnCap false;
+:global SafeUpdateNeighborIdentity "";
+# Install *ALL* updates automatically!
+# Set to all upper-case "Yes, please!" to enable.
+:global SafeUpdateAll "no";
+
+# Defer the reboot for night on automatic (non-interactive) update
+:global PackagesUpdateDeferReboot false;
# These thresholds control when to send health notification
# on temperature and voltage.
@@ -103,7 +149,7 @@
cpu-temperature=70;
board-temperature1=50;
board-temperature2=50;
-}
+};
# This is deviation on recovery threshold against notification flooding.
:global CheckHealthTemperatureDeviation 3;
:global CheckHealthVoltageLow 115;
@@ -112,6 +158,7 @@
# 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";
@@ -125,7 +172,11 @@
"Staking"; "Thundering"; "Ultra"; "Unreal" };
{ "Belief"; "Button"; "Curtain"; "Edge"; "Jewel";
"String"; "Whistle" }
-}
+};
+
+# Specify how to assemble DNS names in ipsec-to-dns.
+:global HostNameInZone true;
+:global PrefixInZone true;
# Run different commands with multiple mode-button presses.
:global ModeButton {
@@ -147,11 +198,18 @@
# 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;
+# This is the base url to fetch scripts from.
: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/";
@@ -160,7 +218,7 @@
#: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";
+#:global ScriptUpdatesUrlSuffix "?h=next";
# Use this for defaults with $ScriptRunOnce
# Install module with:
@@ -182,16 +240,21 @@
:global CertRenewPass {
"v3ry-s3cr3t";
"4n0th3r-s3cr3t";
-}
+};
+:global CertWarnTime 2w;
:global CertIssuedExportPass {
"cert1-cn"="v3ry-s3cr3t";
"cert2-cn"="4n0th3r-s3cr3t";
-}
+};
-# load custom settings from overlay
+# load custom settings from overlay and snippets
# Warning: Do *NOT* copy this code to overlay!
-:do {
- /system/script/run global-config-overlay;
-} on-error={
- :log error ("Loading configuration from overlay failed!");
+:foreach Script in=([ /system/script/find where name="global-config-overlay" ], \
+ [ /system/script/find where name~"^global-config-overlay.d/" ]) do={
+ :do {
+ /system/script/run $Script;
+ } on-error={
+ :log error ("Loading configuration from overlay or snippet " . \
+ [ /system/script/get $Script name ] . " failed!");
+ }
}
diff --git a/global-functions b/global-functions.rsc
index f4c47d7..1e98019 100644
--- a/global-functions
+++ b/global-functions.rsc
@@ -1,49 +1,65 @@
#!rsc by RouterOS
# RouterOS script: global-functions
-# Copyright (c) 2013-2022 Christian Hesse <mail@eworm.de>
+# Copyright (c) 2013-2024 Christian Hesse <mail@eworm.de>
# Michael Gisbers <michael@gisbers.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
+# requires RouterOS, version=7.12
+#
# global functions
# https://git.eworm.de/cgit/routeros-scripts/about/
-:local 0 "global-functions";
+:local ScriptName [ :jobname ];
# expected configuration version
-:global ExpectedConfigVersion 85;
+:global ExpectedConfigVersion 124;
# global variables not to be changed by user
:global GlobalFunctionsReady false;
:global Identity [ /system/identity/get name ];
# global functions
+:global AlignRight;
:global CertificateAvailable;
:global CertificateDownload;
:global CertificateNameByCN;
+:global CharacterMultiply;
:global CharacterReplace;
:global CleanFilePath;
+:global CleanName;
:global DeviceInfo;
+:global Dos2Unix;
:global DownloadPackage;
:global EitherOr;
:global EscapeForRegEx;
+:global FetchUserAgentStr;
+:global FormatLine;
+:global FormatMultiLines;
:global GetMacVendor;
:global GetRandom20CharAlNum;
:global GetRandom20CharHex;
:global GetRandomNumber;
+:global Grep;
:global HexToNum;
+:global HumanReadableNum;
:global IfThenElse;
:global IsDefaultRouteReachable;
:global IsDNSResolving;
:global IsFullyConnected;
+:global IsMacLocallyAdministered;
:global IsTimeSync;
+:global LogPrint;
:global LogPrintExit2;
+:global LogPrintOnce;
+:global MAX;
+:global MIN;
:global MkDir;
:global NotificationFunctions;
+:global ParseDate;
+:global ParseJson;
:global ParseKeyValueStore;
:global PrettyPrint;
-:global QuotedPrintable;
:global RandomDelay;
-:global Read;
:global RequiredRouterOS;
:global ScriptFromTerminal;
:global ScriptInstallUpdate;
@@ -52,6 +68,7 @@
:global SendNotification2;
:global SymbolByUnicodeName;
:global SymbolForNotification;
+:global Unix2Dos;
:global UrlEncode;
:global ValidateSyntax;
:global VersionToNum;
@@ -61,23 +78,37 @@
:global WaitFullyConnected;
:global WaitTimeSync;
+# align string to the right
+:set AlignRight do={
+ :local Input [ :tostr $1 ];
+ :local Len [ :tonum $2 ];
+
+ :global CharacterMultiply;
+ :global EitherOr;
+
+ :set Len [ $EitherOr $Len 8 ];
+ :local Spaces [ $CharacterMultiply " " $Len ];
+
+ :return ([ :pick $Spaces 0 ($Len - [ :len $Input ]) ] . $Input);
+}
+
# check and download required certificate
:set CertificateAvailable do={
:local CommonName [ :tostr $1 ];
:global CertificateDownload;
- :global LogPrintExit2;
+ :global LogPrint;
:global ParseKeyValueStore;
:if ([ /system/resource/get free-hdd-space ] < 8388608 && \
[ /certificate/settings/get crl-download ] = true && \
[ /certificate/settings/get crl-store ] = "system") do={
- $LogPrintExit2 warning $0 ("This system has low free flash space but " . \
- "is configured to download certificate CRLs to system!") false;
+ $LogPrint warning $0 ("This system has low free flash space but " . \
+ "is configured to download certificate CRLs to system!");
}
:if ([ :len [ /certificate/find where common-name=$CommonName ] ] = 0) do={
- $LogPrintExit2 info $0 ("Certificate with CommonName \"" . $CommonName . "\" not available.") false;
+ $LogPrint info $0 ("Certificate with CommonName \"" . $CommonName . "\" not available.");
:if ([ $CertificateDownload $CommonName ] = false) do={
:return false;
}
@@ -86,8 +117,8 @@
:local CertVal [ /certificate/get [ find where common-name=$CommonName ] ];
:while (($CertVal->"akid") != "" && ($CertVal->"akid") != ($CertVal->"skid")) do={
:if ([ :len [ /certificate/find where skid=($CertVal->"akid") ] ] = 0) do={
- $LogPrintExit2 info $0 ("Certificate chain for \"" . $CommonName . \
- "\" is incomplete, missing \"" . ([ $ParseKeyValueStore ($CertVal->"issuer") ]->"CN") . "\".") false;
+ $LogPrint info $0 ("Certificate chain for \"" . $CommonName . \
+ "\" is incomplete, missing \"" . ([ $ParseKeyValueStore ($CertVal->"issuer") ]->"CN") . "\".");
:if ([ $CertificateDownload $CommonName ] = false) do={
:return false;
}
@@ -105,29 +136,28 @@
:global ScriptUpdatesUrlSuffix;
:global CertificateNameByCN;
- :global LogPrintExit2;
- :global UrlEncode;
+ :global CleanName;
+ :global FetchUserAgentStr;
+ :global LogPrint;
:global WaitForFile;
- $LogPrintExit2 info $0 ("Downloading and importing certificate with " . \
- "CommonName \"" . $CommonName . "\".") false;
+ $LogPrint info $0 ("Downloading and importing certificate with " . \
+ "CommonName \"" . $CommonName . "\".");
:do {
- :local LocalFileName ($CommonName . ".pem");
- :local UrlFileName ([ $UrlEncode $CommonName ] . ".pem");
- /tool/fetch check-certificate=yes-without-crl \
- ($ScriptUpdatesBaseUrl . "certs/" . \
- $UrlFileName . $ScriptUpdatesUrlSuffix) \
- dst-path=$LocalFileName as-value;
- $WaitForFile $LocalFileName;
- /certificate/import file-name=$LocalFileName passphrase="" as-value;
- /file/remove $LocalFileName;
-
- :foreach Cert in=[ /certificate/find where name~("^" . $LocalFileName . "_[0-9]+\$") ] do={
+ :local FileName ([ $CleanName $CommonName ] . ".pem");
+ /tool/fetch check-certificate=yes-without-crl http-header-field=({ [ $FetchUserAgentStr $0 ] }) \
+ ($ScriptUpdatesBaseUrl . "certs/" . $FileName . $ScriptUpdatesUrlSuffix) \
+ dst-path=$FileName as-value;
+ $WaitForFile $FileName;
+ /certificate/import file-name=$FileName passphrase="" as-value;
+ :delay 1s;
+ /file/remove $FileName;
+
+ :foreach Cert in=[ /certificate/find where name~("^" . $FileName . "_[0-9]+\$") ] do={
$CertificateNameByCN [ /certificate/get $Cert common-name ];
}
} on-error={
- $LogPrintExit2 warning $0 ("Failed importing certificate with " . \
- "CommonName \"" . $CommonName . "\"!") false;
+ $LogPrint warning $0 ("Failed importing certificate with CommonName \"" . $CommonName . "\"!");
:return false;
}
:return true;
@@ -137,11 +167,19 @@
:set CertificateNameByCN do={
:local CommonName [ :tostr $1 ];
- :global CharacterReplace;
+ :global CleanName;
:local Cert [ /certificate/find where common-name=$CommonName ];
- /certificate/set $Cert \
- name=[ $CharacterReplace [ $CharacterReplace [ $CharacterReplace $CommonName "'" "-" ] " " "-" ] "---" "-" ];
+ /certificate/set $Cert name=[ $CleanName $CommonName ];
+}
+
+# multiply given character(s)
+:set CharacterMultiply do={
+ :local Return "";
+ :for I from=1 to=$2 do={
+ :set Return ($Return . $1);
+ }
+ :return $Return;
}
# character replace
@@ -183,43 +221,76 @@
:return $Path;
}
+# clean name for DNS, file and more
+:set CleanName do={
+ :local Input [ :tostr $1 ];
+
+ :local Return "";
+
+ :for I from=0 to=([ :len $Input ] - 1) do={
+ :local Char [ :pick $Input $I ];
+ :if ([ :typeof [ find "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-" $Char ] ] = "nil") do={
+ :set Char "-";
+ }
+ :if ($Char != "-" || [ :pick $Return ([ :len $Return ] - 1) ] != "-") do={
+ :set Return ($Return . $Char);
+ }
+ }
+ :return $Return;
+}
+
# get readable device info
:set DeviceInfo do={
:global ExpectedConfigVersion;
:global Identity;
:global IfThenElse;
+ :global FormatLine;
+ :local License [ /system/license/get ];
:local Resource [ /system/resource/get ];
:local RouterBoard;
:do {
:set RouterBoard [[ :parse "/system/routerboard/get" ]];
} on-error={ }
- :local License [ /system/license/get ];
+ :local Snmp [ /snmp/get ];
:local Update [ /system/package/update/get ];
:return ( \
- "Hostname: " . $Identity . \
- "\nBoard name: " . $Resource->"board-name" . \
- "\nArchitecture: " . $Resource->"architecture-name" . \
+ [ $FormatLine "Hostname" $Identity ] . "\n" . \
+ [ $IfThenElse ([ :len ($Snmp->"location") ] > 0) \
+ ([ $FormatLine "Location" ($Snmp->"location") ] . "\n") ] . \
+ [ $IfThenElse ([ :len ($Snmp->"contact") ] > 0) \
+ ([ $FormatLine "Contact" ($Snmp->"contact") ] . "\n") ] . \
+ [ $FormatLine "Board name" ($Resource->"board-name") ] . "\n" . \
+ [ $FormatLine "Architecture" ($Resource->"architecture-name") ] . "\n" . \
[ $IfThenElse ($RouterBoard->"routerboard" = true) \
- ("\nModel: " . $RouterBoard->"model" . \
- [ $IfThenElse ([ :len ($RouterBoard->"revision") ] > 0) \
- (" " . $RouterBoard->"revision") ] . \
- "\nSerial number: " . $RouterBoard->"serial-number") ] . \
+ ([ $FormatLine "Model" ($RouterBoard->"model") ] . \
+ [ $IfThenElse ([ :len ($RouterBoard->"revision") ] > 0) \
+ (" " . $RouterBoard->"revision") ] . "\n" . \
+ [ $FormatLine "Serial number" ($RouterBoard->"serial-number") ] . "\n") ] . \
[ $IfThenElse ([ :len ($License->"level") ] > 0) \
- ("\nLicense: " . $License->"level") ] . \
- "\nRouterOS:" . \
- "\n Channel: " . $Update->"channel" . \
- "\n Installed: " . $Update->"installed-version" . \
+ ([ $FormatLine "License" ($License->"level") ] . "\n") ] . \
+ "RouterOS:\n" . \
+ [ $FormatLine " Channel" ($Update->"channel") ] . "\n" . \
+ [ $FormatLine " Installed" ($Update->"installed-version") ] . "\n" . \
[ $IfThenElse ([ :typeof ($Update->"latest-version") ] != "nothing" && \
$Update->"installed-version" != $Update->"latest-version") \
- ("\n Available: " . $Update->"latest-version") ] . \
+ ([ $FormatLine " Available" ($Update->"latest-version") ] . "\n") ] . \
[ $IfThenElse ($RouterBoard->"routerboard" = true && \
$RouterBoard->"current-firmware" != $RouterBoard->"upgrade-firmware") \
- ("\n Firmware: " . $RouterBoard->"current-firmware") ] . \
- "\nRouterOS-Scripts:" . \
- "\n Version: " . $ExpectedConfigVersion);
+ ([ $FormatLine " Firmware" ($RouterBoard->"current-firmware") ] . "\n") ] . \
+ "RouterOS-Scripts:\n" . \
+ [ $FormatLine " Version" $ExpectedConfigVersion ]);
+}
+
+# convert line endings, DOS -> UNIX
+:set Dos2Unix do={
+ :local Input [ :tostr $1 ];
+
+ :global CharacterReplace;
+
+ :return [ $CharacterReplace $Input ("\r\n") ("\n") ];
}
# download package from upgrade server
@@ -231,7 +302,7 @@
:global CertificateAvailable;
:global CleanFilePath;
- :global LogPrintExit2;
+ :global LogPrint;
:global MkDir;
:global WaitForFile;
@@ -246,22 +317,23 @@
:local PkgDest [ $CleanFilePath ($PkgDir . "/" . $PkgFile) ];
:if ([ $MkDir $PkgDir ] = false) do={
- $LogPrintExit2 warning $0 ("Failed creating directory, not downloading package.") false;
+ $LogPrint warning $0 ("Failed creating directory, not downloading package.");
:return false;
}
:if ([ :len [ /file/find where name=$PkgDest type="package" ] ] > 0) do={
- $LogPrintExit2 info $0 ("Package file " . $PkgName . " already exists.") false;
+ $LogPrint info $0 ("Package file " . $PkgName . " already exists.");
:return true;
}
:if ([ $CertificateAvailable "R3" ] = false) do={
- $LogPrintExit2 error $0 ("Downloading required certificate failed.") true;
+ $LogPrint error $0 ("Downloading required certificate failed.");
+ :return false;
}
:local Url ("https://upgrade.mikrotik.com/routeros/" . $PkgVer . "/" . $PkgFile);
- $LogPrintExit2 info $0 ("Downloading package file '" . $PkgName . "'...") false;
- $LogPrintExit2 debug $0 ("... from url: " . $Url) false;
+ $LogPrint info $0 ("Downloading package file '" . $PkgName . "'...");
+ $LogPrint debug $0 ("... from url: " . $Url);
:local Retry 3;
:while ($Retry > 0) do={
:do {
@@ -272,14 +344,14 @@
:return true;
}
} on-error={
- $LogPrintExit2 debug $0 ("Downloading package file failed.") false;
+ $LogPrint debug $0 ("Downloading package file failed.");
}
/file/remove [ find where name=$PkgDest ];
:set Retry ($Retry - 1);
}
- $LogPrintExit2 warning $0 ("Downloading package file '" . $PkgName . "' failed.") false;
+ $LogPrint warning $0 ("Downloading package file '" . $PkgName . "' failed.");
:return false;
}
@@ -290,6 +362,9 @@
:if ([ :typeof $1 ] = "num") do={
:return [ $IfThenElse ($1 != 0) $1 $2 ];
}
+ :if ([ :typeof $1 ] = "time") do={
+ :return [ $IfThenElse ($1 > 0s) $1 $2 ];
+ }
:return [ $IfThenElse ([ :len [ :tostr $1 ] ] > 0) $1 $2 ];
}
@@ -302,7 +377,7 @@
}
:local Return "";
- :local Chars ("^.[]\$()|*+\?{}\\");
+ :local Chars ("^.[]\$()|*+?{}\\");
:for I from=0 to=([ :len $Input ] - 1) do={
:local Char [ :pick $Input $I ];
@@ -315,20 +390,73 @@
:return $Return;
}
+# generate user agent string for fetch
+:set FetchUserAgentStr do={
+ :local Caller [ :tostr $1 ];
+
+ :local Resource [ /system/resource/get ];
+
+ :return ("User-Agent: Mikrotik/" . $Resource->"version" . " " . \
+ $Resource->"architecture-name" . " " . $Caller . "/Fetch (https://rsc.eworm.de/)");
+}
+
+# format a line for output
+:set FormatLine do={
+ :local Key [ :tostr $1 ];
+ :local Value [ :tostr $2 ];
+ :local Indent [ :tonum $3 ];
+ :local Spaces;
+ :local Return "";
+
+ :global CharacterMultiply;
+ :global EitherOr;
+
+ :set Indent [ $EitherOr $Indent 16 ];
+ :local Spaces [ $CharacterMultiply " " $Indent ];
+
+ :if ([ :len $Key ] > 0) do={ :set Return ($Key . ":"); }
+ :if ([ :len $Key ] > ($Indent - 2)) do={
+ :set Return ($Return . "\n" . [ :pick $Spaces 0 $Indent ] . $Value);
+ } else={
+ :set Return ($Return . [ :pick $Spaces 0 ($Indent - [ :len $Return ]) ] . $Value);
+ }
+
+ :return $Return;
+}
+
+# format multiple lines for output
+:set FormatMultiLines do={
+ :local Key [ :tostr $1 ];
+ :local Values [ :toarray $2 ];
+ :local Indent [ :tonum $3 ];
+ :local Return;
+
+ :global FormatLine;
+
+ :set Return [ $FormatLine $Key ($Values->0) $Indent ];
+ :foreach Value in=[ :pick $Values 1 [ :len $Values ] ] do={
+ :set Return ($Return . "\n" . [ $FormatLine "" $Value $Indent ]);
+ }
+
+ :return $Return;
+}
+
# get MAC vendor
:set GetMacVendor do={
:local Mac [ :tostr $1 ];
:global CertificateAvailable;
- :global LogPrintExit2;
+ :global IsMacLocallyAdministered;
+ :global LogPrint;
- :if ([ :tonum ("0x" . [ :pick $Mac 0 [ :find $Mac ":" ] ]) ] & 2 = 2) do={
+ :if ([ $IsMacLocallyAdministered $Mac ] = true) do={
:return "locally administered";
}
:do {
- :if ([ $CertificateAvailable "R3" ] = false) do={
- $LogPrintExit2 warning $0 ("Downloading required certificate failed.") true;
+ :if ([ $CertificateAvailable "GTS CA 1P5" ] = false) do={
+ $LogPrint warning $0 ("Downloading required certificate failed.");
+ :error false;
}
:local Vendor ([ /tool/fetch check-certificate=yes-without-crl \
("https://api.macvendors.com/" . [ :pick $Mac 0 8 ]) output=user as-value ]->"data");
@@ -337,9 +465,9 @@
:do {
/tool/fetch check-certificate=yes-without-crl ("https://api.macvendors.com/") \
output=none as-value;
- $LogPrintExit2 debug $0 ("The mac vendor is not known in database.") false;
+ $LogPrint debug $0 ("The mac vendor is not known in database.");
} on-error={
- $LogPrintExit2 warning $0 ("Failed getting mac vendor.") false;
+ $LogPrint warning $0 ("Failed getting mac vendor.");
}
:return "unknown vendor";
}
@@ -366,19 +494,67 @@
:return [ :rndnum from=0 to=[ $EitherOr [ :tonum $1 ] 4294967295 ] ];
}
+# return first line that matches a pattern
+:set Grep do={
+ :local Input ([ :tostr $1 ] . "\n");
+ :local Pattern [ :tostr $2 ];
+
+ :if ([ :typeof [ :find $Input $Pattern ] ] = "nil") do={
+ :return [];
+ }
+
+ :do {
+ :local Line [ :pick $Input 0 [ :find $Input "\n" ] ];
+ :if ([ :typeof [ :find $Line $Pattern ] ] = "num") do={
+ :return $Line;
+ }
+ :set Input [ :pick $Input ([ :find $Input "\n" ] + 1) [ :len $Input ] ];
+ } while=([ :len $Input ] > 0);
+
+ :return [];
+}
+
# convert from hex (string) to num
:set HexToNum do={
:local Input [ :tostr $1 ];
- :local Hex "0123456789abcdef0123456789ABCDEF";
- :local Multi 1;
- :local Return 0;
- :for I from=([ :len $Input ] - 1) to=0 do={
- :set Return ($Return + (([ :find $Hex [ :pick $Input $I ] ] % 16) * $Multi));
- :set Multi ($Multi * 16);
+ :global HexToNum;
+
+ :if ([ :pick $Input 0 ] = "*") do={
+ :return [ $HexToNum [ :pick $Input 1 [ :len $Input ] ] ];
}
- :return $Return;
+ :return [ :tonum ("0x" . $Input) ];
+}
+
+# return human readable number
+:set HumanReadableNum do={
+ :local Input [ :tonum $1 ];
+ :local Base [ :tonum $2 ];
+
+ :global EitherOr;
+
+ :local Prefix "kMGTPE";
+ :local Pow 1;
+
+ :set Base [ $EitherOr $Base 1024 ];
+
+ :if ($Input < $Base) do={
+ :return $Input;
+ }
+
+ :for I from=0 to=[ :len $Prefix ] do={
+ :set Pow ($Pow * $Base);
+ :if ($Input / $Base < $Pow) do={
+ :set Prefix [ :pick $Prefix $I ];
+ :local Tmp1 ($Input * 100 / $Pow);
+ :local Tmp2 ($Tmp1 / 100);
+ :if ($Tmp2 >= 100) do={
+ :return ($Tmp2 . $Prefix);
+ }
+ :return ($Tmp2 . "." . [ :pick $Tmp1 [ :len $Tmp2 ] ([ :len $Tmp1 ] - [ :len $Tmp2 ] + 1) ] . $Prefix);
+ }
+ }
}
# mimic conditional/ternary operator (condition ? consequent : alternative)
@@ -399,8 +575,6 @@
# check if DNS is resolving
:set IsDNSResolving do={
- :global CharacterReplace;
-
:do {
:resolve "low-ttl.eworm.de";
} on-error={
@@ -427,11 +601,20 @@
:return true;
}
+# check if mac address is locally administered
+:set IsMacLocallyAdministered do={
+ :if ([ :tonum ("0x" . [ :pick $1 0 [ :find $1 ":" ] ]) ] & 2 = 2) do={
+ :return true;
+ }
+ :return false;
+}
+
# check if system time is sync
:set IsTimeSync do={
:global IsTimeSyncCached;
+ :global IsTimeSyncResetNtp;
- :global LogPrintExit2;
+ :global LogPrint;
:if ($IsTimeSyncCached = true) do={
:return true;
@@ -442,9 +625,28 @@
:set IsTimeSyncCached true;
:return true;
}
+
+ :if ([ :typeof $IsTimeSyncResetNtp ] = "nothing") do={
+ :set IsTimeSyncResetNtp 0s;
+ }
+ :local Uptime [ /system/resource/get uptime ];
+ :if ($Uptime - $IsTimeSyncResetNtp < 3m) do={
+ :return false;
+ }
+
+ :set IsTimeSyncResetNtp $Uptime;
+ /system/ntp/client/set enabled=no;
+ :delay 20ms;
+ /system/ntp/client/set enabled=yes;
:return false;
}
+ :if ([ /system/license/get ]->"level" = "free" || \
+ [ /system/resource/get ]->"board-name" = "x86") do={
+ $LogPrint debug $0 ("No ntp client configured, relying on RTC for CHR free license and x86.");
+ :return true;
+ }
+
:if ([ /ip/cloud/get update-time ] = true) do={
:if ([ :typeof [ /ip/cloud/get public-address ] ] = "ip") do={
:set IsTimeSyncCached true;
@@ -453,16 +655,15 @@
:return false;
}
- $LogPrintExit2 debug $0 ("No time source configured! Returning gracefully...") false;
+ $LogPrint debug $0 ("No time source configured! Returning gracefully...");
:return true;
}
-# log and print with same text, optionally exit
-:set LogPrintExit2 do={
+# log and print with same text
+:set LogPrint do={
:local Severity [ :tostr $1 ];
:local Name [ :tostr $2 ];
:local Message [ :tostr $3 ];
- :local Exit [ :tostr $4 ];
:global PrintDebug;
:global PrintDebugOverride;
@@ -493,44 +694,126 @@
}
:if ($Severity != "debug" || $Debug = true) do={
- :if ($Exit = "true") do={
- :error ([ $PrintSeverity $Severity ] . ": " . $Message);
- } else={
- :put ([ $PrintSeverity $Severity ] . ": " . $Message);
- }
+ :put ([ $PrintSeverity $Severity ] . ": " . $Message);
}
}
+# log and print with same text, optionally exit
+# Deprectated! - TODO: remove later
+:set LogPrintExit2 do={
+ :local Severity [ :tostr $1 ];
+ :local Name [ :tostr $2 ];
+ :local Message [ :tostr $3 ];
+ :local Exit [ :tostr $4 ];
+
+ :global LogPrint;
+ :global LogPrintOnce;
+
+ $LogPrintOnce warning $0 \
+ ("This function is deprecated and will be removed. Please make your adjustments!");
+
+ $LogPrint $1 $2 $3;
+
+ :if ($Exit = "true") do={
+ :error ("Hard error to exit.");
+ }
+}
+
+# log and print, once until reboot
+:set LogPrintOnce do={
+ :local Severity [ :tostr $1 ];
+ :local Name [ :tostr $2 ];
+ :local Message [ :tostr $3 ];
+
+ :global LogPrint;
+
+ :global LogPrintOnceMessages;
+
+ :if ([ :typeof $LogPrintOnceMessages ] = "nothing") do={
+ :set LogPrintOnceMessages ({});
+ }
+
+ :if ($LogPrintOnceMessages->$Message = 1) do={
+ :return false;
+ }
+
+ :if ([ :len [ /log/find where message=($Name . ": " . $Message) ] ] > 0) do={
+ $LogPrint warning $0 \
+ ("The message is already in log, scripting subsystem may have crashed before!");
+ }
+
+ :set ($LogPrintOnceMessages->$Message) 1;
+ $LogPrint $Severity $Name $Message;
+ :return true;
+}
+
+# get max value
+:set MAX do={
+ :if ($1 > $2) do={ :return $1; }
+ :return $2;
+}
+
+# get min value
+:set MIN do={
+ :if ($1 < $2) do={ :return $1; }
+ :return $2;
+}
+
# create directory
:set MkDir do={
- :local Dir [ :tostr $1 ];
+ :local Path [ :tostr $1 ];
:global CleanFilePath;
- :global GetRandom20CharHex;
- :global LogPrintExit2;
+ :global LogPrint;
:global WaitForFile;
- :set Dir [ $CleanFilePath $Dir ];
+ :local MkTmpfs do={
+ :global LogPrint;
+ :global WaitForFile;
- :if ($Dir = "") do={
+ :if ([ :len [ /disk/find where slot=tmpfs type=tmpfs ] ] = 1) do={
+ :return true;
+ }
+
+ $LogPrint info $0 ("Creating disk of type tmpfs.");
+ /file/remove [ find where name="tmpfs" type="directory" ];
+ :do {
+ /disk/add slot=tmpfs type=tmpfs tmpfs-max-size=([ /system/resource/get total-memory ] / 3);
+ $WaitForFile "tmpfs";
+ } on-error={
+ $LogPrint warning $0 ("Creating disk of type tmpfs failed!");
+ :return false;
+ }
:return true;
}
- :if ([ :len [ /file/find where name=$Dir type="directory" ] ] = 1) do={
+ :set Path [ $CleanFilePath $Path ];
+
+ :if ($Path = "") do={
:return true;
}
- :local Return true;
- :local Name ($Dir . "-" . [ $GetRandom20CharHex ]);
+ :if ([ :len [ /file/find where name=$Path type="directory" ] ] = 1) do={
+ :return true;
+ }
+
+ :if ([ :pick $Path 0 5 ] = "tmpfs") do={
+ :if ([ $MkTmpfs ] = false) do={
+ :return false;
+ }
+ }
+
:do {
- /ip/smb/share/add disabled=yes directory=$Dir name=$Name;
- $WaitForFile $Dir;
+ :local File ($Path . "/file");
+ /file/add name=$File;
+ $WaitForFile $File;
+ /file/remove $File;
} on-error={
- $LogPrintExit2 warning $0 ("Making directory '" . $Dir . "' failed!") false;
- :set Return false;
+ $LogPrint warning $0 ("Making directory '" . $Path . "' failed!");
+ :return false;
}
- /ip/smb/share/remove [ find where name=$Name ];
- :return $Return;
+
+ :return true;
}
# prepare NotificationFunctions array
@@ -538,6 +821,75 @@
:set NotificationFunctions ({});
}
+# parse the date and return a named array
+:set ParseDate do={
+ :local Date [ :tostr $1 ];
+
+ :return ({ "year"=[ :tonum [ :pick $Date 0 4 ] ];
+ "month"=[ :tonum [ :pick $Date 5 7 ] ];
+ "day"=[ :tonum [ :pick $Date 8 10 ] ] });
+}
+
+# parse JSON into array
+# Warning: This is not a complete parser!
+:set ParseJson do={
+ :local Input [ :tostr $1 ];
+
+ :local InLen;
+ :local Return ({});
+ :local Skip 0;
+
+ :if ([ :pick $Input 0 ] = "{") do={
+ :set Input [ :pick $Input 1 ([ :len $Input ] - 1) ];
+ }
+ :set Input [ :toarray $Input ];
+ :set InLen [ :len $Input ];
+
+ :for I from=0 to=$InLen do={
+ :if ($Skip > 0 || $Input->$I = "\n" || $Input->$I = "\r\n") do={
+ :if ($Skip > 0) do={
+ :set $Skip ($Skip - 1);
+ }
+ } else={
+ :local Done false;
+ :local Key ($Input->$I);
+ :local Val1 ($Input->($I + 1));
+ :local Val2 ($Input->($I + 2));
+ :if ($Val1 = ":") do={
+ :set Skip 2;
+ :set ($Return->$Key) $Val2;
+ :set Done true;
+ }
+ :if ($Done = false && $Val1 = ":[") do={
+ :local Last false;
+ :set Skip 1;
+ :set ($Return->$Key) ({});
+ :do {
+ :set Skip ($Skip + 1);
+ :local ValX ($Input->($I + $Skip));
+ :if ([ :pick $ValX ([ :len $ValX ] - 1) ] = "]") do={
+ :set Last true;
+ :set ValX [ :pick $ValX 0 ([ :len $ValX ] - 1) ];
+ }
+ :set ($Return->$Key) (($Return->$Key), $ValX);
+ } while=($Last = false && $I + $Skip < $InLen);
+ :set Done true;
+ }
+ :if ($Done = false && $Val1 = ":[]") do={
+ :set Skip 1;
+ :set ($Return->$Key) ({});
+ :set Done true;
+ }
+ :if ($Done = false) do={
+ :set Skip 1;
+ :set ($Return->$Key) [ :pick $Val1 1 [ :len $Val1 ] ];
+ }
+ }
+ }
+
+ :return $Return;
+}
+
# parse key value store
:set ParseKeyValueStore do={
:local Source $1;
@@ -560,58 +912,25 @@
:set PrettyPrint do={
:local Input [ :tostr $1 ];
- :global CharacterReplace;
-
- :put [ $CharacterReplace $Input ("\n") ("\n\r") ];
-}
-
-# 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);
- }
+ :global Unix2Dos;
- :if ($Input = $Return) do={
- :return $Input;
- }
-
- :return ("=\?utf-8\?Q\?" . $Return . "\?=");
+ :put [ $Unix2Dos $Input ];
}
# delay a random amount of seconds
:set RandomDelay do={
+ :local Time [ :tonum $1 ];
+ :local Unit [ :tostr $2 ];
+
:global EitherOr;
:global GetRandomNumber;
+ :global MAX;
- :delay ([ $GetRandomNumber $1 ] . [ $EitherOr $2 "s" ]);
-}
+ :if ($Time = 0) do={
+ :return false;
+ }
-# read input from user
-:set Read do={
- :return;
+ :delay ([ $MAX 10 [ $GetRandomNumber ([ :tonsec [ :totime ($Time . [ $EitherOr $Unit "s" ]) ] ] / 1000000) ] ] . "ms");
}
# check for required RouterOS version
@@ -621,12 +940,18 @@
:local Warn [ :tostr $3 ];
:global IfThenElse;
- :global LogPrintExit2;
+ :global LogPrint;
:global VersionToNum;
+
+ :if (!($Required ~ "^\\d+\\.\\d+((alpha|beta|rc|\\.)\\d+|)\$")) do={
+ $LogPrint error $0 ("No valid RouterOS version: " . $Required);
+ :return false;
+ }
+
:if ([ $VersionToNum $Required ] > [ $VersionToNum [ /system/package/update/get installed-version ] ]) do={
:if ($Warn = "true") do={
- $LogPrintExit2 warning $0 ("This " . [ $IfThenElse ([ :pick $Caller 0 ] = ("\$")) "function" "script" ] . \
- " '" . $Caller . "' (at least specific functionality) requires RouterOS " . $Required . ". Please update!") false;
+ $LogPrint warning $0 ("This " . [ $IfThenElse ([ :pick $Caller 0 ] = ("\$")) "function" "script" ] . \
+ " '" . $Caller . "' (at least specific functionality) requires RouterOS " . $Required . ". Please update!");
}
:return false;
}
@@ -637,7 +962,7 @@
:set ScriptFromTerminal do={
:local Script [ :tostr $1 ];
- :global LogPrintExit2;
+ :global LogPrint;
:foreach Job in=[ /system/script/job/find where script=$Script ] do={
:set Job [ /system/script/job/get $Job ];
@@ -645,11 +970,11 @@
:set Job [ /system/script/job/get [ find where .id=($Job->"parent") ] ];
}
:if (($Job->"type") = "login") do={
- $LogPrintExit2 debug $0 ("Script " . $Script . " started from terminal.") false;
+ $LogPrint debug $0 ("Script " . $Script . " started from terminal.");
:return true;
}
}
- $LogPrintExit2 debug $0 ("Script " . $Script . " NOT started from terminal.") false;
+ $LogPrint debug $0 ("Script " . $Script . " NOT started from terminal.");
:return false;
}
@@ -663,30 +988,29 @@
:global Identity;
:global IDonate;
:global NoNewsAndChangesNotification;
- :global NotificationsWithSymbols;
:global ScriptUpdatesBaseUrl;
- :global ScriptUpdatesFetch;
:global ScriptUpdatesUrlSuffix;
:global CertificateAvailable;
+ :global EitherOr;
+ :global FetchUserAgentStr;
+ :global Grep;
:global IfThenElse;
- :global LogPrintExit2;
+ :global LogPrint;
+ :global LogPrintOnce;
:global ParseKeyValueStore;
+ :global RequiredRouterOS;
:global SendNotification2;
:global SymbolForNotification;
:global ValidateSyntax;
- :if ([ $CertificateAvailable "R3" ] = false) do={
- $LogPrintExit2 warning $0 ("Downloading certificate failed, trying without.") false;
- }
-
:if ([ $CertificateAvailable "E1" ] = false) do={
- $LogPrintExit2 warning $0 ("Downloading certificate failed, trying without.") false;
+ $LogPrint warning $0 ("Downloading certificate failed, trying without.");
}
:foreach Script in=$Scripts do={
:if ([ :len [ /system/script/find where name=$Script ] ] = 0) do={
- $LogPrintExit2 info $0 ("Adding new script: " . $Script) false;
+ $LogPrint info $0 ("Adding new script: " . $Script);
/system/script/add name=$Script owner=$Script source="#!rsc by RouterOS\n" comment=$NewComment;
}
}
@@ -695,46 +1019,37 @@
:local ReloadGlobalFunctions false;
:local ReloadGlobalConfig false;
- :foreach Script in=[ /system/script/find where source~"^#!rsc by RouterOS\n" ] do={
+ :foreach Script in=[ /system/script/find where source~"^#!rsc by RouterOS\r?\n" ] do={
:local ScriptVal [ /system/script/get $Script ];
- :local ScriptFile [ /file/find where name=("script-updates/" . $ScriptVal->"name") ];
+ :local ScriptInfo [ $ParseKeyValueStore ($ScriptVal->"comment") ];
:local SourceNew;
- :if ([ :len $ScriptFile ] > 0) do={
- :set SourceNew [ /file/get $ScriptFile content ];
- /file/remove $ScriptFile;
- }
:foreach Scheduler in=[ /system/scheduler/find where on-event~("\\b" . $ScriptVal->"name" . "\\b") ] do={
:local SchedulerVal [ /system/scheduler/get $Scheduler ];
:if ($ScriptVal->"policy" != $SchedulerVal->"policy") do={
- $LogPrintExit2 warning $0 ("Policies differ for script '" . $ScriptVal->"name" . \
- "' and its scheduler '" . $SchedulerVal->"name" . "'!") false;
+ $LogPrint warning $0 ("Policies differ for script '" . $ScriptVal->"name" . \
+ "' and its scheduler '" . $SchedulerVal->"name" . "'!");
}
}
- :if ([ :len $SourceNew ] = 0 && $ScriptUpdatesFetch = true) do={
- :local Comment [ $ParseKeyValueStore ($ScriptVal->"comment") ];
- :if (!($Comment->"ignore" = true)) do={
- :do {
- :local BaseUrl $ScriptUpdatesBaseUrl;
- :local UrlSuffix $ScriptUpdatesUrlSuffix;
- :if ([ :typeof ($Comment->"base-url") ] = "str") do={ :set BaseUrl ($Comment->"base-url"); }
- :if ([ :typeof ($Comment->"url-suffix") ] = "str") do={ :set UrlSuffix ($Comment->"url-suffix"); }
- :local Url ($BaseUrl . $ScriptVal->"name" . $UrlSuffix);
-
- $LogPrintExit2 debug $0 ("Fetching script '" . $ScriptVal->"name" . "' from url: " . $Url) false;
- :local Result [ /tool/fetch check-certificate=yes-without-crl $Url output=user as-value ];
- :if ($Result->"status" = "finished") do={
- :set SourceNew ($Result->"data");
- }
- } on-error={
- :if ($ScriptVal->"source" = "#!rsc by RouterOS\n") do={
- $LogPrintExit2 warning $0 ("Failed fetching script '" . $ScriptVal->"name" . \
- "', removing dummy. Typo on installation?") false;
- /system/script/remove $Script;
- } else={
- $LogPrintExit2 warning $0 ("Failed fetching script '" . $ScriptVal->"name" . "'!") false;
- }
+ :if (!($ScriptInfo->"ignore" = true)) do={
+ :do {
+ :local BaseUrl [ $EitherOr ($ScriptInfo->"base-url") $ScriptUpdatesBaseUrl ];
+ :local UrlSuffix [ $EitherOr ($ScriptInfo->"url-suffix") $ScriptUpdatesUrlSuffix ];
+ :local Url ($BaseUrl . $ScriptVal->"name" . ".rsc" . $UrlSuffix);
+ $LogPrint debug $0 ("Fetching script '" . $ScriptVal->"name" . "' from url: " . $Url);
+ :local Result [ /tool/fetch check-certificate=yes-without-crl \
+ http-header-field=({ [ $FetchUserAgentStr $0 ] }) $Url output=user as-value ];
+ :if ($Result->"status" = "finished") do={
+ :set SourceNew ($Result->"data");
+ }
+ } on-error={
+ :if ($ScriptVal->"source" = "#!rsc by RouterOS\n") do={
+ $LogPrint warning $0 ("Failed fetching script '" . $ScriptVal->"name" . \
+ "', removing dummy. Typo on installation?");
+ /system/script/remove $Script;
+ } else={
+ $LogPrint warning $0 ("Failed fetching script '" . $ScriptVal->"name" . "'!");
}
}
}
@@ -742,64 +1057,77 @@
:if ([ :len $SourceNew ] > 0) do={
:if ($SourceNew != $ScriptVal->"source") do={
:if ([ :pick $SourceNew 0 18 ] = "#!rsc by RouterOS\n") do={
- :if ([ $ValidateSyntax $SourceNew ] = true) do={
- $LogPrintExit2 info $0 ("Updating script: " . $ScriptVal->"name") false;
- /system/script/set owner=($ScriptVal->"name") source=$SourceNew $Script;
- :if ($ScriptVal->"name" = "global-config") do={
- :set ReloadGlobalConfig true;
- }
- :if ($ScriptVal->"name" = "global-functions" || $ScriptVal->"name" ~ ("^mod/.")) do={
- :set ReloadGlobalFunctions true;
+ :local Required ([ $ParseKeyValueStore [ $Grep $SourceNew ("\23 requires RouterOS, ") ] ]->"version");
+ :if ([ $RequiredRouterOS $0 [ $EitherOr $Required "0.0" ] false ] = true) do={
+ :if ([ $ValidateSyntax $SourceNew ] = true) do={
+ $LogPrint info $0 ("Updating script: " . $ScriptVal->"name");
+ /system/script/set owner=($ScriptVal->"name") source=$SourceNew $Script;
+ :if ($ScriptVal->"name" = "global-config") do={
+ :set ReloadGlobalConfig true;
+ }
+ :if ($ScriptVal->"name" = "global-functions" || $ScriptVal->"name" ~ ("^mod/.")) do={
+ :set ReloadGlobalFunctions true;
+ }
+ } else={
+ $LogPrint warning $0 ("Syntax validation for script '" . $ScriptVal->"name" . \
+ "' failed! Ignoring!");
}
} else={
- $LogPrintExit2 warning $0 ("Syntax validation for script '" . $ScriptVal->"name" . \
- "' failed! Ignoring!") false;
+ $LogPrintOnce warning $0 ("The script '" . $ScriptVal->"name" . "' requires RouterOS " . \
+ $Required . ", which is not met by your installation. Ignoring!");
}
} else={
- $LogPrintExit2 warning $0 ("Looks like new script '" . $ScriptVal->"name" . \
- "' is not valid (missing shebang). Ignoring!") false;
+ $LogPrint warning $0 ("Looks like new script '" . $ScriptVal->"name" . \
+ "' is not valid (missing shebang). Ignoring!");
}
} else={
- $LogPrintExit2 debug $0 ("Script '" . $ScriptVal->"name" . "' did not change.") false;
+ $LogPrint debug $0 ("Script '" . $ScriptVal->"name" . "' did not change.");
}
} else={
- $LogPrintExit2 debug $0 ("No update for script '" . $ScriptVal->"name" . "'.") false;
+ $LogPrint debug $0 ("No update for script '" . $ScriptVal->"name" . "'.");
}
}
:if ($ReloadGlobalFunctions = true) do={
- $LogPrintExit2 info $0 ("Reloading global functions.") false;
+ $LogPrint info $0 ("Reloading global functions.");
:do {
/system/script/run global-functions;
} on-error={
- $LogPrintExit2 error $0 ("Reloading global functions failed!") false;
+ $LogPrint error $0 ("Reloading global functions failed!");
}
}
:if ($ReloadGlobalConfig = true) do={
- $LogPrintExit2 info $0 ("Reloading global configuration.") false;
+ $LogPrint info $0 ("Reloading global configuration.");
:do {
/system/script/run global-config;
} on-error={
- $LogPrintExit2 error $0 ("Reloading global configuration failed!" . \
- " Syntax error or missing overlay\?") false;
+ $LogPrint error $0 ("Reloading global configuration failed!" . \
+ " Syntax error or missing overlay?");
}
}
- :if ($ExpectedConfigVersionBefore != $ExpectedConfigVersion) do={
+ :if ($ExpectedConfigVersionBefore > $ExpectedConfigVersion) do={
+ $LogPrint warning $0 ("The configuration version decreased from " . \
+ $ExpectedConfigVersionBefore . " to " . $ExpectedConfigVersion . \
+ ". Installed an older version?");
+ }
+
+ :if ($ExpectedConfigVersionBefore < $ExpectedConfigVersion) do={
:global GlobalConfigChanges;
:global GlobalConfigMigration;
:local ChangeLogCode;
:do {
- :local Url ($ScriptUpdatesBaseUrl . "global-config.changes" . $ScriptUpdatesUrlSuffix);
- $LogPrintExit2 debug $0 ("Fetching news, changes and migration: " . $Url) false;
- :local Result [ /tool/fetch check-certificate=yes-without-crl $Url output=user as-value ];
+ :local Url ($ScriptUpdatesBaseUrl . "news-and-changes.rsc" . $ScriptUpdatesUrlSuffix);
+ $LogPrint debug $0 ("Fetching news, changes and migration: " . $Url);
+ :local Result [ /tool/fetch check-certificate=yes-without-crl \
+ http-header-field=({ [ $FetchUserAgentStr $0 ] }) $Url output=user as-value ];
:if ($Result->"status" = "finished") do={
:set ChangeLogCode ($Result->"data");
}
} on-error={
- $LogPrintExit2 warning $0 ("Failed fetching news, changes and migration!") false;
+ $LogPrint warning $0 ("Failed fetching news, changes and migration!");
}
:if ([ :len $ChangeLogCode ] > 0) do={
@@ -807,10 +1135,10 @@
:do {
[ :parse $ChangeLogCode ];
} on-error={
- $LogPrintExit2 warning $0 ("The changelog failed to run!") false;
+ $LogPrint warning $0 ("The changelog failed to run!");
}
} else={
- $LogPrintExit2 warning $0 ("The changelog failed syntax validation!") false;
+ $LogPrint warning $0 ("The changelog failed syntax validation!");
}
}
@@ -819,14 +1147,14 @@
:local Migration ($GlobalConfigMigration->[ :tostr $I ]);
:if ([ :typeof $Migration ] = "str") do={
:if ([ $ValidateSyntax $Migration ] = true) do={
- $LogPrintExit2 info $0 ("Applying migration for change " . $I . ": " . $Migration) false;
+ $LogPrint info $0 ("Applying migration for change " . $I . ": " . $Migration);
:do {
[ :parse $Migration ];
} on-error={
- $LogPrintExit2 warning $0 ("Migration code for change " . $I . " failed to run!") false;
+ $LogPrint warning $0 ("Migration code for change " . $I . " failed to run!");
}
} else={
- $LogPrintExit2 warning $0 ("Migration code for change " . $I . " failed syntax validation!") false;
+ $LogPrint warning $0 ("Migration code for change " . $I . " failed syntax validation!");
}
}
}
@@ -835,15 +1163,15 @@
:local NotificationMessage ("The configuration version on " . $Identity . " increased " . \
"to " . $ExpectedConfigVersion . ", current configuration may need modification. " . \
"Please review and update global-config-overlay, then re-run global-config.");
- $LogPrintExit2 info $0 ($NotificationMessage) false;
+ $LogPrint info $0 ($NotificationMessage);
:if ([ :len $GlobalConfigChanges ] > 0) do={
:set NotificationMessage ($NotificationMessage . "\n\nChanges:");
:for I from=($ExpectedConfigVersionBefore + 1) to=$ExpectedConfigVersion do={
:local Change ($GlobalConfigChanges->[ :tostr $I ]);
:set NotificationMessage ($NotificationMessage . "\n " . \
- [ $IfThenElse ($NotificationsWithSymbols = true) ("\E2\97\8F") "*" ] . " " . $Change);
- $LogPrintExit2 info $0 ("Change " . $I . ": " . $Change) false;
+ [ $SymbolForNotification "pushpin" "*" ] . $Change);
+ $LogPrint info $0 ("Change " . $I . ": " . $Change);
}
} else={
:set NotificationMessage ($NotificationMessage . "\n\nNews and changes are not available.");
@@ -857,7 +1185,7 @@
"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.");
- :set Link "https://git.eworm.de/cgit/routeros-scripts/about/#donate";
+ :set Link "https://rsc.eworm.de/#donate";
}
$SendNotification2 ({ origin=$0; \
@@ -873,12 +1201,11 @@
# lock script against multiple invocation
:set ScriptLock do={
:local Script [ :tostr $1 ];
- :local DoReturn $2;
- :local WaitMax ([ :tonum $3 ] * 10);
+ :local WaitMax ([ :tonum $3 ] * 10);
- :global GetRandom20CharHex;
+ :global GetRandom20CharAlNum;
:global IfThenElse;
- :global LogPrintExit2;
+ :global LogPrint;
:global ScriptLockOrder;
:if ([ :typeof $ScriptLockOrder ] = "nothing") do={
@@ -964,20 +1291,22 @@
}
:if ([ :len [ /system/script/find where name=$Script ] ] = 0) do={
- $LogPrintExit2 error $0 ("A script named '" . $Script . "' does not exist!") true;
+ $LogPrint error $0 ("A script named '" . $Script . "' does not exist!");
+ :error false;
}
:if ([ $JobCount $Script ] = 0) do={
- $LogPrintExit2 error $0 ("No script '" . $Script . "' is running!") true;
+ $LogPrint error $0 ("No script '" . $Script . "' is running!");
+ :error false;
}
:if ([ $TicketCount $Script ] >= [ $JobCount $Script ]) do={
- $LogPrintExit2 error $0 ("More tickets than running scripts '" . $Script . "', resetting!") false;
+ $LogPrint error $0 ("More tickets than running scripts '" . $Script . "', resetting!");
:set ($ScriptLockOrder->$Script) ({});
/system/script/job/remove [ find where script=$Script ];
}
- :local MyTicket [ $GetRandom20CharHex ];
+ :local MyTicket [ $GetRandom20CharAlNum 6 ];
$AddTicket $Script $MyTicket;
:local WaitCount 0;
@@ -989,13 +1318,13 @@
:if ([ $IsFirstTicket $Script $MyTicket ] = true && [ $TicketCount $Script ] = [ $JobCount $Script ]) do={
$RemoveTicket $Script $MyTicket;
$CleanupTickets $Script;
- :return false;
+ :return true;
}
$RemoveTicket $Script $MyTicket;
- $LogPrintExit2 info $0 ("Script '" . $Script . "' started more than once" . [ $IfThenElse ($WaitCount > 0) \
- " and timed out waiting for lock" "" ] . "... Aborting.") [ $IfThenElse ($DoReturn = true) false true ];
- :return true;
+ $LogPrint info $0 ("Script '" . $Script . "' started more than once" . [ $IfThenElse ($WaitCount > 0) \
+ " and timed out waiting for lock" "" ] . "...");
+ :return false;
}
# send notification via NotificationFunctions - expects at least two string arguments
@@ -1020,9 +1349,17 @@
# return UTF-8 symbol for unicode name
:set SymbolByUnicodeName do={
+ :local Name [ :tostr $1 ];
+
+ :global LogPrintOnce;
+
:local Symbols {
+ "abacus"="\F0\9F\A7\AE";
"alarm-clock"="\E2\8F\B0";
+ "arrow-down"="\E2\AC\87";
+ "arrow-up"="\E2\AC\86";
"calendar"="\F0\9F\93\85";
+ "card-file-box"="\F0\9F\97\83";
"chart-decreasing"="\F0\9F\93\89";
"chart-increasing"="\F0\9F\93\88";
"cloud"="\E2\98\81";
@@ -1030,30 +1367,45 @@
"earth"="\F0\9F\8C\8D";
"fire"="\F0\9F\94\A5";
"floppy-disk"="\F0\9F\92\BE";
+ "gear"="\E2\9A\99";
+ "heart"="\E2\99\A5";
"high-voltage-sign"="\E2\9A\A1";
"incoming-envelope"="\F0\9F\93\A8";
+ "information"="\E2\84\B9";
+ "large-orange-circle"="\F0\9F\9F\A0";
+ "large-red-circle"="\F0\9F\94\B4";
"link"="\F0\9F\94\97";
"lock-with-ink-pen"="\F0\9F\94\8F";
"memo"="\F0\9F\93\9D";
"mobile-phone"="\F0\9F\93\B1";
"pushpin"="\F0\9F\93\8C";
"scissors"="\E2\9C\82";
+ "smiley-partying-face"="\F0\9F\A5\B3";
+ "smiley-smiling-face"="\E2\98\BA";
+ "smiley-winking-face-with-tongue"="\F0\9F\98\9C";
"sparkles"="\E2\9C\A8";
- "up-arrow"="\E2\AC\86";
+ "speech-balloon"="\F0\9F\92\AC";
+ "star"="\E2\AD\90";
"warning-sign"="\E2\9A\A0";
"white-heavy-check-mark"="\E2\9C\85"
}
- :return ($Symbols->$1);
+ :if ([ :len ($Symbols->$Name) ] = 0) do={
+ $LogPrintOnce warning $0 ("No symbol available for name '" . $Name . "'!");
+ :return "";
+ }
+
+ :return (($Symbols->$Name) . "\EF\B8\8F");
}
# return symbol for notification
:set SymbolForNotification do={
:global NotificationsWithSymbols;
:global SymbolByUnicodeName;
+ :global IfThenElse;
:if ($NotificationsWithSymbols != true) do={
- :return "";
+ :return [ $IfThenElse ([ :len $2 ] > 0) ([ :tostr $2 ] . " ") "" ];
}
:local Return "";
:foreach Symbol in=[ :toarray $1 ] do={
@@ -1062,6 +1414,16 @@
:return ($Return . " ");
}
+# convert line endings, UNIX -> DOS
+:set Unix2Dos do={
+ :local Input [ :tostr $1 ];
+
+ :global CharacterReplace;
+
+ :return [ $CharacterReplace [ $CharacterReplace $Input \
+ ("\n") ("\r\n") ] ("\r\r\n") ("\r\n") ];
+}
+
# url encoding
:set UrlEncode do={
:local Input [ :tostr $1 ];
@@ -1071,7 +1433,7 @@
}
:local Return "";
- :local Chars ("\n\r !\"#\$%&'()*+,:;<=>\?@[\\]^`{|}~");
+ :local Chars ("\n\r !\"#\$%&'()*+,:;<=>?@[\\]^`{|}~");
:local Subs { "%0A"; "%0D"; "%20"; "%21"; "%22"; "%23"; "%24"; "%25"; "%26"; "%27";
"%28"; "%29"; "%2A"; "%2B"; "%2C"; "%3A"; "%3B"; "%3C"; "%3D"; "%3E"; "%3F";
"%40"; "%5B"; "%5C"; "%5D"; "%5E"; "%60"; "%7B"; "%7C"; "%7D"; "%7E" };
@@ -1109,8 +1471,10 @@
:global CharacterReplace;
- :set Input [ $CharacterReplace [ $CharacterReplace [ $CharacterReplace $Input \
- "." "," ] "beta" ",beta," ] "rc" ",rc," ];
+ :set Input [ $CharacterReplace $Input "." "," ];
+ :foreach I in={ "alpha"; "beta"; "rc" } do={
+ :set Input [ $CharacterReplace $Input $I ("," . $I . ",") ];
+ }
:foreach Value in=([ :toarray $Input ], 0) do={
:local Num [ :tonum $Value ];
@@ -1119,7 +1483,8 @@
:set Return ($Return + 0xff00);
:set Multi ($Multi / 0x100);
} else={
- :if ($Value = "beta") do={ :set Return ($Return + 0x3f00); }
+ :if ($Value = "alpha") do={ :set Return ($Return + 0x3f00); }
+ :if ($Value = "beta") do={ :set Return ($Return + 0x5f00); }
:if ($Value = "rc") do={ :set Return ($Return + 0x7f00); }
}
}
@@ -1150,18 +1515,21 @@
# wait for file to be available
:set WaitForFile do={
- :local FileName [ :tostr $1 ];
+ :local FileName [ :tostr $1 ];
+ :local WaitTime [ :totime $2 ];
:global CleanFilePath;
+ :global EitherOr;
:set FileName [ $CleanFilePath $FileName ];
- :local I 0;
+ :local I 1;
+ :local Delay ([ :totime [ $EitherOr $WaitTime 2s ] ] / 20);
:while ([ :len [ /file/find where name=$FileName ] ] = 0) do={
- :if ($I > 20) do={
+ :if ($I >= 20) do={
:return false;
}
- :delay 100ms;
+ :delay $Delay;
:set I ($I + 1);
}
:return true;
@@ -1194,15 +1562,17 @@
:do {
/system/script/run $Script;
} on-error={
- $LogPrintExit2 error $0 ("Module '" . $ScriptVal->"name" . "' failed to run.") false;
+ $LogPrint error $0 ("Module '" . $ScriptVal->"name" . "' failed to run.");
}
} else={
- $LogPrintExit2 error $0 ("Module '" . $ScriptVal->"name" . "' failed syntax validation, skipping.") false;
+ $LogPrint error $0 ("Module '" . $ScriptVal->"name" . "' failed syntax validation, skipping.");
}
}
-# check for required RouterOS version
-$RequiredRouterOS $0 "7.1" true;
+# Log success
+:local Resource [ /system/resource/get ];
+$LogPrintOnce info $ScriptName ("Loaded on " . $Resource->"board-name" . \
+ " with RouterOS " . $Resource->"version" . ".");
# signal we are ready
:set GlobalFunctionsReady true;
diff --git a/global-wait b/global-wait.rsc
index 43cc5cc..f8c767b 100644
--- a/global-wait
+++ b/global-wait.rsc
@@ -1,11 +1,12 @@
#!rsc by RouterOS
# RouterOS script: global-wait
-# Copyright (c) 2020-2022 Christian Hesse <mail@eworm.de>
+# Copyright (c) 2020-2024 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
+# requires RouterOS, version=7.12
+#
# 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; }
diff --git a/gps-track b/gps-track
deleted file mode 100644
index 56a26c5..0000000
--- a/gps-track
+++ /dev/null
@@ -1,34 +0,0 @@
-#!rsc by RouterOS
-# RouterOS script: gps-track
-# Copyright (c) 2018-2022 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;
-}
diff --git a/gps-track.rsc b/gps-track.rsc
new file mode 100644
index 0000000..1aeab84
--- /dev/null
+++ b/gps-track.rsc
@@ -0,0 +1,50 @@
+#!rsc by RouterOS
+# RouterOS script: gps-track
+# Copyright (c) 2018-2024 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# requires RouterOS, version=7.12
+#
+# track gps data by sending json data to http server
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/gps-track.md
+
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:do {
+ :local ScriptName [ :jobname ];
+
+ :global GpsTrackUrl;
+ :global Identity;
+
+ :global LogPrint;
+ :global ScriptLock;
+ :global WaitFullyConnected;
+
+ :if ([ $ScriptLock $ScriptName ] = false) do={
+ :error false;
+ }
+ $WaitFullyConnected;
+
+ :local CoordinateFormat [ /system/gps/get coordinate-format ];
+ :local Gps [ /system/gps/monitor once as-value ];
+
+ :if ($Gps->"valid" = true) do={
+ :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;
+ $LogPrint debug $ScriptName ("Sending GPS data in " . $CoordinateFormat . " format: " . \
+ "lat: " . ($Gps->"latitude") . " " . \
+ "lon: " . ($Gps->"longitude"));
+ } on-error={
+ $LogPrint warning $ScriptName ("Failed sending GPS data!");
+ }
+ } else={
+ $LogPrint debug $ScriptName ("GPS data not valid.");
+ }
+} on-error={ }
diff --git a/hotspot-to-wpa b/hotspot-to-wpa
deleted file mode 100644
index add2893..0000000
--- a/hotspot-to-wpa
+++ /dev/null
@@ -1,72 +0,0 @@
-#!rsc by RouterOS
-# RouterOS script: hotspot-to-wpa
-# Copyright (c) 2019-2022 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;
-}
diff --git a/hotspot-to-wpa-cleanup b/hotspot-to-wpa-cleanup
deleted file mode 100644
index 26610ae..0000000
--- a/hotspot-to-wpa-cleanup
+++ /dev/null
@@ -1,51 +0,0 @@
-#!rsc by RouterOS
-# RouterOS script: hotspot-to-wpa-cleanup
-# Copyright (c) 2021-2022 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;
-}
diff --git a/hotspot-to-wpa-cleanup.capsman.rsc b/hotspot-to-wpa-cleanup.capsman.rsc
new file mode 100644
index 0000000..0540ad5
--- /dev/null
+++ b/hotspot-to-wpa-cleanup.capsman.rsc
@@ -0,0 +1,74 @@
+#!rsc by RouterOS
+# RouterOS script: hotspot-to-wpa-cleanup.capsman
+# Copyright (c) 2021-2024 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# provides: lease-script, order=80
+# requires RouterOS, version=7.12
+#
+# manage and clean up private WPA passphrase after hotspot login
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/hotspot-to-wpa.md
+#
+# !! Do not edit this file, it is generated from template!
+
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:do {
+ :local ScriptName [ :jobname ];
+
+ :global EitherOr;
+ :global LogPrint;
+ :global ParseKeyValueStore;
+ :global ScriptLock;
+
+ :if ([ $ScriptLock $ScriptName 10 ] = false) do={
+ :error false;
+ }
+
+ :local DHCPServers ({});
+ :foreach Server in=[ /ip/dhcp-server/find where comment~"hotspot-to-wpa" ] do={
+ :local ServerVal [ /ip/dhcp-server/get $Server ]
+ :local ServerInfo [ $ParseKeyValueStore ($ServerVal->"comment") ];
+ :if (($ServerInfo->"hotspot-to-wpa") = "wpa") do={
+ :set ($DHCPServers->($ServerVal->"name")) \
+ [ :totime [ $EitherOr ($ServerInfo->"timeout") 4w ] ];
+ }
+ }
+
+ :foreach Client in=[ /caps-man/registration-table/find where comment~"^hotspot-to-wpa:" ] do={
+ :local ClientVal [ /caps-man/registration-table/get $Client ];
+ :foreach Lease in=[ /ip/dhcp-server/lease/find where dynamic \
+ mac-address=($ClientVal->"mac-address") ] do={
+ :if (($DHCPServers->[ /ip/dhcp-server/lease/get $Lease server ]) > 0s) do={
+ $LogPrint info $ScriptName ("Client with mac address " . ($ClientVal->"mac-address") . \
+ " connected to WPA, making lease static.");
+ /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:" \
+ !(comment~[ /system/clock/get date ]) ] do={
+ :local ClientVal [ /caps-man/access-list/get $Client ];
+ :if ([ :len [ /ip/dhcp-server/lease/find where !dynamic comment~"^hotspot-to-wpa:" \
+ mac-address=($ClientVal->"mac-address") ] ] = 0) do={
+ $LogPrint info $ScriptName ("Client with mac address " . ($ClientVal->"mac-address") . \
+ " did not connect to WPA, removing from access list.");
+ /caps-man/access-list/remove $Client;
+ }
+ }
+
+ :foreach Server,Timeout in=$DHCPServers do={
+ :foreach Lease in=[ /ip/dhcp-server/lease/find where !dynamic status="waiting" \
+ server=$Server last-seen>$Timeout comment~"^hotspot-to-wpa:" ] do={
+ :local LeaseVal [ /ip/dhcp-server/lease/get $Lease ];
+ $LogPrint info $ScriptName ("Client with mac address " . ($LeaseVal->"mac-address") . \
+ " was not seen for " . ($LeaseVal->"last-seen") . ", removing.");
+ /caps-man/access-list/remove [ find where comment~"^hotspot-to-wpa:" \
+ mac-address=($LeaseVal->"mac-address") ];
+ /ip/dhcp-server/lease/remove $Lease;
+ }
+ }
+} on-error={ }
diff --git a/hotspot-to-wpa-cleanup.template.rsc b/hotspot-to-wpa-cleanup.template.rsc
new file mode 100644
index 0000000..6f3b3e1
--- /dev/null
+++ b/hotspot-to-wpa-cleanup.template.rsc
@@ -0,0 +1,81 @@
+#!rsc by RouterOS
+# RouterOS script: hotspot-to-wpa-cleanup%TEMPL%
+# Copyright (c) 2021-2024 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# provides: lease-script, order=80
+# requires RouterOS, version=7.12
+#
+# manage and clean up private WPA passphrase after hotspot login
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/hotspot-to-wpa.md
+#
+# !! This is just a template to generate the real script!
+# !! Pattern '%TEMPL%' is replaced, paths are filtered.
+
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:do {
+ :local ScriptName [ :jobname ];
+
+ :global EitherOr;
+ :global LogPrint;
+ :global ParseKeyValueStore;
+ :global ScriptLock;
+
+ :if ([ $ScriptLock $ScriptName 10 ] = false) do={
+ :error false;
+ }
+
+ :local DHCPServers ({});
+ :foreach Server in=[ /ip/dhcp-server/find where comment~"hotspot-to-wpa" ] do={
+ :local ServerVal [ /ip/dhcp-server/get $Server ]
+ :local ServerInfo [ $ParseKeyValueStore ($ServerVal->"comment") ];
+ :if (($ServerInfo->"hotspot-to-wpa") = "wpa") do={
+ :set ($DHCPServers->($ServerVal->"name")) \
+ [ :totime [ $EitherOr ($ServerInfo->"timeout") 4w ] ];
+ }
+ }
+
+ :foreach Client in=[ /caps-man/registration-table/find where comment~"^hotspot-to-wpa:" ] do={
+ :foreach Client in=[ /interface/wifi/registration-table/find where comment~"^hotspot-to-wpa:" ] do={
+ :local ClientVal [ /caps-man/registration-table/get $Client ];
+ :local ClientVal [ /interface/wifi/registration-table/get $Client ];
+ :foreach Lease in=[ /ip/dhcp-server/lease/find where dynamic \
+ mac-address=($ClientVal->"mac-address") ] do={
+ :if (($DHCPServers->[ /ip/dhcp-server/lease/get $Lease server ]) > 0s) do={
+ $LogPrint info $ScriptName ("Client with mac address " . ($ClientVal->"mac-address") . \
+ " connected to WPA, making lease static.");
+ /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:" \
+ :foreach Client in=[ /interface/wifi/access-list/find where comment~"^hotspot-to-wpa:" \
+ !(comment~[ /system/clock/get date ]) ] do={
+ :local ClientVal [ /caps-man/access-list/get $Client ];
+ :local ClientVal [ /interface/wifi/access-list/get $Client ];
+ :if ([ :len [ /ip/dhcp-server/lease/find where !dynamic comment~"^hotspot-to-wpa:" \
+ mac-address=($ClientVal->"mac-address") ] ] = 0) do={
+ $LogPrint info $ScriptName ("Client with mac address " . ($ClientVal->"mac-address") . \
+ " did not connect to WPA, removing from access list.");
+ /caps-man/access-list/remove $Client;
+ /interface/wifi/access-list/remove $Client;
+ }
+ }
+
+ :foreach Server,Timeout in=$DHCPServers do={
+ :foreach Lease in=[ /ip/dhcp-server/lease/find where !dynamic status="waiting" \
+ server=$Server last-seen>$Timeout comment~"^hotspot-to-wpa:" ] do={
+ :local LeaseVal [ /ip/dhcp-server/lease/get $Lease ];
+ $LogPrint info $ScriptName ("Client with mac address " . ($LeaseVal->"mac-address") . \
+ " was not seen for " . ($LeaseVal->"last-seen") . ", removing.");
+ /caps-man/access-list/remove [ find where comment~"^hotspot-to-wpa:" \
+ /interface/wifi/access-list/remove [ find where comment~"^hotspot-to-wpa:" \
+ mac-address=($LeaseVal->"mac-address") ];
+ /ip/dhcp-server/lease/remove $Lease;
+ }
+ }
+} on-error={ }
diff --git a/hotspot-to-wpa-cleanup.wifi.rsc b/hotspot-to-wpa-cleanup.wifi.rsc
new file mode 100644
index 0000000..9c79628
--- /dev/null
+++ b/hotspot-to-wpa-cleanup.wifi.rsc
@@ -0,0 +1,74 @@
+#!rsc by RouterOS
+# RouterOS script: hotspot-to-wpa-cleanup.wifi
+# Copyright (c) 2021-2024 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# provides: lease-script, order=80
+# requires RouterOS, version=7.12
+#
+# manage and clean up private WPA passphrase after hotspot login
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/hotspot-to-wpa.md
+#
+# !! Do not edit this file, it is generated from template!
+
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:do {
+ :local ScriptName [ :jobname ];
+
+ :global EitherOr;
+ :global LogPrint;
+ :global ParseKeyValueStore;
+ :global ScriptLock;
+
+ :if ([ $ScriptLock $ScriptName 10 ] = false) do={
+ :error false;
+ }
+
+ :local DHCPServers ({});
+ :foreach Server in=[ /ip/dhcp-server/find where comment~"hotspot-to-wpa" ] do={
+ :local ServerVal [ /ip/dhcp-server/get $Server ]
+ :local ServerInfo [ $ParseKeyValueStore ($ServerVal->"comment") ];
+ :if (($ServerInfo->"hotspot-to-wpa") = "wpa") do={
+ :set ($DHCPServers->($ServerVal->"name")) \
+ [ :totime [ $EitherOr ($ServerInfo->"timeout") 4w ] ];
+ }
+ }
+
+ :foreach Client in=[ /interface/wifi/registration-table/find where comment~"^hotspot-to-wpa:" ] do={
+ :local ClientVal [ /interface/wifi/registration-table/get $Client ];
+ :foreach Lease in=[ /ip/dhcp-server/lease/find where dynamic \
+ mac-address=($ClientVal->"mac-address") ] do={
+ :if (($DHCPServers->[ /ip/dhcp-server/lease/get $Lease server ]) > 0s) do={
+ $LogPrint info $ScriptName ("Client with mac address " . ($ClientVal->"mac-address") . \
+ " connected to WPA, making lease static.");
+ /ip/dhcp-server/lease/make-static $Lease;
+ /ip/dhcp-server/lease/set comment=($ClientVal->"comment") $Lease;
+ }
+ }
+ }
+
+ :foreach Client in=[ /interface/wifi/access-list/find where comment~"^hotspot-to-wpa:" \
+ !(comment~[ /system/clock/get date ]) ] do={
+ :local ClientVal [ /interface/wifi/access-list/get $Client ];
+ :if ([ :len [ /ip/dhcp-server/lease/find where !dynamic comment~"^hotspot-to-wpa:" \
+ mac-address=($ClientVal->"mac-address") ] ] = 0) do={
+ $LogPrint info $ScriptName ("Client with mac address " . ($ClientVal->"mac-address") . \
+ " did not connect to WPA, removing from access list.");
+ /interface/wifi/access-list/remove $Client;
+ }
+ }
+
+ :foreach Server,Timeout in=$DHCPServers do={
+ :foreach Lease in=[ /ip/dhcp-server/lease/find where !dynamic status="waiting" \
+ server=$Server last-seen>$Timeout comment~"^hotspot-to-wpa:" ] do={
+ :local LeaseVal [ /ip/dhcp-server/lease/get $Lease ];
+ $LogPrint info $ScriptName ("Client with mac address " . ($LeaseVal->"mac-address") . \
+ " was not seen for " . ($LeaseVal->"last-seen") . ", removing.");
+ /interface/wifi/access-list/remove [ find where comment~"^hotspot-to-wpa:" \
+ mac-address=($LeaseVal->"mac-address") ];
+ /ip/dhcp-server/lease/remove $Lease;
+ }
+ }
+} on-error={ }
diff --git a/hotspot-to-wpa.capsman.rsc b/hotspot-to-wpa.capsman.rsc
new file mode 100644
index 0000000..37c8464
--- /dev/null
+++ b/hotspot-to-wpa.capsman.rsc
@@ -0,0 +1,98 @@
+#!rsc by RouterOS
+# RouterOS script: hotspot-to-wpa.capsman
+# Copyright (c) 2019-2024 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# requires RouterOS, version=7.12
+#
+# add private WPA passphrase after hotspot login
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/hotspot-to-wpa.md
+#
+# !! Do not edit this file, it is generated from template!
+
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:do {
+ :local ScriptName [ :jobname ];
+
+ :global EitherOr;
+ :global LogPrint;
+ :global ParseKeyValueStore;
+ :global ScriptLock;
+
+ :local MacAddress $"mac-address";
+ :local UserName $username;
+
+ :if ([ $ScriptLock $ScriptName ] = false) do={
+ :error false;
+ }
+
+ :if ([ :typeof $MacAddress ] = "nothing" || [ :typeof $UserName ] = "nothing") do={
+ $LogPrint error $ScriptName ("This script is supposed to run from hotspot on login.");
+ :error false;
+ }
+
+ :local Date [ /system/clock/get date ];
+ :local UserVal ({});
+ :if ([ :len [ /ip/hotspot/user/find where name=$UserName ] ] > 0) do={
+ :set 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;
+ $LogPrint warning $ScriptName ("Added disabled access-list entry with comment '--- hotspot-to-wpa above ---'.");
+ }
+ :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;
+ $LogPrint warning $ScriptName ("Added template in access-list for hotspot '" . $Hotspot . "'.");
+ }
+ :local Template [ /caps-man/access-list/get ([ find where \
+ comment=("hotspot-to-wpa template " . $Hotspot) disabled ]->0) ];
+
+ :if ($Template->"action" = "reject") do={
+ $LogPrint info $ScriptName ("Ignoring login for hotspot '" . $Hotspot . "'.");
+ :error true;
+ }
+
+ # allow login page to load
+ :delay 1s;
+
+ $LogPrint info $ScriptName ("Adding/updating access-list entry for mac address " . $MacAddress . \
+ " (user " . $UserName . ").");
+ /caps-man/access-list/remove [ find where mac-address=$MacAddress comment~"^hotspot-to-wpa: " ];
+ /caps-man/access-list/add private-passphrase=($UserVal->"password") ssid-regexp="-wpa\$" \
+ mac-address=$MacAddress comment=("hotspot-to-wpa: " . $UserName . ", " . $MacAddress . ", " . $Date) \
+ action=reject 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;
+ }
+
+ :delay 2s;
+ /caps-man/access-list/set $Entry action=accept;
+} on-error={ }
diff --git a/hotspot-to-wpa.template.rsc b/hotspot-to-wpa.template.rsc
new file mode 100644
index 0000000..cbce42a
--- /dev/null
+++ b/hotspot-to-wpa.template.rsc
@@ -0,0 +1,118 @@
+#!rsc by RouterOS
+# RouterOS script: hotspot-to-wpa%TEMPL%
+# Copyright (c) 2019-2024 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# requires RouterOS, version=7.12
+#
+# add private WPA passphrase after hotspot login
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/hotspot-to-wpa.md
+#
+# !! This is just a template to generate the real script!
+# !! Pattern '%TEMPL%' is replaced, paths are filtered.
+
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:do {
+ :local ScriptName [ :jobname ];
+
+ :global EitherOr;
+ :global LogPrint;
+ :global ParseKeyValueStore;
+ :global ScriptLock;
+
+ :local MacAddress $"mac-address";
+ :local UserName $username;
+
+ :if ([ $ScriptLock $ScriptName ] = false) do={
+ :error false;
+ }
+
+ :if ([ :typeof $MacAddress ] = "nothing" || [ :typeof $UserName ] = "nothing") do={
+ $LogPrint error $ScriptName ("This script is supposed to run from hotspot on login.");
+ :error false;
+ }
+
+ :local Date [ /system/clock/get date ];
+ :local UserVal ({});
+ :if ([ :len [ /ip/hotspot/user/find where name=$UserName ] ] > 0) do={
+ :set 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={
+ :if ([ :len [ /interface/wifi/access-list/find where comment="--- hotspot-to-wpa above ---" disabled ] ] = 0) do={
+ /caps-man/access-list/add comment="--- hotspot-to-wpa above ---" disabled=yes;
+ /interface/wifi/access-list/add comment="--- hotspot-to-wpa above ---" disabled=yes;
+ $LogPrint warning $ScriptName ("Added disabled access-list entry with comment '--- hotspot-to-wpa above ---'.");
+ }
+ :local PlaceBefore ([ /caps-man/access-list/find where comment="--- hotspot-to-wpa above ---" disabled ]->0);
+ :local PlaceBefore ([ /interface/wifi/access-list/find where comment="--- hotspot-to-wpa above ---" disabled ]->0);
+
+ :if ([ :len [ /caps-man/access-list/find where \
+ :if ([ :len [ /interface/wifi/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;
+ /interface/wifi/access-list/add comment=("hotspot-to-wpa template " . $Hotspot) disabled=yes place-before=$PlaceBefore;
+ $LogPrint warning $ScriptName ("Added template in access-list for hotspot '" . $Hotspot . "'.");
+ }
+ :local Template [ /caps-man/access-list/get ([ find where \
+ :local Template [ /interface/wifi/access-list/get ([ find where \
+ comment=("hotspot-to-wpa template " . $Hotspot) disabled ]->0) ];
+
+ :if ($Template->"action" = "reject") do={
+ $LogPrint info $ScriptName ("Ignoring login for hotspot '" . $Hotspot . "'.");
+ :error true;
+ }
+
+ # allow login page to load
+ :delay 1s;
+
+ $LogPrint info $ScriptName ("Adding/updating access-list entry for mac address " . $MacAddress . \
+ " (user " . $UserName . ").");
+ /caps-man/access-list/remove [ find where mac-address=$MacAddress comment~"^hotspot-to-wpa: " ];
+ /interface/wifi/access-list/remove [ find where mac-address=$MacAddress comment~"^hotspot-to-wpa: " ];
+ /caps-man/access-list/add private-passphrase=($UserVal->"password") ssid-regexp="-wpa\$" \
+ /interface/wifi/access-list/add passphrase=($UserVal->"password") ssid-regexp="-wpa\$" \
+ mac-address=$MacAddress comment=("hotspot-to-wpa: " . $UserName . ", " . $MacAddress . ", " . $Date) \
+ action=reject place-before=$PlaceBefore;
+
+ :local Entry [ /caps-man/access-list/find where mac-address=$MacAddress \
+ :local Entry [ /interface/wifi/access-list/find where mac-address=$MacAddress \
+ comment=("hotspot-to-wpa: " . $UserName . ", " . $MacAddress . ", " . $Date) ];
+# NOT /caps-man/ #
+ :set ($Template->"private-passphrase") ($Template->"passphrase");
+# NOT /caps-man/ #
+ :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;
+ /interface/wifi/access-list/set $Entry !passphrase;
+ } else={
+ /caps-man/access-list/set $Entry private-passphrase=$PrivatePassphrase;
+ /interface/wifi/access-list/set $Entry 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;
+ /interface/wifi/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;
+ /interface/wifi/access-list/set $Entry vlan-id=$VlanId;
+ }
+# NOT /interface/wifi/ #
+ :local VlanMode [ $EitherOr ($UserInfo->"vlan-mode") ($Template->"vlan-mode") ];
+ :if ([ :len $VlanMode] > 0) do={
+ /caps-man/access-list/set $Entry vlan-mode=$VlanMode;
+ }
+# NOT /interface/wifi/ #
+
+ :delay 2s;
+ /caps-man/access-list/set $Entry action=accept;
+ /interface/wifi/access-list/set $Entry action=accept;
+} on-error={ }
diff --git a/hotspot-to-wpa.wifi.rsc b/hotspot-to-wpa.wifi.rsc
new file mode 100644
index 0000000..86aeed7
--- /dev/null
+++ b/hotspot-to-wpa.wifi.rsc
@@ -0,0 +1,95 @@
+#!rsc by RouterOS
+# RouterOS script: hotspot-to-wpa.wifi
+# Copyright (c) 2019-2024 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# requires RouterOS, version=7.12
+#
+# add private WPA passphrase after hotspot login
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/hotspot-to-wpa.md
+#
+# !! Do not edit this file, it is generated from template!
+
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:do {
+ :local ScriptName [ :jobname ];
+
+ :global EitherOr;
+ :global LogPrint;
+ :global ParseKeyValueStore;
+ :global ScriptLock;
+
+ :local MacAddress $"mac-address";
+ :local UserName $username;
+
+ :if ([ $ScriptLock $ScriptName ] = false) do={
+ :error false;
+ }
+
+ :if ([ :typeof $MacAddress ] = "nothing" || [ :typeof $UserName ] = "nothing") do={
+ $LogPrint error $ScriptName ("This script is supposed to run from hotspot on login.");
+ :error false;
+ }
+
+ :local Date [ /system/clock/get date ];
+ :local UserVal ({});
+ :if ([ :len [ /ip/hotspot/user/find where name=$UserName ] ] > 0) do={
+ :set 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 [ /interface/wifi/access-list/find where comment="--- hotspot-to-wpa above ---" disabled ] ] = 0) do={
+ /interface/wifi/access-list/add comment="--- hotspot-to-wpa above ---" disabled=yes;
+ $LogPrint warning $ScriptName ("Added disabled access-list entry with comment '--- hotspot-to-wpa above ---'.");
+ }
+ :local PlaceBefore ([ /interface/wifi/access-list/find where comment="--- hotspot-to-wpa above ---" disabled ]->0);
+
+ :if ([ :len [ /interface/wifi/access-list/find where \
+ comment=("hotspot-to-wpa template " . $Hotspot) disabled ] ] = 0) do={
+ /interface/wifi/access-list/add comment=("hotspot-to-wpa template " . $Hotspot) disabled=yes place-before=$PlaceBefore;
+ $LogPrint warning $ScriptName ("Added template in access-list for hotspot '" . $Hotspot . "'.");
+ }
+ :local Template [ /interface/wifi/access-list/get ([ find where \
+ comment=("hotspot-to-wpa template " . $Hotspot) disabled ]->0) ];
+
+ :if ($Template->"action" = "reject") do={
+ $LogPrint info $ScriptName ("Ignoring login for hotspot '" . $Hotspot . "'.");
+ :error true;
+ }
+
+ # allow login page to load
+ :delay 1s;
+
+ $LogPrint info $ScriptName ("Adding/updating access-list entry for mac address " . $MacAddress . \
+ " (user " . $UserName . ").");
+ /interface/wifi/access-list/remove [ find where mac-address=$MacAddress comment~"^hotspot-to-wpa: " ];
+ /interface/wifi/access-list/add passphrase=($UserVal->"password") ssid-regexp="-wpa\$" \
+ mac-address=$MacAddress comment=("hotspot-to-wpa: " . $UserName . ", " . $MacAddress . ", " . $Date) \
+ action=reject place-before=$PlaceBefore;
+
+ :local Entry [ /interface/wifi/access-list/find where mac-address=$MacAddress \
+ comment=("hotspot-to-wpa: " . $UserName . ", " . $MacAddress . ", " . $Date) ];
+ :set ($Template->"private-passphrase") ($Template->"passphrase");
+ :local PrivatePassphrase [ $EitherOr ($UserInfo->"private-passphrase") ($Template->"private-passphrase") ];
+ :if ([ :len $PrivatePassphrase ] > 0) do={
+ :if ($PrivatePassphrase = "ignore") do={
+ /interface/wifi/access-list/set $Entry !passphrase;
+ } else={
+ /interface/wifi/access-list/set $Entry passphrase=$PrivatePassphrase;
+ }
+ }
+ :local SsidRegexp [ $EitherOr ($UserInfo->"ssid-regexp") ($Template->"ssid-regexp") ];
+ :if ([ :len $SsidRegexp ] > 0) do={
+ /interface/wifi/access-list/set $Entry ssid-regexp=$SsidRegexp;
+ }
+ :local VlanId [ $EitherOr ($UserInfo->"vlan-id") ($Template->"vlan-id") ];
+ :if ([ :len $VlanId ] > 0) do={
+ /interface/wifi/access-list/set $Entry vlan-id=$VlanId;
+ }
+
+ :delay 2s;
+ /interface/wifi/access-list/set $Entry action=accept;
+} on-error={ }
diff --git a/ip-addr-bridge b/ip-addr-bridge.rsc
index 218eb2e..758cd46 100644
--- a/ip-addr-bridge
+++ b/ip-addr-bridge.rsc
@@ -1,6 +1,6 @@
#!rsc by RouterOS
# RouterOS script: ip-addr-bridge
-# Copyright (c) 2018-2022 Christian Hesse <mail@eworm.de>
+# Copyright (c) 2018-2024 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
diff --git a/ipsec-to-dns b/ipsec-to-dns
deleted file mode 100644
index c6cfdc4..0000000
--- a/ipsec-to-dns
+++ /dev/null
@@ -1,68 +0,0 @@
-#!rsc by RouterOS
-# RouterOS script: ipsec-to-dns
-# Copyright (c) 2021-2022 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 LogPrintExit2;
-:global IfThenElse;
-
-: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=$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 Comment ($CommentPrefix . $PeerVal->"id");
-:put ($PeerVal->"id");
- :local HostName [ :pick ($PeerVal->"id") 0 [ :find ($PeerVal->"id" . ".") "." ] ];
-:put $HostName;
-
- :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;
- }
-}
diff --git a/ipsec-to-dns.rsc b/ipsec-to-dns.rsc
new file mode 100644
index 0000000..dd40ca2
--- /dev/null
+++ b/ipsec-to-dns.rsc
@@ -0,0 +1,79 @@
+#!rsc by RouterOS
+# RouterOS script: ipsec-to-dns
+# Copyright (c) 2021-2024 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# requires RouterOS, version=7.12
+#
+# and add/remove/update DNS entries from IPSec mode-config
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/ipsec-to-dns.md
+
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:do {
+ :local ScriptName [ :jobname ];
+
+ :global Domain;
+ :global HostNameInZone;
+ :global Identity;
+ :global PrefixInZone;
+
+ :global CharacterReplace;
+ :global EscapeForRegEx;
+ :global IfThenElse;
+ :global LogPrint;
+ :global ScriptLock;
+
+ :if ([ $ScriptLock $ScriptName ] = false) do={
+ :error false;
+ }
+
+ :local Zone \
+ ([ $IfThenElse ($PrefixInZone = true) "ipsec." ] . \
+ [ $IfThenElse ($HostNameInZone = true) ($Identity . ".") ] . $Domain);
+ :local Ttl 5m;
+ :local CommentPrefix ("managed by " . $ScriptName . " for ");
+ :local CommentString ("--- " . $ScriptName . " above ---");
+
+ :if ([ :len [ /ip/dns/static/find where (name=$CommentString or (comment=$CommentString and name=-)) type=NXDOMAIN disabled ] ] = 0) do={
+ /ip/dns/static/add name=$CommentString type=NXDOMAIN disabled=yes;
+ $LogPrint warning $ScriptName ("Added disabled static dns record with name '" . $CommentString . "'.");
+ }
+ :local PlaceBefore ([ /ip/dns/static/find where (name=$CommentString or (comment=$CommentString and 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={
+ $LogPrint debug $ScriptName ("Peer " . $PeerId . " (" . $DnsRecordVal->"name" . ") still exists. Not deleting DNS entry.");
+ } else={
+ :local Found false;
+ $LogPrint info $ScriptName ("Peer " . $PeerId . " (" . $DnsRecordVal->"name" . ") has gone, deleting DNS entry.");
+ /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={
+ $LogPrint debug $ScriptName ("DNS entry for " . $Fqdn . " does not need updating.");
+ } else={
+ $LogPrint info $ScriptName ("Replacing DNS entry for " . $Fqdn . ", new address is " . $PeerVal->"dynamic-address" . ".");
+ /ip/dns/static/set name=$Fqdn address=($PeerVal->"dynamic-address") ttl=$Ttl comment=$Comment $DnsRecord;
+ }
+ } else={
+ $LogPrint info $ScriptName ("Adding new DNS entry for " . $Fqdn . ", address is " . $PeerVal->"dynamic-address" . ".");
+ /ip/dns/static/add name=$Fqdn address=($PeerVal->"dynamic-address") ttl=$Ttl comment=$Comment place-before=$PlaceBefore;
+ }
+ }
+} on-error={ }
diff --git a/ipv6-update b/ipv6-update
deleted file mode 100644
index b81a0ec..0000000
--- a/ipv6-update
+++ /dev/null
@@ -1,62 +0,0 @@
-#!rsc by RouterOS
-# RouterOS script: ipv6-update
-# Copyright (c) 2013-2022 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 Address [ /ipv6/address/find where from-pool=$Pool interface=($Comment->"interface") ];
- :if ([ :len $Address ] = 1) do={
- :set Address [ /ipv6/address/get $Address address ];
- $LogPrintExit2 info $0 ("Updating IPv6 address list with new IPv6 prefix " . $Address . \
- " from interface " . ($Comment->"interface")) false;
- /ipv6/firewall/address-list/set address=$Address $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/get [ find where interface=($Comment->"interface") from-pool=$Pool global ] 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;
- }
-}
diff --git a/ipv6-update.rsc b/ipv6-update.rsc
new file mode 100644
index 0000000..65bb959
--- /dev/null
+++ b/ipv6-update.rsc
@@ -0,0 +1,87 @@
+#!rsc by RouterOS
+# RouterOS script: ipv6-update
+# Copyright (c) 2013-2024 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# requires RouterOS, version=7.12
+#
+# update firewall and dns settings on IPv6 prefix change
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/ipv6-update.md
+
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:do {
+ :local ScriptName [ :jobname ];
+
+ :global LogPrint;
+ :global ParseKeyValueStore;
+ :global ScriptLock;
+
+ :local PdPrefix $"pd-prefix";
+
+ :if ([ $ScriptLock $ScriptName ] = false) do={
+ :error false;
+ }
+
+ :if ([ :typeof $PdPrefix ] = "nothing") do={
+ $LogPrint error $ScriptName ("This script is supposed to run from ipv6 dhcp-client.");
+ :error false;
+ }
+
+ :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);
+ $LogPrint warning $ScriptName ("Added ipv6 address list entry for ipv6-pool-" . $Pool);
+ }
+ :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={
+ $LogPrint info $ScriptName ("Updating IPv6 address list with new IPv6 prefix " . $PdPrefix);
+ /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));
+
+ $LogPrint info $ScriptName ("Updating IPv6 address list with new IPv6 host address " . $Address . \
+ " from interface " . ($Comment->"interface"));
+ /ipv6/firewall/address-list/set address=$Address $ListEntry;
+ } else={
+ $LogPrint info $ScriptName ("Updating IPv6 address list with new IPv6 prefix " . $Prefix . \
+ " from interface " . ($Comment->"interface"));
+ /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));
+
+ $LogPrint info $ScriptName ("Updating DNS record for " . ($RecordVal->"name") . \
+ ($RecordVal->"regexp") . " to " . $Address);
+ /ip/dns/static/set address=$Address $Record;
+ }
+ }
+ }
+} on-error={ }
diff --git a/lease-script b/lease-script
deleted file mode 100644
index cc1b6e5..0000000
--- a/lease-script
+++ /dev/null
@@ -1,54 +0,0 @@
-#!rsc by RouterOS
-# RouterOS script: lease-script
-# Copyright (c) 2013-2022 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 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 Name [ /system/script/get $Script name ];
- :local Store [ /system/script/get $Script source ];
-
- :set Store [ :pick $Store ([ :find $Store "\n# provides: lease-script, " ] + 27) [ :len $Store ] ];
- :set Store [ :pick $Store 0 [ :find $Store "\n" ] ];
- :set Store [ $ParseKeyValueStore $Store ];
-
- :set ($RunOrder->($Store->"order")) $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;
- }
-}
diff --git a/lease-script.rsc b/lease-script.rsc
new file mode 100644
index 0000000..8e1e8f6
--- /dev/null
+++ b/lease-script.rsc
@@ -0,0 +1,59 @@
+#!rsc by RouterOS
+# RouterOS script: lease-script
+# Copyright (c) 2013-2024 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# requires RouterOS, version=7.12
+#
+# run scripts on DHCP lease
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/lease-script.md
+
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:do {
+ :local ScriptName [ :jobname ];
+
+ :global Grep;
+ :global IfThenElse;
+ :global LogPrint;
+ :global ParseKeyValueStore;
+ :global ScriptLock;
+
+ :if ([ :typeof $leaseActIP ] = "nothing" || \
+ [ :typeof $leaseActMAC ] = "nothing" || \
+ [ :typeof $leaseServerName ] = "nothing" || \
+ [ :typeof $leaseBound ] = "nothing") do={
+ $LogPrint error $ScriptName ("This script is supposed to run from ip dhcp-server.");
+ :error false;
+ }
+
+ $LogPrint debug $ScriptName ("DHCP Server " . $leaseServerName . " " . [ $IfThenElse ($leaseBound = 0) \
+ "de" "" ] . "assigned lease " . $leaseActIP . " to " . $leaseActMAC);
+
+ :if ([ $ScriptLock $ScriptName 10 ] = false) do={
+ :error false;
+ }
+
+ :if ([ :len [ /system/script/job/find where script=$ScriptName ] ] > 1) do={
+ $LogPrint debug $ScriptName ("More invocations are waiting, exiting early.");
+ :error true;
+ }
+
+ :local RunOrder ({});
+ :foreach Script in=[ /system/script/find where source~("\n# provides: lease-script\\b") ] do={
+ :local ScriptVal [ /system/script/get $Script ];
+ :local Store [ $ParseKeyValueStore [ $Grep ($ScriptVal->"source") ("\23 provides: lease-script, ") ] ];
+
+ :set ($RunOrder->($Store->"order" . "-" . $ScriptVal->"name")) ($ScriptVal->"name");
+ }
+
+ :foreach Order,Script in=$RunOrder do={
+ :do {
+ $LogPrint debug $ScriptName ("Running script with order " . $Order . ": " . $Script);
+ /system/script/run $Script;
+ } on-error={
+ $LogPrint warning $ScriptName ("Running script '" . $Script . "' failed!");
+ }
+ }
+} on-error={ }
diff --git a/leds-day-mode b/leds-day-mode.rsc
index 78e1fae..b7c6b5b 100644
--- a/leds-day-mode
+++ b/leds-day-mode.rsc
@@ -1,6 +1,6 @@
#!rsc by RouterOS
# RouterOS script: leds-day-mode
-# Copyright (c) 2013-2022 Christian Hesse <mail@eworm.de>
+# Copyright (c) 2013-2024 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# enable LEDs
diff --git a/leds-night-mode b/leds-night-mode.rsc
index 112f9a1..fb7c7a2 100644
--- a/leds-night-mode
+++ b/leds-night-mode.rsc
@@ -1,6 +1,6 @@
#!rsc by RouterOS
# RouterOS script: leds-night-mode
-# Copyright (c) 2013-2022 Christian Hesse <mail@eworm.de>
+# Copyright (c) 2013-2024 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# disable LEDs
diff --git a/leds-toggle-mode b/leds-toggle-mode.rsc
index 225ceb2..136c9d1 100644
--- a/leds-toggle-mode
+++ b/leds-toggle-mode.rsc
@@ -1,6 +1,6 @@
#!rsc by RouterOS
# RouterOS script: leds-toggle-mode
-# Copyright (c) 2018-2022 Christian Hesse <mail@eworm.de>
+# Copyright (c) 2018-2024 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# toggle LEDs mode
diff --git a/log-forward b/log-forward
deleted file mode 100644
index 6ccad4f..0000000
--- a/log-forward
+++ /dev/null
@@ -1,94 +0,0 @@
-#!rsc by RouterOS
-# RouterOS script: log-forward
-# Copyright (c) 2020-2022 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 EscapeForRegEx;
-:global HexToNum;
-:global IfThenElse;
-:global LogPrintExit2;
-:global QuotedPrintable;
-:global ScriptLock;
-:global SendNotification2;
-:global SymbolByUnicodeName;
-: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 LogForwardFilterLogForwarding ("^Error sending e-mail <(" . \
- [ $EscapeForRegEx [ $QuotedPrintable ("[" . $Identity . "] " . \
- [ $SymbolForNotification "memo" ] . "Log Forwarding") ] ] . "|" . \
- [ $EscapeForRegEx [ $QuotedPrintable ("[" . $Identity . "] " . \
- [ $SymbolForNotification "warning-sign" ] . "Log Forwarding") ] ] . ")>:");
-:foreach Message in=[ /log/find where (!(message="") and !(message~$LogForwardFilterLogForwarding) 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);
- }
-}
diff --git a/log-forward.rsc b/log-forward.rsc
new file mode 100644
index 0000000..a919d8f
--- /dev/null
+++ b/log-forward.rsc
@@ -0,0 +1,102 @@
+#!rsc by RouterOS
+# RouterOS script: log-forward
+# Copyright (c) 2020-2024 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# requires RouterOS, version=7.12
+#
+# forward log messages via notification
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/log-forward.md
+
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:do {
+ :local ScriptName [ :jobname ];
+
+ :global Identity;
+ :global LogForwardFilter;
+ :global LogForwardFilterMessage;
+ :global LogForwardInclude;
+ :global LogForwardIncludeMessage;
+ :global LogForwardLast;
+ :global LogForwardRateLimit;
+
+ :global EitherOr;
+ :global HexToNum;
+ :global IfThenElse;
+ :global LogForwardFilterLogForwarding;
+ :global LogPrint;
+ :global MAX;
+ :global ScriptLock;
+ :global SendNotification2;
+ :global SymbolForNotification;
+
+ :if ([ $ScriptLock $ScriptName ] = false) do={
+ :error false;
+ }
+
+ :if ([ :typeof $LogForwardRateLimit ] = "nothing") do={
+ :set LogForwardRateLimit 0;
+ }
+
+ :if ($LogForwardRateLimit > 30) do={
+ :set LogForwardRateLimit ($LogForwardRateLimit - 1);
+ $LogPrint info $ScriptName ("Rate limit in action, not forwarding logs, if any!");
+ :error false;
+ }
+
+ :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 ];
+ :local Bullet "information";
+
+ :if ($Last < [ $HexToNum ($MessageVal->".id") ]) do={
+ :local DupCount ($MessageDups->($MessageVal->"message"));
+ :if ($MessageVal->"topics" ~ "(warning)") do={
+ :set Warning true;
+ :set Bullet "large-orange-circle";
+ }
+ :if ($MessageVal->"topics" ~ "(emergency|alert|critical|error)") do={
+ :set Warning true;
+ :set Bullet "large-red-circle";
+ }
+ :if ($DupCount < 3) do={
+ :set Messages ($Messages . "\n" . [ $SymbolForNotification $Bullet ] . \
+ $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=$ScriptName; \
+ 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={
+ :set LogForwardRateLimit [ $MAX 0 ($LogForwardRateLimit - 1) ];
+ }
+} on-error={ }
diff --git a/logo.avif b/logo.avif
index d118cc3..399a2f5 100644
--- a/logo.avif
+++ b/logo.avif
Binary files differ
diff --git a/logo.png b/logo.png
index 142e918..d97b75d 100644
--- a/logo.png
+++ b/logo.png
Binary files differ
diff --git a/logo.svg b/logo.svg
index f572033..a30e04e 100644
--- a/logo.svg
+++ b/logo.svg
@@ -1,23 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
-<svg width="96" height="96" version="1.1" viewBox="0 0 25.4 25.4" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
- <metadata>
- <rdf:RDF>
- <cc:Work rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <path d="m23.177 23.177c-2.9635 2.9635-17.991 2.9635-20.955 0-2.9635-2.9635-2.9635-17.991 0-20.955 2.9635-2.9635 17.991-2.9635 20.955 0 2.9635 2.9635 2.9635 17.991 0 20.955z" fill="#fff"/>
- <g stroke-width=".26458px" aria-label="#!rsc">
- <path d="m7.4832 16.646v-1.0239h-0.54606l0.16577-1.2774h-1.2774l-0.16577 1.2774h-0.97511l0.16577-1.2774h-1.2774l-0.16577 1.2774h-0.70208v1.0239h0.56556l-0.24378 1.8722h-0.68257v1.0239h0.54606l-0.18527 1.3944h1.2774l0.18527-1.3944h0.97511l-0.18527 1.3944h1.2774l0.18527-1.3944h0.70208v-1.0239h-0.56556l0.24378-1.8722zm-2.2037 1.8722h-0.97511l0.24378-1.8722h0.97511z"/>
- <path d="m9.6187 14.179h-1.6382l0.19502 4.271h1.2481zm-0.81909 5.1583c-0.48755 0-0.8776 0.39979-0.8776 0.8776 0 0.48755 0.39004 0.88735 0.8776 0.88735 0.4973 0 0.88735-0.39979 0.88735-0.88735 0-0.4778-0.39004-0.8776-0.88735-0.8776z"/>
- <path d="m13.373 15.612c-0.59482 0-1.1019 0.42905-1.3359 1.1506l-0.13652-1.0044h-1.3359v5.1778h1.5407v-2.6035c0.17552-0.77033 0.4388-1.2286 1.0726-1.2286 0.16577 0 0.30228 0.02925 0.46805 0.06826l0.24378-1.4919c-0.17552-0.04875-0.32178-0.06826-0.51681-0.06826z"/>
- <path d="m16.181 15.592c-1.3066 0-2.116 0.69233-2.116 1.5797 0 0.79959 0.50706 1.3261 1.5309 1.6187 0.9361 0.26328 1.0921 0.37054 1.0921 0.72158 0 0.31203-0.28278 0.48755-0.75083 0.48755-0.50706 0-0.98486-0.20477-1.3749-0.50706l-0.75083 0.83859c0.50706 0.4583 1.2676 0.77033 2.1647 0.77033 1.2871 0 2.3012-0.63382 2.3012-1.7064 0-0.92635-0.57531-1.3554-1.5992-1.6479-0.92635-0.27303-1.0629-0.39004-1.0629-0.66307 0-0.23402 0.20477-0.39004 0.62407-0.39004 0.44855 0 0.8776 0.14627 1.2774 0.39979l0.56556-0.86784c-0.4778-0.39004-1.1506-0.63382-1.9015-0.63382z"/>
- <path d="m21.281 15.592c-1.5504 0-2.5353 1.1506-2.5353 2.7986 0 1.6382 0.97511 2.7108 2.5645 2.7108 0.71183 0 1.2676-0.23403 1.7454-0.61432l-0.67282-0.9556c-0.37054 0.23403-0.62407 0.35104-0.99461 0.35104-0.61432 0-1.0239-0.35104-1.0239-1.5017 0-1.1604 0.38029-1.6089 1.0434-1.6089 0.35104 0 0.65332 0.11701 0.98486 0.37054l0.66307-0.9166c-0.4973-0.4193-1.0531-0.63382-1.7747-0.63382z"/>
- </g>
- <g transform="matrix(2 0 0 2 -.41134 3.175)" fill="#676867" fill-rule="evenodd" stroke-width=".13229">
- <path d="m4.9596-1.0196c0.40797 2.8312 1.9272 4.5499 5.0239 4.691-2.918 1.1164-5.9253-1.5076-5.0239-4.691"/>
- <path d="m3.3407-0.52096c0.034969-0.00777 0.038854 0.015542 0.041445 0.041445 0.098431 1.8054 0.85998 3.1744 1.8689 4.1108 1.0089 0.93639 2.3636 1.6941 4.274 1.6604-3.5772 1.4247-7.337-1.9984-6.1856-5.8126"/>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg id="svg" width="96" height="96" version="1.1" viewBox="0 0 25.4 25.4" xmlns="http://www.w3.org/2000/svg">
+ <defs id="defs">
+ <radialGradient id="radGradDark" cx="7.5006" cy="9.4015" r="9.7854" gradientTransform="matrix(1.6107 1.0797 -.58681 .87543 .93614 -7.022)" gradientUnits="userSpaceOnUse">
+ <stop id="dark-1" stop-color="#222" offset="0"/>
+ <stop id="dark-2" stop-color="#444" offset="1"/>
+ </radialGradient>
+ <radialGradient id="radGradRed" cx="14.501" cy="10.029" r="2.6711" gradientTransform="matrix(1.3827 .62837 -.44627 .98203 -1.0744 -8.9965)" gradientUnits="userSpaceOnUse">
+ <stop id="red-1" stop-color="#a00" offset="0"/>
+ <stop id="red-2" stop-color="#c22" offset="1"/>
+ </radialGradient>
+ </defs>
+ <g id="layer1">
+ <rect id="background" x="4.5766e-15" width="25.4" height="25.4" ry="5.1528" fill="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-width="13.229"/>
+ <path id="hexagon" d="m17.758 12.437-1.3326 0.77071a0.94166 0.94328 0 0 1-0.94166 0l-1.3326-0.77071a0.94166 0.94328 0 0 1-0.47083-0.8169v-1.5414a0.94166 0.94328 0 0 1 0.47083-0.8169l1.3326-0.77071a0.94166 0.94328 0 0 1 0.94166 0l1.3326 0.77071a0.94166 0.94328 0 0 1 0.47083 0.8169v1.5414a0.94166 0.94328 0 0 1-0.47083 0.8169z" fill="url(#radGradRed)" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width=".79375"/>
+ <g id="text" fill="url(#radGradDark)" stroke="#000" stroke-width=".1">
+ <g id="shebang" aria-label="#!">
+ <path id="path904" d="m13.767 4.8761v-1.6506h-0.88032l0.26724-2.0593h-2.0593l-0.26724 2.0593h-1.572l0.26724-2.0593h-2.0593l-0.26724 2.0593h-1.1318v1.6506h0.91175l-0.393 3.0182h-1.1004v1.6506h0.88031l-0.29868 2.2479h2.0593l0.29868-2.2479h1.572l-0.29868 2.2479h2.0593l0.29868-2.2479h1.1318v-1.6506h-0.91175l0.393-3.0182zm-3.5527 3.0182h-1.572l0.393-3.0182h1.572z"/>
+ <path id="path906" d="m17.209 0.89898h-2.6409l0.3144 6.8853s0.66885-0.28785 1.0123-0.28746c0.33937 3.865e-4 0.99985 0.28746 0.99985 0.28746z"/>
+ </g>
+ <g id="rsc" aria-label="rsc">
+ <path id="path910" d="m7.5809 12.875c-0.92632 0-1.716 0.66817-2.0804 1.7919l-0.2126-1.5641h-2.0804v8.0636h2.3993v-4.0546c0.27334-1.1997 0.68335-1.9134 1.6704-1.9134 0.25816 0 0.47075 0.04556 0.72891 0.1063l0.37964-2.3234c-0.27334-0.075928-0.50112-0.1063-0.80484-0.1063z"/>
+ <path id="path912" d="m11.954 12.845c-2.0349 0-3.2953 1.0782-3.2953 2.4601 0 1.2452 0.78965 2.0652 2.3841 2.5208 1.4578 0.41001 1.7008 0.57705 1.7008 1.1237 0 0.48594-0.44038 0.75928-1.1693 0.75928-0.78965 0-1.5337-0.3189-2.1412-0.78965l-1.1693 1.306c0.78965 0.71372 1.9741 1.1997 3.3712 1.1997 2.0045 0 3.5838-0.98706 3.5838-2.6575 0-1.4426-0.89595-2.1108-2.4904-2.5664-1.4426-0.4252-1.6552-0.60742-1.6552-1.0326 0-0.36445 0.3189-0.60742 0.97188-0.60742 0.69854 0 1.3667 0.22778 1.9893 0.62261l0.88076-1.3515c-0.7441-0.60742-1.7919-0.98706-2.9612-0.98706z"/>
+ <path id="path914" d="m19.896 12.845c-2.4145 0-3.9483 1.7919-3.9483 4.3583 0 2.5512 1.5186 4.2216 3.9938 4.2216 1.1085 0 1.9741-0.36446 2.7182-0.95669l-1.0478-1.4882c-0.57705 0.36445-0.97188 0.54668-1.5489 0.54668-0.95669 0-1.5945-0.54668-1.5945-2.3386 0-1.8071 0.59224-2.5056 1.6249-2.5056 0.54668 0 1.0174 0.18223 1.5337 0.57705l1.0326-1.4274c-0.77446-0.65298-1.64-0.98706-2.7638-0.98706z"/>
+ </g>
+ </g>
</g>
</svg>
diff --git a/mod/bridge-port-to b/mod/bridge-port-to.rsc
index 3f62e6f..567a762 100644
--- a/mod/bridge-port-to
+++ b/mod/bridge-port-to.rsc
@@ -1,6 +1,6 @@
#!rsc by RouterOS
# RouterOS script: mod/bridge-port-to
-# Copyright (c) 2013-2022 Christian Hesse <mail@eworm.de>
+# Copyright (c) 2013-2024 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# reset bridge ports to default bridge
@@ -12,7 +12,7 @@
:local BridgePortTo [ :tostr $1 ];
:global IfThenElse;
- :global LogPrintExit2;
+ :global LogPrint;
:global ParseKeyValueStore;
:local InterfaceReEnable ({});
@@ -24,21 +24,22 @@
: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;
+ $LogPrint warning $0 ([ $IfThenElse ([ :len $DHCPClient ] = 0) "Missing" "Duplicate" ] . \
+ " dhcp client configuration for interface " . $BridgePortVal->"interface" . "!");
+ :return false;
}
: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;
+ $LogPrint info $0 ("Disabling bridge port for interface " . $BridgePortVal->"interface" . ", enabling dhcp client.");
/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;
+ $LogPrint info $0 ("Enabling bridge port for interface " . $BridgePortVal->"interface" . ", changing to " . $BridgePortTo . \
+ " bridge " . $BridgeDefault . ", disabling dhcp client.");
:if ([ :len $DHCPClient ] = 1) do={
/ip/dhcp-client/disable $DHCPClient;
:delay 200ms;
@@ -50,16 +51,16 @@
}
/interface/bridge/port/set disabled=no bridge=$BridgeDefault $BridgePort;
} else={
- $LogPrintExit2 debug $0 ("Interface " . $BridgePortVal->"interface" . " already connected to " . $BridgePortTo . \
- " bridge " . $BridgeDefault . ".") false;
+ $LogPrint debug $0 ("Interface " . $BridgePortVal->"interface" . " already connected to " . $BridgePortTo . \
+ " bridge " . $BridgeDefault . ".");
}
}
}
}
}
:if ([ :len $InterfaceReEnable ] > 0) do={
- :delay 2s;
- $LogPrintExit2 info $0 ("Re-enabling interfaces...") false;
+ :delay 5s;
+ $LogPrint info $0 ("Re-enabling interfaces...");
/interface/ethernet/enable $InterfaceReEnable;
}
}
diff --git a/mod/bridge-port-vlan b/mod/bridge-port-vlan.rsc
index 9a6e08a..aee5ef9 100644
--- a/mod/bridge-port-vlan
+++ b/mod/bridge-port-vlan.rsc
@@ -1,6 +1,6 @@
#!rsc by RouterOS
# RouterOS script: mod/bridge-port-vlan
-# Copyright (c) 2013-2022 Christian Hesse <mail@eworm.de>
+# Copyright (c) 2013-2024 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# manage VLANs on bridge ports
@@ -12,7 +12,7 @@
:local ConfigTo [ :tostr $1 ];
:global IfThenElse;
- :global LogPrintExit2;
+ :global LogPrint;
:global ParseKeyValueStore;
:local InterfaceReEnable ({});
@@ -24,13 +24,14 @@
: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;
+ $LogPrint warning $0 ([ $IfThenElse ([ :len $DHCPClient ] = 0) "Missing" "Duplicate" ] . \
+ " dhcp client configuration for interface " . $BridgePortVal->"interface" . "!");
+ :return false;
}
: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;
+ $LogPrint info $0 ("Disabling bridge port for interface " . $BridgePortVal->"interface" . ", enabling dhcp client.");
/interface/bridge/port/disable $BridgePort;
:delay 200ms;
/ip/dhcp-client/enable $DHCPClient;
@@ -41,12 +42,13 @@
: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;
+ $LogPrint warning $0 ("Could not find VLAN '" . $Vlan . "' for interface " . $BridgePortVal->"interface" . "!");
+ :return false;
}
}
: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;
+ $LogPrint info $0 ("Enabling bridge port for interface " . $BridgePortVal->"interface" . ", changing to " . $ConfigTo . \
+ " vlan " . $Vlan . [ $IfThenElse ($Vlan != $VlanName) (" (" . $VlanName . ")") ] . ", disabling dhcp client.");
:if ([ :len $DHCPClient ] = 1) do={
/ip/dhcp-client/disable $DHCPClient;
:delay 200ms;
@@ -58,16 +60,16 @@
}
/interface/bridge/port/set disabled=no pvid=$Vlan $BridgePort;
} else={
- $LogPrintExit2 debug $0 ("Interface " . $BridgePortVal->"interface" . " already connected to " . $ConfigTo . \
- " vlan " . $Vlan . ".") false;
+ $LogPrint debug $0 ("Interface " . $BridgePortVal->"interface" . " already connected to " . $ConfigTo . \
+ " vlan " . $Vlan . ".");
}
}
}
}
}
:if ([ :len $InterfaceReEnable ] > 0) do={
- :delay 2s;
- $LogPrintExit2 info $0 ("Re-enabling interfaces...") false;
+ :delay 5s;
+ $LogPrint info $0 ("Re-enabling interfaces...");
/interface/ethernet/enable $InterfaceReEnable;
}
}
diff --git a/mod/inspectvar b/mod/inspectvar.rsc
index 2629b6e..577abf3 100644
--- a/mod/inspectvar
+++ b/mod/inspectvar.rsc
@@ -1,7 +1,10 @@
#!rsc by RouterOS
# RouterOS script: mod/inspectvar
-# Copyright (c) 2020-2022 Christian Hesse <mail@eworm.de>
+# Copyright (c) 2020-2024 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# inspect variables
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/mod/inspectvar.md
:global InspectVar;
:global InspectVarReturn;
@@ -36,7 +39,7 @@
:local TypeOf [ :typeof $Input ];
:local Return [ $IndentReturn "type" $TypeOf $Level ];
-
+
:if ($TypeOf = "array") do={
:foreach Key,Value in=$Input do={
:set $Return ($Return . "\n" . \
diff --git a/mod/ipcalc b/mod/ipcalc.rsc
index 14bb1ea..b098b44 100644
--- a/mod/ipcalc
+++ b/mod/ipcalc.rsc
@@ -1,7 +1,10 @@
#!rsc by RouterOS
# RouterOS script: mod/ipcalc
-# Copyright (c) 2020-2022 Christian Hesse <mail@eworm.de>
+# Copyright (c) 2020-2024 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# ip address calculation
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/mod/ipcalc.md
:global IPCalc;
:global IPCalcReturn;
@@ -10,18 +13,19 @@
:set IPCalc do={
:local Input [ :tostr $1 ];
+ :global FormatLine;
: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");
+ [ $FormatLine "Address" ($Values->"address") ] . "\n" . \
+ [ $FormatLine "Netmask" ($Values->"netmask") ] . "\n" . \
+ [ $FormatLine "Network" ($Values->"network") ] . "\n" . \
+ [ $FormatLine "HostMin" ($Values->"hostmin") ] . "\n" . \
+ [ $FormatLine "HostMax" ($Values->"hostmax") ] . "\n" . \
+ [ $FormatLine "Broadcast" ($Values->"broadcast") ]);
}
# calculate and return netmask, network, min host, max host and broadcast
diff --git a/mod/notification-email b/mod/notification-email
deleted file mode 100644
index 0c07beb..0000000
--- a/mod/notification-email
+++ /dev/null
@@ -1,133 +0,0 @@
-#!rsc by RouterOS
-# RouterOS script: mod/notification-email
-# Copyright (c) 2013-2022 Christian Hesse <mail@eworm.de>
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
-
-:global FlushEmailQueue;
-:global NotificationFunctions;
-: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 ];
-
- :if ([ /tool/e-mail/get last-status ] = "in-progress") do={
- $LogPrintExit2 debug $0 ("Sending mail in 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 [ /system/scheduler/find where name=$0 ] ] > 0 && $QueueLen = 0) do={
- $LogPrintExit2 warning $0 ("Flushing E-Mail messages from scheduler, but queue is empty.") false;
- }
-
- /system/scheduler/set interval=($QueueLen . "m") [ find where name=$0 ];
-
- :foreach Id,Message in=$EmailQueue do={
- :if ([ :typeof $Message ] = "array" ) do={
- :local Attach [ $EitherOr ($Message->"attach") "" ];
- :while ([ /tool/e-mail/get last-status ] = "in-progress") do={ :delay 1s; }
- /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=[ :toarray $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 [ find where name=$0 ];
- :set EmailQueue;
- } else={
- /system/scheduler/set interval=1m [ find where name=$0 ];
- }
-}
-
-# send notification via e-mail - expects one array argument
-:set ($NotificationFunctions->"email") do={
- :local Notification $1;
-
- :global Identity;
- :global EmailGeneralTo;
- :global EmailGeneralToOverride;
- :global EmailGeneralCc;
- :global EmailGeneralCcOverride;
- :global EmailQueue;
-
- :global EitherOr;
- :global IfThenElse;
- :global QuotedPrintable;
-
- :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=[ $QuotedPrintable ("[" . $Identity . "] " . ($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 \
- on-event=(":global FlushEmailQueue; \$FlushEmailQueue;");
- }
-}
-
-# 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;
-}
diff --git a/mod/notification-email.rsc b/mod/notification-email.rsc
new file mode 100644
index 0000000..0d83d69
--- /dev/null
+++ b/mod/notification-email.rsc
@@ -0,0 +1,240 @@
+#!rsc by RouterOS
+# RouterOS script: mod/notification-email
+# Copyright (c) 2013-2024 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# requires RouterOS, version=7.12
+#
+# send notifications via e-mail
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/mod/notification-email.md
+
+:global EMailGenerateFrom;
+:global FlushEmailQueue;
+:global LogForwardFilterLogForwarding;
+:global NotificationEMailSubject;
+:global NotificationFunctions;
+:global PurgeEMailQueue;
+:global QuotedPrintable;
+:global SendEMail;
+:global SendEMail2;
+
+# generate from-property with display name
+:set EMailGenerateFrom do={
+ :global Identity;
+
+ :global CleanName;
+
+ :local From [ /tool/e-mail/get from ];
+
+ :if ($From ~ "<.*>\$") do={
+ :return $From;
+ }
+
+ :return ([ $CleanName $Identity ] . " via routeros-scripts <" . $From . ">");
+}
+
+# flush e-mail queue
+:set FlushEmailQueue do={
+ :global EmailQueue;
+
+ :global EitherOr;
+ :global EMailGenerateFrom;
+ :global IsDNSResolving;
+ :global IsTimeSync;
+ :global LogPrint;
+
+ :local AllDone true;
+ :local QueueLen [ :len $EmailQueue ];
+ :local Scheduler [ /system/scheduler/find where name="_FlushEmailQueue" ];
+
+ :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={
+ $LogPrint debug $0 ("Sending mail is currently in progress, not flushing.");
+ :return false;
+ }
+
+ :if ([ $IsTimeSync ] = false) do={
+ $LogPrint debug $0 ("Time is not synced, not flushing.");
+ :return false;
+ }
+
+ :local EMailSettings [ /tool/e-mail/get ];
+ :if ([ :typeof [ :toip ($EMailSettings->"server") ] ] != "ip" && [ $IsDNSResolving ] = false) do={
+ $LogPrint debug $0 ("Server address is a DNS name and resolving fails, not flushing.");
+ :return false;
+ }
+
+ :if ([ :len $Scheduler ] > 0 && $QueueLen = 0) do={
+ $LogPrint warning $0 ("Flushing E-Mail messages from scheduler, but queue is empty.");
+ }
+
+ /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={
+ $LogPrint warning $0 ("File '" . $File . "' does not exist, can not attach.");
+ }
+ }
+ /tool/e-mail/send from=[ $EMailGenerateFrom ] 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 NotificationEMailSignature;
+ :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->"server") = "0.0.0.0" || ($EMailSettings->"from") = "<>") do={
+ :return false;
+ }
+
+ :if ([ :typeof $EmailQueue ] = "nothing") do={
+ :set EmailQueue ({});
+ }
+ :local Signature [ $EitherOr [ $NotificationEMailSignature ] [ /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;");
+ }
+}
+
+# purge the e-mail queue
+:set PurgeEMailQueue do={
+ :global EmailQueue;
+
+ /system/scheduler/remove [ find where name="_FlushEmailQueue" ];
+ :set EmailQueue;
+}
+
+# convert string to quoted-printable
+:global QuotedPrintable do={
+ :local Input [ :tostr $1 ];
+
+ :global CharacterMultiply;
+
+ :if ([ :len $Input ] = 0) do={
+ :return $Input;
+ }
+
+ :local Return "";
+ :local Chars ( \
+ "\00\01\02\03\04\05\06\07\08\09\0A\0B\0C\0D\0E\0F\10\11\12\13\14\15\16\17\18\19\1A\1B\1C\1D\1E\1F" . \
+ [ $CharacterMultiply ("\00") 29 ] . "=\00?" . [ $CharacterMultiply ("\00") 63 ] . "\7F" . \
+ "\80\81\82\83\84\85\86\87\88\89\8A\8B\8C\8D\8E\8F\90\91\92\93\94\95\96\97\98\99\9A\9B\9C\9D\9E\9F" . \
+ "\A0\A1\A2\A3\A4\A5\A6\A7\A8\A9\AA\AB\AC\AD\AE\AF\B0\B1\B2\B3\B4\B5\B6\B7\B8\B9\BA\BB\BC\BD\BE\BF" . \
+ "\C0\C1\C2\C3\C4\C5\C6\C7\C8\C9\CA\CB\CC\CD\CE\CF\D0\D1\D2\D3\D4\D5\D6\D7\D8\D9\DA\DB\DC\DD\DE\DF" . \
+ "\E0\E1\E2\E3\E4\E5\E6\E7\E8\E9\EA\EB\EC\ED\EE\EF\F0\F1\F2\F3\F4\F5\F6\F7\F8\F9\FA\FB\FC\FD\FE\FF");
+ :local Hex "0123456789ABCDEF";
+
+ :for I from=0 to=([ :len $Input ] - 1) do={
+ :local Char [ :pick $Input $I ];
+ :local Replace [ :find $Chars $Char ];
+
+ :if ([ :typeof $Replace ] = "num") do={
+ :set Char ("=" . [ :pick $Hex ($Replace / 16)] . [ :pick $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;
+}
diff --git a/mod/notification-matrix b/mod/notification-matrix.rsc
index 5a5f8cf..aa95841 100644
--- a/mod/notification-matrix
+++ b/mod/notification-matrix.rsc
@@ -1,31 +1,37 @@
#!rsc by RouterOS
# RouterOS script: mod/notification-matrix
-# Copyright (c) 2013-2022 Michael Gisbers <michael@gisbers.de>
+# Copyright (c) 2013-2024 Michael Gisbers <michael@gisbers.de>
# Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# send notifications via Matrix
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/mod/notification-matrix.md
:global FlushMatrixQueue;
:global NotificationFunctions;
+:global PurgeMatrixQueue;
:global SendMatrix;
:global SendMatrix2;
+:global SetupMatrixAuthenticate;
+:global SetupMatrixJoinRoom;
# flush Matrix queue
:set FlushMatrixQueue do={
:global MatrixQueue;
:global IsFullyConnected;
- :global LogPrintExit2;
+ :global LogPrint;
:if ([ $IsFullyConnected ] = false) do={
- $LogPrintExit2 debug $0 ("System is not fully connected, not flushing.") false;
+ $LogPrint debug $0 ("System is not fully connected, not flushing.");
: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;
+ :if ([ :len [ /system/scheduler/find where name="_FlushMatrixQueue" ] ] > 0 && $QueueLen = 0) do={
+ $LogPrint warning $0 ("Flushing Matrix messages from scheduler, but queue is empty.");
}
:foreach Id,Message in=$MatrixQueue do={
@@ -39,14 +45,14 @@
$Message->"formatted" . "\" }") as-value;
:set ($MatrixQueue->$Id);
} on-error={
- $LogPrintExit2 debug $0 ("Sending queued Matrix message failed.") false;
+ $LogPrint debug $0 ("Sending queued Matrix message failed.");
:set AllDone false;
}
}
}
:if ($AllDone = true && $QueueLen = [ :len $MatrixQueue ]) do={
- /system/scheduler/remove [ find where name=$0 ];
+ /system/scheduler/remove [ find where name="_FlushMatrixQueue" ];
:set MatrixQueue;
}
}
@@ -56,6 +62,7 @@
:local Notification $1;
:global Identity;
+ :global IdentityExtra;
:global MatrixAccessToken;
:global MatrixAccessTokenOverride;
:global MatrixHomeServer;
@@ -65,7 +72,7 @@
:global MatrixRoomOverride;
:global EitherOr;
- :global LogPrintExit2;
+ :global LogPrint;
:global SymbolForNotification;
:local PrepareText do={
@@ -106,10 +113,11 @@
:return false;
}
- :local Plain [ $PrepareText ("## [" . $Identity . "] " . ($Notification->"subject") . "\n```\n" . \
- ($Notification->"message") . "\n```") "plain" ];
- :local Formatted ("<h2>" . [ $PrepareText ("[" . $Identity . "] " . ($Notification->"subject")) "format" ] . "</h2>" . \
- "<pre><code>" . [ $PrepareText ($Notification->"message") "format" ] . "</code></pre>");
+ :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" ]);
@@ -126,7 +134,7 @@
"\"format\": \"org.matrix.custom.html\", \"formatted_body\": \"" . \
$Formatted . "\" }") as-value;
} on-error={
- $LogPrintExit2 info $0 ("Failed sending Matrix notification! Queuing...") false;
+ $LogPrint info $0 ("Failed sending Matrix notification! Queuing...");
:if ([ :typeof $MatrixQueue ] = "nothing") do={
:set MatrixQueue ({});
@@ -139,13 +147,21 @@
: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 \
+ :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;");
}
}
}
+# purge the Matrix queue
+:set PurgeMatrixQueue do={
+ :global MatrixQueue;
+
+ /system/scheduler/remove [ find where name="_FlushMatrixQueue" ];
+ :set MatrixQueue;
+}
+
# send notification via Matrix - expects at least two string arguments
:set SendMatrix do={
:global SendMatrix2;
@@ -161,3 +177,84 @@
($NotificationFunctions->"matrix") ("\$NotificationFunctions->\"matrix\"") $Notification;
}
+
+# setup - get home server and access token
+:set SetupMatrixAuthenticate do={
+ :local User [ :tostr $1 ];
+ :local Pass [ :tostr $2 ];
+
+ :global CharacterReplace;
+ :global LogPrint;
+ :global ParseJson;
+
+ :global MatrixAccessToken;
+ :global MatrixHomeServer;
+
+ :local Domain [ :pick $User ([ :find $User ":" ] + 1) [ :len $User] ];
+ :do {
+ :local Data ([ /tool/fetch check-certificate=yes-without-crl output=user \
+ ("https://" . $Domain . "/.well-known/matrix/client") as-value ]->"data");
+ :set MatrixHomeServer ([ $ParseJson ([ $ParseJson [ $CharacterReplace $Data " " "" ] ]->"m.homeserver") ]->"base_url");
+ $LogPrint debug $0 ("Home server is: " . $MatrixHomeServer);
+ } on-error={
+ $LogPrint error $0 ("Failed getting home server!");
+ :return false;
+ }
+
+ :if ([ :pick $MatrixHomeServer 0 8 ] = "https://") do={
+ :set MatrixHomeServer [ :pick $MatrixHomeServer 8 [ :len $MatrixHomeServer ] ];
+ }
+
+ :do {
+ :local Data ([ /tool/fetch check-certificate=yes-without-crl output=user \
+ http-method=post http-data=("{\"type\":\"m.login.password\", \"user\":\"" . $User . "\", \"password\":\"" . $Pass . "\"}") \
+ ("https://" . $MatrixHomeServer . "/_matrix/client/r0/login") as-value ]->"data");
+ :set MatrixAccessToken ([ $ParseJson $Data ]->"access_token");
+ $LogPrint debug $0 ("Access token is: " . $MatrixAccessToken);
+ } on-error={
+ $LogPrint error $0 ("Failed logging in (and getting access token)!");
+ :return false;
+ }
+
+ :do {
+ /system/script/set global-config-overlay source=([ get global-config-overlay source ] . "\n" . \
+ ":global MatrixHomeServer \"" . $MatrixHomeServer . "\";\n" . \
+ ":global MatrixAccessToken \"" . $MatrixAccessToken . "\";\n");
+ $LogPrint info $0 ("Appended configuration to global-config-overlay. Now create and join a room, please!");
+ } on-error={
+ $LogPrint error $0 ("Failed appending configuration to global-config-overlay!");
+ :return false;
+ }
+}
+
+# setup - join a room
+:set SetupMatrixJoinRoom do={
+ :global MatrixRoom [ :tostr $1 ];
+
+ :global LogPrint;
+ :global UrlEncode;
+
+ :global MatrixAccessToken;
+ :global MatrixHomeServer;
+ :global MatrixRoom;
+
+ :do {
+ /tool/fetch check-certificate=yes-without-crl output=none \
+ http-method=post http-data="" \
+ ("https://" . $MatrixHomeServer . "/_matrix/client/r0/rooms/" . [ $UrlEncode $MatrixRoom ] . \
+ "/join?access_token=" . [ $UrlEncode $MatrixAccessToken ]) as-value;
+ $LogPrint debug $0 ("Joined the room.");
+ } on-error={
+ $LogPrint error $0 ("Failed joining the room!");
+ :return false;
+ }
+
+ :do {
+ /system/script/set global-config-overlay source=([ get global-config-overlay source ] . "\n" . \
+ ":global MatrixRoom \"" . $MatrixRoom . "\";\n");
+ $LogPrint info $0 ("Appended configuration to global-config-overlay. Please review and cleanup!");
+ } on-error={
+ $LogPrint error $0 ("Failed appending configuration to global-config-overlay!");
+ :return false;
+ }
+}
diff --git a/mod/notification-ntfy.rsc b/mod/notification-ntfy.rsc
new file mode 100644
index 0000000..6d48a59
--- /dev/null
+++ b/mod/notification-ntfy.rsc
@@ -0,0 +1,136 @@
+#!rsc by RouterOS
+# RouterOS script: mod/notification-ntfy
+# Copyright (c) 2013-2024 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# send notifications via Ntfy (ntfy.sh)
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/mod/notification-ntfy.md
+
+:global FlushNtfyQueue;
+:global NotificationFunctions;
+:global PurgeNtfyQueue;
+:global SendNtfy;
+:global SendNtfy2;
+
+# flush ntfy queue
+:set FlushNtfyQueue do={
+ :global NtfyQueue;
+ :global NtfyMessageIDs;
+
+ :global IsFullyConnected;
+ :global LogPrint;
+
+ :if ([ $IsFullyConnected ] = false) do={
+ $LogPrint debug $0 ("System is not fully connected, not flushing.");
+ :return false;
+ }
+
+ :local AllDone true;
+ :local QueueLen [ :len $NtfyQueue ];
+
+ :if ([ :len [ /system/scheduler/find where name="_FlushNtfyQueue" ] ] > 0 && $QueueLen = 0) do={
+ $LogPrint warning $0 ("Flushing Ntfy messages from scheduler, but queue is empty.");
+ }
+
+ :foreach Id,Message in=$NtfyQueue do={
+ :if ([ :typeof $Message ] = "array" ) do={
+ :do {
+ /tool/fetch check-certificate=yes-without-crl output=none http-method=post \
+ ($Message->"url") http-header-field=($Message->"headers") http-data=($Message->"text") as-value;
+ :set ($NtfyQueue->$Id);
+ } on-error={
+ $LogPrint debug $0 ("Sending queued Ntfy message failed.");
+ :set AllDone false;
+ }
+ }
+ }
+
+ :if ($AllDone = true && $QueueLen = [ :len $NtfyQueue ]) do={
+ /system/scheduler/remove [ find where name="_FlushNtfyQueue" ];
+ :set NtfyQueue;
+ }
+}
+
+# send notification via ntfy - expects one array argument
+:set ($NotificationFunctions->"ntfy") do={
+ :local Notification $1;
+
+ :global Identity;
+ :global IdentityExtra;
+ :global NtfyQueue;
+ :global NtfyServer;
+ :global NtfyServerOverride;
+ :global NtfyTopic;
+ :global NtfyTopicOverride;
+
+ :global CertificateAvailable;
+ :global EitherOr;
+ :global IfThenElse;
+ :global LogPrint;
+ :global SymbolForNotification;
+ :global UrlEncode;
+
+ :local Server [ $EitherOr ($NtfyServerOverride->($Notification->"origin")) $NtfyServer ];
+ :local Topic [ $EitherOr ($NtfyTopicOverride->($Notification->"origin")) $NtfyTopic ];
+
+ :if ([ :len $Topic ] = 0) do={
+ :return false;
+ }
+
+ :local Url ("https://" . $NtfyServer . "/" . [ $UrlEncode $NtfyTopic ]);
+ :local Headers ({ ("Priority: " . [ $IfThenElse ($Notification->"silent") "low" "default" ]); \
+ ("Title: " . "[" . $IdentityExtra . $Identity . "] " . ($Notification->"subject")) });
+ :local Text (($Notification->"message") . "\n");
+ :if ([ :len ($Notification->"link") ] > 0) do={
+ :set Text ($Text . "\n" . [ $SymbolForNotification "link" ] . ($Notification->"link"));
+ }
+
+ :do {
+ :if ($NtfyServer = "ntfy.sh") do={
+ :if ([ $CertificateAvailable "R3" ] = false) do={
+ $LogPrint warning $0 ("Downloading required certificate failed.");
+ :error false;
+ }
+ }
+ /tool/fetch check-certificate=yes-without-crl output=none http-method=post \
+ $Url http-header-field=$Headers http-data=$Text as-value;
+ } on-error={
+ $LogPrint info $0 ("Failed sending ntfy notification! Queuing...");
+
+ :if ([ :typeof $NtfyQueue ] = "nothing") do={
+ :set NtfyQueue ({});
+ }
+ :set Text ($Text . "\n" . [ $SymbolForNotification "alarm-clock" ] . \
+ "This message was queued since " . [ /system/clock/get date ] . " " . \
+ [ /system/clock/get time ] . " and may be obsolete.");
+ :set ($NtfyQueue->[ :len $NtfyQueue ]) { url=$Url; headers=$Headers; text=$Text };
+ :if ([ :len [ /system/scheduler/find where name="_FlushNtfyQueue" ] ] = 0) do={
+ /system/scheduler/add name="_FlushNtfyQueue" interval=1m start-time=startup \
+ on-event=(":global FlushNtfyQueue; \$FlushNtfyQueue;");
+ }
+ }
+}
+
+# purge the Ntfy queue
+:set PurgeNtfyQueue do={
+ :global NtfyQueue;
+
+ /system/scheduler/remove [ find where name="_FlushNtfyQueue" ];
+ :set NtfyQueue;
+}
+
+# send notification via ntfy - expects at least two string arguments
+:set SendNtfy do={
+ :global SendNtfy2;
+
+ $SendNtfy2 ({ subject=$1; message=$2; link=$3; silent=$4 });
+}
+
+# send notification via ntfy - expects one array argument
+:set SendNtfy2 do={
+ :local Notification $1;
+
+ :global NotificationFunctions;
+
+ ($NotificationFunctions->"ntfy") ("\$NotificationFunctions->\"ntfy\"") $Notification;
+}
diff --git a/mod/notification-telegram b/mod/notification-telegram.rsc
index 849740b..506ec80 100644
--- a/mod/notification-telegram
+++ b/mod/notification-telegram.rsc
@@ -1,51 +1,58 @@
#!rsc by RouterOS
# RouterOS script: mod/notification-telegram
-# Copyright (c) 2013-2022 Christian Hesse <mail@eworm.de>
+# Copyright (c) 2013-2024 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# send notifications via Telegram
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/mod/notification-telegram.md
:global FlushTelegramQueue;
:global NotificationFunctions;
+:global PurgeTelegramQueue;
:global SendTelegram;
:global SendTelegram2;
# flush telegram queue
:set FlushTelegramQueue do={
:global TelegramQueue;
+ :global TelegramMessageIDs;
:global IsFullyConnected;
- :global LogPrintExit2;
+ :global LogPrint;
+ :global ParseJson;
+ :global UrlEncode;
:if ([ $IsFullyConnected ] = false) do={
- $LogPrintExit2 debug $0 ("System is not fully connected, not flushing.") false;
+ $LogPrint debug $0 ("System is not fully connected, not flushing.");
: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;
+ :if ([ :len [ /system/scheduler/find where name="_FlushTelegramQueue" ] ] > 0 && $QueueLen = 0) do={
+ $LogPrint warning $0 ("Flushing Telegram messages from scheduler, but queue is empty.");
}
:foreach Id,Message in=$TelegramQueue do={
:if ([ :typeof $Message ] = "array" ) do={
:do {
- /tool/fetch check-certificate=yes-without-crl output=none http-method=post \
+ :local Data ([ /tool/fetch check-certificate=yes-without-crl output=user http-method=post \
("https://api.telegram.org/bot" . ($Message->"tokenid") . "/sendMessage") \
- http-data=("chat_id=" . ($Message->"chatid") . \
- "&disable_notification=" . ($Message->"silent") . \
- "&disable_web_page_preview=true&parse_mode=" . ($Message->"parsemode") . \
- "&text=" . ($Message->"text")) as-value;
+ http-data=("chat_id=" . ($Message->"chatid") . "&disable_notification=" . ($Message->"silent") . \
+ "&reply_to_message_id=" . ($Message->"replyto") . "&disable_web_page_preview=true" . \
+ "&parse_mode=MarkdownV2&text=" . [ $UrlEncode ($Message->"text") ]) as-value ]->"data");
:set ($TelegramQueue->$Id);
+ :set ($TelegramMessageIDs->([ $ParseJson ([ $ParseJson $Data ]->"result") ]->"message_id")) 1;
} on-error={
- $LogPrintExit2 debug $0 ("Sending queued Telegram message failed.") false;
+ $LogPrint debug $0 ("Sending queued Telegram message failed.");
:set AllDone false;
}
}
}
:if ($AllDone = true && $QueueLen = [ :len $TelegramQueue ]) do={
- /system/scheduler/remove [ find where name=$0 ];
+ /system/scheduler/remove [ find where name="_FlushTelegramQueue" ];
:set TelegramQueue;
}
}
@@ -55,9 +62,10 @@
:local Notification $1;
:global Identity;
+ :global IdentityExtra;
:global TelegramChatId;
:global TelegramChatIdOverride;
- :global TelegramFixedWidthFont;
+ :global TelegramMessageIDs;
:global TelegramQueue;
:global TelegramTokenId;
:global TelegramTokenIdOverride;
@@ -66,20 +74,15 @@
:global CharacterReplace;
:global EitherOr;
:global IfThenElse;
- :global LogPrintExit2;
+ :global LogPrint;
+ :global ParseJson;
: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"={ "\\"; "`" };
@@ -97,15 +100,21 @@
:return $Return;
}
- :local ChatId [ $EitherOr ($TelegramChatIdOverride->($Notification->"origin")) $TelegramChatId ];
+ :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;
}
+ :if ([ :typeof $TelegramMessageIDs ] = "nothing") do={
+ :set TelegramMessageIDs ({});
+ }
+
:local Truncated false;
- :local Text ("*__" . [ $EscapeMD ("[" . $Identity . "] " . ($Notification->"subject")) "plain" ] . "__*\n\n");
+ :local Text ("*__" . [ $EscapeMD ("[" . $IdentityExtra . $Identity . "] " . \
+ ($Notification->"subject")) "plain" ] . "__*\n\n");
:local LenSubject [ :len $Text ];
:local LenMessage [ :len ($Notification->"message") ];
:local LenLink [ :len ($Notification->"link") ];
@@ -121,38 +130,47 @@
}
:if ($Truncated = true) do={
:set Text ($Text . "\n" . [ $SymbolForNotification "scissors" ] . \
- [ $EscapeMD ("The Telegram message was too long and has been truncated, cut off " . \
+ [ $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;
+ $LogPrint warning $0 ("Downloading required certificate failed.");
+ :error false;
}
- /tool/fetch check-certificate=yes-without-crl output=none http-method=post \
+ :local Data ([ /tool/fetch check-certificate=yes-without-crl output=user http-method=post \
("https://api.telegram.org/bot" . $TokenId . "/sendMessage") \
http-data=("chat_id=" . $ChatId . "&disable_notification=" . ($Notification->"silent") . \
- "&disable_web_page_preview=true&parse_mode=" . $ParseMode . "&text=" . $Text) as-value;
+ "&reply_to_message_id=" . ($Notification->"replyto") . "&disable_web_page_preview=true" . \
+ "&parse_mode=MarkdownV2&text=" . [ $UrlEncode $Text ]) as-value ]->"data");
+ :set ($TelegramMessageIDs->([ $ParseJson ([ $ParseJson $Data ]->"result") ]->"message_id")) 1;
} on-error={
- $LogPrintExit2 info $0 ("Failed sending telegram notification! Queuing...") false;
+ $LogPrint info $0 ("Failed sending telegram notification! Queuing...");
:if ([ :typeof $TelegramQueue ] = "nothing") do={
:set TelegramQueue ({});
}
- :set Text ($Text . [ $UrlEncode ("\n" . [ $SymbolForNotification "alarm-clock" ] . \
+ :set Text ($Text . "\n" . [ $SymbolForNotification "alarm-clock" ] . \
[ $EscapeMD ("This message was queued since " . [ /system/clock/get date ] . \
- " " . [ /system/clock/get time ] . " and may be obsolete.") "plain" ]) ]);
+ " " . [ /system/clock/get time ] . " and may be obsolete.") "plain" ]);
:set ($TelegramQueue->[ :len $TelegramQueue ]) { chatid=$ChatId; tokenid=$TokenId;
- parsemode=$ParseMode; text=$Text; silent=($Notification->"silent") };
- :if ([ :len [ /system/scheduler/find where name="\$FlushTelegramQueue" ] ] = 0) do={
- /system/scheduler/add name="\$FlushTelegramQueue" interval=1m start-time=startup \
+ 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;");
}
}
}
+# purge the Telegram queue
+:set PurgeTelegramQueue do={
+ :global TelegramQueue;
+
+ /system/scheduler/remove [ find where name="_FlushTelegramQueue" ];
+ :set TelegramQueue;
+}
+
# send notification via telegram - expects at least two string arguments
:set SendTelegram do={
:global SendTelegram2;
diff --git a/mod/scriptrunonce b/mod/scriptrunonce.rsc
index 6cca175..85d465a 100644
--- a/mod/scriptrunonce
+++ b/mod/scriptrunonce.rsc
@@ -1,7 +1,10 @@
#!rsc by RouterOS
# RouterOS script: mod/scriptrunonece
-# Copyright (c) 2020-2022 Christian Hesse <mail@eworm.de>
+# Copyright (c) 2020-2024 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# download script and run it once
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/mod/scriptrunonce.md
:global ScriptRunOnce;
@@ -12,34 +15,35 @@
:global ScriptRunOnceBaseUrl;
:global ScriptRunOnceUrlSuffix;
- :global LogPrintExit2;
+ :global LogPrint;
:global ValidateSyntax;
:foreach Script in=$Scripts do={
- :if (!($Script ~ "^(ftp|https\?|sftp)://")) 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;
+ $LogPrint warning $0 ("Script '" . $Script . "' is not an url and base url is not available.");
+ :return false;
}
- :set Script ($ScriptRunOnceBaseUrl . $Script . $ScriptRunOnceUrlSuffix);
+ :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;
+ $LogPrint warning $0 ("Failed fetching script '" . $Script . "'!");
}
:if ([ :len $Source ] > 0) do={
:if ([ $ValidateSyntax $Source ] = true) do={
:do {
- $LogPrintExit2 info $0 ("Running script '" . $Script . "' now.") false;
+ $LogPrint info $0 ("Running script '" . $Script . "' now.");
[ :parse $Source ];
} on-error={
- $LogPrintExit2 warning $0 ("The script '" . $Script . "' failed to run!") false;
+ $LogPrint warning $0 ("The script '" . $Script . "' failed to run!");
}
} else={
- $LogPrintExit2 warning $0 ("The script '" . $Script . "' failed syntax validation!") false;
+ $LogPrint warning $0 ("The script '" . $Script . "' failed syntax validation!");
}
}
}
diff --git a/mod/ssh-keys-import.rsc b/mod/ssh-keys-import.rsc
new file mode 100644
index 0000000..6716958
--- /dev/null
+++ b/mod/ssh-keys-import.rsc
@@ -0,0 +1,112 @@
+#!rsc by RouterOS
+# RouterOS script: mod/ssh-keys-import
+# Copyright (c) 2020-2024 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# requires RouterOS, version=7.12
+#
+# import ssh keys for public key authentication
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/mod/ssh-keys-import.md
+
+:global SSHKeysImport;
+:global SSHKeysImportFile;
+
+# import single key passed as string
+:set SSHKeysImport do={
+ :local Key [ :tostr $1 ];
+ :local User [ :tostr $2 ];
+
+ :global CharacterReplace;
+ :global GetRandom20CharAlNum;
+ :global LogPrint;
+ :global MkDir;
+ :global WaitForFile;
+
+ :if ([ :len $Key ] = 0 || [ :len $User ] = 0) do={
+ $LogPrint warning $0 ("Missing argument(s), please pass key and user!");
+ :return false;
+ }
+
+ :if ([ :len [ /user/find where name=$User ] ] = 0) do={
+ $LogPrint warning $0 ("User '" . $User . "' does not exist.");
+ :return false;
+ }
+
+ :local KeyVal [ :toarray [ $CharacterReplace $Key " " "," ] ];
+ :if (!($KeyVal->0 = "ssh-ed25519" || $KeyVal->0 = "ssh-rsa")) do={
+ $LogPrint warning $0 ("SSH key of type '" . $KeyVal->0 . "' is not supported.");
+ :return false;
+ }
+
+ :if ([ $MkDir "tmpfs/ssh-keys-import" ] = false) do={
+ $LogPrint warning $0 ("Creating directory 'tmpfs/ssh-keys-import' failed!");
+ :return false;
+ }
+
+ :local FingerPrintMD5 [ :convert from=base64 transform=md5 to=hex ($KeyVal->1) ];
+
+ :if ([ :len [ /user/ssh-keys/find where user=$User key-owner~("\\bmd5=" . $FingerPrintMD5 . "\\b") ] ] > 0) do={
+ $LogPrint warning $0 ("The ssh public key (MD5:" . $FingerPrintMD5 . \
+ ") is already available for user '" . $User . "'.");
+ :return false;
+ }
+
+ :local FileName ("tmpfs/ssh-keys-import/key-" . [ $GetRandom20CharAlNum 6 ] . ".pub");
+ /file/add name=$FileName contents=($Key . ", md5=" . $FingerPrintMD5);
+ $WaitForFile $FileName;
+
+ :do {
+ /user/ssh-keys/import public-key-file=$FileName user=$User;
+ $LogPrint info $0 ("Imported ssh public key (" . $KeyVal->2 . ", " . $KeyVal->0 . ", " . \
+ "MD5:" . $FingerPrintMD5 . ") for user '" . $User . "'.");
+ } on-error={
+ $LogPrint warning $0 ("Failed importing key.");
+ :return false;
+ }
+}
+
+# import keys from a file
+:set SSHKeysImportFile do={
+ :local FileName [ :tostr $1 ];
+ :local User [ :tostr $2 ];
+
+ :global CharacterReplace;
+ :global EitherOr;
+ :global LogPrint;
+ :global ParseKeyValueStore;
+ :global SSHKeysImport;
+
+ :if ([ :len $FileName ] = 0 || [ :len $User ] = 0) do={
+ $LogPrint warning $0 ("Missing argument(s), please pass file name and user!");
+ :return false;
+ }
+
+ :local File [ /file/find where name=$FileName ];
+ :if ([ :len $File ] = 0) do={
+ $LogPrint warning $0 ("File '" . $FileName . "' does not exist.");
+ :return false;
+ }
+ :local Keys ([ /file/get $FileName contents ] . "\n");
+
+ :do {
+ :local Continue false;
+ :local Line [ :pick $Keys 0 [ :find $Keys "\n" ] ];
+ :set Keys [ :pick $Keys ([ :find $Keys "\n" ] + 1) [ :len $Keys ] ];
+ :local KeyVal [ :toarray [ $CharacterReplace $Line " " "," ] ];
+ :if ($KeyVal->0 = "ssh-ed25519" || $KeyVal->0 = "ssh-rsa") do={
+ :do {
+ $SSHKeysImport $Line $User;
+ } on-error={
+ $LogPrint warning $0 ("Failed importing key for user '" . $User . "'.");
+ }
+ :set Continue true;
+ }
+ :if ($Continue = false && $KeyVal->0 = "#") do={
+ :set User [ $EitherOr ([ $ParseKeyValueStore [ :pick $Line 2 [ :len $Line ] ] ]->"user") $User ];
+ :set Continue true;
+ }
+ :if ($Continue = false && [ :len ($KeyVal->0) ] > 0) do={
+ $LogPrint warning $0 ("SSH key of type '" . $KeyVal->0 . "' is not supported.");
+ }
+ } while=([ :len $Keys ] > 0);
+}
diff --git a/mode-button b/mode-button
deleted file mode 100644
index e4d33cc..0000000
--- a/mode-button
+++ /dev/null
@@ -1,76 +0,0 @@
-#!rsc by RouterOS
-# RouterOS script: mode-button
-# Copyright (c) 2018-2022 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 ];
-}
diff --git a/mode-button.rsc b/mode-button.rsc
new file mode 100644
index 0000000..f453f11
--- /dev/null
+++ b/mode-button.rsc
@@ -0,0 +1,81 @@
+#!rsc by RouterOS
+# RouterOS script: mode-button
+# Copyright (c) 2018-2024 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# requires RouterOS, version=7.12
+#
+# act on multiple mode and reset button presses
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/mode-button.md
+
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:do {
+ :local ScriptName [ :jobname ];
+
+ :global ModeButton;
+
+ :global LogPrint;
+
+ :set ($ModeButton->"count") ($ModeButton->"count" + 1);
+
+ :local Scheduler [ /system/scheduler/find where name="_ModeButtonScheduler" ];
+
+ :if ([ :len $Scheduler ] = 0) do={
+ $LogPrint info $ScriptName ("Creating scheduler _ModeButtonScheduler, counting presses...");
+ :global ModeButtonScheduler do={
+ :global ModeButton;
+
+ :global LogPrint;
+ :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 [ find where name="_ModeButtonScheduler" ];
+
+ :if ([ :len $Code ] > 0) do={
+ :if ([ $ValidateSyntax $Code ] = true) do={
+ $LogPrint info $ScriptName ("Acting on " . $Count . " mode-button presses: " . $Code);
+
+ :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={
+ $LogPrint warning $ScriptName ("The code for " . $Count . " mode-button presses failed syntax validation!");
+ }
+ } else={
+ $LogPrint info $ScriptName ("No action defined for " . $Count . " mode-button presses.");
+ }
+ }
+ /system/scheduler/add name="_ModeButtonScheduler" \
+ on-event=":global ModeButtonScheduler; \$ModeButtonScheduler;" interval=3s;
+ } else={
+ $LogPrint debug $ScriptName ("Updating scheduler _ModeButtonScheduler...");
+ /system/scheduler/set $Scheduler start-time=[ /system/clock/get time ];
+ }
+} on-error={ }
diff --git a/netwatch-dns b/netwatch-dns
deleted file mode 100644
index f828de0..0000000
--- a/netwatch-dns
+++ /dev/null
@@ -1,94 +0,0 @@
-#!rsc by RouterOS
-# RouterOS script: netwatch-dns
-# Copyright (c) 2022 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;
- }
-}
diff --git a/netwatch-dns.rsc b/netwatch-dns.rsc
new file mode 100644
index 0000000..50c2b4c
--- /dev/null
+++ b/netwatch-dns.rsc
@@ -0,0 +1,130 @@
+#!rsc by RouterOS
+# RouterOS script: netwatch-dns
+# Copyright (c) 2022-2024 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# requires RouterOS, version=7.12
+#
+# monitor and manage dns/doh with netwatch
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/netwatch-dns.md
+
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:do {
+ :local ScriptName [ :jobname ];
+
+ :global CertificateAvailable;
+ :global EitherOr;
+ :global LogPrint;
+ :global ParseKeyValueStore;
+ :global ScriptLock;
+
+ :if ([ $ScriptLock $ScriptName ] = false) do={
+ :error false;
+ }
+
+ :local SettleTime (5m30s - [ /system/resource/get uptime ]);
+ :if ($SettleTime > 0s) do={
+ $LogPrint info $ScriptName ("System just booted, giving netwatch " . $SettleTime . " to settle.");
+ :error true;
+ }
+
+ :local DnsServers ({});
+ :local DnsFallback ({});
+ :local DnsCurrent [ /ip/dns/get servers ];
+
+ :foreach Host in=[ /tool/netwatch/find where comment~"\\bdns\\b" status="up" ] do={
+ :local HostVal [ /tool/netwatch/get $Host ];
+ :local HostInfo [ $ParseKeyValueStore ($HostVal->"comment") ];
+
+ :if ($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={
+ $LogPrint info $ScriptName ("Updating DNS servers: " . [ :tostr $DnsServers ]);
+ /ip/dns/set servers=$DnsServers;
+ /ip/dns/cache/flush;
+ }
+ } else={
+ :if ([ :len $DnsFallback ] > 0) do={
+ :if ($DnsFallback != $DnsCurrent) do={
+ $LogPrint info $ScriptName ("Updating DNS servers to fallback: " . [ :tostr $DnsFallback ]);
+ /ip/dns/set servers=$DnsFallback;
+ /ip/dns/cache/flush;
+ }
+ }
+ }
+
+ :local DohCurrent [ /ip/dns/get use-doh-server ];
+ :local DohServers ({});
+
+ :foreach Host in=[ /tool/netwatch/find where comment~"\\bdoh\\b" status="up" ] do={
+ :local HostVal [ /tool/netwatch/get $Host ];
+ :local HostInfo [ $ParseKeyValueStore ($HostVal->"comment") ];
+ :local HostName [ /ip/dns/static/find where name address=($HostVal->"host") \
+ (!type or type="A" or type="AAAA") !disabled !dynamic ];
+ :if ([ :len $HostName ] > 0) do={
+ :set HostName [ /ip/dns/static/get ($HostName->0) name ];
+ }
+
+ :if ($HostInfo->"doh" = true && $HostInfo->"disabled" != true) do={
+ :if ([ :len ($HostInfo->"doh-url") ] = 0) do={
+ :set ($HostInfo->"doh-url") ("https://" . [ $EitherOr $HostName ($HostVal->"host") ] . "/dns-query");
+ }
+
+ :if ($DohCurrent = $HostInfo->"doh-url") do={
+ $LogPrint debug $ScriptName ("Current DoH server is still up: " . $DohCurrent);
+ :error true;
+ }
+
+ :set ($DohServers->[ :len $DohServers ]) $HostInfo;
+ }
+ }
+
+ :if ([ :len $DohCurrent ] > 0) do={
+ $LogPrint info $ScriptName ("Current DoH server is down, disabling: " . $DohCurrent);
+ /ip/dns/set use-doh-server="";
+ /ip/dns/cache/flush;
+ }
+
+ :foreach DohServer in=$DohServers do={
+ :if ([ :len ($DohServer->"doh-cert") ] > 0) do={
+ :if ([ $CertificateAvailable ($DohServer->"doh-cert") ] = false) do={
+ $LogPrint warning $ScriptName ("Downloading certificate failed, trying without.");
+ }
+ }
+
+ :local Data false;
+ :do {
+ :set Data ([ /tool/fetch check-certificate=yes-without-crl output=user \
+ http-header-field=({ "accept: application/dns-message" }) \
+ url=(($DohServer->"doh-url") . "?dns=" . [ :convert to=base64 ([ :rndstr length=2 ] . \
+ "\01\00" . "\00\01" . "\00\00" . "\00\00" . "\00\00" . "\09doh-check\05eworm\02de\00" . \
+ "\00\10" . "\00\01") ]) as-value ]->"data");
+ } on-error={
+ $LogPrint warning $ScriptName ("Request to DoH server failed (network or certificate issue): " . \
+ ($DohServer->"doh-url"));
+ }
+
+ :if ($Data != false) do={
+ :if ([ :typeof [ :find $Data "doh-check-OK" ] ] = "num") do={
+ /ip/dns/set use-doh-server=($DohServer->"doh-url") verify-doh-cert=yes;
+ /ip/dns/cache/flush;
+ $LogPrint info $ScriptName ("Setting DoH server: " . ($DohServer->"doh-url"));
+ :error true;
+ } else={
+ $LogPrint warning $ScriptName ("Received unexpected response from DoH server: " . \
+ ($DohServer->"doh-url"));
+ }
+ }
+ }
+} on-error={ }
diff --git a/netwatch-notify b/netwatch-notify
deleted file mode 100644
index f95f426..0000000
--- a/netwatch-notify
+++ /dev/null
@@ -1,180 +0,0 @@
-#!rsc by RouterOS
-# RouterOS script: netwatch-notify
-# Copyright (c) 2020-2022 Christian Hesse <mail@eworm.de>
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
-#
-# monitor netwatch and send notifications
-# https://git.eworm.de/cgit/routeros-scripts/about/doc/netwatch-notify.md
-
-:local 0 "netwatch-notify";
-:global GlobalFunctionsReady;
-:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
-
-:global NetwatchNotify;
-
-:global EitherOr;
-:global IfThenElse;
-:global IsDNSResolving;
-:global LogPrintExit2;
-:global ParseKeyValueStore;
-:global ScriptLock;
-:global SendNotification2;
-:global SymbolForNotification;
-
-:local NetwatchNotifyHook do={
- :local Name [ :tostr $1 ];
- :local Type [ :tostr $2 ];
- :local State [ :tostr $3 ];
- :local Hook [ :tostr $4 ];
-
- :global LogPrintExit2;
- :global ValidateSyntax;
-
- :if ([ $ValidateSyntax $Hook ] = true) do={
- :do {
- [ :parse $Hook ];
- } on-error={
- $LogPrintExit2 warning $0 ("The " . $State . "-hook for " . $Type . " '" . $Name . \
- "' failed to run.") false;
- :return ("The hook failed to run.");
- }
- } else={
- $LogPrintExit2 warning $0 ("The " . $State . "-hook for " . $Type . " '" . $Name . \
- "' failed syntax validation.") false;
- :return ("The hook failed syntax validation.");
- }
-
- $LogPrintExit2 info $0 ("Ran hook on " . $Type . " '" . $Name . "' " . $State . ": " . \
- $Hook) false;
- :return ("Ran hook:\n" . $Hook);
-}
-
-$ScriptLock $0;
-
-:if ([ /system/resource/get uptime ] < 5m) do={
- $LogPrintExit2 info $0 ("System just booted, giving netwatch some time to settle.") true;
-}
-
-:if ([ :typeof $NetwatchNotify ] = "nothing") do={
- :set NetwatchNotify ({});
-}
-
-:foreach Host in=[ /tool/netwatch/find where comment~"notify" !disabled ] do={
- :local HostVal [ /tool/netwatch/get $Host ];
- :local Type [ $IfThenElse ($HostVal->"type" ~ "^(http-get|tcp-conn)\$") "service" "host" ];
- :local HostInfo [ $ParseKeyValueStore ($HostVal->"comment") ];
- :local HostDetails ($HostVal->"host" . \
- [ $IfThenElse ([ :len ($HostInfo->"resolve") ] > 0) (", " . $HostInfo->"resolve") ]);
-
- :if ($HostInfo->"notify" = true && $HostInfo->"disabled" != true) do={
- :local Name [ $EitherOr ($HostInfo->"name") ($HostVal->"name") ];
-
- :local Metric { "count-down"=0; "count-up"=0; "notified"=false; "resolve-failcnt"=0 };
- :if ([ :typeof ($NetwatchNotify->$Name) ] = "array") do={
- :set $Metric ($NetwatchNotify->$Name);
- }
-
- :if ([ :typeof ($HostInfo->"resolve") ] = "str") do={
- :if ([ $IsDNSResolving ] = true) do={
- :do {
- :local Resolve [ :resolve ($HostInfo->"resolve") ];
- :if ($Resolve != $HostVal->"host") do={
- $LogPrintExit2 info $0 ("Name '" . $HostInfo->"resolve" . [ $IfThenElse \
- ($HostInfo->"resolve" != $HostInfo->"name") ("' for " . $Type . " '" . \
- $HostInfo->"name") "" ] . "' resolves to different address " . $Resolve . \
- ", updating.") false;
- /tool/netwatch/set host=$Resolve $Host;
- :set ($Metric->"resolve-failcnt") 0;
- }
- } on-error={
- :set ($Metric->"resolve-failcnt") ($Metric->"resolve-failcnt" + 1);
- :if ($Metric->"resolve-failcnt" = 3) do={
- $LogPrintExit2 warning $0 ("Resolving name '" . $HostInfo->"resolve" . [ $IfThenElse \
- ($HostInfo->"resolve" != $HostInfo->"name") ("' for " . $Type . " '" . \
- $HostInfo->"name") "" ] . "' failed.") false;
- }
- }
- }
- }
-
- :if ($HostVal->"status" = "up") do={
- :local CountDown ($Metric->"count-down");
- :if ($CountDown > 0) do={
- $LogPrintExit2 info $0 \
- ("The " . $Type . " '" . $Name . "' (" . $HostDetails . ") is up.") false;
- :set ($Metric->"count-down") 0;
- }
- :set ($Metric->"count-up") ($Metric->"count-up" + 1);
- :if ($Metric->"notified" = true) do={
- :local Message ("The " . $Type . " '" . $Name . "' (" . $HostDetails . \
- ") is up since " . $HostVal->"since" . ".\n" . \
- "It was down for " . $CountDown . " checks since " . ($Metric->"since") . ".");
- :if ([ :typeof ($HostInfo->"up-hook") ] = "str") do={
- :set Message ($Message . "\n\n" . [ $NetwatchNotifyHook $Name $Type "up" \
- ($HostInfo->"up-hook") ]);
- }
- $SendNotification2 ({ origin=$0; \
- subject=([ $SymbolForNotification "white-heavy-check-mark" ] . "Netwatch Notify: " . \
- $Name . " up"); \
- message=$Message });
- }
- :set ($Metric->"notified") false;
- :set ($Metric->"parent") ($HostInfo->"parent");
- :set ($Metric->"since");
- } else={
- :set ($Metric->"count-down") ($Metric->"count-down" + 1);
- :set ($Metric->"count-up") 0;
- :set ($Metric->"parent") ($HostInfo->"parent");
- :set ($Metric->"since") ($HostVal->"since");
- :local CountDown [ $IfThenElse ([ :tonum ($HostInfo->"count-down") ] > 0) ($HostInfo->"count-down") 5 ];
- :local Parent ($HostInfo->"parent");
- :local ParentUp false;
- :while ([ :len $Parent ] > 0) do={
- :set CountDown ($CountDown + 1);
- :set Parent ($NetwatchNotify->$Parent->"parent");
- }
- :set Parent ($HostInfo->"parent");
- :local ParentNotified false;
- :while ($ParentNotified = false && [ :len $Parent ] > 0) do={
- :set ParentNotified [ $IfThenElse (($NetwatchNotify->$Parent->"notified") = true) \
- true false ];
- :set ParentUp ($NetwatchNotify->$Parent->"count-up");
- :if ($ParentNotified = false) do={
- :set Parent ($NetwatchNotify->$Parent->"parent");
- }
- }
- $LogPrintExit2 [ $IfThenElse ($HostInfo->"no-down-notification" != true) info debug ] $0 \
- ("The " . $Type . " '" . $Name . "' (" . $HostDetails . ") is down for " . \
- $Metric->"count-down" . " checks, " . [ $IfThenElse ($ParentNotified = false) [ $IfThenElse \
- ($Metric->"notified" = true) ("already notified.") ($CountDown - $Metric->"count-down" . \
- " to go.") ] ("parent " . $Type . " " . $Parent . " is down.") ]) false;
- :if ((($CountDown * 2) - ($Metric->"count-down" * 3)) / 2 = 0 && \
- [ :typeof ($HostInfo->"pre-down-hook") ] = "str") do={
- $NetwatchNotifyHook $Name $Type "pre-down" ($HostInfo->"pre-down-hook");
- }
- :if ($ParentNotified = false && $Metric->"count-down" >= $CountDown && \
- ($ParentUp = false || $ParentUp > 2) && $Metric->"notified" != true) do={
- :local Message ("The " . $Type . " '" . $Name . "' (" . $HostDetails . \
- ") is down since " . $HostVal->"since" . ".");
- :if ([ :typeof ($HostInfo->"down-hook") ] = "str") do={
- :set Message ($Message . "\n\n" . [ $NetwatchNotifyHook $Name $Type "down" \
- ($HostInfo->"down-hook") ]);
- }
- :if ($HostInfo->"no-down-notification" != true) do={
- $SendNotification2 ({ origin=$0; \
- subject=([ $SymbolForNotification "cross-mark" ] . "Netwatch Notify: " . \
- $Name . " down"); \
- message=$Message });
- }
- :set ($Metric->"notified") true;
- }
- }
- :set ($NetwatchNotify->$Name) {
- "count-down"=($Metric->"count-down");
- "count-up"=($Metric->"count-up");
- "notified"=($Metric->"notified");
- "parent"=($Metric->"parent");
- "resolve-failcnt"=($Metric->"resolve-failcnt");
- "since"=($Metric->"since") };
- }
-}
diff --git a/netwatch-notify.rsc b/netwatch-notify.rsc
new file mode 100644
index 0000000..bdabe2e
--- /dev/null
+++ b/netwatch-notify.rsc
@@ -0,0 +1,219 @@
+#!rsc by RouterOS
+# RouterOS script: netwatch-notify
+# Copyright (c) 2020-2024 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# requires RouterOS, version=7.12
+#
+# monitor netwatch and send notifications
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/netwatch-notify.md
+
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:do {
+ :local ScriptName [ :jobname ];
+
+ :global NetwatchNotify;
+
+ :global EitherOr;
+ :global IfThenElse;
+ :global IsDNSResolving;
+ :global LogPrint;
+ :global ParseKeyValueStore;
+ :global ScriptFromTerminal;
+ :global ScriptLock;
+ :global SendNotification2;
+ :global SymbolForNotification;
+
+ :local NetwatchNotifyHook do={
+ :local ScriptName [ :tostr $1 ];
+ :local Name [ :tostr $2 ];
+ :local Type [ :tostr $3 ];
+ :local State [ :tostr $4 ];
+ :local Hook [ :tostr $5 ];
+
+ :global LogPrint;
+ :global ValidateSyntax;
+
+ :if ([ $ValidateSyntax $Hook ] = true) do={
+ :do {
+ [ :parse $Hook ];
+ } on-error={
+ $LogPrint warning $ScriptName ("The " . $State . "-hook for " . $Type . " '" . $Name . "' failed to run.");
+ :return ("The hook failed to run.");
+ }
+ } else={
+ $LogPrint warning $ScriptName ("The " . $State . "-hook for " . $Type . " '" . $Name . "' failed syntax validation.");
+ :return ("The hook failed syntax validation.");
+ }
+
+ $LogPrint info $ScriptName ("Ran hook on " . $Type . " '" . $Name . "' " . $State . ": " . $Hook);
+ :return ("Ran hook:\n" . $Hook);
+ }
+
+ :local ResolveExpected do={
+ :local ScriptName [ :tostr $1 ];
+ :local Name [ :tostr $2 ];
+ :local Expected [ :tostr $3 ];
+
+ :global GetRandom20CharAlNum;
+
+ :local FwAddrList ($ScriptName . "-" . [ $GetRandom20CharAlNum ]);
+ /ip/firewall/address-list/add address=$Name list=$FwAddrList dynamic=yes timeout=1s;
+ :delay 20ms;
+ :if ([ :len [ /ip/firewall/address-list/find where list=$FwAddrList address=$Expected ] ] > 0) do={
+ :return true;
+ }
+ /ipv6/firewall/address-list/add address=$Name list=$FwAddrList dynamic=yes timeout=1s;
+ :delay 20ms;
+ :if ([ :len [ /ipv6/firewall/address-list/find where list=$FwAddrList address=$Expected ] ] > 0) do={
+ :return true;
+ }
+
+ :return false;
+ }
+
+ :if ([ $ScriptLock $ScriptName ] = false) do={
+ :error false;
+ }
+
+ :local ScriptFromTerminalCached [ $ScriptFromTerminal $ScriptName ];
+
+ :if ([ :typeof $NetwatchNotify ] = "nothing") do={
+ :set NetwatchNotify ({});
+ }
+
+ :foreach Host in=[ /tool/netwatch/find where comment~"\\bnotify\\b" !disabled status!="unknown" ] do={
+ :local HostVal [ /tool/netwatch/get $Host ];
+ :local Type [ $IfThenElse ($HostVal->"type" ~ "^(https?-get|tcp-conn)\$") "service" "host" ];
+ :local HostInfo [ $ParseKeyValueStore ($HostVal->"comment") ];
+ :local HostDetails ($HostVal->"host" . \
+ [ $IfThenElse ([ :len ($HostInfo->"resolve") ] > 0) (", " . $HostInfo->"resolve") ]);
+
+ :if ($HostInfo->"notify" = true && $HostInfo->"disabled" != true) do={
+ :local Name [ $EitherOr ($HostInfo->"name") ($HostVal->"name") ];
+
+ :local Metric { "count-down"=0; "count-up"=0; "notified"=false; "resolve-failcnt"=0 };
+ :if ([ :typeof ($NetwatchNotify->$Name) ] = "array") do={
+ :set $Metric ($NetwatchNotify->$Name);
+ }
+
+ :if ([ :typeof ($HostInfo->"resolve") ] = "str") do={
+ :if ([ $IsDNSResolving ] = true) do={
+ :do {
+ :local Resolve [ :resolve ($HostInfo->"resolve") ];
+ :if ($Resolve != $HostVal->"host") do={
+ :if ([ $ResolveExpected $ScriptName ($HostInfo->"resolve") ($HostVal->"host") ] = false) do={
+ $LogPrint info $ScriptName ("Name '" . $HostInfo->"resolve" . [ $IfThenElse \
+ ($HostInfo->"resolve" != $HostInfo->"name") ("' for " . $Type . " '" . \
+ $HostInfo->"name") "" ] . "' resolves to different address " . $Resolve . \
+ ", updating.");
+ /tool/netwatch/set host=$Resolve $Host;
+ :set ($Metric->"resolve-failcnt") 0;
+ :set ($HostVal->"status") "unknown";
+ }
+ }
+ } on-error={
+ :set ($Metric->"resolve-failcnt") ($Metric->"resolve-failcnt" + 1);
+ :if ($Metric->"resolve-failcnt" = 3) do={
+ $LogPrint warning $ScriptName ("Resolving name '" . $HostInfo->"resolve" . [ $IfThenElse \
+ ($HostInfo->"resolve" != $HostInfo->"name") ("' for " . $Type . " '" . \
+ $HostInfo->"name") "" ] . "' failed.");
+ }
+ }
+ }
+ }
+
+ :if ($HostVal->"status" = "up") do={
+ :local CountDown ($Metric->"count-down");
+ :if ($CountDown > 0) do={
+ $LogPrint info $ScriptName \
+ ("The " . $Type . " '" . $Name . "' (" . $HostDetails . ") is up.");
+ :set ($Metric->"count-down") 0;
+ }
+ :set ($Metric->"count-up") ($Metric->"count-up" + 1);
+ :if ($Metric->"notified" = true) do={
+ :local Message ("The " . $Type . " '" . $Name . "' (" . $HostDetails . \
+ ") is up since " . $HostVal->"since" . ".\n" . \
+ "It was down for " . $CountDown . " checks since " . ($Metric->"since") . ".");
+ :if ([ :typeof ($HostInfo->"note") ] = "str") do={
+ :set Message ($Message . "\n\nNote:\n" . ($HostInfo->"note"));
+ }
+ :if ([ :typeof ($HostInfo->"up-hook") ] = "str") do={
+ :set Message ($Message . "\n\n" . [ $NetwatchNotifyHook $ScriptName $Name $Type "up" \
+ ($HostInfo->"up-hook") ]);
+ }
+ $SendNotification2 ({ origin=[ $EitherOr ($HostInfo->"origin") $ScriptName ]; silent=($HostInfo->"silent"); \
+ subject=([ $SymbolForNotification "white-heavy-check-mark" ] . "Netwatch Notify: " . $Name . " up"); \
+ message=$Message; link=($HostInfo->"link") });
+ }
+ :set ($Metric->"notified") false;
+ :set ($Metric->"parent") ($HostInfo->"parent");
+ :set ($Metric->"since");
+ }
+
+ :if ($HostVal->"status" = "down") do={
+ :set ($Metric->"count-down") ($Metric->"count-down" + 1);
+ :set ($Metric->"count-up") 0;
+ :set ($Metric->"parent") ($HostInfo->"parent");
+ :set ($Metric->"since") ($HostVal->"since");
+ :local CountDown [ $IfThenElse ([ :tonum ($HostInfo->"count") ] > 0) ($HostInfo->"count") 5 ];
+ :local Parent ($HostInfo->"parent");
+ :local ParentUp false;
+ :while ([ :len $Parent ] > 0) do={
+ :set CountDown ($CountDown + 1);
+ :set Parent ($NetwatchNotify->$Parent->"parent");
+ }
+ :set Parent ($HostInfo->"parent");
+ :local ParentNotified false;
+ :while ($ParentNotified = false && [ :len $Parent ] > 0) do={
+ :set ParentNotified [ $IfThenElse (($NetwatchNotify->$Parent->"notified") = true) \
+ true false ];
+ :set ParentUp ($NetwatchNotify->$Parent->"count-up");
+ :if ($ParentNotified = false) do={
+ :set Parent ($NetwatchNotify->$Parent->"parent");
+ }
+ }
+ :if ($Metric->"notified" = false || $Metric->"count-down" % 120 = 0 || \
+ $ScriptFromTerminalCached = true) do={
+ $LogPrint [ $IfThenElse ($HostInfo->"no-down-notification" != true) info debug ] $ScriptName \
+ ("The " . $Type . " '" . $Name . "' (" . $HostDetails . ") is down for " . \
+ $Metric->"count-down" . " checks, " . [ $IfThenElse ($ParentNotified = false) [ $IfThenElse \
+ ($Metric->"notified" = true) ("already notified.") ($CountDown - $Metric->"count-down" . \
+ " to go.") ] ("parent " . $Type . " " . $Parent . " is down.") ]);
+ }
+ :if ((($CountDown * 2) - ($Metric->"count-down" * 3)) / 2 = 0 && \
+ [ :typeof ($HostInfo->"pre-down-hook") ] = "str") do={
+ $NetwatchNotifyHook $ScriptName $Name $Type "pre-down" ($HostInfo->"pre-down-hook");
+ }
+ :if ($ParentNotified = false && $Metric->"count-down" >= $CountDown && \
+ ($ParentUp = false || $ParentUp > 2) && $Metric->"notified" != true) do={
+ :local Message ("The " . $Type . " '" . $Name . "' (" . $HostDetails . \
+ ") is down since " . $HostVal->"since" . ".");
+ :if ([ :typeof ($HostInfo->"note") ] = "str") do={
+ :set Message ($Message . "\n\nNote:\n" . ($HostInfo->"note"));
+ }
+ :if ([ :typeof ($HostInfo->"down-hook") ] = "str") do={
+ :set Message ($Message . "\n\n" . [ $NetwatchNotifyHook $ScriptName $Name $Type "down" \
+ ($HostInfo->"down-hook") ]);
+ }
+ :if ($HostInfo->"no-down-notification" != true) do={
+ $SendNotification2 ({ origin=[ $EitherOr ($HostInfo->"origin") $ScriptName ]; silent=($HostInfo->"silent"); \
+ subject=([ $SymbolForNotification "cross-mark" ] . "Netwatch Notify: " . $Name . " down"); \
+ message=$Message; link=($HostInfo->"link") });
+ }
+ :set ($Metric->"notified") true;
+ }
+ }
+
+ :set ($NetwatchNotify->$Name) {
+ "count-down"=($Metric->"count-down");
+ "count-up"=($Metric->"count-up");
+ "notified"=($Metric->"notified");
+ "parent"=($Metric->"parent");
+ "resolve-failcnt"=($Metric->"resolve-failcnt");
+ "since"=($Metric->"since") };
+ }
+ }
+} on-error={ }
diff --git a/news-and-changes.rsc b/news-and-changes.rsc
new file mode 100644
index 0000000..1f1094d
--- /dev/null
+++ b/news-and-changes.rsc
@@ -0,0 +1,60 @@
+# News, changes and migration by RouterOS Scripts
+# Copyright (c) 2019-2024 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+
+:global IDonate;
+
+:global IfThenElse;
+:global RequiredRouterOS;
+:global SymbolForNotification;
+
+:local Resource [ /system/resource/get ];
+
+# News, changes and migration up to change 95:
+# https://git.eworm.de/cgit/routeros-scripts/plain/global-config.changes?h=change-95
+
+# Changes for global-config to be added to notification on script updates
+:global GlobalConfigChanges {
+ 96="Added support for notes in 'netwatch-notify', these are included verbatim into the notification.";
+ 97="Modified 'dhcp-to-dns' to always add A records for names with mac address, and optionally add CNAME records if the host name is available.";
+ 98="Extended 'check-certificates' to download new certificate by SubjectAltNames if download by CommonName fails.";
+ 99="Modified 'dhcp-to-dns', which dropped global configuration. Settings moved to dhcp server's network definitions.";
+ 100="The script 'ssh-keys-import' became a module 'mod/ssh-keys-import' with enhanced functionality.";
+ 101="Introduced new script 'fw-addr-lists' to download, import and update firewall address-lists.";
+ 102="Modified 'hotspot-to-wpa' to support non-local (radius) users.";
+ 103="Dropped hard-coded name and timeout from 'hotspot-to-wpa-cleanup', instead a comment is required for dhcp server now.";
+ 104="All relevant scripts were ported to new wifiwave2 and are available for AX devices now!";
+ 105="Extended 'check-routeros-update' to support automatic update from specific neighbor(s).";
+ 106="Modified 'telegram-chat' to make it act on message replies, without activation. Also made it answer a single question mark with a short notice.";
+ 107="Dropped support for non-fixed width font in Telegram notifications.";
+ 108="Enhanced 'log-forward' to list log messages with colorful bullets to indicate severity.";
+ 109="Added support to send notifications via Ntfy (ntfy.sh).";
+ 110="Dropped support for loading scripts from local storage.";
+ 111="Modified 'dhcp-to-dns' to allow multiple records for one mac address.";
+ 112="Enhanced 'mod/ssh-keys-import' to record the fingerprint of keys.";
+ 113="Added helper functions for easier setup to Matrix notification module.";
+ 114="All relevant scripts were ported to new wifi package for RouterOS 7.13 and later. Migration is complex and thus not done automatically!";
+ 115=("Celebrating " . [ $SymbolForNotification "sparkles,star" ] . "1.000 stars " . [ $SymbolForNotification "star,sparkles" ] . "on Github! Please continue starring...");
+ 116=("... and also please keep in mind that it takes a huge amount of time maintaining these scripts. " . [ $IfThenElse ($IDonate != true) \
+ ("Following the donation hint " . [ $SymbolForNotification "arrow-down" "below" ] . "to keep me motivated is much appreciated. Thanks!") \
+ ("Looks like you did donate already. " . [ $SymbolForNotification "heart" "<3" ] . "Much appreciated, thanks!") ]);
+ 117="Enhanced 'packages-update' to support deferred reboot on automatically installed updates.";
+ 118=("RouterOS packages increase in size with each release. This becomes a problem for devices with 16MB storage and below. " . \
+ [ $IfThenElse ($Resource->"total-hdd-space" < 16000000) ("Your " . $Resource->"board-name" . " is specifically affected! ") \
+ [ $IfThenElse ($Resource->"free-hdd-space" > 4000000) ("(Your " . $Resource->"board-name" . " does not suffer this issue.) ") ] ] . \
+ "Huge configuration and lots of scripts give an extra risk. Take care!");
+ 119="Added support for IPv6 to script 'fw-addr-lists'.";
+ 120="Implemented a workaround in 'backup-cloud'. Now script should no longer just crash, but send notification with error.";
+ 121="The 'wifiwave2' scripts are finally gone. Development continues with 'wifi' in RouterOS 7.13 and later.";
+ 122="The global configuration was enhanced to support loading snippets. Configuration can be split off to scripts where name starts with 'global-config-overlay.d/'.";
+ 123="Introduced new function '\$LogPrint', and deprecated '\$LogPrintExit2'. Please update custom scripts if you use it.";
+ 124="Added support for links in 'netwatch-notify', these are added below the formatted notification text.";
+};
+
+# Migration steps to be applied on script updates
+:global GlobalConfigMigration {
+ 97=":local Rec [ /ip/dns/static/find where comment~\"^managed by dhcp-to-dns for \" ]; :if ([ :len \$Rec ] > 0) do={ /ip/dns/static/remove \$Rec; /system/script/run dhcp-to-dns; }";
+ 100=":global ScriptInstallUpdate; :if ([ :len [ /system/script/find where name=\"ssh-keys-import\" source~\"^#!rsc by RouterOS\\n\" ] ] > 0) do={ /system/script/set name=\"mod/ssh-keys-import\" ssh-keys-import; \$ScriptInstallUpdate; }";
+ 104=":global CharacterReplace; :global ScriptInstallUpdate; :foreach Script in={ \"capsman-download-packages\"; \"capsman-rolling-upgrade\"; \"hotspot-to-wpa\"; \"hotspot-to-wpa-cleanup\" } do={ /system/script/set name=(\$Script . \".capsman\") [ find where name=\$Script ]; :foreach Scheduler in=[ /system/scheduler/find where on-event~(\$Script . \"([^-.]|\\\$)\") ] do={ /system/scheduler/set \$Scheduler on-event=[ \$CharacterReplace [ get \$Scheduler on-event ] \$Script (\$Script . \".capsman\") ]; }; }; /ip/hotspot/user/profile/set on-login=\"hotspot-to-wpa.capsman\" [ find where on-login=\"hotspot-to-wpa\" ]; \$ScriptInstallUpdate;";
+ 111=":local Rec [ /ip/dns/static/find where comment~\"^managed by dhcp-to-dns for \" ]; :if ([ :len \$Rec ] > 0) do={ /ip/dns/static/remove \$Rec; /system/script/run dhcp-to-dns; }";
+};
diff --git a/ospf-to-leds b/ospf-to-leds
deleted file mode 100644
index c4acb30..0000000
--- a/ospf-to-leds
+++ /dev/null
@@ -1,35 +0,0 @@
-#!rsc by RouterOS
-# RouterOS script: ospf-to-leds
-# Copyright (c) 2020-2022 Christian Hesse <mail@eworm.de>
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
-#
-# visualize ospf instance state via leds
-# https://git.eworm.de/cgit/routeros-scripts/about/doc/ospf-to-leds.md
-
-:local 0 "ospf-to-leds";
-:global GlobalFunctionsReady;
-:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
-
-:global LogPrintExit2;
-:global ParseKeyValueStore;
-
-:foreach Instance in=[ /routing/ospf/instance/find where comment~"^ospf-to-leds," ] do={
- :local InstanceVal [ /routing/ospf/instance/get $Instance ];
- :local LED ([ $ParseKeyValueStore ($InstanceVal->"comment") ]->"leds");
- :local LEDType [ /system/leds/get [ find where leds=$LED ] type ];
-
- :local NeighborCount 0;
- :foreach Area in=[ /routing/ospf/area/find where instance=($InstanceVal->"name") ] do={
- :local AreaName [ /routing/ospf/area/get $Area name ];
- :set NeighborCount ($NeighborCount + [ :len [ /routing/ospf/neighbor/find where area=$AreaName ] ]);
- }
-
- :if ($NeighborCount > 0 && $LEDType = "off") do={
- $LogPrintExit2 info $0 ("OSPF instance " . $InstanceVal->"name" . " has " . $NeighborCount . " neighbors, led on!") false;
- /system/leds/set type=on [ find where leds=$LED ];
- }
- :if ($NeighborCount = 0 && $LEDType = "on") do={
- $LogPrintExit2 info $0 ("OSPF instance " . $InstanceVal->"name" . " has no neighbors, led off!") false;
- /system/leds/set type=off [ find where leds=$LED ];
- }
-}
diff --git a/ospf-to-leds.rsc b/ospf-to-leds.rsc
new file mode 100644
index 0000000..a22e5a5
--- /dev/null
+++ b/ospf-to-leds.rsc
@@ -0,0 +1,45 @@
+#!rsc by RouterOS
+# RouterOS script: ospf-to-leds
+# Copyright (c) 2020-2024 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# requires RouterOS, version=7.12
+#
+# visualize ospf instance state via leds
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/ospf-to-leds.md
+
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:do {
+ :local ScriptName [ :jobname ];
+
+ :global LogPrint;
+ :global ParseKeyValueStore;
+ :global ScriptLock;
+
+ :if ([ $ScriptLock $ScriptName ] = false) do={
+ :error false;
+ }
+
+ :foreach Instance in=[ /routing/ospf/instance/find where comment~"^ospf-to-leds," ] do={
+ :local InstanceVal [ /routing/ospf/instance/get $Instance ];
+ :local LED ([ $ParseKeyValueStore ($InstanceVal->"comment") ]->"leds");
+ :local LEDType [ /system/leds/get [ find where leds=$LED ] type ];
+
+ :local NeighborCount 0;
+ :foreach Area in=[ /routing/ospf/area/find where instance=($InstanceVal->"name") ] do={
+ :local AreaName [ /routing/ospf/area/get $Area name ];
+ :set NeighborCount ($NeighborCount + [ :len [ /routing/ospf/neighbor/find where area=$AreaName ] ]);
+ }
+
+ :if ($NeighborCount > 0 && $LEDType = "off") do={
+ $LogPrint info $ScriptName ("OSPF instance " . $InstanceVal->"name" . " has " . $NeighborCount . " neighbors, led on!");
+ /system/leds/set type=on [ find where leds=$LED ];
+ }
+ :if ($NeighborCount = 0 && $LEDType = "on") do={
+ $LogPrint info $ScriptName ("OSPF instance " . $InstanceVal->"name" . " has no neighbors, led off!");
+ /system/leds/set type=off [ find where leds=$LED ];
+ }
+ }
+} on-error={ }
diff --git a/packages-update b/packages-update
deleted file mode 100644
index 2922759..0000000
--- a/packages-update
+++ /dev/null
@@ -1,93 +0,0 @@
-#!rsc by RouterOS
-# RouterOS script: packages-update
-# Copyright (c) 2019-2022 Christian Hesse <mail@eworm.de>
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
-#
-# download packages and reboot for installation
-# https://git.eworm.de/cgit/routeros-scripts/about/doc/packages-update.md
-
-:local 0 "packages-update";
-:global GlobalFunctionsReady;
-:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
-
-:global DownloadPackage;
-:global LogPrintExit2;
-:global ScriptFromTerminal;
-:global ScriptLock;
-:global VersionToNum;
-
-$ScriptLock $0;
-
-:local Update [ /system/package/update/get ];
-
-:if ([ :typeof ($Update->"latest-version") ] = "nothing") do={
- $LogPrintExit2 warning $0 ("Latest version is not known.") true;
-}
-
-:if ($Update->"installed-version" = $Update->"latest-version") do={
- $LogPrintExit2 info $0 ("Version " . $Update->"latest-version" . " is already installed.") true;
-}
-
-:local NumInstalled [ $VersionToNum ($Update->"installed-version") ];
-:local NumLatest [ $VersionToNum ($Update->"latest-version") ];
-
-:local DoDowngrade false;
-:if ($NumInstalled > $NumLatest) do={
- :if ([ $ScriptFromTerminal $0 ] = true) do={
- :put "Latest version is older than installed one. Want to downgrade? [y/N]";
- :if (([ /terminal/inkey timeout=60 ] % 32) = 25) do={
- :set DoDowngrade true;
- } else={
- :put "Canceled...";
- }
- } else={
- $LogPrintExit2 warning $0 ("Not installing downgrade automatically.") true;
- }
-}
-
-:foreach Package in=[ /system/package/find where !bundle ] do={
- :local PkgName [ /system/package/get $Package name ];
- :if ([ $DownloadPackage $PkgName ($Update->"latest-version") ] = false) do={
- $LogPrintExit2 error $0 ("Download for package " . $PkgName . " failed, update aborted.") true;
- }
-}
-
-:foreach Script in=[ /system/script/find where source~"\n# provides: backup-script\n" ] do={
- :local ScriptName [ /system/script/get $Script name ];
- :do {
- $LogPrintExit2 info $0 ("Running backup script " . $ScriptName . " before update.") false;
- /system/script/run $Script;
- } on-error={
- $LogPrintExit2 warning $0 ("Running backup script " . $ScriptName . " before update failed!") false;
- :if ([ $ScriptFromTerminal $0 ] = true) do={
- :put "Do you want to continue anyway? [y/N]";
- :if (([ /terminal/inkey timeout=60 ] % 32) = 25) do={
- $LogPrintExit2 info $0 ("User requested to continue anyway.") false;
- } else={
- $LogPrintExit2 info $0 ("Canceled update...") true;
- }
- } else={
- $LogPrintExit2 info $0 ("Canceled non-interactive update.") true;
- }
- }
-}
-
-:if ($DoDowngrade = true) do={
- $LogPrintExit2 info $0 ("Rebooting for downgrade.") false;
- :delay 1s;
- /system/package/downgrade;
-}
-
-:if ([ $ScriptFromTerminal $0 ] = true) do={
- :put "Do you want to (s)chedule reboot or (r)eboot now? [s/R]";
- :if (([ /terminal/inkey timeout=60 ] % 32) = 19) do={
- /system/scheduler/add name="reboot-for-update" start-time=03:00:00 interval=1d \
- on-event=(":global RandomDelay; \$RandomDelay 3600; " . \
- "/system/scheduler/remove reboot-for-update; /system/reboot;");
- $LogPrintExit2 info $0 ("Scheduled reboot for update between 03:00 and 04:00.") true;
- }
-}
-
-$LogPrintExit2 info $0 ("Rebooting for update.") false;
-:delay 1s;
-/system/reboot;
diff --git a/packages-update.rsc b/packages-update.rsc
new file mode 100644
index 0000000..0208b1e
--- /dev/null
+++ b/packages-update.rsc
@@ -0,0 +1,145 @@
+#!rsc by RouterOS
+# RouterOS script: packages-update
+# Copyright (c) 2019-2024 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# requires RouterOS, version=7.13
+#
+# download packages and reboot for installation
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/packages-update.md
+
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:do {
+ :local ScriptName [ :jobname ];
+
+ :global DownloadPackage;
+ :global Grep;
+ :global LogPrint;
+ :global ParseKeyValueStore;
+ :global ScriptFromTerminal;
+ :global ScriptLock;
+ :global VersionToNum;
+
+ :global PackagesUpdateDeferReboot;
+ :global PackagesUpdateBackupFailure;
+
+ :local Schedule do={
+ :local ScriptName [ :tostr $1 ];
+
+ :global GetRandomNumber;
+ :global LogPrint;
+
+ :global RebootForUpdate do={
+ /system/reboot;
+ }
+
+ :local StartTime [ :tostr [ :totime (10800 + [ $GetRandomNumber 7200 ]) ] ];
+ /system/scheduler/add name="_RebootForUpdate" start-time=$StartTime interval=1d \
+ on-event=("/system/scheduler/remove \"_RebootForUpdate\"; " . \
+ ":global RebootForUpdate; \$RebootForUpdate;");
+ $LogPrint info $ScriptName ("Scheduled reboot for update at " . $StartTime . \
+ " local time (" . [ /system/clock/get time-zone-name ] . ").");
+ :return true;
+ }
+
+ :if ([ $ScriptLock $ScriptName ] = false) do={
+ :error false;
+ }
+
+ :local Update [ /system/package/update/get ];
+
+ :if ([ :typeof ($Update->"latest-version") ] = "nothing") do={
+ $LogPrint warning $ScriptName ("Latest version is not known.");
+ :error false;
+ }
+
+ :if ($Update->"installed-version" = $Update->"latest-version") do={
+ $LogPrint info $ScriptName ("Version " . $Update->"latest-version" . " is already installed.");
+ :error true;
+ }
+
+ :local NumInstalled [ $VersionToNum ($Update->"installed-version") ];
+ :local NumLatest [ $VersionToNum ($Update->"latest-version") ];
+
+ :local DoDowngrade false;
+ :if ($NumInstalled > $NumLatest) do={
+ :if ([ $ScriptFromTerminal $ScriptName ] = true) do={
+ :put "Latest version is older than installed one. Want to downgrade? [y/N]";
+ :if (([ /terminal/inkey timeout=60 ] % 32) = 25) do={
+ :set DoDowngrade true;
+ } else={
+ :put "Canceled...";
+ }
+ } else={
+ $LogPrint warning $ScriptName ("Not installing downgrade automatically.");
+ :error false;
+ }
+ }
+
+ :foreach Package in=[ /system/package/find where !bundle ] do={
+ :local PkgName [ /system/package/get $Package name ];
+ :if ([ $DownloadPackage $PkgName ($Update->"latest-version") ] = false) do={
+ $LogPrint error $ScriptName ("Download for package " . $PkgName . " failed, update aborted.");
+ :error false;
+ }
+ }
+
+ :local RunOrder ({});
+ :foreach Script in=[ /system/script/find where source~("\n# provides: backup-script\\b") ] do={
+ :local ScriptVal [ /system/script/get $Script ];
+ :local Store [ $ParseKeyValueStore [ $Grep ($ScriptVal->"source") ("\23 provides: backup-script, ") ] ];
+
+ :set ($RunOrder->($Store->"order" . "-" . $ScriptVal->"name")) ($ScriptVal->"name");
+ }
+
+ :foreach Order,Script in=$RunOrder do={
+ :set PackagesUpdateBackupFailure false;
+ :do {
+ $LogPrint info $ScriptName ("Running backup script " . $Script . " before update.");
+ /system/script/run $Script;
+ } on-error={
+ :set PackagesUpdateBackupFailure true;
+ }
+
+ :if ($PackagesUpdateBackupFailure = true) do={
+ $LogPrint warning $ScriptName ("Running backup script " . $Script . " before update failed!");
+ :if ([ $ScriptFromTerminal $ScriptName ] = true) do={
+ :put "Do you want to continue anyway? [y/N]";
+ :if (([ /terminal/inkey timeout=60 ] % 32) = 25) do={
+ $LogPrint info $ScriptName ("User requested to continue anyway.");
+ } else={
+ $LogPrint info $ScriptName ("Canceled update...");
+ :error false;
+ }
+ } else={
+ $LogPrint warning $ScriptName ("Canceled non-interactive update.");
+ :error false;
+ }
+ }
+ }
+
+ :if ($DoDowngrade = true) do={
+ $LogPrint info $ScriptName ("Rebooting for downgrade.");
+ :delay 1s;
+ /system/package/downgrade;
+ }
+
+ :if ([ $ScriptFromTerminal $ScriptName ] = true) do={
+ :put "Do you want to (s)chedule reboot or (r)eboot now? [s/R]";
+ :if (([ /terminal/inkey timeout=60 ] % 32) = 19) do={
+ $Schedule $ScriptName;
+ :error true;
+ }
+ } else={
+ :if ($PackagesUpdateDeferReboot = true) do={
+ $Schedule $ScriptName;
+ :error true;
+ }
+ }
+
+ $LogPrint info $ScriptName ("Rebooting for update.");
+ :delay 1s;
+ /system/reboot;
+} on-error={ }
diff --git a/ppp-on-up b/ppp-on-up
deleted file mode 100644
index 0529352..0000000
--- a/ppp-on-up
+++ /dev/null
@@ -1,34 +0,0 @@
-#!rsc by RouterOS
-# RouterOS script: ppp-on-up
-# Copyright (c) 2013-2022 Christian Hesse <mail@eworm.de>
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
-#
-# run scripts on ppp up
-# https://git.eworm.de/cgit/routeros-scripts/about/doc/ppp-on-up.md
-
-:local 0 "ppp-on-up";
-:global GlobalFunctionsReady;
-:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
-
-:global LogPrintExit2;
-
-:local Interface $interface;
-
-:if ([ :typeof $Interface ] = "nothing") do={
- $LogPrintExit2 error $0 ("This script is supposed to run from ppp on-up script hook.") true;
-}
-
-:local IntName [ /interface/get $Interface name ];
-$LogPrintExit2 info $0 ("PPP interface " . $IntName . " is up.") false;
-
-/ipv6/dhcp-client/release [ find where interface=$IntName !disabled ];
-
-:foreach Script in=[ /system/script/find where source~("\n# provides: ppp-on-up\n") ] do={
- :local ScriptName [ /system/script/get $Script name ];
- :do {
- $LogPrintExit2 debug $0 ("Running script: " . $ScriptName) false;
- /system/script/run $Script;
- } on-error={
- $LogPrintExit2 warning $0 ("Running script '" . $ScriptName . "' failed!") false;
- }
-}
diff --git a/ppp-on-up.rsc b/ppp-on-up.rsc
new file mode 100644
index 0000000..61766c0
--- /dev/null
+++ b/ppp-on-up.rsc
@@ -0,0 +1,40 @@
+#!rsc by RouterOS
+# RouterOS script: ppp-on-up
+# Copyright (c) 2013-2024 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# requires RouterOS, version=7.12
+#
+# run scripts on ppp up
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/ppp-on-up.md
+
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:do {
+ :local ScriptName [ :jobname ];
+
+ :global LogPrint;
+
+ :local Interface $interface;
+
+ :if ([ :typeof $Interface ] = "nothing") do={
+ $LogPrint error $ScriptName ("This script is supposed to run from ppp on-up script hook.");
+ :error false;
+ }
+
+ :local IntName [ /interface/get $Interface name ];
+ $LogPrint info $ScriptName ("PPP interface " . $IntName . " is up.");
+
+ /ipv6/dhcp-client/release [ find where interface=$IntName !disabled ];
+
+ :foreach Script in=[ /system/script/find where source~("\n# provides: ppp-on-up\n") ] do={
+ :local ScriptName [ /system/script/get $Script name ];
+ :do {
+ $LogPrint debug $ScriptName ("Running script: " . $ScriptName);
+ /system/script/run $Script;
+ } on-error={
+ $LogPrint warning $ScriptName ("Running script '" . $ScriptName . "' failed!");
+ }
+ }
+} on-error={ }
diff --git a/sms-action b/sms-action
deleted file mode 100644
index a1fa4e9..0000000
--- a/sms-action
+++ /dev/null
@@ -1,31 +0,0 @@
-#!rsc by RouterOS
-# RouterOS script: sms-action
-# Copyright (c) 2018-2022 Christian Hesse <mail@eworm.de>
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
-#
-# run action on received SMS
-# https://git.eworm.de/cgit/routeros-scripts/about/doc/sms-action.md
-
-:local 0 "sms-action";
-:global GlobalFunctionsReady;
-:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
-
-:global SmsAction;
-
-:global LogPrintExit2;
-:global ValidateSyntax;
-
-:local Action $action;
-
-:if ([ :typeof $Action ] = "nothing") do={
- $LogPrintExit2 error $0 ("This script is supposed to run from SMS hook with action=...") true;
-}
-
-:local Code ($SmsAction->$Action);
-:if ([ $ValidateSyntax $Code ] = true) do={
- :log info ("Acting on SMS action '" . $Action . "': " . $Code);
- :delay 1s;
- [ :parse $Code ];
-} else={
- $LogPrintExit2 warning $0 ("The code for action '" . $Action . "' failed syntax validation!") false;
-}
diff --git a/sms-action.rsc b/sms-action.rsc
new file mode 100644
index 0000000..4c37565
--- /dev/null
+++ b/sms-action.rsc
@@ -0,0 +1,37 @@
+#!rsc by RouterOS
+# RouterOS script: sms-action
+# Copyright (c) 2018-2024 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# requires RouterOS, version=7.12
+#
+# run action on received SMS
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/sms-action.md
+
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:do {
+ :local ScriptName [ :jobname ];
+
+ :global SmsAction;
+
+ :global LogPrint;
+ :global ValidateSyntax;
+
+ :local Action $action;
+
+ :if ([ :typeof $Action ] = "nothing") do={
+ $LogPrint error $ScriptName ("This script is supposed to run from SMS hook with action=...");
+ :error false;
+ }
+
+ :local Code ($SmsAction->$Action);
+ :if ([ $ValidateSyntax $Code ] = true) do={
+ :log info ("Acting on SMS action '" . $Action . "': " . $Code);
+ :delay 1s;
+ [ :parse $Code ];
+ } else={
+ $LogPrint warning $ScriptName ("The code for action '" . $Action . "' failed syntax validation!");
+ }
+} on-error={ }
diff --git a/sms-forward b/sms-forward
deleted file mode 100644
index aa2e71c..0000000
--- a/sms-forward
+++ /dev/null
@@ -1,62 +0,0 @@
-#!rsc by RouterOS
-# RouterOS script: sms-forward
-# Copyright (c) 2013-2022 Christian Hesse <mail@eworm.de>
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
-#
-# forward SMS to e-mail
-# https://git.eworm.de/cgit/routeros-scripts/about/doc/sms-forward.md
-
-:local 0 "sms-forward";
-:global GlobalFunctionsReady;
-:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
-
-:global Identity;
-
-:global IfThenElse;
-:global LogPrintExit2;
-:global ScriptLock;
-:global SendNotification2;
-:global SymbolForNotification;
-:global WaitFullyConnected;
-
-$ScriptLock $0;
-
-:if ([ /tool/sms/get receive-enabled ] = false) do={
- $LogPrintExit2 warning $0 ("Receiving of SMS is not enabled.") true;
-}
-
-$WaitFullyConnected;
-
-:local Settings [ /tool/sms/get ];
-
-# forward SMS in a loop
-:while ([ :len [ /tool/sms/inbox/find ] ] > 0) do={
- :local Phone [ /tool/sms/inbox/get ([ find ]->0) phone ];
- :local Messages "";
- :local Delete ({});
-
- :foreach Sms in=[ /tool/sms/inbox/find where phone=$Phone ] do={
- :local SmsVal [ /tool/sms/inbox/get $Sms ];
-
- :if ($Phone = $Settings->"allowed-number" && \
- ($SmsVal->"message")~("^:cmd " . $Settings->"secret" . " script ")) do={
- $LogPrintExit2 debug $0 ("Removing SMS, which started a script.") false;
- /tool/sms/inbox/remove $Sms;
- } else={
- :set Messages ($Messages . "\n\nOn " . $SmsVal->"timestamp" . \
- " type " . $SmsVal->"type" . ":\n" . $SmsVal->"message");
- :set Delete ($Delete, $Sms);
- }
- }
-
- :if ([ :len $Messages ] > 0) do={
- :local Count [ :len $Delete ];
- $SendNotification2 ({ origin=$0; \
- subject=([ $SymbolForNotification "incoming-envelope" ] . "SMS Forwarding from " . $Phone); \
- message=("Received " . [ $IfThenElse ($Count = 1) "this message" ("these " . $Count . " messages") ] . \
- " by " . $Identity . " from " . $Phone . ":" . $Messages) });
- :foreach Sms in=$Delete do={
- /tool/sms/inbox/remove $Sms;
- }
- }
-}
diff --git a/sms-forward.rsc b/sms-forward.rsc
new file mode 100644
index 0000000..b0966c3
--- /dev/null
+++ b/sms-forward.rsc
@@ -0,0 +1,95 @@
+#!rsc by RouterOS
+# RouterOS script: sms-forward
+# Copyright (c) 2013-2024 Christian Hesse <mail@eworm.de>
+# Anatoly Bubenkov <bubenkoff@gmail.com>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# requires RouterOS, version=7.12
+#
+# forward SMS to e-mail
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/sms-forward.md
+
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:do {
+ :local ScriptName [ :jobname ];
+
+ :global Identity;
+ :global SmsForwardHooks;
+
+ :global IfThenElse;
+ :global LogPrint;
+ :global LogPrintOnce;
+ :global ScriptLock;
+ :global SendNotification2;
+ :global SymbolForNotification;
+ :global ValidateSyntax;
+ :global WaitFullyConnected;
+
+ :if ([ $ScriptLock $ScriptName ] = false) do={
+ :error false;
+ }
+
+ :if ([ /tool/sms/get receive-enabled ] = false) do={
+ $LogPrintOnce warning $ScriptName ("Receiving of SMS is not enabled.");
+ :error false;
+ }
+
+ $WaitFullyConnected;
+
+ :local Settings [ /tool/sms/get ];
+
+ :if ([ /interface/lte/get ($Settings->"port") running ] != true) do={
+ $LogPrint info $ScriptName ("The LTE interface is not in running state, skipping.");
+ :error true;
+ }
+
+ # forward SMS in a loop
+ :while ([ :len [ /tool/sms/inbox/find ] ] > 0) do={
+ :local Phone [ /tool/sms/inbox/get ([ find ]->0) phone ];
+ :local Messages "";
+ :local Delete ({});
+
+ :foreach Sms in=[ /tool/sms/inbox/find where phone=$Phone ] do={
+ :local SmsVal [ /tool/sms/inbox/get $Sms ];
+
+ :if ($Phone = $Settings->"allowed-number" && \
+ ($SmsVal->"message")~("^:cmd " . $Settings->"secret" . " script ")) do={
+ $LogPrint debug $ScriptName ("Removing SMS, which started a script.");
+ /tool/sms/inbox/remove $Sms;
+ } else={
+ :set Messages ($Messages . "\n\nOn " . $SmsVal->"timestamp" . \
+ " type " . $SmsVal->"type" . ":\n" . $SmsVal->"message");
+ :foreach Hook in=$SmsForwardHooks do={
+ :if ($Phone~($Hook->"allowed-number") && ($SmsVal->"message")~($Hook->"match")) do={
+ :if ([ $ValidateSyntax ($Hook->"command") ] = true) do={
+ $LogPrint info $ScriptName ("Running hook '" . $Hook->"match" . "': " . $Hook->"command");
+ :do {
+ :local Command [ :parse ($Hook->"command") ];
+ $Command Phone=$Phone Message=($SmsVal->"message");
+ :set Messages ($Messages . "\n\nRan hook '" . $Hook->"match" . "':\n" . $Hook->"command");
+ } on-error={
+ $LogPrint warning $ScriptName ("The code for hook '" . $Hook->"match" . "' failed to run!");
+ }
+ } else={
+ $LogPrint warning $ScriptName ("The code for hook '" . $Hook->"match" . "' failed syntax validation!");
+ }
+ }
+ }
+ :set Delete ($Delete, $Sms);
+ }
+ }
+
+ :if ([ :len $Messages ] > 0) do={
+ :local Count [ :len $Delete ];
+ $SendNotification2 ({ origin=$ScriptName; \
+ subject=([ $SymbolForNotification "incoming-envelope" ] . "SMS Forwarding from " . $Phone); \
+ message=("Received " . [ $IfThenElse ($Count = 1) "this message" ("these " . $Count . " messages") ] . \
+ " by " . $Identity . " from " . $Phone . ":" . $Messages) });
+ :foreach Sms in=$Delete do={
+ /tool/sms/inbox/remove $Sms;
+ }
+ }
+ }
+} on-error={ }
diff --git a/ssh-keys-import b/ssh-keys-import
deleted file mode 100644
index 70ddf3d..0000000
--- a/ssh-keys-import
+++ /dev/null
@@ -1,11 +0,0 @@
-#!rsc by RouterOS
-# RouterOS script: ssh-keys-import
-# Copyright (c) 2013-2022 Christian Hesse <mail@eworm.de>
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
-#
-# import ssh keys from file
-# https://git.eworm.de/cgit/routeros-scripts/about/doc/ssh-keys-import.md
-
-:foreach Key in=[ /file/find where type="ssh key" ] do={
- /user/ssh-key/import user=admin public-key-file=[ /file/get $Key name ];
-}
diff --git a/super-mario-theme b/super-mario-theme.rsc
index ae52fd1..63308b0 100644
--- a/super-mario-theme
+++ b/super-mario-theme.rsc
@@ -1,6 +1,6 @@
#!rsc by RouterOS
# RouterOS script: super-mario-theme
-# Copyright (c) 2013-2022 Christian Hesse <mail@eworm.de>
+# Copyright (c) 2013-2024 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# play Super Mario theme
diff --git a/telegram-chat.rsc b/telegram-chat.rsc
new file mode 100644
index 0000000..9ae5967
--- /dev/null
+++ b/telegram-chat.rsc
@@ -0,0 +1,180 @@
+#!rsc by RouterOS
+# RouterOS script: telegram-chat
+# Copyright (c) 2023-2024 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# requires RouterOS, version=7.12
+#
+# use Telegram to chat with your Router and send commands
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/telegram-chat.md
+
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:do {
+ :local ScriptName [ :jobname ];
+
+ :global Identity;
+ :global TelegramChatActive;
+ :global TelegramChatGroups;
+ :global TelegramChatId;
+ :global TelegramChatIdsTrusted;
+ :global TelegramChatOffset;
+ :global TelegramChatRunTime;
+ :global TelegramMessageIDs;
+ :global TelegramRandomDelay;
+ :global TelegramTokenId;
+
+ :global CertificateAvailable;
+ :global EitherOr;
+ :global EscapeForRegEx;
+ :global GetRandom20CharAlNum;
+ :global IfThenElse;
+ :global LogPrint;
+ :global MAX;
+ :global MIN;
+ :global MkDir;
+ :global ParseJson;
+ :global RandomDelay;
+ :global ScriptLock;
+ :global SendTelegram2;
+ :global SymbolForNotification;
+ :global ValidateSyntax;
+ :global WaitForFile;
+ :global WaitFullyConnected;
+
+ :if ([ $ScriptLock $ScriptName ] = false) do={
+ :error false;
+ }
+
+ $WaitFullyConnected;
+
+ :if ([ :typeof $TelegramChatOffset ] != "array") do={
+ :set TelegramChatOffset { 0; 0; 0 };
+ }
+ :if ([ :typeof $TelegramRandomDelay ] != "num") do={
+ :set TelegramRandomDelay 0;
+ }
+
+ :if ([ $CertificateAvailable "Go Daddy Secure Certificate Authority - G2" ] = false) do={
+ $LogPrint warning $ScriptName ("Downloading required certificate failed.");
+ :error false;
+ }
+
+ $RandomDelay $TelegramRandomDelay;
+
+ :local Data false;
+ :for I from=1 to=4 do={
+ :if ($Data = false) do={
+ :do {
+ :set Data ([ /tool/fetch check-certificate=yes-without-crl output=user \
+ ("https://api.telegram.org/bot" . $TelegramTokenId . "/getUpdates?offset=" . \
+ $TelegramChatOffset->0 . "&allowed_updates=%5B%22message%22%5D") as-value ]->"data");
+ :set TelegramRandomDelay [ $MAX 0 ($TelegramRandomDelay - 1) ];
+ } on-error={
+ :if ($I < 4) do={
+ $LogPrint debug $ScriptName ("Fetch failed, " . $I . ". try.");
+ :set TelegramRandomDelay [ $MIN 15 ($TelegramRandomDelay + 5) ];
+ :delay (($I * $I) . "s");
+ }
+ }
+ }
+ }
+
+ :if ($Data = false) do={
+ $LogPrint warning $ScriptName ("Failed getting updates from Telegram.");
+ :error false;
+ }
+
+ :local UpdateID 0;
+ :local Uptime [ /system/resource/get uptime ];
+ :foreach UpdateArray in=([ $ParseJson $Data ]->"result") do={
+ :local Update [ $ParseJson $UpdateArray ];
+ :set UpdateID ($Update->"update_id");
+ :local Message [ $ParseJson ($Update->"message") ];
+ :local IsReply [ :len ($Message->"reply_to_message") ];
+ :local IsMyReply ($TelegramMessageIDs->([ $ParseJson ($Message->"reply_to_message") ]->"message_id"));
+ :if (($IsMyReply = 1 || $TelegramChatOffset->0 > 0 || $Uptime > 5m) && $UpdateID >= $TelegramChatOffset->2) do={
+ :local Trusted false;
+ :local Chat [ $ParseJson ($Message->"chat") ];
+ :local From [ $ParseJson ($Message->"from") ];
+
+ :foreach IdsTrusted in=($TelegramChatId, $TelegramChatIdsTrusted) do={
+ :if ($From->"id" = $IdsTrusted || $From->"username" = $IdsTrusted) do={
+ :set Trusted true;
+ }
+ }
+
+ :if ($Trusted = true) do={
+ :local Done false;
+ :if ($Message->"text" = "?") do={
+ $LogPrint info $ScriptName ("Sending notice for update " . $UpdateID . ".");
+ $SendTelegram2 ({ origin=$ScriptName; chatid=($Chat->"id"); silent=true; replyto=($Message->"message_id"); \
+ subject=([ $SymbolForNotification "speech-balloon" ] . "Telegram Chat"); \
+ message=("Online" . [ $IfThenElse $TelegramChatActive " (and active!)" ] . ", awaiting your commands!") });
+ :set Done true;
+ }
+ :if ($Done = false && [ :pick ($Message->"text") 0 1 ] = "!") do={
+ :if ($Message->"text" ~ ("^! *(" . [ $EscapeForRegEx $Identity ] . "|@" . $TelegramChatGroups . ")\$")) do={
+ :set TelegramChatActive true;
+ } else={
+ :set TelegramChatActive false;
+ }
+ $LogPrint info $ScriptName ("Now " . [ $IfThenElse $TelegramChatActive "active" "passive" ] . \
+ " from update " . $UpdateID . "!");
+ :set Done true;
+ }
+ :if ($Done = false && ($IsMyReply = 1 || ($IsReply = 0 && $TelegramChatActive = true)) && [ :len ($Message->"text") ] > 0) do={
+ :if ([ $ValidateSyntax ($Message->"text") ] = true) do={
+ :local State "";
+ :local File ("tmpfs/telegram-chat/" . [ $GetRandom20CharAlNum 6 ]);
+ :if ([ $MkDir "tmpfs/telegram-chat" ] = false) do={
+ $LogPrint error $ScriptName ("Failed creating directory!");
+ :error false;
+ }
+ $LogPrint info $ScriptName ("Running command from update " . $UpdateID . ": " . $Message->"text");
+ :execute script=(":do {\n" . $Message->"text" . "\n} on-error={ /file/add name=\"" . $File . ".failed\" };" . \
+ "/file/add name=\"" . $File . ".done\"") file=($File . "\00");
+ :if ([ $WaitForFile ($File . ".done") [ $EitherOr $TelegramChatRunTime 20s ] ] = false) do={
+ :set State ([ $SymbolForNotification "warning-sign" ] . "The command did not finish, still running in background.\n\n");
+ }
+ :if ([ :len [ /file/find where name=($File . ".failed") ] ] > 0) do={
+ :set State ([ $SymbolForNotification "cross-mark" ] . "The command failed with an error!\n\n");
+ }
+ :local Content [ /file/get $File contents ];
+ $SendTelegram2 ({ origin=$ScriptName; chatid=($Chat->"id"); silent=true; replyto=($Message->"message_id"); \
+ subject=([ $SymbolForNotification "speech-balloon" ] . "Telegram Chat"); \
+ message=([ $SymbolForNotification "gear" ] . "Command:\n" . $Message->"text" . "\n\n" . \
+ $State . [ $IfThenElse ([ :len $Content ] > 0) \
+ ([ $SymbolForNotification "memo" ] . "Output:\n" . $Content) [ $IfThenElse ([ /file/get $File size ] > 0) \
+ ([ $SymbolForNotification "warning-sign" ] . "Output exceeds file read size.") \
+ ([ $SymbolForNotification "memo" ] . "No output.") ] ]) });
+ /file/remove "tmpfs/telegram-chat";
+ } else={
+ $LogPrint info $ScriptName ("The command from update " . $UpdateID . " failed syntax validation!");
+ $SendTelegram2 ({ origin=$ScriptName; chatid=($Chat->"id"); silent=false; replyto=($Message->"message_id"); \
+ subject=([ $SymbolForNotification "speech-balloon" ] . "Telegram Chat"); \
+ message=([ $SymbolForNotification "gear" ] . "Command:\n" . $Message->"text" . "\n\n" . \
+ [ $SymbolForNotification "cross-mark" ] . "The command failed syntax validation!") });
+ }
+ }
+ } else={
+ :local MessageText ("Received a message from untrusted contact " . \
+ [ $IfThenElse ([ :len ($From->"username") ] = 0) "without username" ("'" . $From->"username" . "'") ] . \
+ " (ID " . $From->"id" . ") in update " . $UpdateID . "!");
+ :if ($Message->"text" ~ ("^! *" . [ $EscapeForRegEx $Identity ] . "\$")) do={
+ $LogPrint warning $ScriptName $MessageText;
+ $SendTelegram2 ({ origin=$ScriptName; chatid=($Chat->"id"); silent=false; replyto=($Message->"message_id"); \
+ subject=([ $SymbolForNotification "speech-balloon" ] . "Telegram Chat"); \
+ message=("You are not trusted.") });
+ } else={
+ $LogPrint info $ScriptName $MessageText;
+ }
+ }
+ } else={
+ $LogPrint debug $ScriptName ("Already handled update " . $UpdateID . ".");
+ }
+ }
+ :set TelegramChatOffset ([ :pick $TelegramChatOffset 1 3 ], \
+ [ $IfThenElse ($UpdateID >= $TelegramChatOffset->2) ($UpdateID + 1) ($TelegramChatOffset->2) ]);
+} on-error={ }
diff --git a/unattended-lte-firmware-upgrade b/unattended-lte-firmware-upgrade.rsc
index fafda62..904f952 100644
--- a/unattended-lte-firmware-upgrade
+++ b/unattended-lte-firmware-upgrade.rsc
@@ -1,6 +1,6 @@
#!rsc by RouterOS
# RouterOS script: unattended-lte-firmware-upgrade
-# Copyright (c) 2018-2022 Christian Hesse <mail@eworm.de>
+# Copyright (c) 2018-2024 Christian Hesse <mail@eworm.de>
# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
#
# schedule unattended lte firmware upgrade
@@ -24,13 +24,17 @@
:set LTEFirmwareUpgrade;
/system/scheduler/remove ($1 . "-firmware-upgrade");
- /interface/lte/firmware-upgrade $1 upgrade=yes;
- :log info ("LTE firmware upgrade on '" . $1 . "' finished, waiting for reset.");
- :delay 240s;
- :local Firmware [ /interface/lte/firmware-upgrade $1 once as-value ];
- :if (($Firmware->"installed") != ($Firmware->"latest")) do={
- :log warning ("LTE firmware versions still differ. Resetting again...");
- /interface/lte/at-chat $1 input="AT+RESET";
+ :do {
+ /interface/lte/firmware-upgrade $1 upgrade=yes;
+ :log info ("LTE firmware upgrade on '" . $1 . "' finished, waiting for reset.");
+ :delay 240s;
+ :local Firmware [ /interface/lte/firmware-upgrade $1 once as-value ];
+ :if (($Firmware->"installed") != ($Firmware->"latest")) do={
+ :log warning ("LTE firmware versions still differ. Resetting again...");
+ /interface/lte/at-chat $1 input="AT+RESET";
+ }
+ } on-error={
+ :log error ("LTE firmware upgrade on '" . $1 . "' failed.");
}
}
diff --git a/update-gre-address b/update-gre-address
deleted file mode 100644
index d1402e3..0000000
--- a/update-gre-address
+++ /dev/null
@@ -1,31 +0,0 @@
-#!rsc by RouterOS
-# RouterOS script: update-gre-address
-# Copyright (c) 2013-2022 Christian Hesse <mail@eworm.de>
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
-#
-# update gre interface remote address with dynamic address from
-# ipsec remote peer
-# https://git.eworm.de/cgit/routeros-scripts/about/doc/update-gre-address.md
-
-:local 0 "update-gre-address";
-:global GlobalFunctionsReady;
-:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
-
-:global LogPrintExit2;
-
-/interface/gre/set remote-address=0.0.0.0 disabled=yes [ find where !running !disabled ];
-
-:foreach Peer in=[ /ip/ipsec/active-peers/find ] do={
- :local PeerVal [ /ip/ipsec/active-peers/get $Peer ];
- :local GreInt [ /interface/gre/find where comment=$PeerVal->"id" ];
- :if ([ :len $GreInt ] > 0) do={
- :local GreIntVal [ /interface/gre/get $GreInt ];
- :if ([ :typeof ($PeerVal->"dynamic-address") ] = "str" && \
- ($PeerVal->"dynamic-address" != $GreIntVal->"remote-address" || \
- $GreIntVal->"disabled" = true)) do={
- $LogPrintExit2 info $0 ("Updating remote address for interface " . $GreIntVal->"name" . " to " . $PeerVal->"dynamic-address") false;
- /interface/gre/set remote-address=0.0.0.0 disabled=yes [ find where remote-address=$PeerVal->"dynamic-address" name!=$GreIntVal->"name" ];
- /interface/gre/set $GreInt remote-address=($PeerVal->"dynamic-address") disabled=no;
- }
- }
-}
diff --git a/update-gre-address.rsc b/update-gre-address.rsc
new file mode 100644
index 0000000..74967cd
--- /dev/null
+++ b/update-gre-address.rsc
@@ -0,0 +1,42 @@
+#!rsc by RouterOS
+# RouterOS script: update-gre-address
+# Copyright (c) 2013-2024 Christian Hesse <mail@eworm.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# requires RouterOS, version=7.12
+#
+# update gre interface remote address with dynamic address from
+# ipsec remote peer
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/update-gre-address.md
+
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:do {
+ :local ScriptName [ :jobname ];
+
+ :global CharacterReplace;
+ :global LogPrint;
+ :global ScriptLock;
+
+ :if ([ $ScriptLock $ScriptName ] = false) do={
+ :error false;
+ }
+
+ /interface/gre/set remote-address=0.0.0.0 disabled=yes [ find where !running !disabled ];
+
+ :foreach Peer in=[ /ip/ipsec/active-peers/find ] do={
+ :local PeerVal [ /ip/ipsec/active-peers/get $Peer ];
+ :local GreInt [ /interface/gre/find where comment=($PeerVal->"id") or comment=[ $CharacterReplace ($PeerVal->"id") "CN=" "" ] ];
+ :if ([ :len $GreInt ] > 0) do={
+ :local GreIntVal [ /interface/gre/get $GreInt ];
+ :if ([ :typeof ($PeerVal->"dynamic-address") ] = "str" && \
+ ($PeerVal->"dynamic-address" != $GreIntVal->"remote-address" || \
+ $GreIntVal->"disabled" = true)) do={
+ $LogPrint info $ScriptName ("Updating remote address for interface " . $GreIntVal->"name" . " to " . $PeerVal->"dynamic-address");
+ /interface/gre/set remote-address=0.0.0.0 disabled=yes [ find where remote-address=$PeerVal->"dynamic-address" name!=$GreIntVal->"name" ];
+ /interface/gre/set $GreInt remote-address=($PeerVal->"dynamic-address") disabled=no;
+ }
+ }
+ }
+} on-error={ }
diff --git a/update-tunnelbroker b/update-tunnelbroker
deleted file mode 100644
index 0075273..0000000
--- a/update-tunnelbroker
+++ /dev/null
@@ -1,48 +0,0 @@
-#!rsc by RouterOS
-# RouterOS script: update-tunnelbroker
-# Copyright (c) 2013-2022 Christian Hesse <mail@eworm.de>
-# Michael Gisbers <michael@gisbers.de>
-# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
-#
-# provides: ppp-on-up
-#
-# update local address of tunnelbroker interface
-# https://git.eworm.de/cgit/routeros-scripts/about/doc/update-tunnelbroker.md
-
-:local 0 "update-tunnelbroker";
-:global GlobalFunctionsReady;
-:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
-
-:global CertificateAvailable;
-:global LogPrintExit2;
-:global ParseKeyValueStore;
-
-:if ([ /ip/cloud/get ddns-enabled ] != true) do={
- $LogPrintExit2 error $0 ("IP cloud DDNS is not enabled.") true;
-}
-
-# Get the current ip address from cloud
-/ip/cloud/force-update;
-:while ([ /ip/cloud/get status ] != "updated") do={
- :delay 1s;
-}
-:local PublicAddress [ /ip/cloud/get public-address ];
-
-:foreach Interface in=[ /interface/6to4/find where comment~"^tunnelbroker" !disabled ] do={
- :local InterfaceVal [ /interface/6to4/get $Interface ];
-
- :if ($PublicAddress != $InterfaceVal->"local-address") do={
- :local Comment [ $ParseKeyValueStore ($InterfaceVal->"comment") ];
-
- :if ([ $CertificateAvailable "Starfield Secure Certificate Authority - G2" ] = false) do={
- $LogPrintExit2 error $0 ("Downloading required certificate failed.") true;
- }
- $LogPrintExit2 info $0 ("Local address changed, sending UPDATE to tunnelbroker! New address: " . $PublicAddress) false;
- /tool/fetch check-certificate=yes-without-crl \
- ("https://ipv4.tunnelbroker.net/nic/update\?hostname=" . $Comment->"id") \
- user=($Comment->"user") password=($Comment->"pass") output=none as-value;
- /interface/6to4/set $Interface local-address=$PublicAddress;
- } else={
- $LogPrintExit2 debug $0 ("All tunnelbroker configuration is up to date for interface " . $InterfaceVal->"name" . ".") false;
- }
-}
diff --git a/update-tunnelbroker.rsc b/update-tunnelbroker.rsc
new file mode 100644
index 0000000..c76b7ec
--- /dev/null
+++ b/update-tunnelbroker.rsc
@@ -0,0 +1,67 @@
+#!rsc by RouterOS
+# RouterOS script: update-tunnelbroker
+# Copyright (c) 2013-2024 Christian Hesse <mail@eworm.de>
+# Michael Gisbers <michael@gisbers.de>
+# https://git.eworm.de/cgit/routeros-scripts/about/COPYING.md
+#
+# provides: ppp-on-up
+# requires RouterOS, version=7.12
+#
+# update local address of tunnelbroker interface
+# https://git.eworm.de/cgit/routeros-scripts/about/doc/update-tunnelbroker.md
+
+:global GlobalFunctionsReady;
+:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
+
+:do {
+ :local ScriptName [ :jobname ];
+
+ :global CertificateAvailable;
+ :global LogPrint;
+ :global ParseKeyValueStore;
+ :global ScriptLock;
+
+ :if ([ $ScriptLock $ScriptName ] = false) do={
+ :error false;
+ }
+
+ :if ([ $CertificateAvailable "Starfield Secure Certificate Authority - G2" ] = false) do={
+ $LogPrint error $ScriptName ("Downloading required certificate failed.");
+ :error false;
+ }
+
+ :foreach Interface in=[ /interface/6to4/find where comment~"^tunnelbroker" !disabled ] do={
+ :local Data false;
+ :local InterfaceVal [ /interface/6to4/get $Interface ];
+ :local Comment [ $ParseKeyValueStore ($InterfaceVal->"comment") ];
+
+ :for I from=2 to=0 do={
+ :if ($Data = false) do={
+ :do {
+ :set Data ([ /tool/fetch check-certificate=yes-without-crl \
+ ("https://ipv4.tunnelbroker.net/nic/update?hostname=" . $Comment->"id") \
+ user=($Comment->"user") password=($Comment->"pass") output=user as-value ]->"data");
+ } on-error={
+ $LogPrint debug $ScriptName ("Failed downloading, " . $I . " retries pending.");
+ :delay 2s;
+ }
+ }
+ }
+
+ :if (!($Data ~ "^(good|nochg) ")) do={
+ $LogPrint error $ScriptName ("Failed sending the local address to tunnelbroker or unexpected response!");
+ :error false;
+ }
+
+ :local PublicAddress [ :pick $Data ([ :find $Data " " ] + 1) [ :find $Data "\n" ] ];
+
+ :if ($PublicAddress != $InterfaceVal->"local-address") do={
+ :if ([ :len [ /ip/address find where address~("^" . $PublicAddress . "/") ] ] < 1) do={
+ $LogPrint warning $ScriptName ("The address " . $PublicAddress . " is not configured on your device. NAT by ISP?");
+ }
+
+ $LogPrint info $ScriptName ("Local address changed, updating tunnel configuration with address: " . $PublicAddress);
+ /interface/6to4/set $Interface local-address=$PublicAddress;
+ }
+ }
+} on-error={ }