unifios-utilities icon indicating copy to clipboard operation
unifios-utilities copied to clipboard

ipv6 ULA Support via boot script

Open helbgd opened this issue 4 years ago • 22 comments

Hello, would it anyhow possible to add ipv6 ULA support to the UDM ?

The Problem is I like to provide a pihole container trough podman in a mgmt Network that is reachable via all VLAN separated networks.

My ISP changes my ipv6 prefix from time to time and therefore a fix configured ULA would help me in assigning this ipv6 IP of the pihole to my end client's via SLAAC from the UDM ...

I think the script should manually assign all bridged vlan interfaces on the UDM a own specified ipv6 ULA address then enable SLAAC Service to provide additional ULA adsresses to end clients in the VLAN's

Does anyone have a comment on this ?

the counterpart on the USG can be found under following topic: https://community.ui.com/questions/IPv6-Have-USG-assign-Unique-Local-Address-through-Router-Advertisement/e516521b-9f40-4a00-a0b6-60311790276d#comment/5cb60cbc-7bb4-46cd-a284-1d52db8b1173

helbgd avatar Jan 22 '21 15:01 helbgd

I use IPv6 ULAs in the boot script already. They work fine but there are some caveats.

  1. You have to manually assign the ULA prefix (fd00::1/64 for example) to the bridge interface first.
  2. SLAAC is enabled and clients automatically configure addresses with the ULA prefix as long as Router Advertisement and Prefix Delegation is enabled in the Unifi Network LAN settings for that VLAN.
  3. The Unifi OS automatically deletes the ULA prefix from the bridge interfaces every few hours because it reconfigures them in response to prefix and lease changes. This means that you need a watcher daemon that watches the interfaces for when the ULA prefix is deleted and readds it if needed.

(1) and (2) are easy to solve. For (3), solving it is as simple as using a shell script with an infinite loop that checks the interface every second and re-adds it it's missing, then running the script with nohup in the background. Alternatively, you can code a go program that watches the netlink messages for when the interface is changed and act on that message. I've done both solutions and both work.

peacey avatar Jan 22 '21 16:01 peacey

okay ...

what if you leave the ipv6 completely disabled via Unifi gui for the networks and assign a prefix assigned ipv6 gua and ula to each bridge interface manually via the init.d scripts ?

and write custom config files to /var/run/dnsmasq.conf.d/ to provide the slaac config by ourself ? i hope adding just a single file into that directory should not get deleted by the udapi server binary the dnsmasq thenn simply can get reloaded with kill -HUP and something like this as it reloads each config file in that directory ... as per

ps a |grep dnsmasq
 1447 root     grep dnsmasq
27465 nobody   /usr/sbin/dnsmasq --conf-dir=/run/dnsmasq.conf.d/

the other question: not sure how this is normally beeing done:

Under a linux router how is a gua based on the prefix from the wan interface be built and kept refreshed once it changes ?

I hope the udapi server binary does not touch this ipv6 on the bridge interface at all once it is disabled in the gui for that network

helbgd avatar Jan 22 '21 22:01 helbgd

# ps auxwww |grep dhcp6c
 2166 root     /usr/sbin/odhcp6c -e -v -s /usr/share/ubios-udapi-server/ubios-odhcp6c-script -P 58 eth4
 4643 root     grep dhcp6c
# 

bad this is done by /usr/share/ubios-udapi-server/ubios-odhcp6c-script wich is getting triggered once the prefix changes this is a binary so once we want to do this we have to rewrite that ubios-odhcp6c-script ourself in a shell script or simmilar ...

not sure if this is worth the effort

helbgd avatar Jan 22 '21 22:01 helbgd

I use IPv6 ULAs in the boot script already. They work fine but there are some caveats.

  1. You have to manually assign the ULA prefix (fd00::1/64 for example) to the bridge interface first.
  2. SLAAC is enabled and clients automatically configure addresses with the ULA prefix as long as Router Advertisement and Prefix Delegation is enabled in the Unifi Network LAN settings for that VLAN.
  3. The Unifi OS automatically deletes the ULA prefix from the bridge interfaces every few hours because it reconfigures them in response to prefix and lease changes. This means that you need a watcher daemon that watches the interfaces for when the ULA prefix is deleted and readds it if needed.

(1) and (2) are easy to solve. For (3), solving it is as simple as using a shell script with an infinite loop that checks the interface every second and re-adds it it's missing, then running the script with nohup in the background. Alternatively, you can code a go program that watches the netlink messages for when the interface is changed and act on that message. I've done both solutions and both work.

Do you have an example of the go and shell script ?

Thanks in advance

helbgd avatar Jan 23 '21 11:01 helbgd

Do you have an example of the go and shell script ?

