Linux bridge forwarding table

Hi everybody,

This goes about knowing the learning table of the linux bridge (where the well known as "br-lan" is for sure the most popular case). The objective is then to find out on which bridge's port each MAC address from the outside is detected.

Installing the "bridge" package this information appears with the following command:

root@OpenWrt:~# brctl showmacs br-lan
port no mac addr                is local?       ageing timer
  1     2c:db:07:54:47:7d       no                29.21
  1     44:d4:54:a0:0c:f1       no                 7.96
  1     60:32:b1:fd:80:37       yes                0.00
  1     60:32:b1:fd:80:37       yes                0.00
  3     62:32:b1:fd:80:39       yes                0.00
  3     62:32:b1:fd:80:39       yes                0.00
  2     62:32:b1:fd:80:40       yes                0.00
  2     62:32:b1:fd:80:40       yes                0.00
  4     62:32:b1:fd:80:41       yes                0.00
  4     62:32:b1:fd:80:41       yes                0.00
  5     68:ff:7b:3b:53:90       no                 8.00
  5     6a:ff:7b:3b:53:8e       no                31.42
  1     d4:3d:7e:bc:ec:cf       no                 0.40

There is another better way to obtain this information? I mean, a more appropiate inter process communication. For example, if this was available via an ubus object the usage could be more efficient and also it will guarantee a parsing schema (json in that case).

As a software will need this, if I go with the "cli" approach I will need to parse the human readable text as it is, with a risk of a future update where this syntax could be changed and all would stop working.

Thanks in advance.

Pablo.

You can parse the binary file /sys/class/net/br-lan/brforward. It is a packed series of struct __fdb_entry structures.

Each record is 16 bytes. Bytes 0-5 are the mac address, byte 6 is the bridge port number, byte 7 designates the local state, bytes 8-11 the timer value and byte 12 the high port number index in case the value exceeds 255. The remaining three bytes are padding/reserved.

A crude implementation in shell may look like the code below:

hexdump -v -e '5/1 "%02x:" 1/1 "%02x " 1/1 "%u " 1/1 "%u " 1/4 "%u " 1/1 "%u " 3/1 "" "\n"' /sys/class/net/br-lan/brforward | \
while read mac port_lo islocal timer port_hi; do
  echo "MAC: $mac / Local? $islocal / Port: $((port_hi << 16 | port_lo)) / Timer: $((timer / 100)).$((timer % 100))"
done

This will produce an output similar to this:

MAC: dc:f7:56:49:c5:1c / Local? 0 / Port: 3 / Timer: 125.56
MAC: b0:4a:39:0b:b8:e0 / Local? 0 / Port: 1 / Timer: 35.45
MAC: 42:b6:67:b7:18:81 / Local? 0 / Port: 1 / Timer: 18.98
MAC: f8:4d:89:7b:62:83 / Local? 0 / Port: 1 / Timer: 39.23
MAC: 10:12:00:00:65:98 / Local? 0 / Port: 1 / Timer: 38.26
MAC: 4e:dc:78:4f:7e:65 / Local? 0 / Port: 1 / Timer: 48.41
MAC: 44:c6:5d:65:15:bf / Local? 0 / Port: 1 / Timer: 43.94
MAC: 00:d8:61:a5:d2:da / Local? 0 / Port: 2 / Timer: 0.0
MAC: fc:bd:67:9d:1f:31 / Local? 0 / Port: 1 / Timer: 3.97
MAC: 00:0d:b9:35:88:49 / Local? 0 / Port: 1 / Timer: 0.26
MAC: 74:83:ef:4f:99:d5 / Local? 0 / Port: 1 / Timer: 2.70
MAC: 74:ac:b9:a1:84:4b / Local? 1 / Port: 6 / Timer: 0.0
MAC: 74:ac:b9:a1:84:4a / Local? 1 / Port: 5 / Timer: 0.0
MAC: 74:ac:b9:a1:84:4a / Local? 1 / Port: 5 / Timer: 0.0
MAC: 74:ac:b9:a1:84:49 / Local? 1 / Port: 4 / Timer: 0.0
MAC: 74:ac:b9:a1:84:49 / Local? 1 / Port: 4 / Timer: 0.0
MAC: 74:ac:b9:a1:84:48 / Local? 1 / Port: 3 / Timer: 0.0
MAC: 74:ac:b9:a1:84:48 / Local? 1 / Port: 3 / Timer: 0.0
MAC: 74:ac:b9:a1:84:47 / Local? 1 / Port: 2 / Timer: 0.0
MAC: 74:ac:b9:a1:84:47 / Local? 1 / Port: 2 / Timer: 0.0
MAC: 74:ac:b9:a1:84:46 / Local? 1 / Port: 1 / Timer: 0.0

