Page MenuHomeVyOS Platform

vrf_zones blocking ipv6 traffic
Confirmed, HighPublicBUG

Description

Left a comment on another closed ticket: https://vyos.dev/T3655#178710 which seemed entirely relevant but @Viacheslav left feedback indicating that it is an unspecified problem that is unrelated, feel free to change the title of this bug report to better address the problem if not accurate. Here is a demonstration of the problem:

table inet vrf_zones {
        map ct_iface_map {
                typeof iifname : ct zone
                elements = { "HE" : 132,
                             "WAN" : 128,
                             "eth0" : 128,
                             "tun0" : 132,
                             "eth1" : 256,
                             "eth2" : 384,
                             "veth0" : 132,
                             "veth1" : 256,
                             "VMNET" : 256,
                             "FASTNETMON" : 384 }
        }

        chain vrf_zones_ct_in {
                type filter hook prerouting priority raw; policy accept;
                counter packets 37682 bytes 9857007 ct original zone set iifname map @ct_iface_map
        }

        chain vrf_zones_ct_out {
                type filter hook output priority raw; policy accept;
                counter packets 10822 bytes 1502078 ct original zone set oifname map @ct_iface_map
        }
}

this table doesn't work for IPv6:

vyos@vyos:~$ sudo ip vrf exec VMNET ping 198.18.5.0
PING 198.18.5.0 (198.18.5.0) 56(84) bytes of data.
64 bytes from 198.18.5.0: icmp_seq=1 ttl=64 time=0.070 ms
^C
--- 198.18.5.0 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.070/0.070/0.070/0.000 ms
vyos@vyos:~$ sudo ip vrf exec VMNET ping6 2001:470:1f15:1ed:1::1
PING 2001:470:1f15:1ed:1::1(2001:470:1f15:1ed:1::1) 56 data bytes
^C
--- 2001:470:1f15:1ed:1::1 ping statistics ---
2 packets transmitted, 0 received, 100% packet loss, time 1039ms

deleting the table fixes it:

vyos@vyos:~$ sudo nft delete table inet vrf_zones
vyos@vyos:~$ sudo ip vrf exec VMNET ping6 2001:470:1f15:1ed:1::1
PING 2001:470:1f15:1ed:1::1(2001:470:1f15:1ed:1::1) 56 data bytes
64 bytes from 2001:470:1f15:1ed:1::1: icmp_seq=1 ttl=64 time=0.063 ms
64 bytes from 2001:470:1f15:1ed:1::1: icmp_seq=2 ttl=64 time=0.051 ms
^C
--- 2001:470:1f15:1ed:1::1 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1014ms
rtt min/avg/max/mdev = 0.051/0.057/0.063/0.006 ms

I'm at a bit of a loss for how to "fix" this without having to delete the table, maybe the point here arises from the fact that I'm simply connecting two vrfs together with a veth pair:

set interfaces virtual-ethernet veth0 peer-name veth1
set interfaces virtual-ethernet veth1 peer-name veth0
set interfaces virtual-ethernet veth0 address 2001:470:1f15:1ed:1::1/80
set interfaces virtual-ethernet veth1 address 2001:470:1f15:1ed:1::2/80
set interfaces virtual-ethernet veth0 address 198.18.5.0/23
set interfaces virtual-ethernet veth1 address 198.18.4.1/23
set interfaces virtual-ethernet veth0 vrf HE
set interfaces virtual-ethernet veth1 vrf VMNET
vyos@vyos# sudo ip vrf exec VMNET ping6 2001:470:1f15:1ed:1::1
PING 2001:470:1f15:1ed:1::1(2001:470:1f15:1ed:1::1) 56 data bytes
^C
--- 2001:470:1f15:1ed:1::1 ping statistics ---
2 packets transmitted, 0 received, 100% packet loss, time 1033ms