Sure, here is a simple shell script that checks every second and re-adds the ULAs if they're missing. Rename it to ula-watcher.sh (could only upload .txt), chmod +x ula-watcher.sh, then run it in the background with nohup ./ula-watcher.sh > ula-watcher.log &

peacey avatar Jan 23 '21 23:01 peacey

@peacey @helbgd will you guys open a pull request and get this contributed to the repo?

boostchicken avatar Jan 30 '21 01:01 boostchicken

@peacey I have tested your script. It works great. It's a shame that the UDM isn't capable to do this from the UI... Is it the right way, to use boostchicken/on-boot-script for this? I just copied my ula-watcher.sh to '/mnt/data/on_boot.d'. It works fine, but maybe you tell us, how you would deploy it in production?

Menschomat avatar Feb 22 '21 14:02 Menschomat

@Menschomat, the script I wrote doesn't run in the background by default, so it will actually block other scripts from running if you use it as is in on_boot.d. You have to use the nohup ... & command to run it in the background.

I would place the ula-watcher.sh script in /mnt/data or /mnt/data/scripts (not in on_boot.d). Then make a boot script in /mnt/data/on_boot.d/run-ula-watcher.sh with these contents to run the script in the background:

#!/bin/sh
nohup /mnt/data/scripts/ula-watcher.sh > /mnt/data/scripts/ula-watcher.log &

Then run chmod +x on it. This way the script will run in the background. Confirm it is running in the background after you reboot by checking ps | grep ula-watcher.

The nohup is necessary to run things in the background because the on-boot-script opens an SSH connection to the UDM base OS to run the boot scripts, and after the SSH connection is closed, all programs, even those running in the background will be terminated because SIGHUP will be sent to the programs. To get around that, we use nohup which runs the command in the background and makes the program ignore SIGHUP so it doesn't get terminated after the on-boot-script SSH session closes.

peacey avatar Feb 22 '21 18:02 peacey

@peacey thank you very much for the fast response. This was very helpful 👍 It works like a charm.

Menschomat avatar Feb 22 '21 22:02 Menschomat

Worth noting that we really should be careful in assigning prefixes -- note the MUST NOT in https://tools.ietf.org/html/rfc4193#section-3.2. Don't use the same hard-coded prefixes as everyone else :).

There's a tool to generate prefixes using the recommended algorithm here: https://cd34.com/rfc4193/

andrewaylett avatar Feb 28 '21 10:02 andrewaylett

An alternative implementation:

In /mnt/data/ula/ensure_ula, ensure you set ULA_PREFIX using a value from https://cd34.com/rfc4193/ and you probably want to match the subnet numbers with any existing global subnets for each bridge interface rather than copying mine.

#!/bin/sh

ULA_PREFIX="fd60:e0ce:679d"

add_ula () {
  if [ -z "`ip address show dev $1 to $ULA_PREFIX::/48`" ]
  then
    ip address add $ULA_PREFIX:$2::1/64 dev $1
  fi
}

add_ula br0 0
add_ula br2 1
add_ula br3 2
add_ula br30 3
add_ula br40 4

In /mnt/data/on_boot.d/17-ula.sh, sets things up to run ensure_ula every minute:

#!/bin/sh

echo "* * * * * sh /mnt/data/ula/ensure_ula" > /etc/cron.d/ula

Ideally we'd have something that can run without needing to be edited -- a script that generates a ULA prefix if none exists, save it in a file that'll be kept, and examines existing IPv6 addresses if there's any assigned to see which subnet to use.

andrewaylett avatar Feb 28 '21 13:02 andrewaylett

Thanks for the script, but I need to bump this. Same issue here with German Telecom. They mostly offer PD with changing addresses unless you have a business account with fixed addresses. But still I would rather use NATv6 and do not assign a public addresses. ULA support is needed on all Ubiquiti Products.

Are you planning on implementing this into the udm-tools? Is there a way to help besides submitting a pull request and developing it?

ikkerus avatar Mar 30 '21 12:03 ikkerus

simple and dirty workaround if you want RA for a ULA prefix from dnsmasq enter following into on-state-change.sh after adding the ipv6 ula address to br0 device for example:

#!/bin/sh

ULA_PREFIX="fd60:e0ce:679d"
# on-state-change.sh 

add_ula () {
  if [ -z "`ip address show dev $1 to $ULA_PREFIX::/48`" ]
  then
    ip address add $ULA_PREFIX:$2::1/64 dev $1
  fi
}

add_ula br0 0
#add_ula br2 1
#add_ula br3 2
#add_ula br30 3
#add_ula br40 4

