opnsense / core

OPNsense GUI, API and systems backend
https://opnsense.org/
BSD 2-Clause "Simplified" License
3.36k stars 753 forks source link

NPTv6: Use an internally delegated prefix as the external prefix (following 'Track Interface' feature) #6158

Closed martingruening closed 10 months ago

martingruening commented 1 year ago

Important notices

Before you add a new report, we ask you kindly to acknowledge the following:

Is your feature request related to a problem? Please describe.

When using NPTv6 and leaving the external prefix field empty the NPTv6 mechanism uses IPv6 address of the interface instead of the delegated prefix.

Describe the solution you like Use the delegated prefix of the interface (like 'Track interface' would do for an interface)

**A clear and concise description of what you want to happen. When the external prefix field is left empty for NPTv6 use the delegated prefix of the interface

fichtner commented 1 year ago

I know what you mean but the request is a little difficult to digest. The feature itself only exists since 22.7.8 and uses the address of the interface selected (most likely WAN).

As said elsewhere we can make this more complex, but I want to see if the current approach is stable enough. We don’t have much data yet.

fichtner commented 1 year ago

5284

martingruening commented 1 year ago

Ok. When entering the delegated prefix explicitly into the field this works super stable for me - but I have to change the setting manually every 24 hours.

jasonistre commented 1 year ago

NTPv6 currently does not work when prefix field is empty. I must manually find the delegated prefix from the overview section for WAN and paste that into the NTPv6 prefix field everytime the ISP decides to change it.

fichtner commented 1 year ago

NTPv6 currently does not work when prefix field is empty.

I would be careful with these false statements. You just don't get the PD, but you get the prefix of the WAN interface unless you don't have one at all there.

Generally #5284 seems to be a success despite the complains it won't do what people expect. ;)

It made sense as a first step to use the WAN prefix. There is nothing wrong with using the WAN prefix for NPT. Changing the expectation that it should use something else not present on WAN when in reality you actually use the WAN interface for the rule as an explicit selection is difficult and the problem modelling at least from this issue is nonexistent.

I know what to do, but I would hope requests in the future will have more technical depth than just "it doesn't work make it do something else".

Cheers, Franco

martingruening commented 1 year ago

Franco, I highly appreciate your work here. The NPTv6 feature as implemented in 22.7.8 works like a charm over the last weeks here. No problem whatsoever. If any further technical depth is required I am happy to help to get to the right point (I see that you are not quoting me, but I am happy to give any support I can give).

fichtner commented 1 year ago

@martingruening sure, I've decided to make your ticket the feature ticket for the actual issue: we are missing an interface-type selection for which prefix to use. In that case we don't have to deal with using a prefix length detection because if we can say translate using the current prefix /64 of the LAN interface instead of WAN that would fix the issue. You just have to add one NPT per internal tracking or fudge the prefix length if you hand part of it off to other devices (which is manual anyway).

jasonistre commented 1 year ago

I would be careful with these false statements. You just don't get the PD, but you get the prefix of the WAN interface unless you don't have one at all there.

Sorry if I offended. Please educate me. When the ISP assigns a 2001:x:x:x:x:x:x:x/128 WAN address how does NTPv6 auto detect the prefix and do successful translation?

fichtner commented 1 year ago

The ISP will always assign at least a /64. You can't use anything bigger than /64 here since the WAN is probably not on your PD so the ISP will not allow NPT prefix rewrite greater than /64 in this case.

markusj commented 1 year ago

It made sense as a first step to use the WAN prefix. There is nothing wrong with using the WAN prefix for NPT. Changing the expectation that it should use something else not present on WAN when in reality you actually use the WAN interface for the rule as an explicit selection is difficult and the problem modelling at least from this issue is nonexistent.