[edit]
vyos@vyos# sudo nft delete table inet vrf_zones
[edit]
vyos@vyos# sudo ip vrf exec VMNET ping6 2001:470:1f15:1ed:1::1
PING 2001:470:1f15:1ed:1::1(2001:470:1f15:1ed:1::1) 56 data bytes
64 bytes from 2001:470:1f15:1ed:1::1: icmp_seq=1 ttl=64 time=0.063 ms
64 bytes from 2001:470:1f15:1ed:1::1: icmp_seq=2 ttl=64 time=0.040 ms
^C
--- 2001:470:1f15:1ed:1::1 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1053ms
rtt min/avg/max/mdev = 0.040/0.051/0.063/0.011 ms
[edit]
vyos@vyos#

Details

Version
1.5-rolling-202402270022
Is it a breaking change?
Perfectly compatible
Issue type
Bug (incorrect behavior)

Event Timeline

n.fort changed the task status from Open to Confirmed.Mar 6 2024, 1:32 PM

@paigeadelethompson I've noticed that issue some weeks ago too. @n.fort is already trying to fix it - so VyOS team is working on it.

c-po triaged this task as High priority.
c-po edited a custom field.

Something I just figured out is that the minute I do:

set nat source rule 100 outbound-interface eth0
set nat source rule 100 translation address masquerade

ipv6 stops working, and deleting the nat rule then commit doesn't fix it. After adding masquerade, a rule is added to two chains that were not there before:

chain vrf_zones_ct_in {
              type filter hook prerouting priority raw; policy accept;
              counter packets 288 bytes 67582 ct original zone set iifname map @ct_iface_map
      }

      chain vrf_zones_ct_out {
              type filter hook output priority raw; policy accept;
              counter packets 92 bytes 9056 ct original zone set oifname map @ct_iface_map
      }

and when you delete nat and commit, those two rules are still there

It only seems to affect IPv6 though and I'm not really sure why because the rules to seem to be matching ipv6:

/sbin/conntrack -f ipv6 -L
tcp      6 111 SYN_SENT src=fc01::b dst=fc01::a sport=54876 dport=179 zone-orig=102 [UNREPLIED] src=fc01::a dst=fc01::b sport=179 dport=54876 mark=0 use=1
tcp      6 110 SYN_SENT src=fc00::a dst=fc00::b sport=36484 dport=179 zone-orig=101 [UNREPLIED] src=fc00::b dst=fc00::a sport=179 dport=36484 mark=0 use=1
tcp      6 110 SYN_SENT src=fc01::a dst=fc01::b sport=43862 dport=179 zone-orig=101 [UNREPLIED] src=fc01::b dst=fc01::a sport=179 dport=43862 mark=0 use=1
tcp      6 111 SYN_SENT src=fc00::b dst=fc00::a sport=42132 dport=179 zone-orig=100 [UNREPLIED] src=fc00::a dst=fc00::b sport=179 dport=42132 mark=0 use=1
udp      17 29 src=fe80::52eb:1aff:fe77:e5ff dst=ff02::2 sport=8888 dport=8888 zone-orig=100 [UNREPLIED] src=ff02::2 dst=fe80::52eb:1aff:fe77:e5ff sport=8888 dport=8888 mark=0 use=1

diff before / after (a -> b):

