Open kubrickfr opened 2 years ago
If anybody is interested, I have turned it into a NetworkManager dispatch script.
Also, it uses the existing mullvadmangle6
table managed by the Mullvad daemon.
#!/usr/bin/env bash
set -e
[[ "$CONNECTION_ID" == "wg-mullvad" ]] || exit 0
if [ "$2" == "up" ]; then
# Collect data
LOCAL_IP=$(ip a show dev wg-mullvad | grep -o 'fc00[0-9a-f\:]*')
REMOTE_IP=$(curl --retry-all-errors --retry-max-time 19 --retry 10 -s https://ipv6.am.i.mullvad.net/) || echo "Error getting remote IP" | systemd-cat -p error -t dispatch_script
[[ "$REMOTE_IP" != "" ]] || exit 1
echo "Adding masquerade rules from $LOCAL_IP to $REMOTE_IP" | systemd-cat -p info -t dispatch_script
# Insert new rules
nft add rule ip6 mullvadmangle6 nat ip6 saddr $REMOTE_IP snat to $LOCAL_IP
nft add chain ip6 mullvadmangle6 nat-in { type nat hook prerouting priority 0 \; }
nft add rule ip6 mullvadmangle6 nat-in ip6 daddr $LOCAL_IP ct state new dnat to $REMOTE_IP
ip addr add $REMOTE_IP dev wg-mullvad
fi
If anybody is interested, I have turned it into a NetworkManager dispatch script.
Is there a way to trigger this dispatch script when connecting to a new mullvad endpoint through the app? It does work well when initially connecting but not when changing endpoints since the wg-mullvad link seems to stay up even when disconnected so NetworkManager-dispatch doesn't seem to get any kind of event.
I'm also on arch, using the beta version of Mullvad though, and I don't have this issue.
You are correct. There seems to a kernel netlink issue with 5.19.0-0.rc4 unrelated to this which prevents interfaces to be correctly removed on my device. Your script works great, thanks!
I just thought I'd post an up to date version of the dispatch script I'm using, which is compatible with the latest versions
#!/usr/bin/env bash
set -e
[[ "$CONNECTION_ID" == "wg0-mullvad" ]] || exit 0
if [ "$2" == "up" ]; then
# Collect data
LOCAL_IP=$(ip a show dev $CONNECTION_ID | grep -o 'fc00[0-9a-f\:]*')
REMOTE_IP=$(curl --retry-all-errors --retry-max-time 19 --retry 10 -s https://ipv6.am.i.mullvad.net/) || echo "Error getting remote IP" | systemd-cat -p error -t dispatch_script
[[ "$REMOTE_IP" != "" ]] || exit 1
echo "Adding masquerade rules from $LOCAL_IP to $REMOTE_IP" | systemd-cat -p info -t dispatch_script
# Insert new rules
nft add rule inet mullvad nat ip6 saddr $REMOTE_IP snat to $LOCAL_IP
nft add chain inet mullvad nat-in { type nat hook prerouting priority 0 \; }
nft add rule inet mullvad nat-in ip6 daddr $LOCAL_IP ct state new dnat to $REMOTE_IP
ip addr add $REMOTE_IP dev $CONNECTION_ID
fi
Is there any progress on this issue?
To be completely fair, it's not really a Mullvad issue, although it would be a good quality of life improvement if they did implement the proposed hack.
Hi, and thanks for you patience :yellow_heart:
We have not looked into implementing this yet, and I am unable to promise that we will do so in the foreseeable future. With that said, it is something that we probably want to do eventually, so we'll keep this issue open.
Although the Mullvad app is compatible with IPv6 on Linux, and can provide IPv6 inside the tunnels, it actually doesn't get used most of the time.
Mullvad provides IPv6 in the same way it provides IPv4: by providing a ULA in the range
fc00::/7
which is then NATed to a public IP on the Mullvad server.However, Linux implements RFC 3484 via the libc's
getaddrinfo()
, and is configured by default in/etc/gai.conf
to not return an IPv6 address if only ULAs are configured. Quoting the configuration file:One way to make use of the connectivity provided by Mullvad is to change this default configuration. However it is not sufficient as it only helps with programs using
getaddressinfo()
, other software, in particular those not using DNS, check that the address is global unicast themselves, like transmission or WebRTCAlso, very few users are going to change this obscure piece of configuration...
I have implemented a way that, although hacky, should be fairly reliable:
This scrip is my little PoC:
The only limitation that I am aware of, is that if a local client wanted to establish a connection to a service from another user using the same node this would fail. To fix it we would have to route traffic to
$LOCAL_IP
via the tunnel if it doesn't come from the tunnel.I think this should be fairly easy to implement inside the daemon considering we're already using nftables and doing IP detection there.