Written by Andrew Thompson.
It is sometimes useful to divide one physical network (such as an Ethernet segment)
into two separate network segments without having to create IP subnets and use a router
to connect the segments together. A device that connects two networks together in this
fashion is called a “bridge”. A FreeBSD system with two network interface
cards can act as a bridge.
The bridge works by learning the MAC layer addresses (Ethernet addresses) of the
devices on each of its network interfaces. It forwards traffic between two networks only
when its source and destination are on different networks.
In many respects, a bridge is like an Ethernet switch with very few ports.
There are many common situations in which a bridge is used today.
The basic operation of a bridge is to join two or more network segments together.
There are many reasons to use a host based bridge over plain networking equipment such as
cabling constraints, firewalling or connecting pseudo networks such as a Virtual Machine
interface. A bridge can also connect a wireless interface running in hostap mode to a
wired network and act as an access point.
A common situation is where firewall functionality is needed without routing or
network address translation (NAT).
An example is a small company that is connected via DSL or ISDN to their ISP. They
have a 13 globally-accessible IP addresses from their ISP and have 10 PCs on their
network. In this situation, using a router-based firewall is difficult because of
subnetting issues.
A bridge-based firewall can be configured and dropped into the path just downstream of
their DSL/ISDN router without any IP numbering issues.
A bridge can join two network segments and be used to inspect all Ethernet frames that
pass between them. This can either be from using bpf(4)/tcpdump(1) on the
bridge interface or by sending a copy of all frames out an additional interface (span
port).
Two Ethernet networks can be joined across an IP link by bridging the networks to an
EtherIP tunnel or a tap(4) based solution
such as OpenVPN.
A network can be connected together with multiple links and use the Spanning Tree
Protocol to block redundant paths. For an Ethernet network to function properly only one
active path can exist between two devices, Spanning Tree will detect loops and put the
redundant links into a blocked state. Should one of the active links fail then the
protocol will calculate a different tree and reenable one of the blocked paths to restore
connectivity to all points in the network.
This section covers if_bridge(4) bridge
implementation, a netgraph bridging driver is also available, for more information see ng_bridge(4) manual
page.
The bridge driver is a kernel module and will be automatically loaded by ifconfig(8) when
creating a bridge interface. It is possible to compile the bridge in to the kernel by
adding device if_bridge to your kernel configuration file.
Packet filtering can be used with any firewall package that hooks in via the pfil(9) framework. The
firewall can be loaded as a module or compiled into the kernel.
The bridge can be used as a traffic shaper with altq(4) or dummynet(4).
The bridge is created using interface cloning. To create a bridge use ifconfig(8), if the
bridge driver is not present in the kernel then it will be loaded automatically.
# ifconfig bridge create
bridge0
# ifconfig bridge0
bridge0: flags=8802<BROADCAST,SIMPLEX,MULTICAST> metric 0 mtu 1500
ether 96:3d:4b:f1:79:7a
id 00:00:00:00:00:00 priority 32768 hellotime 2 fwddelay 15
maxage 20 holdcnt 6 proto rstp maxaddr 100 timeout 1200
root id 00:00:00:00:00:00 priority 0 ifcost 0 port 0
A bridge interface is created and is automatically assigned a randomly generated
Ethernet address. The maxaddr and timeout parameters control how many MAC addresses the bridge will
keep in its forwarding table and how many seconds before each entry is removed after it
is last seen. The other parameters control how Spanning Tree operates.
Add the member network interfaces to the bridge. For the bridge to forward packets all
member interfaces and the bridge need to be up:
# ifconfig bridge0 addm fxp0 addm fxp1 up
# ifconfig fxp0 up
# ifconfig fxp1 up
The bridge is now forwarding Ethernet frames between fxp0
and fxp1. The equivalent configuration in /etc/rc.conf so the bridge is created at startup is:
cloned_interfaces="bridge0"
ifconfig_bridge0="addm fxp0 addm fxp1 up"
ifconfig_fxp0="up"
ifconfig_fxp1="up"
If the bridge host needs an IP address then the correct place to set this is on the
bridge interface itself rather than one of the member interfaces. This can be set
statically or via DHCP:
# ifconfig bridge0 inet 192.168.0.1/24
It is also possible to assign an IPv6 address to a bridge interface.
When packet filtering is enabled, bridged packets will pass through the filter inbound
on the originating interface, on the bridge interface and outbound on the appropriate
interfaces. Either stage can be disabled. When direction of the packet flow is important
it is best to firewall on the member interfaces rather than the bridge itself.
The bridge has several configurable settings for passing non-IP and ARP packets, and
layer2 firewalling with IPFW. See if_bridge(4) for more
information.
The bridge driver implements the Rapid Spanning Tree Protocol (RSTP or 802.1w) with
backwards compatibility with the legacy Spanning Tree Protocol (STP). Spanning Tree is
used to detect and remove loops in a network topology. RSTP provides faster Spanning Tree
convergence than legacy STP, the protocol will exchange information with neighbouring
switches to quickly transition to forwarding without creating loops.
The following table shows the supported operating modes:
Spanning Tree can be enabled on member interfaces using the stp command. For a bridge with fxp0 and
fxp1 as the current interfaces, enable STP with the
following:
# ifconfig bridge0 stp fxp0 stp fxp1
bridge0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500
ether d6:cf:d5:a0:94:6d
id 00:01:02:4b:d4:50 priority 32768 hellotime 2 fwddelay 15
maxage 20 holdcnt 6 proto rstp maxaddr 100 timeout 1200
root id 00:01:02:4b:d4:50 priority 32768 ifcost 0 port 0
member: fxp0 flags=1c7<LEARNING,DISCOVER,STP,AUTOEDGE,PTP,AUTOPTP>
port 3 priority 128 path cost 200000 proto rstp
role designated state forwarding
member: fxp1 flags=1c7<LEARNING,DISCOVER,STP,AUTOEDGE,PTP,AUTOPTP>
port 4 priority 128 path cost 200000 proto rstp
role designated state forwarding
This bridge has a spanning tree ID of 00:01:02:4b:d4:50 and a
priority of 32768. As the root id is
the same it indicates that this is the root bridge for the tree.
Another bridge on the network also has spanning tree enabled:
bridge0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500
ether 96:3d:4b:f1:79:7a
id 00:13:d4:9a:06:7a priority 32768 hellotime 2 fwddelay 15
maxage 20 holdcnt 6 proto rstp maxaddr 100 timeout 1200
root id 00:01:02:4b:d4:50 priority 32768 ifcost 400000 port 4
member: fxp0 flags=1c7<LEARNING,DISCOVER,STP,AUTOEDGE,PTP,AUTOPTP>
port 4 priority 128 path cost 200000 proto rstp
role root state forwarding
member: fxp1 flags=1c7<LEARNING,DISCOVER,STP,AUTOEDGE,PTP,AUTOPTP>
port 5 priority 128 path cost 200000 proto rstp
role designated state forwarding
The line root id 00:01:02:4b:d4:50 priority 32768 ifcost 400000
port 4 shows that the root bridge is 00:01:02:4b:d4:50 as
above and has a path cost of 400000 from this bridge, the path
to the root bridge is via port 4 which is fxp0.
The bridge supports monitor mode, where the packets are discarded after bpf(4) processing, and
are not processed or forwarded further. This can be used to multiplex the input of two or
more interfaces into a single bpf(4) stream. This is
useful for reconstructing the traffic for network taps that transmit the RX/TX signals
out through two separate interfaces.
To read the input from four network interfaces as one stream:
# ifconfig bridge0 addm fxp0 addm fxp1 addm fxp2 addm fxp3 monitor up
# tcpdump -i bridge0
A copy of every Ethernet frame received by the bridge will be transmitted out a
designated span port. The number of span ports configured on a bridge is unlimited, if an
interface is designated as a span port then it may not also be used as a regular bridge
port. This is most useful for snooping a bridged network passively on another host
connected to one of the span ports of the bridge.
To send a copy of all frames out the interface named fxp4:
# ifconfig bridge0 span fxp4
A private interface does not forward any traffic to any other port that is also a
private interface. The traffic is blocked unconditionally so no Ethernet frames will be
forwarded, including ARP. If traffic needs to be selectively blocked then a firewall
should be used instead.
If a bridge member interface is marked as sticky then dynamically learned address
entries are treated at static once entered into the forwarding cache. Sticky entries are
never aged out of the cache or replaced, even if the address is seen on a different
interface. This gives the benefit of static address entries without the need to
pre-populate the forwarding table, clients learnt on a particular segment of the bridge
can not roam to another segment.
Another example of using sticky addresses would be to combine the bridge with VLANs to
create a router where customer networks are isolated without wasting IP address space.
Consider that CustomerA is on vlan100
and CustomerB is on vlan101. The bridge
has the address 192.168.0.1 and is also an internet router.
# ifconfig bridge0 addm vlan100 sticky vlan100 addm vlan101 sticky vlan101
# ifconfig bridge0 inet 192.168.0.1/24
Both clients see 192.168.0.1 as their default gateway and
since the bridge cache is sticky they can not spoof the MAC address of the other customer
to intercept their traffic.
Any communication between the VLANs can be blocked using private interfaces (or a
firewall):
# ifconfig bridge0 private vlan100 private vlan101
The customers are completely isolated from each other, the full /24 address range can be allocated without subnetting.
The number of unique source MAC addresses behind an interface can be limited. Once the
limit is reached packets with unknown source addresses are dropped until an existing host
cache entry expires or is removed.
The following example sets the maximum number of Ethernet devices for CustomerA on vlan100 to 10.
# ifconfig bridge0 ifmaxaddr vlan100 10
The bridge interface and STP parameters can be monitored via the SNMP daemon which is
included in the FreeBSD base system. The exported bridge MIBs conform to the IETF
standards so any SNMP client or monitoring package can be used to retrieve the data.
On the bridge machine uncomment the begemotSnmpdModulePath."bridge" = "/usr/lib/snmp_bridge.so" line
from /etc/snmp.config and start the bsnmpd daemon. Other configuration such as community names and
access lists may need to be modified. See bsnmpd(1) and snmp_bridge(3) for
more information.
The following examples use the Net-SNMP software (net-mgmt/net-snmp) to query a bridge, the net-mgmt/bsnmptools port can also be used. From the SNMP client
host add to $HOME/.snmp/snmp.conf the following lines to import
the bridge MIB definitions in to Net-SNMP:
mibdirs +/usr/share/snmp/mibs
mibs +BRIDGE-MIB:RSTP-MIB:BEGEMOT-MIB:BEGEMOT-BRIDGE-MIB
To monitor a single bridge via the IETF BRIDGE-MIB (RFC4188) do
% snmpwalk -v 2c -c public bridge1.example.com mib-2.dot1dBridge
BRIDGE-MIB::dot1dBaseBridgeAddress.0 = STRING: 66:fb:9b:6e:5c:44
BRIDGE-MIB::dot1dBaseNumPorts.0 = INTEGER: 1 ports
BRIDGE-MIB::dot1dStpTimeSinceTopologyChange.0 = Timeticks: (189959) 0:31:39.59 centi-seconds
BRIDGE-MIB::dot1dStpTopChanges.0 = Counter32: 2
BRIDGE-MIB::dot1dStpDesignatedRoot.0 = Hex-STRING: 80 00 00 01 02 4B D4 50
...
BRIDGE-MIB::dot1dStpPortState.3 = INTEGER: forwarding(5)
BRIDGE-MIB::dot1dStpPortEnable.3 = INTEGER: enabled(1)
BRIDGE-MIB::dot1dStpPortPathCost.3 = INTEGER: 200000
BRIDGE-MIB::dot1dStpPortDesignatedRoot.3 = Hex-STRING: 80 00 00 01 02 4B D4 50
BRIDGE-MIB::dot1dStpPortDesignatedCost.3 = INTEGER: 0
BRIDGE-MIB::dot1dStpPortDesignatedBridge.3 = Hex-STRING: 80 00 00 01 02 4B D4 50
BRIDGE-MIB::dot1dStpPortDesignatedPort.3 = Hex-STRING: 03 80
BRIDGE-MIB::dot1dStpPortForwardTransitions.3 = Counter32: 1
RSTP-MIB::dot1dStpVersion.0 = INTEGER: rstp(2)
The dot1dStpTopChanges.0 value is two which means that the
STP bridge topology has changed twice, a topology change means that one or more links in
the network have changed or failed and a new tree has been calculated. The dot1dStpTimeSinceTopologyChange.0 value will show when this
happened.
To monitor multiple bridge interfaces one may use the private BEGEMOT-BRIDGE-MIB:
% snmpwalk -v 2c -c public bridge1.example.com
enterprises.fokus.begemot.begemotBridge
BEGEMOT-BRIDGE-MIB::begemotBridgeBaseName."bridge0" = STRING: bridge0
BEGEMOT-BRIDGE-MIB::begemotBridgeBaseName."bridge2" = STRING: bridge2
BEGEMOT-BRIDGE-MIB::begemotBridgeBaseAddress."bridge0" = STRING: e:ce:3b:5a:9e:13
BEGEMOT-BRIDGE-MIB::begemotBridgeBaseAddress."bridge2" = STRING: 12:5e:4d:74:d:fc
BEGEMOT-BRIDGE-MIB::begemotBridgeBaseNumPorts."bridge0" = INTEGER: 1
BEGEMOT-BRIDGE-MIB::begemotBridgeBaseNumPorts."bridge2" = INTEGER: 1
...
BEGEMOT-BRIDGE-MIB::begemotBridgeStpTimeSinceTopologyChange."bridge0" = Timeticks: (116927) 0:19:29.27 centi-seconds
BEGEMOT-BRIDGE-MIB::begemotBridgeStpTimeSinceTopologyChange."bridge2" = Timeticks: (82773) 0:13:47.73 centi-seconds
BEGEMOT-BRIDGE-MIB::begemotBridgeStpTopChanges."bridge0" = Counter32: 1
BEGEMOT-BRIDGE-MIB::begemotBridgeStpTopChanges."bridge2" = Counter32: 1
BEGEMOT-BRIDGE-MIB::begemotBridgeStpDesignatedRoot."bridge0" = Hex-STRING: 80 00 00 40 95 30 5E 31
BEGEMOT-BRIDGE-MIB::begemotBridgeStpDesignatedRoot."bridge2" = Hex-STRING: 80 00 00 50 8B B8 C6 A9
To change the bridge interface being monitored via the mib-2.dot1dBridge subtree do:
% snmpset -v 2c -c private bridge1.example.com
BEGEMOT-BRIDGE-MIB::begemotBridgeDefaultBridgeIf.0 s bridge2