Port forward for IPv6 with fw4

I have the following firewall rule to make my server located in the LAN reachable from the Internet:

config redirect
        option name 'HTTPS'
        list proto 'tcp'
        list proto 'udp'
        option src 'wan'
        option src_dport '443'
        option dest 'lan'
        option dest_ip '192.168.0.5'
        option dest_port '443'
        option family 'any'
        option target 'DNAT'

The problem: The access is done via IPv4 as well as IPv6. The access via IPv4 works, the IPv6 does not (thanks again @jow for this hint).
So I set the option family to any, hoping that now IPv6 works too - but unfortunately it doesn't.
In the user guide, however, there is a hint for the family option that practically only IPv4 is possible, since fw3 does not support DNAT for IPv6. I however use fw4.

In principle, I realize that I could also directly release the global IPv6 of the internal server and then open accesses to it through the firewall. But I would actually prefer that only the IP of the router is known and it - as with IPv4 also - takes over the redirect.

How can I solve the problem?

You cannot port forward an IPv6 packet to an IPv4 destination. Therefore the rule applies only to the destination IP family.
You can do DNAT in IPv6 but this is very particular case and should not be used, as IPv6 doesn't have the issues of IPv4 with lack of addresses.

I thought so - so i can use the link local ipv6-adress of the server instead?

If dnat is not to be used for ipv6, how would I go about having different targets for different ports instead?
E.G.: If I were to assign the IPv6 address of my internal server to my domain, then I could use port 80 and 443 accordingly through the domain. My CalDav server on the other hand, which has a different address, would then not work. The domain is only linked to one address.

Basically I would need a reverse proxy behind the router? That would be a bit of a shame such an extra complexity, since with IPv4 port forwarding in the router is sufficient. There I can simply distribute the different services on different ports to different devices with the router.

No, the link local addresses are, as the name suggests, valid on the link only.

You have misunderstood something. You can have multiple A or AAAA records to a single IP address. And there is no need for DNAT as the IPv6 addresses assigned from the ISP are global and public.

Yes - linked to the router.
So the router is able to reach the server with his link local address.
But I could also take the address from the local subnet.
Just thought that link local would remain stable over time.

