#!/bin/bash

# Network interface configuration
#
# Copyright (c) 2002-2006 SuSE Linux AG Nuernberg, Germany.
# All rights reserved.
#
# 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 2 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/>.
#
# Authors: Michal Svec <msvec@suse.cz>
#          Mads Martin Joergensen <mmj@suse.de>
#          Marius Tomaschewski <mt@suse.de>
#
# $Id$
#

. /etc/sysconfig/network/scripts/functions.common

NETWORK_RUNFILE="$RUN_FILES_BASE/started"
STAMPFILE_STUB="$RUN_FILES_BASE/new-stamp-"
SYSTEMD_CGROUP_DIR="/sys/fs/cgroup/systemd"
NETWORK_SERVICE_CGROUP_DIR="${SYSTEMD_CGROUP_DIR}/system/network.service"
NETWORK_SERVICE_CGROUP_TASKS="${NETWORK_SERVICE_CGROUP_DIR}/tasks"


systemd_running ()
{
	# We simply test whether systemd cgroup hierarchy is mounted
	# where we return 0, non-zero if not.
	mountpoint -q "${SYSTEMD_CGROUP_DIR}"
}

systemd_booting () {
	# returns 0 when we boot, 1 in running system
	systemctl show -p ActiveState default.target | grep -qi inactive
}

netcontrol_running() {
	test -f "$NETWORK_RUNFILE"
}

in_network_service_cgroup()
{
	local pid dummy
	if test -f "${NETWORK_SERVICE_CGROUP_TASKS}" ; then
		while read pid dummy ; do
			test "$pid" = "$$" && return 0
		done < "${NETWORK_SERVICE_CGROUP_TASKS}"
	fi
	return 1
}

add_to_network_service_cgroup()
{
	if test ! -d "${NETWORK_SERVICE_CGROUP_DIR}" ; then
		systemd_running && \
		mkdir -p ${NETWORK_SERVICE_CGROUP_DIR} || \
		return 1
	fi
	if test -f "${NETWORK_SERVICE_CGROUP_TASKS}" ; then
		echo "$$" > "${NETWORK_SERVICE_CGROUP_TASKS}" && \
		return 0
	fi
	return 1
}


#
# to test the next two functions:
#
# for i in {0..32}; do
# 	echo $i: $(pfxlen2mask $i) " ---> " $(mask2pfxlen $(pfxlen2mask $i))
# done

mask2pfxlen() {
	local i octet width=0 mask=(${1//./ })
	test ${#mask[@]} -eq 4 || return 1
	for octet in 0 1 2 3; do
		test "${mask[octet]}" -ge 0 -a \
		     "${mask[octet]}" -le 255 2>/dev/null \
			|| return 1
		for i in 128 192 224 240 248 252 254 255; do
			test ${mask[octet]} -ge $i && ((width++))
		done
	done
	echo $width
	return 0
}
pfxlen2mask() {
	test -n "$1" || return 1
	local o i n=0 adr=() len=$(($1))
	for o in 0 1 2 3; do
		adr[$o]=0
		for i in 128 64 32 16 8 4 2 1; do
			((n++ < len)) && \
			((adr[$o] = ${adr[$o]} + $i))
		done
	done
	echo ${adr[0]}.${adr[1]}.${adr[2]}.${adr[3]}
	return 0
}

add_to_wordlist() {
	local v="${1}"
	local l=(${!v}) ; shift
	local a w
	for a in ${@} ; do
		for w in ${l[@]} ; do
			[ "x$w" = "x$a" ] && \
			continue 2
		done
		l=(${l[@]} $a)
	done
	eval "$v='${l[@]}'"
}

is_valid_interface_name()
{
	local LANG=C LC_ALL=C
	local INTERFACE="$1"
	local IFNAME_RX='^[[:alnum:]._:-]{1,15}$'
	[[ ${INTERFACE} =~ ${IFNAME_RX} ]]
}

# returns 0 if ifup is currently working on this interface
ifup_on_iface() {
	ps axhww | grep -qs "[i]fup.* $INTERFACE\>"
}

is_iface_available () {
	local IFNAME=${1}
	local IFTYPE=${2:-$INTERFACETYPE}
	test -z "$IFNAME"              &&       return 1
	test -d /sys/class/net/$IFNAME &&       return 0
	test "${SCRIPTNAME%%-*}" = ifdown -a    \
	     "$MODE" = hotplug               && return 0
	: ${IFTYPE:=$(get_iface_type             "$IFNAME")}
	: ${IFTYPE:=$(get_iface_type_from_config "$IFNAME")}
	test "${SCRIPTNAME%%-*}" = ifup      && { \
	    case ${IFTYPE} in
		bond|bridge|vlan|ibchild)       return 0 ;;
		ppp|isdn|dummy|tun|tap)		return 0 ;;
		ipip|ip6tnl|sit|gre|mip6mnha)	return 0 ;;
	    esac
	}
	test -d /sys/class/net/$IFNAME
}

is_iface_up () {
	test -z "$1" && return 1
	test -d /sys/class/net/$1 || return 1
	case "`LC_ALL=POSIX ip link show $1 2>/dev/null`" in
		*$1*UP*) ;;
		*) return 1 ;;
	esac
}

is_connected () {
	case `read_cached_config_data status $1` in
		connected) return 0 ;;
		connecting) return 0 ;; # might be wrong, test for link to
	esac
	return 1
}

has_link () {
	case `read_cached_config_data link $1` in
		yes) return 0 ;;
	esac
	return 1
}

link_ready_check () {
        local c=`cat /sys/class/net/${1}/carrier 2>/dev/null`
        local d=`cat /sys/class/net/${1}/dormant 2>/dev/null`
        local o=`cat /sys/class/net/${1}/operstate 2>/dev/null`

        #debug "link ready ${1}: carrier=$c, dormant=$d, operstate=$o"
        if test -e "/sys/class/net/${1}/operstate" ; then
                # SLE 11 has carrier + operstate + dormant
		test "$d" = "0" || return 3
		test "$c" = "1" || return 2
		test \( "$o" = "up" -o "$o" = "unknown" \) || return 1
        else
                # e.g. SLE 10 does not have operstate/dormant
                test "$c" = "1" || return 1
        fi
	return 0
}

