#!/bin/sh
#
# network-functions-ipv6
#
#@-INF@# RedHat 6.2 + 7.0
#@-INF@#
#@-INF@# directory: /etc/sysconfig/network-scripts
#@-INF@#
#@-INF@# permissions: 644
#@-INF@#
#@-INF@# description: Several IPv6 related functions
#@-INF@# Contains: 
#@-INF@#		test_ipv6:              test whether system is prepared for IPv6
#@-INF@#		ifup_ipv6_real:         configure an IPv6 address for a specified interface
#@-INF@#		ifdown_ipv6_real:	    remove an IPv6 address on a specified interface
#@-INF@#		ifdown_ipv6_real_all:   remove all IPv6 addresses assigned on a specified interface
#@-INF@#		ifup_ipv6_autotunnel: 	enabling automatic tunneling
#@-INF@#		ifdown_ipv6_autotunnel:	disable automatic tunneling
#@-INF@#		ifup_ipv6_tunnel:       configure an IPv6 tunnel
#@-INF@#		ifdown_ipv6_tunnel:     remove an IPv6 tunnel
#@-INF@#		ifup_ipv6_route:        add an IPv6 route
#@-INF@#		ifdown_ipv6_route:      remove an IPv6 route
#@-INF@#		forwarding_ipv6:        control IPv6 forwarding (globally or per device)
#@-INF@#
#@+RH7@# Taken from:
# (P) & (C) 1997-2001 by Peter Bieringer <pb@bieringer.de>
#
# Version: 2001-02-19
#
#@-INF@#  This function libary is now explicitly GPL'ed 
#@-INF@#   http://www.gnu.org/copyleft/gpl.html (20001125, Peter Bieringer)
#@-INF@#
#@-INF@#  Suggestions, comments and improvements are welcome!
#@-INF@#
#@-INF@#  Best viewed with tabsize 4
#@-INF@#
#@-INF@#  You will find more information in the IPv6-HowTo for Linux at
#@-INF@#   http://www.bieringer.de/linux/IPv6/
#@-INF@#
#@-INF@# Known bugs:
#@-INF@#  Duplicate address test on interface won't work, if given one is in full
#@-INF@#   IPv6 notation
#@-INF@#
#@-INF@# Changes to:
#@-INF@#  20000703: initial review for new layout, based on 'functions-ip6' 2.37
#@-INF@#             only ifup/ifdown works
#@-INF@#  20000704: review ifup-ipv6-tunnel, ifdown-ipv6-tunnel
#@-INF@#  20000723: minor review
#@-INF@#  20000731: fix misnamed function "forwarding-ipv6-route" -> "forwarding-ipv6"
#@-INF@#  20000816: rename all functions names: "-" to "_"
#@-INF@#             Mauro Tortonesi <mauro@ferrara.linux.it> reports incompatiblity
#@-INF@#             problems
#@-INF@#  20001125: explicitly GPL'ed
#@-INF@#  20001215: fix a bug regarding variable name length->prefixlength in setting
#@-INF@#             up interfaces, thanks to Marcus Windisch <marcus.windisch@gmx.net>
#@-INF@#  20010201: if a tunnel interface named sit0 is requested to set up, only bring
#@-INF@#             sit0 up to life (automatic tunneling support only)
#@-INF@#            begin to mark all major debug code for fast removing via grep
#@-INF@#  20010202: Backpatch changes done in Rawhide/initscripts-5.60-1.src.rpm by others
#@-INF@#            Tag several lines for easier stripping of unnecessary lines
#@-INF@#            Enable ifup_ipv6_real/ifdown_ipv6_real to extract prefix length from
#@-INF@#             given address, if not extra specified
#@-INF@#            Network route setup on interface initialization disabled (no longer needed?)
#@-INF@#  20010203: Remove obsolete kernel 2.1.x (x < 90) code, remove some information output
#@-INF@#            Add function ifdown_ipv6_real_all, usage prevents from kernel crashing
#@-INF@#             if on shutdown one address was forgotten
#@-INF@#            Add some "prevent of kernel crashing" airbags and duplicate configurations
#@-INF@#  20010205  Start interface, if down (for an IPv6 only configuration)
#@-INF@#             hint by Robert.Wachinger@icn.siemens.de
#@-INF@#  20010207  Change from direct writing to /proc to sysctl
#@-INF@#             Implement some rawhide fixes
#@-INF@#  20010208  Remove backward compatiblity for extra given prefix length
#@-INF@#            Remove no longer used routing setup on interfaces
#@-INF@#            Implement additional rawhide fixes
#@-INF@#  20010219  Cosmetic fix (remove a "!" on an echo output)