Yes obviously I seem to be misunderstanding something.
I am aware that you can assign multiple addresses to a "domain" (surely that's what you meant instead of ip address?). But what does that change about my services being differentiated by port?

Let's do it best with a concrete example:

Router: 2003::1
Web server: 2003::2
Mail server: 2003::3
Domain: example.org

Now an HTTP request of the form https://example.org/ should finally land on the web server and an SMTP request of the form example.org:587 on the mail server.
With IPv4 no problem - everything lands on the router and it forwards it to the appropriate servers depending on the port used.
Now with IPv6 I do what?
I could assign 2003::2 as well as 2003::3 to the domain example.org - but what for? In that case 50% of the requests would be routed to the wrong server.

So yes - I really seem to misunderstand something.

It is better to use ULA addresses, which are same as the private IPv4 addresses. The default OpenWrt installation is assigning a random ULA prefix to the router, starting from fdxx:xxxx

example.org.	3600	IN	A	192.0.2.1
example.org.	86400	IN	MX	10	mx1.example.org.
example.org.	86400	IN	MX	20	mx2.example.org.
mx1.example.org.	3600	IN	A	192.0.2.1
mx2.example.org.	86400	IN	A	1.2.3.4 <- some outside IP for secondary mx
www.example.org.	3600	IN	A	192.0.2.1
router.example.org.	3600	IN	A	192.0.2.1

example.org.	3600	IN	AAAA	2003::2
mx1.example.org.	86400	IN	AAAA	2003::3
mx2.example.org.	86400	IN	AAAA	2003::4 <- second mx, not possible with single IPv4
www.example.org.	3600	IN	AAAA	2003::2
router.example.org.	86400	IN	AAAA	2003::1

I hope this helps.

1 Like

thx - that's what i meant with "local subnet".
I will use this.

Not really - this solution based on subdomains.
That was not my described scenario.

In principle, however, correct - clearly one could circumvent the problem with it.

However, I am not willing to do this, because I do not have fixed IPs, but dynamic ones. It was a little cramp to assign at least the one IPv6 to the domain dynamically. To extend the config by several sub-addresses would be a real pain and much more complicated than the dnat solution in the IPv4 world.
But for static addresses I would actually prefer - thanks anyway.
Actually I already have several subdomains - but they are processed by the reverse proxy behind the router.
However, there are many people - especially those with free ddns providers - who cannot create subdomains.
There must be a solution for them, too?

Switch to cloudflare and use their DDNS API which is supported by OpenWrt.

Ok, well then let's try to implement the clean option with the direct client IPv6 address. For the sake of simplicity, let's start with a single reverse proxy and not multiple clients. But here I run into some partial problems, which still need to be solved:

  1. the router runs the DDNS client for my domain. Therefore it must know the global IPv6 of the server(!) when changing the IP. How does the router know this address? Currently, I see only as a possibility on the server to set up another DDNS client, which is responsible for the IPv6 of the domain.

  2. the firewall in the router must allow requests to the server on port 443 and 80. For understandable reasons I don't want to allow accesses to 443,80 to zone lan in general but to the specific server only. For this the firewall rule would have to know the destination address of the server (see 1.). And on top of that this would be dynamic, so that the rule itself would have to dynamically adjust the IP. How do I solve this? Could I use the destination Mac address instead - as I understand it, this is not yet known to the firewall at the time of filtering?

DDNS runs for a hostname. Run more DDNS clients on all servers to update them all.

1 Like

Point 2 is solvable by only matching the host part (last 64bit) of the IPv6 address. Something like this:

config rule
  option src wan
  option dest lan
  option proto tcp
  option dest_port 443
  option dest_ip ::d63d:b86c:1778:d43e/::ffff:ffff:ffff:ffff
  option target accept
1 Like

Thanks - so as suspected

Ah - that's a really clever approach - thanks!

I'm afraid I'll have to ask again.
I gave my server a fixed IPv6 token (via networkd IPv6Token), so it now has a global IP of the form 2003:xxx::5.
So I thought that now only the following 2 rules are necessary:

config rule
        option name 'allow-ipv6-https-to-server'
        option src 'wan'
        option dest 'lan'
        option dest_port '443'
        option family 'ipv6'
        list proto 'tcp'
        list proto 'udp'
        option target 'ACCEPT'
        list dest_ip '::0:0:5/::ffff:ffff:ffff'

config rule
        option name 'allow-ipv6-http-to-server'
        option src 'wan'
        option dest 'lan'
        option dest_port '80'
        option family 'ipv6'
        list proto 'tcp'
        list proto 'udp'
        option target 'ACCEPT'
        list dest_ip '::0:0:5/::ffff:ffff:ffff'

Still, I can't manage to access the server from the outside via https://[2003:xxx::5]/.
What else am I missing here?

ip6tables-save -c | grep to-server

1 Like

I'm on fw4 so i did nft list ruleset | grep to-server:

ip6 daddr & ::ffff:255.255.255.255 == ::5 tcp dport 443 counter packets 4 bytes 320 jump accept_to_lan comment "!fw4: allow-ipv6-https-to-server"
ip6 daddr & ::ffff:255.255.255.255 == ::5 udp dport 443 counter packets 0 bytes 0 jump accept_to_lan comment "!fw4: allow-ipv6-https-to-server"
ip6 daddr & ::ffff:255.255.255.255 == ::5 tcp dport 80 counter packets 0 bytes 0 jump accept_to_lan comment "!fw4: allow-ipv6-http-to-server"
ip6 daddr & ::ffff:255.255.255.255 == ::5 udp dport 80 counter packets 0 bytes 0 jump accept_to_lan comment "!fw4: allow-ipv6-http-to-server"

I'm starting to get embarrassed because whenever I post first, my problem suddenly disappears.
I've tried unlied 1h and nothing came through.
Then just now again (after you wrote) - and tada! - it works all at once (as you can see in the logs).
For whatever reason.

So thank you for your time and sorry for hogging it like this.

1 Like

The above only covers the last 48 bits, is this intentional? I think it should be ::0:0:0:5/::ffff:ffff:ffff:ffff. Probably makes no difference right now as the additional bits are unused, but better be as explicit as possible.

Also in general I suggest to do an echo f > /proc/net/nf_conntrack to forcibly flush the conntrack table while testing firewall changes, just to rule out stale policies applied to yet-established flows.

1 Like

Yes, that was actually intentional on my part. I had at first ::5/::ffff standing there.
Then I thought - ok - two more zero groups would not be wrong for the unambiguity.
But yes, of course you are absolutely right to define the full 64 bits. - i will do so.

Hm maybe this causes the delayed solution.

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.