Revamp of bridging code.

- Use sysfs to read bridge information from the system instead of
  parsing the brctl outputs.
- Allow setting of all bridge configuration parameters using new sysfs
  methods, modelled after bonding configuration. Also works for per-port
  bridge interface parameters.
- Document pre-starting an empty bridge for dynamic add.
- Check for interface existence before adding to bridge.
- Should fix bug #293046, #309185.
This commit is contained in:
Robin H. Johnson 2010-12-11 12:26:31 -08:00
parent 900d54b0fc
commit dbb5af2023
3 changed files with 98 additions and 25 deletions

2
TODO
View File

@ -1,3 +1,5 @@
- ensure all forks block, restore and unblock signals. needs review - ensure all forks block, restore and unblock signals. needs review
- add support somehow for optional translations - add support somehow for optional translations
- oldnet[bridging]: Review setting of bridge configuration on dynamic interface add

View File

@ -805,6 +805,18 @@
#sethello 0 #sethello 0
#stp off" #stp off"
# You can also configure the bridge or bridge members via sysfs on 2.6 kernels
# or newer. See the kernel bridge documentation for a description of these
# options.
#stp_state_br0="0"
#forward_delay_br0="10"
#hairpin_mode_eth0="1"
# If you want to start an empty bridge, and then dynmically add ports to it you
# MUST set the following variables (with the correct interface name).
# If you get the error "Misconfigured static bridge detected", this means you.
#bridge_br0=''
#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
# RFC 2684 Bridge Support # RFC 2684 Bridge Support
# For RFC 2684 bridge support emerge net-misc/br2684ctl # For RFC 2684 bridge support emerge net-misc/br2684ctl

View File