# Filter tags (for stripping, empty lines following if all is stripped)
#  #@-DEB@#  : Additional debug code
#  #@-INF@#  : Additional information
#  #@-RH7@#  : Not necessary for RedHat 7 rawhide
#  #@+RH7@#  : Necessary for RedHat 7 rawhide


#@-DEB@# DEBUG_IPV6 & 1 "set -x" mode
#@-DEB@# DEBUG_IPV6 & 2 prevents from executing any network configuration
#@-DEB@# DEBUG_IPV6 & 4 shows messages in the test section

#DEBUG_IPV6=$[ 65535 - 1 -2  -4]										#@-DEB@#
DEBUG_IPV6=0															#@-DEB@#
#DEBUG_IPV6=2															#@-DEB@#

[ -z $DEBUG_IPV6 ]  && DEBUG_IPV6=0										#@-DEB@#

# Return values
#  0 = ok
#  1 = error occurs
#  2 = not enabled, i.e. no IPv6 kernel support or switched off by configuration


##### Test for IPv6 capabilites

function test_ipv6()
{
	[ $[ $DEBUG_IPV6 & 4 ] = 0 ] || echo " Tests for IPv6" 				#@-DEB@# 

    # Test for IPv6 enabled kernel
	[ $[ $DEBUG_IPV6 & 4 ] = 0 ] || echo -n "  Test kernel for IPv6..."	#@-DEB@#

	if ! [ -f /proc/net/if_inet6 ]; then
		echo "Did not find IPv6 in kernel, trying to load module"		#@-RH7@#
		modprobe ipv6
	
		if ! [ -f /proc/net/if_inet6 ]; then
			if ! [ $[ $DEBUG_IPV6 & 4 ] = 0 ] ; then					#@-DEB@#
				echo "Not compiled for IPv6 - stop!"					#@-DEB@#
			else														#@-DEB@#
				echo $"Kernel is not compiled with IPv6 support"
			fi															#@-DEB@#
			return 2
		fi
	else																#@-DEB@#
		[ $[ $DEBUG_IPV6 & 4 ] = 0 ] || echo "Ok!"						#@-DEB@#
	fi

	# Test for IPv6 enabled needed binaries										#@-RH7@#
	[ $[ $DEBUG_IPV6 & 4 ] = 0 ] || echo -n "  Test binaries for IPv6 capability..."	#@-DEB@# #@-RH7@#
																				#@-RH7@#
	if ! ifconfig -? 2>&1 | grep -q "(IPv6)"; then								#@-RH7@#
		echo "'`which ifconfig`' (net-tools) not compiled for IPv6 - stop!"		#@-RH7@#
		return 2																#@-RH7@#
	fi																			#@-RH7@#
																				#@-RH7@#
	if ! route -? 2>&1 | grep -q "(IPv6)"; then									#@-RH7@#
		echo "'`which route`' (net-tools) not compiled for IPv6 - stop!"		#@-RH7@#
		return 2																#@-RH7@#
	fi																			#@-RH7@#

	[ $[ $DEBUG_IPV6 & 4 ] = 0 ] || echo "Ok!"										#@-DEB@#

	# Info about executing																#@-DEB@#
	[ $DEBUG_IPV6 -gt 0 ]  && echo "  Executing in DEBUG_IPV6 mode: $DEBUG_IPV6"	#@-DEB@#
	return 0
}

##### Control IPv6 forwarding
# Display usage
function forwarding_ipv6_usage() {
	echo $"Usage: $0 yes|no [device]"
}

# For compatibility																		#@-RH7@#
function forwarding_ipv6_route() {														#@-RH7@#
	echo "Please use 'forwarding-ipv6' instead of 'forwarding-ipv6-route'"				#@-RH7@#
}																						#@-RH7@#

