# 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/ifconfig,v 1.21.4.8 2005/03/23 14:39:11 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)
ifconfig() {
	LC_ALL=C /sbin/ifconfig "$@"
}

ifconfig_tunnel() {
	LC_ALL=C /sbin/iptunnel "$@"
}

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

# bool ifconfig_check_installed(void)
#
# Returns 1 if ifconfig is installed, otherwise 0
ifconfig_check_installed() {
	[[ -x /sbin/ifconfig ]] && return 0
	${1:-false} && eerror "For ifconfig support, emerge sys-apps/net-tools"
	return 1
}

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

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

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

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

	return 0
}

# bool ifconfig_exists(char *interface, bool report)
#
# Returns 1 if the interface exists, otherwise 0
ifconfig_exists() {
	local e=$( ifconfig -a | grep -o "^${1}" ) report=${2:-false}
	[[ -n ${e} ]] && return 0
	${report} && eerror "${1} does not exist"
	return 1
}

# void ifconfig_up(char *iface)
#
# provides a generic interface for bringing interfaces up
ifconfig_up() {
	ifconfig ${1} up &>${devnull}
}

# void ifconfig_down(char *iface)
#
# provides a generic interface for bringing interfaces down
ifconfig_down() {
	ifconfig ${1} down &>${devnull}
}

# bool ifconfig_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
ifconfig_is_up() {
	local check="\<UP\>" addr=${2:-false}
	${addr} && check="\<addr:.*${check}"
	ifconfig ${1} 2>${devnull} | grep -v Scope | xargs | grep -Eq "${check}" && return 0
	return 1
}

# void ifconfig_set_flag(char *iface, char *flag, bool enabled)
#
# Sets or disables the interface flag 
ifconfig_set_flag() {
	local iface=${1} flag=${2} enable=${3}
	${enable} || flag="-${flag}"
	ifconfig ${iface} ${flag} &>${devnull}
}

# void ifconfig_loopback_create(void)
#
# Creates our loopback interface
ifconfig_loopback_create() {
	ifconfig lo 127.0.0.1 up 2>/dev/null
	/sbin/route add -net 127.0.0.0 netmask 255.0.0.0 \
		gw 127.0.0.1 dev lo 2> /dev/null
}

# void ifconfig_get_address(char *interface)
#
# Fetch the address retrieved by DHCP.  If successful, echoes the
# address on stdout, otherwise echoes nothing.
ifconfig_get_address() {
	ifconfig ${1} | grep -m1 -o 'inet addr:[^ ]*' | cut -d: -f2
}

# void ifconfig_get_mac_address(char *interface)
#
# Fetch the mac address assingned to the network card
ifconfig_get_mac_address() {
	ifconfig ${1} | grep -m1 -o 'HWaddr [^ ]*' | cut -d" " -f2
}

# void ifconfig_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"
ifconfig_get_aliases_rev() {
	ifconfig | grep -o "^${1}:[0-9]*" | tac | xargs
}

# bool ifconfig_interface_del_addresses(char *interface)
#
# Remove addresses from interface.  Returns 0 (true) if there
# were addresses to remove (whether successful or not).  Returns 1
# (false) if there were no addresses to remove.
ifconfig_del_addresses() {
	local iface=${1} i

	# We don't remove addresses from aliases
	[[ ${iface} == *:* ]] && return 0

	# If the interface doesn't exist, don't try and delete
	ifconfig_exists ${iface} || return 0

	# iproute2 can add many addresses to an iface unlike ifconfig ...
	# iproute2 added addresses cause problems for ifconfig
	# as we delete an address, a new one appears, so we have to
	# keep polling
	while ifconfig ${iface} | grep -q -m1 -o 'inet addr:[^ ]*' ; do
		ifconfig ${iface} 0.0.0.0 || break
	done
	
	# Remove IPv6 addresses
	for i in "$(ifconfig ${iface} | awk '$1=="inet6" && $4!="Scope:Link" {print $3}')"; do
		/sbin/ifconfig ${interface} inet6 del ${i} &>/dev/null
	done

	return 0
}

