[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-Id: <ed6b04eab48a70d6416a6b021f04f9901f7e9f01.1547830302.git.sbrivio@redhat.com>
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