# Control IPv6 forwarding
#  $1: control [yes|no|on|off]
#  $2: network device (if not given, global IPv6 forwarding is set)
function forwarding_ipv6() {
	control=$1
	device=$2		# maybe empty

	if [ -z $control ]; then
		echo $"Missing parameter forwarding control'"
		forwarding_ipv6_usage
		return 1
	fi

	if ! [ "$control" = "yes" -o "$control" = "no" -o "$control" = "on" -o "$control" = "off" ]; then
		echo $"Don't understand forwarding control parameter '$control'"
		forwarding_ipv6_usage
		return 1
	fi
	
	# Device "lo" need no IPv6 configuration
	if [ "$device" = "lo" ]; then
		echo $"Device lo needs no IPv6 configuration"									#@-RH7@#
		return 0;
	fi

	# Run IPv6 test
	test_ipv6 || return    

	[ $[ $DEBUG_IPV6 & 1 ] = 0 ] || set -x												#@-DEB@#

	if [ "$control" = "yes" -o "$control" = "on" ]; then
		status=1
		string="Enable"																#@-RH7@#
	else
		status=0
		string="Disable"																#@-RH7@#
	fi

	# Global control? (if no device is given)
	if [ -z $device ]; then
		echo "$string IPv6 forwarding for all devices"									#@-RH7@#
		if [ $[ $DEBUG_IPV6 & 2 ] = 0 ]; then											#@-DEB@#
			sysctl -w net.ipv6.conf.all.forwarding=$status >/dev/null 2>&1
		fi																				#@-DEB@#
	fi
	
	# Per device control
	if [ ! -z $device ]; then
		echo "$string IPv6 forwarding for device '$device'"							#@-RH7@#
		if [ $[ $DEBUG_IPV6 & 2 ] = 0 ]; then										#@-DEB@#
			sysctl -w net.ipv6.conf.$device.forwarding=$status >/dev/null 2>&1
		fi																			#@-DEB@#
	fi
	
	[ $[ $DEBUG_IPV6 & 1 ] = 0 ] || set +x												#@-DEB@#
}


##### Static IPv6 route configuration

# Display usage
function ifupdown_ipv6_route_usage() {
	echo $"Usage: $0 IPv6-network IPv6-gateway [device]"
}

# Set static IPv6 route
#  $1: IPv6 network to route
#  $2: IPv6 gateway over which $1 should be routed
#  $3: Interface (optional)
function ifup_ipv6_route() {
	networkipv6=$1
	gatewayipv6=$2
	device=$3		# maybe empty

	if [ -z $networkipv6 ]; then
		echo $"Missing parameter 'IPv6-network'"
		ifupdown_ipv6_route_usage
		return 1
	fi

	if [ -z $gatewayipv6 ]; then
		echo $"Missing parameter 'IPv6-gateway'"
		ifupdown_ipv6_route_usage
		return 1
	fi

	# Device "lo" need no IPv6 configuration
	if [ "$device" = "lo" ]; then
		echo $"Device lo needs no IPv6 configuration"									#@-RH7@#
		return 0;
	fi
	
	# Run IPv6 test
	test_ipv6 || return    

	[ $[ $DEBUG_IPV6 & 1 ] = 0 ] || set -x												#@-DEB@#
	
	if [ -z $device ]; then
		echo "Add IPv6 route '$networkipv6' gateway '$gatewayipv6'"						#@-RH7@#
		if [ $[ $DEBUG_IPV6 & 2 ] = 0 ]; then											#@-DEB@#
			route -A inet6 add $networkipv6 gw $gatewayipv6
		fi																				#@-DEB@#
	else
		echo "Add IPv6 route '$networkipv6' gateway '$gatewayipv6' device '$device'"	#@-RH7@#
		if [ $[ $DEBUG_IPV6 & 2 ] = 0 ]; then											#@-DEB@#
			route -A inet6 add $networkipv6 gw $gatewayipv6 dev $device
		fi																				#@-DEB@#
	fi

	[ $[ $DEBUG_IPV6 & 1 ] = 0 ] || set +x												#@-DEB@#
}

