From dbb5af2023910c43b4780852fada099cb94cae96 Mon Sep 17 00:00:00 2001 From: "Robin H. Johnson" Date: Sat, 11 Dec 2010 12:26:31 -0800 Subject: [PATCH] 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. --- TODO | 2 + doc/net.example.Linux.in | 12 +++++ net/bridge.sh | 109 ++++++++++++++++++++++++++++++--------- 3 files changed, 98 insertions(+), 25 deletions(-) diff --git a/TODO b/TODO index d04785b9..e008fca6 100644 --- a/TODO +++ b/TODO @@ -1,3 +1,5 @@ - ensure all forks block, restore and unblock signals. needs review - add support somehow for optional translations + +- oldnet[bridging]: Review setting of bridge configuration on dynamic interface add diff --git a/doc/net.example.Linux.in b/doc/net.example.Linux.in index 4ebe1a75..582c1618 100644 --- a/doc/net.example.Linux.in +++ b/doc/net.example.Linux.in @@ -805,6 +805,18 @@ #sethello 0 #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 # For RFC 2684 bridge support emerge net-misc/br2684ctl diff --git a/net/bridge.sh b/net/bridge.sh index 7456a5be..2f6711e6 100644 --- a/net/bridge.sh +++ b/net/bridge.sh @@ -11,34 +11,61 @@ _config_vars="$_config_vars bridge bridge_add brctl" _is_bridge() { - # Ignore header line so as to allow for bridges named 'bridge' - brctl show 2>/dev/null | sed '1,1d' | grep -q "^${IFACE}[[:space:]]" + [ -d /sys/class/net/"${1:-${IFACE}}"/bridge ] + 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() { - local brif= iface="${IFACE}" e= x= + local brif= oiface="${IFACE}" e= x= local ports="$(_get_array "bridge_${IFVAR}")" local opts="$(_get_array "brctl_${IFVAR}")" + # brif is used for dynamic add eval brif=\$bridge_add_${IFVAR} - eval x=\${bridge_${IFVAR}-y\} - if [ -z "${brif}" -a -z "${opts}" ]; then - [ -n "${ports}" -o "${x}" != "y" ] || return 0 + # ports is for static add + eval bridge_unset=\${bridge_${IFVAR}-y\} + 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 - [ -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 + # Dynamic mode detected ports="${IFACE}" IFACE="${brif}" + IFVAR=$(shell_var "${IFACE}") else + # Static mode detected ports="${ports}" metric=1000 fi - if ! _is_bridge; then + if ! _is_bridge ; then ebegin "Creating bridge ${IFACE}" if ! brctl addbr "${IFACE}"; then eend 1 @@ -46,6 +73,12 @@ bridge_pre_start() 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" for x in ${opts}; do unset IFS @@ -57,21 +90,50 @@ bridge_pre_start() done 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 einfo "Adding ports to ${IFACE}" eindent - local OIFACE="${IFACE}" + local BR_IFACE="${IFACE}" for x in ${ports}; do ebegin "${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 _up - if ! brctl addif "${OIFACE}" "${x}"; then + if ! brctl addif "${BR_IFACE}" "${x}"; then _set_flag -promisc eend 1 return 1 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 done eoutdent @@ -86,27 +148,24 @@ bridge_post_stop() { local port= ports= delete=false extra= - if _is_bridge; then + if _is_bridge "${IFACE}"; then ebegin "Destroying bridge ${IFACE}" _down - # Ignore header line so as to allow for bridges named 'bridge' - ports="$(brctl show 2>/dev/null | \ - sed -n -e '1,1d' -e '/^'"${IFACE}"'[[:space:]]/,/^\S/ { /^\('"${IFACE}"'[[:space:]]\|\t\)/s/^.*\t//p }')" + for x in /sys/class/net/"${IFACE}"/brif/*; do + [ -s $x ] || continue + n=${x##*/} + ports="${ports} ${n}" + done delete=true iface=${IFACE} eindent else - # Work out if we're added to a bridge for removal or not - # Ignore header line so as to allow for bridges named 'bridge' - eval set -- $(brctl show 2>/dev/null | sed -e '1,1d' -e "s/'/'\\\\''/g" -e "s/$/'/g" -e "s/^/'/g") - local line= - for line; do - set -- ${line} - if [ "$3" = "${IFACE}" ]; then - iface=$1 - break - fi - done + # We are taking down an interface that is part of a bridge maybe + ports="${IFACE}" + local brport_dir="/sys/class/net/${IFACE}/brport" + [ -d ${brport_dir} ] || return 0 + iface=$(readlink ${brport_dir}/bridge) + iface=${iface##*/} [ -z "${iface}" ] && return 0 extra=" from ${iface}" fi