]> git.ipfire.org Git - people/stevee/network.git/blobdiff - functions.stp
iptables: Replace state module by conntrack module.
[people/stevee/network.git] / functions.stp
index d6c5c99676cd3b482c6dd5879d9c6067eb5f1141..433fb34c8b888670531749d4092080d2029978d4 100644 (file)
 #                                                                             #
 ###############################################################################
 
-function stp_init() {
-       module_load stp
+# The default mode.
+#   We default to RSTP, because it has the better user experience and
+#   faster convergence times. Despite of that, it completely downgradeable
+#   to plain STP.
+STP_DEFAULT_MODE="rstp"
 
-       assert binary_exists brctl
-       assert binary_exists rstpctl
-}
-
-init_register stp_init
+# Allowed modes of the spanning tree protocol.
+STP_ALLOWED_MODES="rstp stp"
 
-function __rstpctl_bridge_get() {
+function stp_enable() {
        local bridge=${1}
-       local param=${2}
-
        assert isset bridge
-       assert isset param
-
-       local key
-       local val
-       rstpctl dumpbridge ${bridge} | \
-               while read bridge key val; do
-               if [ "${key}" = "${param}" ]; then
-                       echo "${val}"
-                       return ${EXIT_OK}
-               fi
-       done
 
-       return ${EXIT_ERROR}
+       # Tell the kernel to enable STP.
+       print 1 > ${SYS_CLASS_NET}/${bridge}/bridge/stp_state
 }
 
-function __rstpctl_port_get() {
+function stp_disable() {
        local bridge=${1}
-       local port=${2}
-       local param=${3}
-
        assert isset bridge
-       assert isset port
-       assert isset param
-
-       local key
-       local val
-       rstpctl dumpports ${bridge} | \
-               while read por key val; do
-               if [ "${port}" = "${por}" -a "${key}" = "${param}" ]; then
-                       echo "${val}"
-                       return ${EXIT_OK}
-               fi
-       done
 
-       return ${EXIT_ERROR}
+       # Tell the kernel to disable STP.
+       print 0 > ${SYS_CLASS_NET}/${bridge}/bridge/stp_state
 }
 
-function stp_enable() {
+function stp_is_enabled() {
        local bridge=${1}
+       assert isset bridge
 
+       local state=$(__device_get_file ${bridge} bridge/stp_state)
+
+       case "${state}" in
+               0)
+                       return ${EXIT_FALSE}
+                       ;;
+               *)
+                       return ${EXIT_TRUE}
+                       ;;
+       esac
+}
+
+function stp_is_userspace() {
+       local bridge=${1}
        assert isset bridge
-       assert zone_exists ${bridge}
 
-       brctl stp ${bridge} on
+       local state=$(__device_get_file ${bridge} bridge/stp_state)
+       case "${state}" in
+               2)
+                       return ${EXIT_TRUE}
+                       ;;
+               *)
+                       return ${EXIT_FALSE}
+                       ;;
+       esac
+}
 
-       local mode=$(zone_config_get ${bridge} STP_MODE)
+function stp_get_name() {
+       local proto=${1}
 
-       case "${mode}" in
+       case "${proto}" in
                stp)
-                       rstpctl setforcevers ${bridge} slow
+                       echo "Spanning Tree Protocol"
                        ;;
                rstp)
-                       rstpctl setforcevers ${bridge} normal
+                       echo "Rapid Spanning Tree Protocol"
                        ;;
-               *)
-                       log WARNING "Unknown protocol version: ${mode}."
-                       log WARNING "Using default mode."
+               mstp)
+                       echo "Multiple Spanning Tree Protocol"
                        ;;
        esac
+
+       return ${EXIT_OK}
 }
 
-function stp_disable() {
+function stp_bridge_set_protocol() {
        local bridge=${1}
-
        assert isset bridge
-       assert zone_exists ${bridge}
 
-       brctl stp ${bridge} off
+       local mode=${2}
+       assert isset mode
+
+       if ! listmatch ${mode} ${STP_ALLOWED_MODES}; then
+               log WARNING "Unknown protocol version: ${mode}."
+               log WARNING "Using default mode."
+
+               mode="${STP_DEFAULT_MODE}"
+       fi
+
+       cmd mstpctl setforcevers ${bridge} ${mode}
+       assert [ $? -eq 0 ]
 }
 
 function stp_bridge_get_protocol() {
@@ -108,184 +116,232 @@ function stp_bridge_get_protocol() {
 
        assert isset bridge
 
-       local enabled=$(__device_get_file ${bridge} "bridge/stp_state")
-       if ! enabled ${enabled}; then
-               return ${EXIT_OK}
-       fi
-
-       local mode=$(__rstpctl_bridge_get ${bridge} protocol_version)
+       # Let's check what the kernel is telling us about it's STP state.
+       local state=$(__device_get_file ${bridge} "bridge/stp_state")
 
-       case "${mode}" in
+       case "${state}" in
                0)
+                       # STP is disabled.
+                       return ${EXIT_OK}
+                       ;;
+               1)
+                       # Kernel mode STP is running.
                        echo "stp"
+                       return ${EXIT_OK}
                        ;;
                2)
