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 <sbri...@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 <sbri...@redhat.com>
> Acked-by: Phil Sutter <p...@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 <buyt...@gnu.org>
> +Stephen Hemminger <step...@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 <sbri...@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

Reply via email to