ipv6_addr_dad_check()
{
	local iface="$1" word i
	local nodad=1 tentative=1 dadfailed=1
	test -f "/sys/class/net/$iface/ifindex" || return 1
	while read -a word ; do
		test "${word[0]}" != "inet6" && continue
		for((i=2; i<${#word[@]}; ++i)) ; do
			case ${word[$i]} in
			nodad)		nodad=0     ;;
			tentative)	tentative=0 ;;
			dadfailed)	dadfailed=0 ;;
			flags)  ((i++))
				rx='^[[:xdigit:]]+$'
				[[ "${word[$i]}" =~ $rx ]] || continue
				hx="0x${word[$i]}"
				test $(( $hx & 0x02 )) -ne 0 && nodad=0
				test $(( $hx & 0x08 )) -ne 0 && dadfailed=0
				test $(( $hx & 0x40 )) -ne 0 && tentative=0
			;;
			esac
		done
		#debug "ipv6 dad $iface: nodad=$nodad, dadfailed=$dadfailed, tentative=$tentative"
		test $nodad     -eq 0 && continue
		test $dadfailed -eq 0 && return 2
		test $tentative -eq 0 && return 3
	done < <(LC_ALL=C ip -6 addr show ${iface:+dev "$iface"} 2>/dev/null)
	return $R_SUCCESS
}

link_ready_wait ()
{
	local iface=$1
	local -i wsecs=${2:-0}
	local -i uwait=25000
	local -i loops=$(((wsecs * 1000000) / $uwait))
	local -i loop=0 ret=0

	link_ready_check "$iface" ; ret=$?
	while ((ret != 0 && loop++ < loops)) ; do
		usleep $uwait
		link_ready_check "$iface" ; ret=$?
	done
	return $ret
}

ipv6_addr_dad_wait()
{
	local iface=$1
	local -i wsecs=${2:-0}
	local -i uwait=25000
	local -i loops=$(((wsecs * 1000000) / $uwait))
	local -i loop=0 ret=0

	ipv6_addr_dad_check "$iface" ; ret=$?
	while ((ret == 3 && loop++ < loops)) ; do
		usleep $uwait
		ipv6_addr_dad_check "$iface" ; ret=$?
	done
	return $ret
}

get_ethtool_drv_info () {
	test -n "$1" || return 1
	local ethtool="/sbin/ethtool"
	if [ ! -x $ethtool ] ; then
		[ -x /usr${ethtool} ] && ethtool="/usr${ethtool}"  || return 1
	fi
	local key val
	$ethtool -i "$1" 2>/dev/null |
	while read key val ; do
		case "$key" in
			driver:)           printf 'ETHTOOL_DRV_NAME=%q\n'       "$val" ;;
			version:)          printf 'ETHTOOL_DRV_VERSION=%q\n'    "$val" ;;
			firmware-version:) printf 'ETHTOOL_DRV_FW_VERSION=%q\n' "$val" ;;
			bus-info:)         printf 'ETHTOOL_DRV_BUS_INFO=%q\n'   "$val" ;;
		esac
	done 2>/dev/null # bash printf required for %q format
}

get_iface_type () {
	local IF=$1 TYPE
	test -n "$IF" || return 1
	test -d /sys/class/net/$IF || return 2
	case "`cat /sys/class/net/$IF/type`" in
		    1)
		     	TYPE=eth
		     	# Ethernet, may also be wireless, ...
		     	if test -d /sys/class/net/$IF/wireless -o \
		     	        -L /sys/class/net/$IF/phy80211 ; then
		     	    TYPE=wlan
		     	elif test -d /sys/class/net/$IF/bridge ; then
		     	    TYPE=bridge
		     	elif test -f /proc/net/vlan/$IF ; then
		     	    TYPE=vlan
		     	elif test -d /sys/class/net/$IF/bonding ; then
		     	    TYPE=bond
		     	elif test -f /sys/class/net/$IF/tun_flags ; then
		     	    TYPE=tap
		     	elif test -d /sys/devices/virtual/net/$IF ; then
		     		case $IF in
		     		  (dummy*) TYPE=dummy ;;
		     		esac
		     	fi
		     	;;
		   24)	TYPE=eth ;; # firewire ;; # IEEE 1394 IPv4 - RFC 2734
		   32)	# InfiniBand
			if test -d /sys/class/net/$IF/bonding ; then
				TYPE=bond
			elif test -d /sys/class/net/$IF/create_child ; then
				TYPE=ib
			else
				TYPE=ibchild
			fi
		      	;;
		  512)	TYPE=ppp ;;
		  768)	TYPE=ipip ;; # IPIP tunnel
		  769)	TYPE=ip6tnl ;; # IP6IP6 tunnel
		  772)	TYPE=lo ;;
		  776)	TYPE=sit ;; # sit0 device - IPv6-in-IPv4
		  778)	TYPE=gre ;; # GRE over IP
		  783)	TYPE=irda ;; # Linux-IrDA
		  801)	TYPE=wlan_aux ;;
		65534)	TYPE=tun ;;
	esac
	# The following case statement still has to be replaced by something
	# which does not rely on the interface names.
	case $IF in
		ippp*|isdn*) TYPE=isdn;;
		mip6mnha*)   TYPE=mip6mnha;;
	esac
	test -n "$TYPE" && echo $TYPE && return 0
	return 3
}

