Create Macro or small script?

Hi,

What would be the simplest way to enable, disable a couples a firewall rules and others settings in one command ?

I don’t If it’s possible to write a resident script ?

Thanks.

The usual rule of scripting is: don’t. If you really have to, keep it short and simple. It’s especially not suggested to write resident scripts, again, if you can avoid it.

Mikrotik provides a lot of features for achieving dynamic behavior, e.g. for PPP (VPN) connections, it allows to automatically put the created interface into interface lists, add allocated addresses to address lists, etc. Carefully look at whether what you want can be accomplished using these features.

Of course it’s not always possible. The generic and useful formula for what you want is this:

/ip firewall filter [ find comment~"_disableme_" ] set disabled=yes

This finds all rules that have a comment that matches (contains) the regexp "_disableme_" and applies the specified changes to them.

Using the comment field to mark the rules/whatever you want to change is sort of a trope in MT scripting. It’s obviously much better than using numerical indices, etc.

EDIT: In most cases commands in your script behave as command executed in the terminal. Use the terminal to properly formulate and check your commands and then copy them to scripts. Scripts while being executed are notoriously hard to debug.

It’s worth to mention that you cannot add comments to dynamic entries. Only static ones could be marked/tagged with tem.

But (IMHO) also not entirely satisfying.
Besides what BartoszP just noted, the risk is that in 6 months or 1 year time the user will have completely forgot about the script, find the comment somehow inadequate and change it (What’s in a comment? Won’t a route by any other comment connect as well?).
Personally (but probably it is just me) I prefer to use the actual dst-address and gateway, if possible.

As to not being able to change/enable/disable dynamic entries this way… If you want to do this, you have other things to solve as well.

About changing comments unwittingly: that’s why I use a specific notation, mine is “_flag” to indicate that it’s used in a script. And that’s why I match using a regex, because that way a human-readable comment can be added as well and that part can be edited freely.

Matching routes based on the dst field may make sense in certain situations (specifically for multi-wan), OP’s question was specifically for firewall rules, where matching on the particulars of the rules is hard to do correctly and without unintended side-effects (e.g. accidentally matching a newly added rule as well because of not sufficiently narrow filtering.)

Anyway, just shared what I personally found useful. Everyone is allowed their own style.

Probably, in a perfect world, there would be an added field loosely like “Find selector” (separate from comment) and possibly with a confirmation request like “Do you really want to change contents of this field? (Scripts using it as selector might stop working.)” when editing it in Winbox and Webfig.

You can create a functions to do act as a “macro”. Parameters can be either shell-like using $1 $2… or it can take named parameters. Functions are a variable type, so you declare them using :global (or :local but those require more explanation). And they do “stay resident”, once load in user context.

To create a “macro”, which is a function, you use the do={} syntax:

:global logallfirewall do={
    :if ($1="") do={:error "must provide 'yes' or 'no'"}
    /ip/firewall/filter/set [find dynamic=no] log=$1 log-prefix="$prefix"  
}

You call it using:

[user@forum] > $logallfirewall yes prefix="added by macro" 
[user@forum] > $logallfirewall no

You can put any valid commands inside the function (which is declared using do={} on a variable). I just provide a dumb example. There some rules like if you want :global function to call another, it must be declared in function body to be available:

:global nologfirewall do={
    :global logallfirewall
    $logallfirewall no
}

To make them “persist”, you can add the :global declarations like above to a /system/script. Then in CLI to load it, which needs to be done once user session. You can schedule it too to do at startup.

But assuming something like above, you load them in CLI:
/system/script/run load-my-saved-functions

That’s the basics, the Scripting docs detail functions more. But that about is close to “TSR” as you’re going to get.

:local do={} are also valid, but they cannot call other functions nor can they reference variables outside it’s own do={}… Useful but more confusing than thinking functions are “global” - especially if you’re thinking in terms of “macros”

Function are variables too, so using a :global put in the current user’s environment.

If only there would be an additional usable “tag” field for all object (static/dynamic) ones.
Comment is a comment.

+1, no doubt. I often use [find comment~"#mytag"] to mimic it — but someone has to know a “comment is code”…

But, yes, I ignored some of the oddities/limitations of scripting ;). So explaining using a comment to “find” what you want, I skipped exactly for this reason. Also, trying to script /interface/bridge, well, harder than it should be. But in /ip/firewall there are plenty of ways to locate an entry (i.e. [find chain=input dst-port=80]) without resorting to comments.

But global functions do mimic “macros” and IMO not used often enough to stash away tricky syntax. Now, to be clear, if goal here was some “firewall actions” to invoke a script, that is not possible – only changing firewall config is allowed‡.

‡ and you’d likely want eBPF module if firewall action= is what you wanted “script” — RouterOS scripting be way, way too slow for packet-level processing

IMHO It is so simple addition that there should bo NO PROBLEMS to implement it.
Do not know internals but adding one field to each object in the configuration should be easy. Maybe it’s one line in the base entity object + adding search funcionality.

Please, please, please …

1 Like