Hi all,
So my provider finally moved to dynamic dual stack solution. That made HE.net tunnel obsolete, but also gave me some issues - like I don't have static routable IPv6 addressing. Here is the set-up only for IPv6. Some refinement needed but all bones are here.
Some issues/workarounds which exists:
a) 3 entrance to Dynamic DNS (here HE.net) - 1 IPv4 and 2 IPv6
1, MT does not have a dedicated resolve for IPv6, so if you have A and AAAA record pointing to same name MT will always choose IPv4
2. Same goes to i.e. WireGuard for iOS. Client will always choose IPv4 over IPv6. There is bug request to change it but it is there for few years and probably it will not be fixed soon
b) With HE.net you can have different "Update key" for A and AAAA record for the same name. Based on time spent on those scripts - make it the same. A and AAAA update keys should be the same
c) In main script I disable and enable IPv6 addressing - this is done because after reboot even if my code states which address goes first but I've seen that even thou i.e. wg0 is called 3rd it can take first prefix. That solves that, better solution would be going through some sort of array and be independent but....
d) Probably you can have a script which takes IP addresses from WireGuard peers and do automation - I am not a coder, I used to write some code like 25 years ago but...
Disclaimers:
I - Due to issue of dynamic addressing I will be using site-local addressing. If you don't know much about this - get familiar with RFCs: 6296, 7157
II - I am using 4 prefixes - 0 for WAN, 1 for LAN, 2 for wg0 (family interface with full access to network), 3 for wg1 (friends access - private VPN)
III - each interface (less WAN) gets site-local addressing - in LAN I am using this for Cisco WLC or different UPSes I have
IV - main script limits interactions with HE.net to necessary minimums. Hence it will check by itself if address needs updating and if it needs it will validate that it was successful.
V - my global prefixes are sequential - it would be awesome if there is a mechanism so I could change assigned prefixes to each interface. Any ideas?
Addressing
/ipv6 address add address=::1 advertise=no from-pool=ISP-v6 interface=WAN
/ipv6 address add address=::1 from-pool=ISP-v6 interface=LAN
/ipv6 address add address=fdc0:1::1 interface=LAN
/ipv6 address add address=::1 from-pool=ISP-v6 interface=wg0
/ipv6 address add address=fdc0:2::1 interface=wg0
/ipv6 address add address=::1 from-pool=ISP-v6 interface=wg1
/ipv6 address add address=fdc0:2::1 interface=wg1
Firewall Filter
/ipv6 firewall address-list add address=fdc0:1::::/64 comment="LAN interface" list="Allowed Site-local addresses"
/ipv6 firewall address-list add address=fdc0:2::::/64 comment="wg0" list="Allowed Site-local addresses"
/ipv6 firewall address-list add address=fdc0:3::::/64 comment="wg1" list="Allowed Site-local addresses"
/ipv6 firewall filter add action=accept chain=forward comment="Allow Site-Locals to Internet" out-interface=WAN src-address-list="Allowed Site-local addresses"
Now each device needs its own translation lines in Firewall NAT. Maybe someone knows how to automate this with the script. I did this manually (sample of the device from LAN)
/ipv6 firewall nat add action=src-nat chain=srcnat src-address=fdc0:1::220/128 to-address=2102:1010:a3ba:7501::220/128
/ipv6 firewall nat add action=dst-nat chain=dstnat dst-address=2102:1010:a3ba:7501::220/128 to-address=fdc0:1::220/128
A script runs every 1 minute to check for a change in either IP address. If it sees a change, it runs the next script:
/system scheduler add interval=1m name=IP-Check on-event=IP-Check policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon start-time=startup
/system script add dont-require-permissions=yes name=IP-Check owner=admin policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#Detele this and copy script via www"
Just copy-paste below script via web
# Check dynamic IPv4 and IPv6 addresses
:global currentIPv4;
:global currentIPv6;
:local WANiface WAN
:local newIPv4
:local newIPv6
:set newIPv4 [/ip address get [find interface="$WANiface"] address]
:set newIPv6 [/ipv6 address get [find interface="$WANiface" global] address]
# If IPv6 changed, then log change and execute HE-Update script
:if ($newIPv6 != $currentIPv6) do={
:log info ("New IPv6 detected on " . $WANiface . "!!! New address is: " . $newIPv6 . ". Running HE-Update script")
:set currentIPv6 $newIPv6
/system/script
run HE-Updater
};
# If IPv4 changed, then log change and execute HE-Update script
:if ($newIPv4 != $currentIPv4) do={
:log info ("New IPv4 detected on " . $WANiface . "!!! New address is: " . $newIPv4 . ". Running HE-Update script")
:set currentIPv4 $newIPv4
/system/script
run HE-Updater
};
And now - main script. Same as above
/system script add dont-require-permissions=yes name=HE-Updater owner=admin policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#Detele this and copy script via www"
Actual script:
# Update IPv6-NAT and Hurricane Electric DDNS IPv4 and IPv6 address
:local ddnshost1 "some address"
:local ddnshost2 "dedicated ipv6 address"
:local key1 "common key for common ipv4 and ipv6"
:local key2 "dedicated key for dedicated ipv6"
:local updatehost "dyn.dns.he.net"
:local updatepath "/nic/update"
:local WANinterface "WAN"
:local outputfile1 ("HE_DDNS_IPv4" . ".txt")
:local outputfile2 ("HE_DDNS_IPv6_Dedicated" . ".txt")
:local outputfile3 ("HE_DDNS_IPv6" . ".txt")
# Internal processing below...
# ----------------------------------
:local ipv4addr
:local ipv6addr
:local ipv6pref
:local ipv6post
:local ipv6postpref 0::1/64
:local HEaddress1
:local HEaddress2
/ip/dns/cache/flush
:set HEaddress1 [:resolve $ddnshost1]
:set HEaddress2 [:resolve $ddnshost2]
# Get WAN interface IPv4 and IPv6 addresses
:set ipv4addr [/ip address get [/ip address find interface=$WANinterface] address]
:set ipv4addr [:pick [:tostr $ipv4addr] 0 [:find [:tostr $ipv4addr] "/"]]
:set ipv6addr [/ipv6 address get [/ipv6 address find interface=$WANinterface global] address]
:set ipv6addr [:pick [:tostr $ipv6addr] 0 [:find [:tostr $ipv6addr] "/"]]
:if ([:len $ipv4addr] = 0) do={
:log error ("Could not get IPv4 for interface " . $WANinterface)
:error ("Could not get IPv4 for interface " . $WANinterface)
}
:if ([:len $ipv6addr] = 0) do={
:log error ("Could not get IPv6 for interface " . $WANinterface)
:error ("Could not get IPv6 for interface " . $WANinterface)
}
# Make sure that all prefixes are in the correct order (WAN=0, LAN=1, wg0=2, wg1=3) - important for Firewall NAT
:set ipv6post [:pick [:tostr $ipv6addr] 19 26]
:if ($ipv6post != $ipv6postpref) do={
/ipv6/address
disable [find interface="WAN" global]
disable [find interface="LAN" global]
disable [find interface="wg0" global]
disable [find interface="wg1" global]
/ipv6/dhcp-client
disable [find interface="WAN"]
:delay 1s
enable [find interface="WAN"]
:delay 1s
/ipv6/address
enable [find interface="WAN" global]
:delay 1s
enable [find interface="LAN" global]
:delay 1s
enable [find interface="wg0" global]
:delay 1s
enable [find interface="wg1" global]
/ip/dns/cache/flush
:set ipv6addr ::1/128
:set ipv6addr [/ipv6 address get [/ipv6 address find interface=$WANinterface global] address]
:set ipv6addr [:pick [:tostr $ipv6addr] 0 [:find [:tostr $ipv6addr] "/"]]
:if ([:len $ipv6addr] = 0) do={
:log error ("Could not get IPv6 for interface " . $WANinterface)
:error ("Could not get IPv6 for interface " . $WANinterface)
}
}
# Update IPv6-Firewall-NAT - each address has 2 entires, either do by type (like I did) or by address. This can be done better. One IP example for each prefix
:set ipv6pref [:pick [:tostr $ipv6addr] 0 18]
:if ($HEaddress2 != $ipv6addr) do={
/ipv6/firewall/nat
set 0 to-address=($ipv6pref . "1::5/128")
set 1 to-address=($ipv6pref . "2::80/128")
set 2 to-address=($ipv6pref . "3::200/128")
set 3 dst-address=($ipv6pref . "1::5/128")
set 4 dst-address=($ipv6pref . "2::80/128")
set 5 dst-address=($ipv6pref . "3::200/128")
}
# Check if IPv6 addresses need change - if yes than change; if IPv4 is also changed change it as well
:while ($HEaddress2 != $ipv6addr) do={
:log info ("Updating DDNS IPv6 dedicated address" . " Client IPv6 address to new IP " . $ipv6addr . "...")
/tool fetch mode=https host=($updatehost) url=("https://" . $updatehost . $updatepath . "?hostname=" . $ddnshost2 . "&myip=" . $ipv6addr) user=($ddnshost2) password=($key2) dst-path=$outputfile2
:log info ([/file get ($outputfile2) contents])
:delay 10s
:log info ("Updating DDNS IPv6 address" . " Client IPv6 address to new IP " . $ipv6addr . "...")
/tool fetch mode=https host=($updatehost) url=("https://" . $updatehost . $updatepath . "?hostname=" . $ddnshost1 . "&myip=" . $ipv6addr) user=($ddnshost1) password=($key1) dst-path=$outputfile3
:log info ([/file get ($outputfile3) contents])
:delay 10s
:if ($HEaddress1 != $ipv4addr) do={
:log info ("Updating DDNS IPv4 address" . " Client IPv4 address to new IP " . $ipv4addr . "...")
/tool fetch mode=https host=($updatehost) url=("https://" . $updatehost . $updatepath . "?hostname=" . $ddnshost1 . "&myip=" . $ipv4addr) user=($ddnshost1) password=($key1) dst-path=$outputfile1
:log info ([/file get ($outputfile1) contents])
};
:delay 310s
/ip/dns/cache/flush
:set HEaddress2 [:resolve $ddnshost2]
:set HEaddress1 [:resolve $ddnshost1]
};
:log info ("Client IPv6 address is " . $ipv6addr . ". Confirmed with HE.net DNS service")
:delay 5s
# Check if IPv4 address needs change - if yes than change
:while ($HEaddress1 != $ipv4addr) do={
:log info ("Updating DDNS IPv4 address" . " Client IPv4 address to new IP " . $ipv4addr . "...")
/tool fetch mode=https host=($updatehost) url=("https://" . $updatehost . $updatepath . "?hostname=" . $ddnshost1 . "&myip=" . $ipv4addr) user=($ddnshost1) password=($key1) dst-path=$outputfile1
:log info ([/file get ($outputfile1) contents])
:delay 310s
/ip/dns/cache/flush
:set HEaddress1 [:resolve $ddnshost1]
};
:log info ("Client IPv4 address is " . $ipv4addr . ". Confirmed with HE.net DNS service")
# :delay 10s
# /file remove ($outputfile2)
# /file remove ($outputfile3)
# /file remove ($outputfile1)
WG - client setup sample:
[Interface]
PrivateKey = [PRIVATE]
DNS = [MT IPv4 address for that Interface]
Address = 10.0.1.220/32,fdc0:1::220/128
[Peer]
PublicKey = WtXIFRTHmr8b0n7y5GuCAIZ98zIVf1hZwJs8RazQ/1A=
PresharedKey = [PRIVATE]
AllowedIPs = 0.0.0.0/0,::/0
Endpoint = somewhere.over.internet:52895
PersistentKeepalive = 25
Any thoughts?