Created
April 22, 2020 12:06
-
-
Save PhrozenByte/51bfa358de8c9aec121f436db1b9ac77 to your computer and use it in GitHub Desktop.
Establishing an IPv6 tunnel using 6to4
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
#!/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