Well, at least for my ISP (Vodafone Germany, cable internet access), it is wrong. The assigned WAN address assigned via DHCPv6 is disjoint from the delegated prefix. Thus, the mapped addresses are invalid and packets are either discarded by the ISP or not routed back. Some kind of tracking of the delegated prefix is necessary for such ISP setups to make NPTv6 work at all.

jasonistre commented 1 year ago

Well, at least for my ISP (Vodafone Germany, cable internet access), it is wrong. The assigned WAN address assigned via DHCPv6 is disjoint from the delegated prefix. Thus, the mapped addresses are invalid and packets are either discarded by the ISP or not routed back. Some kind of tracking of the delegated prefix is necessary for such ISP setups to make NPTv6 work at all.

Same for my ISP, xfinity in Texas, also cable.

fichtner commented 1 year ago

Well, at least for my ISP (Vodafone Germany, cable internet access), it is wrong. The assigned WAN address assigned via DHCPv6 is disjoint from the delegated prefix. Thus, the mapped addresses are invalid and packets are either discarded by the ISP or not routed back. Some kind of tracking of the delegated prefix is necessary for such ISP setups to make NPTv6 work at all.

That would NOT be true for using /64 as the network size to run NPT on. The ISP cannot discard /64 packets from your WAN...

markusj commented 1 year ago

That would NOT be true for using /64 as the network size to run NPT on. The ISP cannot discard /64 packets from your WAN...

Via DHCPv6 IA_NA only a /128 address is assigned to the WAN interface. The /64 subnet is managed and owned by my ISPs DHCPv6 server. It is wrong to presume one could offhandedly allocate/use any other address within this subnet.

The ISP can legitimately discard any packet that is neither designated to my delegated prefix nor the WAN /128 address (except multicast, but that is another story).

fichtner commented 1 year ago

Sure, when ISPs think a /64 with individual /128 makes sense stuffing stuff all customers in there they should do that. IA_NA is not of much concern. It's neither offered by saner IPv6 implementations nor is it of much use in a firewall/router and has been known to break connectivity in some cases.

IPv6 will always require you to fine tune it and people will continue to derail discussions just because they think all should work out of the box all the time. This will also be true in terms of solving NPT with dynamic prefixes (which ISPs are also to blame for) which will never be an automatic feature.

Cheers, Franco

fichtner commented 1 year ago

Sorry, doesn't fit 23.1 timeframe anymore.

marcusgrando commented 1 year ago

Hi guys. Is anything new on this? I started to use OPNSense and have this problem with my case (load-balancing WAN with IPv6).

fichtner commented 1 year ago

I have this in my queue, but it doesn’t have a high priority considering recent work on IPv6 connectivity being more fundamental and useful to user base at large.

odin81 commented 1 year ago

I'm also looking out for this feature. I have to change my NATv6 every time my internet connection gets reconnected. I installed 23.7 today and found out that the feature sadly moved to the next release. Is there maybe a way to solve this as a workaround by script?

martingruening commented 1 year ago

Is there maybe a way to solve this as a workaround by script?

I haven't found one yet.

c-vo commented 1 year ago

+1. I am also looking for this feature.

kathampy commented 1 year ago

I too need NPTv6 to use the delegated prefix. The WAN address is a /128 on a different subnet which is not usable. I also do not use track interface on LAN as OPNsense is connected to an L3 switch via a transit VLAN with no other hosts. I have only a ULA on the LAN interface for management access.

TheNightmanX commented 1 year ago

I would love to have that feature, too. I would then change to all ULA in my privat net. Prefix delegation changes often and result in distored ipv6 communication due to local clients using outdated ipv6 addresses.

c-vo commented 1 year ago

I would love to have that feature, too. I would then change to all ULA in my privat net. Prefix delegation changes often and result in distored ipv6 communication due to local clients using outdated ipv6 addresses.

I had similar problems with the annoying dynamic IP address assignments. But you should not use ULA. ULA is broken. For example, see these articles: https://forum.opnsense.org/index.php?topic=33902.0 or https://blog.ipspace.net/2022/05/ipv6-ula-made-useless.html

