Filtering and Rate-Limiting ICMPv6 on a GNU/Linux Server


ICMP (Internet Control Message Protocol) in IPv6 is used for a variety of purposes. As opposed to IPv4, support for some of the applications of ICMP by nodes participating in an IPV6 network is mandatory. On the other hand, due to the security-relvant nature of some of the applications, where in IPv4 it may have sufficed to simply allow or disallow all ICMP traffic or certain types of ICMP packages, deeper inspection of ICMP traffic is required in IPv6.

In the following I draft an example of a set of rules for a Linux netfilter firewall on a simple, standalone application server (not a virtualization host).

Design Influences

To some degree I try to follow the advice given by the authors of RFC 4890 „ICMPv6 Filtering Recommendations“ (see also section „References“), but the detection of invalid traffic is far less detailed that what they are presenting in the code examples in appendix A of RFC 4890. I also drop support for Secure Neighbour Discovery (SeND), because this protocol is not implemented on the application server.

I try to minimize the attackable surface by blocking nonessential types of ICMPv6 traffic.

Please note that the code snippets presented in this article are put out of context and at some points simplified, they do not form a complete solution that could be run standalone. Most notably, the code assumes that the policy of the ip6tables chains INPUT and OUPUT is set to DROP, but on the other hand, if those policies are set, many more rules are required to have operative IPv6 on the server. The scope of such setup is outside of this document.

Example: Inbound Traffic

Consider the following excerpts from a practical ip6tables setup that is applied on a GNU/Linux server in insular operation at a hosting service provider.

The script has beforehand set the policies of the ip6tables chains INPUT and OUTPUT to DROP.

The following excerpt deals with inbound traffic, manipulating the chain INPUT:

ip6tables -N INPUT-ICMPv6
for icmpv6_type in 1 2 3 4 128 129 133 134 135 136 141 142 151 152 153; do
    case "$icmpv6_type" in
            ip6tables -A INPUT-ICMPv6 -p icmpv6 --icmpv6-type $icmpv6_type \
                -m limit --limit 500/min -j ACCEPT
            ip6tables -A INPUT-ICMPv6 -p icmpv6 --icmpv6-type $icmpv6_type \
                -j DROP
            ip6tables -A INPUT-ICMPv6 -p icmpv6 --icmpv6-type $icmpv6_type \
                -m hl --hl-eq 255 -j ACCEPT
            ip6tables -A INPUT-ICMPv6 -p icmpv6 --icmpv6-type $icmpv6_type -j ACCEPT
ip6tables -A INPUT -p icmpv6 -j INPUT-ICMPv6
  • At line 1 a new rulechain INPUT-ICMPv6 is declared.
  • At line 2 the ICMPv6 types are listed that are specified by RFC4890, „ICMPv6 Filtering Recommendations“, section „4.4. Recommendations for ICMPv6 Local Configuration Traffic“, subsection „4.4.1 Traffic That Must Not Be Dropped“, but the types 148 and 149 which are listed as „SEND Certificate Path Notification messages“ are not included.
  • Lines 4 to 7 consider type 128, the ICMPv6 echo request and types 152, 152 and 153, the package types specified by RFC 4286, „Multicast Router Discovery“. Packets of such type are rate-limited to 500 packets per minute (with an allowable burst of 5). Packets of those types that exceed the ratelimit are dropped.
  • Lines 10 to 13 consider ICMPv6 packet types specified by RFC 4861 „Neighbor Discovery in IPv6“. Packets of such types are accepted if they have their field „Hop Limit“ set to 255. The exact reasoning is given by RFC 4861, section „11.2. Securing Neighbor Discovery Messages“. This validation ensures that the packets have originated from a neighbouring router.
  • Lines 14 to 16 consider all other acceptable types of ICMPv6 and accepts them without further validation.

A note about rate-limiting of the inbound traffic: Each packet type is handled by an individual rate-limiting, that means, echo requests and each type of MRD packet are rate-limited individually, not summarily. This may lead to increased CPU load because individual counters have to be maintained per packet type. A more complex ruleset could redirect matched traffic to a different dedicated chain where one single rate-limiting was applied, but this had the unwelcome consequence that exceeding the ratelimit with one protocol would also cause other protocols to be dropped, for example, by exceeding ratelimit with echo requests, Multicast Router Discovery was shut down as well.

Stateful Matching Considerations

Many iptables-scripts include a line like

ip6tables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

For above rulechain to apply properly to ICMP echo requests, it is crucial that such a rule occurs later in the INPUT ruleset than the above filtering rules. The reason is that echo requests from the same source with an incrementing sequence numer are treated as „related“, and if we do not filter them out beforehand, the stateful inspection would accept them and they would never encounter the INPUT-ICMPv6 chain.

For a similar reason, the DROP rule on lines 7 and 8 exists. If such a stateful rule as outlined above that accepts RELATED packets occurs later in the INPUT ruleset, no matter if packets make it through the ratelimit or not, that rule would accept them. This is why we immediately drop everything that is of proper packet type but has not been accepted by the ratelimit.