-                       echo "rstp"
+                       # User-space STP is running.
                        ;;
-               # When rstpctl has an error, we assume that rstpd is not running and
-               # return the slow mode.
-               "")
-                       echo "stp"
+               *)
+                       log ERROR "Kernel is running in an unknown STP state."
+                       return ${EXIT_ERROR}
                        ;;
        esac
-}
 
-function stp_bridge_set_protocol() {
-       : XXX WANTED
+       # We get here, when STP is running in user-space mode.
+
+       # Get the current protocol version.
+       mstpctl showbridge ${bridge} force-protocol-version 2>/dev/null
+
+       return ${EXIT_OK}
 }
 
 function stp_bridge_get_id() {
        local bridge=${1}
-
        assert isset bridge
 
-       case "$(stp_bridge_get_protocol ${bridge})" in
-               rstp)
-                       __rstpctl_bridge_get ${bridge} "id"
-                       return ${EXIT_OK}
-                       ;;
-               stp)
-                       __device_get_file ${bridge} "bridge/bridge_id"
-                       return ${EXIT_OK}
-                       ;;
-       esac
+       __device_get_file ${bridge} "bridge/bridge_id"
 
-       return ${EXIT_ERROR}
+       return $?
 }
 
 function stp_bridge_get_forward_delay() {
        local bridge=${1}
+       assert isset bridge
+
+       if stp_is_userspace ${bridge}; then
+               cmd mstpctl showbridge ${bridge} forward-delay
+       else
+               local output=$(__device_get_file ${bridge} bridge/forward_delay)
+               __stp_div_100 ${output}
+       fi
+
+       return ${EXIT_OK}
+}
 
+function stp_bridge_set_forward_delay() {
+       local bridge=${1}
        assert isset bridge
 
-       case "$(stp_bridge_get_protocol ${bridge})" in
-               rstp)
-                       __rstpctl_bridge_get ${bridge} "bridge_forward_delay"
-                       return ${EXIT_OK}
-                       ;;
-               stp)
-                       __device_get_file ${bridge} "bridge/forward_delay"
-                       return ${EXIT_OK}
-                       ;;
-       esac
+       local fdelay=${2}
+       assert isinteger fdelay
 
-       return ${EXIT_ERROR}
+       # Check if the setting we want is already set.
+       local current_fdelay=$(stp_bridge_get_forward_delay ${bridge})
+       [ ${fdelay} -eq ${current_fdelay} ] && return ${EXIT_OK}
+
+       # The smallest value that may be set is 2.
+       if [ ${fdelay} -lt 2 ]; then
+               fdelay=2
+       fi
+
+       # Set the new value.
+       log INFO "Changing forward delay for '${bridge}': ${current_fdelay} --> ${fdelay}"
+       print "$(( ${fdelay} * 100 ))" > ${SYS_CLASS_NET}/${bridge}/bridge/forward_delay
+
+       return ${EXIT_OK}
 }
 
 function stp_bridge_get_hello_time() {
        local bridge=${1}
+       assert isset bridge
+
+       local ht=$(__device_get_file ${bridge} bridge/hello_time)
+       __stp_div_100 ${ht}
 
+       return ${EXIT_OK}
+}
+
+function stp_bridge_set_hello_time() {
+       local bridge=${1}
        assert isset bridge
 
-       case "$(stp_bridge_get_protocol ${bridge})" in
-               rstp)
-                       __rstpctl_bridge_get ${bridge} "bridge_hello_time"
-                       return ${EXIT_OK}
-                       ;;
-               stp)
-                       __device_get_file ${bridge} "bridge/hello_time"
-                       return ${EXIT_OK}
-                       ;;
-       esac
+       local hello=${2}
+       assert isinteger hello
 