# Delete static IPv6 route
#  $1: IPv6 network to route
#  $2: IPv6 gateway over which $1 should be routed
#  $3: Interface (optional)
function ifdown_ipv6_route() {
	networkipv6=$1
	gatewayipv6=$2
	device=$3		# maybe empty

	if [ -z $networkipv6 ]; then
		echo $"Missing parameter IPv6-network'"
		ifup_ipv6_route_usage
		return 1
	fi

	if [ -z $gatewayipv6 ]; then
		echo $"Missing parameter 'IPv6-gateway'"
		ifup_ipv6_route_usage
		return 1
	fi

	# Device "lo" need no IPv6 configuration
	if [ "$device" = "lo" ]; then
		echo $"Device lo needs no IPv6 configuration"									#@-RH7@#
		return 0;
	fi

	# Run IPv6 test
	test_ipv6 || return    

	[ $[ $DEBUG_IPV6 & 1 ] = 0 ] || set -x												#@-DEB@#
	
	if [ -z $device ]; then
		echo "Delete IPv6 route '$networkipv6' gateway '$gatewayipv6'"					#@-RH7@#
		if [ $[ $DEBUG_IPV6 & 2 ] = 0 ]; then											#@-DEB@#
			route -A inet6 del $networkipv6 gw $gatewayipv6
		fi																				#@-DEB@#
	else
    	echo "Delete IPv6 route '$networkipv6' gateway '$gatewayipv6' device '$device'"	#@-RH7@#
		if [ $[ $DEBUG_IPV6 & 2 ] = 0 ]; then											#@-DEB@#
			route -A inet6 del $networkipv6 gw $gatewayipv6 dev $device
		fi																				#@-DEB@#
	fi

	[ $[ $DEBUG_IPV6 & 1 ] = 0 ] || set +x												#@-DEB@#
}


##### automatic tunneling configuration

## Configure automatic tunneling up
function ifup_ipv6_autotunnel() {
	
	# Run IPv6 test
	test_ipv6 || return    

    [ $[ $DEBUG_IPV6 & 1 ] = 0 ] || set -x												#@-DEB@#

	# enable IPv6-over-IPv4 tunnels
	if ifconfig sit0 | grep -q "UP "; then
		# already up, do nothing
		true
	else
		# basic tunnel device to up
	    echo "Bring up basic tunnel device 'sit0'"										#@-RH7@#
		if [ $[ $DEBUG_IPV6 & 2 ] = 0 ] ; then											#@-DEB@#
			ifconfig sit0 up

			# Switch on forwarding
			forwarding_ipv6 on sit0
		fi																				#@-DEB@#
	fi
	
	[ $[ $DEBUG_IPV6 & 1 ] = 0 ] || set +x												#@-DEB@#
}


## Configure automatic tunneling down
function ifdown_ipv6_autotunnel() {

	# Run IPv6 test
	test_ipv6 || return    

	[ $[ $DEBUG_IPV6 & 1 ] = 0 ] ||	set -x												#@-DEB@#

	# disable IPv6-over-IPv4 tunnels (if a tunnel is no longer up)
	if route -A inet6 -n | grep sit0 | grep -v -q "^::"; then
		# existing routes, do nothing
		true
	else
		# basic tunnel device to down
		echo "Bring down basic tunnel device 'sit0'"									#@-RH7@#
		if [ $[ $DEBUG_IPV6 & 2 ] = 0 ] ; then											#@-DEB@#
			# Switch off forwarding 
			forwarding_ipv6 off sit0
			
			ifconfig sit0 down
		fi																				#@-DEB@#
	fi

	[ $[ $DEBUG_IPV6 & 1 ] = 0 ] || set +x												#@-DEB@#
}	


##### static tunneling configuration

function ifupdown_ipv6_tunnel_usage() {
	echo $"Usage: $0 interfacename IPv4-tunneladdress IPv6-route"
}


