Bug 1105821

Summary: firewalld breaks ipv6 link-local communication due to blocked neighbor-advert packets
Product: [openSUSE] openSUSE Tumbleweed Reporter: Jiri Slaby <jslaby>
Component: NetworkAssignee: Markos Chandras <mchandras>
Status: RESOLVED FIXED QA Contact: E-mail List <qa-bugs>
Severity: Normal    
Priority: P5 - None CC: matthias.gerstner, meissner, mkubecek
Version: Current   
Target Milestone: ---   
Hardware: Other   
OS: Other   
Whiteboard:
Found By: --- Services Priority:
Business Priority: Blocker: ---
Marketing QA Status: --- IT Deployment: ---
Attachments: nft list ruleset
new ruleset

Description Jiri Slaby 2018-08-23 18:00:43 UTC
Created attachment 780614 [details]
nft list ruleset

It seems that after firewalld switched to nft, icmpv6 is completely dropped:
ping6 fe80::4748:4aeb:6d22:254d%br-lan
PING fe80::4748:4aeb:6d22:254d%br-lan (fe80::4748:4aeb:6d22:254d%br-lan): 56 data bytes
^C
--- fe80::4748:4aeb:6d22:254d%br-lan ping statistics ---
2 packets transmitted, 0 packets received, 100% packet loss


I checked both icmp and ipv6-icmp in the firewalld GUI and I am attaching the resulting nft ruleset.
Comment 1 Jiri Slaby 2018-08-23 18:04:26 UTC
I am reporting this as I lost IPv6 due to dropped router advertisements.

This:
# systemctl stop firewalld.service
makes indeed ping6 and ipv6 working. The former immediately, the latter after provoking router solicitation.
Comment 2 Jiri Slaby 2018-08-24 05:35:59 UTC
Of course, switching back to:
 FirewallBackend=iptables
in
 /etc/firewalld/firewalld.conf
makes it work again:
> Chain INPUT_direct (1 references)
>  pkts bytes target     prot opt in     out     source               destination         
>     9   664 ACCEPT     icmpv6    *      *       ::/0                 ::/0                

I wonder why I have ICMPv4 and IGMPv4 in ip6tables though:
> Chain IN_drop_allow (1 references)
>  pkts bytes target     prot opt in     out     source               destination         
> ...
>     0     0 ACCEPT     udp      *      *       ::/0                 ::/0                 udp dpt:1716 ctstate NEW
>     0     0 ACCEPT     icmp     *      *       ::/0                 ::/0                 ctstate NEW
>     0     0 ACCEPT     2        *      *       ::/0                 ::/0                 ctstate NEW

