opnsense / core

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

Allow an interface to track itself when getting a prefix via DHCPv6 #6233

Closed albertofustinoni closed 1 year ago

albertofustinoni commented 1 year ago

My ISP (Flet's Hikari by NTT in Japan) only hands out a /56 prefix, without assigning a separate IPv6 for the WAN interface. The ISP also only supports IPv4 connectivity via DS-Lite.

It would be really helpful if one could set up the WAN link to assign itself an IP and /64 subnet out of the /56 allocation, so that the WAN interface could be used as the parent interface for the GIF tunnel needed by DL-Lite.

As it is, I either have to use one of the LAN interfaces (not ideal since if that one interface goes down so does IPv4 connectivity for the whole network) or create a virtual loopback and manually assign an IP and subnet to it (also not good since a prefix change would break IPv4 connectivity).

For reference, OpenWRT has an option to do all this automatically when configuring the WAN for IPv6 prefix delegation.

OPNsense-bot commented 1 year ago

Thank you for creating an issue. Since the ticket doesn't seem to be using one of our templates, we're marking this issue as low priority until further notice.

For more information about the policies for this repository, please read https://github.com/opnsense/core/blob/master/CONTRIBUTING.md for further details.

The easiest option to gain traction is to close this ticket and open a new one using one of our templates.

fichtner commented 1 year ago

Hi @albertofustinoni,

At the moment there is nothing automatic about this. dhcp6c won't accept this configuration and it likely won't be fixed ever in its current state over at https://github.com/opnsense/dhcp6c

I did the POC in issue #5630 to select a prefix ID from WAN which is something that should likely be revived and I'm aware of Luke's pfSense script to assign an address but the current way of implementing it is not problem free in the code flow.

If you want to test your idea I'm happy to review via PR or send the pointer to Luke's work. At the moment I don't plan to work on it myself due to resource constraints.

Cheers, Franco

marcquark commented 1 year ago
#!/usr/local/bin/python3

import ipaddress
import socket
import subprocess
import sys

if __name__ == '__main__':
    if len(sys.argv) != 2:
        sys.exit("please provide exactly one argument which must be a valid interface name")
    ifname = sys.argv[1]

    os_interfaces = socket.if_nameindex()
    if not [ifname for os_interface in os_interfaces if ifname in os_interface]:
        sys.exit("interface not found, please supply a valid interface name")

    sp = subprocess.run(["/usr/local/sbin/ifctl","-6pi",ifname], capture_output=True, text=True)
    delegated_prefix = sp.stdout.strip()
    if not delegated_prefix:
        sys.exit(f"no delegated prefix found on {ifname}, aborting")
    prefix = ipaddress.IPv6Network(delegated_prefix)
    last_subnet = list(prefix.subnets(new_prefix=64))[-1]

    address_already_present = False
    sp = subprocess.run(["/sbin/ifconfig",ifname,"inet6"], capture_output=True, text=True)
    for line in sp.stdout.splitlines():
        if not "inet6" in line:
            continue

        address_string = line.strip().split(" ")[1]
        address = ipaddress.IPv6Address(address_string)
        if address.is_global:
            if  not address in last_subnet:
                subprocess.run(["/sbin/ifconfig",ifname,"inet6",str(address),"delete"])
            else:
                address_already_present = True

    if not address_already_present:
        pass
        subprocess.run(["/sbin/ifconfig",ifname,"inet6",str(last_subnet.network_address),"eui64"])

I've crafted a little helper script and did some manual testing. Certainly not perfect and may lead to weird behaviour depending on the exact setup. But for me it gets the job done; picks a /64 from the delegated prefix and assigns it to the WAN interface. I currently make the assumption that track interfaces start counting at 0 and go upwards, so the script is using the last available /64 in the delegated prefix. Most ISPs i know assign a /56 so unless you're already using all 256 /64s or you're specifically using the highest one, that won't be an issue. The fix would be to change the index in line 22 to whatever suits your environment.

Now, it seems that the Cron settings don't allow executing custom scripts, which i know is part of the philosophy of OPNsense. My idea is to tie this thing into IPv6 Gateway Monitoring failure. Just need to set the monitor IP to some GUA of choice and if it's no longer in "online" state, trigger the script because most likely the delegated prefix has expired. But i'm too newbish to craft an appropriate Monit rule and action. Is anybody able to point me in the right direction here?

/edit: I think i've managed to misuse Monit as a cronjob scheduler with the benefit of sending an alert should the script fail. I created a service with the following parameters: Name: pppoe0_gua Type: Custom Path: /root/gua_from_pd.py pppoe0 Tests: NonZeroStatus

Obviously /root/gua_from_pd.py is the above script. File must be executable. pppoe0 is my WAN interface that i need a GUA on. I'm going to keep this running for a couple of days to see how it works.

fichtner commented 1 year ago

There's 2 big issues and a smaller one here:

  1. The trigger. We would need dhcp6c to run a script whenever a PD is being delivered to weave this into the existing backend.
  2. The configuration. I did the POC and we should reserve a prefix ID (optionally) from WAN configuration in order for the script to kick in.
  3. The language (small). All the code lives in PHP and it would be a lot easier to integrate.

I'm not trying to derail this... just trying to point out how we can achieve this without dealing with repercussions later...

Cheers, Franco

marcquark commented 1 year ago

Hey @fichtner thanks for the feedback! I was contemplating these two big issues aswell when I read through POC issue you linked, aswell as some other issues on the subject. There was also a thread over at the Netgate forums which resulted in luckman212's script, that was my starting point, but i saw the opportunity to leverage OPNsense's ifctl utility to simplify the process

It seems complicated to add this functionality as a fail-safe feature with dhcp6c (i.e. to do it right). I tried messing with dhcp6c's script parameter but never got it to call any script i supplied. In the end, it may just not be worth it. As you already stated somewhere, not a priority and upstream changes are coming into FreeBSD. Plus also it's most likely only an issue for home users and possibly small businesses that may either not even notice (IPv6 for LAN devices works, after all), or can just use IPv4 as a fallback.

So my idea was to create this workaround and run it periodically using Cron (or Monit as i did now). Look at making it a full-blown feature again in a year or two. In the meantime, either put a section into the docs describing this workaround or, alternatively, a plugin. I could give it a try. Seeing some quick-win improvements aswell (configurable prefix ID, pass it as a parameter to the script, auto-create cronjob - boom it should even work with multiple connections simultaneously). Yes, there will be a gap between the time of the new prefix being received and the new GUA being set, but that will normally be during a pppoe reset after 24h, where a short hiccup is expected anyway.

What do you think?

OPNsense-bot commented 1 year ago

This issue has been automatically timed-out (after 180 days of inactivity).

For more information about the policies for this repository, please read https://github.com/opnsense/core/blob/master/CONTRIBUTING.md for further details.

If someone wants to step up and work on this issue, just let us know, so we can reopen the issue and assign an owner to it.

sage-132 commented 9 months ago

I just wanted to say I set this up on my opnsense with my fios connection and it works great. I'd definitely look into making this a plugin. I wish I had the experience. I do know a guy who could probably make it work. Would it be okay if we used your code as a base? @marcquark

fichtner commented 9 months ago

I might take another stab at this, but it's still not an ideal world given the constraints of dhcp6c (and lack of an upstream improving it).

sage-132 commented 9 months ago

Yeah Definitely. I mean I was looking at a plugin that just detects changes to ipv6 in maybe a more primitive way or does a more ddns type method where it just checks every 5 minutes or so.

On Tue, Feb 13, 2024 at 7:26 AM Franco Fichtner @.***> wrote:

I might take another stab at this, but it's still not an ideal world given the constraints of dhcp6c (and lack of an upstream improving it).

— Reply to this email directly, view it on GitHub https://github.com/opnsense/core/issues/6233#issuecomment-1941400845, or unsubscribe https://github.com/notifications/unsubscribe-auth/AQNZP7RXSLW663K765XL2GDYTNLXHAVCNFSM6AAAAAATUL45TSVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMYTSNBRGQYDAOBUGU . You are receiving this because you are subscribed to this thread.Message ID: @.***>

fichtner commented 9 months ago

There is netlink in FreeBSD 13.2 now so that might help listening for changes.

Cheers, Franco

marcquark commented 9 months ago

@sage-132 to answer your question, yes of course you or anybody else may go ahead and use the script above as a starting point for something better/improved