Last active
May 1, 2023 19:23
-
-
Save iam-TJ/135b47d29bd8ee4e0f3330aef73243d5 to your computer and use it in GitHub Desktop.
Easily set up IPv4 in IPv6 tunnel
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env sh | |
usage() | |
{ | |
cat <<-EOF | |
usage: ${0} [c[lient] | g[ateway]] [u[p] | d[own]] | |
Copyright 2023 Tj <hacker@iam.tj> | |
Licensed on the terms of the GNU General Public Licence version 3. | |
Create IPv4 in IPv6 tunnel on IPv6-only networks | |
Expects gateway to have an upstream IPv4 route and IPv6 internally | |
Expects client to be IPv6 only (but existing IPv4 does not stop this tunnel working) | |
Execute on both gateway and client(s) with appproriate overrides for the default values | |
which can be viewed at the top of this script. | |
Set variables in environment on gateway then "$0 g up" | |
Set variables in environment on client then "$0 c up" | |
Use the same values to drop the tunnel: "$0 g d" or "c d" | |
Recommendation: wrap calls to this script from your own script which sets your defaults | |
Defaults: | |
EOF | |
# shellcheck disable=SC2016 | |
head -n 55 "$0" | grep -E '^[^ ]*\${.*:-.*}' | |
} | |
# each of these variables can be overridden in the environment prior to calling this script | |
# so automation is possible via a parent script | |
# addresses for the tunnel only | |
ip4_prefix="${ip4_prefix:-24}" | |
ip4_net="${ip4_net:-10.9.8}" | |
ip4_gateway="${ip4_gateway:-${ip4_net}.1}" | |
ip4_client="${ip4_client:-${ip4_net}.7}" | |
# interface names on gateway | |
if_gw="${if_gw:-to_clients}" # tunnel | |
if_forward="${if_forward:-WAN}" # existing upstream link | |
# interface name on client(s) | |
if_tun="${if_tun:-to_gw}" # tunnel | |
# using ULA not publicly routable address for the tunnel | |
ip6_gateway="${gateway:-fddc:7e00:e001:ee00::5}" | |
# default client address here is very unlikely to be correct for your network client! | |
# ensure it is in the same subnet as your 'ip6_gateway' | |
# ensure each client gets a different address and that client address doesn't change due to privacy settings | |
# otherwise the tunnel will mysteriously stop working each time the kernel changes the privacy address! | |
ip6_client="${ip6_client:-fddc:7e00:e001:ee00:eb52:8b0a:9228:675f}" | |
# client end of IPv4 in IPv6 tunnel | |
client() | |
{ | |
far_ip6="${far_ip6:-$ip6_gateway}" | |
interface="${interface:-$(ip -6 route show | grep -Eo 'dev [^ ]*')}" | |
if [ -z "$interface" ]; then | |
>&2 echo "Error: interface is empty" | |
exit 1 | |
else | |
interface="${interface##* }" | |
fi | |
near_ip6="$(ip -6 addr show dev "${interface}" | grep -Eom 1 'inet6 [^/ ]*')" | |
if [ -z "$near_ip6" ]; then | |
>&2 echo "Error: no IPv6 address found on ${interface}" | |
exit 2 | |
else | |
near_ip6="${near_ip6##* }" | |
fi | |
case "$1" in | |
u*) | |
ip link add name "${if_tun}" type ip6tnl local "${near_ip6}" remote "${far_ip6}" mode ipip6 | |
ip link set up "${if_tun}" | |
ip -4 address add "${ip4_client}/${ip4_prefix}" dev "${if_tun}" | |
ip -4 route add default dev "${if_tun}" | |
;; | |
d*) | |
ip -4 route del default dev "${if_tun}" | |
ip -4 address del "${ip4_client}/${ip4_prefix}" dev "${if_tun}" | |
ip link set down "${if_tun}" | |
ip link delete "${if_tun}" type ip6tnl | |
;; | |
esac | |
} | |
# gateway for clients that also does PNAT masquerading | |
gateway() | |
{ | |
case "$1" in | |
u*) | |
ip link add name "${if_gw}" type ip6tnl local "${gateway}" remote "${ip6_client}" mode ipip6 | |
ip link set up "${if_gw}" | |
ip -4 addr add "${ip4_net}.1/${ip4_prefix}" dev "${if_gw}" | |
ip -4 route add "${ip4_net}.0/${ip4_prefix}" dev "${if_gw}" | |
iptables --insert FORWARD --out-interface "${if_forward}" --in-interface "${if_gw}" --match conntrack --ctstate NEW --jump ACCEPT | |
iptables --insert FORWARD --in-interface "${if_forward}" --match conntrack --ctstate ESTABLISHED,RELATED --jump ACCEPT | |
iptables --table nat --insert POSTROUTING --out-interface "${if_forward}" --jump MASQUERADE | |
;; | |
d*) | |
iptables --table nat --delete POSTROUTING --out-interface "${if_forward}" --jump MASQUERADE | |
iptables --delete FORWARD --in-interface "${if_forward}" --match conntrack --ctstate ESTABLISHED,RELATED --jump ACCEPT | |
iptables --delete FORWARD --out-interface "${if_forward}" --in-interface "${if_gw}" --match conntrack --ctstate NEW --jump ACCEPT | |
ip -4 route del "${ip4_net}.0/${ip4_prefix}" dev "${if_gw}" | |
ip -4 addr del "${ip4_net}.1/${ip4_prefix}" dev "${if_gw}" | |
ip link set down "${if_gw}" | |
ip link delete "${if_gw}" type ip6tnl | |
;; | |
esac | |
} | |
if [ -z "$2" ]; then | |
usage | |
exit 1 | |
fi | |
case "$1" in | |
c*) | |
client "$2" | |
;; | |
g*) | |
gateway "$2" | |
;; | |
*) | |
usage | |
;; | |
esac |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment