Loosing IPV6 with security.ipv6_filtering

Hello,

Here is my bridge config :

config:
ipv4.address: [redacted].1/24
ipv4.nat: “true”
ipv4.nat.address: [redacted]
ipv6.address: [redacted]::1/64
ipv6.dhcp: “true”
ipv6.dhcp.stateful: “true”
ipv6.nat: “true”
ipv6.nat.address: [redacted]
description: “”
type: bridge

And my container’s :

devices:
eth0:
ipv4.address: [redacted].2
ipv6.address: [redacted]::2
name: eth0
network: [redacted]
security.ipv4_filtering: “true”
security.ipv6_filtering: “true”
type: nic

And :

cat /etc/netplan/50-cloud-init.yaml

network:
version: 2
ethernets:
eth0:
dhcp4: true
dhcp6: false

The issue I have is that the container gets its static IP on boot, but then losses it. It seems it get a delay for the lease (DHCPv6 client: Valid lifetime expires in 1h), and renew fails :

2025-11-28T17:43:31.807216+11:00 front systemd-networkd[374]: eth0: DHCPv6 client: Sent Renew
2025-11-28T17:43:31.807589+11:00 front systemd-networkd[374]: eth0: DHCPv6 client: Next retransmission in 19s

With TCPdump I can see the renew request, but dnsmasq doesn’t reply to it…

That’s the same as Enabling security.ipv6_filtering causes the host to stop responding to DHCPv6 renew requests - #14 by xenon

I’ve been trying to sort this one out on and off but haven’t yet gotten to the bottom of it. It’s certainly possible to add/relax the rules to make it work, but not in a way that wouldn’t also allow potential spoofing or DoS… It’s also really weird as the very packet we see in tcpdump matches perfectly the rule in nft and in fact clears it during the initial request.

So SLAAC ip only for now with security.ipv6_filtering ?

Yeah, SLAAC should work reliably as we don’t have the same problem with the firewall getting in the way of the RAs.

@stgraber The thing is that I need to have a static IPV6 for my proxy device :

root@server:~# incus restart front
Error: Failed to start device “proxy-http6”: Connect IP “fd00:[redacted]::2” must be one of the instance’s static IPv6 addresses
Try incus info --show-log front for more info

Any idea to solve that ? ipv6.dhcp.expiry=5y ?

I put together a dnsmasq DHCP script a while back for doing IPv6 filtering, etc. with an externally managed bridge. While it might not help directly with your problem, maybe you can work around it by configuring a bridge manually (e.g., with systemd-networkd) and running your own dnsmasq on it. In my admittedly limited testing, I don’t think I have had any problems with DHCPv6 renewal until now.

There is no release yet, but from my perspective it’s currently feature complete:

But it would need to be supplemented with your own nftables config for the bridge, in which you’d “jump” to the generated chains. So, it’s probably a bit much effort and you’d need to ensure security on your own instead of leaving it to Incus. It might be an alternative to consider, though.

Thanks, but it isn’t really related as I want to continue to rely on an Incus managed bridge.

I want :

  • security.ipv6_filtering: “true”
  • Ipv6 connectivity for my container
  • Ipv6 proxy devices from a single host’s public IP to a couple containers

@stgraber Retested this on Incus 7.3 (Zabbly stable, Ubuntu 26.04, Zabbly kernel, nftables driver). Still reproducible, and the behavior is very clean to observe with a short lease:

incus network set br-test ipv6.dhcp.stateful=true ipv6.dhcp.expiry=3m

Container NIC has security.ipv6_filtering=true, address assigned from the stateful range at boot (fd42:100::d000:0:0:1). Then valid_lft just counts down to zero, not a single renewal gets through:

14:19:38 inet6 fd42:100::d000:0:0:1/128 … valid_lft 163sec
14:20:23 inet6 fd42:100::d000:0:0:1/128 … valid_lft 118sec
14:21:08 inet6 fd42:100::d000:0:0:1/128 … valid_lft 72sec
14:21:53 inet6 fd42:100::d000:0:0:1/128 … valid_lft 27sec
14:22:38 (address gone, gateway unreachable)

Restarting the container brings the address back immediately, which matches the rule analysis from this thread: the filtering accept rule only matches DHCPv6 to the multicast address (ip6 daddr ff02::1:2 udp dport 547), so the initial multicast SOLICIT works while the unicast RENEW to the server is dropped before dnsmasq ever sees it.

So on 7.3 the practical options are still the same: ipv6.dhcp.expiry=87600h on the bridge, or static ipv6.address on the NIC device (which is what pins the filtering anyway). Would extending the accept rule to also match unicast DHCPv6 from fe80::/10 to the bridge’s own address on port 547 be an acceptable relaxation? That would keep the source restriction while letting renewals reach dnsmasq.