You can then freely format it to your needs, maybe even turn it into an rpcd exec plugin to output the above contents formatted as JSON via ubus.

3 Likes

Thank you very much. I am exploring these files but I do not find where I can obtain the pairing between the port number and the "ifname" of each interface.

With the cli the pairing can be seen with:

 brctl showstp br-lan

Any idea?

Yes, you can identify those via /sys/class/net/$bridgename/lower_*/brport/port_no

Complete example script:

#!/bin/sh

lookup_port() {
	local bridge=$1
	local portno=$(printf "0x%x" $2)
	local path

	for path in $(grep -l $portno /sys/class/net/"$bridge"/lower_*/brport/port_no); do
		basename $(readlink "${path%/brport/port_no}")
		return 0
	done

	return 1
}

parse_fdb() {
	local bridge=$1
	local callback=${2:-echo}
	local record

	IFS=$'\n'

	for record in $(
		hexdump -v -e '5/1 "%02x:" 1/1 "%02x " 1/1 "%u " 1/1 "%u " 1/4 "%u " 1/1 "%u " 3/1 "" "\n"' \
			"/sys/class/net/$bridge/brforward"
	); do
		IFS=' '
		set -- $record

		local mac=$1
		local port=$(lookup_port "$bridge" $(($5 << 16 | $2)))
		local islocal=$3
		local timer=$4

		${callback:-echo} "$mac" "$port" "$islocal" "$timer" 
	done
}

dump_entry() {
	local mac=$1
	local ifname=$2
	local islocal=$3
	local timer=$4

	printf "MAC: %s  Ifname: %s\tLocal? %d  Timer: %d.%d\n" \
		"$mac" "$ifname" "$islocal" \
		$((timer / 100)) $((timer % 100))
}

parse_fdb "${1:-br-lan}" dump_entry

Produces the following output for me (the bridge on my system is called switch0):

root@er-x:~# sh  /root/fdb.sh switch0
MAC: 00:d8:61:a5:d2:da  Ifname: eth1	Local? 0  Timer: 0.1
MAC: 42:b6:67:b7:18:81  Ifname: eth0	Local? 0  Timer: 27.43
MAC: 10:12:00:00:65:98  Ifname: eth0	Local? 0  Timer: 12.36
MAC: b0:4a:39:0b:b8:e0  Ifname: eth0	Local? 0  Timer: 13.31
MAC: f8:4d:89:7b:62:83  Ifname: eth0	Local? 0  Timer: 15.40
MAC: 4e:dc:78:4f:7e:65  Ifname: eth0	Local? 0  Timer: 77.29
MAC: 44:c6:5d:65:15:bf  Ifname: eth0	Local? 0  Timer: 2.40
MAC: fc:bd:67:9d:1f:31  Ifname: eth0	Local? 0  Timer: 7.43
MAC: 00:0d:b9:35:88:49  Ifname: eth0	Local? 0  Timer: 0.36
MAC: 74:83:ef:4f:99:d5  Ifname: eth0	Local? 0  Timer: 1.24
MAC: 74:ac:b9:a1:84:4b  Ifname: eth5	Local? 1  Timer: 0.0
MAC: 74:ac:b9:a1:84:4a  Ifname: eth4	Local? 1  Timer: 0.0
MAC: 74:ac:b9:a1:84:4a  Ifname: eth4	Local? 1  Timer: 0.0
MAC: 74:ac:b9:a1:84:49  Ifname: eth3	Local? 1  Timer: 0.0
MAC: 74:ac:b9:a1:84:49  Ifname: eth3	Local? 1  Timer: 0.0
MAC: 74:ac:b9:a1:84:48  Ifname: eth2	Local? 1  Timer: 0.0
MAC: 74:ac:b9:a1:84:48  Ifname: eth2	Local? 1  Timer: 0.0
MAC: 74:ac:b9:a1:84:47  Ifname: eth1	Local? 1  Timer: 0.0
MAC: 74:ac:b9:a1:84:47  Ifname: eth1	Local? 1  Timer: 0.0
MAC: 74:ac:b9:a1:84:46  Ifname: eth0	Local? 1  Timer: 0.0
root@er-x:~# 
3 Likes

Great, thank you very much.

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.