[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20190131134905.7a4d60b2@redhat.com>
Date: Thu, 31 Jan 2019 13:49:05 +0100
From: Stefano Brivio <sbrivio@...hat.com>
To: Phil Sutter <phil@....cc>, Alexander Wirt <formorer@...ian.org>,
Luca Boccassi <bluca@...ian.org>,
Santiago Garcia Mantinan <manty@...ian.org>,
Steve Langasek <steve.langasek@...ntu.com>,
Christian Hesse <mail@...rm.de>,
Ronald van Haren <ronald@...hlinux.org>,
Terry Chvatal <tchvatal@...e.com>
Cc: David Ahern <dsahern@...il.com>, Eric Garver <egarver@...hat.com>,
Tomas Dolezal <todoleza@...hat.com>,
Stephen Hemminger <stephen@...workplumber.org>,
Lennert Buytenhek <buytenh@....org>, netdev@...r.kernel.org
Subject: Re: [PATCH iproute2-next] Introduce ip-brctl shell script
Hi,
For your information: I recently submitted a patch that implements
brctl as a wrapper using ip-link and 'bridge' from iproute2, that
allows to drop bridge-utils while still providing brctl functionality:
https://patchwork.ozlabs.org/patch/1027627/
This wasn't exactly met with enthusiasm upstream (full discussion at:
https://marc.info/?l=linux-netdev&m=154783087917432), but feel free to
carry it downstream if you're interested.
Thanks,
--
Stefano
On Fri, 18 Jan 2019 18:00:45 +0100
Stefano Brivio <sbrivio@...hat.com> wrote:
> 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
Powered by blists - more mailing lists