# Linux bridge forwarding table

**URL:** <https://forum.openwrt.org/t/linux-bridge-forwarding-table/126169>\
**Category:** For Developers\
**Created:** [April 25, 2022, 7:27pm UTC](https://forum.openwrt.org/t/linux-bridge-forwarding-table/126169 "2022-04-25T19:27:21Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![Pablomagno](https://forum.openwrt.org/user_avatar/forum.openwrt.org/pablomagno/32/114753_2.png) [@Pablomagno](https://forum.openwrt.org/u/Pablomagno)\
**Post date:** [April 25, 2022, 7:27pm UTC](https://forum.openwrt.org/t/linux-bridge-forwarding-table/126169/1 "2022-04-25T19:27:21Z")

</div>

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:

```nohighlight
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.

---

<div class="post-metadata">

**Author:** ![jow](https://forum.openwrt.org/user_avatar/forum.openwrt.org/jow/32/13_2.png) [@jow](https://forum.openwrt.org/u/jow)\
**Post date:** [April 25, 2022, 10:29pm UTC](https://forum.openwrt.org/t/linux-bridge-forwarding-table/126169/2 "2022-04-25T22:29:13Z")

</div>

> [@Pablomagno](#):
>
> There is another better way to obtain this information?

You can parse the binary file `/sys/class/net/br-lan/brforward`. It is a packed series of [`struct __fdb_entry`](https://docs.huihoo.com/doxygen/linux/kernel/3.7/struct____fdb__entry.html) 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:

```bash
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:

```plaintext
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](https://openwrt.org/docs/techref/rpcd#plugin_executables) to output the above contents formatted as JSON via ubus.

---

<div class="post-metadata">

**Author:** ![Pablomagno](https://forum.openwrt.org/user_avatar/forum.openwrt.org/pablomagno/32/114753_2.png) [@Pablomagno](https://forum.openwrt.org/u/Pablomagno)\
**Post date:** [April 28, 2022, 3:51am UTC](https://forum.openwrt.org/t/linux-bridge-forwarding-table/126169/3 "2022-04-28T03:51:56Z")

</div>

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:

```nohighlight
 brctl showstp br-lan

```

 ![image](https://forum.openwrt.org/uploads/default/original/3X/3/1/31a879d1cc1d500de7f3a61d164f2485c55b00a4.png)

Any idea?

---

<div class="post-metadata">

**Author:** ![jow](https://forum.openwrt.org/user_avatar/forum.openwrt.org/jow/32/13_2.png) [@jow](https://forum.openwrt.org/u/jow)\
**Post date:** [April 28, 2022, 7:51am UTC](https://forum.openwrt.org/t/linux-bridge-forwarding-table/126169/4 "2022-04-28T07:51:21Z")

</div>

> [@Pablomagno](#):
>
> Any idea?

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

Complete example script:

```bash
#!/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`):

```plaintext
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:~# 

```

---

<div class="post-metadata">

**Author:** ![Pablomagno](https://forum.openwrt.org/user_avatar/forum.openwrt.org/pablomagno/32/114753_2.png) [@Pablomagno](https://forum.openwrt.org/u/Pablomagno)\
**Post date:** [April 28, 2022, 8:45pm UTC](https://forum.openwrt.org/t/linux-bridge-forwarding-table/126169/5 "2022-04-28T20:45:15Z")

</div>

> [@jow](#):
>
> `1`

Great, thank you very much.

---

<div class="post-metadata">

**Author:** ![system](https://forum.openwrt.org/uploads/default/original/3X/7/4/740520c71f00039bbf001045c682f0ecb25e905a.png) [@system](https://forum.openwrt.org/u/system)\
**Post date:** [May 8, 2022, 8:46pm UTC](https://forum.openwrt.org/t/linux-bridge-forwarding-table/126169/6 "2022-05-08T20:46:12Z")

</div>

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