## Configure static tunnels up
#  $1: Interface (not needed - dummy)
#  $2: IPv4 address of foreign tunnel
#  $3: IPv6 route through this tunnel
function ifup_ipv6_tunnel() {
	device=$1
	addressipv4tunnel=$2
	routeipv6=$3

	if [ -z $device ]; then
		echo $"Missing parameter 'device'"
		ifupdown_ipv6_tunnel_usage
		return 1
	fi

	if [ -z $addressipv4tunnel ]; then
		echo $"Missing parameter 'IPv4-tunneladdress'"
		ifupdown_ipv6_tunnel_usage
		return 1
	fi

	if [ -z $routeipv6 ]; then
		echo $"Missing parameter 'IPv6-route'"
		ifupdown_ipv6_tunnel_usage
		return 1
	fi
	
	# Run IPv6 test
	test_ipv6 || return    

	[ $[ $DEBUG_IPV6 & 1 ] = 0 ] || set -x											#@-DEB@#

	# enable general IPv6-over-IPv4 tunneling
	ifup_ipv6_autotunnel
	
	echo "Add NBMA-styled tunnel to $routeipv6 over foreign endpoint $addressipv4tunnel (virtual tunnel $device)"	#@-RH7@#
	if [ $[ $DEBUG_IPV6 & 2 ] = 0 ]; then											#@-DEB@#
    	# Set up a tunnel
		route -A inet6 add $routeipv6 gw ::$addressipv4tunnel dev sit0
	fi																				#@-DEB@#

	[ $[ $DEBUG_IPV6 & 1 ] = 0 ] || set +x											#@-DEB@#
}	


## Configure static tunnels down
#  $1: Interface (not used - dummy)
#  $2: IPv4 address of foreign tunnel
#  $3: IPv6 route through this tunnel
function ifdown_ipv6_tunnel() {
	device=$1
	addressipv4tunnel=$2
	routeipv6=$3

	if [ -z $device ]; then
		echo $"Missing parameter 'device'"
		ifupdown_ipv6_tunnel_usage
		return 1
	fi

	if [ -z $addressipv4tunnel ]; then
		echo $"Missing parameter 'IPv4-tunneladdress'"
		ifupdown_ipv6_tunnel_usage
		return 1
	fi

	if [ -z $routeipv6 ]; then
		echo $"Missing parameter 'IPv6-route'"
		ifupdown_ipv6_tunnel_usage
		return 1
	fi

	# Run IPv6 test
	test_ipv6 || return    

	[ $[ $DEBUG_IPV6 & 1 ] = 0 ] ||	set -x												#@-DEB@#

	echo "Delete NBMA-styled tunnel to $routeipv6 over foreign endpoint $addressipv4tunnel (virtual tunnel $device)"	#@-RH7@#
	if [ $[ $DEBUG_IPV6 & 2 ] = 0 ]; then												#@-DEB@#
		# Set up a tunnel
		route -A inet6 del $routeipv6 gw ::$addressipv4tunnel dev sit0
	fi																					#@-DEB@#

	# disable IPv6-over-IPv4 tunneling (if no longer a tunnel is up)
	ifdown_ipv6_autotunnel

	[ $[ $DEBUG_IPV6 & 1 ] = 0 ] || set +x												#@-DEB@#
}	


##### Interface configuration
function ifupdown_ipv6_usage() {
	echo $"Usage: $0 interfacename IPv6-address/IPv6-prefixlength"
}

## Add an IPv6 address for given interface
#  $1: Interface 
#  $2: IPv6 address
function ifup_ipv6_real() {
	device=$1
	address=$2

	if [ -z $device ]; then
		echo $"Missing parameter 'device'"
		ifupdown_ipv6_usage
		return 1
	fi

   	# Device "lo" need no IPv6 configuration
   	if [ "$device" = "lo" ]; then
		echo $"Device lo needs no IPv6 configuration"						#@-RH7@#			
		return 0;
	fi

	if [ -z $address ]; then
		echo $"Missing parameter 'IPv6-address'"
		ifupdown_ipv6_usage
		return 1
	fi

	# Test status of interface
	if ifconfig $device | grep -q "UP "; then
		# Interface is up
			true
	else
		# no IPv4 for this interface, interface is still down, do up ...
		ifconfig $device up
	fi

	# Extract address parts
	prefixlength_implicit="`echo $address | awk -F/ '{ print $2 }'`"
	address_implicit="`echo $address | awk -F/ '{ print $1 }'`"

	# Test for prefix length
	if [ -z $prefixlength_implicit ]; then
		echo $"Missing 'prefix length' for given address"
		ifupdown_ipv6_usage
		return 1
	elif [ $prefixlength_implicit -lt 0 -o $prefixlength_implicit -gt 128 ]; then
		echo $"'prefix length' on given address is out of range (0-128)"
		ifupdown_ipv6_usage
		return 1
	fi

	# Run IPv6 test
	test_ipv6 || return    

	[ $[ $DEBUG_IPV6 & 1 ] = 0 ] || set -x									#@-DEB@#

	# Only add, if address do not already exist
	address_configured="`ifconfig $device | grep "inet6 addr:" | grep "$address" | awk '{ print $3 }'`"
	address_configured_type="`ifconfig $device | grep "inet6 addr:" | grep "$address" | awk '{ print $4 }'`"

	if [ "$address_configured" = "$address" ]; then
		echo "Given IPv6 address $address for dev $device is already configured - skip"		#@-RH7@#
		true
	else
		echo "Add for dev $device IPv6 address $address"						#@-RH7@#
		if [ $[ $DEBUG_IPV6 & 2 ] = 0 ]; then									#@-DEB@#
			ifconfig $device add $address || return 2
		fi																		#@-DEB@#
	fi

	[ $[ $DEBUG_IPV6 & 1 ] = 0 ] || set +x									#@-DEB@#
}