s-bauer commented 1 year ago

I'm wanting to switch from PFSense to OPNSense. PFSense added NPt support with the ability to track the interface prefix recently. This is pretty much a deal breaker for me. In my network, I want/need static IPv6 addresses and properly configurable NPt that works with dynamic prefixes (my ISP provides me a /56 prefix. I want to use a /64 section of that for NPt. In PFSense that is possible.)

phi2lIP commented 1 year ago

Another vote from my side for this feature! If you use Multi-WAN setups, NPTv6 currently seems to be the only valid option to run IPv6 in LAN and map it to dynamically delegated prefixes.

djrarky commented 12 months ago

Another vote here too.

I'm behind CGNAT but have native IPv6. The IPv6 delegation changes every couple of weeks - between this, and the inability to use DHCPv6 with a tracked interface, I can't open ports to any services. It's a necessity, so I'm gonna move back to pfsense till this is resolved :(

Lindenk commented 10 months ago

Another +1. This issue seems mislabeled as feature when it should be bug. The tooltip states tracking the external prefix by default is the expected behavior

Although this might be a seperate issue, regular NAT with ipv6 also has this issue (ie. setting the translation target to interface address silently fails to match anything)

fichtner commented 10 months ago

It’s tracking the assigned WAN interface prefix, which is always a /64. The delegated prefix is not assigned and this is what makes this a missing feature.

djrarky commented 10 months ago

I don't understand why you keep saying it's always a /64. As l and others have said on this thread and others, that isn't always the case. It's always ISP dependent. I have a /128 on my WAN address just like others.

fichtner commented 10 months ago

Sure, here is the core of the issue. There are only up to 5 people who can contribute meaningful input to this topic. From those 5 people just 3 work on patches as time permits. The topic isn’t simple or else somebody would just solve this with an appropriate patch, but in the end the work is pretty hard and challenging and also hard to understand.

People could also write firewall plugins fixing this but they don’t, because the topic is non-trivial.

That being said it’s not on my priorities list also for lack of business use cases.

Cheers, Franco

djrarky commented 10 months ago

No worries!

Do greatly appreciate the work your team does, but sadly it's hindering my migration from pfsense and god awful Netgate :(

As an msp it's best to keep things uniform across clients

fichtner commented 10 months ago

We will be discussing this topic again on Wednesday. The original plan was to replace the page with MVC/API while working on this but that’s likely not on the list for 24.1 anymore. Given the feedback maybe we can move forward with a compromise.

Cheers, Franco

Kariton commented 10 months ago

looks like it was the right time to add IPv6 GUA to my networks. :)

in my case i get a /128 on WAN and the dynamic /56 PD. those are not part of the same subnet.

i have configured ULAs for everything to be independent from the changing PD. (like others have too)

on LAN (and others) i can use the "Track Interface" option to make IPv6 GUA available for other clients. additionally i use ULAs. works like a charm. ULA access across networks is working as expected, GUA also... exept: WireGuard clients

my WireGuard clients are also configured with ULA - which does work. for obvious reasons i cannot assign GUA addresses there.

NPTv6 does work for those - if i add the prefix to the rule. i understand why the autodiscovery is not working; was well explained in this thread.

i also would love to see the same "track interface" behavior from interface dhcp because this would make it easy to have a dedicated dynamic GUA subnet per NTPv6 rule.

in the meantime i have to stick to updating the NTPv6 prefix manually or try good'ol NAT.

Kariton commented 10 months ago

well - im lazy so i hacked a python script together that would do what i need. runs as cron job every minute.

this is a very dirty hack! its writing changes directly to the config file.

side effects are possible and it is only tested twice!

this script is predestined for race conditions when opnsense itself writes to the configuration file. changes are not tracked by the firewall itself.

/usr/local/opnsense/scripts/system/nptupdate.py

#!/usr/local/bin/python3