@ -11,29 +11,56 @@ _config_vars="$_config_vars bridge bridge_add brctl"
_is_bridge() _is_bridge()
{ {
# Ignore header line so as to allow for bridges named 'bridge' [ -d /sys/class/net/"${1:-${IFACE}}"/bridge ]
brctl show 2>/dev/null | sed '1,1d' | grep -q "^${IFACE}[[:space:]]" return $?
}
_is_bridge_port()
{
[ -d /sys/class/net/"${1:-${IFACE}}"/brport ]
return $?
}
_bridge_ports()
{
for x in /sys/class/net/"${1:-${IFACE}}"/brif/*; do
n=${x##*/}
echo $n
done
} }
bridge_pre_start() bridge_pre_start()
{ {
local brif= iface="${IFACE}" e= x= local brif= oiface="${IFACE}" e= x=
local ports="$(_get_array "bridge_${IFVAR}")" local ports="$(_get_array "bridge_${IFVAR}")"
local opts="$(_get_array "brctl_${IFVAR}")" local opts="$(_get_array "brctl_${IFVAR}")"
# brif is used for dynamic add
eval brif=\$bridge_add_${IFVAR} eval brif=\$bridge_add_${IFVAR}
eval x=\${bridge_${IFVAR}-y\} # ports is for static add
if [ -z "${brif}" -a -z "${opts}" ]; then eval bridge_unset=\${bridge_${IFVAR}-y\}
[ -n "${ports}" -o "${x}" != "y" ] || return 0 eval brctl_unset=\${brctl_${IFVAR}-y\}
# If we are not doing dynamic add on $IFACE, check for static ports.
if [ -z "${brif}" -a "${brctl_unset}" == 'y' ]; then
if [ -z "${ports}" -a "${bridge_unset}" == "y" ]; then
#eerror "Misconfigured static bridge detected (see net.example)"
return 0
fi
fi fi
[ -n "${ports}" ] && bridge_post_stop # If the bridge was already up, we should clear it
[ "${bridge_unset}" != "y" ] && bridge_post_stop
( (
# Normalize order of variables
if [ -z "${ports}" -a -n "${brif}" ]; then if [ -z "${ports}" -a -n "${brif}" ]; then
# Dynamic mode detected
ports="${IFACE}" ports="${IFACE}"
IFACE="${brif}" IFACE="${brif}"
IFVAR=$(shell_var "${IFACE}")
else else
# Static mode detected
ports="${ports}" ports="${ports}"
metric=1000 metric=1000
fi fi
@ -46,6 +73,12 @@ bridge_pre_start()
fi fi
fi fi
# TODO: does this reset the bridge every time we add a interface to the
# bridge? We should probably NOT do that.
# Old configuration set mechanism
# Only a very limited subset of the options are available in the old
# configuration method. The sysfs interface is in the next block instead.
local IFS="$__IFS" local IFS="$__IFS"
for x in ${opts}; do for x in ${opts}; do
unset IFS unset IFS
@ -57,21 +90,50 @@ bridge_pre_start()
done done
unset IFS unset IFS
# New configuration set mechanism, matches bonding
for x in /sys/class/net/"${IFACE}"/bridge/*; do
[ -f "${x}" ] || continue
n=${x##*/}
eval s=\$${n}_${IFVAR}
if [ -n "${s}" ]; then
einfo "Setting ${n}: ${s}"
echo "${s}" >"${x}" || \
eerror "Failed to configure $n (${n}_${IFVAR})"
fi
done
if [ -n "${ports}" ]; then if [ -n "${ports}" ]; then
einfo "Adding ports to ${IFACE}" einfo "Adding ports to ${IFACE}"
eindent eindent
local OIFACE="${IFACE}" local BR_IFACE="${IFACE}"
for x in ${ports}; do for x in ${ports}; do
ebegin "${x}" ebegin "${x}"
local IFACE="${x}" local IFACE="${x}"
local IFVAR=$(shell_var "${IFACE}")
if ! _exists "${IFACE}" ; then
eerror "Cannot add non-existent interface ${IFACE} to ${BR_IFACE}"
return 1
fi
# The interface is known to exist now
_set_flag promisc _set_flag promisc
_up _up
if ! brctl addif "${OIFACE}" "${x}"; then if ! brctl addif "${BR_IFACE}" "${x}"; then
_set_flag -promisc _set_flag -promisc
eend 1 eend 1
return 1 return 1
fi fi
# Per-interface bridge settings
for x in /sys/class/net/"${IFACE}"/brport/*; do
[ -f "${x}" ] || continue
n=${x##*/}
eval s=\$${n}_${IFVAR}
if [ -n "${s}" ]; then
einfo "Setting ${n}@${IFACE}: ${s}"
echo "${s}" >"${x}" || \
eerror "Failed to configure $n (${n}_${IFVAR})"
fi
done
eend 0 eend 0
done done
eoutdent eoutdent
@ -86,27 +148,24 @@ bridge_post_stop()
{ {
local port= ports= delete=false extra= local port= ports= delete=false extra=
if _is_bridge; then if _is_bridge "${IFACE}"; then
ebegin "Destroying bridge ${IFACE}" ebegin "Destroying bridge ${IFACE}"
_down _down
# Ignore header line so as to allow for bridges named 'bridge' for x in /sys/class/net/"${IFACE}"/brif/*; do
ports="$(brctl show 2>/dev/null | \ [ -s $x ] || continue
sed -n -e '1,1d' -e '/^'"${IFACE}"'[[:space:]]/,/^\S/ { /^\('"${IFACE}"'[[:space:]]\|\t\)/s/^.*\t//p }')" n=${x##*/}
ports="${ports} ${n}"
done
delete=true delete=true
iface=${IFACE} iface=${IFACE}
eindent eindent
else else
# Work out if we're added to a bridge for removal or not # We are taking down an interface that is part of a bridge maybe
# Ignore header line so as to allow for bridges named 'bridge' ports="${IFACE}"
eval set -- $(brctl show 2>/dev/null | sed -e '1,1d' -e "s/'/'\\\\''/g" -e "s/$/'/g" -e "s/^/'/g") local brport_dir="/sys/class/net/${IFACE}/brport"
local line= [ -d ${brport_dir} ] || return 0
for line; do iface=$(readlink ${brport_dir}/bridge)
set -- ${line} iface=${iface##*/}
if [ "$3" = "${IFACE}" ]; then
iface=$1
break
fi
done
[ -z "${iface}" ] && return 0 [ -z "${iface}" ] && return 0
extra=" from ${iface}" extra=" from ${iface}"
fi fi