## Remove all IPv6 routes and addresses for given interface
#   cleanup to prevent kernel crashes
#  $1: Interface 
function ifdown_ipv6_real_all() {
	device=$1

	if [ -z $device ]; then
		echo $"Missing parameter 'device'"
		echo $"Usage: ifdown_ipv6_real_all interfacename"
		return 1
	fi

	# Get all IPv6 routes through given interface and remove them
	route -A inet6 | grep $device | while read ipv6net nexthop flags metric ref use iface args; do
		if [ "$device" = "$iface" ]; then
			if echo $flags | grep -v -q "A"; then
				# Only non addrconf (automatic installed) routes should be removed
				ifdown_ipv6_route $ipv6net $nexthop $iface
			fi
		fi
	done
	
	# Get all IPv6 addresses assigned to given interface and remove them
	ifconfig $device | grep "inet6 addr:" | awk '{ print $3 }' | while read ipv6addr args; do
		ifdown_ipv6_real $device $ipv6addr	
	done
}

## Remove an IPv6 address on given interface
#  $1: Interface 
#  $2: IPv6 address
function ifdown_ipv6_real() {
	device=$1
	address=$2

	if [ -z $device ]; then
		echo $"Missing parameter 'device'"
		ifupdown_ipv6_usage
		return 1
	fi

	# Device "lo" need no IPv6 configuration
	if [ "$device" = "lo" ]; then
		echo $"Device lo needs no IPv6 configuration"						#@-RH7@#
		return 0;
	fi

	if [ -z $address ]; then
		echo $"Missing parameter 'IPv6-address'"
		ifupdown_ipv6_usage
		return 1
	fi

	# Extract address parts
	prefixlength_implicit="`echo $address | awk -F/ '{ print $2 }'`"
	address_implicit="`echo $address | awk -F/ '{ print $1 }'`"

	# Test for prefix length
	if [ -z $prefixlength_implicit ]; then
		echo $"Missing 'prefix length' for given address"
		ifupdown_ipv6_usage
		return 1
	elif [ $prefixlength_implicit -lt 0 -o $prefixlength_implicit -gt 128 ]; then
		echo $"'prefix length' on given address is out of range (0-128)"
		ifupdown_ipv6_usage
		return 1
	fi

	# Run IPv6 test
	test_ipv6 || return    

	[ $[ $DEBUG_IPV6 & 1 ] = 0 ] || set -x									#@-DEB@#

	# Only remove, if address exists and is not link-local (prevents from kernel crashing)
	address_configured="`ifconfig $device | grep "inet6 addr:" | grep "$address" | awk '{ print $3 }'`"
	address_configured_type="`ifconfig $device | grep "inet6 addr:" | grep "$address" | awk '{ print $4 }'`"
	if [ ! -z "$address_configured" ]; then
		if [ "$address_configured_type" = "Scope:Link" ]; then
			echo "Given IPv6 address $address on dev $device is link-local - skip"	#@-RH7@#
			true
		else
			echo "Delete dev $device IPv6 address $address"						#@-RH7@#
			if [ $[ $DEBUG_IPV6 & 2 ] = 0 ]; then								#@-DEB@#
				ifconfig $device del $address || return 2
			fi																	#@-DEB@#
		fi
	else
		echo "Given IPv6 address $address doesn't exist on dev $device"		#@-RH7@#
		true
	fi
    
	[ $[ $DEBUG_IPV6 & 1 ] = 0 ] || set +x									#@-DEB@#
}

