Feature request: Grouping of firewall rules/DHCP leases

Currently I have a many routers with few dozen firewall rules. Most of them can be easily grouped to 2-3 groups like “VPN” or “MOBILE”.
Similar issue is also bugging me in few home setups where I assigned static leases for every device. It will be handy to group devices by user (currently almost anyone have at least smartphone and a laptop).

It will be nice to see grouping in firewall and dhcp leases. Comments for every rule is nice, but having a group with comment will be even nicer.
I believe it’s not that hard to implement that :wink:

For firewall rules you can already do that using chains (use a single action=jump rule based on the selection criteria for your group, and then jump to a firewall chain that holds all the rules for that group).

I’m speaking more about grouping in terms of visual representation of the data.
Something like interface for queues:

Maybe not exactly like parent/child structure (because it doesn’t make sense) but just simple group tag similar to these used in Winbox “Managed” tab.

Have you tried to give those devices dhcp static ips and adding them grouped per user to an address list. later apply your rules using those address lists.

IMHO, the parent/child structure actually makes perfect sense for firewall rules, much more so than the group one.

As mentioned already, you can group stuff with “chains”, which is essentially like declaring the “parent” of a rule. And the built in chains are effectively the “root nodes” in the parent/child hierarchy.

+1 for those.

I’m not sure about the DHCP list though… Grouped by a DHCP server maybe? That’s the only possible grouping I see.

It’s a patch-like solution to archive some sort of clearer view.
Real world example where firewall grouping will be really handy:

1st group - WAN open ports
Screenshot 2015-12-21 00.48.57.png
2nd group - VPN rules
Screenshot 2015-12-21 00.49.05.png
Of course on this device (which is just a test one) there’re only 12 rules, but on production one I sometimes have 50+.

Right, and again, the current way of dealing with those is to separate them into different CUSTOM chains.

e.g.

add chain=input in-interface=ether1-gateway action=jump jump-target=input-wan comment="WAN input group"
add chain=input-wan protocol=tcp dst-port=8291 action=accept comment="Accept Winbox connections from WAN"
add chain=input-wan protocol=icmp action=accept comment="Accept ICMP packets from WAN"
add chain=input-wan action=drop comment="Drop everything else from WAN"

add chain=input in-interface=ether2-local-master action=jump jump-target=input-lan comment="LAN input group"
add chain=input-lan protocol=icmp action=accept comment="Accept ICMP packets from LAN"
add chain=input-lan action=drop comment="Drop everything else from LAN"

The only thing missing is the visualization of those custom chains, akin to that of the queues.

@boen_robot: I feel we’re abusing chains. Chain is useful if you’re jumping to it from more than one place - using it that way creates unnecessary overhead wasting CPU cycles :wink:



…maybe as a programmer I cary too much? :wink:

Although I’m not entirely sure how they work under the hood, I think of firewall rules more like “if” statements…

Each rule is like an “if”, and each chain (custom or built in) is like a function body (each rule in the chain being a statement inside that body). Assuming this is how they indeed work under the hood, I think using custom chains would actually improve performance in cases where each rule may be checking the same things over and over again.

Compare (pseudo PHP script):

function input($packet) {
    //add chain=input in-interface=ether1-gateway action=jump jump-target=input_wan comment="WAN input group"
    if ($packet['in-interface'] === 'ether1-gateway') {
        input_wan($packet);
    }
    
    //add chain=input in-interface=ether2-local-master action=jump jump-target=input_lan comment="LAN input group"
    if ($packet['in-interface'] === 'ether2-local-master') {
        input_lan($packet);
    }
}

function input_wan($packet) {
    //add chain=input_wan protocol=tcp dst-port=8291 action=accept comment="Accept Winbox connections from WAN"
    if ($packet['protocol'] === 'tcp' && $packet['dst-port'] === 8291) {
        exec('accept');
    }
    
    //add chain=input_wan protocol=icmp action=accept comment="Accept ICMP packets from WAN"
    if ($packet['protocol'] === 'icmp') {
        exec('accept');
    }
    
    //add chain=input_wan action=drop comment="Drop everything else from WAN"
    exec('drop');
}


function input_lan($packet) {
    //add chain=input_lan protocol=icmp action=accept comment="Accept ICMP packets from LAN"
    if ($packet['protocol'] === 'icmp') {
        exec('accept');
    }
    
    //add chain=input_lan action=drop comment="Drop everything else from LAN"
    exec('drop');
}

vs

function input($packet) {
    //add chain=input in-interface=ether1-gateway protocol=tcp dst-port=8291 action=accept comment="Accept Winbox connections from WAN"
    if ($packet['in-interface'] === 'ether1-gateway' && $packet['protocol'] === 'tcp' && $packet['dst-port'] === 8291) {
        exec('accept');
    }
    
    //add chain=input in-interface=ether1-gateway protocol=icmp action=accept comment="Accept ICMP packets from WAN"
    if ($packet['in-interface'] === 'ether1-gateway' && $packet['protocol'] === 'icmp') {
        exec('accept');
    }
    
    //add chain=input in-interface=ether1-gateway action=drop comment="Drop everything else from WAN"
    if ($packet['in-interface'] === 'ether1-gateway') {
        exec('drop');
    }
        
    //add chain=input in-interface=ether2-local-master protocol=icmp action=accept comment="Accept ICMP packets from LAN"
    if ($packet['in-interface'] === 'ether2-local-master' && $packet['protocol'] === 'icmp') {
        exec('accept');
    }
    
    //add chain=input in-interface=ether2-local-master action=drop comment="Drop everything else from LAN"
    if ($packet['in-interface'] === 'ether2-local-master') {
        exec('drop');
    }
}

Perhaps in small rule sets like the above, the second example may be more efficient, but on a larger scale, making the same check over and over again will ultimately be more CPU intensive than just a few jumps (one per check) that ultimately reach a verdict on the action for the packet.

MikroTik: any word about this? It will be really useful…

Chains can speed things up by simplifying the number of checks performed.

Consider that you want different policies on dstnat pinholes - one rule in forward chain excludes all packets that aren’t pinhole traffic:
chain=forward connection-nat-state=dstnat action=jump jump-target=pinholes

Or if you have several rules in a row with all but one condition are the same, check the most with a single jump rule and in the chain, each check only requires one comparison- say it was 3 similar things- protocol, port, and in-interface but different IPs each - and 5 different IPs - then it would be 20 checks w/o a chain, but 3 checks, a jump and then 5 checks. (8 total)

Not only all this but they make managing complex rule sets much more maintainable.

No, it is the other way around!
Using multiple chains you can avoid that a long input chain has to be checked rule-by-rule before traffic is matched.
In the input chain you put some rules that match input interface, for example, and jump to the next chain.
Then the rules in that chain are only evaluated for traffic coming in on that interface.