-
Notifications
You must be signed in to change notification settings - Fork 98
Safe Mode
RouterOS Safe Mode is a safety net for risky configuration changes. While safe mode is active every configuration change is recorded, and if the session that owns safe mode disconnects without committing, RouterOS automatically rolls back all those changes. This is exactly what saves you when a firewall or interface change locks you out of the router — drop the connection and the box reverts itself.
tik4net exposes the four RouterOS safe-mode verbs on ITikConnection:
void SafeModeTake(); // /safe-mode/take — enter safe mode, bound to THIS connection
void SafeModeRelease(); // /safe-mode/release — keep the changes and leave safe mode (commit)
void SafeModeUnroll(); // /safe-mode/unroll — discard the changes now, stay connected (rollback)
bool SafeModeGet(); // is safe mode currently held by this connection? (client-side tracked)The most important behaviour needs no method call at all: if you take safe mode and the connection drops
before SafeModeRelease(), RouterOS rolls everything back on its own. SafeModeUnroll() is the explicit,
on-demand version of that rollback (without disconnecting).
Safe mode only makes sense on a persistent, session-oriented channel, because the whole mechanism hinges on "the session that took safe mode is still alive".
| Transport | Safe mode | Mechanism |
|---|---|---|
Api / ApiSsl
|
✅ take / release / unroll | scriptable `/safe-mode/take |
Telnet |
✅ take / release / unroll | terminal control keys: Ctrl+X takes, a second Ctrl+X commits, Ctrl+D unrolls — works on any RouterOS |
Ssh |
✅ take / release / unroll |
Ctrl+X takes/commits over the SSH shell; unroll uses the scriptable /safe-mode/unroll (RouterOS 7.18+) because Ctrl+D is the SSH EOF convention and would close the channel. Older RouterOS: unroll falls back to a disconnect-rollback |
MacTelnet |
✅ take / release / unroll | same control keys, over the MAC-layer terminal |
WinboxCli / WinboxCliMac
|
✅ take / release / unroll | same control keys, over the encrypted WinBox terminal |
WinboxNative / WinboxNativeMac
|
✅ take / release | native M2 toggleSafeMode commands (handler [17], take 0x80003 / release 0x80005). No in-place unroll — to roll back, drop the connection without releasing. RouterOS 7.18+ |
Rest / RestSsl
|
❌ | stateless — verified: /safe-mode/take returns OK but the change is never rolled back after the HTTP request ends, so there is no protection. Throws NotSupportedException
|
if (connection.Supports(TikConnectionCapability.SafeMode))
{
// SafeModeTake/Release/Unroll/Get are available
}On the CLI transports RouterOS changes the shell prompt to [admin@MikroTik] <SAFE> > while safe mode is held;
tik4net recognises that prompt transparently, so all your normal commands keep working between
SafeModeTake() and SafeModeRelease().
Why Ctrl+X on the CLI instead of
/safe-mode/take? The control keys work on every RouterOS version, including those older than 7.18 where the scriptable/safe-modecommands do not exist. The binary-API and native-WinBox paths use the scriptable mechanism because they have no terminal to send keystrokes to.
Make a batch of changes under the safety net, then commit so they become permanent:
using (ITikConnection connection = ConnectionFactory.CreateConnection(TikConnectionType.Api))
{
connection.Open(HOST, USER, PASS);
connection.SafeModeTake(); // ← from here, every change is reversible
try
{
// ... risky changes — e.g. tighten the firewall ...
connection.CreateCommandAndParameters("/ip/firewall/filter/add",
"chain", "input",
"action", "drop",
"src-address", "10.0.0.0/8").ExecuteNonQuery();
// still reachable? good — make it permanent
connection.SafeModeRelease(); // ← changes are now kept; a later disconnect won't undo them
}
catch
{
// Do NOT release. SafeModeUnroll() discards immediately, or just let the using-block
// close the connection — either way RouterOS rolls everything back.
connection.SafeModeUnroll();
throw;
}
}If anything goes wrong — an exception, a lost link, the process dies — simply never call
SafeModeRelease(). When the connection drops, RouterOS reverts every change made since SafeModeTake():
string ruleId;
using (ITikConnection connection = ConnectionFactory.CreateConnection(TikConnectionType.Api))
{
connection.Open(HOST, USER, PASS);
connection.SafeModeTake();
ruleId = connection.CreateCommandAndParameters("/ip/firewall/filter/add",
"chain", "input",
"action", "drop",
"src-address", "0.0.0.0/0") // oops — this would lock everyone out
.ExecuteScalar();
// We deliberately do NOT release and just leave the using-block:
// Dispose() closes the connection, the safe-mode owner is gone,
// and RouterOS rolls the firewall rule back on its own.
}
// Reconnect: the rule added above is gone — RouterOS rolled it back.
using (ITikConnection check = ConnectionFactory.CreateConnection(TikConnectionType.Api))
{
check.Open(HOST, USER, PASS);
var found = check.CreateCommandAndParameters("/ip/firewall/filter/print", TikSpecialProperties.Id, ruleId)
.ExecuteList();
// found is empty — the change was rolled back
}SafeModeUnroll() reverts the changes immediately and keeps the connection usable — handy to abandon a failed
attempt and retry on the same session (not available on native WinBox):
connection.SafeModeTake();
try
{
ApplyRiskyConfig(connection);
if (!StillReachable(connection))
connection.SafeModeUnroll(); // back to the pre-take state, connection stays open
else
connection.SafeModeRelease();
}
finally
{
if (connection.SafeModeGet()) // still holding it? (e.g. an exception slipped through)
connection.SafeModeUnroll();
}Understanding the router-side model explains the edge cases you will meet:
-
Owned by one session at a time. Safe mode has a single owner (
/safe-mode/printshowsowner=api/console/winbox, theuser, andenabled/current). A secondSafeModeTake()from anyone else is refused — tik4net surfaces it as aTikCommandException("Safe mode is already held by another session"). -
Rollback is triggered by the owner session ending. On a clean
Close()this is near-instant; on a hard network loss RouterOS waits for its connection-tracking timeout (a few minutes) before reverting. -
The lock can linger after a drop — the key gotcha. When the owning session disconnects without
releasing, RouterOS rolls the changes back but may keep the lock itself held by that now-dead session until
the timeout expires. A new connection then sees
enabled=trueand gets "Safe Mode is taken by current user in another session" when it tries to take it. This is normal RouterOS behaviour, not a tik4net bug — it is the same lock you would see in WinBox/console after an unclean exit. - ~100-action history limit. Safe mode tracks at most ~100 actions; exceeding the limit ends safe mode and keeps the changes (no rollback). Keep safe-mode batches small.
A release from any fresh session of the same user clears a stale lock (it commits the dead session's
already-rolled-back — i.e. empty — change set and drops the enabled flag). First inspect, then clear:
// Inspect — is a lock held, and by whom?
using (var conn = ConnectionFactory.OpenConnection(TikConnectionType.Api, HOST, USER, PASS))
{
var st = conn.CreateCommand("/safe-mode/print").ExecuteSingleRow();
// st["enabled"], st["owner"], st["user"], st["current"]
// Clear it: a release from a fresh session releases the stale hold. Harmless if nothing is held.
conn.CreateCommand("/safe-mode/release").ExecuteNonQuery();
}Equivalent from a terminal / WinBox console: open a new session and press Ctrl+X twice (take, then release),
or run /safe-mode/release. If release is refused because the lock is owned elsewhere, take it over first with
/safe-mode/take (=on-error=unroll to discard whatever the dead session staged) and then release:
conn.CreateCommandAndParameters("/safe-mode/take", "on-error", "unroll").ExecuteNonQuery();
conn.CreateCommand("/safe-mode/release").ExecuteNonQuery();tik4net's own test suite clears any stale lock this way (a
/safe-mode/releasefrom a throwaway API connection) before each safe-mode test — seeSafeModeTest.OnInitialize.
-
SafeModeGet()reflects this connection's state, tracked client-side (trueafter take,falseafter release/unroll). It does not query the router; use/safe-mode/printfor the router-side view. -
SafeModeTake/Release/UnrollthrowNotSupportedExceptionon transports that cannot bind safe mode to a connection (Rest), andSafeModeUnrolladditionally throws onWinboxNative(no in-place unroll there).