# Copyright (c) 2004-2005 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
# $Header: /var/cvsroot/gentoo-src/rc-scripts/net-scripts/net.modules.d/iproute2,v 1.19.4.5 2005/04/14 09:39:24 uberlord Exp $

# Contributed by Roy Marples (uberlord@gentoo.org)

# Fix any potential localisation problems
# Note that LC_ALL trumps LC_anything_else according to locale(7)
ip() {
	LC_ALL=C /sbin/ip "$@"
}

iproute2_tunnel() {
	LC_ALL=C /sbin/ip tunnel "$@"
}

# void iproute2_depend(void)
#
# Sets up the dependancies for the module
iproute2_depend() {
	after macchanger wireless
}

# bool iproute2_check_installed(void)
#
# Returns 1 if iproute2 is installed, otherwise 0
iproute2_check_installed() {
	local report=${1:-false} installed=0
	if [[ ! -x /sbin/ip ]]; then
		installed=1
		${report} && eerror "For iproute2 support, emerge sys-apps/iproute2"
	fi
	if [[ ! -e /proc/net/netlink ]]; then
		installed=1
		${report} && eerror "iproute2 requires NetLink enabled in the kernel"
	fi
	return ${installed}
}

# char* iproute2_provides(void)
#
# Returns a string to change module definition for starting up
iproute2_provides() {
	echo "interface"
}

# char* iproute2_module(void)
#
# Returns the module name
# This is needed by dhclient as we run different scripts
# based on the interface
iproute2_module() {
	echo "iproute2"
}

# bool iproute2_check_depends(void)
#
# Checks to see if we have the needed functions
iproute2_check_depends() {
	local f

	for f in interface_device interface_variable; do
		[[ $( type -t ${f} ) == function ]] && continue
		eerror "iproute2: missing required function ${f}\n"
		return 1
	done

	return 0
}

# bool iproute2_exists(char *interface, bool report)
#
# Returns 1 if the interface exists, otherwise 0
iproute2_exists() {
	local e=$( ip addr show label ${1} ) report=${2:-false}
	[[ -n ${e} ]] && return 0
	${report} && eerror "${1} does not exist"
	return 1
}

# void iproute2_up(char *interface)
#
# provides a generic interface for bringing interfaces up
iproute2_up() {
	ip link set up dev ${1} &>${devnull}
}

# void iproute2_down(char *interface)
#
# provides a generic interface for bringing interfaces up
iproute2_down() {
	ip link set down dev ${1} &>${devnull}
}

# bool ifproute2_is_up(char *iface, bool withaddress)
#
# Returns 0 if the interface is up, otherwise 1
# If withaddress is true then the interface has to have an IPv4 address
# assigned as well
iproute2_is_up() {
	local iface=${1} addr=${2:-false}

	ip link show ${iface} 2>${devnull} | grep -Eq "\<UP\>" || return 1
	! ${addr} && return 0
	ip addr show ${iface} 2>${devnull} | grep -v link | grep -q 'inet ' || return 1
}

# void iproute2_set_flag(char *iface, char *flag, bool enabled)
#
# Sets or disables the interface flag 
iproute2_set_flag() {
	local iface=${1} flag=${2} enable=${3} opt="on"
	${enable} || opt="off"
	ip link set ${iface} ${flag} ${opt} &>${devnull}
}

# void loopback_create(void)
#
# Creates our loopback interface
iproute2_loopback_create() {
	ip addr add 127.0.0.1/8 dev lo brd + scope host &>/dev/null
	iproute2_up lo
	ip route add 127.0.0.0/8 dev lo &>/dev/null
}

# void iproute2_get_address(char *interface)
#
# Fetch the address retrieved by DHCP.  If successful, echoes the
# address on stdout, otherwise echoes nothing.
iproute2_get_address() {
	ip -family inet addr show ${1} 2>${devnull} | awk '/inet/ {print $2}' | cut -d/ -f1
}

# void get_mac_address(char *interface)
#
# Fetch the mac address assingned to the network card
iproute2_get_mac_address() {
	ip link show ${1} 2>${devnull} | awk '/link/ {print $2}' | cut -d/ -f1
}

# void iproute2_get_aliases_rev(char *interface)
#
# Fetch the list of aliases for an interface.  
# Outputs a space-separated list on stdout, in reverse order, for
# example "eth0:2 eth0:1"
iproute2_get_aliases_rev() {
	local iface=$( interface_device ${1} )
	ip addr show dev ${iface} 2>${devnull} | awk -v re="^${1}:" \
		'$NF~re {print $NF}' | tac | xargs
}

