Skip to content

Instantly share code, notes, and snippets.

@iam-TJ
Last active May 1, 2023 19:23
Show Gist options
  • Save iam-TJ/135b47d29bd8ee4e0f3330aef73243d5 to your computer and use it in GitHub Desktop.
Save iam-TJ/135b47d29bd8ee4e0f3330aef73243d5 to your computer and use it in GitHub Desktop.
Easily set up IPv4 in IPv6 tunnel
#!/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