# char* ifconfig_get_vars(char *interface)
#
# Returns a string spaced with possible user set
# configuration variables
ifconfig_get_vars() {
        echo "config_${1} routes_${1} fallback_${1}  ifconfig_${1} ifconfig_fallback_${1} routes_${1} inet6_${1} iface_${1} alias_${1} broadcast_${1} netmask_${1}"
	# The depreciated gateway var has to be handled by
	# each module if needed
}

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

	eval config=( \"\$\{ifconfig_${ifvar}\[@\]\}\" )
	eval config_fallback=( \"\$\{ifconfig_fallback_${ifvar}\[@\]\}\" )
	eval inet6=( \"\$\{inet6_${ifvar}\[@\]\}\" )

	# BACKWARD COMPATIBILITY: populate the config_IFACE array
	# if iface_IFACE is set (fex. iface_eth0 instead of ifconfig_eth0)
	eval local i=\"\$\{iface_${ifvar}\}\"
	if [[ -n ${i} && -z ${config} ]]; then
		# Make sure these get evaluated as arrays
		local -a aliases broadcasts netmasks

		# Start with the primary interface
		config=( "${i}" )

		# ..then add aliases
		eval aliases=( \$\{alias_${ifvar}\} )
		eval broadcasts=( \$\{broadcast_${ifvar}\} )
		eval netmasks=( \$\{netmask_${ifvar}\} )
		for (( i=0; i<${#aliases[@]}; i++ )); do
			config[i+1]="${aliases[i]} ${broadcasts[i]:+broadcast ${broadcasts[i]}} ${netmasks[i]:+netmask ${netmasks[i]}}"
		done
	fi

	# 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 ifconfig_iface_stop(char *interface)
#
# Do final shutdown for an interface or alias.
#
# Returns 0 (true) when successful, non-zero (false) on failure
ifconfig_iface_stop() {
	# If an alias is already down, then "ifconfig eth0:1 down"
	# will try to bring it up with an address of "down" which
	# fails.  Do some double-checking before returning error
	# status
	ifconfig_is_up $1 || return 0
	ifconfig_down ${1} && return 0

	# It is sometimes impossible to transition an alias from the
	# UP state... particularly if the alias has no address.  So
	# ignore the failure, which should be okay since the entire
	# interface will be shut down eventually.
	[[ ${1} == *:* ]] && return 0
	return 1
}

# bool ifconfig_post_start(char *iface)
#
# Bring up iface using ifconfig utilities, called from iface_start
#
# Returns 0 (true) when successful on the primary interface, non-zero
# (false) when the primary interface fails.  Aliases are allowed to
# fail, the routine should still return success to indicate that
# net.eth0 was successful
ifconfig_post_start() {
	local iface=${1} ifvar=$( interface_variable ${1} ) routes x

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

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

	# BACKWARD COMPATIBILITY: set the default gateway
	if [[ ${gateway} == ${iface}/* ]]; then
		# We don't add the old gateway if one has been set in routes_IFACE
		local gw=true
		for x in "${routes[@]}"; do
			[[ ${x} != "*default gw*" ]] && continue
			gw=false
			break
		done
		${gw} && routes=( "${routes[@]}" "default gw ${gateway#*/}" )
	fi

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

	# Add routes for this interface, might even include default gw
	einfo "Adding routes"
	eindent
	for x in "${routes[@]}"; do
		# Support iproute2 style routes
		x=${x//via/gw}
		
		ebegin "${x}"
		# Support adding IPv6 addresses easily
		if [[ ${x} == *:* && ${x} != *'-A inet6'* ]]; then
			/sbin/route add -A inet6 ${x} &>${devnull}
		else
			/sbin/route add ${x} &>${devnull}
		fi
		eend $?
	done
	eoutdent
	
	return 0
}

# bool ifconfig_add_address(char *iface, char *options ...)
#
# Adds the given address to the interface
ifconfig_add_address() {
	local iface=${1} i=0

	ifconfig_exists ${iface} true || return 1

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

	if [[ ${config[0]} == *:* ]]; then
		# Support IPv6 - nice and simple
		config[0]="inet6 add ${config[0]}"
	else
		# IPv4 is tricky - ifconfig requires an aliased device
		# for multiple addresses
		if ifconfig ${iface} | grep -v Scope | xargs | grep -Eq "\<addr:.*" ; then
			# Get the last alias made for the interface and add 1 to it
			i=$( ifconfig | tac | grep -m 1 -o "^${iface}:[0-9]*" | awk -F: '{ print $2 }' )
			i=${i:-0}
			(( i++ ))
			iface=${iface}:${i}
		fi

		# Support iproute2 style config where possible
		config=( "${config[@]//brd/broadcast}" )
		config=( "${config[@]//peer/pointtopoint}" )

               local cidr="${config[0]##*/}" ip="${config[0]%%/*}"
               if [[ -n ${cidr} && ${cidr} != ${ip} ]]; then
                   local netmask=$( cidr2netmask "${cidr}" )
                   config[0]="${ip} netmask ${netmask}"
               fi
	fi

	ifconfig ${iface} ${config[@]} &>${devnull}
	eend $?
	return $?
}
