From: Stefano Brivio <sbrivio@redhat.com>
To: Phil Sutter <phil@nwl.cc>, Alexander Wirt <formorer@debian.org>,
Luca Boccassi <bluca@debian.org>,
Santiago Garcia Mantinan <manty@debian.org>,
Steve Langasek <steve.langasek@ubuntu.com>,
Christian Hesse <mail@eworm.de>,
Ronald van Haren <ronald@archlinux.org>,
Terry Chvatal <tchvatal@suse.com>
Cc: David Ahern <dsahern@gmail.com>, Eric Garver <egarver@redhat.com>,
Tomas Dolezal <todoleza@redhat.com>,
Stephen Hemminger <stephen@networkplumber.org>,
Lennert Buytenhek <buytenh@gnu.org>,
netdev@vger.kernel.org
Subject: Re: [PATCH iproute2-next] Introduce ip-brctl shell script
Date: Thu, 31 Jan 2019 13:49:05 +0100 [thread overview]
Message-ID: <20190131134905.7a4d60b2@redhat.com> (raw)
In-Reply-To: <ed6b04eab48a70d6416a6b021f04f9901f7e9f01.1547830302.git.sbrivio@redhat.com>
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@redhat.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@redhat.com>
> Acked-by: Phil Sutter <phil@nwl.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|@SYSCONFDIR@|$(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@gnu.org>
> +Stephen Hemminger <stephen@networkplumber.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@redhat.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
prev parent reply other threads:[~2019-01-31 12:49 UTC|newest]
Thread overview: 17+ messages / expand[flat|nested] mbox.gz Atom feed top
2019-01-18 17:00 [PATCH iproute2-next] Introduce ip-brctl shell script Stefano Brivio
2019-01-23 15:09 ` Nikolay Aleksandrov
2019-01-23 16:33 ` Roopa Prabhu
2019-01-25 10:05 ` Stefano Brivio
2019-01-28 5:08 ` Roopa Prabhu
2019-01-28 7:57 ` Stefano Brivio
2019-01-30 22:30 ` Roopa Prabhu
2019-01-31 12:46 ` Stefano Brivio
2019-01-31 16:28 ` Roopa Prabhu
2019-02-05 22:50 ` Stephen Hemminger
2019-02-06 10:55 ` Stefano Brivio
2019-01-25 10:04 ` Stefano Brivio
2019-01-30 4:51 ` David Ahern
2019-01-30 10:55 ` Stefano Brivio
2019-01-31 5:12 ` David Ahern
2019-01-31 12:46 ` Stefano Brivio
2019-01-31 12:49 ` Stefano Brivio [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20190131134905.7a4d60b2@redhat.com \
--to=sbrivio@redhat.com \
--cc=bluca@debian.org \
--cc=buytenh@gnu.org \
--cc=dsahern@gmail.com \
--cc=egarver@redhat.com \
--cc=formorer@debian.org \
--cc=mail@eworm.de \
--cc=manty@debian.org \
--cc=netdev@vger.kernel.org \
--cc=phil@nwl.cc \
--cc=ronald@archlinux.org \
--cc=stephen@networkplumber.org \
--cc=steve.langasek@ubuntu.com \
--cc=tchvatal@suse.com \
--cc=todoleza@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.