#!/bin/sh
#
# network-functions-ipv6
#
# Taken from:
# (P) & (C) 1997-2001 by Peter Bieringer <pb@bieringer.de>
#
# Version: 2001-02-08
#

# Filter tags (for stripping, empty lines following if all is stripped)

# 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

test_ipv6()
{
    # Test for IPv6 enabled kernel

	if ! [ -f /proc/net/if_inet6 ]; then
		modprobe ipv6

		if ! [ -f /proc/net/if_inet6 ]; then
				echo $"Kernel is not compiled with IPv6 support"
			return 2
		fi
	fi
	return 0
}

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

# Control IPv6 forwarding
#  $1: control [yes|no|on|off]
#  $2: network device (if not given, global IPv6 forwarding is set)
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
		return 0;
	fi

	# Run IPv6 test
	test_ipv6 || return

	if [ "$control" = yes -o "$control" = on ]; then
		status=1
	else
		status=0
	fi

	# Global control? (if no device is given)
	if [ -z $device ]; then
		sysctl -w net.ipv6.conf.all.forwarding=$status >/dev/null 2>&1
	fi

	# Per device control
	if [ ! -z $device ]; then
		sysctl -w net.ipv6.conf.$device.forwarding=$status >/dev/null 2>&1
	fi
}


##### Static IPv6 route configuration

# Display usage
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)
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
		return 0;
	fi

	# Run IPv6 test
	test_ipv6 || return

	if [ -z $device ]; then
			route -A inet6 add $networkipv6 gw $gatewayipv6
	else
			route -A inet6 add $networkipv6 gw $gatewayipv6 dev $device
	fi
}

# Delete static IPv6 route
#  $1: IPv6 network to route
#  $2: IPv6 gateway over which $1 should be routed
#  $3: Interface (optional)
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
		return 0;
	fi

	# Run IPv6 test
	test_ipv6 || return

	if [ -z $device ]; then
			route -A inet6 del $networkipv6 gw $gatewayipv6
	else
			route -A inet6 del $networkipv6 gw $gatewayipv6 dev $device
	fi
}


##### automatic tunneling configuration

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


	# enable IPv6-over-IPv4 tunnels
	if LC_ALL= LANG= ifconfig sit0 |fgrep -qs ' UP '; then
		# already up, do nothing
		true
	else
		# basic tunnel device to up
			ifconfig sit0 up

			# Switch on forwarding
			forwarding_ipv6 on sit0
	fi
}


## Configure automatic tunneling down
ifdown_ipv6_autotunnel()
{
	# Run IPv6 test
	test_ipv6 || return


	# 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
			# Switch off forwarding
			forwarding_ipv6 off sit0

			ifconfig sit0 down
	fi
}


##### static tunneling configuration

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
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


	# enable general IPv6-over-IPv4 tunneling
	ifup_ipv6_autotunnel

    	# Set up a tunnel
		route -A inet6 add $routeipv6 gw ::$addressipv4tunnel dev sit0
}


## Configure static tunnels down
#  $1: Interface (not used - dummy)
#  $2: IPv4 address of foreign tunnel
#  $3: IPv6 route through this tunnel
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

		# Set up a tunnel
		route -A inet6 del $routeipv6 gw ::$addressipv4tunnel dev sit0

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

}


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

## Add an IPv6 address for given interface
#  $1: Interface
#  $2: IPv6 address
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
		return 0;
	fi

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

	# Test status of interface
	if LC_ALL= LANG= ifconfig $device |fgrep -qs ' 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

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

	if [ "$address_configured" = "$address" ]; then
		true
	else
		ifconfig $device add $address || return 2
	fi
}


## Remove all IPv6 routes and addresses for given interface
#   cleanup to prevent kernel crashes
#  $1: Interface
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
	LC_ALL= LANG= ifconfig $device |fgrep '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
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
		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

	# Only remove, if address exists and is not link-local (prevents from kernel crashing)
	address_configured="`LC_ALL= LANG= ifconfig $device |fgrep 'inet6 addr:' |fgrep "$address" | awk '{ print $3 }'`"
	address_configured_type="`LC_ALL= LANG= ifconfig $device |fgrep 'inet6 addr:' |fgrep "$address" | awk '{ print $4 }'`"
	if [ ! -z "$address_configured" ]; then
		if [ "$address_configured_type" = "Scope:Link" ]; then
			true
		else
				ifconfig $device del $address || return 2
		fi
	else
		true
	fi
}