get_iface_type_from_config () {
	local VARIABLE VALUE TYPE IF=$1
	local INTERFACETYPE WIRELESS WIRELESS_MODE BONDING_MASTER ETHERDEVICE
	local BRIDGE TUNNEL PPPMODE MODEM_DEVICE ENCAP
	test -z "$IF" && return 3

	get_ibft_iface_type "$IF" && return 0

	test -r ifcfg-$IF || return 2
	# If variable (1st word) has the right value (2nd word) then this
	# interface is of this type (3rd word). If variable can have any
	# value except empty, then use "_not_empty_" as value.
	# INTERFACETYPE gets special handling and has variable type
	while read VARIABLE VALUE TYPE; do
		eval $VARIABLE=
		get_variable $VARIABLE $IF
		if [ "$VALUE" == _not_empty_ ] ; then
			test -n "${!VARIABLE}" && break
		else
			test "${!VARIABLE}" == "$VALUE" && break
		fi
	done <<- EOL
		INTERFACETYPE _not_empty_
		WIRELESS yes wlan
		WIRELESS_MODE _not_empty_ wlan
		BONDING_MASTER yes bond
		ETHERDEVICE _not_empty_ vlan
		BRIDGE yes bridge
		TUNNEL tap tap
		TUNNEL tun tun
		TUNNEL sit sit
		TUNNEL gre gre
		TUNNEL ipip ipip
		PPPMODE pppoe ppp
		PPPMODE pppoatm ppp
		PPPMODE capi-adsl ppp
		PPPMODE pptp ppp
		MODEM_DEVICE _not_empty_ ppp
		ENCAP syncppp isdn
		ENCAP rawip isdn
	EOL
	case "$IF" in
		ib*.*) INTERFACETYPE=ibchild ;;
		ib*)   INTERFACETYPE=ib      ;;
	esac

	# bnc#458412: map obsolete (s390) eth type overrides
	case "$INTERFACETYPE" in
		(qeth|hsi|lcs|ctc|iucv|tr) INTERFACETYPE=eth ;;
	esac

	test -n "$INTERFACETYPE" && echo $INTERFACETYPE && return
	if [ -z "$TYPE" ] ; then
		case $IF in
			lo)          TYPE=lo;;
			dummy*)      TYPE=dummy;;
			ib*)         TYPE=ib;;
			ip6tnl*)     TYPE=ip6tnl;;
			mip6mnha*)   TYPE=mip6mnha;;
		esac
	fi
	# There is a config file, but it has no special type: This must be ethernet
	test -n "$TYPE" && echo $TYPE || echo eth
}

get_hwaddress () {
	cat /sys/class/net/$1/address 2>/dev/null
}

get_bonding_slave_hwaddrs()
{
	local ifname="${1}"
	local saddrs=()
	if test -f "/proc/net/bonding/$ifname" ; then
		while read line ; do
			case $line in
			"Permanent HW addr:"*)
				saddr+=("${line//Permanent HW addr: /}")
			;;
			esac
		done < "/proc/net/bonding/$ifname" 2>/dev/null
		echo "${saddr[*]}"
		return 0
	fi
	return 1
}

# This will echo the first address listed for the given interface
# ignoring all addresses with a label.
get_ipv4address () {
	test -z "$1" && return 1
	local a b c
	while read a b c; do
		if [ "$a" = inet ] ; then
			break
		fi
	done < <(LC_ALL=POSIX ip -4 address show dev "$1" label "$1" 2>/dev/null)
	test -z "$b" && return 1
	echo ${b%%/*}
}

convert_ipv4address_to_6to4 () {
	printf "2002:%02x%02x:%02x%02x::1\n" $(IFS=.; echo $1)
}

convert_6to4_to_ipv4address () {
	ADDR=$1
	LIST=($(IFS=:; echo $ADDR))
	if [ ${#LIST[@]} -ne 4 -o "${LIST[0]}" != "2002" ] ; then
		echo $ADDR
	fi
	NORM_1=`printf "%04x" 0x${LIST[1]} 2>/dev/null`
	NORM_2=`printf "%04x" 0x${LIST[2]} 2>/dev/null`

	printf "::%u.%u.%u.%u" \
		0x${NORM_1:0:2} 0x${NORM_1:2:2} \
		0x${NORM_2:0:2} 0x${NORM_2:2:2}
}

get_variable () {
	local line
	while read line; do
		eval $line
	done < <(read_iface_config_data "$2" | grep "^[[:space:]]*$1" 2>/dev/null)
}

get_startmode () {
	local STARTMODE
	get_variable STARTMODE $1
	echo  "$STARTMODE"
}

get_ifplugd_priority () {
	local IFPLUGD_PRIORITY=0
	declare -i IFPLUGD_PRIORITY
	get_variable IFPLUGD_PRIORITY $1
	echo "$IFPLUGD_PRIORITY"
}

# return vlan interface name for given base device and vlan id
get_vlan_ifname()
{
	test "x$1" = x -o "x$2" = x && return 1
	LANG=C LC_ALL=C awk -F '[ \t]*[|][ \t]*' -v dev="$1" -v vid="$2" -- \
	'$3 == dev && $2 == vid { print $1; }' /proc/net/vlan/config 2>/dev/null
}
# return base device (e.g. eth0) of given vlan interface (e.g. eth0.42)
get_vlan_if_dev()
{
	test "x$1" = x && return 1
	LANG=C LC_ALL=C awk -F '[ \t]*[|][ \t]*' -v vif="$1" -- \
	'$1 == vif { print $3; }' /proc/net/vlan/config 2>/dev/null
}
# return vlan id (e.g. 42) of given vlan interface (e.g. eth0.42)
get_vlan_if_vid()
{
	test "x$1" = x && return 1
	LANG=C LC_ALL=C awk -F '[ \t]*[|][ \t]*' -v vif="$1" -- \
	'$1 == vif { print $2; }' /proc/net/vlan/config 2>/dev/null
}

# Parse iSCSI Boot Firmare Table settings of given interface
# and provide as adequate ifcfg/netcontrol scripts settings
# (basically STARTMODE and BOOTPROTO variables) on stdout.
get_ibft_iface_ref_glob()
{
	local edir=$1 # /sys/firmware/ibft/ethernetX

	# iBFT refers NICs by PCI Bus/Dev/Func, but in case
	# of at least virtio, the interface isn't attached
	# directly as PCI device ($edir/device/virtio0/net/).
	if test -d "$edir/device/net" ; then
		echo "$edir/device/net/*"
	else
		echo "$edir/device/*/net/*"
	fi
	return 0
}

read_ibft_iface_config()
{
	local ifname=$1
	case $ifname in ""|.|..|*/*) return 1 ;; esac

	# check if ibft firmware info is available
	local sysfs_ibft="/sys/firmware/ibft"
	# done in initrd on hosts using ibft
	#test -d "$sysfs_ibft" || modprobe -qs iscsi_ibft 2>/dev/null
	test -d "$sysfs_ibft" || return 1

	local edir ibft_e_flags ibft_e_origin ibft_e_vlan
	local restore_nullglob=`shopt -p nullglob`
	shopt -s nullglob
	for edir in "$sysfs_ibft/ethernet"* ; do
		# Flag Bits: Block Valid, FW Boot Selected, Global/Link Local
		read -s ibft_e_flags 2>/dev/null < "$edir/flags"

		# Check flag bit 0: Block Valid Flag (0 = no, 1 = yes)
		# and ignore when the IBFT settings not marked valid
		((ibft_e_flags & 0x01)) || continue

		# Read the vlan id from iBFT and compare
		read -s ibft_e_vlan 2>/dev/null < "$edir/vlan"

		local vid="${ibft_e_vlan:-0}"
		if test "${vid}" != "0" ; then
			local ref dev vif
			for ref in `get_ibft_iface_ref_glob "$edir"` ; do
				test -d "$ref"          || continue 2
				dev="${ref##*/}"
				vif=`get_vlan_ifname "${dev}" "${vid}"`
				vif="${vif:-${dev}.${vid}}"
				break
			done

			# no ibft -> iface reference?!
			test -n "$dev" || continue

			# check if it is the physical iBFT vlan base
			if test "$dev" = "$ifname" ; then
				# just a minimal set here ...
				echo "STARTMODE='nfsroot'"
				echo "BOOTPROTO='none'"
				echo "IBFT='yes'"
				echo "IBFT_NIC='${edir##*/}'"
				return 0
			fi

			# check if ifname is a vlan iBFT interface
			test "$vif" = "$ifname" || continue

			echo "ETHERDEVICE='$dev'"
			echo "VLAN_ID='$vid'"
		else
			# check if ifname is a physical iBFT interface
			for ref in `get_ibft_iface_ref_glob "$edir"` ; do
				test -d "$ref"          || continue 2
				dev="${ref##*/}"
				test "$dev" = "$ifname" || continue 2
				break
                        done
		fi

		# iBFT interfaces are always nfsroot
		echo "STARTMODE='nfsroot'"

		# Enum: Other,Manual,WellKnown,Dhcp,RouterAdv
		read -s ibft_e_origin  2>/dev/null < "$edir/origin"
		case $ibft_e_origin in
		0)	# Other, but it is used e.g. on kvm gPXE
			# (11.4), even the data is from dhcp.
			case $ibft_e_dhcp in
				[1-9]*.*.*.*) echo "BOOTPROTO='dhcp4'" ;;
				*)            echo "BOOTPROTO='none'"  ;;
			esac
		;;
		3)	# IpPrefixOriginDhcp
			echo "BOOTPROTO='dhcp4'"
		;;
		*)	echo "BOOTPROTO='none'"	;;
		esac

		# the complete set of IBFT variables
		echo "IBFT='yes'"
		# Check bit 1, FW Boot Selected (0 = no, 1 = yes)
		# and set IBFT_PRIMARY=yes (force a dhcp primary)
		if ((ibft_e_flags & 0x02)) ; then
			echo "IBFT_PRIMARY='yes'"
		fi
		echo "IBFT_NIC='${edir##*/}'"
		echo "IBFT_VLAN='$ibft_e_vlan'"
		echo "IBFT_FLAGS='$ibft_e_flags'"
		echo "IBFT_ORIGIN='$ibft_e_origin'"
		# read the other ibft ethernet variables
		local v
		for v in ip-addr primary-dns secondary-dns gateway index dhcp mac ; do
			local vv=''
			read  vv 2>/dev/null < "$edir/$v"
			case $v in
				ip-addr)	echo "IBFT_IPADDR='$vv'" ;;
				primary-dns)	echo "IBFT_DNS_1='$vv'"  ;;
				secondary-dns)	echo "IBFT_DNS_2='$vv'"  ;;
				gateway)	echo "IBFT_GATEWAY='$vv'";;
				index)		echo "IBFT_INDEX='$vv'"  ;;
				dhcp)		echo "IBFT_DHCP='$vv'"   ;;
				mac)		echo "IBFT_MAC='$vv'"    ;;
				#index|dhcp|gateway|mac)
				#	echo "IBFT_${f^^}='$vv'"
				#;;
			esac
		done
		eval $restore_nullglob
		return 0
	done
	eval $restore_nullglob
	return 1
}

# prints (the "eth" or "vlan") interface type of given ibft interface
# note, that when a ibft vlan interface name does not exist yet, the
# default <physical interface name>.<vlan id> name scheme is used.
get_ibft_iface_type()
{
	local ifname=$1
	case $ifname in ""|.|..|*/*) return 1 ;; esac

	# check if ibft firmware info is available
	local sysfs_ibft="/sys/firmware/ibft"
	# done in initrd on hosts using ibft
	#test -d "$sysfs_ibft" || modprobe -qs iscsi_ibft 2>/dev/null
	test -d "$sysfs_ibft" || return 1

	local iftype=""
	local edir ibft_e_flags ibft_e_origin ibft_e_vlan
	local restore_nullglob=`shopt -p nullglob`
	shopt -s nullglob
	for edir in "$sysfs_ibft/ethernet"* ; do
		# Flag Bits: Block Valid, FW Boot Selected, Global/Link Local
		read -s ibft_e_flags 2>/dev/null < "$edir/flags"

		# Check flag bit 0: Block Valid Flag (0 = no, 1 = yes)
		# and ignore when the IBFT settings not marked valid
		((ibft_e_flags & 0x01)) || continue

		# Read the vlan id from iBFT and compare
		read -s ibft_e_vlan 2>/dev/null < "$edir/vlan"

		local vid="${ibft_e_vlan:-0}"
		if test "${vid}" != "0" ; then
			local ref dev vif
			for ref in `get_ibft_iface_ref_glob "$edir"` ; do
				test -d "$ref"          || continue 2
				dev="${ref##*/}"
				vif=`get_vlan_ifname "${dev}" "${vid}"`
				vif="${vif:-${dev}.${vid}}"
				break
			done

			# no ibft -> iface reference?!
			test -n "$dev" || continue

			# check if it is the physical iBFT vlan base
			if test "$dev" = "$ifname" ; then
				iftype="eth"
				break
			fi

			# check if ifname is a vlan iBFT interface
			test "$vif" = "$ifname" || continue

			iftype="vlan"
			break
		else
			# check if ifname is a physical iBFT interface
			for ref in `get_ibft_iface_ref_glob "$edir"` ; do
				test -d "$ref"          || continue 2
				dev="${ref##*/}"
				test "$dev" = "$ifname" || continue 2
				break
                        done
			iftype="eth"
			break
		fi
	done
	eval $restore_nullglob
	test -n "$iftype" && echo "$iftype"
}

# returns names of physical _and_ vlan interface names referenced by ibft
get_ibft_config_names()
{
	local ifname_list=""
	local restore_nullglob=`shopt -p nullglob`

	# check if ibft firmware info is available
	local sysfs_ibft="/sys/firmware/ibft"
	# done in initrd on hosts using ibft
	#test -d "$sysfs_ibft" || modprobe -qs iscsi_ibft 2>/dev/null
	if test -d "$sysfs_ibft" ; then
		local edir ref ibft_e_flags ibft_e_vlan
		shopt -s nullglob
		for edir in "$sysfs_ibft/ethernet"* ; do
			# Flag Bits: Block Valid, FW Boot Selected, Global/Link Local
			read -s ibft_e_flags 2>/dev/null < "$edir/flags"
			read -s ibft_e_vlan  2>/dev/null < "$edir/vlan"

			# Check flag bit 0: Block Valid Flag (0 = no, 1 = yes)
			# and ignore when the IBFT settings not marked valid
			((ibft_e_flags & 0x01)) || continue

			for ref in `get_ibft_iface_ref_glob "$edir"` ; do
				test -d "$ref"          || continue 2
				local dev="${ref##*/}"
				add_to_wordlist ifname_list "$dev"
				if test "${ibft_e_vlan:-0}" != 0 ; then
					local vid="${ibft_e_vlan:-0}"
					local vif=`get_vlan_ifname "${dev}" "${vid}"`
					add_to_wordlist ifname_list "${vif:-${dev}.${vid}}"
				fi
				break
			done
		done
		echo $ifname_list
	fi

	eval $restore_nullglob
	return 0
}

# returns ifcfg configured interface names
# note: config name is ifcfg-<interface name>,
#       there is no ifcfg-bus-pci... any more
get_ifcfg_config_names()
{
	local ifname_list=""
	local restore_nullglob=`shopt -p nullglob`

	local ifcfg_dir=/etc/sysconfig/network
	local ifcfg_pfx=ifcfg-
	for ifcfg in "${ifcfg_dir}/${ifcfg_pfx}"* ; do
		local ifname=${ifcfg##*/${ifcfg_pfx}}
		case "$ifname" in
			""|*~|*.old|*.rpmnew|*.rpmsave|*.scpmbackup)
			continue
		;;
		esac
		add_to_wordlist ifname_list "$ifname"
	done

	eval $restore_nullglob
	echo $ifname_list
	return 0
}

# returns a list of ibft and ifcfg configured interface names
get_iface_config_names()
{
	local ifname_list=""

	# TODO: ibft may return eth0.42 (that does not exists yet)
	#       what when there is a ifcfg-vlan42 on top of eth0 ?
	#	basically the second one will fail to create ...
	add_to_wordlist ifname_list `get_ibft_config_names`
	add_to_wordlist ifname_list `get_ifcfg_config_names`

	echo $ifname_list
}

# reads interface configuration data to stdout
read_iface_config_data()
{
	case $1 in ""|.|..|*/*) return 1 ;; esac

	read_ibft_iface_config "$1" && return 0
	test -f "./ifcfg-$1" && cat "./ifcfg-$1" 2>/dev/null
}

# sources interface config from ibft or from ifcfg file
source_iface_config()
{
	case $1 in ""|.|..|*/*) return 1 ;; esac

	eval IBFT=no `read_ibft_iface_config "$1"`
	test "$IBFT" = "yes" && return 0
	test -f "./ifcfg-$1" && . "./ifcfg-$1" 2>/dev/null
}

# returns true when interface is configured in ibft
# or when a ifcfg-<interface name> config file exists
exists_iface_config()
{
	case $1 in ""|.|..|*/*) return 1 ;; esac

	for name in `get_ibft_config_names` ; do
		test "x$1" = "x$name" && return 0
	done
	test -f "./ifcfg-$1"
}

# return the content of the BOOTIF variable from /proc/cmdline
get_pxe_bootif_param()
{
	local cmdline=$(cat /proc/cmdline 2>/dev/null)
	local rx='^(.*[[:space:]])?BOOTIF=([[:xdigit:]]{2}([-[:xdigit:]]{2})+)([[:space:]].*)?$'
	if [[ ${cmdline} =~ ${rx} ]] ; then
		echo "${BASH_REMATCH[2]}"
	fi
}

is_pxe_boot_iface()
{
	# params <interface name>
	# output 'yes': bootif param available, $1 interface match
	#        'no' : bootif param available, no $1 interface match
	#        ''   : otherwise (no bootif param, errors)
	# returns 0 on success, 1 on errors
	local bootif=`get_pxe_bootif_param 2>/dev/null`
	test "x$bootif" = x && return 0

	local ifname="$1"
	test  "x$ifname" != x -a -d "/sys/class/net/$ifname" || return 1
	local iftype=`cat "/sys/class/net/$ifname/type" 2>/dev/null`
	local ifaddr=`get_hwaddress "$ifname"`
	test "x$iftype" != x -a "x$ifaddr" != x || return 1

	local maddr=${bootif//-/:}
	local addrs="$ifaddr" addr
	case $iftype in
	1)
		# TODO: bridge and vlan inherit hwaddrs as well
		if test -d "/sys/class/net/$ifname/bonding" ; then
			addrs=`get_bonding_slave_hwaddrs "$ifname" 2>/dev/null`
		fi
	;;
	esac
	for addr in ${addrs} ; do
		local x=`printf "%02x:%s" "$iftype" "$addr" 2>/dev/null`
		if test "x$maddr" = "x$x" ; then
			echo "yes"
			return 0
		fi
	done
	echo "no"
}

is_ibft_primary_iface()
{
	# params <interface name>
	# output 'yes': ibft interfaces available, $1 interface is ibft primary
	#        'no' : ibft interfaces available, $1 interface is not primary
	#        ''   : otherwise (no ibft, errors)
	# returns 0 on success, 1 on errors

	if test "x$IBFT" = "xyes" ; then
		echo ${IBFT_PRIMARY:-no}
		return 0
	else
		echo ''
		return 0
	fi
}

get_slaves () {
	local ret=1
	local v vv
	case $1 in
		ib*.*) echo -n "${1%%\.*} " ; ret=0 ;;
	esac
	for v in BONDING_SLAVE ETHERDEVICE TUNNEL_DEVICE \
	         TUNNEL_LOCAL_INTERFACE BRIDGE_PORTS; do
		get_variable $v $1
		for vv in `eval echo \$\{\!$v\*\}`; do
			if [ -n "${!vv}" ] ; then
				echo -n "${!vv} "
				ret=0
			fi
			unset $vv
		done
		test $ret = 0 && return 0
	done
	return 1
}

get_bonding_master () {
	local IF="$1"
	test "x$IF" = "x" && return 1

	local a master slave
	while IFS=- read a master; do
		case $master in
		""|*~|*.old|*.rpmnew|*.rpmsave|*.scpmbackup)
			continue
		;;
		esac

		local BONDING_MASTER
		get_variable BONDING_MASTER "$master"
		test "$BONDING_MASTER" = yes || continue

		unset ${!BONDING_SLAVE*}
		get_variable BONDING_SLAVE "$master"
		for slave in ${!BONDING_SLAVE*} ; do
			if test "x$IF" = "x${!slave}" ; then
				echo "$master"
				return 0
			fi
		done
	done < <(ls -1d /etc/sysconfig/network/ifcfg-* 2>/dev/null)
}

get_bridge_parent () {
	local IF="$1"
	test "x$IF" = "x" && return 1

	local a bridge port
	while IFS=- read a bridge; do
		case $bridge in
		""|*~|*.old|*.rpmnew|*.rpmsave|*.scpmbackup)
			continue
		;;
		esac

		local BRIDGE
		get_variable BRIDGE "$bridge"
		test "$BRIDGE" = yes || continue

		unset BRIDGE_PORTS
		get_variable BRIDGE_PORTS "$bridge"
		for port in $BRIDGE_PORTS ; do
			if test "x$IF" = "x${port}" ; then
				echo "$bridge"
				return 0
			fi
		done
	done < <(ls -1d /etc/sysconfig/network/ifcfg-* 2>/dev/null)
}

# This function looks for interfaces which depend on the given interface. It
# prints a list with all depending interfaces. It returns 0 if there are
# depending interfaces and !=0 if not.
# Currently it checks only for vlan and optionally bonding slave interfaces.
# FIXME: Add other types of interfaces that depend on others.
get_depending_ifaces() {
	local VLAN_PATH BOND_PATH DEP_VLANS DEP_BONDS BASE_IFACE i
	local -a DEP_IFACES=()
	VLAN_PATH="/proc/net/vlan"
	BOND_PATH=""
	while [ $# -gt 0 ]; do
		case $1 in
		--with-bonding-slaves)
			BOND_PATH="/proc/net/bonding"
			shift
		;;
		-*)	shift ;;
		*)	break ;;
		esac
	done
	BASE_IFACE="$1"

	if [ -z "$BASE_IFACE" ]; then
		return 1
	fi

	if [ -d "$VLAN_PATH" ]; then
		DEP_VLANS=`cd "$VLAN_PATH"
			grep -lws "Device: *$BASE_IFACE" *`
		DEP_IFACES+=($DEP_VLANS)
	fi

	if [ -n "$BOND_PATH" -a -d "$BOND_PATH" ]; then
		DEP_BONDS=`cd "$BOND_PATH"
			grep -s '^Slave Interface:' $BASE_IFACE |
			while IFS=':' read text iface ; do echo -n "$iface" ; done`
		DEP_IFACES+=($DEP_BONDS)
	fi

	case $BASE_IFACE in
		(ib*.*)	;;
		(ib*)	# the infiniband children -- is there a better way?
			for i in `ls -1 /sys/class/net/ 2>/dev/null` ; do
				test -d /sys/class/net/$i || continue
				case $i in (${BASE_IFACE}.*)
					DEP_IFACES+=($i)
				;;
				esac
		     	done
		;;
	esac

	if [ ${#DEP_IFACES[*]} -gt 0 ]; then
		echo "${DEP_IFACES[*]}"
		return 0
	else
		return 1
	fi
}

resolve_iface_startorder()
{
    #
    # resolve_iface_startorder <name of the result variable>
    #                          <list of interfaces to resolve>
    #                          [ <skip list> [filter fuction] ]
    #
    # This function creates a start ordered list of virtual interfaces
    # as bondings, vlans and bridges known by the get_slaves() function.
    # It reads the base or "slave" interfaces of each virtual interface
    # recursively and inserts the slaves into the result list before the
    # virtual interface itself. Further it detects interface usage loops
    # like: interface "a" needs "b" and "b" needs "a" and skips them.
    #
    # The optional skip list allows to stop the recursion insertion at
    # e.g. already started / existing physical slave interfaces.
    # Addding interfaces to resolve to this skip list causes resolving
    # of the recursive list of slave interfaces without the requested
    # interfaces themself (resolve_iface_startorder slaves "br0" "br0").
    #
    # The optional filter function allows to filter out interfaces as
    # well as all depending interfaces. For example a vlan interface
    # will be filtered out, when the underlying interface gets filtered
    # out. This happens also, when the function does not cause to filter
    # out the vlan interface directly. Main purpose is to filter out
    # interfaces, that are not supported without a mounted remotefs.
    #
    # $1:  the name of the result start ordered list variable to set
    # $2:  list of virtual interfaces to resolve the start order for
    # $3:  optional list of interfaces to skip/stop reading
    # $4:  optional (white list) filter function returning true for
    #      acceptable interfaces and false to filter out an interface.
    #
    # returns 0 on success,
    #         1 on empty result list name,
    #         [2 on interface usage loop error; disabled]
    #
    local NAME="$1"
    local TODO="$2"
    local SKIP="$3"
    local FUNC="$4"
    local LIST=()

    _resolve_iface_startorder() {
	local  todo="$1"
	local  skip="$2"
	local  func="$3"
	local guard="$4"
	local level="$5"
	local slaves iface i ret
	local result=0
	for iface in ${todo} ; do
		for i in ${guard} ; do
			if [ "x${i}" = "x${iface}" ] ; then
				err_mesg "Interface dependency loop " \
				         "detected: ${guard} ${iface}"
				# guard non-empty in level > 0
				return 2
			fi
		done

		if [ "x$func" != x ] && ! $func "$iface" &>/dev/null ; then
			[ $level -eq 0 ] && continue || return 3
		fi

		slaves=(`get_slaves $iface 2>/dev/null`)
		if [ $? = 0 -a ${#slaves[@]} -gt 0 ] ; then
			ret=0
			_resolve_iface_startorder \
				"${slaves[*]}"    \
				"${skip}"         \
				"${func}"         \
				"${guard} $iface" \
                                $(($level+1))     \
				|| ret=$?
			if [ $ret -ne 0 ] ; then
				if [ $level -eq 0 ] ; then
					#result=$ret
					continue
				else
					return $ret
				fi
			fi
		fi

		for i in ${LIST[@]} ${skip} ; do
			[ "x$i" = "x$iface" ] && continue 2
		done

		LIST=(${LIST[@]} $iface)
	done
	return $result
    }

    [ "x$NAME" = x ] && return 1

    _resolve_iface_startorder "$TODO" "$SKIP" "$FUNC" "" 0 || return $?

    eval "$NAME='${LIST[@]}'"
}


# returns 0 if there is a dhcp client running on this interface
# prints pids of all dhcp clients on this interface
# prints nothing if called with option '-q'
# Usually it should not happen that more then one dhcpcd is running on one
# interface, but it may happen. So better safe than sorry!
dhcpc4_on_iface() {
	local pid retval=1
	[ "x$DHCLIENT"  != x -a "x$INTERFACE" != x ] || return $retval
	# when the dhcp client forks, it may be not visible
	# in the process list for a short (usleep) while...
	typeset -i retries=3
	for ((; retries > 0; retries--)) ; do
	    for pid in `pgrep -f "^(/.*/)?$DHCLIENT\>.*\<$INTERFACE\>$" 2>/dev/null` ; do
		retval=0
		test "x$1" == "x-q" && break
		echo $pid
	    done
	    (( retval == 0 )) && break || usleep 100000
	done
	return $retval
}
dhcpc6_on_iface() {
	local pid retval=1
	[ "x$DHCLIENT6"  != x -a "x$INTERFACE" != x ] || return $retval
	# when the dhcp client forks, it may be not visible
	# in the process list for a short (usleep) while...
	typeset -i retries=3
	for ((; retries > 0; retries--)) ; do
	    for pid in `pgrep -f "^(/.*/)?$DHCLIENT6\>.*\<$INTERFACE\>$" 2>/dev/null` ; do
		retval=0
		test "x$1" == "x-q" && break
		echo $pid
	    done
	    (( retval == 0 )) && break || usleep 100000
	done
	return $retval
}
dhcpc_on_iface() {
	local pid retval=1 pattern
	[ "x$DHCLIENT"  != x ] && pattern=$DHCLIENT || pattern=""
	[ "x$DHCLIENT6" != x ] && pattern="${pattern:+$pattern|}$DHCLIENT6"
	[ "x$pattern" != x -a "x$INTERFACE" != x ] || return $retval
	# when the dhcp client forks, it may be not visible
	# in the process list for a short (usleep) while...
	typeset -i retries=3
	for ((; retries > 0; retries--)) ; do
	    for pid in `pgrep -f "^(/.*/)?($pattern)\>.*\<$INTERFACE\>$" 2>/dev/null` ; do
		retval=0
		test "$1" == "-q" && break
		echo $pid
	    done
	    (( retval == 0 )) && break || usleep 100000
	done
	return $retval
}
any_dhcpc_on_iface()
{
	local pid retval=1 pattern=""
	[ "x$INTERFACE" != x ] || return $retval
	# just search for all known dhcp clients
	pattern="dhcpcd|dhclient|dhcp6c|dhclient6"
	# when the dhcp client forks, it may be not visible
	# in the process list for a short (usleep) while...
	typeset -i retries=3
	for ((; retries > 0; retries--)) ; do
	    for pid in `pgrep -f "^(/.*/)?($pattern)\>.*\<$INTERFACE\>$" 2>/dev/null` ; do
		retval=0
		test "$1" == "-q" && break
		echo $pid
	    done
	    (( retval == 0 )) && break || usleep 100000
	done
	return $retval
}
dhcp_interfaces() {
	local old_if=$INTERFACE
	for INTERFACE in `ls -1 /sys/class/net`; do
		if test -d "/sys/class/net/$INTERFACE" ; then
			any_dhcpc_on_iface -q && echo "$INTERFACE"
		fi
	done
	INTERFACE=$old_if
	return 0
}

# We have to write status files per interface or per configuration for at least
# these reasons:
# 1) remember the used configuration if getcfg cannot get it after the device
#    has been unplugged  --> OBSOLETE: no more getcfg
# 2) store ifup options while restarting the network (e.g. the choosen provider)
# 3) pass status information to smpppd to allow kinternet to show them to the
#    user.
# 4) control running ifup/down processes (ifdown has to stop a running ifup)
# To handle this cached information, there are the *_cached_config_data
# functions.

# write_cached_config_data <type> <data> <name> [PFX=<prefix>]
# needs at least 3 arguments
# - the type of data to write: config, options, state, ...
# - the data itself
# - the configuration or interface name
# - the file prefix is optional and must be given in the form PFX=<prefix>
#   (default prefix is 'if-'
# prints nothing
# You have to commit changes after writing with commit_cached_config_data()
write_cached_config_data () {
	touch $RUN_FILES_BASE/tmp/test 2>/dev/null || return 1
	local PFX FILE TMPFILE MODFILE
	test -n "$4" && eval $4
	: ${PFX:=if-}
	FILE=$RUN_FILES_BASE/$PFX$3
	MODFILE=$RUN_FILES_BASE/tmp/$PFX$3.$$                  # MODFILE
	TMPFILE=$RUN_FILES_BASE/tmp/$PFX$3.$$.tmp              # MODFILE
	test -f $MODFILE || cp $FILE $MODFILE 2>/dev/null
	FILE=$MODFILE                                       # MODFILE
	touch $FILE
	while IFS== read a b; do
		case $a in
			$1) ;;
			 *) echo "$a=$b" ;;
		esac
	done < <(cat $FILE) > $TMPFILE
	if [ -n "$2" ] ; then
		echo "$1=$2" >> $TMPFILE
	fi
	if [ -f $TMPFILE ] ; then
		mv $TMPFILE $FILE
	fi
}

# INTERFACE=`read_cached_config_data <type> <name> [PFX=<prefix>]`
# needs at least 2 arguments
# - the type of data to read: config, options, state, ...
# - the configuration or interface name
# - the file prefix is optional and must be given in the form PFX=<prefix>
#   (default prefix is 'if-'
# prints the wanted data
read_cached_config_data () {
	touch $RUN_FILES_BASE/tmp/test 2>/dev/null || return 1
	local PFX
	test -n "$3" && eval $3
	: ${PFX:=if-}
	if [ -r "$RUN_FILES_BASE/$PFX$2" ] ; then
		while IFS== read a b; do
			case $a in
				$1) echo "$b" ;;
				 *) ;;
			esac
		done < $RUN_FILES_BASE/$PFX$2
	fi
}

# delete_from_cached_config_data <type> [<data> [<name>]] [PFX=<prefix>]
# Deletes an entry "$1=$2" from all config data cache files.
# If there is a third argument, we delete it only from this configuration. All
# handled files that are empty after modification will be deleted.
# If $2 is empty then remove line $1=* from this ($3) or all configuration.
# If $1 is '*' it will remove all entries.
#
# !!! WIP !!!
# It currently works only on one file and 2nd and 3rd argument are mandatory
# !!! WIP !!!
#
# needs at least 1 argument
# - the type of data to delete: config, options, state, ...
# - optional the data itself
# - optional the configuration or interface name
# - the file prefix is also optional and must be given in the form PFX=<prefix>
#   (default prefix is 'if-'
# prints nothing
# You have to commit changes after deleting with commit_cached_config_data()
delete_from_cached_config_data () {
	touch $RUN_FILES_BASE/tmp/test 2>/dev/null || return 1
	local TYPE DATA PFX FILE TMPFILE MODFILE NAME
	TYPE=$1; shift
	if [ "$1" = "${1#PFX}" ] ; then
		DATA=$1; shift
	fi
	if [ "$1" = "${1#PFX}" ] ; then
		NAME=$1; shift
	fi
	test -n "$1" && eval $1
	: ${PFX:=if-}
	FILE=$RUN_FILES_BASE/$PFX$NAME                 # MODFILE
	MODFILE=$RUN_FILES_BASE/tmp/$PFX$NAME.$$          # MODFILE
	TMPFILE=$RUN_FILES_BASE/tmp/$PFX$NAME.$$.tmp      # MODFILE
	test -f $MODFILE || cp $FILE $MODFILE 2>/dev/null
	FILE=$MODFILE                                       # MODFILE
	touch $FILE
		if [ -s "$FILE" ] ; then
			while IFS== read a b; do
				case $a in
					$TYPE)
						if [ "$b" != "$DATA" -a -n "$DATA" ] ; then
							echo "$a=$b" 
						fi
						;;
					 *) echo "$a=$b" ;;
				esac
			done < <(cat $FILE) > $TMPFILE
		fi
		if [ -f $TMPFILE ] ; then
			mv $TMPFILE $FILE
		fi
		if [ ! -s $FILE ] ; then
			rm -Rf $FILE
		fi
#	done   MODFILE
}

# HWDESC NIX < <(grep_cached_config_data <type> <data> [PFX=<prefix>])
# needs 2 arguments:
# - the type of data to grep for: config, options, state, ...
# - the data itself
# - the file prefix is optional and must be given in the form PFX=<prefix>
#   (default prefix is 'if-'
# prints all matching configuration names in a single line
grep_cached_config_data () {
	touch $RUN_FILES_BASE/tmp/test 2>/dev/null || return 1
	local PFX
	test -n "$3" && eval $3
	: ${PFX:=if-}
	local restore_nullglob="$(shopt -p nullglob)"
	shopt -s nullglob
	for f in $RUN_FILES_BASE/$PFX*; do
		while IFS== read a b; do
			case $a in
				$1)
					if [ "$b" = "$2" ] ; then
						echo -n "${f#$RUN_FILES_BASE/$PFX} " 
					fi
					;;
			esac
		done < $f
	done
	eval $restore_nullglob
	echo
}

# Writing and deleting cached config data is always done in temporary files. To
# make this changes visible in the right file you must commit the changes. This
# helps to make file changes atomic.
commit_cached_config_data () {
	touch $RUN_FILES_BASE/tmp/test 2>/dev/null || return 1
	local PFX FILE MODFILE
	test -n "$2" && eval $2
	: ${PFX:=if-}
	FILE=$RUN_FILES_BASE/$PFX$1
	MODFILE=$RUN_FILES_BASE/tmp/$PFX$1.$$
	if [ -f $MODFILE ] ; then
		mv $MODFILE $FILE
	else
		rm -f $FILE
	fi
}


_set_ethtool_options()
{
	local iface=$1
	local ethtool=$2
	local options=$3
	local ret out option xifacex settings

	test "x$options" = "x"                    && return 0
	test "x$iface" = "x" -o "x$ethtool" = "x" && return 1

	case $options in
	-*)	# got an option, replace second word with current interface name
		read option xifacex settings < <(echo "$options")
		options="$option $iface $settings"
	;;
	*)	# old style, setting a parameter...
		options="-s $iface $options"
	;;
	esac

	out=`$ethtool $options 2>&1`
	ret=$?
	if test $ret -eq 0 ; then
		info_mesg "$ethtool $options${out:+: $out}"
	else
		err_mesg "Error while executing '$ethtool $options': [$ret] $out"
	fi
	return $ret
}

set_ethtool_options()
{
	local iface=${1:-$INTERFACE}
	local tool var

	test "x$iface" = x && return 1
	for tool in /sbin/ethtool /usr/sbin/ethtool ; do
		test -x "$tool" && break || unset tool
	done

	for var in ${!ETHTOOL_OPTIONS*} ; do
		test "x${!var}" = "x" && continue
		if test -z "$tool" ; then
			err_mesg "ethtool is not installed"
			return 1
		fi
		_set_ethtool_options "$iface" "$tool" "${!var}"
	done
	return 0
}

have_ethtool_options()
{
	local var
	for var in ${!ETHTOOL_OPTIONS*} ; do
		test "x${!var}" = "x" || return 0
	done
	return 1
}