import os
import sys
import ipaddress
import xml.etree.ElementTree as ET
from datetime import datetime, timedelta
from netaddr import IPNetwork

def is_file_older_than(file, delta):
    cutoff = datetime.utcnow() - timedelta(minutes=delta)
    mtime = datetime.utcfromtimestamp(os.path.getmtime(file))
    if mtime < cutoff:
        return True
    return False

def get_wan_prefix_from_file(file_path):
    # Read the WAN interface prefix from the specified file
    try:
        with open(file_path, 'r') as file:
            wan_prefix = file.readline().strip()
        return wan_prefix
    except FileNotFoundError:
        raise FileNotFoundError(f"Error: File '{file_path}' not found.")

def calculate_new_destination_subnet(wan_prefix, prefix_size, prefix_id):
    # Use netaddr to manipulate IPv6 addresses
    try:
        new_subnet = list(ipaddress.ip_network(wan_prefix).subnets(new_prefix=prefix_size))[prefix_id]
        return new_subnet
    except ValueError as e:
        raise ValueError(f"Error: {e}")

def update_xml(xml_file_path, new_destination_subnet, npt_description):
    # Parse the XML file
    try:
        tree = ET.parse(xml_file_path)
    except ET.ParseError:
        raise ET.ParseError(f"Error: Failed to parse XML file '{xml_file_path}'.")

    root = tree.getroot()

    # Find the specific XML block to update by description
    npt_element = root.find(f".//npt[descr='{npt_description}']")
    if npt_element is not None:
        # Update the destination/address element
        destination_element = npt_element.find(".//destination/address")
        if destination_element is not None:
            current_destination_value = destination_element.text
            if current_destination_value != str(new_destination_subnet):
                destination_element.text = str(new_destination_subnet)
            else:
                print("Destination value is already present. No update needed.")
                return
        else:
            raise ValueError("Error: Couldn't find destination/address element.")
    else:
        raise ValueError(f"Error: Couldn't find npt element with descr='{npt_description}'.")

    # Save the modified XML back to the file
    try:
        tree.write(xml_file_path)
    except PermissionError:
        raise PermissionError(f"Error: Permission denied. Unable to write to '{xml_file_path}'.")

if __name__ == "__main__":
    script_name = os.path.basename(__file__)

    if len(sys.argv) != 6:
        print(f"Usage: {script_name} <offset> <interface> <prefix_size> <prefix_id> <npt_description>")
        sys.exit(1)

    offset = int(sys.argv[1])
    interface = sys.argv[2]
    prefix_size = int(sys.argv[3])
    prefix_id = int(sys.argv[4])
    npt_description = sys.argv[5]

    config_xml_path = '/conf/config.xml'
    interface_prefix_file = f'/tmp/{interface}_prefixv6'

    try:
        if not is_file_older_than(interface_prefix_file, offset):
            # Read WAN interface prefix from file
            wan_prefix = get_wan_prefix_from_file(interface_prefix_file)

            new_destination_subnet = calculate_new_destination_subnet(wan_prefix, prefix_size, prefix_id)

            update_xml(config_xml_path, new_destination_subnet, npt_description)

            print("Config updated successfully. Triggering 'configctl filter reload' now...")

            # Run 'configctl filter reload'
            os.system('configctl filter reload')

    except Exception as e:
        print(f"Error: {e}")

make it executable: chmod +x /usr/local/opnsense/scripts/system/nptupdate.py

/usr/local/opnsense/service/conf/actions.d/actions_nptupdate.conf

[renew]
command:/usr/local/opnsense/scripts/system/nptupdate.py
parameters:%s %s %s %s %s
type:script
message:Renew NPTv6 external prefix
description:Renew NPTv6 external prefix

dont forget to restart configd after adding those files: service configd restart

then create a cron job System/Settings/Cron:

'*' everything (for every minute) Parameters: 2 igb0 64 128 wg0mobile

