-
-
Save Nullcaller/c9c9bf97df783a355a7d6a24c7b6fd50 to your computer and use it in GitHub Desktop.
# Function that converts number type variables to pointer strings used to retrieve log messages | |
:global wlsGetPointer do={ | |
# Convert argument to hexadecimal form | |
:local number [:tonum $1]; | |
:local hexadec "0"; | |
:local remainder 0; | |
:local hexChars "0123456789abcdef"; | |
:if ($number > 0) do={ | |
:set hexadec ""; | |
} | |
:while ($number > 0) do={ | |
:set remainder ($number % 16); | |
:set number (($number-$remainder) / 16); | |
:set hexadec ([:pick $hexChars $remainder].$hexadec); | |
} | |
# Return pointer string | |
:return "*$hexadec"; | |
} | |
# Store log iterator in a global variable to preserve state between script restarts | |
:global wlsLogIterator; | |
# Initialize log iterator in case this is the first time the script is ran after a restart | |
:if ($wlsLogIterator="") do={ | |
:set wlsLogIterator 0; | |
} | |
# Store current log message pointer string in a local cache not to overload the CPU with constant number conversions | |
:local wlsLogIteratorPointer; | |
# Initialize local log message pointer cache | |
:set wlsLogIteratorPointer [$wlsGetPointer $wlsLogIterator]; | |
# Declare loop-iteration-local variables | |
:local wlsCurrentMessage; | |
:local wlsMAC; | |
:local wlsSMAC; | |
:local wlsIF; | |
:local wlsACT ""; | |
# Run loop perpetually until script is manually stopped | |
:while (true) do={ | |
# Sleep for 10 ms between log message parse attempts | |
:delay 10ms; | |
# Check whether the current log message is empty (non-empty messages have multiple attributes: ".id=<>;buffer=<>;message=<>...", empty ones only have an id: ".id=<>") | |
:if ([:log get $wlsLogIteratorPointer] != ".id=$wlsLogIteratorPointer") do={ | |
# Set current message content variable | |
:set $wlsCurrentMessage [:log get $wlsLogIteratorPointer message]; | |
# Attempt to parse current message | |
# Detect connection messages | |
:if ($wlsCurrentMessage~"^(([0-9ABCDEF]{2}:){5}([0-9ABCDEF]{2}))@([0-9A-z-]*) connected") do={ | |
:set wlsMAC [:pick $wlsCurrentMessage 0 17]; | |
:set wlsIF [:pick $wlsCurrentMessage 18 [:find $wlsCurrentMessage " " 18]]; | |
:set wlsACT "CONNECT"; | |
} else={ | |
# Detect disconnection messages | |
:if ($wlsCurrentMessage~"^(([0-9ABCDEF]{2}:){5}([0-9ABCDEF]{2}))@([0-9A-z-]*) disconnected") do={ | |
:set wlsMAC [:pick $wlsCurrentMessage 0 17]; | |
:set wlsIF [:pick $wlsCurrentMessage 18 [:find $wlsCurrentMessage " " 18]]; | |
# Detect 'wireless' package style station roam messages | |
:if ($wlsCurrentMessage~"^(([0-9ABCDEF]{2}:){5}([0-9ABCDEF]{2}))@([0-9A-z-]*) disconnected, registered to other interface") do={ | |
:set wlsACT "IF_SWITCH"; | |
} else={ | |
:set wlsACT "DISCONNECT"; | |
} | |
} else={ | |
# Detect 'wifi-qcom' and 'wifi-qcom-ac' package style roam messages | |
:if ($wlsCurrentMessage~"^(([0-9ABCDEF]{2}:){5}([0-9ABCDEF]{2}))@([0-9A-z-]*) roamed") do={ | |
:set wlsMAC [:pick $wlsCurrentMessage 0 17]; | |
:set wlsIF [:pick $wlsCurrentMessage 18 [:find $wlsCurrentMessage " " 18]]; | |
:set wlsACT "IF_SWITCH"; | |
} | |
} | |
} | |
# // if-else constructs are a mess in ROS scripts | |
# Check that the current message is about an action of a wireless client | |
:if ($wlsACT != "") do={ | |
# Sanitize MAC address, replacing ':' with '-' and writing to $wlsSMAC | |
:set wlsSMAC ""; | |
:for i from=0 to=([:len $wlsMAC] - 1) do={ | |
:local char [:pick $wlsMAC $i]; | |
:if ($char = ":") do={ | |
:set $char "-"; | |
} | |
:set $wlsSMAC ($wlsSMAC . $char); | |
} | |
# Determine if the wireless interface name ends in "...vpn" | |
:local iflen [:len $wlsIF]; | |
:if ([:pick $wlsIF ($iflen-3) $iflen]="vpn") do={ | |
# If it does, and the action of the client is CONNECT | |
:if ($wlsACT="CONNECT") do={ | |
# Remove the client's LAN MAC-based domain from the address list of clients connected to the vpn-routed wireless network if it's somehow already there | |
:if ([/ip firewall address-list find address="$wlsSMAC.lan" list=wifi-vpn]!="") do={ | |
/ip firewall address-list remove [/ip firewall address-list find address="$wlsSMAC.lan" list=wifi-vpn]; | |
} | |
# Add the client's LAN MAC-based domain to the address list of clients connected to the vpn-routed wireless network | |
# The LAN MAC-based domain will be resolved to the client's IP address if it uses DHCP to obtain an IP address | |
/ip firewall address-list add address="$wlsSMAC.lan" list=wifi-vpn; | |
} else={ | |
# If it does, and the action of the client is DISCONNECT | |
:if ($wlsACT="DISCONNECT") do={ | |
# Remove the client's LAN MAC-based domain from the address list of clients connected to the vpn-routed wireless network | |
/ip firewall address-list remove [/ip firewall address-list find address="$wlsSMAC.lan" list=wifi-vpn]; | |
} | |
} | |
} else={ | |
### Additional interface name checks can be added here to do multiple differently-routed networks | |
### But don't forget to put the following into the last else={...} statement, like it is now: | |
# If it doesn't, and the action of the client is CONNECT | |
:if ($wlsACT="CONNECT") do={ | |
# Check if the client's LAN MAC-based domain is somehow in the address list of clients connected to the vpn-routed wireless network | |
:if ([/ip firewall address-list find address="$wlsSMAC.lan" list=wifi-vpn]!="") do={ | |
# If it is, remove it from the address list of clients connected to the vpn-routed wireless network | |
/ip firewall address-list remove [/ip firewall address-list find address="$wlsSMAC.lan" list=wifi-vpn]; | |
} | |
} | |
} | |
} | |
# Reset the action of the client | |
:set wlsACT ""; | |
# Advance the log iterator | |
:set wlsLogIterator ($wlsLogIterator+1); | |
# Update local log message pointer cache to check the next message on the next loop iteration | |
:set wlsLogIteratorPointer [$wlsGetPointer $wlsLogIterator]; | |
} | |
} |
The script is designed to run on a CAPsMAN controller.
The script WILL NOT WORK CORRECTLY if you're NOT using CAPsMAN to control your APs OR using a mix of MikroTik and non-MikroTik wireless hardware.
If you're not running a multi-AP setup (which, for 5 GHz wifi, frankly, you probably should), disregard this message.
Also note that the script provides you with the means of adjusting the routing, but does not do any routing adjustment in and of itself. You have to do that yourself with firewall rules and routing tables.
Last, but not least, this is NOT a secure way of forcing adjusted routing. For that, you need to use VLANs. This script assumes that you trust the clients to a certain degree and that you want to simply allow them to choose between adjusted and non-adjusted routing. If a client does not use DHCP and instead assigns itself an IP address of its own choosing, this script will not work correctly for that client.
This script is designed to run perpetually in the background and check RouterOS logs for new wifi connection/disconnection/roam events. Make sure the logging of those events is not disabled if you want to use this script, then make the router run it on startup:
The script WILL NOT WORK without you using a DHCP server script that adds LAN MAC-based domains to the local DNS resolver static records.