Compare commits
86 commits
main
...
routeros-v
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2cda1e9c4c | ||
|
|
2ef06e4533 | ||
|
|
d4305b53bf | ||
|
|
c6adece271 | ||
|
|
fe7543cef5 | ||
|
|
646a7f8c11 | ||
|
|
eaf972d618 | ||
|
|
79665d29ce | ||
|
|
06bd3a96b5 | ||
|
|
ef437367a7 | ||
|
|
23ba2ce67a | ||
|
|
68fdcaf259 | ||
|
|
27f7ef169f | ||
|
|
cdb698acbf | ||
|
|
1b3f2620a7 | ||
|
|
a5c233b28c | ||
|
|
5f0a7efe1e | ||
|
|
1b5f261f3d | ||
|
|
58f7782477 | ||
|
|
e49e454381 | ||
|
|
357618a1a5 | ||
|
|
af589df82c | ||
|
|
c84017de2c | ||
|
|
087e7bc3b7 | ||
|
|
2b647bef80 | ||
|
|
0479f59aa4 | ||
|
|
4cda1281f1 | ||
|
|
006e054517 | ||
|
|
d9c4e1e8d5 | ||
|
|
2e8f457109 | ||
|
|
7a714ad182 | ||
|
|
e7481f3ca8 | ||
|
|
f5465d3e34 | ||
|
|
2c2a3013f3 | ||
|
|
82c0e1c44c | ||
|
|
58f5a4fced | ||
|
|
1cddf5590d | ||
|
|
5570258ce8 | ||
|
|
d21cd2271f | ||
|
|
0695c99318 | ||
|
|
fab05ecd7d | ||
|
|
98b08bcbd2 | ||
|
|
ddb181fbc2 | ||
|
|
e25250e27b | ||
|
|
b35e39b0fa | ||
|
|
c5cc72de0e | ||
|
|
6336da6bd7 | ||
|
|
184959bc77 | ||
|
|
bf6c7d27df | ||
|
|
b11a83aac2 | ||
|
|
d0699857c9 | ||
|
|
a7da21e185 | ||
|
|
a45b8d7d8c | ||
|
|
3f6b5595f3 | ||
|
|
82d45eca08 | ||
|
|
fed4757eaa | ||
|
|
9419e34077 | ||
|
|
39871f2221 | ||
|
|
d1c4eb2303 | ||
|
|
10bb0a9f52 | ||
|
|
cd1de8145b | ||
|
|
06bdce1378 | ||
|
|
17b3e04d41 | ||
|
|
798fd7cc86 | ||
|
|
d1c9fe4dce | ||
|
|
5e7735a507 | ||
|
|
5f52e3e3f4 | ||
|
|
2f965630fa | ||
|
|
a9805365d2 | ||
|
|
b60dcb5fad | ||
|
|
3dd3452303 | ||
|
|
943b60bfe3 | ||
|
|
32cc55f7d2 | ||
|
|
c95e816148 | ||
|
|
0c607a8f9c | ||
|
|
c03da561cd | ||
|
|
8d49babb01 | ||
|
|
b2f45c0350 | ||
|
|
52bd08b750 | ||
|
|
221b0409d5 | ||
|
|
0a9d0473f3 | ||
|
|
9024e20c0b | ||
|
|
5363df3568 | ||
|
|
25338ca384 | ||
|
|
ee9818e34d | ||
|
|
7d5c967995 |
10
.gitignore
vendored
|
|
@ -1,13 +1,3 @@
|
||||||
# backup and temporary files
|
|
||||||
*~
|
*~
|
||||||
|
|
||||||
# patches and related files
|
|
||||||
*.orig
|
|
||||||
*.patch
|
*.patch
|
||||||
*.rej
|
|
||||||
|
|
||||||
# html files (as generated from markdown)
|
|
||||||
*.html
|
*.html
|
||||||
|
|
||||||
# Mac OS X folder settings file
|
|
||||||
.DS_Store
|
|
||||||
|
|
|
||||||
50
BRANCHES.md
|
|
@ -1,50 +0,0 @@
|
||||||
Installing from branches
|
|
||||||
========================
|
|
||||||
|
|
||||||
[](https://github.com/eworm-de/routeros-scripts/stargazers)
|
|
||||||
[](https://github.com/eworm-de/routeros-scripts/network)
|
|
||||||
[](https://github.com/eworm-de/routeros-scripts/watchers)
|
|
||||||
[](https://mikrotik.com/download/changelogs/)
|
|
||||||
[](https://t.me/routeros_scripts)
|
|
||||||
[](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 "base-url=https://rsc.eworm.de/next/";
|
|
||||||
|
|
||||||
## Switch existing script
|
|
||||||
|
|
||||||
Alternatively switch an existing script to update from `next` branch:
|
|
||||||
|
|
||||||
/system/script/set comment="base-url=https://rsc.eworm.de/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 ScriptUpdatesBaseUrl "https://rsc.eworm.de/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)
|
|
||||||
|
Before Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 22 KiB |
|
|
@ -1,82 +0,0 @@
|
||||||
Certificate name from browser
|
|
||||||
=============================
|
|
||||||
|
|
||||||
[](https://github.com/eworm-de/routeros-scripts/stargazers)
|
|
||||||
[](https://github.com/eworm-de/routeros-scripts/network)
|
|
||||||
[](https://github.com/eworm-de/routeros-scripts/watchers)
|
|
||||||
[](https://mikrotik.com/download/changelogs/)
|
|
||||||
[](https://t.me/routeros_scripts)
|
|
||||||
[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)
|
|
||||||
|
|
||||||
[⬅️ Go back to main README](README.md)
|
|
||||||
|
|
||||||
All well known desktop, mobile and server operating systems come with a
|
|
||||||
certificate store that is populated with a set of well known and trusted
|
|
||||||
certificates, acting as *trust anchors*.
|
|
||||||
|
|
||||||
However RouterOS does not, still sometimes a specific certificate is
|
|
||||||
required to properly verify a chain of trust. One example is downloading
|
|
||||||
the scripts from this repository with `fetch` command, thus the very
|
|
||||||
first step of [installation](README.md#the-long-way-in-detail) is importing
|
|
||||||
the certificate.
|
|
||||||
|
|
||||||
The scripts can install additional certificates when required. This happens
|
|
||||||
from this repository if available, or from [mkcert.org](https://mkcert.org)
|
|
||||||
as a fallback.
|
|
||||||
|
|
||||||
Get the certificate's CommonName
|
|
||||||
--------------------------------
|
|
||||||
|
|
||||||
But how to determine what certificate may be required? Often easiest way
|
|
||||||
is to use a desktop browser to get that information. This demonstration uses
|
|
||||||
[Mozilla Firefox](https://www.mozilla.org/firefox/).
|
|
||||||
|
|
||||||
Let's assume we want to make sure the certificate for
|
|
||||||
[git.eworm.de](https://git.eworm.de/) is available. Open that page in the
|
|
||||||
browser, then click the *lock* icon in addressbar, followed by "*Connection
|
|
||||||
secure*".
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
The dialog will change, click "*More information*".
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
A new window opens, click the button "*View Certificate*". (That window
|
|
||||||
can be closed now.)
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
A new tab opens, showing information on the server certificate and its
|
|
||||||
chain of trust. The leftmost certificate is what we are interested in.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
Now we know that "`ISRG Root X2`" is required, some scripts need just
|
|
||||||
that information.
|
|
||||||
|
|
||||||
Import a certificate by CommonName
|
|
||||||
----------------------------------
|
|
||||||
|
|
||||||
Running the function `$CertificateAvailable` with that name as parameter
|
|
||||||
makes sure the certificate is available in the device's store:
|
|
||||||
|
|
||||||
$CertificateAvailable "ISRG Root X2";
|
|
||||||
|
|
||||||
If the certificate is actually available already nothing happens, and there
|
|
||||||
is no output. Otherwise the certificate is downloaded and imported.
|
|
||||||
|
|
||||||
If importing a certificate with that exact name fails a warning is given
|
|
||||||
and nothing is actually imported.
|
|
||||||
|
|
||||||
See also
|
|
||||||
--------
|
|
||||||
|
|
||||||
* [Download, import and update firewall address-lists](doc/fw-addr-lists.md)
|
|
||||||
* [Manage DNS and DoH servers from netwatch](doc/netwatch-dns.md)
|
|
||||||
* [Send notifications via Matrix](doc/mod/notification-matrix.md)
|
|
||||||
* [Send notifications via Ntfy](doc/mod/notification-ntfy.md)
|
|
||||||
|
|
||||||
---
|
|
||||||
[⬅️ Go back to main README](README.md)
|
|
||||||
[⬆️ Go back to top](#top)
|
|
||||||
|
|
@ -1,29 +1,18 @@
|
||||||
Past Contributions
|
Past Contributions
|
||||||
==================
|
==================
|
||||||
|
|
||||||
[](https://github.com/eworm-de/routeros-scripts/stargazers)
|
[◀ Go back to main README](README.md)
|
||||||
[](https://github.com/eworm-de/routeros-scripts/network)
|
|
||||||
[](https://github.com/eworm-de/routeros-scripts/watchers)
|
|
||||||
[](https://mikrotik.com/download/changelogs/)
|
|
||||||
[](https://t.me/routeros_scripts)
|
|
||||||
[](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!
|
||||||
|
|
||||||
Thanks a lot for your contributions! ❤️
|
|
||||||
|
|
||||||
## Patches
|
## Patches
|
||||||
|
|
||||||
These persons contributed code or documentation. See the git history
|
These persons contributed code or documentation. See the git history
|
||||||
for details!
|
for details!
|
||||||
|
|
||||||
* [Anatoly Bubenkov](mailto:bubenkoff@gmail.com) (@bubenkoff)
|
|
||||||
* [Ben Harris](mailto:mail@bharr.is) (@bharrisau)
|
* [Ben Harris](mailto:mail@bharr.is) (@bharrisau)
|
||||||
* [Daniel Ziegenberg](mailto:daniel@ziegenberg.at) (@ziegenberg)
|
* [Daniel Ziegenberg](mailto:daniel@ziegenberg.at) (@ziegenberg)
|
||||||
* [Ignacio Serrano](mailto:ignic@ignic.com) (@ignic)
|
|
||||||
* [Michael Gisbers](mailto:michael@gisbers.de) (@mgisbers)
|
* [Michael Gisbers](mailto:michael@gisbers.de) (@mgisbers)
|
||||||
* [Miquel Bonastre](mailto:mbonastre@yahoo.com) (@mbonastre)
|
|
||||||
* @netravnen
|
|
||||||
* [netztrip](mailto:dave-tvg@netztrip.de) (@netztrip)
|
* [netztrip](mailto:dave-tvg@netztrip.de) (@netztrip)
|
||||||
* [Stefan Müller](mailto:stefan.mueller.83@gmail.com) (@PackElend)
|
* [Stefan Müller](mailto:stefan.mueller.83@gmail.com) (@PackElend)
|
||||||
|
|
||||||
|
|
@ -33,31 +22,20 @@ Add yourself to the list,
|
||||||
[donate with PayPal](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)!
|
[donate with PayPal](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)!
|
||||||
|
|
||||||
* Abdul Mannan Abbasi
|
* Abdul Mannan Abbasi
|
||||||
* Andrea Ruffini Perico
|
|
||||||
* Andrew Cox
|
* Andrew Cox
|
||||||
* Christoph Boss (@Kampfwurst)
|
* Christoph Boss (@Kampfwurst)
|
||||||
* Daniel Ziegenberg (@ziegenberg)
|
|
||||||
* Devin Dean (@dd2594gh)
|
* Devin Dean (@dd2594gh)
|
||||||
* Evaldo Gardenal
|
* Evaldo Gardenal
|
||||||
* Florian Estraviz
|
|
||||||
* Giorgio Bikos
|
|
||||||
* Harold Schoemaker
|
|
||||||
* Hugo BV
|
|
||||||
* Klaus Michael Rübsam
|
* Klaus Michael Rübsam
|
||||||
* Leonardo Valeri Manera
|
|
||||||
* Linux-Schmie.de Michael Gisbers
|
* Linux-Schmie.de Michael Gisbers
|
||||||
* Manuel Kuhn
|
* Manuel Kuhn
|
||||||
* Marek Čábák
|
* Marek Čábák
|
||||||
* Oleksandr Yukhymchuk
|
* Oleksandr Yukhymchuk
|
||||||
* Peter Holtkamp
|
* Peter Holtkamp
|
||||||
* Peter Ponzel
|
|
||||||
* Reiner Vehrenkamp
|
* Reiner Vehrenkamp
|
||||||
* Richard Österreicher
|
|
||||||
* Simon Hitzemann
|
|
||||||
* Sunny Chu (@sunnychuchu)
|
* Sunny Chu (@sunnychuchu)
|
||||||
* Ulrich Wessendorf
|
|
||||||
* Zac Kornilakis
|
* Zac Kornilakis
|
||||||
|
|
||||||
---
|
---
|
||||||
[⬅️ Go back to main README](README.md)
|
[◀ Go back to main README](README.md)
|
||||||
[⬆️ Go back to top](#top)
|
[▲ Go back to top](#top)
|
||||||
|
|
|
||||||
63
DEBUG.md
|
|
@ -1,63 +0,0 @@
|
||||||
Debug output and logs
|
|
||||||
=====================
|
|
||||||
|
|
||||||
[](https://github.com/eworm-de/routeros-scripts/stargazers)
|
|
||||||
[](https://github.com/eworm-de/routeros-scripts/network)
|
|
||||||
[](https://github.com/eworm-de/routeros-scripts/watchers)
|
|
||||||
[](https://mikrotik.com/download/changelogs/)
|
|
||||||
[](https://t.me/routeros_scripts)
|
|
||||||
[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)
|
|
||||||
|
|
||||||
[⬅️ Go back to main README](README.md)
|
|
||||||
|
|
||||||
Sometimes scripts do not behave as expected. In these cases debug output
|
|
||||||
or logs can help.
|
|
||||||
|
|
||||||
## Debug output
|
|
||||||
|
|
||||||
Run this command in a terminal:
|
|
||||||
|
|
||||||
:set PrintDebug true;
|
|
||||||
|
|
||||||
You will then see debug output when running the script from terminal.
|
|
||||||
|
|
||||||
To revert to default output run:
|
|
||||||
|
|
||||||
:set PrintDebug false;
|
|
||||||
|
|
||||||
### Debug output for specific script
|
|
||||||
|
|
||||||
Even having debug output for a specific script or function only (or a
|
|
||||||
set of) is possible. To enable debug output for `telegram-chat` run:
|
|
||||||
|
|
||||||
:set ($PrintDebugOverride->"telegram-chat") true;
|
|
||||||
|
|
||||||
## Debug logs
|
|
||||||
|
|
||||||
The debug info can go to system log. To make it show up in `memory` run:
|
|
||||||
|
|
||||||
/system/logging/add topics=script,debug action=memory;
|
|
||||||
|
|
||||||
Other actions (`disk`, `email`, `remote` or `support`) can be used as
|
|
||||||
well. I do not recommend using `echo` - use [debug output](#debug-output)
|
|
||||||
instead.
|
|
||||||
|
|
||||||
Disable or remote that setting to restore regular logging.
|
|
||||||
|
|
||||||
## Verbose output
|
|
||||||
|
|
||||||
Specific scripts can generate huge amount of output. These do use a function
|
|
||||||
`$LogPrintVerbose`, which is declared, but has no code, intentionally.
|
|
||||||
|
|
||||||
If you *really* want that output set the function to be the same as
|
|
||||||
`$LogPrint`:
|
|
||||||
|
|
||||||
:set LogPrintVerbose $LogPrint;
|
|
||||||
|
|
||||||
To revert that change just run:
|
|
||||||
|
|
||||||
:set LogPrintVerbose;
|
|
||||||
|
|
||||||
---
|
|
||||||
[⬅️ Go back to main README](README.md)
|
|
||||||
[⬆️ Go back to top](#top)
|
|
||||||
|
|
@ -1,53 +1,36 @@
|
||||||
Initial commands
|
Initial commands
|
||||||
================
|
================
|
||||||
|
|
||||||
[](https://github.com/eworm-de/routeros-scripts/stargazers)
|
[◀ Go back to main README](README.md)
|
||||||
[](https://github.com/eworm-de/routeros-scripts/network)
|
|
||||||
[](https://github.com/eworm-de/routeros-scripts/watchers)
|
|
||||||
[](https://mikrotik.com/download/changelogs/)
|
|
||||||
[](https://t.me/routeros_scripts)
|
|
||||||
[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)
|
|
||||||
|
|
||||||
[⬅️ Go back to main README](README.md)
|
> ⚠️ **Warning**: These commands are inteneded for initial setup. If you are
|
||||||
|
|
||||||
> ⚠️ **Warning**: These command are inteneded for initial setup. If you are
|
|
||||||
> not aware of the procedure please follow
|
> not aware of the procedure please follow
|
||||||
> [the long way in detail](README.md#the-long-way-in-detail).
|
> [the long way in detail](README.md#the-long-way-in-detail).
|
||||||
|
|
||||||
Run the complete base installation:
|
Run the complete base installation:
|
||||||
|
|
||||||
{
|
{
|
||||||
/tool/fetch "https://git.eworm.de/cgit/routeros-scripts/plain/certs/ISRG-Root-X2.pem" dst-path="isrg-root-x2.pem" as-value;
|
/ tool fetch "https://git.eworm.de/cgit/routeros-scripts/plain/certs/ISRG-Root-X2.pem" dst-path="isrg-root-x2.pem" as-value;
|
||||||
:delay 1s;
|
:delay 1s;
|
||||||
/certificate/import file-name="isrg-root-x2.pem" passphrase="";
|
/ certificate import file-name=isrg-root-x2.pem passphrase="";
|
||||||
:if ([ :len [ /certificate/find where fingerprint="69729b8e15a86efc177a57afb7171dfc64add28c2fca8cf1507e34453ccb1470" ] ] != 1) do={
|
:if ([ :len [ / certificate find where fingerprint="69729b8e15a86efc177a57afb7171dfc64add28c2fca8cf1507e34453ccb1470" ] ] != 1) do={
|
||||||
:error "Something is wrong with your certificates!";
|
:error "Something is wrong with your certificates!";
|
||||||
};
|
};
|
||||||
|
/ file remove "isrg-root-x2.pem";
|
||||||
:delay 1s;
|
: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={
|
:foreach Script in={ "global-config"; "global-config-overlay"; "global-functions" } do={
|
||||||
/system/script/remove [ find where name=$Script ];
|
/ system script add name=$Script source=([ / tool fetch check-certificate=yes-without-crl ("https://git.eworm.de/cgit/routeros-scripts/plain/" . $Script . "\?h=routeros-v6") output=user as-value]->"data");
|
||||||
/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 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; }";
|
||||||
/system/scheduler/add name="global-scripts" start-time=startup on-event="/system/script { run global-config; run global-functions; }";
|
|
||||||
:global CertificateNameByCN;
|
:global CertificateNameByCN;
|
||||||
$CertificateNameByCN "ISRG Root X2";
|
$CertificateNameByCN "ISRG Root X2";
|
||||||
};
|
}
|
||||||
|
|
||||||
Then continue setup with
|
Optional to update the scripts automatically:
|
||||||
[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 main README](README.md)
|
||||||
[⬆️ Go back to top](#top)
|
[▲ Go back to top](#top)
|
||||||
|
|
|
||||||
27
Makefile
|
|
@ -2,35 +2,24 @@
|
||||||
# template scripts -> final scripts
|
# template scripts -> final scripts
|
||||||
# markdown files -> html files
|
# markdown files -> html files
|
||||||
|
|
||||||
CAPSMAN = $(wildcard *.capsman.rsc)
|
TEMPLATE = $(wildcard *.template)
|
||||||
LOCAL = $(wildcard *.local.rsc)
|
CAPSMAN = $(TEMPLATE:.template=.capsman)
|
||||||
WIFI = $(wildcard *.wifi.rsc)
|
LOCAL = $(TEMPLATE:.template=.local)
|
||||||
|
|
||||||
MARKDOWN = $(wildcard *.md doc/*.md doc/mod/*.md)
|
MARKDOWN = $(wildcard *.md doc/*.md doc/mod/*.md)
|
||||||
HTML = $(MARKDOWN:.md=.html)
|
HTML = $(MARKDOWN:.md=.html)
|
||||||
|
|
||||||
all: $(CAPSMAN) $(LOCAL) $(WIFI) $(HTML)
|
all: $(CAPSMAN) $(LOCAL) $(HTML)
|
||||||
|
|
||||||
%.html: %.md Makefile
|
%.html: %.md Makefile
|
||||||
markdown $< | sed 's/href="\([-_\./[:alnum:]]*\)\.md"/href="\1.html"/g' > $@
|
markdown $< | sed 's/href="\([-_\./[:alnum:]]*\)\.md"/href="\1.html"/g' > $@
|
||||||
|
|
||||||
%.capsman.rsc: %.template.rsc Makefile
|
%.local: %.template Makefile
|
||||||
sed -e '/\/interface\/wifi\//d' -e '/\/interface\/wireless\//d' -e 's|%TEMPL%|.capsman|' \
|
sed -e '/\/ caps-man/d' -e 's|%PATH%|interface wireless|' -e 's|%TEMPL%|$(suffix $@)|' \
|
||||||
-e '/^# NOT \/caps-man\/ #$$/,/^# NOT \/caps-man\/ #$$/d' \
|
|
||||||
-e '/^# !!/,/^# !!/c # !! Do not edit this file, it is generated from template!' \
|
-e '/^# !!/,/^# !!/c # !! Do not edit this file, it is generated from template!' \
|
||||||
< $< > $@
|
< $< > $@
|
||||||
|
|
||||||
%.local.rsc: %.template.rsc Makefile
|
%.capsman: %.template Makefile
|
||||||
sed -e '/\/caps-man\//d' -e '/\/interface\/wifi\//d' -e 's|%TEMPL%|.local|' \
|
sed -e '/\/ interface wireless/d' -e 's/%PATH%/caps-man/' -e 's/%TEMPL%/$(suffix $@)/' \
|
||||||
-e '/^# NOT \/interface\/wireless\/ #$$/,/^# NOT \/interface\/wireless\/ #$$/d' \
|
|
||||||
-e '/^# !!/,/^# !!/c # !! Do not edit this file, it is generated from template!' \
|
-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!' \
|
|
||||||
< $< > $@
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -f $(HTML)
|
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 7.9 KiB After Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 4.5 KiB |
BIN
README.d/05-edit-global-config-overlay.avif
Normal file
|
After Width: | Height: | Size: 6.4 KiB |
|
Before Width: | Height: | Size: 1.9 KiB |
BIN
README.d/06-run-and-schedule-scripts.avif
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 5 KiB |
BIN
README.d/07-schedule-update.avif
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 2.4 KiB |
BIN
README.d/08-update-scripts.avif
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
README.d/09-install-scripts.avif
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 2.4 KiB |
BIN
README.d/10-schedule-script.avif
Normal file
|
After Width: | Height: | Size: 2 KiB |
|
Before Width: | Height: | Size: 4 KiB |
BIN
README.d/11-setup-lease-script.avif
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
BIN
README.d/12-install-custom-script.avif
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 7.5 KiB |
|
Before Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 1.1 KiB |
|
|
@ -1,3 +1,3 @@
|
||||||
#!rsc by RouterOS
|
#!rsc by RouterOS
|
||||||
|
|
||||||
:put ("Hello World from " . [ /system/identity/get name ] . "!");
|
:put ("Hello World from " . [ / system identity get name ] . "!");
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 14 KiB |
195
README.md
|
|
@ -4,7 +4,7 @@ RouterOS Scripts
|
||||||
[](https://github.com/eworm-de/routeros-scripts/stargazers)
|
[](https://github.com/eworm-de/routeros-scripts/stargazers)
|
||||||
[](https://github.com/eworm-de/routeros-scripts/network)
|
[](https://github.com/eworm-de/routeros-scripts/network)
|
||||||
[](https://github.com/eworm-de/routeros-scripts/watchers)
|
[](https://github.com/eworm-de/routeros-scripts/watchers)
|
||||||
[](https://mikrotik.com/download/changelogs/)
|
[](https://mikrotik.com/download/changelogs/)
|
||||||
[](https://t.me/routeros_scripts)
|
[](https://t.me/routeros_scripts)
|
||||||
[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)
|
[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=A4ZXBD6YS2W8J)
|
||||||
|
|
||||||
|
|
@ -21,33 +21,11 @@ to manage RouterOS devices or extend their functionality.
|
||||||
Requirements
|
Requirements
|
||||||
------------
|
------------
|
||||||
|
|
||||||
### Software (RouterOS)
|
|
||||||
|
|
||||||
Latest version of the scripts require recent RouterOS to function properly.
|
Latest version of the scripts require recent RouterOS to function properly.
|
||||||
Make sure to install latest updates before you begin. If new functionality
|
Make sure to install latest updates before you begin.
|
||||||
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.
|
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!
|
|
||||||
|
|
||||||
Starting with RouterOS 7.17 the
|
|
||||||
[device-mode](https://help.mikrotik.com/docs/spaces/ROS/pages/93749258/Device-mode)
|
|
||||||
has been extended to give more fine-grained control over what features are
|
|
||||||
available. You need to enable `scheduler` and `fetch` at least, specific
|
|
||||||
scripts may require additional features.
|
|
||||||
|
|
||||||
### 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
|
Initial setup
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
|
|
@ -75,31 +53,27 @@ download the certificates. If you intend to download the scripts from a
|
||||||
different location (for example from github.com) install the corresponding
|
different location (for example from github.com) install the corresponding
|
||||||
certificate chain.
|
certificate chain.
|
||||||
|
|
||||||
/tool/fetch "https://git.eworm.de/cgit/routeros-scripts/plain/certs/ISRG-Root-X2.pem" dst-path="isrg-root-x2.pem";
|
/ tool fetch "https://git.eworm.de/cgit/routeros-scripts/plain/certs/ISRG-Root-X2.pem" dst-path="isrg-root-x2.pem";
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Note that the commands above do *not* verify server certificate, so if you
|
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
|
want to be safe download with your workstations's browser and transfer the
|
||||||
file to your MikroTik device.
|
files to your MikroTik device.
|
||||||
|
|
||||||
* [ISRG Root X2](https://letsencrypt.org/certs/isrg-root-x2.pem)
|
* [ISRG Root X2](https://letsencrypt.org/certs/isrg-root-x2.pem)
|
||||||
|
|
||||||
Then we import the certificate.
|
Then we import the certificates.
|
||||||
|
|
||||||
/certificate/import file-name="isrg-root-x2.pem" passphrase="";
|
/ certificate import file-name=isrg-root-x2.pem passphrase="";
|
||||||
|
|
||||||
Do not worry that the command is not shown - that happens because it contains
|
|
||||||
a sensitive property, the passphrase.
|
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
For basic verification we rename the certificate and print it by
|
For basic verification we rename the certificate and print the count. Make
|
||||||
fingerprint. Make sure exactly this one certificate ("*ISRG-Root-X2*")
|
sure the certificate count is **one**.
|
||||||
is shown.
|
|
||||||
|
|
||||||
/certificate/set name="ISRG-Root-X2" [ find where common-name="ISRG Root X2" ];
|
/ certificate set name="ISRG-Root-X2" [ find where common-name="ISRG Root X2" ];
|
||||||
/certificate/print proplist=name,fingerprint where fingerprint="69729b8e15a86efc177a57afb7171dfc64add28c2fca8cf1507e34453ccb1470";
|
/ certificate print count-only where fingerprint="69729b8e15a86efc177a57afb7171dfc64add28c2fca8cf1507e34453ccb1470";
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
@ -111,56 +85,49 @@ date and time is set correctly!
|
||||||
|
|
||||||
Now let's download the main scripts and add them in configuration on the fly.
|
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 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"); };
|
: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 . "\?h=routeros-v6") output=user as-value]->"data"); };
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
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; }";
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
### 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;";
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
Editing configuration
|
|
||||||
---------------------
|
|
||||||
|
|
||||||
The configuration needs to be tweaked for your needs. Edit
|
The configuration needs to be tweaked for your needs. Edit
|
||||||
`global-config-overlay`, copy relevant configuration from
|
`global-config-overlay`, copy configuration from
|
||||||
[`global-config`](global-config.rsc) (the one without `-overlay`).
|
[`global-config`](global-config) (the one without `-overlay`).
|
||||||
Save changes and exit with `Ctrl-o`.
|
Save changes and exit with `Ctrl-o`.
|
||||||
|
|
||||||
/system/script/edit global-config-overlay source;
|
/ system script edit global-config-overlay source;
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Additionally creating configuration snippets is supported. The script name
|
And finally load configuration and functions and add the scheduler.
|
||||||
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.
|
|
||||||
|
|
||||||
To apply your changes run `global-config`, which will automatically load
|
/ system script { run global-config; run global-functions; };
|
||||||
the overlay as well:
|
/ system scheduler add name="global-scripts" start-time=startup on-event="/ system script { run global-config; run global-functions; }";
|
||||||
|
|
||||||
/system/script/run global-config;
|

|
||||||
|
|
||||||

|
The last step is optional: Add this scheduler **only** if you want the scripts
|
||||||
|
to be updated automatically!
|
||||||
|
|
||||||
This last step is required when ever you make changes to your configuration.
|
/ system scheduler add name="ScriptInstallUpdate" start-time=startup interval=1d on-event=":global ScriptInstallUpdate; \$ScriptInstallUpdate;";
|
||||||
|
|
||||||
> ℹ️ **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:
|
### Changes for RouterOS v6
|
||||||
> `/system/script/set source=[ :tocrlf [ get global-config-overlay source ] ] global-config-overlay;`
|
|
||||||
|
Let's consider RouterOS v6 being legacy. If you want to stay with RouterOS
|
||||||
|
v6 for some time add these lines to your `global-config-overlay`, if missing:
|
||||||
|
|
||||||
|
# Use branch routeros-v6 with RouterOS v6:
|
||||||
|
:global ScriptUpdatesUrlSuffix "\?h=routeros-v6";
|
||||||
|
|
||||||
|
Then reload the configuration.
|
||||||
|
|
||||||
|
### Changes for RouterOS v7
|
||||||
|
|
||||||
|
RouterOS v7 is the future, and default branch `main` expects it. Just drop
|
||||||
|
`$ScriptUpdatesUrlSuffix` from your `global-config-overlay` to use that.
|
||||||
|
|
||||||
|
Then reload the configuration and continue below to update scripts.
|
||||||
|
|
||||||
Updating scripts
|
Updating scripts
|
||||||
----------------
|
----------------
|
||||||
|
|
@ -170,12 +137,7 @@ everything is up-to-date it will not produce any output.
|
||||||
|
|
||||||
$ScriptInstallUpdate;
|
$ScriptInstallUpdate;
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
If the update includes news or requires configuration changes a notification
|
|
||||||
is sent - in addition to terminal output and log messages.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
Adding a script
|
Adding a script
|
||||||
---------------
|
---------------
|
||||||
|
|
@ -185,29 +147,29 @@ a comma separated list of script names.
|
||||||
|
|
||||||
$ScriptInstallUpdate check-certificates,check-routeros-update;
|
$ScriptInstallUpdate check-certificates,check-routeros-update;
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Scheduler and events
|
Scheduler and events
|
||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
Most scripts are designed to run regularly from
|
Most scripts are designed to run regularly from
|
||||||
[scheduler](https://wiki.mikrotik.com/wiki/Manual:System/Scheduler). We just
|
[scheduler](https://wiki.mikrotik.com/wiki/Manual:System/Scheduler). We just
|
||||||
added `check-routeros-update`, so let's run it daily to make sure not to
|
added `check-routeros-update`, so let's run it every hour to make sure not to
|
||||||
miss an update.
|
miss an update.
|
||||||
|
|
||||||
/system/scheduler/add name="check-routeros-update" interval=1d start-time=startup on-event="/system/script/run check-routeros-update;";
|
/ system scheduler add name="check-routeros-update" interval=1h on-event="/ system script run check-routeros-update;";
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Some events can run a script. If you want your DHCP hostnames to be available
|
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
|
in DNS use `dhcp-to-dns` with the events from dhcp server. For a regular
|
||||||
cleanup add a scheduler entry.
|
cleanup add a scheduler entry.
|
||||||
|
|
||||||
$ScriptInstallUpdate dhcp-to-dns,lease-script;
|
$ScriptInstallUpdate dhcp-to-dns,lease-script;
|
||||||
/ip/dhcp-server/set lease-script=lease-script [ find ];
|
/ 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;";
|
/ system scheduler add name="dhcp-to-dns" interval=5m on-event="/ system script run dhcp-to-dns;";
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
There's much more to explore... Have fun!
|
There's much more to explore... Have fun!
|
||||||
|
|
||||||
|
|
@ -231,10 +193,9 @@ Available scripts
|
||||||
* [Comment DHCP leases with info from access list](doc/dhcp-lease-comment.md)
|
* [Comment DHCP leases with info from access list](doc/dhcp-lease-comment.md)
|
||||||
* [Create DNS records for DHCP leases](doc/dhcp-to-dns.md)
|
* [Create DNS records for DHCP leases](doc/dhcp-to-dns.md)
|
||||||
* [Automatically upgrade firmware and reboot](doc/firmware-upgrade-reboot.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)
|
* [Wait for global functions und modules](doc/global-wait.md)
|
||||||
* [Send GPS position to server](doc/gps-track.md)
|
* [Send GPS position to server](doc/gps-track.md)
|
||||||
* [Use WPA network with hotspot credentials](doc/hotspot-to-wpa.md)
|
* [Use WPA2 network with hotspot credentials](doc/hotspot-to-wpa.md)
|
||||||
* [Create DNS records for IPSec peers](doc/ipsec-to-dns.md)
|
* [Create DNS records for IPSec peers](doc/ipsec-to-dns.md)
|
||||||
* [Update configuration on IPv6 prefix change](doc/ipv6-update.md)
|
* [Update configuration on IPv6 prefix change](doc/ipv6-update.md)
|
||||||
* [Manage IP addresses with bridge status](doc/ip-addr-bridge.md)
|
* [Manage IP addresses with bridge status](doc/ip-addr-bridge.md)
|
||||||
|
|
@ -244,17 +205,23 @@ Available scripts
|
||||||
* [Mode button with multiple presses](doc/mode-button.md)
|
* [Mode button with multiple presses](doc/mode-button.md)
|
||||||
* [Manage DNS and DoH servers from netwatch](doc/netwatch-dns.md)
|
* [Manage DNS and DoH servers from netwatch](doc/netwatch-dns.md)
|
||||||
* [Notify on host up and down](doc/netwatch-notify.md)
|
* [Notify on host up and down](doc/netwatch-notify.md)
|
||||||
|
* [Manage remote logging](doc/netwatch-syslog.md)
|
||||||
* [Visualize OSPF state via LEDs](doc/ospf-to-leds.md)
|
* [Visualize OSPF state via LEDs](doc/ospf-to-leds.md)
|
||||||
* [Manage system update](doc/packages-update.md)
|
* [Manage system update](doc/packages-update.md)
|
||||||
* [Run scripts on ppp connection](doc/ppp-on-up.md)
|
* [Run scripts on ppp connection](doc/ppp-on-up.md)
|
||||||
|
* [Rotate NTP servers](doc/rotate-ntp.md)
|
||||||
* [Act on received SMS](doc/sms-action.md)
|
* [Act on received SMS](doc/sms-action.md)
|
||||||
* [Forward received SMS](doc/sms-forward.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)
|
* [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)
|
* [Install LTE firmware upgrade](doc/unattended-lte-firmware-upgrade.md)
|
||||||
* [Update GRE configuration with dynamic addresses](doc/update-gre-address.md)
|
* [Update GRE configuration with dynamic addresses](doc/update-gre-address.md)
|
||||||
* [Update tunnelbroker configuration](doc/update-tunnelbroker.md)
|
* [Update tunnelbroker configuration](doc/update-tunnelbroker.md)
|
||||||
|
|
||||||
|
[comment]: # (TODO: currently undocumented)
|
||||||
|
[comment]: # (* learn-mac-based-vlan)
|
||||||
|
[comment]: # (* manage-umts)
|
||||||
|
|
||||||
Available modules
|
Available modules
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
|
|
@ -262,12 +229,9 @@ Available modules
|
||||||
* [Manage VLANs on bridge ports](doc/mod/bridge-port-vlan.md)
|
* [Manage VLANs on bridge ports](doc/mod/bridge-port-vlan.md)
|
||||||
* [Inspect variables](doc/mod/inspectvar.md)
|
* [Inspect variables](doc/mod/inspectvar.md)
|
||||||
* [IP address calculation](doc/mod/ipcalc.md)
|
* [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 Matrix](doc/mod/notification-matrix.md)
|
||||||
* [Send notifications via Ntfy](doc/mod/notification-ntfy.md)
|
|
||||||
* [Send notifications via Telegram](doc/mod/notification-telegram.md)
|
* [Send notifications via Telegram](doc/mod/notification-telegram.md)
|
||||||
* [Download script and run it once](doc/mod/scriptrunonce.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
|
Installing custom scripts & modules
|
||||||
-----------------------------------
|
-----------------------------------
|
||||||
|
|
@ -278,9 +242,12 @@ 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:
|
This will fetch and install a script `hello-world.rsc` from the given url:
|
||||||
|
|
||||||
$ScriptInstallUpdate hello-world "base-url=https://git.eworm.de/cgit/routeros-scripts-custom/plain/";
|
$ScriptInstallUpdate hello-world.rsc "base-url=https://git.eworm.de/cgit/routeros-scripts/plain/README.d/"
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
(Yes, the example url still belongs to the repository for easy
|
||||||
|
handling - but the url can be what ever you use.)
|
||||||
|
|
||||||
For a script to be considered valid it has to begin with a *magic token*.
|
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
|
Have a look at [any script](README.d/hello-world.rsc) and copy the first line
|
||||||
|
|
@ -289,38 +256,6 @@ without modification.
|
||||||
Starting a script's name with `mod/` makes it a module and it is run
|
Starting a script's name with `mod/` makes it a module and it is run
|
||||||
automatically by `global-functions`.
|
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
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
There is no specific function for script removal. Just remove it from
|
|
||||||
configuration...
|
|
||||||
|
|
||||||
/system/script/remove to-be-removed;
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
Possibly a scheduler and other configuration has to be removed as well.
|
|
||||||
|
|
||||||
Contact
|
Contact
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
|
@ -334,14 +269,12 @@ support!
|
||||||
Contribute
|
Contribute
|
||||||
----------
|
----------
|
||||||
|
|
||||||
Thanks a lot for [past contributions](CONTRIBUTIONS.md)! ❤️
|
Thanks a lot for [past contributions](CONTRIBUTIONS.md)!
|
||||||
|
|
||||||
### Patches, issues and whishlist
|
### Patches, issues and whishlist
|
||||||
|
|
||||||
Feel free to contact me via e-mail or open an
|
Feel free to contact me via e-mail or open an
|
||||||
[issue](https://github.com/eworm-de/routeros-scripts/issues) or
|
[issue at github](https://github.com/eworm-de/routeros-scripts/issues).
|
||||||
[pull request](https://github.com/eworm-de/routeros-scripts/pulls)
|
|
||||||
at github.
|
|
||||||
|
|
||||||
### Donate
|
### Donate
|
||||||
|
|
||||||
|
|
@ -380,4 +313,4 @@ Mirror:
|
||||||
[GitLab.com](https://gitlab.com/eworm-de/routeros-scripts#routeros-scripts)
|
[GitLab.com](https://gitlab.com/eworm-de/routeros-scripts#routeros-scripts)
|
||||||
|
|
||||||
---
|
---
|
||||||
[⬆️ Go back to top](#top)
|
[▲ Go back to top](#top)
|
||||||
|
|
|
||||||
42
accesslist-duplicates.capsman
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
#!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 [ :toarray "" ];
|
||||||
|
:local Shown [ :toarray "" ];
|
||||||
|
|
||||||
|
: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);
|
||||||
|
}
|
||||||
|
|
@ -1,37 +0,0 @@
|
||||||
#!rsc by RouterOS
|
|
||||||
# RouterOS script: accesslist-duplicates.capsman
|
|
||||||
# Copyright (c) 2018-2025 Christian Hesse <mail@eworm.de>
|
|
||||||
# https://rsc.eworm.de/COPYING.md
|
|
||||||
#
|
|
||||||
# requires RouterOS, version=7.15
|
|
||||||
#
|
|
||||||
# print duplicate antries in wireless access list
|
|
||||||
# https://rsc.eworm.de/doc/accesslist-duplicates.md
|
|
||||||
#
|
|
||||||
# !! Do not edit this file, it is generated from template!
|
|
||||||
|
|
||||||
:global GlobalFunctionsReady;
|
|
||||||
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
|
|
||||||
|
|
||||||
:local ExitOK false;
|
|
||||||
: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={
|
|
||||||
:global ExitError; $ExitError $ExitOK [ :jobname ];
|
|
||||||
}
|
|
||||||
42
accesslist-duplicates.local
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
#!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 [ :toarray "" ];
|
||||||
|
:local Shown [ :toarray "" ];
|
||||||
|
|
||||||
|
: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);
|
||||||
|
}
|
||||||
|
|
@ -1,37 +0,0 @@
|
||||||
#!rsc by RouterOS
|
|
||||||
# RouterOS script: accesslist-duplicates.local
|
|
||||||
# Copyright (c) 2018-2025 Christian Hesse <mail@eworm.de>
|
|
||||||
# https://rsc.eworm.de/COPYING.md
|
|
||||||
#
|
|
||||||
# requires RouterOS, version=7.15
|
|
||||||
#
|
|
||||||
# print duplicate antries in wireless access list
|
|
||||||
# https://rsc.eworm.de/doc/accesslist-duplicates.md
|
|
||||||
#
|
|
||||||
# !! Do not edit this file, it is generated from template!
|
|
||||||
|
|
||||||
:global GlobalFunctionsReady;
|
|
||||||
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
|
|
||||||
|
|
||||||
:local ExitOK false;
|
|
||||||
: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={
|
|
||||||
:global ExitError; $ExitError $ExitOK [ :jobname ];
|
|
||||||
}
|
|
||||||
43
accesslist-duplicates.template
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
#!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 [ :toarray "" ];
|
||||||
|
:local Shown [ :toarray "" ];
|
||||||
|
|
||||||
|
: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);
|
||||||
|
}
|
||||||
|
|
@ -1,46 +0,0 @@
|
||||||
#!rsc by RouterOS
|
|
||||||
# RouterOS script: accesslist-duplicates%TEMPL%
|
|
||||||
# Copyright (c) 2018-2025 Christian Hesse <mail@eworm.de>
|
|
||||||
# https://rsc.eworm.de/COPYING.md
|
|
||||||
#
|
|
||||||
# requires RouterOS, version=7.15
|
|
||||||
#
|
|
||||||
# print duplicate antries in wireless access list
|
|
||||||
# https://rsc.eworm.de/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; }
|
|
||||||
|
|
||||||
:local ExitOK false;
|
|
||||||
: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={
|
|
||||||
:global ExitError; $ExitError $ExitOK [ :jobname ];
|
|
||||||
}
|
|
||||||
|
|
@ -1,37 +0,0 @@
|
||||||
#!rsc by RouterOS
|
|
||||||
# RouterOS script: accesslist-duplicates.wifi
|
|
||||||
# Copyright (c) 2018-2025 Christian Hesse <mail@eworm.de>
|
|
||||||
# https://rsc.eworm.de/COPYING.md
|
|
||||||
#
|
|
||||||
# requires RouterOS, version=7.15
|
|
||||||
#
|
|
||||||
# print duplicate antries in wireless access list
|
|
||||||
# https://rsc.eworm.de/doc/accesslist-duplicates.md
|
|
||||||
#
|
|
||||||
# !! Do not edit this file, it is generated from template!
|
|
||||||
|
|
||||||
:global GlobalFunctionsReady;
|
|
||||||
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
|
|
||||||
|
|
||||||
:local ExitOK false;
|
|
||||||
: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={
|
|
||||||
:global ExitError; $ExitError $ExitOK [ :jobname ];
|
|
||||||
}
|
|
||||||
58
backup-cloud
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
#!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;
|
||||||
|
}
|
||||||
104
backup-cloud.rsc
|
|
@ -1,104 +0,0 @@
|
||||||
#!rsc by RouterOS
|
|
||||||
# RouterOS script: backup-cloud
|
|
||||||
# Copyright (c) 2013-2025 Christian Hesse <mail@eworm.de>
|
|
||||||
# https://rsc.eworm.de/COPYING.md
|
|
||||||
#
|
|
||||||
# provides: backup-script, order=40
|
|
||||||
# requires RouterOS, version=7.15
|
|
||||||
#
|
|
||||||
# upload backup to MikroTik cloud
|
|
||||||
# https://rsc.eworm.de/doc/backup-cloud.md
|
|
||||||
|
|
||||||
:global GlobalFunctionsReady;
|
|
||||||
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
|
|
||||||
|
|
||||||
:local ExitOK false;
|
|
||||||
:do {
|
|
||||||
:local ScriptName [ :jobname ];
|
|
||||||
|
|
||||||
:global BackupRandomDelay;
|
|
||||||
:global Identity;
|
|
||||||
:global PackagesUpdateBackupFailure;
|
|
||||||
|
|
||||||
:global DeviceInfo;
|
|
||||||
:global FormatLine;
|
|
||||||
:global HumanReadableNum;
|
|
||||||
:global LogPrint;
|
|
||||||
:global MkDir;
|
|
||||||
:global RandomDelay;
|
|
||||||
:global RmDir;
|
|
||||||
:global ScriptFromTerminal;
|
|
||||||
:global ScriptLock;
|
|
||||||
:global SendNotification2;
|
|
||||||
:global SymbolForNotification;
|
|
||||||
:global WaitForFile;
|
|
||||||
:global WaitFullyConnected;
|
|
||||||
|
|
||||||
:if ([ $ScriptLock $ScriptName ] = false) do={
|
|
||||||
:set PackagesUpdateBackupFailure true;
|
|
||||||
:set ExitOK true;
|
|
||||||
:error false;
|
|
||||||
}
|
|
||||||
|
|
||||||
:if ([ :len [ /system/scheduler/find where name="running-from-backup-partition" ] ] > 0) do={
|
|
||||||
$LogPrint warning $ScriptName ("Running from backup partition, refusing to act.");
|
|
||||||
:set PackagesUpdateBackupFailure true;
|
|
||||||
:set ExitOK 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!");
|
|
||||||
:set ExitOK true;
|
|
||||||
:error false;
|
|
||||||
}
|
|
||||||
|
|
||||||
:local I 5;
|
|
||||||
:do {
|
|
||||||
:execute {
|
|
||||||
:global BackupPassword;
|
|
||||||
|
|
||||||
:local Backup ([ /system/backup/cloud/find ]->0);
|
|
||||||
:if ([ :typeof $Backup ] = "id") do={
|
|
||||||
/system/backup/cloud/upload-file action=create-and-upload \
|
|
||||||
password=$BackupPassword replace=$Backup;
|
|
||||||
} else={
|
|
||||||
/system/backup/cloud/upload-file action=create-and-upload \
|
|
||||||
password=$BackupPassword;
|
|
||||||
}
|
|
||||||
/file/add name="tmpfs/backup-cloud/done";
|
|
||||||
} as-string;
|
|
||||||
:set I ($I - 1);
|
|
||||||
} while=([ $WaitForFile "tmpfs/backup-cloud/done" 200ms ] = false && $I > 0);
|
|
||||||
|
|
||||||
:if ([ $WaitForFile "tmpfs/backup-cloud/done" ] = true) do={
|
|
||||||
:if ($I < 4) do={
|
|
||||||
:log warning ($ScriptName . ": Retry successful, please discard previous connection errors.");
|
|
||||||
}
|
|
||||||
|
|
||||||
: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 ] . "B") ] . "\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;
|
|
||||||
}
|
|
||||||
$RmDir "tmpfs/backup-cloud";
|
|
||||||
} on-error={
|
|
||||||
:global ExitError; $ExitError $ExitOK [ :jobname ];
|
|
||||||
}
|
|
||||||
80
backup-email
Normal file
|
|
@ -0,0 +1,80 @@
|
||||||
|
#!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 [ :toarray "" ];
|
||||||
|
|
||||||
|
# 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 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 });
|
||||||
140
backup-email.rsc
|
|
@ -1,140 +0,0 @@
|
||||||
#!rsc by RouterOS
|
|
||||||
# RouterOS script: backup-email
|
|
||||||
# Copyright (c) 2013-2025 Christian Hesse <mail@eworm.de>
|
|
||||||
# https://rsc.eworm.de/COPYING.md
|
|
||||||
#
|
|
||||||
# provides: backup-script, order=20
|
|
||||||
# requires RouterOS, version=7.15
|
|
||||||
#
|
|
||||||
# create and email backup and config file
|
|
||||||
# https://rsc.eworm.de/doc/backup-email.md
|
|
||||||
|
|
||||||
:global GlobalFunctionsReady;
|
|
||||||
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
|
|
||||||
|
|
||||||
:local ExitOK false;
|
|
||||||
: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.");
|
|
||||||
:set ExitOK true;
|
|
||||||
:error false;
|
|
||||||
}
|
|
||||||
|
|
||||||
:if ($BackupSendBinary != true && \
|
|
||||||
$BackupSendExport != true) do={
|
|
||||||
$LogPrint error $ScriptName ("Configured to send neither backup nor config export.");
|
|
||||||
:set ExitOK true;
|
|
||||||
:error false;
|
|
||||||
}
|
|
||||||
|
|
||||||
:if ([ $ScriptLock $ScriptName ] = false) do={
|
|
||||||
:set PackagesUpdateBackupFailure true;
|
|
||||||
:set ExitOK true;
|
|
||||||
:error false;
|
|
||||||
}
|
|
||||||
|
|
||||||
:if ([ :len [ /system/scheduler/find where name="running-from-backup-partition" ] ] > 0) do={
|
|
||||||
$LogPrint warning $ScriptName ("Running from backup partition, refusing to act.");
|
|
||||||
:set PackagesUpdateBackupFailure true;
|
|
||||||
:set ExitOK 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!");
|
|
||||||
:set ExitOK true;
|
|
||||||
: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;
|
|
||||||
:set ExitOK true;
|
|
||||||
:error false;
|
|
||||||
}
|
|
||||||
:delay 1s;
|
|
||||||
:set I ($I + 1);
|
|
||||||
}
|
|
||||||
} on-error={
|
|
||||||
:global ExitError; $ExitError $ExitOK [ :jobname ];
|
|
||||||
}
|
|
||||||
36
backup-partition
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
#!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;
|
||||||
|
}
|
||||||
|
|
@ -1,126 +0,0 @@
|
||||||
#!rsc by RouterOS
|
|
||||||
# RouterOS script: backup-partition
|
|
||||||
# Copyright (c) 2022-2025 Christian Hesse <mail@eworm.de>
|
|
||||||
# https://rsc.eworm.de/COPYING.md
|
|
||||||
#
|
|
||||||
# provides: backup-script, order=70
|
|
||||||
# requires RouterOS, version=7.15
|
|
||||||
# requires device-mode, scheduler
|
|
||||||
#
|
|
||||||
# save configuration to fallback partition
|
|
||||||
# https://rsc.eworm.de/doc/backup-partition.md
|
|
||||||
|
|
||||||
:global GlobalFunctionsReady;
|
|
||||||
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
|
|
||||||
|
|
||||||
:local ExitOK false;
|
|
||||||
:do {
|
|
||||||
:local ScriptName [ :jobname ];
|
|
||||||
|
|
||||||
:global BackupPartitionCopyBeforeFeatureUpdate;
|
|
||||||
:global PackagesUpdateBackupFailure;
|
|
||||||
|
|
||||||
:global LogPrint;
|
|
||||||
:global ScriptFromTerminal;
|
|
||||||
:global ScriptLock;
|
|
||||||
:global VersionToNum;
|
|
||||||
|
|
||||||
:local CopyTo do={
|
|
||||||
:local ScriptName [ :tostr $1 ];
|
|
||||||
:local FallbackTo [ :toid $2 ];
|
|
||||||
:local FallbackToName [ :tostr $3 ];
|
|
||||||
|
|
||||||
:global LogPrint;
|
|
||||||
|
|
||||||
:do {
|
|
||||||
/partitions/copy-to $FallbackTo;
|
|
||||||
$LogPrint info $ScriptName ("Copied RouterOS to partition '" . $FallbackToName . "'.");
|
|
||||||
:return true;
|
|
||||||
} on-error={
|
|
||||||
$LogPrint error $ScriptName ("Failed copying RouterOS to partition '" . $FallbackToName . "'!");
|
|
||||||
:return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
:if ([ $ScriptLock $ScriptName ] = false) do={
|
|
||||||
:set PackagesUpdateBackupFailure true;
|
|
||||||
:set ExitOK true;
|
|
||||||
:error false;
|
|
||||||
}
|
|
||||||
|
|
||||||
:if ([ :len [ /system/scheduler/find where name="running-from-backup-partition" ] ] > 0) do={
|
|
||||||
$LogPrint warning $ScriptName ("Running from backup partition, refusing to act.");
|
|
||||||
:set PackagesUpdateBackupFailure true;
|
|
||||||
:set ExitOK true;
|
|
||||||
:error false;
|
|
||||||
}
|
|
||||||
|
|
||||||
:if ([ :len [ /partitions/find ] ] < 2) do={
|
|
||||||
$LogPrint error $ScriptName ("Device does not have a fallback partition.");
|
|
||||||
:set PackagesUpdateBackupFailure true;
|
|
||||||
:set ExitOK 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;
|
|
||||||
:set ExitOK true;
|
|
||||||
:error false;
|
|
||||||
}
|
|
||||||
|
|
||||||
:local FallbackToName [ /partitions/get $ActiveRunning fallback-to ];
|
|
||||||
:local FallbackTo [ /partition/find where name=$FallbackToName !active ];
|
|
||||||
|
|
||||||
:if ([ :len $FallbackTo ] < 1) do={
|
|
||||||
$LogPrint error $ScriptName ("There is no inactive partition named '" . $FallbackToName . "'.");
|
|
||||||
:set PackagesUpdateBackupFailure true;
|
|
||||||
:set ExitOK true;
|
|
||||||
:error false;
|
|
||||||
}
|
|
||||||
|
|
||||||
:if ([ /partitions/get $ActiveRunning version ] != [ /partitions/get $FallbackTo version]) do={
|
|
||||||
:if ([ $ScriptFromTerminal $ScriptName ] = true) do={
|
|
||||||
:put ("The partitions have different RouterOS versions. Copy over to '" . $FallbackToName . "'? [y/N]");
|
|
||||||
:if (([ /terminal/inkey timeout=60 ] % 32) = 25) do={
|
|
||||||
:if ([ $CopyTo $ScriptName $FallbackTo $FallbackToName ] = false) do={
|
|
||||||
:set PackagesUpdateBackupFailure true;
|
|
||||||
:set ExitOK true;
|
|
||||||
:error false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else={
|
|
||||||
:local Update [ /system/package/update/get ];
|
|
||||||
:local NumInstalled [ $VersionToNum ($Update->"installed-version") ];
|
|
||||||
:local NumLatest [ $VersionToNum ($Update->"latest-version") ];
|
|
||||||
:local BitMask [ $VersionToNum "255.255zero0" ];
|
|
||||||
:if ($BackupPartitionCopyBeforeFeatureUpdate = true && $NumLatest > 0 && \
|
|
||||||
($NumInstalled & $BitMask) != ($NumLatest & $BitMask)) do={
|
|
||||||
:if ([ $CopyTo $ScriptName $FallbackTo $FallbackToName ] = false) do={
|
|
||||||
:set PackagesUpdateBackupFailure true;
|
|
||||||
:set ExitOK true;
|
|
||||||
:error false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
: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 '" . $FallbackToName . "'.");
|
|
||||||
} on-error={
|
|
||||||
/system/scheduler/remove [ find where name="running-from-backup-partition" ];
|
|
||||||
$LogPrint error $ScriptName ("Failed saving configuration to partition '" . $FallbackToName . "'!");
|
|
||||||
:set PackagesUpdateBackupFailure true;
|
|
||||||
:set ExitOK true;
|
|
||||||
:error false;
|
|
||||||
}
|
|
||||||
} on-error={
|
|
||||||
:global ExitError; $ExitError $ExitOK [ :jobname ];
|
|
||||||
}
|
|
||||||
106
backup-upload
Normal file
|
|
@ -0,0 +1,106 @@
|
||||||
|
#!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 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!";
|
||||||
|
}
|
||||||
|
|
@ -1,178 +0,0 @@
|
||||||
#!rsc by RouterOS
|
|
||||||
# RouterOS script: backup-upload
|
|
||||||
# Copyright (c) 2013-2025 Christian Hesse <mail@eworm.de>
|
|
||||||
# https://rsc.eworm.de/COPYING.md
|
|
||||||
#
|
|
||||||
# provides: backup-script, order=50
|
|
||||||
# requires RouterOS, version=7.15
|
|
||||||
# requires device-mode, fetch
|
|
||||||
#
|
|
||||||
# create and upload backup and config file
|
|
||||||
# https://rsc.eworm.de/doc/backup-upload.md
|
|
||||||
|
|
||||||
:global GlobalFunctionsReady;
|
|
||||||
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
|
|
||||||
|
|
||||||
:local ExitOK false;
|
|
||||||
: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 RmDir;
|
|
||||||
:global RmFile;
|
|
||||||
: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.");
|
|
||||||
:set ExitOK true;
|
|
||||||
:error false;
|
|
||||||
}
|
|
||||||
|
|
||||||
:if ([ $ScriptLock $ScriptName ] = false) do={
|
|
||||||
:set PackagesUpdateBackupFailure true;
|
|
||||||
:set ExitOK true;
|
|
||||||
:error false;
|
|
||||||
}
|
|
||||||
|
|
||||||
:if ([ :len [ /system/scheduler/find where name="running-from-backup-partition" ] ] > 0) do={
|
|
||||||
$LogPrint warning $ScriptName ("Running from backup partition, refusing to act.");
|
|
||||||
:set PackagesUpdateBackupFailure true;
|
|
||||||
:set ExitOK 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!");
|
|
||||||
:set ExitOK true;
|
|
||||||
: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;
|
|
||||||
}
|
|
||||||
|
|
||||||
$RmFile ($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;
|
|
||||||
}
|
|
||||||
|
|
||||||
$RmFile ($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;
|
|
||||||
}
|
|
||||||
|
|
||||||
$RmFile ($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 ] . "B") ]) \
|
|
||||||
[ $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;
|
|
||||||
}
|
|
||||||
$RmDir $DirName;
|
|
||||||
} on-error={
|
|
||||||
:global ExitError; $ExitError $ExitOK [ :jobname ];
|
|
||||||
}
|
|
||||||
89
capsman-download-packages
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
#!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 ($File->"package-name" = "wireless@") do={
|
||||||
|
:set ($File->"package-name") "wireless";
|
||||||
|
}
|
||||||
|
: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 ];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,92 +0,0 @@
|
||||||
#!rsc by RouterOS
|
|
||||||
# RouterOS script: capsman-download-packages.capsman
|
|
||||||
# Copyright (c) 2018-2025 Christian Hesse <mail@eworm.de>
|
|
||||||
# Michael Gisbers <michael@gisbers.de>
|
|
||||||
# https://rsc.eworm.de/COPYING.md
|
|
||||||
#
|
|
||||||
# requires RouterOS, version=7.15
|
|
||||||
#
|
|
||||||
# download and cleanup packages for CAP installation from CAPsMAN
|
|
||||||
# https://rsc.eworm.de/doc/capsman-download-packages.md
|
|
||||||
#
|
|
||||||
# !! Do not edit this file, it is generated from template!
|
|
||||||
|
|
||||||
:global GlobalFunctionsReady;
|
|
||||||
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
|
|
||||||
|
|
||||||
:local ExitOK false;
|
|
||||||
:do {
|
|
||||||
:local ScriptName [ :jobname ];
|
|
||||||
|
|
||||||
:global CleanFilePath;
|
|
||||||
:global DownloadPackage;
|
|
||||||
:global LogPrint;
|
|
||||||
:global MkDir;
|
|
||||||
:global RmFile;
|
|
||||||
:global ScriptLock;
|
|
||||||
:global WaitFullyConnected;
|
|
||||||
|
|
||||||
:if ([ $ScriptLock $ScriptName ] = false) do={
|
|
||||||
:set ExitOK true;
|
|
||||||
: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.");
|
|
||||||
:set ExitOK true;
|
|
||||||
: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!");
|
|
||||||
:set ExitOK true;
|
|
||||||
: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;
|
|
||||||
$RmFile ($File->"name");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
: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 Scripts [ /system/script/find where source~"\n# provides: capsman-rolling-upgrade.capsman\r?\n" ];
|
|
||||||
:if ([ :len $Scripts ] > 0) do={
|
|
||||||
:foreach Script in=$Scripts do={
|
|
||||||
/system/script/run $Script;
|
|
||||||
}
|
|
||||||
} else={
|
|
||||||
/caps-man/remote-cap/upgrade [ find where version!=$InstalledVersion ];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} on-error={
|
|
||||||
:global ExitError; $ExitError $ExitOK [ :jobname ];
|
|
||||||
}
|
|
||||||
|
|
@ -1,103 +0,0 @@
|
||||||
#!rsc by RouterOS
|
|
||||||
# RouterOS script: capsman-download-packages%TEMPL%
|
|
||||||
# Copyright (c) 2018-2025 Christian Hesse <mail@eworm.de>
|
|
||||||
# Michael Gisbers <michael@gisbers.de>
|
|
||||||
# https://rsc.eworm.de/COPYING.md
|
|
||||||
#
|
|
||||||
# requires RouterOS, version=7.15
|
|
||||||
#
|
|
||||||
# download and cleanup packages for CAP installation from CAPsMAN
|
|
||||||
# https://rsc.eworm.de/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; }
|
|
||||||
|
|
||||||
:local ExitOK false;
|
|
||||||
:do {
|
|
||||||
:local ScriptName [ :jobname ];
|
|
||||||
|
|
||||||
:global CleanFilePath;
|
|
||||||
:global DownloadPackage;
|
|
||||||
:global LogPrint;
|
|
||||||
:global MkDir;
|
|
||||||
:global RmFile;
|
|
||||||
:global ScriptLock;
|
|
||||||
:global WaitFullyConnected;
|
|
||||||
|
|
||||||
:if ([ $ScriptLock $ScriptName ] = false) do={
|
|
||||||
:set ExitOK true;
|
|
||||||
: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.");
|
|
||||||
:set ExitOK true;
|
|
||||||
: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!");
|
|
||||||
:set ExitOK true;
|
|
||||||
: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;
|
|
||||||
$RmFile ($File->"name");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
: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 Scripts [ /system/script/find where source~"\n# provides: capsman-rolling-upgrade%TEMPL%\r?\n" ];
|
|
||||||
:if ([ :len $Scripts ] > 0) do={
|
|
||||||
:foreach Script in=$Scripts 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={
|
|
||||||
:global ExitError; $ExitError $ExitOK [ :jobname ];
|
|
||||||
}
|
|
||||||
|
|
@ -1,94 +0,0 @@
|
||||||
#!rsc by RouterOS
|
|
||||||
# RouterOS script: capsman-download-packages.wifi
|
|
||||||
# Copyright (c) 2018-2025 Christian Hesse <mail@eworm.de>
|
|
||||||
# Michael Gisbers <michael@gisbers.de>
|
|
||||||
# https://rsc.eworm.de/COPYING.md
|
|
||||||
#
|
|
||||||
# requires RouterOS, version=7.15
|
|
||||||
#
|
|
||||||
# download and cleanup packages for CAP installation from CAPsMAN
|
|
||||||
# https://rsc.eworm.de/doc/capsman-download-packages.md
|
|
||||||
#
|
|
||||||
# !! Do not edit this file, it is generated from template!
|
|
||||||
|
|
||||||
:global GlobalFunctionsReady;
|
|
||||||
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
|
|
||||||
|
|
||||||
:local ExitOK false;
|
|
||||||
:do {
|
|
||||||
:local ScriptName [ :jobname ];
|
|
||||||
|
|
||||||
:global CleanFilePath;
|
|
||||||
:global DownloadPackage;
|
|
||||||
:global LogPrint;
|
|
||||||
:global MkDir;
|
|
||||||
:global RmFile;
|
|
||||||
:global ScriptLock;
|
|
||||||
:global WaitFullyConnected;
|
|
||||||
|
|
||||||
:if ([ $ScriptLock $ScriptName ] = false) do={
|
|
||||||
:set ExitOK true;
|
|
||||||
: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.");
|
|
||||||
:set ExitOK true;
|
|
||||||
: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!");
|
|
||||||
:set ExitOK true;
|
|
||||||
: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;
|
|
||||||
$RmFile ($File->"name");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
: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 Scripts [ /system/script/find where source~"\n# provides: capsman-rolling-upgrade.wifi\r?\n" ];
|
|
||||||
:if ([ :len $Scripts ] > 0) do={
|
|
||||||
:foreach Script in=$Scripts do={
|
|
||||||
/system/script/run $Script;
|
|
||||||
}
|
|
||||||
} else={
|
|
||||||
/interface/wifi/capsman/remote-cap/upgrade [ find where version!=$InstalledVersion ];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} on-error={
|
|
||||||
:global ExitError; $ExitError $ExitOK [ :jobname ];
|
|
||||||
}
|
|
||||||
36
capsman-rolling-upgrade
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
#!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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,50 +0,0 @@
|
||||||
#!rsc by RouterOS
|
|
||||||
# RouterOS script: capsman-rolling-upgrade.capsman
|
|
||||||
# Copyright (c) 2018-2025 Christian Hesse <mail@eworm.de>
|
|
||||||
# Michael Gisbers <michael@gisbers.de>
|
|
||||||
# https://rsc.eworm.de/COPYING.md
|
|
||||||
#
|
|
||||||
# provides: capsman-rolling-upgrade.capsman
|
|
||||||
# requires RouterOS, version=7.15
|
|
||||||
#
|
|
||||||
# upgrade CAPs one after another
|
|
||||||
# https://rsc.eworm.de/doc/capsman-rolling-upgrade.md
|
|
||||||
#
|
|
||||||
# !! Do not edit this file, it is generated from template!
|
|
||||||
|
|
||||||
:global GlobalFunctionsReady;
|
|
||||||
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
|
|
||||||
|
|
||||||
:local ExitOK false;
|
|
||||||
:do {
|
|
||||||
:local ScriptName [ :jobname ];
|
|
||||||
|
|
||||||
:global LogPrint;
|
|
||||||
:global ScriptLock;
|
|
||||||
|
|
||||||
:if ([ $ScriptLock $ScriptName ] = false) do={
|
|
||||||
:set ExitOK true;
|
|
||||||
: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={
|
|
||||||
:global ExitError; $ExitError $ExitOK [ :jobname ];
|
|
||||||
}
|
|
||||||
|
|
@ -1,58 +0,0 @@
|
||||||
#!rsc by RouterOS
|
|
||||||
# RouterOS script: capsman-rolling-upgrade%TEMPL%
|
|
||||||
# Copyright (c) 2018-2025 Christian Hesse <mail@eworm.de>
|
|
||||||
# Michael Gisbers <michael@gisbers.de>
|
|
||||||
# https://rsc.eworm.de/COPYING.md
|
|
||||||
#
|
|
||||||
# provides: capsman-rolling-upgrade%TEMPL%
|
|
||||||
# requires RouterOS, version=7.15
|
|
||||||
#
|
|
||||||
# upgrade CAPs one after another
|
|
||||||
# https://rsc.eworm.de/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; }
|
|
||||||
|
|
||||||
:local ExitOK false;
|
|
||||||
:do {
|
|
||||||
:local ScriptName [ :jobname ];
|
|
||||||
|
|
||||||
:global LogPrint;
|
|
||||||
:global ScriptLock;
|
|
||||||
|
|
||||||
:if ([ $ScriptLock $ScriptName ] = false) do={
|
|
||||||
:set ExitOK true;
|
|
||||||
: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={
|
|
||||||
:global ExitError; $ExitError $ExitOK [ :jobname ];
|
|
||||||
}
|
|
||||||
|
|
@ -1,51 +0,0 @@
|
||||||
#!rsc by RouterOS
|
|
||||||
# RouterOS script: capsman-rolling-upgrade.wifi
|
|
||||||
# Copyright (c) 2018-2025 Christian Hesse <mail@eworm.de>
|
|
||||||
# Michael Gisbers <michael@gisbers.de>
|
|
||||||
# https://rsc.eworm.de/COPYING.md
|
|
||||||
#
|
|
||||||
# provides: capsman-rolling-upgrade.wifi
|
|
||||||
# requires RouterOS, version=7.15
|
|
||||||
#
|
|
||||||
# upgrade CAPs one after another
|
|
||||||
# https://rsc.eworm.de/doc/capsman-rolling-upgrade.md
|
|
||||||
#
|
|
||||||
# !! Do not edit this file, it is generated from template!
|
|
||||||
|
|
||||||
:global GlobalFunctionsReady;
|
|
||||||
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
|
|
||||||
|
|
||||||
:local ExitOK false;
|
|
||||||
:do {
|
|
||||||
:local ScriptName [ :jobname ];
|
|
||||||
|
|
||||||
:global LogPrint;
|
|
||||||
:global ScriptLock;
|
|
||||||
|
|
||||||
:if ([ $ScriptLock $ScriptName ] = false) do={
|
|
||||||
:set ExitOK true;
|
|
||||||
: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={
|
|
||||||
:global ExitError; $ExitError $ExitOK [ :jobname ];
|
|
||||||
}
|
|
||||||
38
certificate-renew-issued
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
#!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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,52 +0,0 @@
|
||||||
#!rsc by RouterOS
|
|
||||||
# RouterOS script: certificate-renew-issued
|
|
||||||
# Copyright (c) 2019-2025 Christian Hesse <mail@eworm.de>
|
|
||||||
# https://rsc.eworm.de/COPYING.md
|
|
||||||
#
|
|
||||||
# requires RouterOS, version=7.15
|
|
||||||
#
|
|
||||||
# renew locally issued certificates
|
|
||||||
# https://rsc.eworm.de/doc/certificate-renew-issued.md
|
|
||||||
|
|
||||||
:global GlobalFunctionsReady;
|
|
||||||
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
|
|
||||||
|
|
||||||
:local ExitOK false;
|
|
||||||
:do {
|
|
||||||
:local ScriptName [ :jobname ];
|
|
||||||
|
|
||||||
:global CertIssuedExportPass;
|
|
||||||
|
|
||||||
:global LogPrint;
|
|
||||||
:global MkDir;
|
|
||||||
:global ScriptLock;
|
|
||||||
|
|
||||||
:if ([ $ScriptLock $ScriptName ] = false) do={
|
|
||||||
:set ExitOK true;
|
|
||||||
: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={
|
|
||||||
:global ExitError; $ExitError $ExitOK [ :jobname ];
|
|
||||||
}
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
# Issuer: CN=Certum Trusted Network CA O=Unizeto Technologies S.A. OU=Certum Certification Authority
|
|
||||||
# Subject: CN=Certum Trusted Network CA O=Unizeto Technologies S.A. OU=Certum Certification Authority
|
|
||||||
# Label: "Certum Trusted Network CA"
|
|
||||||
# Serial: 279744
|
|
||||||
# MD5 Fingerprint: d5:e9:81:40:c5:18:69:fc:46:2c:89:75:62:0f:aa:78
|
|
||||||
# SHA1 Fingerprint: 07:e0:32:e0:20:b7:2c:3f:19:2f:06:28:a2:59:3a:19:a7:0f:06:9e
|
|
||||||
# SHA256 Fingerprint: 5c:58:46:8d:55:f5:8e:49:7e:74:39:82:d2:b5:00:10:b6:d1:65:37:4a:cf:83:a7:d4:a3:2d:b7:68:c4:40:8e
|
|
||||||
-----BEGIN CERTIFICATE-----
|
|
||||||
MIIDuzCCAqOgAwIBAgIDBETAMA0GCSqGSIb3DQEBBQUAMH4xCzAJBgNVBAYTAlBM
|
|
||||||
MSIwIAYDVQQKExlVbml6ZXRvIFRlY2hub2xvZ2llcyBTLkEuMScwJQYDVQQLEx5D
|
|
||||||
ZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxIjAgBgNVBAMTGUNlcnR1bSBU
|
|
||||||
cnVzdGVkIE5ldHdvcmsgQ0EwHhcNMDgxMDIyMTIwNzM3WhcNMjkxMjMxMTIwNzM3
|
|
||||||
WjB+MQswCQYDVQQGEwJQTDEiMCAGA1UEChMZVW5pemV0byBUZWNobm9sb2dpZXMg
|
|
||||||
Uy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MSIw
|
|
||||||
IAYDVQQDExlDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENBMIIBIjANBgkqhkiG9w0B
|
|
||||||
AQEFAAOCAQ8AMIIBCgKCAQEA4/t9o3K6wvDJFIf1awFO4W5AB7ptJ11/91sts1rH
|
|
||||||
UV+rpDKmYYe2bg+G0jACl/jXaVehGDldamR5xgFZrDwxSjh80gTSSyjoIF87B6LM
|
|
||||||
TXPb865Px1bVWqeWifrzq2jUI4ZZJ88JJ7ysbnKDHDBy3+Ci6dLhdHUZvSqeexVU
|
|
||||||
BBvXQzmtVSjF4hq79MDkrjhJM8x2hZ85RdKknvISjFH4fOQtf/WsX+sWn7Et0brM
|
|
||||||
kUJ3TCXJkDhv2/DM+44el1k+1WBO5gUo7Ul5E0u6SNsv+XLTOcr+H9g0cvW0QM8x
|
|
||||||
AcPs3hEtF10fuFDRXhmnad4HMyjKUJX5p1TLVIZQRan5SQIDAQABo0IwQDAPBgNV
|
|
||||||
HRMBAf8EBTADAQH/MB0GA1UdDgQWBBQIds3LB/8k9sXN7buQvOKEN0Z19zAOBgNV
|
|
||||||
HQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQEFBQADggEBAKaorSLOAT2mo/9i0Eidi15y
|
|
||||||
sHhE49wcrwn9I0j6vSrEuVUEtRCjjSfeC4Jj0O7eDDd5QVsisrCaQVymcODU0HfL
|
|
||||||
I9MA4GxWL+FpDQ3Zqr8hgVDZBqWo/5U30Kr+4rP1mS1FhIrlQgnXdAIv94nYmem8
|
|
||||||
J9RHjboNRhx3zxSkHLmkMcScKHQDNP8zGSal6Q10tz6XxnboJ5ajZt3hrvJBW8qY
|
|
||||||
VoNzcOSGGtIxQbovvi0TWnZvTuhOgQ4/WwMioBK+ZlgRSssDxLQqKi2WF+A5VLxI
|
|
||||||
03YnnZotBqbJ7DnSq9ufmgsnAjUpsUCV5/nonFWIGUbWtzT1fs45mtk48VH3Tyw=
|
|
||||||
-----END CERTIFICATE-----
|
|
||||||
29
certs/DigiCert Global Root CA.pem
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
# Issuer: CN=DigiCert Global Root CA O=DigiCert Inc OU=www.digicert.com
|
||||||
|
# Subject: CN=DigiCert Global Root CA O=DigiCert Inc OU=www.digicert.com
|
||||||
|
# Label: "DigiCert Global Root CA"
|
||||||
|
# Serial: 10944719598952040374951832963794454346
|
||||||
|
# MD5 Fingerprint: 79:e4:a9:84:0d:7d:3a:96:d7:c0:4f:e2:43:4c:89:2e
|
||||||
|
# SHA1 Fingerprint: a8:98:5d:3a:65:e5:e5:c4:b2:d7:d6:6d:40:c6:dd:2f:b1:9c:54:36
|
||||||
|
# SHA256 Fingerprint: 43:48:a0:e9:44:4c:78:cb:26:5e:05:8d:5e:89:44:b4:d8:4f:96:62:bd:26:db:25:7f:89:34:a4:43:c7:01:61
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh
|
||||||
|
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
|
||||||
|
d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD
|
||||||
|
QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT
|
||||||
|
MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
|
||||||
|
b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG
|
||||||
|
9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB
|
||||||
|
CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97
|
||||||
|
nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt
|
||||||
|
43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P
|
||||||
|
T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4
|
||||||
|
gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO
|
||||||
|
BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR
|
||||||
|
TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw
|
||||||
|
DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr
|
||||||
|
hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg
|
||||||
|
06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF
|
||||||
|
PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls
|
||||||
|
YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk
|
||||||
|
CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
# Issuer: CN=DigiCert Global Root G3 O=DigiCert Inc OU=www.digicert.com
|
|
||||||
# Subject: CN=DigiCert Global Root G3 O=DigiCert Inc OU=www.digicert.com
|
|
||||||
# Label: "DigiCert Global Root G3"
|
|
||||||
# Serial: 7089244469030293291760083333884364146
|
|
||||||
# MD5 Fingerprint: f5:5d:a4:50:a5:fb:28:7e:1e:0f:0d:cc:96:57:56:ca
|
|
||||||
# SHA1 Fingerprint: 7e:04:de:89:6a:3e:66:6d:00:e6:87:d3:3f:fa:d9:3b:e8:3d:34:9e
|
|
||||||
# SHA256 Fingerprint: 31:ad:66:48:f8:10:41:38:c7:38:f3:9e:a4:32:01:33:39:3e:3a:18:cc:02:29:6e:f9:7c:2a:c9:ef:67:31:d0
|
|
||||||
-----BEGIN CERTIFICATE-----
|
|
||||||
MIICPzCCAcWgAwIBAgIQBVVWvPJepDU1w6QP1atFcjAKBggqhkjOPQQDAzBhMQsw
|
|
||||||
CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu
|
|
||||||
ZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMzAe
|
|
||||||
Fw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVTMRUw
|
|
||||||
EwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20x
|
|
||||||
IDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEczMHYwEAYHKoZIzj0CAQYF
|
|
||||||
K4EEACIDYgAE3afZu4q4C/sLfyHS8L6+c/MzXRq8NOrexpu80JX28MzQC7phW1FG
|
|
||||||
fp4tn+6OYwwX7Adw9c+ELkCDnOg/QW07rdOkFFk2eJ0DQ+4QE2xy3q6Ip6FrtUPO
|
|
||||||
Z9wj/wMco+I+o0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAd
|
|
||||||
BgNVHQ4EFgQUs9tIpPmhxdiuNkHMEWNpYim8S8YwCgYIKoZIzj0EAwMDaAAwZQIx
|
|
||||||
AK288mw/EkrRLTnDCgmXc/SINoyIJ7vmiI1Qhadj+Z4y3maTD/HMsQmP3Wyr+mt/
|
|
||||||
oAIwOWZbwmSNuJ5Q3KjVSaLtx9zRSX8XAbjIho9OjIgrqJqpisXRAL34VOKa5Vt8
|
|
||||||
sycX
|
|
||||||
-----END CERTIFICATE-----
|
|
||||||
|
|
@ -1,58 +0,0 @@
|
||||||
# Makefile to check certificates
|
|
||||||
|
|
||||||
CURL = curl \
|
|
||||||
--capath /dev/null \
|
|
||||||
--connect-timeout 5 \
|
|
||||||
--output /dev/null \
|
|
||||||
--silent
|
|
||||||
|
|
||||||
DOMAINS_DUAL = \
|
|
||||||
api.macvendors.com/GTS-Root-R4 \
|
|
||||||
api.telegram.org/Go-Daddy-Root-Certificate-Authority-G2 \
|
|
||||||
cloudflare-dns.com/DigiCert-Global-Root-G2 \
|
|
||||||
dns.google/GTS-Root-R4 \
|
|
||||||
dns.quad9.net/DigiCert-Global-Root-G3 \
|
|
||||||
git.eworm.de/ISRG-Root-X2 \
|
|
||||||
lists.blocklist.de/Certum-Trusted-Network-CA \
|
|
||||||
matrix.org/GTS-Root-R4 \
|
|
||||||
raw.githubusercontent.com/USERTrust-RSA-Certification-Authority \
|
|
||||||
rsc.eworm.de/ISRG-Root-X2 \
|
|
||||||
upgrade.mikrotik.com/ISRG-Root-X1
|
|
||||||
DOMAINS_IPV4 = \
|
|
||||||
1.1.1.1/DigiCert-Global-Root-G2 \
|
|
||||||
8.8.8.8/GTS-Root-R1 \
|
|
||||||
9.9.9.9/DigiCert-Global-Root-G3 \
|
|
||||||
api.mullvad.net/ISRG-Root-X1 \
|
|
||||||
ipv4.showipv6.de/ISRG-Root-X1 \
|
|
||||||
ipv4.tunnelbroker.net/Starfield-Root-Certificate-Authority-G2 \
|
|
||||||
mkcert.org/ISRG-Root-X1 \
|
|
||||||
ntfy.sh/ISRG-Root-X1 \
|
|
||||||
www.dshield.org/ISRG-Root-X1 \
|
|
||||||
www.spamhaus.org/GTS-Root-R4
|
|
||||||
DOMAINS_IPV6 = \
|
|
||||||
[2606\:4700\:4700\:\:1111]/DigiCert-Global-Root-G2 \
|
|
||||||
[2001\:4860\:4860\:\:8888]/GTS-Root-R1 \
|
|
||||||
[2620\:fe\:\:9]/DigiCert-Global-Root-G3 \
|
|
||||||
ipv6.showipv6.de/ISRG-Root-X1
|
|
||||||
|
|
||||||
.PHONY: $(DOMAINS_DUAL) $(DOMAINS_IPV4) $(DOMAINS_IPV6)
|
|
||||||
|
|
||||||
all: $(DOMAINS_DUAL) $(DOMAINS_IPV4) $(DOMAINS_IPV6)
|
|
||||||
|
|
||||||
$(DOMAINS_DUAL):
|
|
||||||
ifndef NOIPV4
|
|
||||||
$(CURL) -4 --cacert $(notdir $@).pem https://$(dir $@)
|
|
||||||
endif
|
|
||||||
ifndef NOIPV6
|
|
||||||
$(CURL) -6 --cacert $(notdir $@).pem https://$(dir $@)
|
|
||||||
endif
|
|
||||||
|
|
||||||
$(DOMAINS_IPV4):
|
|
||||||
ifndef NOIPV4
|
|
||||||
$(CURL) -4 --cacert $(notdir $@).pem https://$(dir $@)
|
|
||||||
endif
|
|
||||||
|
|
||||||
$(DOMAINS_IPV6):
|
|
||||||
ifndef NOIPV6
|
|
||||||
$(CURL) -6 --cacert $(notdir $@).pem https://$(dir $@)
|
|
||||||
endif
|
|
||||||
|
|
@ -1,41 +0,0 @@
|
||||||
# Issuer: CN=USERTrust RSA Certification Authority O=The USERTRUST Network
|
|
||||||
# Subject: CN=USERTrust RSA Certification Authority O=The USERTRUST Network
|
|
||||||
# Label: "USERTrust RSA Certification Authority"
|
|
||||||
# Serial: 2645093764781058787591871645665788717
|
|
||||||
# MD5 Fingerprint: 1b:fe:69:d1:91:b7:19:33:a3:72:a8:0f:e1:55:e5:b5
|
|
||||||
# SHA1 Fingerprint: 2b:8f:1b:57:33:0d:bb:a2:d0:7a:6c:51:f7:0e:e9:0d:da:b9:ad:8e
|
|
||||||
# SHA256 Fingerprint: e7:93:c9:b0:2f:d8:aa:13:e2:1c:31:22:8a:cc:b0:81:19:64:3b:74:9c:89:89:64:b1:74:6d:46:c3:d4:cb:d2
|
|
||||||
-----BEGIN CERTIFICATE-----
|
|
||||||
MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCB
|
|
||||||
iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl
|
|
||||||
cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV
|
|
||||||
BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAw
|
|
||||||
MjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNV
|
|
||||||
BAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU
|
|
||||||
aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2Vy
|
|
||||||
dGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK
|
|
||||||
AoICAQCAEmUXNg7D2wiz0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/MPans9s/B
|
|
||||||
3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2jY0K2dvKpOyuR+OJv0OwWIJAJPuLodMkY
|
|
||||||
tJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFnRghRy4YUVD+8M/5+bJz/
|
|
||||||
Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1dat//O+T23LLb2
|
|
||||||
VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT
|
|
||||||
79uq/nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/zJSZrM233bkf6
|
|
||||||
c0Plfg6lZrEpfDKEY1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmT
|
|
||||||
Yo61Zs8liM2EuLE/pDkP2QKe6xJMlXzzawWpXhaDzLhn4ugTncxbgtNMs+1b/97l
|
|
||||||
c6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8yexDJtC/QV9AqURE9JnnV4ee
|
|
||||||
UB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+eLf8ZxXhyVeE
|
|
||||||
Hg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwIDAQABo0IwQDAd
|
|
||||||
BgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rIDZsswDgYDVR0PAQH/BAQDAgEGMA8G
|
|
||||||
A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAFzUfA3P9wF9QZllDHPF
|
|
||||||
Up/L+M+ZBn8b2kMVn54CVVeWFPFSPCeHlCjtHzoBN6J2/FNQwISbxmtOuowhT6KO
|
|
||||||
VWKR82kV2LyI48SqC/3vqOlLVSoGIG1VeCkZ7l8wXEskEVX/JJpuXior7gtNn3/3
|
|
||||||
ATiUFJVDBwn7YKnuHKsSjKCaXqeYalltiz8I+8jRRa8YFWSQEg9zKC7F4iRO/Fjs
|
|
||||||
8PRF/iKz6y+O0tlFYQXBl2+odnKPi4w2r78NBc5xjeambx9spnFixdjQg3IM8WcR
|
|
||||||
iQycE0xyNN+81XHfqnHd4blsjDwSXWXavVcStkNr/+XeTWYRUc+ZruwXtuhxkYze
|
|
||||||
Sf7dNXGiFSeUHM9h4ya7b6NnJSFd5t0dCy5oGzuCr+yDZ4XUmFF0sbmZgIn/f3gZ
|
|
||||||
XHlKYC6SQK5MNyosycdiyA5d9zZbyuAlJQG03RoHnHcAP9Dc1ew91Pq7P8yF1m9/
|
|
||||||
qS3fuQL39ZeatTXaw2ewh0qpKJ4jjv9cJ2vhsE/zB+4ALtRZh8tSQZXq9EfX7mRB
|
|
||||||
VXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGwsAvgnEzDHNb842m1R0aB
|
|
||||||
L6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gxQ+6IHdfG
|
|
||||||
jjxDah2nGN59PRbxYvnKkKj9
|
|
||||||
-----END CERTIFICATE-----
|
|
||||||
135
check-certificates
Normal file
|
|
@ -0,0 +1,135 @@
|
||||||
|
#!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") ];
|
||||||
|
|
||||||
|
:do {
|
||||||
|
/ ip ipsec identity set certificate=($CertNewVal->"name") [ / ip ipsec identity find where certificate=($CertVal->"name") ];
|
||||||
|
/ ip ipsec identity set remote-certificate=($CertNewVal->"name") [ / ip ipsec identity find where remote-certificate=($CertVal->"name") ];
|
||||||
|
} on-error={
|
||||||
|
$LogPrintExit2 debug $0 ("Setting IPSEC certificates failed. Package 'security' not installed?") false;
|
||||||
|
}
|
||||||
|
|
||||||
|
:do {
|
||||||
|
/ ip hotspot profile set ssl-certificate=($CertNewVal->"name") [ / ip hotspot profile find where ssl-certificate=($CertVal->"name") ];
|
||||||
|
} on-error={
|
||||||
|
$LogPrintExit2 debug $0 ("Setting hotspot certificates failed. Package 'hotspot' not installed?") false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/ 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,242 +0,0 @@
|
||||||
#!rsc by RouterOS
|
|
||||||
# RouterOS script: check-certificates
|
|
||||||
# Copyright (c) 2013-2025 Christian Hesse <mail@eworm.de>
|
|
||||||
# https://rsc.eworm.de/COPYING.md
|
|
||||||
#
|
|
||||||
# requires RouterOS, version=7.15
|
|
||||||
# requires device-mode, fetch
|
|
||||||
#
|
|
||||||
# check for certificate validity
|
|
||||||
# https://rsc.eworm.de/doc/check-certificates.md
|
|
||||||
|
|
||||||
:global GlobalFunctionsReady;
|
|
||||||
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
|
|
||||||
|
|
||||||
:local ExitOK false;
|
|
||||||
: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 CertName [ :tostr $2 ];
|
|
||||||
:local FetchName [ :tostr $3 ];
|
|
||||||
|
|
||||||
:global CertRenewUrl;
|
|
||||||
:global CertRenewPass;
|
|
||||||
|
|
||||||
:global CertificateNameByCN;
|
|
||||||
:global EscapeForRegEx;
|
|
||||||
:global FetchUserAgentStr;
|
|
||||||
:global LogPrint;
|
|
||||||
:global RmFile;
|
|
||||||
:global UrlEncode;
|
|
||||||
:global WaitForFile;
|
|
||||||
|
|
||||||
:foreach Type in={ "p12"; "pem" } do={
|
|
||||||
:local CertFileName ([ $UrlEncode $FetchName ] . "." . $Type);
|
|
||||||
$LogPrint debug $ScriptName ("Trying type '" . $Type . "' for '" . $CertName . \
|
|
||||||
"' (file '" . $CertFileName . "')...");
|
|
||||||
|
|
||||||
: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 I,PassPhrase in=$CertRenewPass do={
|
|
||||||
:do {
|
|
||||||
$LogPrint debug $ScriptName ("Trying " . $I . ". passphrase... ");
|
|
||||||
:local Result [ /certificate/import file-name=$CertFileName passphrase=$PassPhrase as-value ];
|
|
||||||
:if ($Result->"decryption-failures" = 0) do={
|
|
||||||
$LogPrint debug $ScriptName ("Success!");
|
|
||||||
:set DecryptionFailed false;
|
|
||||||
}
|
|
||||||
} on-error={ }
|
|
||||||
}
|
|
||||||
$RmFile $CertFileName;
|
|
||||||
|
|
||||||
:if ($DecryptionFailed = true) do={
|
|
||||||
$LogPrint warning $ScriptName ("Decryption failed for certificate file '" . $CertFileName . "'.");
|
|
||||||
}
|
|
||||||
|
|
||||||
:foreach CertInChain in=[ /certificate/find where common-name!=$CertName !private-key \
|
|
||||||
name~("^" . [ $EscapeForRegEx $CertFileName ] . "_[0-9]+\$") \
|
|
||||||
!(subject-alt-name~("(^|\\W)(DNS|IP):" . [ $EscapeForRegEx $CertName ] . "(\\W|\$)")) \
|
|
||||||
!(common-name=[]) ] do={
|
|
||||||
$CertificateNameByCN [ /certificate/get $CertInChain common-name ];
|
|
||||||
}
|
|
||||||
|
|
||||||
:return true;
|
|
||||||
} on-error={
|
|
||||||
$LogPrint debug $ScriptName ("Could not download certificate file '" . $CertFileName . "'.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
:return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
: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={
|
|
||||||
:set ExitOK true;
|
|
||||||
:error false;
|
|
||||||
}
|
|
||||||
$WaitFullyConnected;
|
|
||||||
|
|
||||||
:foreach Cert in=[ /certificate/find where !revoked !ca !scep-url expires-after<$CertRenewTime ] do={
|
|
||||||
:local CertVal [ /certificate/get $Cert ];
|
|
||||||
:local LastName;
|
|
||||||
:local FetchName;
|
|
||||||
|
|
||||||
: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 FetchName $LastName;
|
|
||||||
:set ImportSuccess [ $CheckCertificatesDownloadImport $ScriptName $LastName $FetchName ];
|
|
||||||
:foreach SAN in=($CertVal->"subject-alt-name") do={
|
|
||||||
:if ($ImportSuccess = false) do={
|
|
||||||
:set LastName [ :pick $SAN ([ :find $SAN ":" ] + 1) [ :len $SAN ] ];
|
|
||||||
:set FetchName $LastName;
|
|
||||||
:set ImportSuccess [ $CheckCertificatesDownloadImport $ScriptName $LastName $FetchName ];
|
|
||||||
:if ($ImportSuccess = false && [ :pick $LastName 0 2 ] = "*.") do={
|
|
||||||
:set FetchName ("star." . [ :pick $LastName 2 [ :len $LastName ] ]);
|
|
||||||
:set ImportSuccess [ $CheckCertificatesDownloadImport $ScriptName $LastName $FetchName ];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
: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.");
|
|
||||||
|
|
||||||
:local CertNew [ /certificate/find where name~("^" . [ $EscapeForRegEx [ $UrlEncode $FetchName ] ] . "\\.(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 Cert $CertNew;
|
|
||||||
: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 $Cert ]) });
|
|
||||||
$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={
|
|
||||||
:global ExitError; $ExitError $ExitOK [ :jobname ];
|
|
||||||
}
|
|
||||||
121
check-health
Normal file
|
|
@ -0,0 +1,121 @@
|
||||||
|
#!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 FormatVoltage do={
|
||||||
|
:local Voltage [ :tonum $1 ];
|
||||||
|
:return (($Voltage / 10) . "." . [ :pick $Voltage ([ :len $Voltage ] - 1) ] . "V");
|
||||||
|
}
|
||||||
|
|
||||||
|
:local CheckHealthCurrent [ / system health get ];
|
||||||
|
|
||||||
|
:if ([ :len $CheckHealthCurrent ] = 0) do={
|
||||||
|
$LogPrintExit2 error $0 ("Your device does not provide any health values.") true;
|
||||||
|
}
|
||||||
|
|
||||||
|
:if ([ :typeof $CheckHealthTemperatureNotified ] != "array") do={
|
||||||
|
:set CheckHealthTemperatureNotified [ :toarray "" ];
|
||||||
|
}
|
||||||
|
|
||||||
|
$ScriptLock $0;
|
||||||
|
|
||||||
|
:foreach Name,Voltage in=$CheckHealthCurrent do={
|
||||||
|
:if ($Name ~ "(battery|voltage)" && \
|
||||||
|
[ :typeof ($CheckHealthLast->$Name) ] = "num" && \
|
||||||
|
[ :typeof $Voltage ] = "num") do={
|
||||||
|
:if ($CheckHealthLast->$Name * (100 + $CheckHealthVoltagePercent) < $Voltage * 100 || \
|
||||||
|
$CheckHealthLast->$Name * 100 > $Voltage * (100 + $CheckHealthVoltagePercent)) do={
|
||||||
|
$SendNotification2 ({ origin=$0; \
|
||||||
|
subject=([ $SymbolForNotification ("high-voltage-sign,chart-" . [ $IfThenElse ($CheckHealthLast->$Name < \
|
||||||
|
$Voltage) "in" "de" ] . "creasing") ] . "Health warning: " . $Name); \
|
||||||
|
message=("The " . $Name . " on " . $Identity . " jumped more than " . $CheckHealthVoltagePercent . "%.\n\n" . \
|
||||||
|
"old value: " . [ $FormatVoltage ($CheckHealthLast->$Name) ] . "\n" . \
|
||||||
|
"new value: " . [ $FormatVoltage $Voltage ]) });
|
||||||
|
} else={
|
||||||
|
:if ($Voltage <= $CheckHealthVoltageLow && $CheckHealthLast->$Name > $CheckHealthVoltageLow) do={
|
||||||
|
$SendNotification2 ({ origin=$0; \
|
||||||
|
subject=([ $SymbolForNotification "high-voltage-sign,chart-decreasing" ] . "Health warning: Low " . $Name); \
|
||||||
|
message=("The " . $Name . " on " . $Identity . " dropped to " . [ $FormatVoltage $Voltage ] . " below hard limit.") });
|
||||||
|
}
|
||||||
|
:if ($Voltage > $CheckHealthVoltageLow && $CheckHealthLast->$Name <= $CheckHealthVoltageLow) do={
|
||||||
|
$SendNotification2 ({ origin=$0; \
|
||||||
|
subject=([ $SymbolForNotification "high-voltage-sign,chart-increasing" ] . "Health recovery: Low " . $Name); \
|
||||||
|
message=("The " . $Name . " on " . $Identity . " recovered to " . [ $FormatVoltage $Voltage ] . " above hard limit.") });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:foreach Name,PSU in=$CheckHealthCurrent do={
|
||||||
|
:if ($Name ~ "psu.*-state" && \
|
||||||
|
[ :typeof ($CheckHealthLast->$Name) ] = "str" && \
|
||||||
|
[ :typeof $PSU ] = "str") do={
|
||||||
|
:if ($CheckHealthLast->$Name = "ok" && \
|
||||||
|
$PSU != "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" && \
|
||||||
|
$PSU = "ok") do={
|
||||||
|
$SendNotification2 ({ origin=$0; \
|
||||||
|
subject=([ $SymbolForNotification "white-heavy-check-mark" ] . "Health recovery: " . $Name); \
|
||||||
|
message=("The power supply unit '" . $Name . "' on " . $Identity . " recovered!") });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:foreach Name,Temperature in=$CheckHealthCurrent do={
|
||||||
|
:if ($Name ~ "temperature" && \
|
||||||
|
[ :typeof $Temperature ] = "num") 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 $Name ];
|
||||||
|
:while ($Temperature != $Validate) do={
|
||||||
|
:set Temperature $Validate;
|
||||||
|
:set Validate [ / system health get $Name ];
|
||||||
|
}
|
||||||
|
:if ($Temperature > $CheckHealthTemperature->$Name && \
|
||||||
|
$CheckHealthTemperatureNotified->$Name != true) do={
|
||||||
|
$SendNotification2 ({ origin=$0; \
|
||||||
|
subject=([ $SymbolForNotification "fire" ] . "Health warning: " . $Name); \
|
||||||
|
message=("The " . $Name . " on " . $Identity . " is above threshold: " . \
|
||||||
|
$Temperature . "\C2\B0" . "C") });
|
||||||
|
:set ($CheckHealthTemperatureNotified->$Name) true;
|
||||||
|
}
|
||||||
|
:if ($Temperature <= ($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: " . \
|
||||||
|
$Temperature . "\C2\B0" . "C") });
|
||||||
|
:set ($CheckHealthTemperatureNotified->$Name) false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:set CheckHealthLast $CheckHealthCurrent;
|
||||||
|
|
@ -1,48 +0,0 @@
|
||||||
#!rsc by RouterOS
|
|
||||||
# RouterOS script: check-health.d/state
|
|
||||||
# Copyright (c) 2019-2025 Christian Hesse <mail@eworm.de>
|
|
||||||
# https://rsc.eworm.de/COPYING.md
|
|
||||||
#
|
|
||||||
# requires RouterOS, version=7.15
|
|
||||||
#
|
|
||||||
# check for RouterOS health state - state plugin
|
|
||||||
# https://rsc.eworm.de/doc/check-health.md
|
|
||||||
|
|
||||||
:global CheckHealthPlugins;
|
|
||||||
|
|
||||||
:set ($CheckHealthPlugins->[ :jobname ]) do={
|
|
||||||
:local FuncName [ :tostr $0 ];
|
|
||||||
|
|
||||||
:global CheckHealthLast;
|
|
||||||
:global Identity;
|
|
||||||
|
|
||||||
:global LogPrint;
|
|
||||||
:global SendNotification2;
|
|
||||||
:global SymbolForNotification;
|
|
||||||
|
|
||||||
:if ([ :len [ /system/health/find where type="" name~"-state\$"] ] = 0) do={
|
|
||||||
$LogPrint debug $FuncName ("Your device does not provide any state health values.");
|
|
||||||
:return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
:foreach State in=[ /system/health/find where type="" name~"-state\$" ] do={
|
|
||||||
:local Name [ /system/health/get $State name ];
|
|
||||||
:local Value [ /system/health/get $State value ];
|
|
||||||
|
|
||||||
:if ([ :typeof ($CheckHealthLast->$Name) ] != "nothing") do={
|
|
||||||
:if ($CheckHealthLast->$Name = "ok" && \
|
|
||||||
$Value != "ok") do={
|
|
||||||
$SendNotification2 ({ origin=$FuncName; \
|
|
||||||
subject=([ $SymbolForNotification "cross-mark" ] . "Health warning: " . $Name); \
|
|
||||||
message=("The device '" . $Name . "' on " . $Identity . " failed!") });
|
|
||||||
}
|
|
||||||
:if ($CheckHealthLast->$Name != "ok" && \
|
|
||||||
$Value = "ok") do={
|
|
||||||
$SendNotification2 ({ origin=$FuncName; \
|
|
||||||
subject=([ $SymbolForNotification "white-heavy-check-mark" ] . "Health recovery: " . $Name); \
|
|
||||||
message=("The device '" . $Name . "' on " . $Identity . " recovered!") });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
:set ($CheckHealthLast->$Name) $Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,74 +0,0 @@
|
||||||
#!rsc by RouterOS
|
|
||||||
# RouterOS script: check-health.d/temperature
|
|
||||||
# Copyright (c) 2019-2025 Christian Hesse <mail@eworm.de>
|
|
||||||
# https://rsc.eworm.de/COPYING.md
|
|
||||||
#
|
|
||||||
# requires RouterOS, version=7.15
|
|
||||||
#
|
|
||||||
# check for RouterOS health state - temperature plugin
|
|
||||||
# https://rsc.eworm.de/doc/check-health.md
|
|
||||||
|
|
||||||
:global CheckHealthPlugins;
|
|
||||||
|
|
||||||
:set ($CheckHealthPlugins->[ :jobname ]) do={
|
|
||||||
:local FuncName [ :tostr $0 ];
|
|
||||||
|
|
||||||
:global CheckHealthLast;
|
|
||||||
:global CheckHealthTemperature;
|
|
||||||
:global CheckHealthTemperatureDeviation;
|
|
||||||
:global CheckHealthTemperatureNotified;
|
|
||||||
:global Identity;
|
|
||||||
|
|
||||||
:global LogPrint;
|
|
||||||
:global SendNotification2;
|
|
||||||
:global SymbolForNotification;
|
|
||||||
|
|
||||||
:if ([ :len [ /system/health/find where type="C" ] ] = 0) do={
|
|
||||||
$LogPrint debug $FuncName ("Your device does not provide any voltage health values.");
|
|
||||||
:return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
:local TempToNum do={
|
|
||||||
:global CharacterReplace;
|
|
||||||
:local T [ :toarray [ $CharacterReplace $1 "." "," ] ];
|
|
||||||
:return ($T->0 * 10 + $T->1);
|
|
||||||
}
|
|
||||||
|
|
||||||
:if ([ :typeof $CheckHealthTemperatureNotified ] != "array") do={
|
|
||||||
:set CheckHealthTemperatureNotified ({});
|
|
||||||
}
|
|
||||||
|
|
||||||
: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 $FuncName ("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=$FuncName; \
|
|
||||||
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=$FuncName; \
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,63 +0,0 @@
|
||||||
#!rsc by RouterOS
|
|
||||||
# RouterOS script: check-health.d/voltage
|
|
||||||
# Copyright (c) 2019-2025 Christian Hesse <mail@eworm.de>
|
|
||||||
# https://rsc.eworm.de/COPYING.md
|
|
||||||
#
|
|
||||||
# requires RouterOS, version=7.15
|
|
||||||
#
|
|
||||||
# check for RouterOS health state - voltage plugin
|
|
||||||
# https://rsc.eworm.de/doc/check-health.md
|
|
||||||
|
|
||||||
:global CheckHealthPlugins;
|
|
||||||
|
|
||||||
:set ($CheckHealthPlugins->[ :jobname ]) do={
|
|
||||||
:local FuncName [ :tostr $0 ];
|
|
||||||
|
|
||||||
:global CheckHealthLast;
|
|
||||||
:global CheckHealthVoltageLow;
|
|
||||||
:global CheckHealthVoltagePercent;
|
|
||||||
:global Identity;
|
|
||||||
|
|
||||||
:global FormatLine;
|
|
||||||
:global IfThenElse;
|
|
||||||
:global LogPrint;
|
|
||||||
:global SendNotification2;
|
|
||||||
:global SymbolForNotification;
|
|
||||||
|
|
||||||
:if ([ :len [ /system/health/find where type="V" ] ] = 0) do={
|
|
||||||
$LogPrint debug $FuncName ("Your device does not provide any voltage health values.");
|
|
||||||
:return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
: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=$FuncName; \
|
|
||||||
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=$FuncName; \
|
|
||||||
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=$FuncName; \
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
110
check-health.rsc
|
|
@ -1,110 +0,0 @@
|
||||||
#!rsc by RouterOS
|
|
||||||
# RouterOS script: check-health
|
|
||||||
# Copyright (c) 2019-2025 Christian Hesse <mail@eworm.de>
|
|
||||||
# https://rsc.eworm.de/COPYING.md
|
|
||||||
#
|
|
||||||
# requires RouterOS, version=7.15
|
|
||||||
#
|
|
||||||
# check for RouterOS health state
|
|
||||||
# https://rsc.eworm.de/doc/check-health.md
|
|
||||||
|
|
||||||
:global GlobalFunctionsReady;
|
|
||||||
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
|
|
||||||
|
|
||||||
:local ExitOK false;
|
|
||||||
:do {
|
|
||||||
:local ScriptName [ :jobname ];
|
|
||||||
|
|
||||||
:global CheckHealthCPUUtilization;
|
|
||||||
:global CheckHealthCPUUtilizationNotified;
|
|
||||||
:global CheckHealthLast;
|
|
||||||
:global CheckHealthRAMUtilizationNotified;
|
|
||||||
:global Identity;
|
|
||||||
|
|
||||||
:global FormatLine;
|
|
||||||
:global HumanReadableNum;
|
|
||||||
:global IfThenElse;
|
|
||||||
:global LogPrint;
|
|
||||||
:global ScriptLock;
|
|
||||||
:global SendNotification2;
|
|
||||||
:global SymbolForNotification;
|
|
||||||
:global ValidateSyntax;
|
|
||||||
|
|
||||||
:local TempToNum do={
|
|
||||||
:global CharacterReplace;
|
|
||||||
:local T [ :toarray [ $CharacterReplace $1 "." "," ] ];
|
|
||||||
:return ($T->0 * 10 + $T->1);
|
|
||||||
}
|
|
||||||
|
|
||||||
:if ([ $ScriptLock $ScriptName ] = false) do={
|
|
||||||
:set ExitOK true;
|
|
||||||
: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 ] . "B") 8 ] . "\n" . \
|
|
||||||
[ $FormatLine "used" ([ $HumanReadableNum ($Resource->"total-memory" - $Resource->"free-memory") 1024 ] . "B") 8 ] . "\n" . \
|
|
||||||
[ $FormatLine "free" ([ $HumanReadableNum ($Resource->"free-memory") 1024 ] . "B") 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
:local Plugins [ /system/script/find where name~"^check-health.d/." ];
|
|
||||||
:if ([ :len $Plugins ] = 0) do={
|
|
||||||
$LogPrint debug $ScriptName ("No plugins installed.");
|
|
||||||
:set ExitOK true;
|
|
||||||
:error true;
|
|
||||||
}
|
|
||||||
|
|
||||||
:global CheckHealthPlugins ({});
|
|
||||||
:if ([ :typeof $CheckHealthLast ] != "array") do={
|
|
||||||
:set CheckHealthLast ({});
|
|
||||||
}
|
|
||||||
|
|
||||||
:foreach Plugin in=$Plugins do={
|
|
||||||
:local PluginVal [ /system/script/get $Plugin ];
|
|
||||||
:if ([ $ValidateSyntax ($PluginVal->"source") ] = true) do={
|
|
||||||
:do {
|
|
||||||
/system/script/run $Plugin;
|
|
||||||
} on-error={
|
|
||||||
$LogPrint error $ScriptName ("Plugin '" . $ScriptVal->"name" . "' failed to run.");
|
|
||||||
}
|
|
||||||
} else={
|
|
||||||
$LogPrint error $ScriptName ("Plugin '" . $ScriptVal->"name" . "' failed syntax validation, skipping.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
:foreach PluginName,Discard in=$CheckHealthPlugins do={
|
|
||||||
($CheckHealthPlugins->$PluginName) \
|
|
||||||
("\$CheckHealthPlugins->\"" . $PluginName . "\"");
|
|
||||||
}
|
|
||||||
|
|
||||||
:set CheckHealthPlugins;
|
|
||||||
} on-error={
|
|
||||||
:global ExitError; $ExitError $ExitOK [ :jobname ];
|
|
||||||
}
|
|
||||||
82
check-lte-firmware-upgrade
Normal file
|
|
@ -0,0 +1,82 @@
|
||||||
|
#!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 [ :toarray "" ];
|
||||||
|
}
|
||||||
|
|
||||||
|
: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 info $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 "check-lte-firmware-upgrade" ] = true) do={
|
||||||
|
$LogPrintExit2 info $0 ("No firmware upgrade available for LTE interface " . $IntName . ".") false;
|
||||||
|
}
|
||||||
|
:return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
:if ([ $ScriptFromTerminal "check-lte-firmware-upgrade" ] = 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;
|
||||||
|
}
|
||||||
|
|
@ -1,107 +0,0 @@
|
||||||
#!rsc by RouterOS
|
|
||||||
# RouterOS script: check-lte-firmware-upgrade
|
|
||||||
# Copyright (c) 2018-2025 Christian Hesse <mail@eworm.de>
|
|
||||||
# https://rsc.eworm.de/COPYING.md
|
|
||||||
#
|
|
||||||
# requires RouterOS, version=7.15
|
|
||||||
#
|
|
||||||
# check for LTE firmware upgrade, send notification
|
|
||||||
# https://rsc.eworm.de/doc/check-lte-firmware-upgrade.md
|
|
||||||
|
|
||||||
:global GlobalFunctionsReady;
|
|
||||||
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
|
|
||||||
|
|
||||||
:local ExitOK false;
|
|
||||||
:do {
|
|
||||||
:local ScriptName [ :jobname ];
|
|
||||||
|
|
||||||
:global SentLteFirmwareUpgradeNotification;
|
|
||||||
|
|
||||||
:global ScriptLock;
|
|
||||||
|
|
||||||
:if ([ $ScriptLock $ScriptName ] = false) do={
|
|
||||||
:set ExitOK true;
|
|
||||||
: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 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={
|
|
||||||
:global ExitError; $ExitError $ExitOK [ :jobname ];
|
|
||||||
}
|
|
||||||
145
check-routeros-update
Normal file
|
|
@ -0,0 +1,145 @@
|
||||||
|
#!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 ([ :len [ / system package find where name="wireless" disabled=no ] ] > 0) do={
|
||||||
|
: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 ([ :len ($Update->"latest-version") ] = 0) do={
|
||||||
|
$LogPrintExit2 info $0 ("An empty string is not a valid version.") true;
|
||||||
|
}
|
||||||
|
|
||||||
|
: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 ($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");
|
||||||
|
}
|
||||||
|
|
@ -1,239 +0,0 @@
|
||||||
#!rsc by RouterOS
|
|
||||||
# RouterOS script: check-routeros-update
|
|
||||||
# Copyright (c) 2013-2025 Christian Hesse <mail@eworm.de>
|
|
||||||
# https://rsc.eworm.de/COPYING.md
|
|
||||||
#
|
|
||||||
# requires RouterOS, version=7.15
|
|
||||||
# requires device-mode, fetch, scheduler
|
|
||||||
#
|
|
||||||
# check for RouterOS update, send notification and/or install
|
|
||||||
# https://rsc.eworm.de/doc/check-routeros-update.md
|
|
||||||
|
|
||||||
:global GlobalFunctionsReady;
|
|
||||||
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
|
|
||||||
|
|
||||||
:local ExitOK false;
|
|
||||||
: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={
|
|
||||||
:local ScriptName [ :tostr $1 ];
|
|
||||||
|
|
||||||
:global LogPrint;
|
|
||||||
|
|
||||||
:if ([ :len [ /system/script/find where name="packages-update" ] ] > 0) do={
|
|
||||||
/system/script/run packages-update;
|
|
||||||
} else={
|
|
||||||
/system/package/update/install without-paging;
|
|
||||||
}
|
|
||||||
$LogPrint info $ScriptName ("Waiting for system to reboot.");
|
|
||||||
}
|
|
||||||
|
|
||||||
:if ([ $ScriptLock $ScriptName ] = false) do={
|
|
||||||
:set ExitOK true;
|
|
||||||
:error false;
|
|
||||||
}
|
|
||||||
|
|
||||||
:if ([ :len [ /system/scheduler/find where name="running-from-backup-partition" ] ] > 0) do={
|
|
||||||
$LogPrint warning $ScriptName ("Running from backup partition, refusing to act.");
|
|
||||||
:set ExitOK true;
|
|
||||||
:error false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$WaitFullyConnected;
|
|
||||||
|
|
||||||
:if ([ :len [ /system/scheduler/find where name="_RebootForUpdate" ] ] > 0) do={
|
|
||||||
:set ExitOK true;
|
|
||||||
:error "A reboot for update is already scheduled.";
|
|
||||||
}
|
|
||||||
|
|
||||||
:local License [ /system/license/get ];
|
|
||||||
:if ([ :typeof ($License->"deadline-at") ] = "str") do={
|
|
||||||
:if ([ :len ($License->"next-renewal-at") ] = 0 && ($License->"limited-upgrades") = true) do={
|
|
||||||
$LogPrint warning $ScriptName ("Your license expired on " . ($License->"deadline-at") . "!");
|
|
||||||
$SendNotification2 ({ origin=$ScriptName; \
|
|
||||||
subject=([ $SymbolForNotification "warning-sign" ] . "License expired!"); \
|
|
||||||
message=("Your license expired on " . ($License->"deadline-at") . \
|
|
||||||
", can no longer update RouterOS on " . $Identity . "...") });
|
|
||||||
:set ExitOK true;
|
|
||||||
:error false;
|
|
||||||
}
|
|
||||||
|
|
||||||
:if ([ :totime ($License->"deadline-at") ] - 3w < [ :timestamp ]) do={
|
|
||||||
$LogPrint warning $ScriptName ("Your license will expire on " . ($License->"deadline-at") . "!");
|
|
||||||
$SendNotification2 ({ origin=$ScriptName; \
|
|
||||||
subject=([ $SymbolForNotification "warning-sign" ] . "License about to expire!"); \
|
|
||||||
message=("Your license failed to renew and is about to expire on " . \
|
|
||||||
($License->"deadline-at") . " on " . $Identity . "...") });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$LogPrint debug $ScriptName ("Checking for updates...");
|
|
||||||
/system/package/update/check-for-updates without-paging as-value;
|
|
||||||
:local Update [ /system/package/update/get ];
|
|
||||||
|
|
||||||
:if (($Update->"installed-version") = ($Update->"latest-version")) do={
|
|
||||||
:if ([ $ScriptFromTerminal $ScriptName ] = true) do={
|
|
||||||
$LogPrint info $ScriptName ("System is already up to date.");
|
|
||||||
}
|
|
||||||
:set ExitOK true;
|
|
||||||
:error true;
|
|
||||||
}
|
|
||||||
|
|
||||||
:if ([ :len ($Update->"latest-version") ] = 0) do={
|
|
||||||
$LogPrint info $ScriptName ("Received an empty version string from server.");
|
|
||||||
:set ExitOK true;
|
|
||||||
:error false;
|
|
||||||
}
|
|
||||||
|
|
||||||
:local NumInstalled [ $VersionToNum ($Update->"installed-version") ];
|
|
||||||
:local NumLatest [ $VersionToNum ($Update->"latest-version") ];
|
|
||||||
:local BitMask [ $VersionToNum "255.255zero0" ];
|
|
||||||
:local NumInstalledFeature ($NumInstalled & $BitMask);
|
|
||||||
:local NumLatestFeature ($NumLatest & $BitMask);
|
|
||||||
:local Link ("https://mikrotik.com/download/changelogs/" . $Update->"channel" . "-release-tree");
|
|
||||||
|
|
||||||
:if ($NumLatest < [ $VersionToNum "7.0" ]) do={
|
|
||||||
$LogPrint warning $ScriptName ("The version '" . ($Update->"latest-version") . "' is not a valid version.");
|
|
||||||
:set ExitOK true;
|
|
||||||
: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 $ScriptName;
|
|
||||||
:set ExitOK true;
|
|
||||||
:error true;
|
|
||||||
}
|
|
||||||
|
|
||||||
:if ($SafeUpdatePatch = true && $NumInstalledFeature = $NumLatestFeature) 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 $ScriptName;
|
|
||||||
:set ExitOK true;
|
|
||||||
:error true;
|
|
||||||
}
|
|
||||||
|
|
||||||
: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 $ScriptName;
|
|
||||||
:set ExitOK true;
|
|
||||||
:error true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
: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 $ScriptName;
|
|
||||||
:set ExitOK true;
|
|
||||||
:error true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
:if ([ $ScriptFromTerminal $ScriptName ] = true) do={
|
|
||||||
:if (($Update->"channel") = "testing" && $NumInstalledFeature < $NumLatestFeature) do={
|
|
||||||
:put ("This is a feature update in testing channel. Switch to channel 'stable'? [y/N]");
|
|
||||||
:if (([ /terminal/inkey timeout=60 ] % 32) = 25) do={
|
|
||||||
/system/package/update/set channel=stable;
|
|
||||||
$LogPrint info $ScriptName ("Switched to channel 'stable', please re-run!");
|
|
||||||
:set ExitOK true;
|
|
||||||
:error true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
:put ("Do you want to install RouterOS version " . $Update->"latest-version" . "? [y/N]");
|
|
||||||
:if (([ /terminal/inkey timeout=60 ] % 32) = 25) do={
|
|
||||||
$DoUpdate $ScriptName;
|
|
||||||
:set ExitOK true;
|
|
||||||
:error true;
|
|
||||||
} else={
|
|
||||||
:put "Canceled...";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
:if ($SentRouterosUpdateNotification = $Update->"latest-version") do={
|
|
||||||
$LogPrint info $ScriptName ("Already sent the RouterOS update notification for version " . \
|
|
||||||
$Update->"latest-version" . ".");
|
|
||||||
:set ExitOK true;
|
|
||||||
: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" . ".");
|
|
||||||
:set ExitOK true;
|
|
||||||
: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={
|
|
||||||
:global ExitError; $ExitError $ExitOK [ :jobname ];
|
|
||||||
}
|
|
||||||
85
collect-wireless-mac.capsman
Normal file
|
|
@ -0,0 +1,85 @@
|
||||||
|
#!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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,100 +0,0 @@
|
||||||
#!rsc by RouterOS
|
|
||||||
# RouterOS script: collect-wireless-mac.capsman
|
|
||||||
# Copyright (c) 2013-2025 Christian Hesse <mail@eworm.de>
|
|
||||||
# https://rsc.eworm.de/COPYING.md
|
|
||||||
#
|
|
||||||
# provides: lease-script, order=40
|
|
||||||
# requires RouterOS, version=7.15
|
|
||||||
#
|
|
||||||
# collect wireless mac adresses in access list
|
|
||||||
# https://rsc.eworm.de/doc/collect-wireless-mac.md
|
|
||||||
#
|
|
||||||
# !! Do not edit this file, it is generated from template!
|
|
||||||
|
|
||||||
:global GlobalFunctionsReady;
|
|
||||||
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
|
|
||||||
|
|
||||||
:local ExitOK false;
|
|
||||||
: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={
|
|
||||||
:set ExitOK true;
|
|
||||||
: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={
|
|
||||||
:global ExitError; $ExitError $ExitOK [ :jobname ];
|
|
||||||
}
|
|
||||||
86
collect-wireless-mac.local
Normal file
|
|
@ -0,0 +1,86 @@
|
||||||
|
#!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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,101 +0,0 @@
|
||||||
#!rsc by RouterOS
|
|
||||||
# RouterOS script: collect-wireless-mac.local
|
|
||||||
# Copyright (c) 2013-2025 Christian Hesse <mail@eworm.de>
|
|
||||||
# https://rsc.eworm.de/COPYING.md
|
|
||||||
#
|
|
||||||
# provides: lease-script, order=40
|
|
||||||
# requires RouterOS, version=7.15
|
|
||||||
#
|
|
||||||
# collect wireless mac adresses in access list
|
|
||||||
# https://rsc.eworm.de/doc/collect-wireless-mac.md
|
|
||||||
#
|
|
||||||
# !! Do not edit this file, it is generated from template!
|
|
||||||
|
|
||||||
:global GlobalFunctionsReady;
|
|
||||||
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
|
|
||||||
|
|
||||||
:local ExitOK false;
|
|
||||||
: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={
|
|
||||||
:set ExitOK true;
|
|
||||||
: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={
|
|
||||||
:global ExitError; $ExitError $ExitOK [ :jobname ];
|
|
||||||
}
|
|
||||||
87
collect-wireless-mac.template
Normal file
|
|
@ -0,0 +1,87 @@
|
||||||
|
#!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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,118 +0,0 @@
|
||||||
#!rsc by RouterOS
|
|
||||||
# RouterOS script: collect-wireless-mac%TEMPL%
|
|
||||||
# Copyright (c) 2013-2025 Christian Hesse <mail@eworm.de>
|
|
||||||
# https://rsc.eworm.de/COPYING.md
|
|
||||||
#
|
|
||||||
# provides: lease-script, order=40
|
|
||||||
# requires RouterOS, version=7.15
|
|
||||||
#
|
|
||||||
# collect wireless mac adresses in access list
|
|
||||||
# https://rsc.eworm.de/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; }
|
|
||||||
|
|
||||||
:local ExitOK false;
|
|
||||||
: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={
|
|
||||||
:set ExitOK true;
|
|
||||||
: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={
|
|
||||||
:global ExitError; $ExitError $ExitOK [ :jobname ];
|
|
||||||
}
|
|
||||||
|
|
@ -1,100 +0,0 @@
|
||||||
#!rsc by RouterOS
|
|
||||||
# RouterOS script: collect-wireless-mac.wifi
|
|
||||||
# Copyright (c) 2013-2025 Christian Hesse <mail@eworm.de>
|
|
||||||
# https://rsc.eworm.de/COPYING.md
|
|
||||||
#
|
|
||||||
# provides: lease-script, order=40
|
|
||||||
# requires RouterOS, version=7.15
|
|
||||||
#
|
|
||||||
# collect wireless mac adresses in access list
|
|
||||||
# https://rsc.eworm.de/doc/collect-wireless-mac.md
|
|
||||||
#
|
|
||||||
# !! Do not edit this file, it is generated from template!
|
|
||||||
|
|
||||||
:global GlobalFunctionsReady;
|
|
||||||
:while ($GlobalFunctionsReady != true) do={ :delay 500ms; }
|
|
||||||
|
|
||||||
:local ExitOK false;
|
|
||||||
: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={
|
|
||||||
:set ExitOK true;
|
|
||||||
: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={
|
|
||||||
:global ExitError; $ExitError $ExitOK [ :jobname ];
|
|
||||||
}
|
|
||||||
|
Before Width: | Height: | Size: 41 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 25 KiB |
|
|
@ -1,12 +0,0 @@
|
||||||
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)));
|
|
||||||
}
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
body {
|
|
||||||
font-family: fira-sans, sans-serif;
|
|
||||||
font-size: 10pt;
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
||||||
|
|
@ -1,40 +0,0 @@
|
||||||
<!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>
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
function visible(cb, element) {
|
|
||||||
document.getElementById(element).style.display = cb.checked ? "block" : "none";
|
|
||||||
}
|
|
||||||
function update(cb, element) {
|
|
||||||
document.getElementById(element).innerHTML = cb.value;
|
|
||||||
}
|
|
||||||
|
|
@ -1,36 +0,0 @@
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
@ -1,35 +0,0 @@
|
||||||
<!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>
|
|
||||||