The Parameters:

  1. offset = how new the PD must be in minutes - does nothing if file (modify) is older then X
  2. interface = which interface does have the PD
  3. prefix_size = desired external prefix size
  4. prefix_id = the Xth available subnet to use; its creating a list of possible networks for the "prefix_size" and picks the list index: 0 for the first,1 for the second and so on
  5. npt_description = the 'Description' from the NPTv6 rule (im not sure if config index is identical to the id)

how it works: the script

Kariton commented 10 months ago

@fichtner, i see that you are already working on a PoC. 😎

while you are at it: i can actually request only a PD (/56) without ever getting a interface ip (/128) for my wan interface. maybe this is an useful information and could be reflected in the upcoming solution. :)

with this my opnsense does only have ipv4 but clients do work with ipv6

fichtner commented 10 months ago

@Kariton aiming for a simple resolution now. the topic is a bit elusive and the NPT destination field was a bit buggy probably from too much copy+paste in the past.

The /128 is another matter more on the DHCPv6 client side (dhcp6). It's probably an ia-na, which you can disable by only requesting a prefix, but some ISPs don't like that either so in some cases you're stuck.

Cheers, Franco

fichtner commented 10 months ago

The test patch for 23.7.10 is https://github.com/opnsense/core/commit/ba952b85544

# opnsense-patch ba952b85544

If anybody could confirm this works that would be nice. The static page is probably not going to be in 24.1 anymore since we want to move ahead with #6383 as well.

Cheers, Franco

Kariton commented 10 months ago

wow. that was way faster then i expected. :)

first thought's:

linebreak. :D the help text shifts the entire layout. (1080p display)

<html>
<body>
<!--StartFragment-->
Enter the external IPv6 prefix for this network prefix translation. Leave empty to auto-detect the prefix address using the specified tracking interface instead. The prefix size specified for the internal prefix will also be applied to the external prefix.
--
 

<br class="Apple-interchange-newline"><!--EndFragment-->
</body>
</html>

if i try to select the same interface for external prefix as the interface itself it throws an error. (yes, i have read the grey "Default to rule interface" info text in that field)

The following input errors were detected:

Destination interface wan is not tracking the current rule interface.

Maybe it could just accept the fact that i dont like defaults.

anyway, after correctly defining the rule it does not work for me. will see if i can see what the issue is. :)

Kariton commented 10 months ago

so the binat rule is created and the firewall logs shows them as "binat rule" on hit. but the ./rules.debug does not look right.

# grep -i 'fd80:0:0:10ff::/64' ./rules.debug
binat log on igb0 inet6 from fd80:0:0:10ff::/64 -> (igb0:0)/64 # wg0mobile
fichtner commented 10 months ago

The rule looks ok to me. That’s the actual magic involved here in pf.conf. The WAN default is there because that’s the interface you already selected anyway. In terms of legacy config storage this doesn’t over complicate things and going forward we can be more flexible also with the GUI since the static PHP page with the weird help text spreading is not going to be in 24.1 as stated previously. 😊

Kariton commented 10 months ago

when the rule should look like this then i have to sniff a bit around to see what actually happens.

another thing: "IPv6 Prefix ID", like on track interfaces, is highly appreciated. i would like to define the used subnet.

fichtner commented 10 months ago

The result is the equivalent of the tracked /64 network on the selected interface. But that was the case before as well when left empty, except that you could not switch the /64 to the tracking interface on the LAN side, which is the easier equivalent of the prefix ID selected there. We’re cutting out the middle man here and go straight for the assigned network currently set on the respective interface.

Kariton commented 10 months ago

The result is the equivalent of the tracked /64 network on the selected interface. But that was the case before as well when left empty [...]

yes. i just thought you could sneak this improvement in as well. 😄 is it worth an additional Issue/FR?

after some tests: when i define the prefix it does work, when i use the tracking feature it uses the WAN ULA as prefix. i have an ULA there because i am cursed to use a modem and have configured that with ULA too.

