#!/sbin/runscript # Copyright 1999-2007 Gentoo Foundation # Copyright 2007 Roy Marples # All rights reserved # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. MODULESDIR="${RC_LIBDIR}/net" MODULESLIST="${RC_SVCDIR}/nettree" _config_vars="config routes" [ -z "${IN_BACKGROUND}" ] && IN_BACKGROUND="NO" description="Configures network interfaces." # Handy var so we don't have to embed new lines everywhere for array splitting __IFS=" " _shell_var() { local rem=$1 c= r= while [ -n "${rem}" ]; do r=${rem#?} c=${rem%${r}} case "${c}" in [a-zA-Z0-9]) printf "%c" "${c}";; *) printf "_";; esac rem=${r} done } depend() { local IFACE=${SVCNAME#*.} local IFVAR=$(_shell_var "${IFACE}") need localmount after bootmisc provide net case "${IFACE}" in lo|lo0) ;; *) after net.lo net.lo0 local prov= eval prov=\$RC_NEED_${IFVAR} [ -n "${prov}" ] && need ${prov} eval prov=\$RC_USE_${IFVAR} [ -n "${prov}" ] && use ${prov} eval prov=\$RC_BEFORE_${IFVAR} [ -n "${prov}" ] && before ${prov} eval prov=\$RC_AFTER_${IFVAR} [ -n "${prov}" ] && after ${prov} eval prov=\$RC_PROVIDE_${IFVAR} [ -n "${prov}" ] && provide ${prov} ;; esac } # Support bash arrays - sigh _get_array() { local _a= if [ -n "${BASH}" ] ; then case "$(declare -p "$1" 2>/dev/null)" in "declare -a "*) eval "set -- \"\${$1[@]}\"" for _a in "$@"; do printf "%s\n" "${_a}" done return 0 ;; esac fi eval _a=\$$1 printf "%s" "${_a}" printf "\n" [ -n "${_a}" ] } # Flatten bash arrays to simple strings _flatten_array() { if [ -n "${BASH}" ] ; then case "$(declare -p "$1" 2>/dev/null)" in "declare -a "*) eval "set -- \"\${$1[@]}\"" for x in "$@"; do printf "'%s' " "$(printf "$x" | sed "s:':'\\\'':g")" done return 0 ;; esac fi eval _a=\$$1 printf "%s" "${_a}" printf "\n" [ -n "${_a}" ] } _wait_for_carrier() { local timeout= efunc=einfon _has_carrier && return 0 eval timeout=\$carrier_timeout_${IFVAR} timeout=${timeout:-${carrier_timeout:-5}} # Incase users don't want this nice feature ... [ ${timeout} -le 0 ] && return 0 yesno ${RC_PARALLEL} && efunc=einfo ${efunc} "Waiting for carrier (${timeout} seconds) " while [ ${timeout} -gt 0 ] ; do sleep 1 if _has_carrier ; then [ "${efunc}" = "einfon" ] && echo eend 0 return 0 fi timeout=$((${timeout} - 1)) [ "${efunc}" = "einfon" ] && printf "." done [ "${efunc}" = "einfon" ] && echo eend 1 return 1 } _netmask2cidr() { local i= len=0 local IFS=. for i in $1; do while [ ${i} != "0" ] ; do len=$((${len} + ${i} % 2)) i=$((${i} >> 1)) done done echo "${len}" } _configure_variables() { local var= v= t= for var in ${_config_vars} ; do local v= for t in "$@" ; do eval v=\$${var}_${t} if [ -n "${v}" ] ; then eval ${var}_${IFVAR}=\$${var}_${t} continue 2 fi done done } _show_address() { einfo "received address $(_get_inet_address "${IFACE}")" } # Basically sorts our modules into order and saves the list _gen_module_list() { local x= f= force=$1 if ! ${force} && [ -s "${MODULESLIST}" -a "${MODULESLIST}" -nt "${MODULESDIR}" ] ; then local update=false for x in "${MODULESDIR}"/* ; do [ -e "${x}" ] || continue if [ "${x}" -nt "${MODULESLIST}" ] ; then update=true break fi done ${update} || return 0 fi einfo "Caching network module dependencies" # Run in a subshell to protect the main script ( after() { eval ${MODULE}_after="\"\${${MODULE}_after}\${${MODULE}_after:+ }$*\"" } before() { local mod=${MODULE} local MODULE= for MODULE in "$@" ; do after "${mod}" done } program() { if [ "$1" = "start" -o "$1" = "stop" ] ; then local s="$1" shift eval ${MODULE}_program_${s}="\"\${${MODULE}_program_${s}}\${${MODULE}_program_${s}:+ }$*\"" else eval ${MODULE}_program="\"\${${MODULE}_program}\${${MODULE}_program:+ }$*\"" fi } provide() { eval ${MODULE}_provide="\"\${${MODULE}_provide}\${${MODULE}_provide:+ }$*\"" local x for x in $* ; do eval ${x}_providedby="\"\${${MODULE}_providedby}\${${MODULE}_providedby:+ }${MODULE}\"" done } for MODULE in "${MODULESDIR}"/* ; do sh -n "${MODULE}" || continue . "${MODULE}" || continue MODULE=${MODULE#${MODULESDIR}/} MODULE=${MODULE%.sh} eval ${MODULE}_depend MODULES="${MODULES} ${MODULE}" done VISITED= SORTED= visit() { case " ${VISITED} " in *" $1 "*) return ;; esac VISITED="${VISITED} $1" eval AFTER=\$${1}_after for MODULE in ${AFTER} ; do eval PROVIDEDBY=\$${MODULE}_providedby if [ -n "${PROVIDEDBY}" ] ; then for MODULE in ${PROVIDEDBY} ; do visit "${MODULE}" done else visit "${MODULE}" fi done eval PROVIDE=\$${1}_provide for MODULE in ${PROVIDE} ; do visit "${MODULE}" done eval PROVIDEDBY=\$${1}_providedby [ -z "${PROVIDEDBY}" ] && SORTED="${SORTED} $1" } for MODULE in ${MODULES} ; do visit "${MODULE}" done printf "" > "${MODULESLIST}" i=0 for MODULE in ${SORTED} ; do eval PROGRAM=\$${MODULE}_program eval PROGRAM_START=\$${MODULE}_program_start eval PROGRAM_STOP=\$${MODULE}_program_stop eval PROVIDE=\$${MODULE}_provide echo "module_${i}='${MODULE}'" >> "${MODULESLIST}" echo "module_${i}_program='${PROGRAM}'" >> "${MODULESLIST}" echo "module_${i}_program_start='${PROGRAM_START}'" >> "${MODULESLIST}" echo "module_${i}_program_stop='${PROGRAM_STOP}'" >> "${MODULESLIST}" echo "module_${i}_provide='${PROVIDE}'" >> "${MODULESLIST}" i=$((${i} + 1)) done echo "module_${i}=" >> "${MODULESLIST}" ) return 0 } _load_modules() { local starting=$1 mymods= # Ensure our list is up to date _gen_module_list false if ! . "${MODULESLIST}" ; then _gen_module_list true . "${MODULESLIST}" fi MODULES= if [ "${IFACE}" != "lo" -a "${IFACE}" != "lo0" ] ; then eval mymods=\$modules_${IFVAR} [ -z "${mymods}" ] && mymods=${modules} fi local i=-1 x= mod= f= provides= while true ; do i=$((${i} + 1)) eval mod=\$module_${i} [ -z "${mod}" ] && break [ -e "${MODULESDIR}/${mod}.sh" ] || continue eval set -- \$module_${i}_program if [ -n "$1" ] ; then x= for x in "$@" ; do [ -x "${x}" ] && break done [ -x "${x}" ] || continue fi if ${starting} ; then eval set -- \$module_${i}_program_start else eval set -- \$module_${i}_program_stop fi if [ -n "$1" ] ; then x= for x in "$@" ; do [ -x "${x}" ] && break done [ -x "${x}" ] || continue fi eval provides=\$module_${i}_provide if ${starting} ; then case " ${mymods} " in *" !${mod} "*) continue ;; *" !${provides} "*) [ -n "${provides}" ] && continue ;; esac fi MODULES="${MODULES}${MODULES:+ }${mod}" # Now load and wrap our functions if ! . "${MODULESDIR}/${mod}.sh" ; then eend 1 "${SVCNAME}: error loading module \`${mod}'" exit 1 fi [ -z "${provides}" ] && continue # Wrap our provides local f= for f in pre_start start post_start ; do eval "${provides}_${f}() { type ${mod}_${f} >/dev/null 2>/dev/null || return 0; ${mod}_${f} \"\$@\"; }" done eval module_${mod}_provides="${provides}" eval module_${provides}_providedby="${mod}" done # Wrap our preferred modules for mod in ${mymods} ; do case " ${MODULES} " in *" ${mod} "*) eval x=\$module_${mod}_provides [ -z "${x}" ] && continue for f in pre_start start post_start ; do eval "${x}_${f}() { type ${mod}_${f} >/dev/null 2>/dev/null || return 0; ${mod}_${f} \"\$@\"; }" done eval module_${x}_providedby="${mod}" ;; esac done # Finally remove any duplicated provides from our list if we're starting # Otherwise reverse the list local LIST="${MODULES}" p= MODULES= if ${starting} ; then for mod in ${LIST} ; do eval x=\$module_${mod}_provides if [ -n "${x}" ] ; then eval p=\$module_${x}_providedby [ "${mod}" != "${p}" ] && continue fi MODULES="${MODULES}${MODULES:+ }${mod}" done else for mod in ${LIST} ; do MODULES="${mod}${MODULES:+ }${MODULES}" done fi veinfo "Loaded modules: ${MODULES}" } _load_config() { local config="$(_get_array "config_${IFVAR}")" local fallback="$(_get_array fallback_${IFVAR})" if [ "${IFACE}" = "lo" -o "${IFACE}" = "lo0" ] ; then [ "${config}" != "null" ] && config="127.0.0.1/8 ${config}" else if [ -z "${config}" ] ; then ewarn "No configuration specified; defaulting to DHCP" config="dhcp" fi fi # We store our config in an array like vars # so modules can influence it config_index=0 local IFS="$__IFS" for cmd in ${config}; do eval config_${config_index}="'${cmd}'" config_index=$((${config_index} + 1)) done # Terminate the list eval config_${config_index}= config_index=0 for cmd in ${fallback}; do eval fallback_${config_index}="'${cmd}'" config_index=$((${config_index} + 1)) done # Terminate the list eval fallback_${config_index}= # Don't set to zero, so any net modules don't have to do anything extra config_index=-1 } start() { local IFACE=${SVCNAME#*.} oneworked=false module= local IFVAR=$(_shell_var "${IFACE}") cmd= metric=0 our_metric=$metric einfo "Bringing up interface ${IFACE}" eindent if [ -z "${MODULES}" ] ; then local MODULES= _load_modules true fi # We up the iface twice if we have a preup to ensure it's up if # available in preup and afterwards incase the user inadvertently # brings it down if type preup >/dev/null 2>/dev/null ; then _up 2>/dev/null ebegin "Running preup" eindent preup || return 1 eoutdent fi _up 2>/dev/null for module in ${MODULES} ; do if type "${module}_pre_start" >/dev/null 2>/dev/null ; then if ! ${module}_pre_start ; then eend 1 exit 1 fi fi done if ! _exists ; then eerror "ERROR: interface ${IFACE} does not exist" eerror "Ensure that you have loaded the correct kernel module for your hardware" return 1 fi if ! _wait_for_carrier ; then if service_started devd ; then ewarn "no carrier, but devd will start us when we have one" mark_service_inactive "${SVCNAME}" else eerror "no carrier" fi return 1 fi local config= config_index= _load_config config_index=0 if [ -n "${our_metric}" ] ; then metric=${our_metric} elif [ "${IFACE}" != "lo" -a "${IFACE}" != "lo0" ] ; then metric=$((${metric} + $(_ifindex))) fi while true ; do eval config=\$config_${config_index} [ -z "${config}" ] && break set -- "${config}" ebegin $1 eindent case "$1" in noop) if [ -n "$(_get_inet_address)" ] ; then oneworked=true break fi ;; null) : ;; [0-9]*|*:*) _add_address ${config} ;; *) if type "${config}_start" >/dev/null 2>/dev/null ; then "${config}"_start else eerror "nothing provides \`${config}'" fi ;; esac if eend $? ; then oneworked=true else eval config=\$fallback_${config_index} if [ -n "${config}" ] ; then eoutdent ewarn "Trying fallback configuration ${config}" eindent eval config_${config_index}=\$config unset fallback_${config_index} config_index=$((${config_index} - 1)) fi fi eoutdent config_index=$((${config_index} + 1)) done if ! ${oneworked} ; then if type failup >/dev/null 2>/dev/null ; then ebegin "Running failup" eindent failup eoutdent fi return 1 fi local hidefirstroute=false first=true local routes="$(_get_array "routes_${IFVAR}")" if [ "${IFACE}" = "lo" -o "${IFACE}" = "lo0" ] ; then if [ "${config_0}" != "null" ]; then routes="127.0.0.0/8 via 127.0.0.1 ${routes}" hidefirstroute=true fi fi local IFS="$__IFS" for cmd in ${routes}; do unset IFS if ${first}; then first=false einfo "Adding routes" fi eindent ebegin ${cmd} # Work out if we're a host or a net if not told case "${cmd}" in *" -net "*|*" -host "*) ;; *" netmask "*) cmd="-net ${cmd}" ;; *) case "${cmd%% *}" in *.*.*.*/32) cmd="-host ${cmd}" ;; *.*.*.*/*|0.0.0.0|default) cmd="-net ${cmd}" ;; *) cmd="-host ${cmd}" ;; esac ;; esac if ${hidefirstroute} ; then _add_route ${cmd} >/dev/null 2>/dev/null hidefirstroute=false else _add_route ${cmd} >/dev/null fi eend $? eoutdent done unset IFS for module in ${MODULES} ; do if type "${module}_post_start" >/dev/null 2>/dev/null ; then if ! ${module}_post_start ; then eend 1 exit 1 fi fi done if type postup >/dev/null 2>/dev/null ; then ebegin "Running postup" eindent postup eoutdent fi return 0 } stop() { local IFACE=${SVCNAME#*.} module= local IFVAR=$(_shell_var "${IFACE}") opts= einfo "Bringing down interface ${IFACE}" eindent if [ -z "${MODULES}" ] ; then local MODULES= _load_modules false fi if type predown >/dev/null 2>/dev/null ; then ebegin "Running predown" eindent predown || return 1 eoutdent else if is_net_fs /; then eerror "root filesystem is network mounted -- can't stop ${IFACE}" return 1 fi fi for module in ${MODULES} ; do if type "${module}_pre_stop" >/dev/null 2>/dev/null ; then if ! ${module}_pre_stop ; then eend 1 exit 1 fi fi done for module in ${MODULES} ; do if type "${module}_stop" >/dev/null 2>/dev/null ; then ${module}_stop fi done # Only delete addresses for non PPP interfaces if ! type is_ppp >/dev/null 2>/dev/null || ! is_ppp ; then _delete_addresses "${IFACE}" fi for module in ${MODULES} ; do if type "${module}_post_stop" >/dev/null 2>/dev/null ; then ${module}_post_stop fi done ! yesno ${IN_BACKGROUND} && \ [ "${IFACE}" != "lo" -a "${IFACE}" != "lo0" ] && \ _down 2>/dev/null [ -x /sbin/resolvconf ] && resolvconf -d "${IFACE}" if type postdown >/dev/null 2>/dev/null ; then ebegin "Running postdown" eindent postdown eoutdent fi return 0 } # vim: set ts=4 sw=4 :