--- /dev/null
+#!/sbin/runscript
+# Copyright (c) 2007-2009 Roy Marples <roy@marples.name>
+# Released under the 2-clause BSD license.
+
+MODULESDIR="${RC_LIBEXECDIR}/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="
+"
+depend()
+{
+ local IFACE=${RC_SVCNAME#*.}
+ local IFVAR=$(shell_var "${IFACE}")
+
+ need localmount
+ after bootmisc
+ provide net
+ keyword -jail -prefix -vserver
+
+ case "${IFACE}" in
+ lo|lo0);;
+ *) after net.lo net.lo0 dbus;;
+ esac
+
+ if [ "$(command -v "depend_${IFVAR}")" = "depend_${IFVAR}" ]; then
+ depend_${IFVAR}
+ fi
+
+ local dep= prov=
+ for dep in need use before after provide keyword; do
+ eval prov=\$rc_${dep}_${IFVAR}
+ if [ -n "${prov}" ]; then
+ ${dep} ${prov}
+ fi
+ done
+}
+
+# Support bash arrays - sigh
+_array_helper()
+{
+ local _a=
+
+ eval _a=\$$1
+ _a=$(echo "${_a}" | sed -e 's:^[[:space:]]*::' -e 's:[[:space:]]*$::' -e '/^$/d' -e 's:[[:space:]]\{1,\}: :g')
+
+ [ -n "${_a}" ] && printf "%s\n" "${_a}"
+}
+
+_get_array()
+{
+ local _a=
+ if [ -n "${BASH}" ]; then
+ case "$(declare -p "$1" 2>/dev/null)" in
+ "declare -a "*)
+ ewarn "You are using a bash array for $1."
+ ewarn "This feature will be removed in the future."
+ ewarn "Please see net.example for the correct format for $1."
+ eval "set -- \"\${$1[@]}\""
+ for _a; do
+ printf "%s\n" "${_a}"
+ done
+ return 0
+ ;;
+ esac
+ fi
+
+ _array_helper $1
+}
+
+# Flatten bash arrays to simple strings
+_flatten_array()
+{
+ if [ -n "${BASH}" ]; then
+ case "$(declare -p "$1" 2>/dev/null)" in
+ "declare -a "*)
+ ewarn "You are using a bash array for $1."
+ ewarn "This feature will be removed in the future."
+ ewarn "Please see net.example for the correct format for $1."
+ eval "set -- \"\${$1[@]}\""
+ for x; do
+ printf "'%s' " "$(printf "$x" | sed "s:':'\\\'':g")"
+ done
+ return 0
+ ;;
+ esac
+ fi
+
+ _array_helper $1
+}
+
+_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 -= 1 ))
+ [ "${efunc}" = "einfon" ] && printf "."
+ done
+
+ [ "${efunc}" = "einfon" ] && echo
+ eend 1
+ return 1
+}
+
+_netmask2cidr()
+{
+ # Some shells cannot handle hex arithmetic, so we massage it slightly
+ # Buggy shells include FreeBSD sh, dash and busybox.
+ # bash and NetBSD sh don't need this.
+ case $1 in
+ 0x*)
+ local hex=${1#0x*} quad=
+ while [ -n "${hex}" ]; do
+ local lastbut2=${hex#??*}
+ quad=${quad}${quad:+.}0x${hex%${lastbut2}*}
+ hex=${lastbut2}
+ done
+ set -- ${quad}
+ ;;
+ esac
+
+ local i= len=
+ local IFS=.
+ for i in $1; do
+ while [ ${i} -ne 0 ]; do
+ : $(( len += i % 2 ))
+ : $(( i >>= 1 ))
+ done
+ done
+
+ echo "${len}"
+}
+
+_configure_variables()
+{
+ local var= v= t=
+
+ for var in ${_config_vars}; do
+ local v=
+ for t; do
+ eval v=\$${var}_${t}
+ if [ -n "${v}" ]; then
+ eval ${var}_${IFVAR}=\$${var}_${t}
+ continue 2
+ fi
+ done
+ done
+}
+
+_which()
+{
+ local i OIFS
+ # Empty
+ [ -z "$1" ] && return
+ # check paths
+ OIFS="$IFS"
+ IFS=:
+ for i in $PATH ; do
+ [ -x $i/$1 ] && echo $i/$1 && break
+ done
+ IFS=$OIFS
+}
+
+# Like _which, but also consider shell builtins, and multiple alternatives
+_program_available()
+{
+ [ -z "$1" ] && return 0
+ local x=
+ for x; do
+ case "${x}" in
+ /*) [ -x "${x}" ] && break;;
+ *) type "${x}" >/dev/null 2>&1 && break;;
+ esac
+ unset x
+ done
+ [ -n "${x}" ] && echo $x && return 0
+ return 1
+}
+
+_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}"/*.sh; 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; 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}"/*.sh; 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 += 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 += 1 ))
+ eval mod=\$module_${i}
+ [ -z "${mod}" ] && break
+ [ -e "${MODULESDIR}/${mod}.sh" ] || continue
+
+ eval set -- \$module_${i}_program
+ if [ -n "$1" ]; then
+ if ! _program_available "$@" >/dev/null; then
+ vewarn "Skipping module $mod due to missing program: $@"
+ continue
+ fi
+ fi
+ if ${starting}; then
+ eval set -- \$module_${i}_program_start
+ else
+ eval set -- \$module_${i}_program_stop
+ fi
+ if [ -n "$1" ]; then
+ if ! _program_available "$@" >/dev/null; then
+ vewarn "Skipping module $mod due to missing program: $@"
+ continue
+ fi
+ 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 "${RC_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}() { [ "$(command -v "${mod}_${f}")" = "${mod}_${f}" ] || 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}() { [ "$(command -v "${mod}_${f}")" = "${mod}_${f}" ] || 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})"
+
+ config_index=0
+ local IFS="$__IFS"
+ set -- ${config}
+
+ # We should support a space separated array for cidr configs
+ # But only as long as they do not contain other parameters for the address
+ if [ $# = 1 ]; then
+ unset IFS
+ set -- ${config}
+ # Of course, we may have a single address added old style.
+ # If the NEXT argument is a v4 or v6 address, it's the next config.
+ # Otherwise, it's arguments to the first config...
+ if [ "${2#*.*}" = "${2}" -a "${2#*:*}" = "${2}" ]; then
+ # Not an IPv4/IPv6
+ local IFS="$__IFS"
+ set -- ${config}
+ fi
+ fi
+
+ # Ensure that loopback has the correct address
+ if [ "${IFACE}" = "lo" -o "${IFACE}" = "lo0" ]; then
+ if [ "$1" != "null" ]; then
+ config_0="127.0.0.1/8"
+ config_index=1
+ fi
+ else
+ if [ -z "$1" ]; then
+ ewarn "No configuration specified; defaulting to DHCP"
+ config_0="dhcp"
+ config_index=1
+ fi
+ fi
+
+
+ # We store our config in an array like vars
+ # so modules can influence it
+ for cmd; do
+ eval config_${config_index}="'${cmd}'"
+ : $(( 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 += 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
+}
+
+# Support functions
+_run_if()
+{
+ local cmd=$1 iface=$2 ifr=${IFACE} ifv=${IFVAR}
+ # Ensure that we don't stamp on real values
+ local IFACE= IFVAR=
+ shift
+ if [ -n "${iface}" ]; then
+ IFACE="${iface}"
+ [ "${iface}" != "${ifr}" ] && IFVAR=$(shell_var "${IFACE}")
+ else
+ IFACE=${ifr}
+ IFVAR=${ifv}
+ fi
+ ${cmd}
+}
+interface_exists()
+{
+ _run_if _exists "$@"
+}
+interface_up()
+{
+ _run_if _up "$@"
+}
+interface_down()
+{
+ _run_if _down "$@"
+}
+
+start()
+{
+ local IFACE=${RC_SVCNAME#*.} oneworked=false fallback=false module=
+ local IFVAR=$(shell_var "${IFACE}") cmd= our_metric=
+ local metric=0 _up_before_preup
+ eval _up_before_preup="\$up_before_preup_${IFVAR}"
+ [ -z "${_up_before_preup}" ] && _up_before_preup=$up_before_preup
+
+ 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 [ "$(command -v preup)" = "preup" ]; then
+ yesno "${_up_before_preup:-yes}" && _up 2>/dev/null
+ ebegin "Running preup"
+ eindent
+ preup || return 1
+ eoutdent
+ fi
+
+ _up 2>/dev/null
+
+ for module in ${MODULES}; do
+ if [ "$(command -v "${module}_pre_start")" = "${module}_pre_start" ]; then
+ ${module}_pre_start || exit $?
+ 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 "${RC_SVCNAME}"
+ else
+ eerror "no carrier"
+ fi
+ return 1
+ fi
+
+ local config= config_index=
+ _load_config
+ config_index=0
+
+ eval our_metric=\$metric_${IFVAR}
+ if [ -n "${our_metric}" ]; then
+ metric=${our_metric}
+ elif [ "${IFACE}" != "lo" -a "${IFACE}" != "lo0" ]; then
+ : $(( metric += $(_ifindex) ))
+ fi
+
+ while true; do
+ eval config=\$config_${config_index}
+ [ -z "${config}" ] && break
+
+ set -- ${config}
+ if [ "$1" != "null" -a "$1" != "noop" ]; then
+ ebegin "$1"
+ fi
+ eindent
+ case "$1" in
+ noop)
+ if [ -n "$(_get_inet_address)" ]; then
+ oneworked=true
+ break
+ fi
+ ;;
+ null) :;;
+ [0-9]*|*:*) _add_address ${config};;
+ *)
+ if [ "$(command -v "${config}_start")" = "${config}_start" ]; 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
+ fallback=true
+ eoutdent
+ ewarn "Trying fallback configuration ${config}"
+ eindent
+ eval config_${config_index}=\$config
+ unset fallback_${config_index}
+ : $(( config_index -= 1 ))
+ fi
+ fi
+ eoutdent
+ : $(( config_index += 1 ))
+ done
+
+ if ! ${oneworked}; then
+ if [ "$(command -v failup)" = "failup" ]; then
+ ebegin "Running failup"
+ eindent
+ failup
+ eoutdent
+ fi
+ return 1
+ fi
+
+ local hidefirstroute=false first=true routes=
+ if ${fallback}; then
+ routes="$(_get_array "fallback_routes_${IFVAR}")"
+ fi
+ if [ -z "${routes}" ]; then
+ routes="$(_get_array "routes_${IFVAR}")"
+ fi
+ 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 OIFS="${IFS}" SIFS="${IFS-y}"
+ 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}";;
+ *.*.*.*/32*) cmd="-host ${cmd}";;
+ *.*.*.*/*|0.0.0.0|0.0.0.0" "*) cmd="-net ${cmd}";;
+ default|default" "*) cmd="-net ${cmd}";;
+ *) cmd="-host ${cmd}";;
+ esac
+ if ${hidefirstroute}; then
+ _add_route ${cmd} >/dev/null 2>&1
+ hidefirstroute=false
+ else
+ _add_route ${cmd} >/dev/null
+ fi
+ eend $?
+ eoutdent
+ done
+ if [ "${SIFS}" = "y" ]; then
+ unset IFS
+ else
+ IFS="${OIFS}"
+ fi
+
+ for module in ${MODULES}; do
+ if [ "$(command -v "${module}_post_start")" = "${module}_post_start" ]; then
+ ${module}_post_start || exit $?
+ fi
+ done
+
+ if [ "$(command -v postup)" = "postup" ]; then
+ ebegin "Running postup"
+ eindent
+ postup
+ eoutdent
+ fi
+
+ return 0
+}
+
+stop()
+{
+ local IFACE=${RC_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 [ "$(command -v predown)" = "predown" ]; 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 [ "$(command -v "${module}_pre_stop")" = "${module}_pre_stop" ]; then
+ ${module}_pre_stop || exit $?
+ fi
+ done
+
+ for module in ${MODULES}; do
+ if [ "$(command -v "${module}_stop")" = "${module}_stop" ]; then
+ ${module}_stop
+ fi
+ done
+
+ # Only delete addresses for interfaces that exist
+ if _exists; then
+ # PPP can manage it's own addresses when IN_BACKGROUND
+ # Important in case "demand" set on the ppp link
+ if ! (yesno ${IN_BACKGROUND} && is_ppp) ; then
+ _delete_addresses "${IFACE}"
+ fi
+ fi
+
+ for module in ${MODULES}; do
+ if [ "$(command -v "${module}_post_stop")" = "${module}_post_stop" ]; then
+ ${module}_post_stop
+ fi
+ done
+
+ # If not in background, and not loopback then bring the interface down
+ # unless overridden.
+ if ! yesno ${IN_BACKGROUND} && \
+ [ "${IFACE}" != "lo" -a "${IFACE}" != "lo0" ]; then
+ eval module=\$ifdown_${IFVAR}
+ module=${module:-${ifdown:-YES}}
+ yesno ${module} && _down 2>/dev/null
+ fi
+
+ type resolvconf >/dev/null 2>&1 && resolvconf -d "${IFACE}" 2>/dev/null
+
+ if [ "$(command -v "postdown")" = "postdown" ]; then
+ ebegin "Running postdown"
+ eindent
+ postdown
+ eoutdent
+ fi
+
+ return 0
+}