0

I'm using ip6tables to nat packages to an IP on a different interface (localhost), but all packets sent there are lost. I have the same iptables rules for IPv4 where everything is working fine.

Setup

The host is a debian 11 VM, the client is a docker container running debian:stable-slim.

/etc/docker/daemon.json:

{
   "ipv6": true,
   "fixed-cidr-v6": "2d00::/8",
   "ip6tables": true,
   "experimental": true
}

The host has the docker0 interface IPs 2d00::1 and 172.17.0.2 for IPv6 and IPv4 respectively. I'm setting up the following ip(6)tables rules to NAT for IPv4 and IPv6 respectively:

ip6tables -t nat -A PREROUTING -d 2d00::1 -p tcp -m tcp --dport 8081 -j DNAT --to-destination [::1]:9999
iptables -t nat -A PREROUTING -d 172.17.0.2 -p tcp -m tcp --dport 8081 -j DNAT --to-destination 127.0.0.1:9999

I have also appended the following rules to my /etc/sysctl.conf:

net.ipv4.ip_forward = 1
net.ipv4.conf.all.route_localnet = 1
net.ipv6.conf.all.use_tempaddr = 0
net.ipv6.conf.all.forwarding = 1

Minimal non-working example

Set up an HTTP server on the host, listening on [::1]:9999:

python3 -m http.server --bind ::1 9999

And try to connect to it from a client container:

docker run --rm -it --name client debian:stable-slim /bin/bash
apt update
apt install -y curl
curl http://[2d00::1]:8081

The result is that curl hangs for a while and continuously tries to establish a connection, but i never get a response.

Things i have tried and observed

I have done the same test as in the non-working example above for

  • IPv4 with nat
  • IPv6 without nat
  • IPv6 with nat to an IP on the same interface as the original destination IP (i.e. just change the port, or NAT to the interface's link-local address)

and they all succeeded. So the only non-working case is IPv6 with NAT where the NAT wants to route to a different interface.

I have also added log outputs to various ip(6)tables chains and i could see that in the successful cases, i can observe packets arriving at the filter table's INPUT chain, as well as the nat table's PREROUTING chain. As soon as i try to NAT IPv6 traffic to a different interface, i no longer get any packets arriving at any chain in the filter table.

I have also used tcpdump to track packets on the host, and observed that in the successful cases i see the packets at the veth* virtual interfaces created by docker, as well as on the docker0 interface. When doing the NAT to a different interface on IPv6, the packets no longer appear at the docker0 interface, but i still see them at the veth* interface.

1 Answers1

0

After some more digging i found out that the problem is not cross-interface routing, but routing to ::1, which this answer to a different thread has shown me, is not allowed in IPv6 as per RFC 4291 Section 2.5.3.