Isn't it bug 1099698 again?
Comment 3 Markos Chandras 2018-08-28 07:30:42 UTC
(In reply to Jiri Slaby from comment #2)
> I wonder why I have ICMPv4 and IGMPv4 in ip6tables though:
> > Chain IN_drop_allow (1 references)
> >  pkts bytes target     prot opt in     out     source               destination         
> > ...
> >     0     0 ACCEPT     udp      *      *       ::/0                 ::/0                 udp dpt:1716 ctstate NEW
> >     0     0 ACCEPT     icmp     *      *       ::/0                 ::/0                 ctstate NEW
> >     0     0 ACCEPT     2        *      *       ::/0                 ::/0                 ctstate NEW
> 
> Isn't it bug 1099698 again?

Actually, I think this behavior is probably intentional. if you select igmp,icmp and any other ipv4 protocol in the UI, then these will also be added to the ipv6 chains. This is because the backend does not know if this protocol is v4 or v6 so it adds it to both. What we fixed in #1099698 is the addition of ipv6-icmp so you can actually manage v6 icmp packets.

Let me investigate about the icmpv6 issue and I will provide a patch asap
Comment 4 Markos Chandras 2018-08-28 08:43:58 UTC
(In reply to Jiri Slaby from comment #2)
> Of course, switching back to:
>  FirewallBackend=iptables
> in
>  /etc/firewalld/firewalld.conf
> makes it work again:
> > Chain INPUT_direct (1 references)
> >  pkts bytes target     prot opt in     out     source               destination         
> >     9   664 ACCEPT     icmpv6    *      *       ::/0                 ::/0                
> 

This is also a bit weird. This is the INPUT_direct chain which is normally what you get when you have added direct rules yourself. Do you have such a thing?

Your nftables ruleset seems correct to me since i see that ipv6-icmp l4 is accepted in filter_IN_drop_allow

I think the reason you see that iptables work instead of nftables is because of the direct iptables rule you are using which is not present in nftables.
Comment 5 Jiri Slaby 2018-08-29 09:04:17 UTC
(In reply to Markos Chandras from comment #4)
> This is also a bit weird. This is the INPUT_direct chain which is normally
> what you get when you have added direct rules yourself. Do you have such a
> thing?
> 
> Your nftables ruleset seems correct to me since i see that ipv6-icmp l4 is
> accepted in filter_IN_drop_allow
> 
> I think the reason you see that iptables work instead of nftables is because
> of the direct iptables rule you are using which is not present in nftables.

Let me kill it, it was a workaround for the bug 1099698.
Comment 6 Jiri Slaby 2018-08-29 11:36:48 UTC
(In reply to Jiri Slaby from comment #5)
> Let me kill it, it was a workaround for the bug 1099698.

It did not help. And I reopened the bug as the fix was incomplete (did not help). And I believe that this bug is the same as the above one – exactly because the direct rule is not applied to nft, hence ipv6 ICMP traffic is dropped.
Comment 7 Jiri Slaby 2018-08-29 11:38:48 UTC
To support the last comment, adding ipv6-icmp without ctstate into nft helps here as well:
> # nft list chain inet firewalld filter_IN_drop_allow
> table inet firewalld {
>       chain filter_IN_drop_allow {
>               tcp dport ssh ct state new accept
>               ip6 daddr fe80::/64 udp dport dhcpv6-client ct state new accept
>               tcp dport 8200 ct state new accept
>               udp dport 1900 ct state new accept
>               udp dport netbios-ns ct state new accept
>               udp dport netbios-dgm ct state new accept
>               tcp dport netbios-ssn ct state new accept
>               tcp dport microsoft-ds ct state new accept
>               ip6 daddr ff02::fb udp dport mdns ct state new accept
>               udp dport tftp ct state new accept
>               tcp dport 60000-65000 ct state new accept
>               udp dport 60000-65000 ct state new accept
>               tcp dport 1716 ct state new accept
>               udp dport 1716 ct state new accept
>               meta l4proto icmp ct state new accept
>               meta l4proto igmp ct state new accept
>               meta l4proto ipv6-icmp ct state new counter packets 0 bytes 0 accept
>               meta l4proto ipv6-icmp counter packets 476 bytes 33272 accept
>       }
> }

You can see the counter is incremented in the last entry, but not in the last but one.
Comment 8 Markos Chandras 2018-08-29 12:18:09 UTC
iptables and nftables can co-exist. So if you have a direct iptables rule then it's still added to the iptables chain but I am not sure if this is the best way to achieve that.

Would it make sense to use a rich rule to achieve what you want?

firewall-cmd --add-rich-rule 'rule family=ipv6 protocol value=ipv6-icmp accept'

Seems like your interface is assigned to the 'drop' zone which is very restrictive by nature.

https://firewalld.org/documentation/man-pages/firewalld.zones.html

What you are effectively asking here is for firewalld to allow non-conntrack packages by default and I am not sure it would make sense for this to become a default option.

Please consider the rich-rule option instead.
Comment 9 Jiri Slaby 2018-08-29 12:45:37 UTC
I use drop zone, as unwanted traffic shall be dropped on good firewalls, not rejected.

So the public zone adds this to IN_public (by default):
>    0     0 ACCEPT     icmpv6    *      *       ::/0                 ::/0                

The drop zone adds this to IN_drop_allow (by adding icmpv6 to allowed protocols in the GUI):
>     0     0 ACCEPT     icmpv6    *      *       ::/0                 ::/0                 ctstate NEW

The latter makes no sense and won't work. If a user chooses icmpv6 to be added to their rules, firewalld should add a working rule, not a rule which actually accomplishes nothing.

If you are afraid of dropping ctstate, add at least "NEW,UNTRACKED", not only NEW (again most icmpv6 traffic is not tracked, so asking conntrack about its ctstate makes no sense).
Comment 10 Jiri Slaby 2018-08-29 12:48:28 UTC
(In reply to Markos Chandras from comment #8)
> firewall-cmd --add-rich-rule 'rule family=ipv6 protocol value=ipv6-icmp
> accept'

And this add guess what :):
> # ip6tables -L -vn|grep icmpv
>    0     0 ACCEPT     icmpv6    *      *       ::/0                 ::/0                 ctstate NEW
> # firewall-cmd --add-rich-rule 'rule family=ipv6 protocol value=ipv6-icmp accept'
> success
> # ip6tables -L -vn|grep icmpv
>    0     0 ACCEPT     icmpv6    *      *       ::/0                 ::/0                 ctstate NEW
>    0     0 ACCEPT     icmpv6    *      *       ::/0                 ::/0                 ctstate NEW


One more ctstate NEW (one added by icmp-ipv6 in GUI), another one from this rich rule.
Comment 11 Markos Chandras 2018-08-29 14:19:53 UTC
diff --git a/src/firewall/core/nftables.py b/src/firewall/core/nftables.py
index be2dfb5c..48d1479a 100644
--- a/src/firewall/core/nftables.py
+++ b/src/firewall/core/nftables.py
@@ -1130,7 +1130,7 @@ class nftables(object):
                           "oif", "missing", "log", "prefix", "\"rpfilter_DROP: \""])
         rules.append(["insert", "rule", "inet", "%s" % TABLE_NAME,
                       "raw_%s" % "PREROUTING",
-                      "icmpv6", "type", "{ nd-router-advert, nd-neighbor-solicit }",
+                      "icmpv6", "type", "{ nd-router-advert, nd-neighbor-advert, nd-neighbor-solicit }",
                       "accept"]) # RHBZ#1058505, RHBZ#1575431 (bug in kernel 4.16-4.17)
         return rules

Could you try this patch in /usr/lib/python3.6/site-packages/firewall/core/nftables.py

or simply add a direct rule to accept ipv6 nd-neighbor-advert ?

Thank you
Comment 12 Jiri Slaby 2018-08-29 15:05:29 UTC
Created attachment 781210 [details]
new ruleset
Comment 13 Markos Chandras 2018-08-29 15:13:29 UTC
I honestly don't understand why the ctstate established/related is not enough to capture subsequent icmpv6 packets once they pass the ctstate=new rule. Adding Michal in case he has more insight on the nftables behavior. It sounds like the packets being accepted by your additional 'meta l4proto ipv6-icmp counter' are untracked so we need to find out what kind of packets they are and why they are untracked.
Comment 14 Markos Chandras 2018-08-29 15:27:11 UTC
Looking at

https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/netfilter/nf_conntrack_proto_icmpv6.c

I see this

if (type >= 0 && type < sizeof(noct_valid_new) &&
	    noct_valid_new[type]) {
		nf_ct_set(skb, NULL, IP_CT_UNTRACKED);
		return NF_ACCEPT;
	}

noct_valid_new[type] returns true for all ND messages. So these messages are rightfully untracked. Do you know what are the packets that are being accepted by your "meta l4proto ipv6-icmp counter" rule?
Comment 15 Markos Chandras 2018-08-29 17:19:58 UTC
I have now opened https://github.com/firewalld/firewalld/pull/379 for consideration. Thank you for your patience and the debug information.
Comment 16 Markos Chandras 2018-09-06 14:50:52 UTC
This has been fixed in https://github.com/firewalld/firewalld/pull/380 and the fix should be available in the next release. If you need it right now as a backport please let me know or feel free to submit an SR yourself :) Thank you for the report and all the debug information.
Comment 17 Swamp Workflow Management 2018-09-17 15:20:06 UTC
This is an autogenerated message for OBS integration:
This bug (1105821) was mentioned in
https://build.opensuse.org/request/show/636196 Factory / firewalld
Comment 18 Swamp Workflow Management 2018-09-24 10:10:17 UTC
This is an autogenerated message for OBS integration:
This bug (1105821) was mentioned in
https://build.opensuse.org/request/show/637406 Factory / firewalld