Project: IPFire
Code Location: git://git.ipfire.org/network.gitmaster
Browse
/
Download File
functions.stp
#!/bin/bash
###############################################################################
#                                                                             #
# IPFire.org - A linux based firewall                                         #
# Copyright (C) 2010  Michael Tremer & Christian Schmidt                      #
#                                                                             #
# This program is free software: you can redistribute it and/or modify        #
# it under the terms of the GNU General Public License as published by        #
# the Free Software Foundation, either version 3 of the License, or           #
# (at your option) any later version.                                         #
#                                                                             #
# This program is distributed in the hope that it will be useful,             #
# but WITHOUT ANY WARRANTY; without even the implied warranty of              #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               #
# GNU General Public License for more details.                                #
#                                                                             #
# You should have received a copy of the GNU General Public License           #
# along with this program.  If not, see <http://www.gnu.org/licenses/>.       #
#                                                                             #
###############################################################################

# 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"

# Allowed modes of the spanning tree protocol.
STP_ALLOWED_MODES="rstp stp"

function stp_enable() {
	local bridge=${1}
	assert isset bridge

	# Tell the kernel to enable STP.
	print 1 > ${SYS_CLASS_NET}/${bridge}/bridge/stp_state
}

function stp_disable() {
	local bridge=${1}
	assert isset bridge

	# Tell the kernel to disable STP.
	print 0 > ${SYS_CLASS_NET}/${bridge}/bridge/stp_state
}

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

	local state=$(__device_get_file ${bridge} bridge/stp_state)
	case "${state}" in
		2)
			return ${EXIT_TRUE}
			;;
		*)
			return ${EXIT_FALSE}
			;;
	esac
}

function stp_get_name() {
	local proto=${1}

	case "${proto}" in
		stp)
			echo "Spanning Tree Protocol"
			;;
		rstp)
			echo "Rapid Spanning Tree Protocol"
			;;
		mstp)
			echo "Multiple Spanning Tree Protocol"
			;;
	esac

	return ${EXIT_OK}
}

function stp_bridge_set_protocol() {
	local bridge=${1}
	assert isset bridge

	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() {
	local bridge=${1}

	assert isset bridge

	# Let's check what the kernel is telling us about it's STP state.
	local state=$(__device_get_file ${bridge} "bridge/stp_state")

	case "${state}" in
		0)
			# STP is disabled.
			return ${EXIT_OK}
			;;
		1)
			# Kernel mode STP is running.
			echo "stp"
			return ${EXIT_OK}
			;;
		2)
			# User-space STP is running.
			;;
		*)
			log ERROR "Kernel is running in an unknown STP state."
			return ${EXIT_ERROR}
			;;
	esac

	# 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

	__device_get_file ${bridge} "bridge/bridge_id"

	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

	local fdelay=${2}
	assert isinteger fdelay

	# 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

	local hello=${2}
	assert isinteger hello

	# 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

	local maxage=${2}
	assert isinteger maxage

	# 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_priority() {
	local bridge=${1}
	assert isset bridge

	__device_get_file ${bridge} "bridge/priority"
	return ${EXIT_OK}
}

function stp_bridge_set_priority() {
	local bridge=${1}
	assert isset bridge

	local priority=${2}
	assert isinteger priority

	# Check if the setting we want is already set.
	local current_priority=$(stp_bridge_get_priority ${bridge})
	[ ${priority} -eq ${current_priority} ] && return ${EXIT_OK}

	# 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_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

	if stp_is_userspace ${bridge}; then
		cmd mstpctl showbridge ${bridge} path-cost
	else
		__device_get_file ${bridge} bridge/root_path_cost
	fi

	return ${EXIT_OK}
}

function stp_bridge_get_root_port_id() {
	local bridge=${1}
	assert isset bridge

	if stp_is_userspace ${bridge}; then
		local root_port=$(cmd mstpctl showbridge ${bridge} root-port)

		# 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 member_id
	for member in $(bridge_get_members ${bridge}); do
		member_id=$(stp_port_get_id ${bridge} ${member})

		if [ "${id}" = "${member_id}" ]; then
			print "${member}"
			return ${EXIT_OK}
		fi
	done

	return ${EXIT_ERROR}
}

function stp_bridge_is_root() {
	local bridge=${1}
	assert isset bridge

	local root_path_cost=$(stp_bridge_get_root_path_cost ${bridge})

	if [ "${root_path_cost}" = "0" ]; then
		return ${EXIT_TRUE}
	fi

	return ${EXIT_FALSE}
}

function stp_bridge_get_topology_change_count() {
	local bridge=${1}
	assert isset bridge

	if stp_is_userspace ${bridge}; then
		cmd mstpctl showbridge ${bridge} topology-change-count
	else
		__device_get_file ${bridge} bridge/topology_change
	fi

	return ${EXIT_OK}
}

function stp_bridge_get_topology_change_timer() {
	local bridge=${1}
	assert isset bridge

	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_OK}
}

function stp_bridge_get_topology_change_detected() {
	local bridge=${1}
	assert isset bridge

	local change

	if stp_is_userspace ${bridge}; then
		change=$(mstpctl showbridge ${bridge} topology-change)
	else
		change=$(__device_get_file ${bridge} bridge/topology_change_detected)
	fi

	if enabled change; then
		print "yes"
		return ${EXIT_TRUE}
	else
		print "no"
		return ${EXIT_FALSE}
	fi
}

function stp_port_get_state() {
	local bridge=${1}
	assert isset bridge

	local port=${2}
	assert isset port

	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

	return ${EXIT_OK}
}

function stp_port_get_id() {
	local bridge=${1}
	assert isset bridge

	local port=${2}
	assert isset port

	dec $(__device_get_file ${bridge} "brif/${port}/port_no")
	return ${EXIT_OK}
}

function stp_port_get_cost() {
	local bridge=${1}
	assert isset bridge

	local port=${2}
	assert isset port

	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}
	assert isset bridge

	local port=${2}
	assert isset port

	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 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}"
}