# bool iproute2_del_addresses(char *interface, bool report)
#
# Remove addresses from interface.
iproute2_del_addresses() {
	local iface=${1}

	ip addr flush label ${iface} scope global &>/dev/null
	ip addr flush label ${iface} scope host &>/dev/null

	return 0
}

# char* iproute2_get_vars(char *interface)
#
# Returns a string spaced with possible user set
# configuration variables
iproute2_get_vars() {
	echo "config_${1} routes_${1} fallback_${1} ipaddr_${1} ipaddr_fallback_${1} iproute_${1} inet6_${1}"
}

# bool iproute2_get_old_config(char *iface)
#
# Returns config and config_fallback for the given interface
iproute2_get_old_config() {
	local ifvar=$( interface_variable ${1} ) inet6

	# iproute2-style config vars
	eval config=( \"\$\{ipaddr_${ifvar}\[@\]\}\" )
	eval config_fallback=( \"\$\{ipaddr_fallback_${ifvar}\[@\]\}\" )
	eval inet6=( \"\$\{inet6_${ifvar}\[@\]\}\" )

	# BACKWARD COMPATIBILITY: check for space-separated inet6 addresses
	[[ ${#inet6[@]} == 1 && ${inet6} == *' '* ]] && inet6=( ${inet6} )

	# Add inet6 addresses to our config if required
	[[ -n ${inet6} ]] && config=( "${config[@]}" "${inet6[@]}" )

	return 0
}

# bool iproute2_iface_stop(char *interface)
#
# Do final shutdown for an interface or alias.
#
# Returns 0 (true) when successful, non-zero (false) on failure
iproute2_iface_stop() {
	local label=${1} iface=$( interface_device ${1} )

	# Shut down the link if this isn't an alias or vlan
	if [[ ${label} == ${iface} ]]; then
		iproute2_down ${iface}
		return $?
	fi
	return 0
}

# bool iproute2_add_address(char *interface, char *options ...)
#
# Adds an the specified address to the interface
# returns 0 on success and non-zero on failure
iproute2_add_address() {
	local iface=${1} x

	iproute2_exists ${iface} true || return 1

	# Extract the config
	local -a config=( "$@" )
	config=( ${config[@]:1} )

	# Convert an ifconfig line to iproute2
	local n=${#config[@]};
	for (( x=0; x<n; x++ )); do
		case ${config[x]} in
			netmask)
				config[0]="${config[0]}/$( netmask2cidr ${config[x+1]} )"
				unset config[x]
				unset config[x+1]
				;;
		esac
	done
	config=( "${config[@]//pointtopoint/peer}" )

	# Make sure interface is marked UP
	# This is required for IPv6 addresses
	iproute2_up ${iface}

	# If the address already exists then the following command
	# will fail.  Catch the failure and be graceful
	x=$( ip addr add dev ${iface} ${config[@]} 2>&1 )
	case "${x}" in
		'RTNETLINK answers: File exists'|'')
			eend 0
			return 0
			;;
		*)
			printf '%s\n' "${x}" >&2
			eend 1
			return 1
	esac
}

# bool iproute2_post_start(char *interface)
#
# Runs any post_start stuff on our interface and adds routes
# Always returns 0
iproute2_post_start() {
	local iface=${1} ifvar=$( interface_variable ${1} ) routes e r

	# Make sure interface is marked UP
	iproute2_up ${iface}

	eval routes=( \"\$\{routes_${ifvar}\[@\]\}\" )

	# Test for old style ipaddr variable
	if [[ -z ${routes} ]]; then
		eval routes=( \"\$\{iproute_${ifvar}\[@\]\}\" )
	fi

	[[ -z ${routes} ]] && return 0

	# Set routes with ip route -- this might also include default route
	einfo "Adding routes"
	eindent
	for x in "${routes[@]}"; do
		# Support net-tools routing too
		x=${x//gw/via}
		x=${x//-A inet6}

		einfo "${x}"
		e=$( ip route append dev ${iface} ${x} 2>&1 )
		case "${e}" in
			'RTNETLINK answers: File exists'|'')
				eend 0
				;;
			*) printf '%s\n' "${e}" >&2
				eend 1
				;;
		esac
	done
	eoutdent

	return 0
}