BUT: removing that ULA results in a correct 1:1 translation!

fichtner commented 10 months ago

Why do you say “WAN”? That part is working since 2022 and yes pf will use the first address on the interface and if it’s ULA it won’t be ignored. Theoretically this could be improved in the kernel long-term.

But isn’t this about a “LAN” here since request only a prefix doesn’t even yield a GUA WAN address in the first place which is the original complaint?

Cheers, Franco

Kariton commented 10 months ago

Now i'm unsure if i have misunderstood something or badly explained my deployment.

i use dhcpv6 on WAN, get a prefix delegation and want to translate my VPN ULA network to use that prefix to connect to the WWW.

the WAN interface does have a PD (/56) and a host ip GUA (/128) and a LL (/64). additionally i have to use a modem where i have had an additional LUA (/64) to access the webinterface and monitor it. (which the kernel prefers and i hence removed).

due to the nature of wireguard and dynamic GUA i assigned ULAs to the opnsense wg interface and wg clients.

then i created a NPT rule:

now my wg clients do pass those ipv6 test websites.

previously i had to define the external prefix - because the host /128 is obviously not usable.

for all other interfaces where i use "Track interface", for example the LAN interface (RA and DHCPv6 configured for those), everything works flawless. as well as some VPS VPN Tunnels where i have to NAT my IPv6 traffic into.

i hope this explains it better and clarifies the scenario.

fichtner commented 10 months ago

Hmm, so "(Default to rule interface)" already worked prior to these patches -- you just needed to leave the external prefix field empty. That's more or less the same now.

The feature here revolves around the fact that WAN doesn't assign its delegated prefix to itself for technical limitations, which is why you want a part of the prefix to route, but it's the prefix of a "tracking" LAN interface instead (where you said you wanted prefix ID selection that's exactly what that interface selector is now including the validation that you can only select a tracked interface of the main rule interface).

In your example the /128 probably fudges a prefix that lthat is /64 so it works eventually but that is sometimes not even in your delegated prefix.

So instead of using the "(Default to rule interface)" you actually select a tracking LAN interface and it should work without ULA stripping given that there is no ULA on that particular LAN. If you need ULA internally as well it would make sense to add a new LAN that only tracks the WAN interface and is used as a "mock" prefix for the NPT.

Cheers, Franco

Kariton commented 10 months ago

Hmm, so "(Default to rule interface)" already worked prior to these patches -- you just needed to leave the external prefix field empty. That's more or less the same now.

Interesting. This is absolutely not the case on my side. In case my rule was indeed defined as intended / expected it did not work. But WITH your patch it does.

For now NPT rules are working as i expected them to. (without the ability to define the used subnet itself)

The feature here revolves around the fact that WAN doesn't assign its delegated prefix to itself for technical limitations [...]

I assumed something different based on the initial description and what others have said. Maybe some of those made the same mistake.

But after a quick google i found this: https://forum.opnsense.org/index.php?topic=28171.0 That's definitely not what i mean but sounds like what you are talking about.

[...] add a new LAN that only tracks the WAN interface and is used as a "mock" prefix for the NPT.

That sounds like a cool idea and might work.

What is the right way to create such a "mock" interface? Quickly tried loopback but this did not work - it didn't get a IPv6 when set to "Track interface". Is a vlan interface necessary?

fichtner commented 10 months ago

But WITH your patch it does.

Unlikely looking at your previous dump: binat log on igb0 inet6 from fd80:0:0:10ff::/64 -> (igb0:0)/64 # wg0mobile

igb0 -> igb0 so always WAN which means empty external prefix as per description in plain 23.7.10 :)

You can try to revert the code and see:

# opnsense-revert opnsense

Maybe the ULA got in the way previously?

What is the right way to create such a "mock" interface?

Well first things first you probably have at least one LAN tracking your WAN... I'd suggest using that and see what happens (with the patch applied).

Cheers, Franco