# only add entry to dnsmasq config if it does not exist
add_dnsmasq () {
 range=dhcp-range=set:ula6_$1,$ULA_PREFIX:$2::2,$ULA_PREFIX:$2::7d1,constructor:$1,64,86400
 conf=$(find /run/dnsmasq.conf.d/ -type f -name "*$1*IPV6.conf")
   if [ -z "`grep ula6_$1 $conf`" ]
   then
     echo $range >> $conf
     # reload dnsmasq config
     pkill -u 0 --signal HUP dnsmasq
   fi
}

add_dnsmasq br0 0


# the same can be done for ip6tables firewall rules:
ip6tables -S | grep custom-80-443-tcp || ip6tables -I UBIOS_WAN_IN_USER -d ::xx:xxxx:xxxx:x/::ffff:ffff:ffff:ffff -p tcp -m comment --comment custom-80-443-tcp -m multiport --dports 80,443 -j RETURN
ip6tables -S | grep custom-80-443-udp || ip6tables -I UBIOS_WAN_IN_USER -d ::xx:xxxx:xxxx:x/::ffff:ffff:ffff:ffff -p udp -m comment --comment custom-80-443-udp -m multiport --dports 80,443 -j RETURN
# the above for example expose port 443 and 80 tcp for example in your ipv6 subnet to internet even with changing prefixes from provider because the firewall entry matches only the host part of the ipv6 GUA (the part after /64) and the provider prefix part of the address can change at any time ::ffff:ffff:ffff:ffff hostmask for masking the IPv6 Address ensures exactly that
# notice the GUA of the host should be always the same as it is calculated via the MAC address of the host
# with the above in place you can fully utilize customize everything you want
# ip6tables -S gives the iptables output in a way you can feed it back via commands to ip6tables for example

helbgd avatar Nov 24 '22 09:11 helbgd

An alternative implementation:

In /mnt/data/ula/ensure_ula, ensure you set ULA_PREFIX using a value from https://cd34.com/rfc4193/ and you probably want to match the subnet numbers with any existing global subnets for each bridge interface rather than copying mine.

#!/bin/sh

ULA_PREFIX="fd60:e0ce:679d"

add_ula () {
  if [ -z "`ip address show dev $1 to $ULA_PREFIX::/48`" ]
  then
    ip address add $ULA_PREFIX:$2::1/64 dev $1
  fi
}

add_ula br0 0
add_ula br2 1
add_ula br3 2
add_ula br30 3
add_ula br40 4

In /mnt/data/on_boot.d/17-ula.sh, sets things up to run ensure_ula every minute:

#!/bin/sh

echo "* * * * * sh /mnt/data/ula/ensure_ula" > /etc/cron.d/ula

Ideally we'd have something that can run without needing to be edited -- a script that generates a ULA prefix if none exists, save it in a file that'll be kept, and examines existing IPv6 addresses if there's any assigned to see which subnet to use.

Works for me, Thanks!

xrh0905 avatar Dec 23 '22 07:12 xrh0905

I was able to use what @helbgd built on my UDM SE, with a couple of modifications.

  1. The subnet mask check for whether the IP was already assigned needed to be $ULA_PREFIX:$2::/64
  2. The persistent data is mounted on /data/ not /mnt/data
  3. I made it so it only restarted dnsmasq if it was required

Here's the new script that works on my Ubiquiti Dream Machine SE

#!/bin/bash

ULA_PREFIX="fded"

add_ula () {
  if [ -z "`ip address show dev $1 to $ULA_PREFIX:$2::/64`" ]
  then
    ip address add $ULA_PREFIX:$2::1/64 dev $1
  fi
}

add_ula br0 0
add_ula br2 2
add_ula br20 20
add_ula br100 100
add_ula br101 101
add_ula br102 102

ADDEDDNSMASQ=0

# only add entry to dnsmasq config if it does not exist
add_dnsmasq () {
  range=dhcp-range=set:ula6_$1,$ULA_PREFIX:$2::2,$ULA_PREFIX:$2::7d1,constructor:$1,64,86400
  conf=$(find /run/dnsmasq.conf.d/ -type f -name "*$1*IPV6.conf")
  if [ -z "`grep ula6_$1 $conf`" ]
  then
    echo $range >> $conf
    echo "Added $range"
    ADDEDDNSMASQ=1
    # reload dnsmasq config
    # pkill -u 0 --signal HUP dnsmasq
  fi
}

[ "$ADDDNSMASQ" == "1" ] && pkill -u 0 --signal HUP dnsmasq

Don't forget to chmod +x that script so it will work.

EdwardCooke avatar Dec 28 '23 01:12 EdwardCooke

The scripts here works great for setting up ULA, however, did anyone get reverse name lookups on the ULA ipv6 addresses working? I'm trying to overcome a "problem" where adguard won't properly assign client names when queries where sent from ipv6 addr

felipejfc avatar Apr 28 '24 15:04 felipejfc