lists.openwall.net | lists / announce owl-users owl-dev john-users john-dev passwdqc-users yescrypt popa3d-users / oss-security kernel-hardening musl sabotage tlsify passwords / crypt-dev xvendor / Bugtraq Full-Disclosure linux-kernel linux-netdev linux-ext4 linux-hardening linux-cve-announce PHC | |
Open Source and information security mailing list archives
| ||
|
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