One can of course use RELATED state matching for specific non-ICMP protocols only:

ip6tables -A INPUT -m state --state ESTABLISHED -j ACCEPT
ip6tables -A INPUT -p tcp -m state --state RELATED -j ACCEPT
ip6tables -A INPUT -p udp -m state --state RELATED -j ACCEPT
ip6tables -A INPUT -p icmpv6 -j INPUT-ICMPv6

Above example would accept all packets of established connections, packets that are part of TCP or UDP connections that relate to an established connection and apply the rules from the INPUT-ICMPv6 chain defined previously to ICMP packets.

Example: Outbound Traffic

Next, consider the following excerpt from the same script, where outbound traffic is managed:

ip6tables -N OUTPUT-ICMPv6
for icmpv6_type in 1 2 3 4 128 129 133 134 135 136 141 142 151 152 153; do
    ip6tables -A OUTPUT-ICMPv6 -p icmpv6 --icmpv6-type $icmpv6_type -j ACCEPT
ip6tables -A OUTPUT -p IPv6-icmp -j OUTPUT-ICMPv6
  • At lime 1 a new chain is declared that is applied to outbound ICMPv6 traffic in line 5.
  • At lines 2 to 4, packets of ICMPv6 types mentioned in RC 4890 Section 4.4.1 not including those implementing the SeND protocol are declared acceptable.

What Rate Limit To Use

Remains the question what an actual practical rate limit in terms of allowable packets per minute (or second) is.

In the example above, I limit each affected type to a maximum of 500 packets per minute with an allowable burst of 5, but does that value actually make sense?

Assume the following scenario:

An attacker wants to kick me off IRC and, by pinging my server, realises that I have a low ratelimit for ICMP echo reply. Now, everytime he wants to kick me off IRC all he has to do is exceed that ratelimit. IRC servers will attempt to ping me once per minute, but with the ratelimit exceeded, their request is not answered, and they have no choice but to assume the connection is dead. In a matter of one minute I am disconnected from all IRC servers.

Therefore I have to raise the ratelimit to a value where the attacker finds it increasingly difficult to estimate the actual ratelimit by mere experimentation. The attacker has to raise a full blown attack to trigger the vulnerability. The packet storm will be mitigated by ratelimit kicking in (temporarily taking down legitimate services alongside), but it also will not go unnoticed easily and can be answered by means of system administration or on precedent transit nodes.

I guess this is one of the reasons why admins are not very verbose about the rate limits they actually set.


Appendix: Table of ICMPv6 Packet Types

The following table lists

  • the possible decimal values of the type field of an ICMPv6 packet
  • if the packet type is considered by RFC 4890 as local configuration traffic that should or should not be dropped
  • a short description of the packet type
  • RFCs for futher reference.
Type # Blocked? Description Reference
1 SHOULD NOT Destination Unreachable RFC 4443
2 SHOULD NOT Packet Too Big RFC 4443
3 SHOULD NOT Time Exceeded RFC 4443
4 SHOULD NOT Parameter Problem RFC 4443
100 Other Private experimentation
101 Other Private experimentation
128 SHOULD NOT Echo Request RFC 4443
129 SHOULD NOT Echo Reply RFC 4443
130 Other Multicast Listener Query RFC 3810
131 Other Version 1 Multicast Listener Report RFC 2710
132 Other Multicast Listener Done RFC 2710
133 SHOULD NOT Router Solicitation RFC 4861
134 SHOULD NOT Router Advertisement RFC 4861
135 SHOULD NOT Neighbor Solicitation RFC 4861
136 SHOULD NOT Neighbor Advertisement RFC 4861
137 Other Redirect RFC 4861
138 Other Router Renumbering RFC 2894
139 SHOULD ICMP Node Information Query RFC 4620
140 SHOULD ICMP Node Information Response RFC 4620
141 SHOULD NOT Inverse Neighbor Discovery Solicitation Message RFC 3122
142 SHOULD NOT Inverse Neighbor Discovery Advertisement Message RFC 3122
143 Other Version 2 Multicast Listener Report RFC 3810
144 Other Home Agent Address Discovery Request Message RFC 3775
145 Other Home Agent Address Discovery Reply Message RFC 3775
146 Other Mobile Prefix Solicitation RFC 3775
147 Other Mobile Prefix Advertisement RFC 3775
148 Other Certification Path Solicitation Message RFC 3971
149 Other Certification Path Advertisement Message RFC 3971
150 Other Experimental mobility protocols (such as Seamoby) RFC 4065
151 Other Multicast Router Advertisement RFC 4286
152 Other Multicast Router Solicitation RFC 4286
153 Other Multicast Router Termination RFC 4286
200 Other Private experimentation
201 Other Private experimentation
255 Other Reserved for expansion