-       return ${EXIT_ERROR}
+       # Check if the setting we want is already set.
+       local current_hello=$(stp_bridge_get_hello_time ${bridge})
+       [ ${hello} -eq ${current_hello} ] && return ${EXIT_OK}
+
+       # Set the new value.
+       log INFO "Changing hello time for '${bridge}': ${current_hello} --> ${hello}"
+       print "$(( ${hello} * 100 ))" > ${SYS_CLASS_NET}/${bridge}/bridge/hellow_time
+
+       return ${EXIT_OK}
 }
 
 function stp_bridge_get_max_age() {
        local bridge=${1}
+       assert isset bridge
+
+       local maxage=$(__device_get_file ${bridge} "bridge/max_age")
+       __stp_div_100 ${maxage}
+
+       return ${EXIT_OK}
+}
 
+function stp_bridge_set_max_age() {
+       local bridge=${1}
        assert isset bridge
 
-       case "$(stp_bridge_get_protocol ${bridge})" in
-               rstp)
-                       __rstpctl_bridge_get ${bridge} "bridge_max_age"
-                       return ${EXIT_OK}
-                       ;;
-               stp)
-                       __device_get_file ${bridge} "bridge/max_age"
-                       return ${EXIT_OK}
-                       ;;
-       esac
+       local maxage=${2}
+       assert isinteger maxage
 
-       return ${EXIT_ERROR}
+       # Check if the setting we want is already set.
+       local current_maxage=$(stp_bridge_get_max_age ${bridge})
+       [ ${maxage} -eq ${current_maxage} ] && return ${EXIT_OK}
+
+       # Set the new value.
+       log INFO "Changing max age for '${bridge}': ${current_maxage} --> ${maxage}"
+       print "$(( ${maxage} * 100 ))" > ${SYS_CLASS_NET}/${bridge}/bridge/max_age
+
+       return ${EXIT_OK}
 }
 
-function stp_bridge_get_designated_root() {
+function stp_bridge_get_priority() {
        local bridge=${1}
-       local output
+       assert isset bridge
 
+       __device_get_file ${bridge} "bridge/priority"
+       return ${EXIT_OK}
+}
+
+function stp_bridge_set_priority() {
+       local bridge=${1}
        assert isset bridge
 
-       case "$(stp_bridge_get_protocol ${bridge})" in
-               rstp)
-                       output=$(__rstpctl_bridge_get ${bridge} "designated_root")
-                       ;;
-               stp)
-                       output=$(__device_get_file ${bridge} "bridge/root_id")
-                       ;;
-       esac
+       local priority=${2}
+       assert isinteger priority
 
-       if ! isset output; then
-               return ${EXIT_ERROR}
-       fi
+       # Check if the setting we want is already set.
+       local current_priority=$(stp_bridge_get_priority ${bridge})
+       [ ${priority} -eq ${current_priority} ] && return ${EXIT_OK}
 
-       mac_format "${output:5:12}"
+       # Set the new value.
+       log INFO "Changing priority for '${bridge}': ${current_priority} --> ${priority}"
+       print "${priority}" > ${SYS_CLASS_NET}/${bridge}/bridge/priority
 
        return ${EXIT_OK}
 }
 
-function stp_bridge_get_root_path_cost() {
+function stp_bridge_get_designated_root() {
        local bridge=${1}
+       assert isset bridge
+
+       local output
+
+       if stp_is_userspace ${bridge}; then
+               output=$(cmd mstpctl showbridge ${bridge} designated-root)
+       else
+               output=$(__device_get_file ${bridge} bridge/root_id)
+       fi
+       output=${output:6}
 
+       # Print output (lowercase).
+       print "${output,,}"
+
+       if isset output; then
+               return ${EXIT_OK}
+       else
+               return ${EXIT_ERROR}
+       fi
+}
+
+function stp_bridge_get_root_path_cost() {
+       local bridge=${1}
        assert isset bridge
 
-       case "$(stp_bridge_get_protocol ${bridge})" in
-               rstp)
-                       __rstpctl_bridge_get ${bridge} "root_path_cost"
-                       return ${EXIT_OK}
-                       ;;
-               stp)
-                       __device_get_file ${bridge} "bridge/root_path_cost"
-                       return ${EXIT_OK}
-                       ;;
-       esac
+       if stp_is_userspace ${bridge}; then
+               cmd mstpctl showbridge ${bridge} path-cost
+       else
+               __device_get_file ${bridge} bridge/root_path_cost
+       fi
 
-       return ${EXIT_ERROR}
+       return ${EXIT_OK}
 }
 
 function stp_bridge_get_root_port_id() {
        local bridge=${1}
-
        assert isset bridge
 
-       case "$(stp_bridge_get_protocol ${bridge})" in
-               rstp)
-                       __rstpctl_bridge_get ${bridge} "root_port"
-                       return ${EXIT_OK}
-                       ;;
-               stp)
-                       __device_get_file ${bridge} "bridge/root_port"
-                       return ${EXIT_OK}
-                       ;;
-       esac
+       if stp_is_userspace ${bridge}; then
+               local root_port=$(cmd mstpctl showbridge ${bridge} root-port)
 
