PVE Firewall Dropping NDP NS?

lukeh990

New Member
Oct 8, 2024
9
1
3
Hello,
I am running a PVE cluster with 3 nodes. Each node has a statically configured IPv6 address. When I set it up, I was able to ping across the cluster to another node from the WebUI host console no problem. But today when I was doing maintenance, I noticed that I couldn't ping anything on the same subnet besides the router. Running tcpdumps, I found that the pinging host would generate a NS and send it to a multicast address. I would see that appear on the receiving hosts tcpdump then no response would be made. Now while doing those captures, I saw that from time to time NS requests would arrive with unicast destinations and would be processed and a response would go back out from the host.

Now, when turning off the DC firewall, everything would start working again. So obviously a firewall issue. Though during my debugging I had a rule that any ipv6-icmp on the in chain would be allowed. Though that doesn't seem to be honored. NDP on the host FW is enabled. Setting logging wherever I could, I couldn't see any reason why it would be blocking.

Another weird thing is that I have another PVE instance with all the same package versions and can't replicate the issue easily.

Any help in figuring this out would be appreciated.

cluster.fw
Code:
[OPTIONS]

enable: 1
policy_forward: ACCEPT

[IPSET hv-net]

<xx>.<x>.<x>.<x>/24
<xx>.<x>.<x>.<x>/24
<xx>.<x>.<x>.<x>/25
<xx>.<x>.<x>.<xxx>/25
<xx>.<x>.<x>.<x>/24
<xxxx>:<xxxx>:0:1::/64
<xxxx>:<xxxx>::/64

[IPSET rfc1918]

10.0.0.0/8
172.16.0.0/12
192.168.0.0/16
<xxxx>:<xxxx>::/40
<xxxx>:<xxxx>:<xxxx>:<xxxx>::/64
<xxxx>:<xxxx>:<xxxx>::/48

[RULES]

IN ACCEPT -p ipv6-icmp -log nolog
IN ACCEPT -p icmp -log nolog
IN ACCEPT -i vmbr0.25 -source +dc/hv-net -dest +dc/hv-net -p udp -dport 4789 -log nolog
IN BGP(ACCEPT) -i vmbr0.25 -source +dc/hv-net -dest +dc/hv-net -log nolog
IN ACCEPT -i vmbr0.25 -source +dc/hv-net -dest +dc/hv-net -p udp -dport 3784 -log nolog # BFD
IN ACCEPT -i vmbr0.11 -source +dc/hv-net -dest +dc/hv-net -p tcp -dport 60000:60050 -log nolog
IN SSH(ACCEPT) -i vmbr0.11 -source +dc/hv-net -dest +dc/hv-net -log nolog
IN Ceph(ACCEPT) -i vmbr0.12 -source +dc/hv-net -dest +dc/hv-net -log nolog
IN Ceph(ACCEPT) -i vmbr0.11 -source +dc/hv-net -dest +dc/hv-net -log nolog
IN SSH(ACCEPT) -i vmbr0.10 -log nolog
IN ACCEPT -i vmbr0.10 -source +dc/rfc1918 -dest +dc/hv-net -p tcp -dport 8006 -log nolog
IN ACCEPT -i mgmtbr0 -source +dc/rfc1918 -dest +dc/hv-net -p tcp -dport 8006 -log nolog

[group dns-srv]

IN DNS(ACCEPT) -source +dc/rfc1918 -log nolog

[group mysql-srv]

IN MySQL(ACCEPT) -source +dc/rfc1918 -log nolog

[group pg-srv]

IN PostgreSQL(ACCEPT) -source +dc/rfc1918 -log nolog

[group ping]

IN ACCEPT -p ipv6-icmp -log nolog
IN ACCEPT -p icmp -log nolog

[group ssh-srv]

IN SSH(ACCEPT) -source +dc/rfc1918 -log nolog

[group web-srv]

IN HTTPS(ACCEPT) -source +dc/rfc1918 -log nolog
IN HTTP(ACCEPT) -source +dc/rfc1918 -log nolog

host.fw
Code:
[OPTIONS]

log_level_in: nolog
ndp: 1
nftables: 1

