home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Chip 2007 January, February, March & April
/
Chip-Cover-CD-2007-02.iso
/
boot
/
i386
/
rescue
/
etc
/
sysconfig
/
network
/
scripts
/
functions
< prev
next >
Wrap
Text File
|
2006-11-29
|
16KB
|
568 lines
#!/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, write to the Free Software Foundation, Inc., 59 Temple
# Place, Suite 330, Boston, MA 02111-1307 USA
#
# Authors: Michal Svec <msvec@suse.cz>
# Mads Martin Joergensen <mmj@suse.de>
#
# $Id: functions 1525 2006-11-20 11:39:48Z zoz $
#
. /etc/sysconfig/network/scripts/functions.common
NETWORK_RUNFILE="$RUN_FILES_BASE/network"
STAMPFILE_STUB="$RUN_FILES_BASE/new-stamp-"
NETWORKMANAGER_BIN=/usr/sbin/NetworkManager
NM_ONLINE_BIN=/usr/bin/nm-online
NM_DISPATCHER_BIN=/usr/sbin/NetworkManagerDispatcher
DHCDBD_BIN=/usr/sbin/dhcdbd
#
# to test the next two functions:
#
# for i in $(seq 0 32); do
# echo $i: $(pfxlen2mask $i) " ---> " $(mask2pfxlen $(pfxlen2mask $i))
# done
mask2pfxlen() {
local i octet mask width=0
IFS_SAVE=$IFS; IFS="."
mask=($*)
IFS=$IFS_SAVE
test -n "$mask" || return
for octet in 0 1 2 3; do
test "${mask[octet]}" -ge 0 -a "${mask[octet]}" -le 255 2>/dev/null \
|| return
for i in 128 192 224 240 248 252 254 255; do
test ${mask[octet]} -ge $i && ((width++))
done
done
test $width -ge 0 && echo $width
}
pfxlen2mask() {
local i bit n=1 width=$1
test -n "$width" || return 0
for ((i=1; $i<=$width; i++)); do
bit[$i]=1
done; echo
for o in 1 2 3 4; do
octet[$o]=0
for i in 128 64 32 16 8 4 2 1; do
test ${bit[$n]:-0} -eq 1 && ((octet[$o] = ${octet[$o]} + $i))
((n++))
done
done
echo ${octet[1]}.${octet[2]}.${octet[3]}.${octet[4]}
}
is_iface_available () {
test -z "$1" && return 1
case $1 in
ippp*|isdn*) return 0 ;;
modem*|dsl*|ppp*) return 0 ;;
vlan*) return 0 ;;
sit*|gre*|ipip*) return 0 ;;
esac
test "${SCRIPTNAME%%-*}" = ifdown -a "$MODE" = hotplug && return 0
test "${SCRIPTNAME%%-*}" = ifup -a "$BONDING_MASTER" = yes && return 0
test "${SCRIPTNAME%%-*}" = ifup -a "$BRIDGE" = yes && return 0
ip link list $1 &>/dev/null
}
is_iface_up () {
test -z "$1" && return 1
case "`LC_ALL=POSIX ip link show $1 2>/dev/null`" in
*$1*UP*) ;;
*) return 1 ;;
esac
}
get_hwaddress () {
test -z "$1" && return 1
local a b=""
for a in $(LC_ALL=POSIX ip link show $1 2>/dev/null ); do
if [ "$b" = "link/ether" ] ; then
echo $a
break
fi
b=$a
done
}
# This will echo the first address listed for the given interface.
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 list "$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
PART_1=`expr $ADDR : '2002:\([^:]*\):[^:]*:'`
PART_2=`expr $ADDR : '2002:[^:]*:\([^:]*\):'`
if [ "$PART_1" = "" -o "$PART_2" = "" ]; then
echo $ADDR
fi
NORM_1=`printf "%04x" 0x$PART_1`
NORM_2=`printf "%04x" 0x$PART_2`
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}
}
# Loads module 'bonding' if not already loaded.
# Creates a new bonding master interface and sets its options.
# Usage: load_bond $INTERFACE $BONDING_MODULE_OPTIONS
# Module option 'max_bonds' will be ignored. Use one configuration file per
# bonding interface instead.
# If first argument is '_no_fail_' then failures in setting interface options
# will not return an error.
load_bond() {
local NIF OPT OPT_NAME OPT_VALUE OLD_OPT_VALUE OLD_OPT_VALUE_2 IF NOFAIL
if [ "$1" == _no_fail_ ] ; then
NOFAIL=1
shift
fi
IF=$1
test -z "$IF" && return 0
shift
if [ -d /sys/class/net/$IF -a ! -d /sys/class/net/$IF/bonding ] ; then
return 1 # Iface exists but of another type
fi
if [ ! -r /sys/class/net/bonding_masters ] ; then
/sbin/modprobe bonding
# If we add module option max_bonds=0 in the modprobe command above then
# we may skip the following lines in this if-fi block.
for a in `seq 33`; do
test -r /sys/class/net/bonding_masters && break
usleep 300000
done
NIF=`cat /sys/class/net/bonding_masters`
if [ -n "$NIF" -a "$NIF" != "$IF" ] ; then
nameif -r $IF $NIF
fi
fi
if [ ! -d /sys/class/net/$IF/bonding ] ; then
echo "+$IF" > /sys/class/net/bonding_masters
fi
for a in `seq 33`; do
test -d /sys/class/net/$IF/bonding && break
usleep 300000
done
if [ ! -d /sys/class/net/$IF/bonding ] ; then
return 1
fi
# Set options
sleep 1
for OPT in $*; do
read OPT_NAME OPT_VALUE < <(IFS==; echo $OPT)
if [ "$OPT_NAME" == max_bonds ] ; then
err_mesg "Don't use option max_bonds."
continue
fi
if [ ! -w /sys/class/net/$IF/bonding/$OPT_NAME ] ; then
err_mesg "There is no option '$OPT_NAME' for interface '$IF'."
echo "-$IF" > /sys/class/net/bonding_masters
return 1 # or continue? I guess its better to fail completely
fi
# Some options may only be changed if the interface is up and slaves are
# already assigned. Others may only be changed if it is down. To avoid
# unneccessary error messages or warnings we check first if the option
# already has the specified value.
# Special case for option 'mode': this sysfs attribute contains two
# words. A string describing the mode and the corresponding number. We
# have to compare both.
read OLD_OPT_VALUE OLD_OPT_VALUE_2 < /sys/class/net/$IF/bonding/$OPT_NAME
if [ "$OLD_OPT_VALUE" == "$OPT_VALUE" \
-o \( "$OPT_NAME" == mode -a "$OLD_OPT_VALUE_2" == "$OPT_VALUE" \) \
] ; then
info_mesg "Bonding interface '$IF':" \
"option '$OPT_NAME' is already set to '$OPT_VALUE'"
continue
fi
info_mesg "Bonding interface '$IF':" \
"Setting option '$OPT_NAME' to '$OPT_VALUE'"
if ! echo "$OPT_VALUE" > /sys/class/net/$IF/bonding/$OPT_NAME \
2>/dev/null ; then
err_mesg "Option '$OPT_NAME' of interface '$IF' cannot be set to" \
"'$OPT_VALUE'."
# Should we continue? Its better to fail if not requested differently
test "$NOFAIL" == 1 && continue
echo "-$IF" > /sys/class/net/bonding_masters
return 1
fi
done
return 0
}
# Removes a bonding master interface
# Usage: remove_bond $INTERFACE
remove_bond () {
local IF=$1
if [ ! -d /sys/class/net/$IF ] ; then
return 0 # Interface does not exist; nothing to do
fi
if [ ! -d /sys/class/net/$IF/bonding ] ; then
return 1 # Interface is not a bonding master
fi
ip link set down dev $1
echo "-$IF" > /sys/class/net/bonding_masters
}
get_variable () {
local line
while read line; do
eval $line
done < <(grep "^[[:space:]]*$1" ifcfg-$2 2>/dev/null)
}
get_startmode () {
local STARTMODE
get_variable STARTMODE $1
echo "$STARTMODE"
}
get_slaves () {
local ret=1
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_ifplugd_priority () {
unset HWD_CONFIG_0
eval `getcfg -d . -f ifcfg- "$1"`
if [ -z "$HWD_CONFIG_0" -a -r $RUN_FILES_BASE/config-$1 ] ; then
# If the interface has gone we cannot always get configuration
# name via getcfg. Therefore we use the stored one as fallback.
read HWD_CONFIG_0 x < $RUN_FILES_BASE/config-$1
fi
local IFPLUGD_PRIORITY=0
declare -i IFPLUGD_PRIORITY
get_variable IFPLUGD_PRIORITY $HWD_CONFIG_0
echo "$IFPLUGD_PRIORITY"
}
# 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
# 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
}
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
}
# 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 bonding interfaces.
# FIXME: Add other types of interfaces that depend on others.
get_depending_ifaces() {
local VLAN_PATH BOND_PATH DEP_IFACES DEP_VLANS DEP_BONDS BASE_IFACE
VLAN_PATH="/proc/net/vlan"
BOND_PATH="/proc/net/bonding"
BASE_IFACE="$1"
DEP_IFACES=""
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 [ -d "$BOND_PATH" ]; then
DEP_BONDS=`cd "$BOND_PATH"
grep -lws "Slave Interface: *$BASE_IFACE" *`
DEP_IFACES="$DEP_IFACES${DEP_BONDS:+ $DEP_BONDS}"
fi
if [ -z "$DEP_IFACES" ]; then
return 1
else
echo "$DEP_IFACES"
return 0
fi
}
nm_running () {
local MSG RET
test -x "$NETWORKMANAGER_BIN" || return
MSG=`checkproc $NETWORKMANAGER_BIN 2>&1`
RET=$?
info_mesg "$MSG"
return $RET
}
netcontrol_running() {
test -f $NETWORK_RUNFILE
}
# Since 'readlink' is located in /usr there is a quick and dirty replacement. It
# is used in my_pidof.
my_readlink() {
test -L $1 || return
local skip=yes
for a in `ls -l $1`; do
if [ "$skip" == yes ] ; then
test "$a" == "->" && skip=no
continue
fi
break
done
test -n "$a" || return
echo $a
}
# replacement for /sbin/pidof which works with lost NFS mounts
# See Bug 55370
my_pidof() {
local line psout pid pids rc
# ps itself doesn't take the full path
psout=$(ps --no-headers -C ${1##*/})
rc=$?
test "$rc" != 0 && return $rc
echo "$psout" | while read pid line; do
# 'exe' is a link to the full path of the executable
case "$(my_readlink /proc/$pid/exe)" in
*$1*) echo -n "$pid "
;;
esac
done
echo
# we return 0 if only matches with differing path were found
# but then the output is empty, so it is sufficient in most cases
# if the 'while read' wouldn't be in a subshell, we could set an exit code...
return $rc
}