@usex/mikrotik-mcp 3.19.0 → 3.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +152 -18
- package/dist/index.d.ts +8 -0
- package/dist/index.js +152 -18
- package/package.json +1 -1
- package/schemas/tool-catalog.json +207 -3
- package/schemas/tools/create_mangle_rule.json +91 -1
- package/schemas/tools/create_nat_rule.json +23 -0
- package/schemas/tools/update_mangle_rule.json +71 -0
- package/schemas/tools/update_nat_rule.json +20 -0
package/dist/cli.js
CHANGED
|
@@ -782,7 +782,6 @@ import { z as z3 } from "zod";
|
|
|
782
782
|
|
|
783
783
|
// src/ssh/safe-mode.ts
|
|
784
784
|
var PROMPT_RE = /\[.+?@.+?\] (?:<SAFE> )?> ?$/m;
|
|
785
|
-
var NORMAL_PROMPT_RE = /\[.+?@.+?\] > ?$/m;
|
|
786
785
|
var ANSI_RE = /\x1B(?:\[[0-9;]*[mA-HJ-MSTfhilnprsu]|[()][0-9A-Za-z]|\[?\?\d+[hl])/g;
|
|
787
786
|
function stripAnsi(text) {
|
|
788
787
|
return text.replace(ANSI_RE, "");
|
|
@@ -793,13 +792,17 @@ function isSafeModeActivated(response) {
|
|
|
793
792
|
return true;
|
|
794
793
|
return /safe mode[^\n]*\b(?:success|taken|enabled|active)\b/i.test(response);
|
|
795
794
|
}
|
|
796
|
-
function
|
|
795
|
+
function lastNonEmptyLine(response) {
|
|
797
796
|
const lines = response.replace(/\r/g, "").split(`
|
|
798
797
|
`).map((l) => l.trimEnd()).filter(Boolean);
|
|
799
|
-
|
|
800
|
-
|
|
798
|
+
return lines.at(-1) ?? "";
|
|
799
|
+
}
|
|
800
|
+
function classifyPrompt(response) {
|
|
801
|
+
const last = lastNonEmptyLine(response);
|
|
802
|
+
if (!PROMPT_RE.test(last))
|
|
803
|
+
return "unknown";
|
|
804
|
+
return last.includes("<SAFE>") ? "safe" : "released";
|
|
801
805
|
}
|
|
802
|
-
|
|
803
806
|
class SafeModeManager {
|
|
804
807
|
deviceName;
|
|
805
808
|
ssh = null;
|
|
@@ -874,13 +877,30 @@ class SafeModeManager {
|
|
|
874
877
|
return this.lock(async () => {
|
|
875
878
|
if (!this.active || !this.channel)
|
|
876
879
|
return "Safe mode is not active. Nothing to commit.";
|
|
880
|
+
this.channel.write(`
|
|
881
|
+
`);
|
|
882
|
+
const probe = await this.readSettledPrompt();
|
|
883
|
+
const before = classifyPrompt(probe);
|
|
884
|
+
if (before === "released") {
|
|
885
|
+
this.cleanup();
|
|
886
|
+
return "Safe mode already exited \u2014 your changes are committed. Safe mode DISABLED.";
|
|
887
|
+
}
|
|
888
|
+
if (before === "unknown") {
|
|
889
|
+
return "Could not read a prompt to determine Safe Mode state. The session is left open so " + "nothing is reverted \u2014 call get_safe_mode_status, retry commit_safe_mode, or " + `rollback_safe_mode. Last output: ${probe.slice(-160)}`;
|
|
890
|
+
}
|
|
877
891
|
this.channel.write(CTRL_X);
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
892
|
+
this.channel.write(`
|
|
893
|
+
`);
|
|
894
|
+
const after = await this.readSettledPrompt();
|
|
895
|
+
switch (classifyPrompt(after)) {
|
|
896
|
+
case "released":
|
|
897
|
+
this.cleanup();
|
|
898
|
+
return "Changes committed successfully. Safe mode DISABLED.";
|
|
899
|
+
case "safe":
|
|
900
|
+
return "Commit not completed \u2014 the device is still in Safe Mode. Your changes remain held " + "in memory (not reverted); call commit_safe_mode again to retry, or rollback_safe_mode " + "to discard them.";
|
|
901
|
+
default:
|
|
902
|
+
return "Commit status unclear \u2014 no prompt seen after exiting Safe Mode. The session is left " + "open so nothing is reverted; verify with get_safe_mode_status or retry commit_safe_mode. " + `Last output: ${after.slice(-160)}`;
|
|
881
903
|
}
|
|
882
|
-
this.cleanup();
|
|
883
|
-
return "Changes committed successfully. Safe mode DISABLED.";
|
|
884
904
|
});
|
|
885
905
|
}
|
|
886
906
|
rollback() {
|
|
@@ -916,6 +936,33 @@ class SafeModeManager {
|
|
|
916
936
|
channel.on("data", onData);
|
|
917
937
|
});
|
|
918
938
|
}
|
|
939
|
+
readSettledPrompt(quietMs = 450, maxMs = 8000) {
|
|
940
|
+
const channel = this.channel;
|
|
941
|
+
if (!channel)
|
|
942
|
+
return Promise.resolve("");
|
|
943
|
+
return new Promise((resolve2) => {
|
|
944
|
+
let buf = "";
|
|
945
|
+
let quiet;
|
|
946
|
+
let hard;
|
|
947
|
+
function done() {
|
|
948
|
+
clearTimeout(hard);
|
|
949
|
+
if (quiet)
|
|
950
|
+
clearTimeout(quiet);
|
|
951
|
+
channel.removeListener("data", onData);
|
|
952
|
+
resolve2(stripAnsi(buf));
|
|
953
|
+
}
|
|
954
|
+
function onData(chunk) {
|
|
955
|
+
buf += decodeOutput(chunk);
|
|
956
|
+
if (PROMPT_RE.test(lastNonEmptyLine(stripAnsi(buf)))) {
|
|
957
|
+
if (quiet)
|
|
958
|
+
clearTimeout(quiet);
|
|
959
|
+
quiet = setTimeout(done, quietMs);
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
hard = setTimeout(done, maxMs);
|
|
963
|
+
channel.on("data", onData);
|
|
964
|
+
});
|
|
965
|
+
}
|
|
919
966
|
extractOutput(raw, command) {
|
|
920
967
|
const text = raw.replace(/\r\n/g, `
|
|
921
968
|
`).replace(/\r/g, `
|
|
@@ -1006,7 +1053,7 @@ function createContext(sendLog, device) {
|
|
|
1006
1053
|
}
|
|
1007
1054
|
|
|
1008
1055
|
// src/core/routeros.ts
|
|
1009
|
-
var BARE_SAFE = /^[\w
|
|
1056
|
+
var BARE_SAFE = /^[\w.\-:/,*@!]+$/;
|
|
1010
1057
|
function quoteValue(value) {
|
|
1011
1058
|
if (typeof value === "number" || typeof value === "boolean")
|
|
1012
1059
|
return String(value);
|
|
@@ -6317,6 +6364,12 @@ async function updateNatRule(a, ctx) {
|
|
|
6317
6364
|
put("protocol", a.protocol);
|
|
6318
6365
|
put("in-interface", a.in_interface);
|
|
6319
6366
|
put("out-interface", a.out_interface);
|
|
6367
|
+
put("in-interface-list", a.in_interface_list);
|
|
6368
|
+
put("out-interface-list", a.out_interface_list);
|
|
6369
|
+
put("src-address-list", a.src_address_list);
|
|
6370
|
+
put("dst-address-list", a.dst_address_list);
|
|
6371
|
+
put("connection-mark", a.connection_mark);
|
|
6372
|
+
put("connection-nat-state", a.connection_nat_state);
|
|
6320
6373
|
put("to-addresses", a.to_addresses);
|
|
6321
6374
|
put("to-ports", a.to_ports);
|
|
6322
6375
|
if (a.comment !== undefined)
|
|
@@ -6353,8 +6406,14 @@ var firewallNatTools = [
|
|
|
6353
6406
|
src_port: z23.string().optional(),
|
|
6354
6407
|
dst_port: z23.string().optional(),
|
|
6355
6408
|
protocol: z23.string().optional(),
|
|
6356
|
-
in_interface: z23.string().optional(),
|
|
6409
|
+
in_interface: z23.string().optional().describe('Negatable, e.g. "!ether1"'),
|
|
6357
6410
|
out_interface: z23.string().optional(),
|
|
6411
|
+
in_interface_list: z23.string().optional().describe('Interface list, e.g. "WAN" or "!LAN"'),
|
|
6412
|
+
out_interface_list: z23.string().optional(),
|
|
6413
|
+
src_address_list: z23.string().optional().describe('Match src in a named list; negate "!name"'),
|
|
6414
|
+
dst_address_list: z23.string().optional().describe('Match dst in a named list; negate "!name"'),
|
|
6415
|
+
connection_mark: z23.string().optional(),
|
|
6416
|
+
connection_nat_state: z23.string().optional().describe('"srcnat" / "dstnat" / "!dstnat"'),
|
|
6358
6417
|
to_addresses: z23.string().optional().describe('Single IP or range e.g. "10.0.0.1" or "10.0.0.1-10.0.0.10"'),
|
|
6359
6418
|
to_ports: z23.string().optional().describe('Single port or range e.g. "8080" or "8080-8090"'),
|
|
6360
6419
|
comment: z23.string().optional(),
|
|
@@ -6394,7 +6453,7 @@ var firewallNatTools = [
|
|
|
6394
6453
|
} else if (a.chain === "dstnat" && !dstnatActions.includes(a.action)) {
|
|
6395
6454
|
return `Error: Invalid action '${a.action}' for dstnat. Must be one of: ${dstnatActions.join(", ")}`;
|
|
6396
6455
|
}
|
|
6397
|
-
const cmd = new Cmd("/ip firewall nat add").set("chain", a.chain).set("action", a.action).opt("src-address", a.src_address).opt("dst-address", a.dst_address).opt("src-port", a.src_port).opt("dst-port", a.dst_port).opt("protocol", a.protocol).opt("in-interface", a.in_interface).opt("out-interface", a.out_interface).opt("to-addresses", a.to_addresses).opt("to-ports", a.to_ports).opt("comment", a.comment).flag("disabled", a.disabled).flag("log", a.log).opt("log-prefix", a.log ? a.log_prefix : undefined).opt("place-before", a.place_before).build();
|
|
6456
|
+
const cmd = new Cmd("/ip firewall nat add").set("chain", a.chain).set("action", a.action).opt("src-address", a.src_address).opt("dst-address", a.dst_address).opt("src-port", a.src_port).opt("dst-port", a.dst_port).opt("protocol", a.protocol).opt("in-interface", a.in_interface).opt("out-interface", a.out_interface).opt("in-interface-list", a.in_interface_list).opt("out-interface-list", a.out_interface_list).opt("src-address-list", a.src_address_list).opt("dst-address-list", a.dst_address_list).opt("connection-mark", a.connection_mark).opt("connection-nat-state", a.connection_nat_state).opt("to-addresses", a.to_addresses).opt("to-ports", a.to_ports).opt("comment", a.comment).flag("disabled", a.disabled).flag("log", a.log).opt("log-prefix", a.log ? a.log_prefix : undefined).opt("place-before", a.place_before).build();
|
|
6398
6457
|
const result = await executeMikrotikCommand(cmd, ctx);
|
|
6399
6458
|
const trimmed = result.trim();
|
|
6400
6459
|
if (looksLikeError(trimmed)) {
|
|
@@ -6492,6 +6551,12 @@ ${result}`;
|
|
|
6492
6551
|
protocol: z23.string().optional(),
|
|
6493
6552
|
in_interface: z23.string().optional(),
|
|
6494
6553
|
out_interface: z23.string().optional(),
|
|
6554
|
+
in_interface_list: z23.string().optional(),
|
|
6555
|
+
out_interface_list: z23.string().optional(),
|
|
6556
|
+
src_address_list: z23.string().optional().describe('Negate with "!name"'),
|
|
6557
|
+
dst_address_list: z23.string().optional().describe('Negate with "!name"'),
|
|
6558
|
+
connection_mark: z23.string().optional(),
|
|
6559
|
+
connection_nat_state: z23.string().optional(),
|
|
6495
6560
|
to_addresses: z23.string().optional(),
|
|
6496
6561
|
to_ports: z23.string().optional(),
|
|
6497
6562
|
comment: z23.string().optional(),
|
|
@@ -6584,10 +6649,33 @@ async function updateMangleRule(a, ctx) {
|
|
|
6584
6649
|
put("protocol", a.protocol);
|
|
6585
6650
|
put("in-interface", a.in_interface);
|
|
6586
6651
|
put("out-interface", a.out_interface);
|
|
6652
|
+
put("in-interface-list", a.in_interface_list);
|
|
6653
|
+
put("out-interface-list", a.out_interface_list);
|
|
6654
|
+
put("src-address-list", a.src_address_list);
|
|
6655
|
+
put("dst-address-list", a.dst_address_list);
|
|
6656
|
+
put("src-address-type", a.src_address_type);
|
|
6657
|
+
put("dst-address-type", a.dst_address_type);
|
|
6587
6658
|
put("connection-mark", a.connection_mark);
|
|
6588
6659
|
put("packet-mark", a.packet_mark);
|
|
6589
6660
|
put("routing-mark", a.routing_mark);
|
|
6590
6661
|
put("connection-state", a.connection_state);
|
|
6662
|
+
put("connection-nat-state", a.connection_nat_state);
|
|
6663
|
+
put("connection-type", a.connection_type);
|
|
6664
|
+
put("connection-bytes", a.connection_bytes);
|
|
6665
|
+
put("connection-limit", a.connection_limit);
|
|
6666
|
+
put("connection-rate", a.connection_rate);
|
|
6667
|
+
put("per-connection-classifier", a.per_connection_classifier);
|
|
6668
|
+
put("tcp-flags", a.tcp_flags);
|
|
6669
|
+
put("dscp", a.dscp);
|
|
6670
|
+
put("priority", a.priority);
|
|
6671
|
+
put("packet-size", a.packet_size);
|
|
6672
|
+
put("layer7-protocol", a.layer7_protocol);
|
|
6673
|
+
put("ipsec-policy", a.ipsec_policy);
|
|
6674
|
+
put("nth", a.nth);
|
|
6675
|
+
put("random", a.random);
|
|
6676
|
+
put("time", a.time);
|
|
6677
|
+
put("hotspot", a.hotspot);
|
|
6678
|
+
put("p2p", a.p2p);
|
|
6591
6679
|
put("new-connection-mark", a.new_connection_mark);
|
|
6592
6680
|
put("new-packet-mark", a.new_packet_mark);
|
|
6593
6681
|
put("new-routing-mark", a.new_routing_mark);
|
|
@@ -6624,7 +6712,7 @@ var firewallMangleTools = [
|
|
|
6624
6712
|
name: "create_mangle_rule",
|
|
6625
6713
|
title: "Create IPv4 Firewall Mangle Rule",
|
|
6626
6714
|
annotations: WRITE,
|
|
6627
|
-
description: "Creates an IPv4 mangle rule (`/ip firewall mangle add`) \u2014 the packet-marking and header-modification table, " + "used to mark connections/packets/routing (for policy routing, QoS and per-connection classification) or to change DSCP/TTL/MSS. " + "For accept/drop decisions use create_filter_rule; for address translation use create_nat_rule; for IPv6 mangle use create_ipv6_mangle_rule. " + "chain: prerouting/input/forward/output/postrouting. " + "action: mark-connection/mark-packet/mark-routing/change-dscp/change-ttl/change-mss/add-src-to-address-list/add-dst-to-address-list/fasttrack-connection/route/set-priority/accept/etc. " + "Set the matching new-*-mark field for mark-* actions and keep passthrough=true so later rules can also match the same packet; " + "for add-*-to-address-list set address_list (and optionally address_list_timeout). " + "place_before accepts a rule number or ID (*N) to control insertion position. " + "Returns the created rule's detail including its `.id`.",
|
|
6715
|
+
description: "Creates an IPv4 mangle rule (`/ip firewall mangle add`) \u2014 the packet-marking and header-modification table, " + "used to mark connections/packets/routing (for policy routing, QoS and per-connection classification) or to change DSCP/TTL/MSS. " + "For accept/drop decisions use create_filter_rule; for address translation use create_nat_rule; for IPv6 mangle use create_ipv6_mangle_rule. " + "chain: prerouting/input/forward/output/postrouting. " + "action: mark-connection/mark-packet/mark-routing/change-dscp/change-ttl/change-mss/add-src-to-address-list/add-dst-to-address-list/fasttrack-connection/route/set-priority/accept/etc. " + "Set the matching new-*-mark field for mark-* actions and keep passthrough=true so later rules can also match the same packet; " + "for add-*-to-address-list set address_list (and optionally address_list_timeout). " + "Full match surface: address-lists (src_address_list/dst_address_list \u2014 negate with a leading '!', e.g. dst_address_list='!IR' to route traffic NOT bound for a list), " + "interface-lists, per_connection_classifier (PCC load-balancing), connection_nat_state, tcp_flags, dscp, layer7_protocol, time, and more. " + "Typical policy-routing rule: chain=prerouting, src_address=<lan>, dst_address_list='!IR', action=mark-routing, new_routing_mark=<table>, then a routing rule/route uses that table. " + "place_before accepts a rule number or ID (*N) to control insertion position. " + "Returns the created rule's detail including its `.id`.",
|
|
6628
6716
|
inputSchema: {
|
|
6629
6717
|
chain: z24.enum(["prerouting", "input", "forward", "output", "postrouting"]),
|
|
6630
6718
|
action: z24.enum([
|
|
@@ -6654,12 +6742,35 @@ var firewallMangleTools = [
|
|
|
6654
6742
|
src_port: z24.string().optional(),
|
|
6655
6743
|
dst_port: z24.string().optional(),
|
|
6656
6744
|
protocol: z24.string().optional(),
|
|
6657
|
-
in_interface: z24.string().optional(),
|
|
6745
|
+
in_interface: z24.string().optional().describe('Negatable, e.g. "!ether1"'),
|
|
6658
6746
|
out_interface: z24.string().optional(),
|
|
6747
|
+
in_interface_list: z24.string().optional().describe('Interface list, negatable e.g. "!WAN"'),
|
|
6748
|
+
out_interface_list: z24.string().optional(),
|
|
6749
|
+
src_address_list: z24.string().optional().describe('Match src in a named list; negate "!name"'),
|
|
6750
|
+
dst_address_list: z24.string().optional().describe('Match dst in a named list; negate "!name" (e.g. "!IR" routes foreign traffic)'),
|
|
6751
|
+
src_address_type: z24.string().optional().describe('e.g. "local", "unicast", "!local"'),
|
|
6752
|
+
dst_address_type: z24.string().optional(),
|
|
6659
6753
|
connection_mark: z24.string().optional(),
|
|
6660
6754
|
packet_mark: z24.string().optional(),
|
|
6661
6755
|
routing_mark: z24.string().optional(),
|
|
6662
|
-
connection_state: z24.string().optional().describe('e.g. "new", "established,related", "invalid"'),
|
|
6756
|
+
connection_state: z24.string().optional().describe('e.g. "new", "established,related", "!invalid"'),
|
|
6757
|
+
connection_nat_state: z24.string().optional().describe('"srcnat" / "dstnat" / "!dstnat"'),
|
|
6758
|
+
connection_type: z24.string().optional().describe('Helper, e.g. "sip", "ftp"'),
|
|
6759
|
+
connection_bytes: z24.string().optional().describe('e.g. "1000000-0" (>1 MB connections)'),
|
|
6760
|
+
connection_limit: z24.string().optional().describe('e.g. "100,32"'),
|
|
6761
|
+
connection_rate: z24.string().optional().describe('e.g. "100k-1M"'),
|
|
6762
|
+
per_connection_classifier: z24.string().optional().describe('PCC for load balancing, e.g. "both-addresses:2/0"'),
|
|
6763
|
+
tcp_flags: z24.string().optional().describe('RouterOS flag expression e.g. "syn,!ack"'),
|
|
6764
|
+
dscp: z24.string().optional().describe("Match incoming DSCP (0-63)"),
|
|
6765
|
+
priority: z24.string().optional().describe("Match packet/queue priority (0-63)"),
|
|
6766
|
+
packet_size: z24.string().optional().describe('e.g. "1500" or "0-500"'),
|
|
6767
|
+
layer7_protocol: z24.string().optional().describe("Name of an /ip firewall layer7-protocol regex"),
|
|
6768
|
+
ipsec_policy: z24.string().optional().describe('e.g. "in,ipsec" or "out,none"'),
|
|
6769
|
+
nth: z24.string().optional().describe('e.g. "2,1" \u2014 every 2nd packet'),
|
|
6770
|
+
random: z24.string().optional().describe("Match a random N% of packets (1-99)"),
|
|
6771
|
+
time: z24.string().optional().describe('e.g. "8h-16h,mon,tue,wed,thu,fri"'),
|
|
6772
|
+
hotspot: z24.string().optional().describe('e.g. "auth", "!auth", "from-client"'),
|
|
6773
|
+
p2p: z24.string().optional(),
|
|
6663
6774
|
new_connection_mark: z24.string().optional(),
|
|
6664
6775
|
new_packet_mark: z24.string().optional(),
|
|
6665
6776
|
new_routing_mark: z24.string().optional(),
|
|
@@ -6678,7 +6789,7 @@ var firewallMangleTools = [
|
|
|
6678
6789
|
},
|
|
6679
6790
|
async handler(a, ctx) {
|
|
6680
6791
|
ctx.info(`Creating mangle rule: chain=${a.chain}, action=${a.action}`);
|
|
6681
|
-
const cmd = new Cmd("/ip firewall mangle add").set("chain", a.chain).set("action", a.action).opt("src-address", a.src_address).opt("dst-address", a.dst_address).opt("src-port", a.src_port).opt("dst-port", a.dst_port).opt("protocol", a.protocol).opt("in-interface", a.in_interface).opt("out-interface", a.out_interface).opt("connection-mark", a.connection_mark).opt("packet-mark", a.packet_mark).opt("routing-mark", a.routing_mark).opt("connection-state", a.connection_state).opt("new-connection-mark", a.new_connection_mark).opt("new-packet-mark", a.new_packet_mark).opt("new-routing-mark", a.new_routing_mark).opt("new-dscp", a.new_dscp).opt("new-ttl", a.new_ttl).opt("new-mss", a.new_mss).opt("routing-table", a.routing_table).opt("address-list", a.address_list).opt("address-list-timeout", a.address_list_timeout).bool("passthrough", a.passthrough).opt("comment", a.comment).flag("disabled", a.disabled).flag("log", a.log).opt("log-prefix", a.log ? a.log_prefix : undefined).opt("place-before", a.place_before).build();
|
|
6792
|
+
const cmd = new Cmd("/ip firewall mangle add").set("chain", a.chain).set("action", a.action).opt("src-address", a.src_address).opt("dst-address", a.dst_address).opt("src-port", a.src_port).opt("dst-port", a.dst_port).opt("protocol", a.protocol).opt("in-interface", a.in_interface).opt("out-interface", a.out_interface).opt("in-interface-list", a.in_interface_list).opt("out-interface-list", a.out_interface_list).opt("src-address-list", a.src_address_list).opt("dst-address-list", a.dst_address_list).opt("src-address-type", a.src_address_type).opt("dst-address-type", a.dst_address_type).opt("connection-mark", a.connection_mark).opt("packet-mark", a.packet_mark).opt("routing-mark", a.routing_mark).opt("connection-state", a.connection_state).opt("connection-nat-state", a.connection_nat_state).opt("connection-type", a.connection_type).opt("connection-bytes", a.connection_bytes).opt("connection-limit", a.connection_limit).opt("connection-rate", a.connection_rate).opt("per-connection-classifier", a.per_connection_classifier).opt("tcp-flags", a.tcp_flags).opt("dscp", a.dscp).opt("priority", a.priority).opt("packet-size", a.packet_size).opt("layer7-protocol", a.layer7_protocol).opt("ipsec-policy", a.ipsec_policy).opt("nth", a.nth).opt("random", a.random).opt("time", a.time).opt("hotspot", a.hotspot).opt("p2p", a.p2p).opt("new-connection-mark", a.new_connection_mark).opt("new-packet-mark", a.new_packet_mark).opt("new-routing-mark", a.new_routing_mark).opt("new-dscp", a.new_dscp).opt("new-ttl", a.new_ttl).opt("new-mss", a.new_mss).opt("routing-table", a.routing_table).opt("address-list", a.address_list).opt("address-list-timeout", a.address_list_timeout).bool("passthrough", a.passthrough).opt("comment", a.comment).flag("disabled", a.disabled).flag("log", a.log).opt("log-prefix", a.log ? a.log_prefix : undefined).opt("place-before", a.place_before).build();
|
|
6682
6793
|
const result = await executeMikrotikCommand(cmd, ctx);
|
|
6683
6794
|
const trimmed = result.trim();
|
|
6684
6795
|
if (looksLikeError(trimmed)) {
|
|
@@ -6772,10 +6883,33 @@ ${result}`;
|
|
|
6772
6883
|
protocol: z24.string().optional(),
|
|
6773
6884
|
in_interface: z24.string().optional(),
|
|
6774
6885
|
out_interface: z24.string().optional(),
|
|
6886
|
+
in_interface_list: z24.string().optional(),
|
|
6887
|
+
out_interface_list: z24.string().optional(),
|
|
6888
|
+
src_address_list: z24.string().optional().describe('Negate with "!name" (e.g. "!IR")'),
|
|
6889
|
+
dst_address_list: z24.string().optional().describe('Negate with "!name" (e.g. "!IR")'),
|
|
6890
|
+
src_address_type: z24.string().optional(),
|
|
6891
|
+
dst_address_type: z24.string().optional(),
|
|
6775
6892
|
connection_mark: z24.string().optional(),
|
|
6776
6893
|
packet_mark: z24.string().optional(),
|
|
6777
6894
|
routing_mark: z24.string().optional(),
|
|
6778
6895
|
connection_state: z24.string().optional(),
|
|
6896
|
+
connection_nat_state: z24.string().optional(),
|
|
6897
|
+
connection_type: z24.string().optional(),
|
|
6898
|
+
connection_bytes: z24.string().optional(),
|
|
6899
|
+
connection_limit: z24.string().optional(),
|
|
6900
|
+
connection_rate: z24.string().optional(),
|
|
6901
|
+
per_connection_classifier: z24.string().optional(),
|
|
6902
|
+
tcp_flags: z24.string().optional(),
|
|
6903
|
+
dscp: z24.string().optional(),
|
|
6904
|
+
priority: z24.string().optional(),
|
|
6905
|
+
packet_size: z24.string().optional(),
|
|
6906
|
+
layer7_protocol: z24.string().optional(),
|
|
6907
|
+
ipsec_policy: z24.string().optional(),
|
|
6908
|
+
nth: z24.string().optional(),
|
|
6909
|
+
random: z24.string().optional(),
|
|
6910
|
+
time: z24.string().optional(),
|
|
6911
|
+
hotspot: z24.string().optional(),
|
|
6912
|
+
p2p: z24.string().optional(),
|
|
6779
6913
|
new_connection_mark: z24.string().optional(),
|
|
6780
6914
|
new_packet_mark: z24.string().optional(),
|
|
6781
6915
|
new_routing_mark: z24.string().optional(),
|
|
@@ -23788,7 +23922,7 @@ function registerPrompts(server) {
|
|
|
23788
23922
|
// package.json
|
|
23789
23923
|
var package_default = {
|
|
23790
23924
|
name: "@usex/mikrotik-mcp",
|
|
23791
|
-
version: "3.
|
|
23925
|
+
version: "3.20.0",
|
|
23792
23926
|
description: "MCP server for MikroTik RouterOS \u2014 660+ tools over SSH for firewall, NAT, routing, DHCP, DNS, WireGuard, wireless, QoS and more.",
|
|
23793
23927
|
keywords: [
|
|
23794
23928
|
"ai",
|
package/dist/index.d.ts
CHANGED
|
@@ -185,6 +185,14 @@ declare class SafeModeManager {
|
|
|
185
185
|
* (safe-mode marker present/absent), not merely the first prompt-shaped line.
|
|
186
186
|
*/
|
|
187
187
|
private readUntilPrompt;
|
|
188
|
+
/**
|
|
189
|
+
* Read until the output SETTLES: once any prompt is visible, wait for a quiet
|
|
190
|
+
* gap (`quietMs` with no new bytes) before resolving, or give up at `maxMs`.
|
|
191
|
+
* Used by commit, where Ctrl+X + Enter can emit a transient `<SAFE>` redraw
|
|
192
|
+
* followed by the real post-commit prompt — settling on the LAST prompt after
|
|
193
|
+
* a quiet period is what makes mode detection reliable.
|
|
194
|
+
*/
|
|
195
|
+
private readSettledPrompt;
|
|
188
196
|
private extractOutput;
|
|
189
197
|
private cleanup;
|
|
190
198
|
}
|
package/dist/index.js
CHANGED
|
@@ -768,7 +768,6 @@ function connectErrorMessage(name, dc, lastError) {
|
|
|
768
768
|
|
|
769
769
|
// src/ssh/safe-mode.ts
|
|
770
770
|
var PROMPT_RE = /\[.+?@.+?\] (?:<SAFE> )?> ?$/m;
|
|
771
|
-
var NORMAL_PROMPT_RE = /\[.+?@.+?\] > ?$/m;
|
|
772
771
|
var ANSI_RE = /\x1B(?:\[[0-9;]*[mA-HJ-MSTfhilnprsu]|[()][0-9A-Za-z]|\[?\?\d+[hl])/g;
|
|
773
772
|
function stripAnsi(text) {
|
|
774
773
|
return text.replace(ANSI_RE, "");
|
|
@@ -779,13 +778,17 @@ function isSafeModeActivated(response) {
|
|
|
779
778
|
return true;
|
|
780
779
|
return /safe mode[^\n]*\b(?:success|taken|enabled|active)\b/i.test(response);
|
|
781
780
|
}
|
|
782
|
-
function
|
|
781
|
+
function lastNonEmptyLine(response) {
|
|
783
782
|
const lines = response.replace(/\r/g, "").split(`
|
|
784
783
|
`).map((l) => l.trimEnd()).filter(Boolean);
|
|
785
|
-
|
|
786
|
-
|
|
784
|
+
return lines.at(-1) ?? "";
|
|
785
|
+
}
|
|
786
|
+
function classifyPrompt(response) {
|
|
787
|
+
const last = lastNonEmptyLine(response);
|
|
788
|
+
if (!PROMPT_RE.test(last))
|
|
789
|
+
return "unknown";
|
|
790
|
+
return last.includes("<SAFE>") ? "safe" : "released";
|
|
787
791
|
}
|
|
788
|
-
|
|
789
792
|
class SafeModeManager {
|
|
790
793
|
deviceName;
|
|
791
794
|
ssh = null;
|
|
@@ -860,13 +863,30 @@ class SafeModeManager {
|
|
|
860
863
|
return this.lock(async () => {
|
|
861
864
|
if (!this.active || !this.channel)
|
|
862
865
|
return "Safe mode is not active. Nothing to commit.";
|
|
866
|
+
this.channel.write(`
|
|
867
|
+
`);
|
|
868
|
+
const probe = await this.readSettledPrompt();
|
|
869
|
+
const before = classifyPrompt(probe);
|
|
870
|
+
if (before === "released") {
|
|
871
|
+
this.cleanup();
|
|
872
|
+
return "Safe mode already exited \u2014 your changes are committed. Safe mode DISABLED.";
|
|
873
|
+
}
|
|
874
|
+
if (before === "unknown") {
|
|
875
|
+
return "Could not read a prompt to determine Safe Mode state. The session is left open so " + "nothing is reverted \u2014 call get_safe_mode_status, retry commit_safe_mode, or " + `rollback_safe_mode. Last output: ${probe.slice(-160)}`;
|
|
876
|
+
}
|
|
863
877
|
this.channel.write(CTRL_X);
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
878
|
+
this.channel.write(`
|
|
879
|
+
`);
|
|
880
|
+
const after = await this.readSettledPrompt();
|
|
881
|
+
switch (classifyPrompt(after)) {
|
|
882
|
+
case "released":
|
|
883
|
+
this.cleanup();
|
|
884
|
+
return "Changes committed successfully. Safe mode DISABLED.";
|
|
885
|
+
case "safe":
|
|
886
|
+
return "Commit not completed \u2014 the device is still in Safe Mode. Your changes remain held " + "in memory (not reverted); call commit_safe_mode again to retry, or rollback_safe_mode " + "to discard them.";
|
|
887
|
+
default:
|
|
888
|
+
return "Commit status unclear \u2014 no prompt seen after exiting Safe Mode. The session is left " + "open so nothing is reverted; verify with get_safe_mode_status or retry commit_safe_mode. " + `Last output: ${after.slice(-160)}`;
|
|
867
889
|
}
|
|
868
|
-
this.cleanup();
|
|
869
|
-
return "Changes committed successfully. Safe mode DISABLED.";
|
|
870
890
|
});
|
|
871
891
|
}
|
|
872
892
|
rollback() {
|
|
@@ -902,6 +922,33 @@ class SafeModeManager {
|
|
|
902
922
|
channel.on("data", onData);
|
|
903
923
|
});
|
|
904
924
|
}
|
|
925
|
+
readSettledPrompt(quietMs = 450, maxMs = 8000) {
|
|
926
|
+
const channel = this.channel;
|
|
927
|
+
if (!channel)
|
|
928
|
+
return Promise.resolve("");
|
|
929
|
+
return new Promise((resolve2) => {
|
|
930
|
+
let buf = "";
|
|
931
|
+
let quiet;
|
|
932
|
+
let hard;
|
|
933
|
+
function done() {
|
|
934
|
+
clearTimeout(hard);
|
|
935
|
+
if (quiet)
|
|
936
|
+
clearTimeout(quiet);
|
|
937
|
+
channel.removeListener("data", onData);
|
|
938
|
+
resolve2(stripAnsi(buf));
|
|
939
|
+
}
|
|
940
|
+
function onData(chunk) {
|
|
941
|
+
buf += decodeOutput(chunk);
|
|
942
|
+
if (PROMPT_RE.test(lastNonEmptyLine(stripAnsi(buf)))) {
|
|
943
|
+
if (quiet)
|
|
944
|
+
clearTimeout(quiet);
|
|
945
|
+
quiet = setTimeout(done, quietMs);
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
hard = setTimeout(done, maxMs);
|
|
949
|
+
channel.on("data", onData);
|
|
950
|
+
});
|
|
951
|
+
}
|
|
905
952
|
extractOutput(raw, command) {
|
|
906
953
|
const text = raw.replace(/\r\n/g, `
|
|
907
954
|
`).replace(/\r/g, `
|
|
@@ -991,7 +1038,7 @@ function createContext(sendLog, device) {
|
|
|
991
1038
|
}
|
|
992
1039
|
|
|
993
1040
|
// src/core/routeros.ts
|
|
994
|
-
var BARE_SAFE = /^[\w
|
|
1041
|
+
var BARE_SAFE = /^[\w.\-:/,*@!]+$/;
|
|
995
1042
|
function quoteValue(value) {
|
|
996
1043
|
if (typeof value === "number" || typeof value === "boolean")
|
|
997
1044
|
return String(value);
|
|
@@ -6329,6 +6376,12 @@ async function updateNatRule(a, ctx) {
|
|
|
6329
6376
|
put("protocol", a.protocol);
|
|
6330
6377
|
put("in-interface", a.in_interface);
|
|
6331
6378
|
put("out-interface", a.out_interface);
|
|
6379
|
+
put("in-interface-list", a.in_interface_list);
|
|
6380
|
+
put("out-interface-list", a.out_interface_list);
|
|
6381
|
+
put("src-address-list", a.src_address_list);
|
|
6382
|
+
put("dst-address-list", a.dst_address_list);
|
|
6383
|
+
put("connection-mark", a.connection_mark);
|
|
6384
|
+
put("connection-nat-state", a.connection_nat_state);
|
|
6332
6385
|
put("to-addresses", a.to_addresses);
|
|
6333
6386
|
put("to-ports", a.to_ports);
|
|
6334
6387
|
if (a.comment !== undefined)
|
|
@@ -6365,8 +6418,14 @@ var firewallNatTools = [
|
|
|
6365
6418
|
src_port: z24.string().optional(),
|
|
6366
6419
|
dst_port: z24.string().optional(),
|
|
6367
6420
|
protocol: z24.string().optional(),
|
|
6368
|
-
in_interface: z24.string().optional(),
|
|
6421
|
+
in_interface: z24.string().optional().describe('Negatable, e.g. "!ether1"'),
|
|
6369
6422
|
out_interface: z24.string().optional(),
|
|
6423
|
+
in_interface_list: z24.string().optional().describe('Interface list, e.g. "WAN" or "!LAN"'),
|
|
6424
|
+
out_interface_list: z24.string().optional(),
|
|
6425
|
+
src_address_list: z24.string().optional().describe('Match src in a named list; negate "!name"'),
|
|
6426
|
+
dst_address_list: z24.string().optional().describe('Match dst in a named list; negate "!name"'),
|
|
6427
|
+
connection_mark: z24.string().optional(),
|
|
6428
|
+
connection_nat_state: z24.string().optional().describe('"srcnat" / "dstnat" / "!dstnat"'),
|
|
6370
6429
|
to_addresses: z24.string().optional().describe('Single IP or range e.g. "10.0.0.1" or "10.0.0.1-10.0.0.10"'),
|
|
6371
6430
|
to_ports: z24.string().optional().describe('Single port or range e.g. "8080" or "8080-8090"'),
|
|
6372
6431
|
comment: z24.string().optional(),
|
|
@@ -6406,7 +6465,7 @@ var firewallNatTools = [
|
|
|
6406
6465
|
} else if (a.chain === "dstnat" && !dstnatActions.includes(a.action)) {
|
|
6407
6466
|
return `Error: Invalid action '${a.action}' for dstnat. Must be one of: ${dstnatActions.join(", ")}`;
|
|
6408
6467
|
}
|
|
6409
|
-
const cmd = new Cmd("/ip firewall nat add").set("chain", a.chain).set("action", a.action).opt("src-address", a.src_address).opt("dst-address", a.dst_address).opt("src-port", a.src_port).opt("dst-port", a.dst_port).opt("protocol", a.protocol).opt("in-interface", a.in_interface).opt("out-interface", a.out_interface).opt("to-addresses", a.to_addresses).opt("to-ports", a.to_ports).opt("comment", a.comment).flag("disabled", a.disabled).flag("log", a.log).opt("log-prefix", a.log ? a.log_prefix : undefined).opt("place-before", a.place_before).build();
|
|
6468
|
+
const cmd = new Cmd("/ip firewall nat add").set("chain", a.chain).set("action", a.action).opt("src-address", a.src_address).opt("dst-address", a.dst_address).opt("src-port", a.src_port).opt("dst-port", a.dst_port).opt("protocol", a.protocol).opt("in-interface", a.in_interface).opt("out-interface", a.out_interface).opt("in-interface-list", a.in_interface_list).opt("out-interface-list", a.out_interface_list).opt("src-address-list", a.src_address_list).opt("dst-address-list", a.dst_address_list).opt("connection-mark", a.connection_mark).opt("connection-nat-state", a.connection_nat_state).opt("to-addresses", a.to_addresses).opt("to-ports", a.to_ports).opt("comment", a.comment).flag("disabled", a.disabled).flag("log", a.log).opt("log-prefix", a.log ? a.log_prefix : undefined).opt("place-before", a.place_before).build();
|
|
6410
6469
|
const result = await executeMikrotikCommand(cmd, ctx);
|
|
6411
6470
|
const trimmed = result.trim();
|
|
6412
6471
|
if (looksLikeError(trimmed)) {
|
|
@@ -6504,6 +6563,12 @@ ${result}`;
|
|
|
6504
6563
|
protocol: z24.string().optional(),
|
|
6505
6564
|
in_interface: z24.string().optional(),
|
|
6506
6565
|
out_interface: z24.string().optional(),
|
|
6566
|
+
in_interface_list: z24.string().optional(),
|
|
6567
|
+
out_interface_list: z24.string().optional(),
|
|
6568
|
+
src_address_list: z24.string().optional().describe('Negate with "!name"'),
|
|
6569
|
+
dst_address_list: z24.string().optional().describe('Negate with "!name"'),
|
|
6570
|
+
connection_mark: z24.string().optional(),
|
|
6571
|
+
connection_nat_state: z24.string().optional(),
|
|
6507
6572
|
to_addresses: z24.string().optional(),
|
|
6508
6573
|
to_ports: z24.string().optional(),
|
|
6509
6574
|
comment: z24.string().optional(),
|
|
@@ -6596,10 +6661,33 @@ async function updateMangleRule(a, ctx) {
|
|
|
6596
6661
|
put("protocol", a.protocol);
|
|
6597
6662
|
put("in-interface", a.in_interface);
|
|
6598
6663
|
put("out-interface", a.out_interface);
|
|
6664
|
+
put("in-interface-list", a.in_interface_list);
|
|
6665
|
+
put("out-interface-list", a.out_interface_list);
|
|
6666
|
+
put("src-address-list", a.src_address_list);
|
|
6667
|
+
put("dst-address-list", a.dst_address_list);
|
|
6668
|
+
put("src-address-type", a.src_address_type);
|
|
6669
|
+
put("dst-address-type", a.dst_address_type);
|
|
6599
6670
|
put("connection-mark", a.connection_mark);
|
|
6600
6671
|
put("packet-mark", a.packet_mark);
|
|
6601
6672
|
put("routing-mark", a.routing_mark);
|
|
6602
6673
|
put("connection-state", a.connection_state);
|
|
6674
|
+
put("connection-nat-state", a.connection_nat_state);
|
|
6675
|
+
put("connection-type", a.connection_type);
|
|
6676
|
+
put("connection-bytes", a.connection_bytes);
|
|
6677
|
+
put("connection-limit", a.connection_limit);
|
|
6678
|
+
put("connection-rate", a.connection_rate);
|
|
6679
|
+
put("per-connection-classifier", a.per_connection_classifier);
|
|
6680
|
+
put("tcp-flags", a.tcp_flags);
|
|
6681
|
+
put("dscp", a.dscp);
|
|
6682
|
+
put("priority", a.priority);
|
|
6683
|
+
put("packet-size", a.packet_size);
|
|
6684
|
+
put("layer7-protocol", a.layer7_protocol);
|
|
6685
|
+
put("ipsec-policy", a.ipsec_policy);
|
|
6686
|
+
put("nth", a.nth);
|
|
6687
|
+
put("random", a.random);
|
|
6688
|
+
put("time", a.time);
|
|
6689
|
+
put("hotspot", a.hotspot);
|
|
6690
|
+
put("p2p", a.p2p);
|
|
6603
6691
|
put("new-connection-mark", a.new_connection_mark);
|
|
6604
6692
|
put("new-packet-mark", a.new_packet_mark);
|
|
6605
6693
|
put("new-routing-mark", a.new_routing_mark);
|
|
@@ -6636,7 +6724,7 @@ var firewallMangleTools = [
|
|
|
6636
6724
|
name: "create_mangle_rule",
|
|
6637
6725
|
title: "Create IPv4 Firewall Mangle Rule",
|
|
6638
6726
|
annotations: WRITE,
|
|
6639
|
-
description: "Creates an IPv4 mangle rule (`/ip firewall mangle add`) \u2014 the packet-marking and header-modification table, " + "used to mark connections/packets/routing (for policy routing, QoS and per-connection classification) or to change DSCP/TTL/MSS. " + "For accept/drop decisions use create_filter_rule; for address translation use create_nat_rule; for IPv6 mangle use create_ipv6_mangle_rule. " + "chain: prerouting/input/forward/output/postrouting. " + "action: mark-connection/mark-packet/mark-routing/change-dscp/change-ttl/change-mss/add-src-to-address-list/add-dst-to-address-list/fasttrack-connection/route/set-priority/accept/etc. " + "Set the matching new-*-mark field for mark-* actions and keep passthrough=true so later rules can also match the same packet; " + "for add-*-to-address-list set address_list (and optionally address_list_timeout). " + "place_before accepts a rule number or ID (*N) to control insertion position. " + "Returns the created rule's detail including its `.id`.",
|
|
6727
|
+
description: "Creates an IPv4 mangle rule (`/ip firewall mangle add`) \u2014 the packet-marking and header-modification table, " + "used to mark connections/packets/routing (for policy routing, QoS and per-connection classification) or to change DSCP/TTL/MSS. " + "For accept/drop decisions use create_filter_rule; for address translation use create_nat_rule; for IPv6 mangle use create_ipv6_mangle_rule. " + "chain: prerouting/input/forward/output/postrouting. " + "action: mark-connection/mark-packet/mark-routing/change-dscp/change-ttl/change-mss/add-src-to-address-list/add-dst-to-address-list/fasttrack-connection/route/set-priority/accept/etc. " + "Set the matching new-*-mark field for mark-* actions and keep passthrough=true so later rules can also match the same packet; " + "for add-*-to-address-list set address_list (and optionally address_list_timeout). " + "Full match surface: address-lists (src_address_list/dst_address_list \u2014 negate with a leading '!', e.g. dst_address_list='!IR' to route traffic NOT bound for a list), " + "interface-lists, per_connection_classifier (PCC load-balancing), connection_nat_state, tcp_flags, dscp, layer7_protocol, time, and more. " + "Typical policy-routing rule: chain=prerouting, src_address=<lan>, dst_address_list='!IR', action=mark-routing, new_routing_mark=<table>, then a routing rule/route uses that table. " + "place_before accepts a rule number or ID (*N) to control insertion position. " + "Returns the created rule's detail including its `.id`.",
|
|
6640
6728
|
inputSchema: {
|
|
6641
6729
|
chain: z25.enum(["prerouting", "input", "forward", "output", "postrouting"]),
|
|
6642
6730
|
action: z25.enum([
|
|
@@ -6666,12 +6754,35 @@ var firewallMangleTools = [
|
|
|
6666
6754
|
src_port: z25.string().optional(),
|
|
6667
6755
|
dst_port: z25.string().optional(),
|
|
6668
6756
|
protocol: z25.string().optional(),
|
|
6669
|
-
in_interface: z25.string().optional(),
|
|
6757
|
+
in_interface: z25.string().optional().describe('Negatable, e.g. "!ether1"'),
|
|
6670
6758
|
out_interface: z25.string().optional(),
|
|
6759
|
+
in_interface_list: z25.string().optional().describe('Interface list, negatable e.g. "!WAN"'),
|
|
6760
|
+
out_interface_list: z25.string().optional(),
|
|
6761
|
+
src_address_list: z25.string().optional().describe('Match src in a named list; negate "!name"'),
|
|
6762
|
+
dst_address_list: z25.string().optional().describe('Match dst in a named list; negate "!name" (e.g. "!IR" routes foreign traffic)'),
|
|
6763
|
+
src_address_type: z25.string().optional().describe('e.g. "local", "unicast", "!local"'),
|
|
6764
|
+
dst_address_type: z25.string().optional(),
|
|
6671
6765
|
connection_mark: z25.string().optional(),
|
|
6672
6766
|
packet_mark: z25.string().optional(),
|
|
6673
6767
|
routing_mark: z25.string().optional(),
|
|
6674
|
-
connection_state: z25.string().optional().describe('e.g. "new", "established,related", "invalid"'),
|
|
6768
|
+
connection_state: z25.string().optional().describe('e.g. "new", "established,related", "!invalid"'),
|
|
6769
|
+
connection_nat_state: z25.string().optional().describe('"srcnat" / "dstnat" / "!dstnat"'),
|
|
6770
|
+
connection_type: z25.string().optional().describe('Helper, e.g. "sip", "ftp"'),
|
|
6771
|
+
connection_bytes: z25.string().optional().describe('e.g. "1000000-0" (>1 MB connections)'),
|
|
6772
|
+
connection_limit: z25.string().optional().describe('e.g. "100,32"'),
|
|
6773
|
+
connection_rate: z25.string().optional().describe('e.g. "100k-1M"'),
|
|
6774
|
+
per_connection_classifier: z25.string().optional().describe('PCC for load balancing, e.g. "both-addresses:2/0"'),
|
|
6775
|
+
tcp_flags: z25.string().optional().describe('RouterOS flag expression e.g. "syn,!ack"'),
|
|
6776
|
+
dscp: z25.string().optional().describe("Match incoming DSCP (0-63)"),
|
|
6777
|
+
priority: z25.string().optional().describe("Match packet/queue priority (0-63)"),
|
|
6778
|
+
packet_size: z25.string().optional().describe('e.g. "1500" or "0-500"'),
|
|
6779
|
+
layer7_protocol: z25.string().optional().describe("Name of an /ip firewall layer7-protocol regex"),
|
|
6780
|
+
ipsec_policy: z25.string().optional().describe('e.g. "in,ipsec" or "out,none"'),
|
|
6781
|
+
nth: z25.string().optional().describe('e.g. "2,1" \u2014 every 2nd packet'),
|
|
6782
|
+
random: z25.string().optional().describe("Match a random N% of packets (1-99)"),
|
|
6783
|
+
time: z25.string().optional().describe('e.g. "8h-16h,mon,tue,wed,thu,fri"'),
|
|
6784
|
+
hotspot: z25.string().optional().describe('e.g. "auth", "!auth", "from-client"'),
|
|
6785
|
+
p2p: z25.string().optional(),
|
|
6675
6786
|
new_connection_mark: z25.string().optional(),
|
|
6676
6787
|
new_packet_mark: z25.string().optional(),
|
|
6677
6788
|
new_routing_mark: z25.string().optional(),
|
|
@@ -6690,7 +6801,7 @@ var firewallMangleTools = [
|
|
|
6690
6801
|
},
|
|
6691
6802
|
async handler(a, ctx) {
|
|
6692
6803
|
ctx.info(`Creating mangle rule: chain=${a.chain}, action=${a.action}`);
|
|
6693
|
-
const cmd = new Cmd("/ip firewall mangle add").set("chain", a.chain).set("action", a.action).opt("src-address", a.src_address).opt("dst-address", a.dst_address).opt("src-port", a.src_port).opt("dst-port", a.dst_port).opt("protocol", a.protocol).opt("in-interface", a.in_interface).opt("out-interface", a.out_interface).opt("connection-mark", a.connection_mark).opt("packet-mark", a.packet_mark).opt("routing-mark", a.routing_mark).opt("connection-state", a.connection_state).opt("new-connection-mark", a.new_connection_mark).opt("new-packet-mark", a.new_packet_mark).opt("new-routing-mark", a.new_routing_mark).opt("new-dscp", a.new_dscp).opt("new-ttl", a.new_ttl).opt("new-mss", a.new_mss).opt("routing-table", a.routing_table).opt("address-list", a.address_list).opt("address-list-timeout", a.address_list_timeout).bool("passthrough", a.passthrough).opt("comment", a.comment).flag("disabled", a.disabled).flag("log", a.log).opt("log-prefix", a.log ? a.log_prefix : undefined).opt("place-before", a.place_before).build();
|
|
6804
|
+
const cmd = new Cmd("/ip firewall mangle add").set("chain", a.chain).set("action", a.action).opt("src-address", a.src_address).opt("dst-address", a.dst_address).opt("src-port", a.src_port).opt("dst-port", a.dst_port).opt("protocol", a.protocol).opt("in-interface", a.in_interface).opt("out-interface", a.out_interface).opt("in-interface-list", a.in_interface_list).opt("out-interface-list", a.out_interface_list).opt("src-address-list", a.src_address_list).opt("dst-address-list", a.dst_address_list).opt("src-address-type", a.src_address_type).opt("dst-address-type", a.dst_address_type).opt("connection-mark", a.connection_mark).opt("packet-mark", a.packet_mark).opt("routing-mark", a.routing_mark).opt("connection-state", a.connection_state).opt("connection-nat-state", a.connection_nat_state).opt("connection-type", a.connection_type).opt("connection-bytes", a.connection_bytes).opt("connection-limit", a.connection_limit).opt("connection-rate", a.connection_rate).opt("per-connection-classifier", a.per_connection_classifier).opt("tcp-flags", a.tcp_flags).opt("dscp", a.dscp).opt("priority", a.priority).opt("packet-size", a.packet_size).opt("layer7-protocol", a.layer7_protocol).opt("ipsec-policy", a.ipsec_policy).opt("nth", a.nth).opt("random", a.random).opt("time", a.time).opt("hotspot", a.hotspot).opt("p2p", a.p2p).opt("new-connection-mark", a.new_connection_mark).opt("new-packet-mark", a.new_packet_mark).opt("new-routing-mark", a.new_routing_mark).opt("new-dscp", a.new_dscp).opt("new-ttl", a.new_ttl).opt("new-mss", a.new_mss).opt("routing-table", a.routing_table).opt("address-list", a.address_list).opt("address-list-timeout", a.address_list_timeout).bool("passthrough", a.passthrough).opt("comment", a.comment).flag("disabled", a.disabled).flag("log", a.log).opt("log-prefix", a.log ? a.log_prefix : undefined).opt("place-before", a.place_before).build();
|
|
6694
6805
|
const result = await executeMikrotikCommand(cmd, ctx);
|
|
6695
6806
|
const trimmed = result.trim();
|
|
6696
6807
|
if (looksLikeError(trimmed)) {
|
|
@@ -6784,10 +6895,33 @@ ${result}`;
|
|
|
6784
6895
|
protocol: z25.string().optional(),
|
|
6785
6896
|
in_interface: z25.string().optional(),
|
|
6786
6897
|
out_interface: z25.string().optional(),
|
|
6898
|
+
in_interface_list: z25.string().optional(),
|
|
6899
|
+
out_interface_list: z25.string().optional(),
|
|
6900
|
+
src_address_list: z25.string().optional().describe('Negate with "!name" (e.g. "!IR")'),
|
|
6901
|
+
dst_address_list: z25.string().optional().describe('Negate with "!name" (e.g. "!IR")'),
|
|
6902
|
+
src_address_type: z25.string().optional(),
|
|
6903
|
+
dst_address_type: z25.string().optional(),
|
|
6787
6904
|
connection_mark: z25.string().optional(),
|
|
6788
6905
|
packet_mark: z25.string().optional(),
|
|
6789
6906
|
routing_mark: z25.string().optional(),
|
|
6790
6907
|
connection_state: z25.string().optional(),
|
|
6908
|
+
connection_nat_state: z25.string().optional(),
|
|
6909
|
+
connection_type: z25.string().optional(),
|
|
6910
|
+
connection_bytes: z25.string().optional(),
|
|
6911
|
+
connection_limit: z25.string().optional(),
|
|
6912
|
+
connection_rate: z25.string().optional(),
|
|
6913
|
+
per_connection_classifier: z25.string().optional(),
|
|
6914
|
+
tcp_flags: z25.string().optional(),
|
|
6915
|
+
dscp: z25.string().optional(),
|
|
6916
|
+
priority: z25.string().optional(),
|
|
6917
|
+
packet_size: z25.string().optional(),
|
|
6918
|
+
layer7_protocol: z25.string().optional(),
|
|
6919
|
+
ipsec_policy: z25.string().optional(),
|
|
6920
|
+
nth: z25.string().optional(),
|
|
6921
|
+
random: z25.string().optional(),
|
|
6922
|
+
time: z25.string().optional(),
|
|
6923
|
+
hotspot: z25.string().optional(),
|
|
6924
|
+
p2p: z25.string().optional(),
|
|
6791
6925
|
new_connection_mark: z25.string().optional(),
|
|
6792
6926
|
new_packet_mark: z25.string().optional(),
|
|
6793
6927
|
new_routing_mark: z25.string().optional(),
|
|
@@ -22188,7 +22322,7 @@ function selectToolModules(filter = {}, catalog = moduleCatalog) {
|
|
|
22188
22322
|
// package.json
|
|
22189
22323
|
var package_default = {
|
|
22190
22324
|
name: "@usex/mikrotik-mcp",
|
|
22191
|
-
version: "3.
|
|
22325
|
+
version: "3.20.0",
|
|
22192
22326
|
description: "MCP server for MikroTik RouterOS \u2014 660+ tools over SSH for firewall, NAT, routing, DHCP, DNS, WireGuard, wireless, QoS and more.",
|
|
22193
22327
|
keywords: [
|
|
22194
22328
|
"ai",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.19.0",
|
|
4
4
|
"generated": "by scripts/gen-schemas.ts — do not edit by hand",
|
|
5
5
|
"toolCount": 721,
|
|
6
6
|
"tools": [
|
|
@@ -10179,11 +10179,34 @@
|
|
|
10179
10179
|
"type": "string"
|
|
10180
10180
|
},
|
|
10181
10181
|
"in_interface": {
|
|
10182
|
+
"description": "Negatable, e.g. \"!ether1\"",
|
|
10182
10183
|
"type": "string"
|
|
10183
10184
|
},
|
|
10184
10185
|
"out_interface": {
|
|
10185
10186
|
"type": "string"
|
|
10186
10187
|
},
|
|
10188
|
+
"in_interface_list": {
|
|
10189
|
+
"description": "Interface list, e.g. \"WAN\" or \"!LAN\"",
|
|
10190
|
+
"type": "string"
|
|
10191
|
+
},
|
|
10192
|
+
"out_interface_list": {
|
|
10193
|
+
"type": "string"
|
|
10194
|
+
},
|
|
10195
|
+
"src_address_list": {
|
|
10196
|
+
"description": "Match src in a named list; negate \"!name\"",
|
|
10197
|
+
"type": "string"
|
|
10198
|
+
},
|
|
10199
|
+
"dst_address_list": {
|
|
10200
|
+
"description": "Match dst in a named list; negate \"!name\"",
|
|
10201
|
+
"type": "string"
|
|
10202
|
+
},
|
|
10203
|
+
"connection_mark": {
|
|
10204
|
+
"type": "string"
|
|
10205
|
+
},
|
|
10206
|
+
"connection_nat_state": {
|
|
10207
|
+
"description": "\"srcnat\" / \"dstnat\" / \"!dstnat\"",
|
|
10208
|
+
"type": "string"
|
|
10209
|
+
},
|
|
10187
10210
|
"to_addresses": {
|
|
10188
10211
|
"description": "Single IP or range e.g. \"10.0.0.1\" or \"10.0.0.1-10.0.0.10\"",
|
|
10189
10212
|
"type": "string"
|
|
@@ -10327,6 +10350,26 @@
|
|
|
10327
10350
|
"out_interface": {
|
|
10328
10351
|
"type": "string"
|
|
10329
10352
|
},
|
|
10353
|
+
"in_interface_list": {
|
|
10354
|
+
"type": "string"
|
|
10355
|
+
},
|
|
10356
|
+
"out_interface_list": {
|
|
10357
|
+
"type": "string"
|
|
10358
|
+
},
|
|
10359
|
+
"src_address_list": {
|
|
10360
|
+
"description": "Negate with \"!name\"",
|
|
10361
|
+
"type": "string"
|
|
10362
|
+
},
|
|
10363
|
+
"dst_address_list": {
|
|
10364
|
+
"description": "Negate with \"!name\"",
|
|
10365
|
+
"type": "string"
|
|
10366
|
+
},
|
|
10367
|
+
"connection_mark": {
|
|
10368
|
+
"type": "string"
|
|
10369
|
+
},
|
|
10370
|
+
"connection_nat_state": {
|
|
10371
|
+
"type": "string"
|
|
10372
|
+
},
|
|
10330
10373
|
"to_addresses": {
|
|
10331
10374
|
"type": "string"
|
|
10332
10375
|
},
|
|
@@ -10452,7 +10495,7 @@
|
|
|
10452
10495
|
"destructiveHint": false,
|
|
10453
10496
|
"openWorldHint": false
|
|
10454
10497
|
},
|
|
10455
|
-
"description": "Creates an IPv4 mangle rule (`/ip firewall mangle add`) — the packet-marking and header-modification table, used to mark connections/packets/routing (for policy routing, QoS and per-connection classification) or to change DSCP/TTL/MSS. For accept/drop decisions use create_filter_rule; for address translation use create_nat_rule; for IPv6 mangle use create_ipv6_mangle_rule. chain: prerouting/input/forward/output/postrouting. action: mark-connection/mark-packet/mark-routing/change-dscp/change-ttl/change-mss/add-src-to-address-list/add-dst-to-address-list/fasttrack-connection/route/set-priority/accept/etc. Set the matching new-*-mark field for mark-* actions and keep passthrough=true so later rules can also match the same packet; for add-*-to-address-list set address_list (and optionally address_list_timeout). place_before accepts a rule number or ID (*N) to control insertion position. Returns the created rule's detail including its `.id`.",
|
|
10498
|
+
"description": "Creates an IPv4 mangle rule (`/ip firewall mangle add`) — the packet-marking and header-modification table, used to mark connections/packets/routing (for policy routing, QoS and per-connection classification) or to change DSCP/TTL/MSS. For accept/drop decisions use create_filter_rule; for address translation use create_nat_rule; for IPv6 mangle use create_ipv6_mangle_rule. chain: prerouting/input/forward/output/postrouting. action: mark-connection/mark-packet/mark-routing/change-dscp/change-ttl/change-mss/add-src-to-address-list/add-dst-to-address-list/fasttrack-connection/route/set-priority/accept/etc. Set the matching new-*-mark field for mark-* actions and keep passthrough=true so later rules can also match the same packet; for add-*-to-address-list set address_list (and optionally address_list_timeout). Full match surface: address-lists (src_address_list/dst_address_list — negate with a leading '!', e.g. dst_address_list='!IR' to route traffic NOT bound for a list), interface-lists, per_connection_classifier (PCC load-balancing), connection_nat_state, tcp_flags, dscp, layer7_protocol, time, and more. Typical policy-routing rule: chain=prerouting, src_address=<lan>, dst_address_list='!IR', action=mark-routing, new_routing_mark=<table>, then a routing rule/route uses that table. place_before accepts a rule number or ID (*N) to control insertion position. Returns the created rule's detail including its `.id`.",
|
|
10456
10499
|
"inputSchema": {
|
|
10457
10500
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
10458
10501
|
"type": "object",
|
|
@@ -10502,11 +10545,34 @@
|
|
|
10502
10545
|
"type": "string"
|
|
10503
10546
|
},
|
|
10504
10547
|
"in_interface": {
|
|
10548
|
+
"description": "Negatable, e.g. \"!ether1\"",
|
|
10505
10549
|
"type": "string"
|
|
10506
10550
|
},
|
|
10507
10551
|
"out_interface": {
|
|
10508
10552
|
"type": "string"
|
|
10509
10553
|
},
|
|
10554
|
+
"in_interface_list": {
|
|
10555
|
+
"description": "Interface list, negatable e.g. \"!WAN\"",
|
|
10556
|
+
"type": "string"
|
|
10557
|
+
},
|
|
10558
|
+
"out_interface_list": {
|
|
10559
|
+
"type": "string"
|
|
10560
|
+
},
|
|
10561
|
+
"src_address_list": {
|
|
10562
|
+
"description": "Match src in a named list; negate \"!name\"",
|
|
10563
|
+
"type": "string"
|
|
10564
|
+
},
|
|
10565
|
+
"dst_address_list": {
|
|
10566
|
+
"description": "Match dst in a named list; negate \"!name\" (e.g. \"!IR\" routes foreign traffic)",
|
|
10567
|
+
"type": "string"
|
|
10568
|
+
},
|
|
10569
|
+
"src_address_type": {
|
|
10570
|
+
"description": "e.g. \"local\", \"unicast\", \"!local\"",
|
|
10571
|
+
"type": "string"
|
|
10572
|
+
},
|
|
10573
|
+
"dst_address_type": {
|
|
10574
|
+
"type": "string"
|
|
10575
|
+
},
|
|
10510
10576
|
"connection_mark": {
|
|
10511
10577
|
"type": "string"
|
|
10512
10578
|
},
|
|
@@ -10517,7 +10583,74 @@
|
|
|
10517
10583
|
"type": "string"
|
|
10518
10584
|
},
|
|
10519
10585
|
"connection_state": {
|
|
10520
|
-
"description": "e.g. \"new\", \"established,related\", \"invalid\"",
|
|
10586
|
+
"description": "e.g. \"new\", \"established,related\", \"!invalid\"",
|
|
10587
|
+
"type": "string"
|
|
10588
|
+
},
|
|
10589
|
+
"connection_nat_state": {
|
|
10590
|
+
"description": "\"srcnat\" / \"dstnat\" / \"!dstnat\"",
|
|
10591
|
+
"type": "string"
|
|
10592
|
+
},
|
|
10593
|
+
"connection_type": {
|
|
10594
|
+
"description": "Helper, e.g. \"sip\", \"ftp\"",
|
|
10595
|
+
"type": "string"
|
|
10596
|
+
},
|
|
10597
|
+
"connection_bytes": {
|
|
10598
|
+
"description": "e.g. \"1000000-0\" (>1 MB connections)",
|
|
10599
|
+
"type": "string"
|
|
10600
|
+
},
|
|
10601
|
+
"connection_limit": {
|
|
10602
|
+
"description": "e.g. \"100,32\"",
|
|
10603
|
+
"type": "string"
|
|
10604
|
+
},
|
|
10605
|
+
"connection_rate": {
|
|
10606
|
+
"description": "e.g. \"100k-1M\"",
|
|
10607
|
+
"type": "string"
|
|
10608
|
+
},
|
|
10609
|
+
"per_connection_classifier": {
|
|
10610
|
+
"description": "PCC for load balancing, e.g. \"both-addresses:2/0\"",
|
|
10611
|
+
"type": "string"
|
|
10612
|
+
},
|
|
10613
|
+
"tcp_flags": {
|
|
10614
|
+
"description": "RouterOS flag expression e.g. \"syn,!ack\"",
|
|
10615
|
+
"type": "string"
|
|
10616
|
+
},
|
|
10617
|
+
"dscp": {
|
|
10618
|
+
"description": "Match incoming DSCP (0-63)",
|
|
10619
|
+
"type": "string"
|
|
10620
|
+
},
|
|
10621
|
+
"priority": {
|
|
10622
|
+
"description": "Match packet/queue priority (0-63)",
|
|
10623
|
+
"type": "string"
|
|
10624
|
+
},
|
|
10625
|
+
"packet_size": {
|
|
10626
|
+
"description": "e.g. \"1500\" or \"0-500\"",
|
|
10627
|
+
"type": "string"
|
|
10628
|
+
},
|
|
10629
|
+
"layer7_protocol": {
|
|
10630
|
+
"description": "Name of an /ip firewall layer7-protocol regex",
|
|
10631
|
+
"type": "string"
|
|
10632
|
+
},
|
|
10633
|
+
"ipsec_policy": {
|
|
10634
|
+
"description": "e.g. \"in,ipsec\" or \"out,none\"",
|
|
10635
|
+
"type": "string"
|
|
10636
|
+
},
|
|
10637
|
+
"nth": {
|
|
10638
|
+
"description": "e.g. \"2,1\" — every 2nd packet",
|
|
10639
|
+
"type": "string"
|
|
10640
|
+
},
|
|
10641
|
+
"random": {
|
|
10642
|
+
"description": "Match a random N% of packets (1-99)",
|
|
10643
|
+
"type": "string"
|
|
10644
|
+
},
|
|
10645
|
+
"time": {
|
|
10646
|
+
"description": "e.g. \"8h-16h,mon,tue,wed,thu,fri\"",
|
|
10647
|
+
"type": "string"
|
|
10648
|
+
},
|
|
10649
|
+
"hotspot": {
|
|
10650
|
+
"description": "e.g. \"auth\", \"!auth\", \"from-client\"",
|
|
10651
|
+
"type": "string"
|
|
10652
|
+
},
|
|
10653
|
+
"p2p": {
|
|
10521
10654
|
"type": "string"
|
|
10522
10655
|
},
|
|
10523
10656
|
"new_connection_mark": {
|
|
@@ -10689,6 +10822,26 @@
|
|
|
10689
10822
|
"out_interface": {
|
|
10690
10823
|
"type": "string"
|
|
10691
10824
|
},
|
|
10825
|
+
"in_interface_list": {
|
|
10826
|
+
"type": "string"
|
|
10827
|
+
},
|
|
10828
|
+
"out_interface_list": {
|
|
10829
|
+
"type": "string"
|
|
10830
|
+
},
|
|
10831
|
+
"src_address_list": {
|
|
10832
|
+
"description": "Negate with \"!name\" (e.g. \"!IR\")",
|
|
10833
|
+
"type": "string"
|
|
10834
|
+
},
|
|
10835
|
+
"dst_address_list": {
|
|
10836
|
+
"description": "Negate with \"!name\" (e.g. \"!IR\")",
|
|
10837
|
+
"type": "string"
|
|
10838
|
+
},
|
|
10839
|
+
"src_address_type": {
|
|
10840
|
+
"type": "string"
|
|
10841
|
+
},
|
|
10842
|
+
"dst_address_type": {
|
|
10843
|
+
"type": "string"
|
|
10844
|
+
},
|
|
10692
10845
|
"connection_mark": {
|
|
10693
10846
|
"type": "string"
|
|
10694
10847
|
},
|
|
@@ -10701,6 +10854,57 @@
|
|
|
10701
10854
|
"connection_state": {
|
|
10702
10855
|
"type": "string"
|
|
10703
10856
|
},
|
|
10857
|
+
"connection_nat_state": {
|
|
10858
|
+
"type": "string"
|
|
10859
|
+
},
|
|
10860
|
+
"connection_type": {
|
|
10861
|
+
"type": "string"
|
|
10862
|
+
},
|
|
10863
|
+
"connection_bytes": {
|
|
10864
|
+
"type": "string"
|
|
10865
|
+
},
|
|
10866
|
+
"connection_limit": {
|
|
10867
|
+
"type": "string"
|
|
10868
|
+
},
|
|
10869
|
+
"connection_rate": {
|
|
10870
|
+
"type": "string"
|
|
10871
|
+
},
|
|
10872
|
+
"per_connection_classifier": {
|
|
10873
|
+
"type": "string"
|
|
10874
|
+
},
|
|
10875
|
+
"tcp_flags": {
|
|
10876
|
+
"type": "string"
|
|
10877
|
+
},
|
|
10878
|
+
"dscp": {
|
|
10879
|
+
"type": "string"
|
|
10880
|
+
},
|
|
10881
|
+
"priority": {
|
|
10882
|
+
"type": "string"
|
|
10883
|
+
},
|
|
10884
|
+
"packet_size": {
|
|
10885
|
+
"type": "string"
|
|
10886
|
+
},
|
|
10887
|
+
"layer7_protocol": {
|
|
10888
|
+
"type": "string"
|
|
10889
|
+
},
|
|
10890
|
+
"ipsec_policy": {
|
|
10891
|
+
"type": "string"
|
|
10892
|
+
},
|
|
10893
|
+
"nth": {
|
|
10894
|
+
"type": "string"
|
|
10895
|
+
},
|
|
10896
|
+
"random": {
|
|
10897
|
+
"type": "string"
|
|
10898
|
+
},
|
|
10899
|
+
"time": {
|
|
10900
|
+
"type": "string"
|
|
10901
|
+
},
|
|
10902
|
+
"hotspot": {
|
|
10903
|
+
"type": "string"
|
|
10904
|
+
},
|
|
10905
|
+
"p2p": {
|
|
10906
|
+
"type": "string"
|
|
10907
|
+
},
|
|
10704
10908
|
"new_connection_mark": {
|
|
10705
10909
|
"type": "string"
|
|
10706
10910
|
},
|
|
@@ -48,11 +48,34 @@
|
|
|
48
48
|
"type": "string"
|
|
49
49
|
},
|
|
50
50
|
"in_interface": {
|
|
51
|
+
"description": "Negatable, e.g. \"!ether1\"",
|
|
51
52
|
"type": "string"
|
|
52
53
|
},
|
|
53
54
|
"out_interface": {
|
|
54
55
|
"type": "string"
|
|
55
56
|
},
|
|
57
|
+
"in_interface_list": {
|
|
58
|
+
"description": "Interface list, negatable e.g. \"!WAN\"",
|
|
59
|
+
"type": "string"
|
|
60
|
+
},
|
|
61
|
+
"out_interface_list": {
|
|
62
|
+
"type": "string"
|
|
63
|
+
},
|
|
64
|
+
"src_address_list": {
|
|
65
|
+
"description": "Match src in a named list; negate \"!name\"",
|
|
66
|
+
"type": "string"
|
|
67
|
+
},
|
|
68
|
+
"dst_address_list": {
|
|
69
|
+
"description": "Match dst in a named list; negate \"!name\" (e.g. \"!IR\" routes foreign traffic)",
|
|
70
|
+
"type": "string"
|
|
71
|
+
},
|
|
72
|
+
"src_address_type": {
|
|
73
|
+
"description": "e.g. \"local\", \"unicast\", \"!local\"",
|
|
74
|
+
"type": "string"
|
|
75
|
+
},
|
|
76
|
+
"dst_address_type": {
|
|
77
|
+
"type": "string"
|
|
78
|
+
},
|
|
56
79
|
"connection_mark": {
|
|
57
80
|
"type": "string"
|
|
58
81
|
},
|
|
@@ -63,7 +86,74 @@
|
|
|
63
86
|
"type": "string"
|
|
64
87
|
},
|
|
65
88
|
"connection_state": {
|
|
66
|
-
"description": "e.g. \"new\", \"established,related\", \"invalid\"",
|
|
89
|
+
"description": "e.g. \"new\", \"established,related\", \"!invalid\"",
|
|
90
|
+
"type": "string"
|
|
91
|
+
},
|
|
92
|
+
"connection_nat_state": {
|
|
93
|
+
"description": "\"srcnat\" / \"dstnat\" / \"!dstnat\"",
|
|
94
|
+
"type": "string"
|
|
95
|
+
},
|
|
96
|
+
"connection_type": {
|
|
97
|
+
"description": "Helper, e.g. \"sip\", \"ftp\"",
|
|
98
|
+
"type": "string"
|
|
99
|
+
},
|
|
100
|
+
"connection_bytes": {
|
|
101
|
+
"description": "e.g. \"1000000-0\" (>1 MB connections)",
|
|
102
|
+
"type": "string"
|
|
103
|
+
},
|
|
104
|
+
"connection_limit": {
|
|
105
|
+
"description": "e.g. \"100,32\"",
|
|
106
|
+
"type": "string"
|
|
107
|
+
},
|
|
108
|
+
"connection_rate": {
|
|
109
|
+
"description": "e.g. \"100k-1M\"",
|
|
110
|
+
"type": "string"
|
|
111
|
+
},
|
|
112
|
+
"per_connection_classifier": {
|
|
113
|
+
"description": "PCC for load balancing, e.g. \"both-addresses:2/0\"",
|
|
114
|
+
"type": "string"
|
|
115
|
+
},
|
|
116
|
+
"tcp_flags": {
|
|
117
|
+
"description": "RouterOS flag expression e.g. \"syn,!ack\"",
|
|
118
|
+
"type": "string"
|
|
119
|
+
},
|
|
120
|
+
"dscp": {
|
|
121
|
+
"description": "Match incoming DSCP (0-63)",
|
|
122
|
+
"type": "string"
|
|
123
|
+
},
|
|
124
|
+
"priority": {
|
|
125
|
+
"description": "Match packet/queue priority (0-63)",
|
|
126
|
+
"type": "string"
|
|
127
|
+
},
|
|
128
|
+
"packet_size": {
|
|
129
|
+
"description": "e.g. \"1500\" or \"0-500\"",
|
|
130
|
+
"type": "string"
|
|
131
|
+
},
|
|
132
|
+
"layer7_protocol": {
|
|
133
|
+
"description": "Name of an /ip firewall layer7-protocol regex",
|
|
134
|
+
"type": "string"
|
|
135
|
+
},
|
|
136
|
+
"ipsec_policy": {
|
|
137
|
+
"description": "e.g. \"in,ipsec\" or \"out,none\"",
|
|
138
|
+
"type": "string"
|
|
139
|
+
},
|
|
140
|
+
"nth": {
|
|
141
|
+
"description": "e.g. \"2,1\" — every 2nd packet",
|
|
142
|
+
"type": "string"
|
|
143
|
+
},
|
|
144
|
+
"random": {
|
|
145
|
+
"description": "Match a random N% of packets (1-99)",
|
|
146
|
+
"type": "string"
|
|
147
|
+
},
|
|
148
|
+
"time": {
|
|
149
|
+
"description": "e.g. \"8h-16h,mon,tue,wed,thu,fri\"",
|
|
150
|
+
"type": "string"
|
|
151
|
+
},
|
|
152
|
+
"hotspot": {
|
|
153
|
+
"description": "e.g. \"auth\", \"!auth\", \"from-client\"",
|
|
154
|
+
"type": "string"
|
|
155
|
+
},
|
|
156
|
+
"p2p": {
|
|
67
157
|
"type": "string"
|
|
68
158
|
},
|
|
69
159
|
"new_connection_mark": {
|
|
@@ -26,11 +26,34 @@
|
|
|
26
26
|
"type": "string"
|
|
27
27
|
},
|
|
28
28
|
"in_interface": {
|
|
29
|
+
"description": "Negatable, e.g. \"!ether1\"",
|
|
29
30
|
"type": "string"
|
|
30
31
|
},
|
|
31
32
|
"out_interface": {
|
|
32
33
|
"type": "string"
|
|
33
34
|
},
|
|
35
|
+
"in_interface_list": {
|
|
36
|
+
"description": "Interface list, e.g. \"WAN\" or \"!LAN\"",
|
|
37
|
+
"type": "string"
|
|
38
|
+
},
|
|
39
|
+
"out_interface_list": {
|
|
40
|
+
"type": "string"
|
|
41
|
+
},
|
|
42
|
+
"src_address_list": {
|
|
43
|
+
"description": "Match src in a named list; negate \"!name\"",
|
|
44
|
+
"type": "string"
|
|
45
|
+
},
|
|
46
|
+
"dst_address_list": {
|
|
47
|
+
"description": "Match dst in a named list; negate \"!name\"",
|
|
48
|
+
"type": "string"
|
|
49
|
+
},
|
|
50
|
+
"connection_mark": {
|
|
51
|
+
"type": "string"
|
|
52
|
+
},
|
|
53
|
+
"connection_nat_state": {
|
|
54
|
+
"description": "\"srcnat\" / \"dstnat\" / \"!dstnat\"",
|
|
55
|
+
"type": "string"
|
|
56
|
+
},
|
|
34
57
|
"to_addresses": {
|
|
35
58
|
"description": "Single IP or range e.g. \"10.0.0.1\" or \"10.0.0.1-10.0.0.10\"",
|
|
36
59
|
"type": "string"
|
|
@@ -33,6 +33,26 @@
|
|
|
33
33
|
"out_interface": {
|
|
34
34
|
"type": "string"
|
|
35
35
|
},
|
|
36
|
+
"in_interface_list": {
|
|
37
|
+
"type": "string"
|
|
38
|
+
},
|
|
39
|
+
"out_interface_list": {
|
|
40
|
+
"type": "string"
|
|
41
|
+
},
|
|
42
|
+
"src_address_list": {
|
|
43
|
+
"description": "Negate with \"!name\" (e.g. \"!IR\")",
|
|
44
|
+
"type": "string"
|
|
45
|
+
},
|
|
46
|
+
"dst_address_list": {
|
|
47
|
+
"description": "Negate with \"!name\" (e.g. \"!IR\")",
|
|
48
|
+
"type": "string"
|
|
49
|
+
},
|
|
50
|
+
"src_address_type": {
|
|
51
|
+
"type": "string"
|
|
52
|
+
},
|
|
53
|
+
"dst_address_type": {
|
|
54
|
+
"type": "string"
|
|
55
|
+
},
|
|
36
56
|
"connection_mark": {
|
|
37
57
|
"type": "string"
|
|
38
58
|
},
|
|
@@ -45,6 +65,57 @@
|
|
|
45
65
|
"connection_state": {
|
|
46
66
|
"type": "string"
|
|
47
67
|
},
|
|
68
|
+
"connection_nat_state": {
|
|
69
|
+
"type": "string"
|
|
70
|
+
},
|
|
71
|
+
"connection_type": {
|
|
72
|
+
"type": "string"
|
|
73
|
+
},
|
|
74
|
+
"connection_bytes": {
|
|
75
|
+
"type": "string"
|
|
76
|
+
},
|
|
77
|
+
"connection_limit": {
|
|
78
|
+
"type": "string"
|
|
79
|
+
},
|
|
80
|
+
"connection_rate": {
|
|
81
|
+
"type": "string"
|
|
82
|
+
},
|
|
83
|
+
"per_connection_classifier": {
|
|
84
|
+
"type": "string"
|
|
85
|
+
},
|
|
86
|
+
"tcp_flags": {
|
|
87
|
+
"type": "string"
|
|
88
|
+
},
|
|
89
|
+
"dscp": {
|
|
90
|
+
"type": "string"
|
|
91
|
+
},
|
|
92
|
+
"priority": {
|
|
93
|
+
"type": "string"
|
|
94
|
+
},
|
|
95
|
+
"packet_size": {
|
|
96
|
+
"type": "string"
|
|
97
|
+
},
|
|
98
|
+
"layer7_protocol": {
|
|
99
|
+
"type": "string"
|
|
100
|
+
},
|
|
101
|
+
"ipsec_policy": {
|
|
102
|
+
"type": "string"
|
|
103
|
+
},
|
|
104
|
+
"nth": {
|
|
105
|
+
"type": "string"
|
|
106
|
+
},
|
|
107
|
+
"random": {
|
|
108
|
+
"type": "string"
|
|
109
|
+
},
|
|
110
|
+
"time": {
|
|
111
|
+
"type": "string"
|
|
112
|
+
},
|
|
113
|
+
"hotspot": {
|
|
114
|
+
"type": "string"
|
|
115
|
+
},
|
|
116
|
+
"p2p": {
|
|
117
|
+
"type": "string"
|
|
118
|
+
},
|
|
48
119
|
"new_connection_mark": {
|
|
49
120
|
"type": "string"
|
|
50
121
|
},
|
|
@@ -33,6 +33,26 @@
|
|
|
33
33
|
"out_interface": {
|
|
34
34
|
"type": "string"
|
|
35
35
|
},
|
|
36
|
+
"in_interface_list": {
|
|
37
|
+
"type": "string"
|
|
38
|
+
},
|
|
39
|
+
"out_interface_list": {
|
|
40
|
+
"type": "string"
|
|
41
|
+
},
|
|
42
|
+
"src_address_list": {
|
|
43
|
+
"description": "Negate with \"!name\"",
|
|
44
|
+
"type": "string"
|
|
45
|
+
},
|
|
46
|
+
"dst_address_list": {
|
|
47
|
+
"description": "Negate with \"!name\"",
|
|
48
|
+
"type": "string"
|
|
49
|
+
},
|
|
50
|
+
"connection_mark": {
|
|
51
|
+
"type": "string"
|
|
52
|
+
},
|
|
53
|
+
"connection_nat_state": {
|
|
54
|
+
"type": "string"
|
|
55
|
+
},
|
|
36
56
|
"to_addresses": {
|
|
37
57
|
"type": "string"
|
|
38
58
|
},
|