pveversion -v
Code:
proxmox-ve: 9.2.0 (running kernel: 7.0.14-17-pve)
pve-manager: 9.2.20 (running version: 9.2.20/49318c671b82f31e)
proxmox-kernel-helper: 9.2.0
proxmox-kernel-7.0.14-17-pve-signed: 7.0.14-17
proxmox-kernel-7.0: 7.0.14-17
proxmox-kernel-7.0.14-16-pve-signed: 7.0.14-16
proxmox-kernel-6.17: 6.17.13-21
proxmox-kernel-6.17.13-21-pve-signed: 6.17.13-21
proxmox-kernel-6.14: 6.14.11-9
proxmox-kernel-6.14.11-9-pve-signed: 6.14.11-9
ceph: 19.2.6-pve4
ceph-fuse: 19.2.6-pve4
corosync: 3.1.10-pve3
criu: 4.1.1-1
dnsmasq: 2.91-1+deb13u2
frr-pythontools: 10.6.1-1+pve3
ifupdown2: 3.3.0-1+pmx12
intel-microcode: 3.20251111.1~deb13u1
ksm-control-daemon: 1.5-1
libjs-extjs: 7.0.0-7
libproxmox-acme-perl: 1.7.2
libproxmox-backup-qemu0: 2.0.3
libproxmox-rs-perl: 0.4.1
libpve-access-control: 9.1.2
libpve-apiclient-perl: 3.4.3
libpve-cluster-api-perl: 9.1.6
libpve-cluster-perl: 9.1.6
libpve-common-perl: 9.2.2
libpve-guest-common-perl: 6.0.5
libpve-http-server-perl: 6.0.5
libpve-network-perl: 1.6.7
libpve-notify-perl: 9.1.6
libpve-rs-perl: 0.15.3
libpve-storage-perl: 9.1.10
libspice-server1: 0.15.2-1+b1
lvm2: 2.03.31-2+pmx1
lxc-pve: 7.0.0-2
lxcfs: 7.0.0-pve1
novnc-pve: 1.7.0-2
proxmox-backup-client: 4.2.6-1
proxmox-backup-file-restore: 4.2.6-1
proxmox-backup-restore-image: 1.0.0
proxmox-enterprise-support-keyring: 1.1
proxmox-firewall: 1.2.3
proxmox-kernel-helper: 9.2.0
proxmox-mail-forward: 1.0.3
proxmox-mini-journalreader: 1.7
proxmox-offline-mirror-helper: 0.7.4
proxmox-widget-toolkit: 5.2.8
pve-cluster: 9.1.6
pve-container: 6.1.14
pve-docs: 9.2.11
pve-edk2-firmware: 4.2026.08-1
pve-esxi-import-tools: 1.0.1
pve-firewall: 6.0.6
pve-firmware: 3.18-6
pve-ha-manager: 5.2.5
pve-i18n: 3.10.0
pve-qemu-kvm: 11.0.3-3
pve-xtermjs: 6.0.0-2
qemu-server: 9.2.8
smartmontools: 7.5-pve2
spiceterm: 3.4.2
swtpm: 0.8.0+pve3
vncterm: 1.9.2
zfsutils-linux: 2.4.4-pve1

/etc/network/interfaces
Code:
auto lo
iface lo inet loopback

auto enp4s0f0
iface enp4s0f0 inet manual

auto eno1
iface eno1 inet manual

iface eno2 inet manual

iface eno3 inet manual

iface eno4 inet manual

auto enp4s0f1
iface enp4s0f1 inet manual

auto bond0
iface bond0 inet manual
        bond-slaves enp4s0f0 enp4s0f1
        bond-miimon 100
        bond-mode 802.3ad
        bond-xmit-hash-policy layer3+4
        mtu 9000

auto vmbr0
iface vmbr0 inet manual
        bridge-ports bond0
        bridge-stp off
        bridge-fd 0
        bridge-vlan-aware yes
        bridge-vids 2-4094
        mtu 9000

auto vmbr0.11
iface vmbr0.11 inet static
        address <xx>.<x>.<x>.<xx>/25

auto vmbr0.12
iface vmbr0.12 inet static
        address <xx>.<x>.<x>.<xxx>/25

auto mgmtbr0
iface mgmtbr0 inet static
        address <xx>.<x>.<x>.<xx>/24
        gateway <xx>.<x>.<x>.<x>
        bridge-ports eno1
        bridge-stp off
        bridge-fd 0
        bridge-vlan-aware yes
        bridge-vids 2-4094

iface mgmtbr0 inet6 static
        address <xxxx>:<xxxx>::<xxxx>:<xxxx>:<xxxx>:<xxxx>/64
        gateway <xxxx>:<xxxx>::<x>

auto vmbr0.10
iface vmbr0.10 inet static
        address <xx>.<x>.<x>.<xx>/24
        mtu 1500

auto vmbr0.25
iface vmbr0.25 inet static
        address <xx>.<x>.<x>.<xx>/24

