lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Date:   Fri, 18 Jan 2019 18:00:45 +0100
From:   Stefano Brivio <sbrivio@...hat.com>
To:     David Ahern <dsahern@...il.com>
Cc:     Phil Sutter <phil@....cc>, Eric Garver <egarver@...hat.com>,
        Tomas Dolezal <todoleza@...hat.com>,
        Stephen Hemminger <stephen@...workplumber.org>,
        Lennert Buytenhek <buytenh@....org>, netdev@...r.kernel.org
Subject: [PATCH iproute2-next] Introduce ip-brctl shell script

This script wraps 'ip' and 'bridge' tools to provide a drop-in replacement
of the standalone 'brctl' utility.

It's bug-to-bug compatible with brctl as of bridge-utils version 1.6,
has no dependencies other than a POSIX shell, and it's less than half
the binary size of brctl on x86_64.

As many users (including myself) seem to find brctl usage vastly more
intuitive than ip-link, possibly due to habit, this might be a lightweight
approach to provide brctl syntax without the need to maintain bridge-utils
any longer.

Signed-off-by: Stefano Brivio <sbrivio@...hat.com>
Acked-by: Phil Sutter <phil@....cc>
---
 man/man8/Makefile   |   5 +-
 man/man8/ip-brctl.8 | 187 +++++++++++++++
 misc/Makefile       |   9 +-
 misc/ip-brctl.in    | 572 ++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 770 insertions(+), 3 deletions(-)
 create mode 100644 man/man8/ip-brctl.8
 create mode 100755 misc/ip-brctl.in

diff --git a/man/man8/Makefile b/man/man8/Makefile
index 932ba1f3c488..26d2370a9cbe 100644
--- a/man/man8/Makefile
+++ b/man/man8/Makefile
@@ -1,5 +1,5 @@
 # SPDX-License-Identifier: GPL-2.0
-TARGETS = ip-address.8 ip-link.8 ip-route.8
+TARGETS = ip-address.8 ip-link.8 ip-route.8 brctl.8
 
 MAN8PAGES = $(TARGETS) $(filter-out $(TARGETS),$(wildcard *.8))
 
@@ -14,6 +14,9 @@ ip-link.8: ip-link.8.in
 ip-route.8: ip-route.8.in
 	sed "s|@...CONFDIR@|$(CONFDIR)|g" $< > $@
 
+brctl.8: ip-brctl.8
+	echo '.so man8/ip-brctl.8' >$@
+
 distclean: clean
 
 clean:
