Once my little Mikrotik RB951-2n was working properly, I decided to give it a proper packet filter. My IPv6 hosts are all quite capable of defending themselves, but a packet filter at the border reduces traffic on the network, plus I wanted to see how Mikrotik did it.
I wanted to stop the Internet getting to the Mikrotik itself, and forward only ICMPv6 and ssh into my network, while not preventing any outbound connections.
This is a pretty minimal approach, but starting small is generally a good idea. The more rules you have the greater the load on your router, and for a very small unit such as the RB951-2n, that can be a real problem. Worst case, large rulesets can slow the delivery of packets. Also, large rulesets are difficult to understand, and therefore harder to create, inspect and modify. Simplicity is itself a security positive. So rulesets should in general be kept as small as possible.
By the way – the input chain controls packets arriving at the router addressed to interfaces actually on the router (any interface on the router, including loopback interfaces). The output chain controls packets leaving the router, sourced from an interface on the router. The forward chain controls packets passing through the router – neither destined for nor sourced from interfaces on the router. It’s possible to create your own chains, too, but that wasn’t needed in this simple ruleset.
Within a given chain, the order of the rules is critical, as they are applied in the order given. If you have a rule that allows everything, followed by a rule that blocks (say) HTTP, then the second rule will never have any effect. Generally speaking, you should have rules that allow specific things, followed by a catch-all rule that blocks everything else. The other approach is to have rules that block specific things, then allow everything else, but this is not as safe.
My first attempt looked like this (note that the default policy on all default chains is “accept”):
/ipv6 firewall filter add action=accept chain=forward connection-state=established in-interface=pppoe-out1 add action=accept chain=forward connection-state=related in-interface=pppoe-out1 add action=accept chain=forward in-interface=pppoe-out1 protocol=icmpv6 add action=accept chain=forward dst-port=22 in-interface=pppoe-out1 protocol=tcp add action=drop chain=forward in-interface=pppoe-out1 add action=accept chain=input in-interface=pppoe-out1 protocol=icmpv6 add action=drop chain=input in-interface=pppoe-out1 add action=drop chain=input in-interface=ether1-gateway
The above allows established and related packets to be forwarded into my networks, it allows ICMPv6 in, and allows anything into my network on port 22 (ssh). It stops all input to the Mikrotik itself from outside, however, except for ICMPv6. And it allows anything outbound.
Why do I need ICMPv6? For two reasons – firstly, it is essential to the operation of IPv6, or at least the Packet Too Big message is. PMTUD (Path MTU Discovery) needs ICMPv6, and fragmentation will not work without PMTUD. Secondly, echo requests and echo responses (“pings”) are very useful tools when troubleshooting. Fine-grained control of ICMPv6 does make sense in larger contexts, but for a SOHO network, allowing all ICMPv6 is simpler.
My new packet filters seemed to be working well at first, but I found that after a while, IPv6 connectivity would disappear. It wasn’t all connectivity – the PPPoE link still showed “connected” and IPv4 was completely unaffected. To figure out what was going on, I edited the rule that blocked all input on pppoe-out1, changing “drop” to “log”, and bounced the PPPoE link. Seconds later it was clear what my mistake had been – I had forgotten that the router is doing DHCPv6-PD, and the remote end needs to be able to communicate with the router on port 546.
The solution was to add a rule permitting inbound DHCPv6 (UDP on port 546) to the PPPoE interface:
/ipv6 firewall filter add action=accept chain=forward connection-state=established in-interface=pppoe-out1 add action=accept chain=forward connection-state=related in-interface=pppoe-out1 add action=accept chain=forward in-interface=pppoe-out1 protocol=icmpv6 add action=accept chain=forward dst-port=22 in-interface=pppoe-out1 protocol=tcp add action=drop chain=forward in-interface=pppoe-out1 add action=accept chain=input in-interface=pppoe-out1 protocol=icmpv6 add action=accept chain=input dst-port=546 in-interface=pppoe-out1 protocol=udp add action=drop chain=input in-interface=pppoe-out1 add action=drop chain=input in-interface=ether1-gateway
With the above rules, a lot of unnecessary traffic is stopped before it reaches my network. The traffic load on my link is irritating, but there is little I can do about it. Choosing to “drop” rather than “reject” packets causes connections to take a lot longer to fail, which is good, but that’s about all you can do. If you have any very persistent “nuisance callers”, discuss them with your ISP – they may be able to help, particularly if the same nuisance is being experienced by other customers.
This is a very simple ruleset. The Mikrotik has a lot of sophisticated features that let you mark, track and rate-limit, plus a lot more besides. But the above is enough for now.
PS: The above discusses only IPv6 rules! If you are also running IPv4, as you almost certainly are, then you will need to set up similar rules for IPv4. An IPv6 ruleset has no effect on IPv4 at all, and an IPv4 ruleset has no effect on IPv6.