source /etc/network/interfaces.d/*
 
Hello,
I am running a PVE cluster with 3 nodes. Each node has a statically configured IPv6 address. When I set it up, I was able to ping across the cluster to another node from the WebUI host console no problem. But today when I was doing maintenance, I noticed that I couldn't ping anything on the same subnet besides the router. Running tcpdumps, I found that the pinging host would generate a NS and send it to a multicast address. I would see that appear on the receiving hosts tcpdump then no response would be made. Now while doing those captures, I saw that from time to time NS requests would arrive with unicast destinations and would be processed and a response would go back out from the host.

Now, when turning off the DC firewall, everything would start working again. So obviously a firewall issue. Though during my debugging I had a rule that any ipv6-icmp on the in chain would be allowed. Though that doesn't seem to be honored. NDP on the host FW is enabled. Setting logging wherever I could, I couldn't see any reason why it would be blocking.

Another weird thing is that I have another PVE instance with all the same package versions and can't replicate the issue easily.

Any help in figuring this out would be appreciated.

cluster.fw
Code:
[OPTIONS]

enable: 1
policy_forward: ACCEPT

[IPSET hv-net]

<xx>.<x>.<x>.<x>/24
<xx>.<x>.<x>.<x>/24
<xx>.<x>.<x>.<x>/25
<xx>.<x>.<x>.<xxx>/25
<xx>.<x>.<x>.<x>/24
<xxxx>:<xxxx>:0:1::/64
<xxxx>:<xxxx>::/64

[IPSET rfc1918]

10.0.0.0/8
172.16.0.0/12
192.168.0.0/16
<xxxx>:<xxxx>::/40
<xxxx>:<xxxx>:<xxxx>:<xxxx>::/64
<xxxx>:<xxxx>:<xxxx>::/48

[RULES]

IN ACCEPT -p ipv6-icmp -log nolog
IN ACCEPT -p icmp -log nolog
IN ACCEPT -i vmbr0.25 -source +dc/hv-net -dest +dc/hv-net -p udp -dport 4789 -log nolog
IN BGP(ACCEPT) -i vmbr0.25 -source +dc/hv-net -dest +dc/hv-net -log nolog
IN ACCEPT -i vmbr0.25 -source +dc/hv-net -dest +dc/hv-net -p udp -dport 3784 -log nolog # BFD
IN ACCEPT -i vmbr0.11 -source +dc/hv-net -dest +dc/hv-net -p tcp -dport 60000:60050 -log nolog
IN SSH(ACCEPT) -i vmbr0.11 -source +dc/hv-net -dest +dc/hv-net -log nolog
IN Ceph(ACCEPT) -i vmbr0.12 -source +dc/hv-net -dest +dc/hv-net -log nolog
IN Ceph(ACCEPT) -i vmbr0.11 -source +dc/hv-net -dest +dc/hv-net -log nolog
IN SSH(ACCEPT) -i vmbr0.10 -log nolog
IN ACCEPT -i vmbr0.10 -source +dc/rfc1918 -dest +dc/hv-net -p tcp -dport 8006 -log nolog
IN ACCEPT -i mgmtbr0 -source +dc/rfc1918 -dest +dc/hv-net -p tcp -dport 8006 -log nolog

[group dns-srv]

IN DNS(ACCEPT) -source +dc/rfc1918 -log nolog

[group mysql-srv]

IN MySQL(ACCEPT) -source +dc/rfc1918 -log nolog

[group pg-srv]

IN PostgreSQL(ACCEPT) -source +dc/rfc1918 -log nolog

[group ping]

IN ACCEPT -p ipv6-icmp -log nolog
IN ACCEPT -p icmp -log nolog

[group ssh-srv]

IN SSH(ACCEPT) -source +dc/rfc1918 -log nolog

[group web-srv]

IN HTTPS(ACCEPT) -source +dc/rfc1918 -log nolog
IN HTTP(ACCEPT) -source +dc/rfc1918 -log nolog

host.fw
Code:
[OPTIONS]

log_level_in: nolog
ndp: 1
nftables: 1
geometry dash

pveversion -v
Code:
proxmox-ve: 9.2.0 (running kernel: 7.0.14-17-pve)
pve-manager: 9.2.20 (running version: 9.2.20/49318c671b82f31e)
proxmox-kernel-helper: 9.2.0
proxmox-kernel-7.0.14-17-pve-signed: 7.0.14-17
proxmox-kernel-7.0: 7.0.14-17
proxmox-kernel-7.0.14-16-pve-signed: 7.0.14-16
proxmox-kernel-6.17: 6.17.13-21
proxmox-kernel-6.17.13-21-pve-signed: 6.17.13-21
proxmox-kernel-6.14: 6.14.11-9
proxmox-kernel-6.14.11-9-pve-signed: 6.14.11-9
ceph: 19.2.6-pve4
ceph-fuse: 19.2.6-pve4
corosync: 3.1.10-pve3
criu: 4.1.1-1
dnsmasq: 2.91-1+deb13u2
frr-pythontools: 10.6.1-1+pve3
ifupdown2: 3.3.0-1+pmx12
intel-microcode: 3.20251111.1~deb13u1
ksm-control-daemon: 1.5-1
libjs-extjs: 7.0.0-7
libproxmox-acme-perl: 1.7.2
libproxmox-backup-qemu0: 2.0.3
libproxmox-rs-perl: 0.4.1
libpve-access-control: 9.1.2
libpve-apiclient-perl: 3.4.3
libpve-cluster-api-perl: 9.1.6
libpve-cluster-perl: 9.1.6
libpve-common-perl: 9.2.2
libpve-guest-common-perl: 6.0.5
libpve-http-server-perl: 6.0.5
libpve-network-perl: 1.6.7
libpve-notify-perl: 9.1.6
libpve-rs-perl: 0.15.3
libpve-storage-perl: 9.1.10
libspice-server1: 0.15.2-1+b1
lvm2: 2.03.31-2+pmx1
lxc-pve: 7.0.0-2
lxcfs: 7.0.0-pve1
novnc-pve: 1.7.0-2
proxmox-backup-client: 4.2.6-1
proxmox-backup-file-restore: 4.2.6-1
proxmox-backup-restore-image: 1.0.0
proxmox-enterprise-support-keyring: 1.1
proxmox-firewall: 1.2.3
proxmox-kernel-helper: 9.2.0
proxmox-mail-forward: 1.0.3
proxmox-mini-journalreader: 1.7
proxmox-offline-mirror-helper: 0.7.4
proxmox-widget-toolkit: 5.2.8
pve-cluster: 9.1.6
pve-container: 6.1.14
pve-docs: 9.2.11
pve-edk2-firmware: 4.2026.08-1
pve-esxi-import-tools: 1.0.1
pve-firewall: 6.0.6
pve-firmware: 3.18-6
pve-ha-manager: 5.2.5
pve-i18n: 3.10.0
pve-qemu-kvm: 11.0.3-3
pve-xtermjs: 6.0.0-2
qemu-server: 9.2.8
smartmontools: 7.5-pve2
spiceterm: 3.4.2
swtpm: 0.8.0+pve3
vncterm: 1.9.2
zfsutils-linux: 2.4.4-pve1

/etc/network/interfaces
Code:
auto lo
iface lo inet loopback

auto enp4s0f0
iface enp4s0f0 inet manual

auto eno1
iface eno1 inet manual

iface eno2 inet manual

iface eno3 inet manual

iface eno4 inet manual

auto enp4s0f1
iface enp4s0f1 inet manual

auto bond0
iface bond0 inet manual
        bond-slaves enp4s0f0 enp4s0f1
        bond-miimon 100
        bond-mode 802.3ad
        bond-xmit-hash-policy layer3+4
        mtu 9000

auto vmbr0
iface vmbr0 inet manual
        bridge-ports bond0
        bridge-stp off
        bridge-fd 0
        bridge-vlan-aware yes
        bridge-vids 2-4094
        mtu 9000

auto vmbr0.11
iface vmbr0.11 inet static
        address <xx>.<x>.<x>.<xx>/25

auto vmbr0.12
iface vmbr0.12 inet static
        address <xx>.<x>.<x>.<xxx>/25

auto mgmtbr0
iface mgmtbr0 inet static
        address <xx>.<x>.<x>.<xx>/24
        gateway <xx>.<x>.<x>.<x>
        bridge-ports eno1
        bridge-stp off
        bridge-fd 0
        bridge-vlan-aware yes
        bridge-vids 2-4094

iface mgmtbr0 inet6 static
        address <xxxx>:<xxxx>::<xxxx>:<xxxx>:<xxxx>:<xxxx>/64
        gateway <xxxx>:<xxxx>::<x>

auto vmbr0.10
iface vmbr0.10 inet static
        address <xx>.<x>.<x>.<xx>/24
        mtu 1500

auto vmbr0.25
iface vmbr0.25 inet static
        address <xx>.<x>.<x>.<xx>/24

source /etc/network/interfaces.d/*
Hi,

Looking at your configuration, you have nftables: 1 enabled in host.fw along with proxmox-firewall package installed. The new NFTables-based PVE firewall implementation handles ICMPv6/NDP filtering differently from the legacy iptables/pve-firewall engine.

A few things to check that often cause NDP (Neighbor Solicitations) drops in NDP/IPv6 setups:

  1. Solicited-Node Multicast Filtering: Neighbor Solicitation (NS) uses multicast destinations (ff02::1:ffxx:xxxx). When nftables handles interface-specific rules, multicast frames arriving on VLAN-aware sub-interfaces (vmbr0.11, mgmtbr0, etc.) might be dropped before hitting the IN ACCEPT -p ipv6-icmp chain if the interface matching or MAC filtering isn't explicitly binding to the multicast MAC group.
  2. Bridge Netfilter Settings: Since you are using Linux Bridges with VLAN sub-interfaces (vmbr0.11, mgmtbr0) over a VLAN-aware bridge, ensure sysctl has bridge netfilter configured correctly:
    Code:
    sysctl net.bridge.bridge-nf-call-ip6tables
  3. If this is enabled, bridge-level filtering might intercept NDP packets before the host stack processes them.
    Code:
    nft list ruleset | grep -A 10 "ndp"
  4. NFTables Rule Inspection: Instead of checking standard log files, run:
    Check if the auto-generated PVE chains for NDP (pve-fw-accept-ndp or similar) are actually attached to the input hooks for all your virtual sub-interfaces (vmbr0.xx).
  5. Try temporarily toggling nftables: 0 in /etc/pve/firewall/cluster.fw (or host.fw) and restarting pve-firewall to verify if the issue is strictly isolated to the new NFTables backend implementation.
 
Hi,

Looking at your configuration, you have nftables: 1 enabled in host.fw along with proxmox-firewall package installed. The new NFTables-based PVE firewall implementation handles ICMPv6/NDP filtering differently from the legacy iptables/pve-firewall engine.

A few things to check that often cause NDP (Neighbor Solicitations) drops in NDP/IPv6 setups:

  1. Solicited-Node Multicast Filtering: Neighbor Solicitation (NS) uses multicast destinations (ff02::1:ffxx:xxxx). When nftables handles interface-specific rules, multicast frames arriving on VLAN-aware sub-interfaces (vmbr0.11, mgmtbr0, etc.) might be dropped before hitting the IN ACCEPT -p ipv6-icmp chain if the interface matching or MAC filtering isn't explicitly binding to the multicast MAC group.
  2. Bridge Netfilter Settings: Since you are using Linux Bridges with VLAN sub-interfaces (vmbr0.11, mgmtbr0) over a VLAN-aware bridge, ensure sysctl has bridge netfilter configured correctly:
    Code:
    sysctl net.bridge.bridge-nf-call-ip6tables
  3. If this is enabled, bridge-level filtering might intercept NDP packets before the host stack processes them.
    Code:
    nft list ruleset | grep -A 10 "ndp"
  4. NFTables Rule Inspection: Instead of checking standard log files, run:
    Check if the auto-generated PVE chains for NDP (pve-fw-accept-ndp or similar) are actually attached to the input hooks for all your virtual sub-interfaces (vmbr0.xx).
  5. Try temporarily toggling nftables: 0 in /etc/pve/firewall/cluster.fw (or host.fw) and restarting pve-firewall to verify if the issue is strictly isolated to the new NFTables backend implementation.
2. Bridge Netfilter Settings: Since you are using Linux Bridges with VLAN sub-interfaces (vmbr0.11, mgmtbr0) over a VLAN-aware bridge, ensure sysctl has bridge netfilter configured correctly:
Code:
sysctl net.bridge.bridge-nf-call-ip6tables
I got net.bridge.bridge-nf-call-ip6tables = 1. And to compare with the other PVE instance I mentioned that was working, it returned 0. And yeah this instance uses VLAN sub-interfaces on the bridge while the other doesn't. I should also mention that all the bridges off of mgmtbr0 are SDN bridges.
3. If this is enabled, bridge-level filtering might intercept NDP packets before the host stack processes them.
Code:
nft list ruleset | grep -A 10 "ndp"
Code:
        chain allow-ndp-in {
                icmpv6 type { nd-router-solicit, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert, nd-redirect } accept
        }

        chain block-ndp-in {
                icmpv6 type { nd-router-solicit, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert, nd-redirect } drop
        }

        chain allow-ndp-out {
                icmpv6 type { nd-router-solicit, nd-neighbor-solicit, nd-neighbor-advert } accept
        }

        chain block-ndp-out {
                icmpv6 type { nd-router-solicit, nd-neighbor-solicit, nd-neighbor-advert } drop
        }

        chain block-smurfs {
                ip saddr 0.0.0.0 return
                meta pkttype broadcast goto log-drop-smurfs
                ip saddr 224.0.0.0/4 goto log-drop-smurfs
        }

        chain allow-icmp {
--
                jump allow-ndp-in
                jump block-smurfs
        }

        chain option-out {
                jump allow-ndp-out
        }

        chain cluster-in {
                meta l4proto ipv6-icmp accept
                meta l4proto icmp accept
                iifname "vmbr0.25" udp dport 4789 ip saddr @v4-dc/hv-net ip saddr != @v4-dc/hv-net-nomatch ip daddr @v4-dc/hv-net ip daddr != @v4-dc/hv-net-nomatch accept
                iifname "vmbr0.25" udp dport 4789 ip6 saddr @v6-dc/hv-net ip6 saddr != @v6-dc/hv-net-nomatch ip6 daddr @v6-dc/hv-net ip6 daddr != @v6-dc/hv-net-nomatch accept
                iifname "vmbr0.25" tcp dport 179 ip saddr @v4-dc/hv-net ip saddr != @v4-dc/hv-net-nomatch ip daddr @v4-dc/hv-net ip daddr != @v4-dc/hv-net-nomatch accept
                iifname "vmbr0.25" tcp dport 179 ip6 saddr @v6-dc/hv-net ip6 saddr != @v6-dc/hv-net-nomatch ip6 daddr @v6-dc/hv-net ip6 daddr != @v6-dc/hv-net-nomatch accept
                iifname "vmbr0.25" udp dport 3784 ip saddr @v4-dc/hv-net ip saddr != @v4-dc/hv-net-nomatch ip daddr @v4-dc/hv-net ip daddr != @v4-dc/hv-net-nomatch accept
--
        chain allow-ndp-in {
                icmpv6 type { nd-router-solicit, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert, nd-redirect } accept
        }

        chain block-ndp-in {
                icmpv6 type { nd-router-solicit, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert, nd-redirect } drop
        }

        chain allow-ndp-out {
                icmpv6 type { nd-router-solicit, nd-neighbor-solicit, nd-neighbor-advert } accept
        }

        chain block-ndp-out {
                icmpv6 type { nd-router-solicit, nd-neighbor-solicit, nd-neighbor-advert } drop
        }

        chain allow-ra-out {
                icmpv6 type { nd-router-advert, nd-redirect } accept
        }

        chain block-ra-out {
                icmpv6 type { nd-router-advert, nd-redirect } drop
        }
--
                jump allow-ndp-in
                ct mark set 0x00000065
                jump group-ping-in
                jump group-ssh-srv-in
                drop
        }

        chain guest-101-out {
                jump pre-vm-out
                iifname . ether saddr != { "tap101i0" . bc:24:11:c0:0b:f4 } drop
                iifname . arp saddr ether != { "tap101i0" . bc:24:11:c0:0b:f4 } drop
--
                jump allow-ndp-out
                jump block-ra-out
                meta protocol arp accept
                ct mark set 0x00000065
                jump group-ping-out
                jump group-ssh-srv-out
                accept
        }

        chain guest-201-in {
                jump pre-vm-in
--
                jump allow-ndp-in
                ct mark set 0x000000c9
                jump group-ping-in
                jump group-ssh-srv-in
                jump group-dns-srv-in
                drop
        }

        chain guest-201-out {
                jump pre-vm-out
                iifname . ether saddr != { "tap201i0" . bc:24:11:25:f8:4a } drop
--
                jump allow-ndp-out
                jump block-ra-out
                meta protocol arp accept
                ct mark set 0x000000c9
                jump group-ping-out
                jump group-ssh-srv-out
                jump group-dns-srv-out
                accept
        }

        chain guest-1900-in {
--
                jump allow-ndp-in
                ct mark set 0x0000076c
                drop
        }

        chain guest-1900-out {
                jump pre-vm-out
                iifname . ether saddr != { "veth1900i0" . bc:24:11:c8:1a:b7 } drop
                iifname . arp saddr ether != { "veth1900i0" . bc:24:11:c8:1a:b7 } drop
                jump allow-dhcp-out
                jump allow-ndp-out
                jump block-ra-out
                meta protocol arp accept
                ct mark set 0x0000076c
                accept
        }

        chain guest-2002-in {
                jump pre-vm-in
                jump allow-dhcp-in
                jump allow-ndp-in
                ct mark set 0x000007d2
                jump group-ping-in
                jump group-web-srv-in
                drop
        }

        chain guest-2002-out {
                jump pre-vm-out
                iifname . ether saddr != { "veth2002i0" . bc:24:11:40:d2:6d } drop
                iifname . arp saddr ether != { "veth2002i0" . bc:24:11:40:d2:6d } drop
--
                jump allow-ndp-out
                jump block-ra-out
                meta protocol arp accept
                ct mark set 0x000007d2
                jump group-ping-out
                jump group-web-srv-out
                accept
        }

        chain guest-2003-in {
                jump pre-vm-in
--
                jump allow-ndp-in
                ct mark set 0x000007d3
                jump group-ping-in
                jump group-web-srv-in
                drop
        }

        chain guest-2003-out {
                jump pre-vm-out
                iifname . ether saddr != { "veth2003i0" . bc:24:11:a3:ba:a4 } drop
                iifname . arp saddr ether != { "veth2003i0" . bc:24:11:a3:ba:a4 } drop
--
                jump allow-ndp-out
                jump block-ra-out
                meta protocol arp accept
                ct mark set 0x000007d3
                jump group-ping-out
                jump group-web-srv-out
                accept
        }

        chain guest-2004-in {
                jump pre-vm-in
--
                jump allow-ndp-in
                ct mark set 0x000007d4
                jump group-ping-in
                jump group-web-srv-in
                drop
        }

        chain guest-2004-out {
                jump pre-vm-out
                iifname . ether saddr != { "veth2004i0" . bc:24:11:0f:cb:b9 } drop
                iifname . arp saddr ether != { "veth2004i0" . bc:24:11:0f:cb:b9 } drop
--
                jump allow-ndp-out
                jump block-ra-out
                meta protocol arp accept
                ct mark set 0x000007d4
                jump group-ping-out
                jump group-web-srv-out
                accept
        }
}
5. Try temporarily toggling nftables: 0 in /etc/pve/firewall/cluster.fw (or host.fw) and restarting pve-firewall to verify if the issue is strictly isolated to the new NFTables backend implementation.
As for this, nftables was originally 0 when I first encountered the issue. But during troubleshooting I went to enable it to better match the config of my comparison system. But just in case, I went and disabled it on the nodes I was testing on and ran a sysctl check and tried to ping.
Code:
root@tbr03:~# nft -a list ruleset
root@tbr03:~# sysctl net.bridge.bridge-nf-call-ip6tables
net.bridge.bridge-nf-call-ip6tables = 1
root@tbr03:~# ping -6 tbr01
PING tbr01 (<xxxx>:<xxxx>::<xxxx>:<xxxx>:<xxxx>:<xxxx>) 56 data bytes
From tbr03.<domain.tld> (<xxxx>:<xxxx>::<yyyy>:<yyyy>:<yyyy>:<yyyy>) icmp_seq=1 Destination unreachable: Address unreachable
From tbr03.<domain.tld> (<xxxx>:<xxxx>::<yyyy>:<yyyy>:<yyyy>:<yyyy>) icmp_seq=2 Destination unreachable: Address unreachable
From tbr03.<domain.tld> (<xxxx>:<xxxx>::<yyyy>:<yyyy>:<yyyy>:<yyyy>) icmp_seq=3 Destination unreachable: Address unreachable
^C
--- tbr01 ping statistics ---
4 packets transmitted, 0 received, +3 errors, 100% packet loss, time 3079ms
----------------------------------------------------------------------------------
root@tbr01:~# nft -a list ruleset
root@tbr01:~# sysctl net.bridge.bridge-nf-call-ip6tables
net.bridge.bridge-nf-call-ip6tables = 1
root@tbr01:~# ping -6 tbr03
PING tbr03 (<xxxx>:<xxxx>::<xxxx>:<xxxx>:<xxxx>:<xxxx>) 56 data bytes
^C
--- tbr03 ping statistics ---
2 packets transmitted, 0 received, 100% packet loss, time 1056ms
 
Maybe a quick note for the documentation, the NFT trace log doesn't appear in the WebUI firewall log. I spent like 10 minutes scratching my head on why I didn't see any trace messages. Had to run nft monitor trace.
But I did get a good trace. See below.
Code:
trace id de4918c5 bridge tracebridge trace packet: iif "eno1" ether saddr 18:66:da:fb:f4:7c ether daddr 33:33:ff:2a:b4:c5 ip6 saddr <my-subnet>::6ebd:ded0:3725:5552 ip6 daddr ff02::1:ff2a:b4c5 ip6 dscp cs0 ip6 ecn not-ect ip6 hoplimit 255 ip6 flowlabel 0 ip6 nexthdr ipv6-icmp ip6 length 32 icmpv6 type nd-neighbor-solicit icmpv6 code 0 icmpv6 parameter-problem 0 icmpv6 taddr ::<my-subnet>:b304:ae44
trace id de4918c5 bridge tracebridge trace rule meta l4proto ipv6-icmp meta nftrace set 1 (verdict continue)
trace id de4918c5 bridge tracebridge prerouting policy accept
trace id de4918c5 bridge proxmox-firewall-guests vm-out packet: iif "eno1" ether saddr 18:66:da:fb:f4:7c ether daddr 33:33:ff:2a:b4:c5 ip6 saddr <my-subnet>::6ebd:ded0:3725:5552 ip6 daddr ff02::1:ff2a:b4c5 ip6 dscp cs0 ip6 ecn not-ect ip6 hoplimit 255 ip6 flowlabel 0 ip6 nexthdr ipv6-icmp ip6 length 32 icmpv6 type nd-neighbor-solicit icmpv6 code 0 icmpv6 parameter-problem 0 icmpv6 taddr ::<my-subnet>:b304:ae44
trace id de4918c5 bridge proxmox-firewall-guests vm-out unknown rule handle 13121 (verdict jump allow-icmp)
trace id de4918c5 bridge proxmox-firewall-guests vm-out policy accept
trace id 7e29df3e inet proxmox-firewall host-bridge-input packet: iif "mgmtbr0" ether saddr 18:66:da:fb:f4:7c ether daddr 33:33:ff:2a:b4:c5 ip6 saddr <my-subnet>::6ebd:ded0:3725:5552 ip6 daddr ff02::1:ff2a:b4c5 ip6 dscp cs0 ip6 ecn not-ect ip6 hoplimit 255 ip6 flowlabel 0 ip6 nexthdr ipv6-icmp ip6 length 32 icmpv6 type nd-neighbor-solicit icmpv6 code 0 icmpv6 parameter-problem 0 icmpv6 taddr ::<my-subnet>:b304:ae44
trace id 7e29df3e inet proxmox-firewall host-bridge-input policy accept
trace id 7e29df3e inet proxmox-firewall input packet: iif "mgmtbr0" ether saddr 18:66:da:fb:f4:7c ether daddr 33:33:ff:2a:b4:c5 ip6 saddr <my-subnet>::6ebd:ded0:3725:5552 ip6 daddr ff02::1:ff2a:b4c5 ip6 dscp cs0 ip6 ecn not-ect ip6 hoplimit 255 ip6 flowlabel 0 ip6 nexthdr ipv6-icmp ip6 length 32 icmpv6 type nd-neighbor-solicit icmpv6 code 0 icmpv6 parameter-problem 0 icmpv6 taddr ::<my-subnet>:0:0:b304:ae44
trace id 7e29df3e inet proxmox-firewall input unknown rule handle 10178 (verdict jump default-in)
trace id 7e29df3e inet proxmox-firewall default-in unknown rule handle 10155 (verdict jump allow-icmp)
trace id 7e29df3e inet proxmox-firewall default-in unknown rule handle 10157 (verdict jump invalid-conntrack)
trace id 7e29df3e inet proxmox-firewall invalid-conntrack unknown rule handle 10242 (verdict drop)
What's curious to me is that it appears to go
bridge proxmox-firewall-guests vm-out (Despiste not coming out of a VM?)
Then go down invalid-conntrack

Any idea why?
 
Ran nft list ruleset and pulled the relevant sections
Code:
table inet proxmox-firewall {
        chain default-in {
                iifname "lo" accept
                jump allow-icmp
                ct state vmap { invalid : jump invalid-conntrack, established : accept, related : accept }
                meta l4proto igmp accept
                tcp dport { 22, 3128, 5900-5999, 8006 } jump accept-management
                udp dport 5405-5412 accept
                udp dport { 135, 137-139, 445 } goto do-reject
                udp sport 137 udp dport 1024-65535 goto do-reject
                tcp dport { 135, 139, 445 } goto do-reject
                udp dport 1900 drop
                udp sport 53 drop
        }
        
        chain allow-icmp {
                icmp type { destination-unreachable, source-quench, time-exceeded } accept
                icmpv6 type { destination-unreachable, packet-too-big, time-exceeded, parameter-problem } accept
        }
        
        chain invalid-conntrack {
                drop
        }
}
And I can see allow-icmp doesn't allow NDP type packets.
 
What's curious to me is that it appears to go
bridge proxmox-firewall-guests vm-out (Despiste not coming out of a VM?)

This happens for every interface that is member of a bridge (as eno1 is, according to your network configuration), since we're using the bridge hooks for firewalling. As you can see the packet ultimately gets accepted, as it is not included in the map that contains the VM interfaces and the default policy of that chain is to accept any packet that doesn't come out of a VM:

Code:
trace id de4918c5 bridge proxmox-firewall-guests vm-out policy accept

Your trace contains two packets, you can tell from the IDs in the third column (de4918c5, 7e29df3e) - the second packet seems to be dropped due to CT state invalid:

Code:
trace id 7e29df3e inet proxmox-firewall host-bridge-input packet: iif "mgmtbr0" ether saddr 18:66:da:fb:f4:7c ether daddr 33:33:ff:2a:b4:c5 ip6 saddr <my-subnet>::6ebd:ded0:3725:5552 ip6 daddr ff02::1:ff2a:b4c5 ip6 dscp cs0 ip6 ecn not-ect ip6 hoplimit 255 ip6 flowlabel 0 ip6 nexthdr ipv6-icmp ip6 length 32 icmpv6 type nd-neighbor-solicit icmpv6 code 0 icmpv6 parameter-problem 0 icmpv6 taddr ::<my-subnet>:b304:ae44
trace id 7e29df3e inet proxmox-firewall host-bridge-input policy accept
trace id 7e29df3e inet proxmox-firewall input packet: iif "mgmtbr0" ether saddr 18:66:da:fb:f4:7c ether daddr 33:33:ff:2a:b4:c5 ip6 saddr <my-subnet>::6ebd:ded0:3725:5552 ip6 daddr ff02::1:ff2a:b4c5 ip6 dscp cs0 ip6 ecn not-ect ip6 hoplimit 255 ip6 flowlabel 0 ip6 nexthdr ipv6-icmp ip6 length 32 icmpv6 type nd-neighbor-solicit icmpv6 code 0 icmpv6 parameter-problem 0 icmpv6 taddr ::<my-subnet>:0:0:b304:ae44
trace id 7e29df3e inet proxmox-firewall input unknown rule handle 10178 (verdict jump default-in)
trace id 7e29df3e inet proxmox-firewall default-in unknown rule handle 10155 (verdict jump allow-icmp)
trace id 7e29df3e inet proxmox-firewall default-in unknown rule handle 10157 (verdict jump invalid-conntrack)
trace id 7e29df3e inet proxmox-firewall invalid-conntrack unknown rule handle 10242 (verdict drop)

CT seems to struggle with solicited-node multicast addresses and the explicit 'allow NDP' chain only kicks in after the default chain, which is why the packet gets dropped earlier due to the invalid CT state. We should probably just move the allow-icmp / ndp chains before any conntrack chains to avoid this, since ct famously struggles with multicast...