-       return ${EXIT_ERROR}
+               # Return error, when there is no root port.
+               if [ "${root_port}" = "none" ]; then
+                       return ${EXIT_ERROR}
+               fi
+
+               print "${root_port}"
+       else
+               __device_get_file ${bridge} bridge/root_port_id
+       fi
+
+       return ${EXIT_OK}
 }
 
 function stp_bridge_get_root_port() {
        local bridge=${1}
-
        assert isset bridge
 
        local id=$(stp_bridge_get_root_port_id ${bridge})
 
-       local member
-       local member_id
+       local member member_id
        for member in $(bridge_get_members ${bridge}); do
                member_id=$(stp_port_get_id ${bridge} ${member})
 
                if [ "${id}" = "${member_id}" ]; then
-                       echo "${member}"
+                       print "${member}"
                        return ${EXIT_OK}
                fi
        done
@@ -295,188 +351,161 @@ function stp_bridge_get_root_port() {
 
 function stp_bridge_is_root() {
        local bridge=${1}
-
        assert isset bridge
 
-       [ -n "$(stp_bridge_get_root_port ${bridge})" ]
-}
+       local root_path_cost=$(stp_bridge_get_root_path_cost ${bridge})
 
-function stp_bridge_get_priority() {
-       local bridge=${1}
-
-       assert isset bridge
-
-       case "$(stp_bridge_get_protocol ${bridge})" in
-               rstp)
-                       local output=$(__rstpctl_bridge_get ${bridge} "root_path_cost")
-                       dec "${output:0:4}"
-                       return ${EXIT_OK}
-                       ;;
-               stp)
-                       __device_get_file ${bridge} "bridge/priority"
-                       return ${EXIT_OK}
-                       ;;
-       esac
+       if [ "${root_path_cost}" = "0" ]; then
+               return ${EXIT_TRUE}
+       fi
 
-       return ${EXIT_ERROR}
+       return ${EXIT_FALSE}
 }
 
 function stp_bridge_get_topology_change_count() {
        local bridge=${1}
-
        assert isset bridge
 
-       case "$(stp_bridge_get_protocol ${bridge})" in
-               rstp)
-                       __rstpctl_bridge_get ${bridge} "topology_change_count"
-                       return ${EXIT_OK}
-                       ;;
-               stp)
-                       __device_get_file ${bridge} "bridge/topology_change"
-                       return ${EXIT_OK}
-                       ;;
-       esac
+       if stp_is_userspace ${bridge}; then
+               cmd mstpctl showbridge ${bridge} topology-change-count
+       else
+               __device_get_file ${bridge} bridge/topology_change
+       fi
 
-       return ${EXIT_ERROR}
+       return ${EXIT_OK}
 }
 
 function stp_bridge_get_topology_change_timer() {
        local bridge=${1}
-
        assert isset bridge
 
-       case "$(stp_bridge_get_protocol ${bridge})" in
-               rstp)
-                       __rstpctl_bridge_get ${bridge} "time_since_topology_change"
-                       return ${EXIT_OK}
-                       ;;
-               stp)
-                       __device_get_file ${bridge} "bridge/topology_change_timer"
-                       return ${EXIT_OK}
-                       ;;
-       esac
+       if stp_is_userspace ${bridge}; then
+               cmd mstpctl showbridge ${bridge} time-since-topology-change
+       else
+               __device_get_file ${bridge} bridge/topology_change_timer
+       fi
 
-       return ${EXIT_ERROR}
+       return ${EXIT_OK}
 }
 
 function stp_bridge_get_topology_change_detected() {
        local bridge=${1}
-
        assert isset bridge
 
-       case "$(stp_bridge_get_protocol ${bridge})" in
-               rstp)
-                       __rstpctl_bridge_get ${bridge} "topology_change"
-                       return ${EXIT_OK}
-                       ;;
-               stp)
-                       __device_get_file ${bridge} "bridge/topology_change_detected"
-                       return ${EXIT_OK}
-                       ;;
-       esac
+       local change
 
-       return ${EXIT_ERROR}
-}
+       if stp_is_userspace ${bridge}; then
+               change=$(mstpctl showbridge ${bridge} topology-change)
+       else
+               change=$(__device_get_file ${bridge} bridge/topology_change_detected)
+       fi
 
-# STP states
-STP_STATE[0]="disabled"
-STP_STATE[1]="listening"
-STP_STATE[2]="learning"
-STP_STATE[3]="forwarding"
-STP_STATE[4]="blocking"
+       if enabled change; then
+               print "yes"
+               return ${EXIT_TRUE}
+       else
+               print "no"
+               return ${EXIT_FALSE}
+       fi
+}
 
 function stp_port_get_state() {
        local bridge=${1}
-       local port=${2}
-       local output
-
        assert isset bridge
-       assert isset port
 
-       case "$(stp_bridge_get_protocol ${bridge})" in
-               rstp)
-                       output=$(__rstpctl_port_get ${bridge} ${port} "state")
-                       ;;
-               stp)
-                       output=$(__device_get_file ${bridge} "brif/${port}/state")
-
-                       # Translate int to name
-                       output="${STP_STATE[${output}]}"
-                       ;;
-       esac
+       local port=${2}
+       assert isset port
 
-       if ! isset output; then
-               return ${EXIT_ERROR}
+       local space
+       if stp_is_userspace ${bridge}; then
+               state=$(mstpctl showportdetail ${bridge} ${port} state)
+               print "${state^^}"
+       else
+               state=$(__device_get_file ${bridge} brif/${port}/state)
+
+               case "${state}" in
+                       0)
+                               print "DISABLED"
+                               ;;
+                       1)
+                               print "LISTENING"
+                               ;;
+                       2)
+                               print "LEARNING"
+                               ;;
+                       3)
+                               print "FORWARDING"
+                               ;;
+                       4)
+                               print "BLOCKING"
+                               ;;
+                       *)
+                               return ${EXIT_ERROR}
+                               ;;
+               esac
        fi
 
-       echo "${output^^}"
-
        return ${EXIT_OK}
 }
 
 function stp_port_get_id() {
        local bridge=${1}
-       local port=${2}
-
        assert isset bridge
-       assert isset port
 
-       case "$(stp_bridge_get_protocol ${bridge})" in
-               rstp)
-                       __rstpctl_port_get ${bridge} ${port} "id"
-                       return ${EXIT_OK}
-                       ;;
-               stp)
-                       dec $(__device_get_file ${bridge} "brif/${port}/port_no")
-                       return ${EXIT_OK}
-                       ;;
-       esac
+       local port=${2}
+       assert isset port
 
-       return ${EXIT_ERROR}
+       dec $(__device_get_file ${bridge} "brif/${port}/port_no")
+       return ${EXIT_OK}
 }
 
 function stp_port_get_cost() {
        local bridge=${1}
-       local port=${2}
-
        assert isset bridge
+
+       local port=${2}
        assert isset port
 
-       case "$(stp_bridge_get_protocol ${bridge})" in
-               rstp)
-                       __rstpctl_port_get ${bridge} ${port} "path_cost"
-                       return ${EXIT_OK}
-                       ;;
-               stp)
-                       __device_get_file ${bridge} "brif/${port}/path_cost"
-                       return ${EXIT_OK}
-                       ;;
-       esac
+       if stp_is_userspace ${bridge}; then
+               cmd mstpctl showportdetail ${bridge} ${port} external-port-cost
+       else
+               __device_get_file ${bridge} brif/${port}/path_cost
+       fi
 
        return ${EXIT_ERROR}
 }
 
 function stp_port_get_designated_root() {
        local bridge=${1}
-       local port=${2}
-       local output
-
        assert isset bridge
+
+       local port=${2}
        assert isset port
 
-       case "$(stp_bridge_get_protocol ${bridge})" in
-               rstp)
-                       output=$(__rstpctl_port_get ${bridge} ${port} "designated_root")
-                       ;;
-               stp)
-                       output=$(__device_get_file ${bridge} "brif/${port}/designated_root")
-                       ;;
-       esac
+       local output
+
+       if stp_is_userspace ${bridge}; then
+               output=$(cmd mstpctl showportdetail ${bridge} ${port} designated-root)
+               output=${output:6}
+       else
+               output=$(__device_get_file ${bridge} brif/${port}/designated_root)
+               output=${output:5}
+       fi
 
-       if [ -n "${output}" ]; then
-               mac_format ${output:5:12}
+       if isset output; then
+               mac_format ${output}
                return ${EXIT_OK}
        fi
 
        return ${EXIT_ERROR}
 }
+
+function __stp_div_100() {
+       local val=${1}
+
+       local split=$((${#val} - 2))
+       val="${val:0:${split}}.${val:${split}:2}"
+
+       # Round the output.
+       print "%.0f" "${val}"
+}