diff --git a/man/man8/ip-brctl.8 b/man/man8/ip-brctl.8
new file mode 100644
index 000000000000..63c1bcdebe9f
--- /dev/null
+++ b/man/man8/ip-brctl.8
@@ -0,0 +1,187 @@
+.\"
+.\"	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 2 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, write to the Free Software
+.\"	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+.\"
+.\"
+.TH IP-BRCTL 8 "January 18, 2019" "" ""
+.SH NAME
+ip-brctl \- ethernet bridge administration
+.SH SYNOPSIS
+.BR "brctl [command]"
+.SH DESCRIPTION
+.B ip-brctl
+is a reimplementation of the traditional
+.B brctl
+utility in shell making use of
+.BR ip " and " bridge
+tools internally. It is supposed to behave identically to
+.BR brctl ,
+hence the remainder of this document will use that name instead of
+.BR ip-brctl .
+
+.B brctl
+is used to set up, maintain, and inspect the ethernet bridge
+configuration in the Linux kernel.
+
+An ethernet bridge is a device commonly used to connect different
+networks of ethernets together, so that these ethernets will appear as
+one ethernet to the participants.
+
+Each of the ethernets being connected corresponds to one physical
+interface in the bridge. These individual ethernets are bundled into
+one bigger ('logical') ethernet, this bigger ethernet corresponds to
+the bridge network interface.
+
+
+.SH INSTANCES
+The command
+.B brctl addbr <name>
+creates a new instance of the ethernet bridge. The network interface
+corresponding to the bridge will be called <name>.
+
+The command
+.B brctl delbr <name>
+deletes the instance <name> of the ethernet bridge. The network
+interface corresponding to the bridge must be down before it can be
+deleted!
+
+The command
+.B brctl show
+shows all current instances of the ethernet bridge.
+
+
+.SH PORTS
+Each bridge has a number of ports attached to it. Network traffic
+coming in on any of these ports will be forwarded to the other ports
+transparently, so that the bridge is invisible to the rest of the
+network (i.e. it will not show up in
+.IR traceroute(8)
+).
+
+The command
+.B brctl addif <brname> <ifname>
+will make the interface <ifname> a port of the bridge <brname>. This
+means that all frames received on <ifname> will be processed as if
+destined for the bridge. Also, when sending frames on <brname>,
+<ifname> will be considered as a potential output interface.
+
+The command
+.B brctl delif <brname> <ifname>
+will detach the interface <ifname> from the bridge <brname>.
+
+The command
+.B brctl show <brname>
+will show some information on the bridge and its attached ports.
+
+
+.SH AGEING
+The bridge keeps track of ethernet addresses seen on each port. When
+it needs to forward a frame, and it happens to know on which port the
+destination ethernet address (specified in the frame) is located, it
+can 'cheat' by forwarding the frame to that port only, thus saving a
+lot of redundant copies and transmits.
+
+However, the ethernet address location data is not static
+data. Machines can move to other ports, network cards can be replaced
+(which changes the machine's ethernet address), etc.
+
+.B brctl showmacs <brname>
+shows a list of learned MAC addresses for this bridge.
+
+.B brctl setageing <brname> <time>
+sets the ethernet (MAC) address ageing time, in seconds. After <time>
+seconds of not having seen a frame coming from a certain address, the
+bridge will time out (delete) that address from the Forwarding
+DataBase (fdb).
+
+.B brctl setgcint <brname> <time>
+sets the garbage collection interval for the bridge <brname> to <time>
+seconds. This means that the bridge will check the forwarding database
+for timed out entries every <time> seconds.
+
+
+.SH SPANNING TREE PROTOCOL
+Multiple ethernet bridges can work together to create even larger
+networks of ethernets using the IEEE 802.1d spanning tree
+protocol. This protocol is used for finding the shortest path between
+two ethernets, and for eliminating loops from the topology. As this
+protocol is a standard, Linux bridges will interwork properly with
+other third party bridge products. Bridges communicate with each other
+by sending and receiving BPDUs (Bridge Protocol Data Units). These
+BPDUs can be recognised by an ethernet destination address of
+01:80:c2:00:00:00.
+
+The spanning tree protocol can also be turned off (for those
+situations where it just doesn't make sense, for example when this
+Linux box is the only bridge on the LAN, or when you know that there
+are no loops in the topology.)
+
+.IR brctl(8)
+can be used for configuring certain spanning tree protocol
+parameters. For an explanation of these parameters, see the IEEE
+802.1d specification (or send me an email). The default values should
+be just fine. If you don't know what these parameters mean, you
+probably won't feel the desire to tweak them.
+
+.B brctl stp <bridge> <state>
+controls this bridge instance's participation in the spanning tree
+protocol. If <state> is "on" or "yes" the STP will be turned on,
+otherwise it will be turned off.  When turned off, the bridge will not
+send or receive BPDUs, and will thus not participate in the spanning
+tree protocol. If your bridge isn't the only bridge on the LAN, or if
+there are loops in the LAN's topology, DO NOT turn this option off. If
+you turn this option off, please know what you are doing.
+
+
+.B brctl setbridgeprio <bridge> <priority>
+sets the bridge's priority to <priority>. The priority value is an
+unsigned 16-bit quantity (a number between 0 and 65535), and has no
+dimension. Lower priority values are 'better'. The bridge with the
+lowest priority will be elected 'root bridge'.
+
+.B brctl setfd <bridge> <time>
+sets the bridge's 'bridge forward delay' to <time> seconds.
+
+.B brctl sethello <bridge> <time>
+sets the bridge's 'bridge hello time' to <time> seconds.
+
+.B brctl setmaxage <bridge> <time>
+sets the bridge's 'maximum message age' to <time> seconds.
+
+.B brctl setpathcost <bridge> <port> <cost>
+sets the port cost of the port <port> to <cost>. This is a
+dimensionless metric.
+
+.B brctl setportprio <bridge> <port> <priority>
+sets the port <port>'s priority to <priority>. The priority value is
+an unsigned 8-bit quantity (a number between 0 and 255), and has no
+dimension. This metric is used in the designated port and root port
+selection algorithms.
+
+.SH NOTES
+.BR brctl(8)
+is obsolete. Some features such as STP guard, harpin mode, fastleave and root
+block are intentionally not implemented in this command.
+Instead use
+.B bridge
+command from
+.B iproute2
+package for a more full set of features.
+
+.SH SEE ALSO
+.BR iptables(8)
+
+.SH AUTHOR
+Lennert Buytenhek <buytenh@....org>
+Stephen Hemminger <stephen@...workplumber.org>
diff --git a/misc/Makefile b/misc/Makefile
index 6a849af4be22..79c6be31b874 100644
--- a/misc/Makefile
+++ b/misc/Makefile
@@ -3,6 +3,7 @@ SSOBJ=ss.o ssfilter.o
 LNSTATOBJ=lnstat.o lnstat_util.o
 
 TARGETS=ss nstat ifstat rtacct lnstat
+SCRIPTS=ip-brctl
 
 include ../config.mk
 
@@ -10,7 +11,7 @@ ifeq ($(HAVE_BERKELEY_DB),y)
 	TARGETS += arpd
 endif
 
-all: $(TARGETS)
+all: $(TARGETS) $(SCRIPTS)
 
 ss: $(SSOBJ)
 	$(QUIET_LINK)$(CC) $^ $(LDFLAGS) $(LDLIBS) -o $@
@@ -33,10 +34,14 @@ ssfilter.c: ssfilter.y
 lnstat: $(LNSTATOBJ)
 	$(QUIET_LINK)$(CC) $^ $(LDFLAGS) $(LDLIBS) -o $@
 
+ip-brctl: ip-brctl.in
+	@sed -e "s|^\(IP=\"\).*\"|\1$(DESTDIR)$(SBINDIR)\/ip\"|" -e "s|^\(BRIDGE=\"\).*\"|\1$(DESTDIR)$(SBINDIR)\/bridge\"|" $< > $@
+
 install: all
-	install -m 0755 $(TARGETS) $(DESTDIR)$(SBINDIR)
+	install -m 0755 $(TARGETS) $(SCRIPTS) $(DESTDIR)$(SBINDIR)
 	ln -sf lnstat $(DESTDIR)$(SBINDIR)/rtstat
 	ln -sf lnstat $(DESTDIR)$(SBINDIR)/ctstat
+	ln -sf ip-brctl $(DESTDIR)$(SBINDIR)/brctl
 
 clean:
 	rm -f *.o $(TARGETS) ssfilter.c
diff --git a/misc/ip-brctl.in b/misc/ip-brctl.in
new file mode 100755
index 000000000000..7e7059aa2c06
--- /dev/null
+++ b/misc/ip-brctl.in
@@ -0,0 +1,572 @@
+#!/bin/sh
+#
+# ip-brctl: Implementation of brctl from bridge-utils as iproute2 wrapper
+#
+# Copyright (c) 2019 Red Hat, Inc.
+# Author: Stefano Brivio <sbrivio@...hat.com>
+#
+# SPDX-License-Identifier: GPL-2.0
+
+# Global variables #############################################################
+
+# These will be replaced by Makefile on install with ip and bridge install paths
+IP="../ip/ip"
+BRIDGE="../bridge/bridge"
+
+usage_text="Usage: brctl [commands]
+commands:
+	addbr     	<bridge>		add bridge
+	delbr     	<bridge>		delete bridge
+	addif     	<bridge> <device>	add interface to bridge
+	delif     	<bridge> <device>	delete interface from bridge
+	hairpin   	<bridge> <port> {on|off}	turn hairpin on/off
+	setageing 	<bridge> <time>		set ageing time
+	setbridgeprio	<bridge> <prio>		set bridge priority
+	setfd     	<bridge> <time>		set bridge forward delay
+	sethello  	<bridge> <time>		set hello time
+	setmaxage 	<bridge> <time>		set max message age
+	setpathcost	<bridge> <port> <cost>	set path cost
+	setportprio	<bridge> <port> <prio>	set port priority
+	show      	[ <bridge> ]		show a list of bridges
+	showmacs  	<bridge>		show a list of mac addrs
+	showstp   	<bridge>		show bridge stp info
+	stp       	<bridge> {on|off}	turn stp on/off"
+
+# List of commands prefixed by minimum number of arguments
+commands="1 addbr 1 delbr 2 addif 2 delif 3 hairpin 2 setageing 2 setbridgeprio
+	  2 setfd 2 sethello 2 setmaxage 3 setpathcost 3 setportprio 0 show
+	  1 showmacs 1 showstp 2 stp"
+
+
+# Helper functions #############################################################
+
+usage() {
+	echo "${usage_text}"
+	exit "${1:-1}"
+}
+
+# Print a single line from usage for given command
+usage_one() {
+	command="${1}"
+
+	ifs="${IFS}"
+	IFS='
+'
+	for line in ${usage_text}; do
+		case ${line} in
+		"	${command}"*)
+			line="${line#	${command}*}"
+			while [ "${line}" != "${line#[[:blank:]]*}" ]; do
+				line="${line#[[:blank:]]*}"
+			done
+			echo "Usage: brctl ${command} ${line}"
+			break
+			;;
+		esac
+	done
+	IFS="${ifs}"
+
+	exit 1
+}
+
+# Print to standard error and exit
+err() {
+	echo "${@}" > /dev/stderr
+	exit 1
+}
+
+# Output token following the given one, from a space-separated string
+parse_next() {
+	needle=${1}
+	str=${2}
+
+	next=0
+	ifs="${IFS}"
+	IFS=' '
+	for token in ${str}; do
+		[ ${next} -eq 1 ] && echo "${token}" && break
+		[ "${token}" = "${needle}" ] && next=1
+	done
+	IFS="${ifs}"
+}
+
+# Output value for given ip-link attribute, for the given device
+parse_iplink() {
+	attr="${1}"
+	dev="${2}"
+
+	out="$(${IP} -d link show dev "${dev}" 2>/dev/null)"
+	parse_next "${attr}" "${out}"
+}
+
+# Once starting token is matched, for each token x with index n = 2 * k, assign
+# value of token with index n + 1 to a variable named by the value of x, using
+# state variables parse_assign_start and parse_assign_prev. Pass one token at
+# a time.
+parse_assign() {
+	token="${1}"
+	start_token="${2}"
+
+	if [ "${token}" = "${start_token}" ]; then
+		parse_assign_start=1
+		parse_assign_prev=
+	elif [ ${parse_assign_start} -eq 0 ]; then
+		:
+	elif [ -z "${parse_assign_prev}" ]; then
+		parse_assign_prev="${token}"
+	else
+		eval "${parse_assign_prev}"="${token}"
+		parse_assign_prev=
+	fi
+}
+
+# Execute ip-link command with given arguments. On failure, print returned error
+# message prefixed by given string and exit.
+exec_iplink() {
+	args="${1}"
+	err_prefix="${2}"
+
+	err_out="$(${IP} link "${args}" 2>&1)" || err "${err_prefix}: ${err_out#*:}"
+}
+
+# Print passed error string and exit if the given device does or does not exist,
+# depending on the condition given. Device type matching is optional.
+err_dev_exists() {
+	cond="${1}"
+	dev="${2}"
+	err_string="${3}"
+	opt_type="${4}"
+
+	[ -n "${opt_type}" ] && opt_type="type ${opt_type}" || opt_type=
+
+	if [ -n "$(${IP} link show dev "${dev}" ${opt_type} 2>/dev/null)" ]; then
+		[ "${cond}" = "y" ] && err "${err_string}"
+	else
+		[ "${cond}" = "n" ] && err "${err_string}"
+	fi
+}
+
+
+# Type checks and conversions ##################################################
+
+# Don't attempt operations on values exceeding limits for a signed long. From
+# POSIX.1-2017, XCU, par. 2.6.4 Arithmetic Expansion:
+#
+#	Only signed long integer arithmetic is required.
+#
+# On failure, print returned error message prefixed by given string and exit.
+check_long() {
+	in="${1}"
+	err_prefix="${2}"
+
+	# Comparing x >= 2^31 may fail, allow up to one digit shorter than that
+	long_max=2147483647
+	if [ ${#in} -ge ${#long_max} ]; then
+		err "${err_prefix}: Numerical result out of range"
+	fi
+}
+
+# On failure, print passed error string and exit
+check_float() {
+	in="${1}"
+	err_string="${2}"
+
+	case ${in} in
+	[0-9.]*) ;;
+	*) err "${err_string}" ;;
+	esac
+}
+
+# Convert values allowed as boolean to given strings for false and true values
+make_bool() {
+	in="${1}"
+	str_false="${2}"
+	str_true="${2}"
+
+	case ${in} in
+	off|no|0) echo "${str_false}" ;;
+	on|yes|1) echo "${str_true}" ;;
+	*) err "expect on/off for argument" ;;
+	esac
+}
+
+# Divide by 100, output number with two fractional digits
+make_float() {
+	in="${1}"
+
+	int=$((in / 100))
+	frac=$((in % 100))
+	[ ${#frac} -eq 1 ] && frac="0${frac}"
+	echo "${int}.${frac}"
+}
+
+# Multiply given float by 100. Multiple decimal separators are allowed, anything
+# after the second one is discarded.
+brctl_timeval() {
+	in="${1}"
+
+	# Drop second decimal separator and following digits, if any
+	drop=${in#[0-9]*.[0-9]*.}
+	time=${in%%.${drop}}
+
+	int=${time%.*}
+
+	# Take up to two digits from fractional part
+	frac=${time#*.}
+	drop=${frac#[0-9][0-9]*}
+	frac=${frac%%${drop}}
+	frac=${frac:-0}
+	[ "${frac}" -lt 10 ] && frac=$((frac * 10))
+
+	echo "$((int * 100 + frac))"
+}
+
+# Strip colons from MAC address in bridge identifiers, pad bytes with 0
+fixup_id() {
+	in="${1}"
+
+	ifs="${IFS}"
+	IFS=':'
+	out=
+	for byte in ${in}; do
+		[ ${#byte} -eq 1 ] && byte="0${byte}"
+		out="${out}${byte}"
+	done
+	IFS="${ifs}"
+	echo "${out}"
+}
+
+
+# Display functions ############################################################
+
+# Pad string to given width -- if not given, width is 7 if float, 4 otherwise
+pad_width() {
+	str="${1}"
+	[ "${str}" != "${str%.*}" ] && width=${2:-7} || width=${2:-4}
+
+	len=${#str}
+	while [ "${len}" -lt "${width}" ]; do
+		str=" ${str}"
+		len=$((len + 1))
+	done
+	echo "${str}"
+}
+
+# Dump bridge information from set variables in 'brctl showstp' format
+#
+# shellcheck disable=SC2154 # shellcheck won't see variables we set under eval
+dump_bridge() {
+	name="${1}"
+
+	for i in bridge_id designated_root; do
+		eval ${i}="\$(fixup_id \$${i})"
+	done
+	for i in max_age hello_time forward_delay ageing_time; do
+		eval ${i}="\$(make_float \$${i})"
+	done
+	for i in root_port root_path_cost max_age hello_time forward_delay \
+		 ageing_time hello_timer tcn_timer topology_change_timer \
+		 gc_timer; do
+		eval ${i}=\""\$(pad_width \$${i})"\"
+	done
+	flags=
+	[ "${topology_change}" != "0" ] && flags="TOPOLOGY_CHANGE "
+	[ "${topology_change_detected}" -ne "0" ] && flags="${flags}TOPOLOGY_CHANGE_DETECTED "
+
+	echo "${name}"
+	echo " bridge id		${bridge_id}"
+	echo " designated root	${designated_root}"
+	echo " root port		${root_port}			path cost		${root_path_cost}"
+	echo " max age		${max_age}			bridge max age		${max_age}"
+	echo " hello time		${hello_time}			bridge hello time	${hello_time}"
+	echo " forward delay		${forward_delay}			bridge forward delay	${forward_delay}"
+	echo " ageing time		${ageing_time}"
+	echo " hello timer		${hello_timer}			tcn timer		${tcn_timer}"
+	echo " topology change timer	${topology_change_timer}			gc timer		${gc_timer}"
+	echo " flags			${flags}"
+	echo
+	echo
+}
+
+# Dump port information from set variables in 'brctl showstp' format
+#
+# shellcheck disable=SC2154 # shellcheck won't see variables we set under eval
+dump_port() {
+	for i in designated_root designated_bridge; do
+		eval ${i}="\$(fixup_id \$${i})"
+	done
+
+	for i in message_age_timer cost message_age_timer forward_delay_timer \
+		 designated_cost hold_timer; do
+		eval ${i}=\""\$(pad_width \$${i})"\"
+	done
+
+	flags=
+	[ "${config_pending}" != "0" ] && flags="CONFIG_PENDING "
+	[ "${topology_change_ack}" -ne "0" ] && flags="${flags}TOPOLOGY_CHANGE_ACK "
+
+	echo "${port_name%*:} (${port_no#0x*})"
+	echo " port id		${port_id#0x*}			state		$(pad_width "${state}" 15)"
+	echo " designated root	${designated_root}	path cost		${cost}"
+	echo " designated bridge	${designated_bridge}	message age timer	${message_age_timer}"
+	echo " designated port	$((8000 + designated_port % 32768))			forward delay timer	${forward_delay_timer}"
+	echo " designated cost	${designated_cost}			hold timer		${hold_timer}"
+	echo " flags ${flags}"
+	[ "${hairpin}" != "off" ] && echo " hairpin mode		$(pad_width 1)"
+	echo
+}
+
+
+# Commands #####################################################################
+
+cmd_addbr() {
+	err_dev_exists y "${1}" "device ${1} already exists; can't create bridge with the same name"
+
+	exec_iplink "add ${1} type bridge" "add bridge failed"
+}
+
+cmd_delbr() {
+	err_dev_exists n "${1}" "bridge ${1} doesn't exist; can't delete it"
+	if [ "$(parse_iplink state "${1}")" != "DOWN" ]; then
+		err "bridge ${1} is still up; can't delete it"
+	fi
+
+	exec_iplink "del ${1}" "can't delete bridge ${1}"
+}
+
+cmd_addif() {
+	err_dev_exists n "${1}" "bridge ${1} does not exist!"
+	err_dev_exists n "${2}" "interface ${2} does not exist!"
+	if [ -n "$(parse_iplink master "${1}")" ]; then
+		err "device ${2} is already a member of a bridge; can't enslave it to bridge ${1}."
+	fi
+	err_dev_exists y "${1}" "device ${1} is a bridge device itself; can't enslave a bridge device to a bridge device." bridge
+
+	exec_iplink "set ${2} master ${1}" "can't add ${2} to bridge ${1}"
+}
+
+cmd_delif() {
+	err_dev_exists n "${1}" "bridge ${1} does not exist!"
+	err_dev_exists n "${2}" "interface ${2} does not exist!"
+	if [ "$(parse_iplink master "${2}")" != "${1}" ]; then
+		err "device ${1} is not a slave of ${2}"
+	fi
+
+	exec_iplink "set ${2} nomaster" "can't delete ${2} from ${1}"
+}
+
+cmd_hairpin() {
+	hairpin="$(make_bool "${3}" off on)"
+	[ -z "${hairpin}" ] && exit 1
+	err_dev_exists n "${2}" "interface ${2} does not exist!"
+	err_dev_exists n "${1}" "bridge ${1} does not exist!"
+
+	exec_iplink "set ${2} type bridge_slave ${hairpin}" "can't set ${2} to hairpin on bridge ${1}"
+}
+
+cmd_setageing() {
+	check_long "${2}" "set ageing time failed"
+	check_float "${2}" "bad ageing time value"
+	err_dev_exists n "${1}" "set ageing time failed: No such device"
+
+	exec_iplink "set ${1} type bridge ageing_time $(brctl_timeval "${2}")" "set ageing time failed"
+}
+
+cmd_setbridgeprio() {
+	check_long "${2}" "set bridge priority failed"
+	check_float "${2}" "bad priority"
+	err_dev_exists n "${1}" "set bridge priority failed: No such device"
+
+	prio=${2%%.*}
+	prio=$((prio % 65536))
+
+	exec_iplink "set ${1} type bridge priority ${prio}" "set bridge priority failed"
+}
+
+cmd_setfd() {
+	check_long "${2}" "set forward delay failed"
+	check_float "${2}" "bad ageing time value"
+	err_dev_exists n "${1}" "set forward delay failed: No such device"
+
+	exec_iplink "set ${1} type bridge forward_delay $(brctl_timeval "${2}")" "set forward delay failed"
+}
+
+cmd_sethello() {
+	check_long "${2}" "set ageing time failed"
+	check_float "${2}" "bad hello timer value"
+	err_dev_exists n "${1}" "set hello timer failed: No such device"
+
+	exec_iplink "set ${1} type bridge hello_time $(brctl_timeval "${2}")" "set hello timer failed"
+}
+
+cmd_setmaxage() {
+	check_long "${2}" "set max age failed"
+	check_float "${2}" "bad max age value"
+	err_dev_exists n "${1}" "set max age failed: No such device"
+
+	exec_iplink "set ${1} max_age $(brctl_timeval "${2}")" "set max age failed"
+}
+
+cmd_setpathcost() {
+	check_long "${3}" "set path cost failed"
+	check_float "${3}" "bad path cost value"
+	err_dev_exists n "${2}" "set path cost failed: No such device"
+
+	cost=${3%%.*}
+
+	exec_iplink "set ${2} type bridge_slave cost ${cost}" "set path cost failed"
+}
+
+cmd_setportprio() {
+	check_long "${3}" "set port priority failed"
+	check_float "${3}" "bad path priority value"
+	err_dev_exists n "${2}" "set port priority failed: No such device"
+
+	prio=${3%%.*}
+	[ "${prio}" -ge 64 ] && err "set port priority failed: Numerical result out of range"
+
+	exec_iplink "set ${2} type bridge_slave priority ${prio}" "set port priority failed"
+}
+
+cmd_stp() {
+	stp="$(make_bool "${2}" 0 1)"
+	[ -z "${stp}" ] && exit 1
+	err_dev_exists n "${1}" "set stp status failed: No such device"
+
+	exec_iplink "set ${1} type bridge stp_state ${stp}" "set stp status failed"
+}
+
+cmd_showstp() {
+	parse_assign_start=0
+	for t in $(${IP} -d link show "${1}" 2>/dev/null); do
+		parse_assign "${t}" bridge
+	done
+	[ ${parse_assign_start} -eq 0 ] && err "${1}: can't get info No such device"
+
+	dump_bridge "${1}"
+
+	parse_assign_start=0
+	for t in $(${IP} -d link show type bridge_slave master "${1}" 2>/dev/null); do
+		case ${t} in
+		[0-9]*:)
+			[ -n "${port_name}" ] && dump_port
+			port_name=
+			parse_assign_start=0
+			continue
+			;;
+		esac
+		[ -z "${port_name}" ] && port_name="${t}"
+
+		parse_assign "${t}" bridge_slave
+	done
+
+	[ -n "${port_name}" ] && dump_port
+}
+
+cmd_show() {
+	dev="${1}"
+
+	if [ -n "${dev}" ]; then
+		err_dev_exists n "${dev}" "bridge ${dev} does not exist!"
+		err_dev_exists n "${dev}" "device ${dev} is not a bridge!" bridge
+	fi
+
+	echo "bridge name	bridge id		STP enabled	interfaces"
+	ifs="${IFS}"
+	IFS='
+'
+	for line in $(${IP} -br link show type bridge "${dev}" 2>/dev/null); do
+		name="${line%% *}"
+		id="$(fixup_id "$(parse_iplink bridge_id "${name}")")"
+		[ "$(parse_iplink stp_state "${name}")" = "0" ] && stp="no" || stp="yes"
+		first=1
+		for s in $(${IP} -br link show type bridge_slave master "${name}"); do
+			if [ ${first} -eq 1 ]; then
+				echo "${name}		${id}	${stp}		${s%% *}"
+				first=0
+			else
+				echo "							${s%% *}"
+			fi
+		done
+		[ ${first} -eq 1 ] && echo "${name}		${id}	${stp}"
+	done
+	IFS="${ifs}"
+}
+
+cmd_showmacs() {
+	err_dev_exists n "${1}" "read of forward table failed: No such device"
+
+	echo "port no	mac addr		is local?	ageing timer"
+	ifs="${IFS}"
+	IFS='
+'
+	for s in $(${IP} -br link show type bridge_slave master "${1}"); do
+		for fdb_entry in $(${BRIDGE} -s fdb show dev "${s%% *}"); do
+			case ${fdb_entry} in
+			*" ${1} "*) ;;
+			*) continue ;;
+			esac
+
+			case ${fdb_entry} in
+			*" permanent"*)
+				is_local="yes"
+				timer="$(pad_width "0.00")"
+				;;
+			*)
+				is_local="no"
+				timer="$(parse_next used "${fdb_entry}")"
+				timer="$(pad_width "${timer#*/}.00")"
+				;;
+			esac
+
+			port_no="$(parse_iplink port_no "${s%% *}")"
+			port_no="${port_no#0x*}"
+
+			echo "$(pad_width "${port_no}" 3)	${fdb_entry%% *}	${is_local}		${timer}"
+		done
+	done
+	IFS="${ifs}"
+}
+
+
+# Start here ###################################################################
+
+cmdname="${1}"
+[ -z "${cmdname}" ] && usage
+
+for arg do
+	case ${arg} in
+	"-V"|"--v"*)
+		echo "ip-link wrapper, compatible with bridge-utils, 1.6"
+		exit 0
+		;;
+	"-h"|"--h"*)
+		usage 0
+		;;
+	"--")
+		usage
+		;;
+	"-")
+		break
+		;;
+	"-"*)
+		echo "${0}: unrecognized option '${arg}'" >/dev/stderr
+		echo "Unknown option '?'" >/dev/stderr
+		usage
+		;;
+	esac
+done
+
+found=0
+min_arg=
+for cmd in ${commands}; do
+	[ -n "${min_arg}" ] && [ "${cmd}" = "${cmdname}" ] && found=1 && break
+	min_arg="${cmd}"
+done
+[ ${found} -eq 0 ] && echo "never heard of command [${cmdname}]" && usage
+[ ${#} -le "${min_arg}" ] && echo "Incorrect number of arguments for command" && usage_one "${cmd}"
+
+shift
+eval "cmd_${cmdname}" "${@}"
+
+exit 0
-- 
2.20.1

Powered by blists - more mailing lists