63a64
>               counter ct original zone set iifname map @ct_iface_map
67a69,98
>               counter ct original zone set oifname map @ct_iface_map
>       }
> }
> table ip vyos_nat {
>       chain PREROUTING {
>               type nat hook prerouting priority dstnat; policy accept;
>               counter jump VYOS_PRE_DNAT_HOOK
>       }
> 
>       chain POSTROUTING {
>               type nat hook postrouting priority srcnat; policy accept;
>               counter jump VYOS_PRE_SNAT_HOOK
>               oifname "eth0" counter masquerade comment "SRC-NAT-100"
>       }
> 
>       chain VYOS_PRE_DNAT_HOOK {
>               return
>       }
> 
>       chain VYOS_PRE_SNAT_HOOK {
>               return
>       }
> }
> table ip vyos_static_nat {
>       chain PREROUTING {
>               type nat hook prerouting priority dstnat; policy accept;
>       }
> 
>       chain POSTROUTING {
>               type nat hook postrouting priority srcnat; policy accept;
138a170,174
>       chain PREROUTING_HELPER {
>               type filter hook prerouting priority filter - 5; policy accept;
>               counter jump VYOS_CT_HELPER
>       }
> 
147a184,188
>       chain OUTPUT_HELPER {
>               type filter hook output priority filter - 5; policy accept;
>               counter jump VYOS_CT_HELPER
>       }
> 
167c208
<               return
---
>               accept

Then after deleting nat and commiting (b -> c)

< table ip vyos_nat {
<       chain PREROUTING {
<               type nat hook prerouting priority dstnat; policy accept;
<               counter jump VYOS_PRE_DNAT_HOOK
<       }
< 
<       chain POSTROUTING {
<               type nat hook postrouting priority srcnat; policy accept;
<               counter jump VYOS_PRE_SNAT_HOOK
<               oifname "eth0" counter masquerade comment "SRC-NAT-100"
<       }
< 
<       chain VYOS_PRE_DNAT_HOOK {
<               return
<       }
< 
<       chain VYOS_PRE_SNAT_HOOK {
<               return
<       }
< }
< table ip vyos_static_nat {
<       chain PREROUTING {
<               type nat hook prerouting priority dstnat; policy accept;
<       }
< 
<       chain POSTROUTING {
<               type nat hook postrouting priority srcnat; policy accept;
<       }
< }

diff between A and C:

63a64
>               counter ct original zone set iifname map @ct_iface_map
67a69
>               counter ct original zone set oifname map @ct_iface_map
138a141,145
>       chain PREROUTING_HELPER {
>               type filter hook prerouting priority filter - 5; policy accept;
>               counter jump VYOS_CT_HELPER
>       }
> 
147a155,159
>       chain OUTPUT_HELPER {
>               type filter hook output priority filter - 5; policy accept;
>               counter jump VYOS_CT_HELPER
>       }
> 
167c179
<               return
---
>               accept

In my experience working with ct zone, and admittedly my experience could be totally one-off, but if you start tagging everything with a ct zone you have to specify the ct zone to match later otherwise it defaults to 0 (which is the default ct zone) and it will never get matched, so like in VYOS_CT_HELPER where you have

tcp dport 21 ct helper set "ftp_tcp" return

you would need to specify

tcp dport 21 ct zone 100 ct helper set "ftp_tcp" return

another example

add    rule    inet filter input                    ct zone 19006 ct state established                        accept
add    rule    inet filter output                   ct zone 19006 ct state new                                accept
add    rule    inet filter output                   ct zone 19006 ct state established                        accept

I could not get it to work any other way, unless you just don't use ct to match otherwise it always defaults to the default zone. Also I could not specify ct zone id in concatenations because it would not serialize / de-serialize correctly. I imagine it is possible but it was really obvious (segfaults, incorrect values) that it needs to be better defined in nftables / associated libraries at least to be used that way if not in general, there's very little documentation on the subject of ct zones around. I wanted to understand the philosophy behind original and reply zone but I couldn't find any information about it.

side note, if you flush ruleset, and only add:

table inet vrf_zones {
        map ct_iface_map {
                typeof iifname : ct zone
                elements = { "VM" : 102,
                             "eth0" : 100,
                             "eth1" : 102,
                             "CORE" : 101,
                             "veth0" : 101,
                             "veth1" : 100,
                             "veth2" : 101,
                             "veth3" : 102,
                             "INTERNET" : 100 }
        }

        chain vrf_zones_ct_in {
                type filter hook prerouting priority raw; policy accept;
                counter packets 800 bytes 233466 ct original zone set iifname map @ct_iface_map
        }

        chain vrf_zones_ct_out {
                type filter hook output priority raw; policy accept;
                counter packets 164 bytes 21824 ct original zone set oifname map @ct_iface_map
        }
}

in theory this would be inconsequential by itself but it actually does block IPv6 traffic by itself, I feel like this shouldn't be the case given that the default behavior without any chains should be to pass / accept, clearly it's not though and I think that's because ct zone is kinda just hanging and doing its own thing without explicitly telling it exactly what to do for every zone. Unless it has something to do with that reply zone that (as near as I can tell is being left unused) I'd say its perhaps a bit broken? Not really sure..

I decided to dig into this a little more and try to trace this out:

sudo nft add chain inet vrf_zones trace_chain { type filter hook prerouting priority -301\; }
sudo nft add rule inet vrf_zones trace_chain meta nftrace set 1
trace id 72ad1891 inet vrf_zones trace_chain packet: iif "veth2" ether saddr 2a:35:be:9a:30:2c ether daddr 06:c9:2e:2e:44:e8 ip6 saddr fc01::b ip6 daddr fc01::a ip6 dscp cs0 ip6 ecn not-ect ip6 hoplimit 64 ip6 flowlabel 191398 ip6 nexthdr ipv6-icmp ip6 length 64 icmpv6 type echo-request icmpv6 code no-route icmpv6 parameter-problem 3452502291 icmpv6 taddr ::a4d1:1e66:0:0:a606:800 
trace id 72ad1891 inet vrf_zones vrf_zones_ct_in packet: iif "veth2" ether saddr 2a:35:be:9a:30:2c ether daddr 06:c9:2e:2e:44:e8 ip6 saddr fc01::b ip6 daddr fc01::a ip6 dscp cs0 ip6 ecn not-ect ip6 hoplimit 64 ip6 flowlabel 191398 ip6 nexthdr ipv6-icmp ip6 length 64 icmpv6 type echo-request icmpv6 code no-route icmpv6 parameter-problem 3452502291 icmpv6 taddr ::a4d1:1e66:0:0:a606:800 
trace id ace0e54e inet vrf_zones trace_chain packet: iif "CORE" ether saddr 2a:35:be:9a:30:2c ether daddr 06:c9:2e:2e:44:e8 ip6 saddr fc01::b ip6 daddr fc01::a ip6 dscp cs0 ip6 ecn not-ect ip6 hoplimit 64 ip6 flowlabel 191398 ip6 nexthdr ipv6-icmp ip6 length 64 icmpv6 type echo-request icmpv6 code no-route icmpv6 parameter-problem 3452502291 icmpv6 taddr ::a4d1:1e66:0:0:a606:800 
trace id ace0e54e inet vrf_zones vrf_zones_ct_in packet: iif "CORE" ether saddr 2a:35:be:9a:30:2c ether daddr 06:c9:2e:2e:44:e8 ip6 saddr fc01::b ip6 daddr fc01::a ip6 dscp cs0 ip6 ecn not-ect ip6 hoplimit 64 ip6 flowlabel 191398 ip6 nexthdr ipv6-icmp ip6 length 64 icmpv6 type echo-request icmpv6 code no-route icmpv6 parameter-problem 3452502291 icmpv6 taddr ::a4d1:1e66:0:0:a606:800

Unfortunately doesn't help me understand why it was not causing this before applying vrf_zones_ct_in and vrf_zones_ct_out

dmbaturin changed Is it a breaking change? from Unspecified (possibly destroys the router) to Perfectly compatible.

Hi! I think I am hitting a similar/the same bug with a very minimal configuration.

I am connecting the default routing instance with a VRF with a veth-pair.
As soon as I enable a firewall rule that uses the conntrack state ping is no longer working between both ends of the veth pair.

Everything is running in a VM without any other configuration:

vyos@vyos:~$ show version 
Version:          VyOS 2025.07.21-0022-rolling
Release train:    current
Release flavor:   generic

Built by:         autobuild@vyos.net
Built on:         Mon 21 Jul 2025 00:22 UTC
Build UUID:       29e3bf4f-6107-431b-9ed6-4756c08e3af4
Build commit ID:  d59967adf99d46

Architecture:     x86_64
Boot via:         installed image
System type:      KVM guest
Secure Boot:      n/a (BIOS)

Hardware vendor:  QEMU
Hardware model:   Standard PC (Q35 + ICH9, 2009)
Hardware S/N:     
Hardware UUID:    36da81d4-d431-4dc4-b3c2-2e6b4a9e8453

Copyright:        VyOS maintainers and contributors

Here is my starting config. It's basically jsut a veth-pair, a VRF and a pair of IP-Adresses. everything else is just the default config.

vyos@vyos# show
 firewall {
     ipv6 {
         input {
             filter {
                 default-action accept
             }
         }
     }
 }
 interfaces {
     ethernet eth0 {
         hw-id 52:54:00:7c:ec:d9
         offload {
             gro
             gso
             sg
             tso
         }
     }
     loopback lo {
     }
     virtual-ethernet veth0 {
         address 2001:db8::/127
         peer-name veth1
     }
     virtual-ethernet veth1 {
         address 2001:db8::1/127
         peer-name veth0
         vrf red
     }
 }
 service {
     ntp {
         allow-client {
             address 127.0.0.0/8
             address 169.254.0.0/16
             address 10.0.0.0/8
             address 172.16.0.0/12
             address 192.168.0.0/16
             address ::1/128
             address fe80::/10
             address fc00::/7
         }
         server time1.vyos.net {
         }
         server time2.vyos.net {
         }
         server time3.vyos.net {
         }
     }
 }
 system {
     config-management {
         commit-revisions 100
     }
     console {
         device ttyS0 {
             speed 115200
         }
     }
     host-name vyos
     login {
         user vyos {
             authentication {
                 encrypted-password $6$rounds=656000$TTe2VWJ4hSJDw8kf$1A/RxxEM0tjtZBkzRWntp7hvdKlCEAcyuJNQynm.DJNHxOid698naUz.uX9BW/rg5y0JUn.B3jocYMSbxiIV4.
                 plaintext-password ""
             }
         }
     }
     option {
         reboot-on-upgrade-failure 5
     }
     syslog {
         local {
             facility all {
                 level info
             }
             facility local7 {
                 level debug
             }
         }
     }
 }
 vrf {
     name red {
         table 1000
     }
 }

ping works as inteneded with this configuration:

vyos@vyos# run ping 2001:db8::1
PING 2001:db8::1(2001:db8::1) 56 data bytes
64 bytes from 2001:db8::1: icmp_seq=1 ttl=64 time=0.029 ms
64 bytes from 2001:db8::1: icmp_seq=2 ttl=64 time=0.083 ms
64 bytes from 2001:db8::1: icmp_seq=3 ttl=64 time=0.074 ms
64 bytes from 2001:db8::1: icmp_seq=4 ttl=64 time=0.068 ms
^C
--- 2001:db8::1 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3106ms
rtt min/avg/max/mdev = 0.029/0.063/0.083/0.020 ms
[edit]
vyos@vyos#

As soon as I add a firewall rule that uses the conntrack established state the ping no longer works. Even when the action is accept everywhere in the chain!

vyos@vyos# set firewall ipv6 input filter rule 10 action accept 
[edit]
vyos@vyos# set firewall ipv6 input filter rule 10 state established 
[edit]

The resulting firewall rule is:

vyos@vyos# show firewall 
 ipv6 {
     input {
         filter {
             default-action accept
             rule 10 {
                 action accept
                 state established
             }
         }
     }
 }

But now no packets get through:

vyos@vyos# run ping 2001:db8::1
PING 2001:db8::1(2001:db8::1) 56 data bytes


^C
--- 2001:db8::1 ping statistics ---
12 packets transmitted, 0 received, 100% packet loss, time 11248ms

The underlying nft table looks like this:

vyos@vyos# sudo nft list table ip6 vyos_filter
table ip6 vyos_filter {
	chain VYOS_IPV6_FORWARD_filter {
		type filter hook forward priority filter; policy accept;
		counter packets 0 bytes 0 accept comment "FWD-filter default-action accept"
	}

	chain VYOS_IPV6_INPUT_filter {
		type filter hook input priority filter; policy accept;
		ct state established counter packets 0 bytes 0 accept comment "ipv6-INP-filter-10"
		counter packets 19 bytes 1712 accept comment "INP-filter default-action accept"
	}

	chain VYOS_IPV6_OUTPUT_raw {
		type filter hook output priority raw; policy accept;
		counter packets 18 bytes 1656 accept comment "OUT-raw default-action accept"
	}

	chain VYOS_IPV6_OUTPUT_filter {
		type filter hook output priority filter; policy accept;
		counter packets 18 bytes 1656 accept comment "OUT-filter default-action accept"
	}

	chain VYOS_IPV6_PREROUTING_raw {
		type filter hook prerouting priority raw; policy accept;
		counter packets 34 bytes 3168 accept comment "PRE-raw default-action accept"
	}

	chain VYOS_FRAG6_MARK {
		type filter hook prerouting priority -450; policy accept;
		exthdr frag exists meta mark set 0x000ffff1 return
	}
}

After deleting the state match it works again as expected:

[edit firewall ipv6 input]
vyos@vyos# delete filter rule 10 state established 
[edit firewall ipv6 input]
vyos@vyos# commit
[edit firewall ipv6 input]
vyos@vyos# show
 filter {
     default-action accept
     rule 10 {
         action accept
     }
 }

Now ping is working again:

vyos@vyos# run ping 2001:db8::1
PING 2001:db8::1(2001:db8::1) 56 data bytes
64 bytes from 2001:db8::1: icmp_seq=1 ttl=64 time=0.031 ms
64 bytes from 2001:db8::1: icmp_seq=2 ttl=64 time=0.074 ms
64 bytes from 2001:db8::1: icmp_seq=3 ttl=64 time=0.079 ms
64 bytes from 2001:db8::1: icmp_seq=4 ttl=64 time=0.084 ms
64 bytes from 2001:db8::1: icmp_seq=5 ttl=64 time=0.073 ms
^C
--- 2001:db8::1 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4084ms
rtt min/avg/max/mdev = 0.031/0.068/0.084/0.019 ms

When monitoring the traffic on the veth interfaces while the state matching is in the rule, the echo requests get sent on veth0 and are received on veth1. However there is no response to be seen on veth1.

Here on veth0

vyos@vyos:~$ monitor traffic interface veth0
1, length 64
13:31:47.055367 IP6 2001:db8:: > 2001:db8::1: ICMP6, echo request, id 9778, seq 2, length 64
13:31:48.079047 IP6 2001:db8:: > 2001:db8::1: ICMP6, echo request, id 9778, seq 3, length 64
13:31:49.103324 IP6 2001:db8:: > 2001:db8::1: ICMP6, echo request, id 9778, seq 4, length 64
13:31:50.127312 IP6 2001:db8:: > 2001:db8::1: ICMP6, echo request, id 9778, seq 5, length 64
13:31:51.151355 IP6 2001:db8:: > 2001:db8::1: ICMP6, echo request, id 9778, seq 6, length 64
13:31:51.343302 IP6 fe80::400a:ff:fe45:b73c > 2001:db8::1: ICMP6, neighbor solicitation, who has 2001:db8::1, length 32
13:31:51.343349 IP6 2001:db8::1 > fe80::400a:ff:fe45:b73c: ICMP6, neighbor advertisement, tgt is 2001:db8::1, length 24
13:31:52.175299 IP6 2001:db8:: > 2001:db8::1: ICMP6, echo request, id 9778, seq 7, length 64
13:31:53.199371 IP6 2001:db8:: > 2001:db8::1: ICMP6, echo request, id 9778, seq 8, length 64
13:31:54.223321 IP6 2001:db8:: > 2001:db8::1: ICMP6, echo request, id 9778, seq 9, length 64

and the other end on veth1:

vyos@vyos:~$ monitor traffic interface veth1 
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on veth1, link-type EN10MB (Ethernet), snapshot length 262144 bytes
13:36:55.342944 IP6 2001:db8:: > 2001:db8::1: ICMP6, echo request, id 9904, seq 
                                                                                6, length 64
13:36:55.471281 IP6 fe80::400a:ff:fe45:b73c > 2001:db8::1: ICMP6, neighbor solicitation, who has 2001:db8::1, length 32
13:36:55.471344 IP6 2001:db8::1 > fe80::400a:ff:fe45:b73c: ICMP6, neighbor advertisement, tgt is 2001:db8::1, length 24
13:36:56.367341 IP6 2001:db8:: > 2001:db8::1: ICMP6, echo request, id 9904, seq 7, length 64
13:36:57.391334 IP6 2001:db8:: > 2001:db8::1: ICMP6, echo request, id 9904, seq 8, length 64
13:36:58.415343 IP6 2001:db8:: > 2001:db8::1: ICMP6, echo request, id 9904, seq 9, length 64
13:36:59.439296 IP6 2001:db8:: > 2001:db8::1: ICMP6, echo request, id 9904, seq 10, length 64
13:37:00.463293 IP6 2001:db8:: > 2001:db8::1: ICMP6, echo request, id 9904, seq 11, length 64
13:37:00.590925 IP6 fe80::7035:73ff:fe91:590b > fe80::400a:ff:fe45:b73c: ICMP6, neighbor solicitation, who has fe80::400a:ff:fe45:b73c, length 32
13:37:00.590992 IP6 fe80::400a:ff:fe45:b73c > fe80::7035:73ff:fe91:590b: ICMP6, neighbor advertisement, tgt is fe80::400a:ff:fe45:b73c, length 24
13:37:01.487331 IP6 2001:db8:: > 2001:db8::1: ICMP6, echo request, id 9904, seq 12, length 64
13:37:02.511381 IP6 2001:db8:: > 2001:db8::1: ICMP6, echo request, id 9904, seq 13, length 64

I forgot to mention, that deleting the vrf_zones table "fixes" the issue.

Also I realized, that the insert_failed and drop counters of conntrack are increasing:

root@vyos:~# conntrack -S
cpu=0           found=0 invalid=0 insert=0 insert_failed=3140 drop=3140 early_drop=0 error=0 search_restart=0 clash_resolve=0 chaintoolong=0 
cpu=1           found=0 invalid=0 insert=0 insert_failed=33818 drop=33818 early_drop=0 error=0 search_restart=0 clash_resolve=0 chaintoolong=0 
root@vyos:~# ping 2001:db8::1
PING 2001:db8::1(2001:db8::1) 56 data bytes
^C
--- 2001:db8::1 ping statistics ---
5 packets transmitted, 0 received, 100% packet loss, time 4119ms

root@vyos:~# conntrack -S
cpu=0           found=0 invalid=0 insert=0 insert_failed=3145 drop=3145 early_drop=0 error=0 search_restart=0 clash_resolve=0 chaintoolong=0 
cpu=1           found=0 invalid=0 insert=0 insert_failed=33830 drop=33830 early_drop=0 error=0 search_restart=0 clash_resolve=0 chaintoolong=0

-> they increased on cpu 0 exactly by the 5 ICMP packets that were sent.
(cpu 1 is increasing because there was an on other ping running in the background as well.)
However I have no idea what insert_failed means and I was unable to find any documentation on it.