Skip to content

Instantly share code, notes, and snippets.

@PhrozenByte
Created April 22, 2020 12:06
Show Gist options
  • Save PhrozenByte/51bfa358de8c9aec121f436db1b9ac77 to your computer and use it in GitHub Desktop.
Save PhrozenByte/51bfa358de8c9aec121f436db1b9ac77 to your computer and use it in GitHub Desktop.
Establishing an IPv6 tunnel using 6to4
#!/bin/bash
# 6to4-daemon - Establishing an IPv6 tunnel using 6to4
# Copyright (C) 2011 Daniel Rudolf <http://www.daniel-rudolf.de/>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
function showHelp() {
echo "Usage: $0 (start|stop|reload|restart|status) device"
echo
echo "Available devices:"
IFS=$'\n'
for AVAILABLE_DEVICE in $(ls -w 1 "/sys/class/net"); do
echo -e "\t$AVAILABLE_DEVICE"
done
}
function log() {
NOW=$(date +"%F %T%z")
if [ "$1" == "notice" ]; then
echo "$NOW: $2" >> "$LOG_FILE"
echo "$NOW: $2"
elif [ "$1" == "error" ]; then
echo "$NOW: $2" >> "$LOG_FILE"
echo "$NOW: $2" >&2
else
echo "$NOW: $2" >> "$LOG_FILE"
fi
}
function createDaemonDirectories() {
# directory of PID files
if [ ! -d "$PID_DIRECTORY" ]; then
mkdir "$PID_DIRECTORY"
if [ ! -d "$PID_DIRECTORY" ]; then
echo "Unable to create '$PID_DIRECTORY': Permission denied" >&2
exit 1
fi
fi
if [ ! -w "$PID_DIRECTORY" ]; then
echo "Unable to write to '$PID_DIRECTORY': Permission denied" >&2
exit 1
fi
# directory of storage files
if [ ! -d "$STORAGE_DIRECTORY" ]; then
mkdir "$STORAGE_DIRECTORY"
if [ ! -d "$STORAGE_DIRECTORY" ]; then
echo "Unable to create '$STORAGE_DIRECTORY': Permission denied" >&2
exit 1
fi
fi
if [ ! -w "$STORAGE_DIRECTORY" ]; then
echo "Unable to write to '$STORAGE_DIRECTORY': Permission denied" >&2
exit 1
fi
# directory of log files
if [ ! -d "$LOG_DIRECTORY" ]; then
mkdir "$LOG_DIRECTORY"
if [ ! -d "$LOG_DIRECTORY" ]; then
echo "Unable to create '$LOG_DIRECTORY': Permission denied" >&2
exit 1
fi
fi
if [ ! -w "$LOG_DIRECTORY" ]; then
echo "Unable to write to '$LOG_DIRECTORY': Permission denied" >&2
exit 1
fi
}
function removeDaemonFiles() {
if [ -e "$PID_FILE" ]; then
rm -f "$PID_FILE"
fi
if [ -e "$STORAGE_FILE" ]; then
rm -f "$STORAGE_FILE"
fi
}
function createLogFile() {
if [ ! -f "$LOG_FILE" ]; then
touch "$LOG_FILE"
fi
}
function determineInternalIPv4Address() {
IP4_INTERNAL=$(/sbin/ip addr show "$DEVICE" | grep -e "^ inet " | cut -d " " -f 6)
IP4_INTERNAL_ADDRESS=$(echo "$IP4_INTERNAL" | cut -d "/" -f 1)
IP4_INTERNAL_NETMASK=$(echo "$IP4_INTERNAL" | cut -d "/" -f 2)
# check if IPv4 address is valid
if [[ ! "$IP4_INTERNAL_ADDRESS" =~ ^((([1-9]?|1[0-9]|2[0-4])[0-9]|25[0-5])\.){3}(([1-9]?|1[0-9]|2[0-4])[0-9]|25[0-5])$ ]]; then
log "error" "Unable to determine internal IPv4 address: '$IP4_INTERNAL_ADDRESS' is not a valid IPv4 address"
exit 1
fi
# check if IPv4 netmask is valid
if [[ ! "$IP4_INTERNAL_NETMASK" =~ ^([1-2]?[0-9]|3[0-2])$ ]]; then
log "error" "Unable to determine internal IPv4 address: '$IP4_INTERNAL_NETMASK' is not a valid IPv4 netmask"
exit 1
fi
if [ "$1" != "--quiet" ]; then
log "notice" "Internal IPv4 address determined: $IP4_INTERNAL"
fi
}
function determineGlobalIPv4Address() {
# check if the given internal IPv4 address is a global IPv4 address
if [ "${IP4_INTERNAL:0:3}" != "10." ] && [[ ! "$IP4_INTERNAL" =~ ^172\.(1[6-9]|2[0-9]|3[0-1])\. ]] && [ "${IP4_INTERNAL:0:8}" != "192.168." ]; then
log "notice" "Determined internal IPv4 address ($IP4_INTERNAL) is already a global address"
IP4_GLOBAL="$IP4_INTERNAL"
IP4_GLOBAL_ADDRESS="$IP4_INTERNAL_ADDRESS"
IP4_GLOBAL_NETMASK="$IP4_INTERNAL_NETMASK"
# determine global IPv4 address
else
i=1
while [ true ]; do
determineGlobalIPv4AddressDyndnsDotOrg
# no answer received
if [ -z "$IP4_GLOBAL_ADDRESS" ]; then
# try again up to 50 times...
if [ $i -le 50 ]; then
log "notice" "Unable to determine global IPv4 address: Try $i of 50"
sleep 3
((i++))
continue
# but give up after some time
else
log "error" "Unable to determine global IPv4 address: Giving up"
exit 1
fi
# we received something
else
if [[ "$IP4_GLOBAL_ADDRESS" =~ ^((([1-9]?|1[0-9]|2[0-4])[0-9]|25[0-5])\.){3}(([1-9]?|1[0-9]|2[0-4])[0-9]|25[0-5])$ ]]; then
IP4_GLOBAL_NETMASK="32"
IP4_GLOBAL="$IP4_GLOBAL_ADDRESS/$IP4_GLOBAL_NETMASK"
if [ "$1" != "--quiet" ] || [ $i != 1 ]; then
log "notice" "Global IPv4 address determined: $IP4_GLOBAL"
fi
break
# but not a valid IPv4 address
else
log "error" "Unable to determine global IPv4 address: '$IP4_GLOBAL_ADDRESS' is not a valid IPv4 address"
exit 1
fi
fi
done
fi
}
function determineGlobalIPv4AddressDyndnsDotOrg() {
IP4_GLOBAL_ADDRESS=$(wget -q -t 1 -O - http://checkip.dyndns.org/)
if [[ "$IP4_GLOBAL_ADDRESS" =~ \<body\>Current\ IP\ Address:\ (.*)\</body\> ]]; then
IP4_GLOBAL_ADDRESS="${BASH_REMATCH[1]}"
fi
}
function computeIPv6Address() {
IP6_GLOBAL_NETMASK="48"
IP6_GLOBAL_ADDRESS=$(printf "2002:%02x%02x:%02x%02x::1" $(echo "$IP4_GLOBAL_ADDRESS" | tr "." " "))
IP6_GLOBAL="$IP6_GLOBAL_ADDRESS/$IP6_GLOBAL_NETMASK"
log "notice" "Global IPv6 address computed: $IP6_GLOBAL"
}
function createVirtualTunnelDevice() {
# check if there is already a network device
if [ -e "/sys/class/net/$VIRTUAL_TUNNEL_DEVICE" ]; then
log "error" "Unable to create virtual 6to4 tunnel device: There is already a network device called '$VIRTUAL_TUNNEL_DEVICE'"
exit 1
fi
/sbin/ip tunnel add "$VIRTUAL_TUNNEL_DEVICE" mode sit ttl 200 remote any local "$IP4_INTERNAL_ADDRESS"
/sbin/ip link set dev "$VIRTUAL_TUNNEL_DEVICE" up
log "notice" "Virtual 6to4 tunnel device created"
}
function destroyVirtualTunnelDevice() {
/sbin/ip -6 addr flush dev "$VIRTUAL_TUNNEL_DEVICE"
/sbin/ip -6 route flush dev "$VIRTUAL_TUNNEL_DEVICE"
/sbin/ip link set dev "$VIRTUAL_TUNNEL_DEVICE" down
/sbin/ip tunnel del "$VIRTUAL_TUNNEL_DEVICE"
log "notice" "Virtual 6to4 tunnel device has been destroyed"
}
function routeIPv6ConnectionsThroughVirtualTunnelDevice() {
/sbin/ip -6 route add 2000::/3 via "::192.88.99.1" dev "$VIRTUAL_TUNNEL_DEVICE" metric 1
}
function addIPv6Address() {
/sbin/ip -6 addr add "$IP6_GLOBAL" dev "$VIRTUAL_TUNNEL_DEVICE"
}
function removeIPv6Address() {
/sbin/ip -6 addr del "$1" dev "$VIRTUAL_TUNNEL_DEVICE"
}
function storeData() {
echo $IP4_INTERNAL > "$STORAGE_FILE"
echo $IP4_GLOBAL >> "$STORAGE_FILE"
echo $IP6_GLOBAL >> "$STORAGE_FILE"
}
function getStoredData() {
# check if storage file exists
if [ ! -f "$STORAGE_FILE" ]; then
log "error" "Unable to read contents of storage file '$STORAGE_FILE': No such file or directory"
fi
# check if storage file is readable
if [ ! -r "$STORAGE_FILE" ]; then
log "error" "Unable to read contents of storage file '$STORAGE_FILE': Permission denied"
fi
OLD_IP4_INTERNAL=$(sed -n 1p "$STORAGE_FILE")
OLD_IP4_GLOBAL=$(sed -n 2p "$STORAGE_FILE")
OLD_IP6_GLOBAL=$(sed -n 3p "$STORAGE_FILE")
}
# parameter count
if [ $# != 2 ]; then
echo "Wrong parameter count" >&2
echo
showHelp
exit 1
fi
# root rights
if [ $(id -u) -ne 0 ]; then
echo "You must have root rights" >&2
exit 1
fi
DEVICE=$2
VIRTUAL_TUNNEL_DEVICE="$DEVICE-6to4"
# network device exists
if [ ! -e "/sys/class/net/$DEVICE" ]; then
echo "There is no network device called '$DEVICE'" >&2
exit 1
fi
PID_DIRECTORY="/var/run/6to4-daemon"
PID_FILE="$PID_DIRECTORY/$DEVICE"
STORAGE_DIRECTORY="/tmp/6to4-daemon"
STORAGE_FILE="$STORAGE_DIRECTORY/$DEVICE"
LOG_DIRECTORY="/var/log/6to4-daemon"
LOG_FILE="$LOG_DIRECTORY/$DEVICE"
case "$1" in
start)
if [ -f "$PID_FILE" ]; then
if (kill -0 $(cat "$PID_FILE") 2> /dev/null); then
echo "Daemon is already running" >&2
exit 1
else
echo "'$PID_FILE' found but daemon is not running." >&2
echo "Propably your previously started daemon crashed. Please view the logfile for details."
echo "If you want to start the daemon please try again."
rm -f "$PID_FILE"
exit 1
fi
fi
# check if network device is already using IPv6
if [ $(/sbin/ip addr show "$DEVICE" | grep -e "^ inet6 .* scope global" | wc -l) -gt 0 ]; then
echo "It seems that you are already using IPv6 on '$DEVICE'" >&2
exit 1
fi
createDaemonDirectories
createLogFile
log "notice" "Establishing 6to4 tunnel..."
determineInternalIPv4Address
determineGlobalIPv4Address
computeIPv6Address
createVirtualTunnelDevice
routeIPv6ConnectionsThroughVirtualTunnelDevice
addIPv6Address
storeData
log "notice" "6to4 tunnel established"
(
while [ true ]; do
getStoredData
determineInternalIPv4Address --quiet
# internal IPv4 address has been changed
if [ "$IP4_INTERNAL" != "$OLD_IP4_INTERNAL" ]; then
log "notice" "Internal IPv4 address has been changed: $IP4_INTERNAL"
determineGlobalIPv4Address
computeIPv6Address
destroyVirtualTunnelDevice
createVirtualTunnelDevice
routeIPv6ConnectionsThroughVirtualTunnelDevice
addIPv6Address
storeData
sleep 3
continue
fi
determineGlobalIPv4Address --quiet
# global IPv4 address has been changed
if [ "$IP4_GLOBAL" != "$OLD_IP4_GLOBAL" ]; then
log "notice" "Global IPv4 address has been changed: $IP4_GLOBAL"
computeIPv6Address
removeIPv6Address "$OLD_IP6_GLOBAL"
addIPv6Address
storeData
sleep 3
continue
fi
# nothing has been changed
sleep 3
done
) &
PID=$!
echo $PID > "$PID_FILE"
wait $PID
RETURN=$?
destroyVirtualTunnelDevice
removeDaemonFiles
exit $RETURN
;;
stop)
if [ -f "$PID_FILE" ]; then
PID=$(cat "$PID_FILE")
if (kill -0 $PID 2> /dev/null); then
log "notice" "Daemon shutdown requested..."
echo -n "Stopping daemon"
if (kill -TERM $PID 2> /dev/null); then
i=0
while [ "$i" -le 40 ]; do
if (kill -0 $PID 2> /dev/null); then
echo -n "."
let "i += 1"
sleep 0.25
else
break
fi
done
fi
if (kill -0 $PID 2> /dev/null); then
echo
echo "Daemon is unable to shut down cleanly - killing" >&2
kill -KILL $PID
exit 1
else
echo " done"
fi
else
echo "'$PID_FILE' found but daemon is not running." >&2
echo "Propably your previously started daemon crashed. Please view the logfile for details."
exit 1
fi
else
echo "Daemon is propably not running" >&2
exit 1
fi
;;
reload)
# check if daemon is running
if [ -f "$PID_FILE" ]; then
if (kill -0 $(cat "$PID_FILE") 2> /dev/null); then
log "notice" "Daemon reload requested..."
determineInternalIPv4Address
determineGlobalIPv4Address
computeIPv6Address
destroyVirtualTunnelDevice
createVirtualTunnelDevice
routeIPv6ConnectionsThroughVirtualTunnelDevice
addIPv6Address
storeData
log "notice" "6to4 tunnel established"
else
echo "'$PID_FILE' found but daemon is not running." >&2
echo "Propably your previously started daemon crashed. Please view the logfile for details."
exit 1
fi
else
echo "Daemon is propably not running" >&2
exit 1
fi
;;
restart)
$0 stop
if [ $? != 0 ]; then
exit $?
fi
$0 start
if [ $? != 0 ]; then
exit $?
fi
;;
status)
if [ -f "$PID_FILE" ]; then
if (kill -0 $(cat "$PID_FILE") 2> /dev/null); then
echo "Daemon is running"
else
echo "'$PID_FILE' found but daemon is not running."
echo "Propably your previously started daemon crashed. Please view the logfile for details."
fi
else
echo "Daemon is propably not running"
fi
;;
*)
showHelp
;;
esac
exit 0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment