1

I have an issue with my firewall configuration. I would like to block all IPv6 fragmented packets in a sopecific interface. So, I tried:

# ip6tables -A INPUT -d <ipv6_address> -i eth0 -m ipv6header --header ipv6-frag --soft -j DROP

But IPv6 fragmented traffic keeps going in.

I have also found this link: https://www.secfu.net/2015/03/25/how-to-block-incoming-ipv6-fragments-in-latest-red-hat-releases/ that explains that ipv6-frag or frag do not block the ipv6 fragmented traffic in the latest CentOS, because after bug id 1011214 (kernel-2.6.32-437.el6) netfilter started processing the reassembled packet instead of the fragments, like IPv4 does. The same article suggests to use nftables which would look like as follows:

table ip6 filter {
   chain preroute500 {
      type filter hook prerouting priority -500; policy accept;
      ip6 nexthdr ipv6-frag counter packets 2 bytes 2104
   }
 } 

How can I do something like that using ip6tables?

# cat /etc/system-release
CentOS Linux release 7.5.1804 (Core)

# uname -r
5.0.8-1.el7.elrepo.x86_64
belabrinel
  • 125
  • 1
  • 6
  • 1
    You can't do it in ip6tables, as the document you linked to explained. Why do you want to do this at all? Perhaps there is some alternate way to accomplish [your real goal](https://meta.stackexchange.com/q/66377/189912). – Michael Hampton Apr 22 '19 at 16:40
  • My real goal is to test the firewall rule with protocol ipv6-frag|frag|44 protocol. I have created with scapy some fragmented IPv6 packets, however no matter which rule I add, they are not dropped. – belabrinel Apr 23 '19 at 10:45

1 Answers1

1

As has been said, you should be sure that blocking fragments is really your goal, and not a method you think is best for your goal, while better methods would exist. Anyway, for what it's worth you must:

  • use a recent enough kernel. The feature was added in kernel 4.16. CentOS uses 3.10. While many features are backported (starting with nftables which isn't available on a vanilla 3.10 kernel but is on RHEL/CentOS's 3.10), this feature wasn't backported as of latest CentOS 7.6's kernel. Since you're using an "elrepo" 5.0.8 kernel, the feature is present. Here's the feature description and why it exists (mostly for routing, or at least non-stateful/non-NAT firewall routing situations):

    netfilter: nf_defrag:

    Skip defrag if NOTRACK is set conntrack defrag is needed only if some module like CONNTRACK or NAT explicitly requests it. For plain forwarding scenarios, defrag is not needed and can be skipped if NOTRACK is set in a rule.

    Since conntrack defrag is currently higher priority than raw table, setting NOTRACK is not sufficient. We need to move raw to a higher priority for iptables only.

    This is achieved by introducing a module parameter "raw_before_defrag" which allows to change the priority of raw table to place it before defrag. By default, the parameter is disabled and the priority of raw table is NF_IP_PRI_RAW to support legacy behavior. If the module parameter is enabled, then the priority of the raw table is set to NF_IP_PRI_RAW_BEFORE_DEFRAG.

  • (if needed remove existing raw ip6tables rules, unload and) load the ip6table_raw module with the parameter raw_before_defrag=1, so the raw's hook (in PREROUTING) switchs from prio -300 to -450, before defrag's -400.

    modprobe ip6table_raw raw_before_defrag=1
    

    which should get a kernel message like ip6table_raw: Enabling raw table before defrag

Now in the raw table, and probably making sense only in PREROUTING, ip6tables will be able to see fragments. It can filter them there, or choose to skip conntrack (iptables -t raw -A PREROUTING ... -j CT --notrack), which as explained in the feature patch, will skip defragmentation too, allowing other chains to handle them (of course not including nat table's chains nor any other conntrack related feature).


Note that to witness the problem described in a test network namespace, one must first artificially enable conntrack operations for conntrack's dependency with defrag, which isn't activated until first needed. Here's a network namespace example:

ip netns add sender
ip netns add receiver
ip -n sender   link add veth0 address 02:00:00:00:00:01 type veth peer netns receiver name veth0 address 02:00:00:00:00:02
ip -n sender   link set veth0 up
ip -n receiver link set veth0 up

This works:

# ip netns exec sender ping6 -s 4000 fe80::ff:fe00:2%veth0
PING fe80::ff:fe00:2%veth0(fe80::ff:fe00:2%veth0) 4000 data bytes
4008 bytes from fe80::ff:fe00:2%veth0: icmp_seq=1 ttl=64 time=0.069 ms
^C

After this, it won't work anymore:

ip netns exec receiver ip6tables -A INPUT -m ipv6header --header ipv6-frag --soft -j DROP

Once conntrack is activated in the network namespace with those commands apparently resulting in a no-op:

ip netns exec receiver ip6tables -A INPUT -m conntrack --ctstate ESTABLISHED
ip netns exec receiver ip6tables -D INPUT -m conntrack --ctstate ESTABLISHED

the ipv6 defragmentation is also activated. Now the previous ping will always work, because the rule in INPUT only sees defragmented packets.

With the previously (re)loaded ip6table_raw module with parameter raw_before_defrag=1, this will restore fragmented ping's blocking:

ip netns exec receiver ip6tables -t raw -A PREROUTING -m ipv6header --header ipv6-frag --soft -j DROP

Alternatively, interesting packets can be marked as notrack, thus exempting them of conntrack, thus not receiving defrag treatement either, allowing them to be filtered again with the rule in the filter/INPUT chain. So instead of the previous raw rule, this would also block the ping packets (3 of them per ping), but this time again in the filter/INPUT chain:

ip netns exec receiver ip6tables -t raw -A PREROUTING -m ipv6header --header ipv6-frag --soft -j CT --notrack

This is an example only which doesn't make much sense.

A.B
  • 9,037
  • 2
  • 19
  • 37
  • added that a packet, including fragments, can also be marked as *notrack* for further (non stateful) processing after the raw table. – A.B Apr 23 '19 at 18:17