diff --git a/docs/layer3_ts.md b/docs/layer3_ts.md
new file mode 100644
index 0000000000000000000000000000000000000000..dafa8661bf408fac543aa5902002546549b8c82b
--- /dev/null
+++ b/docs/layer3_ts.md
@@ -0,0 +1,403 @@
+# Layer 3 Traffic Separation
+
+The EasyMesh R2 specification only specifies behavior for layer 2 traffic
+separation, which IOPSYS Multi-AP components support and will automatically
+setup necessary configuration for when it is enabled.
+
+However, using the same solution layer 3 traffic separation is supported but
+must be configured manually. This primarily refers to the creation of **bridge-vlan** sections,
+which dictates bridge and Ethernet port tagging rules. If a Traffic Separation
+TLV is received for which the necessary **bridge-vlan** sections are already
+present, map-agent will not setup any additional configuration or modify the
+existing rules. Map-agent will still be responsible for setting up tagging
+rules on the wireless interfaces as dictated by the EasyMesh specification and
+passed map-controller configuration.
+
+This leaves users with flexibility to setup layer 3 traffic separation
+or any other configuration as they require.
+
+## How to Configure
+
+To enable layer 3 traffic separation, it is necessary to manually setup the
+**network**, **firewall** and **dhcp** configuration as per any custom
+requirements.
+
+In this how to guide an example it will be shown how to setup a basic use-case
+where guest clients will receive an IPv4 address from a DHCP server running on a
+separate bridge, with all traffic egressing and ingressing over said bridge.
+
+### Network Configuration
+
+#### Guest Bridges
+
+First, the bridges must be created. The bridges can have any IP address,
+netmask etc. In this example we will create two additional guest bridges, one
+for VID 50 and one for VID 20.
+
+```
+config interface 'guest50'
+	option device 'br-guest50'
+	option is_lan '1'
+	option proto 'static'
+	option ipaddr '192.168.50.1'
+	option netmask '255.255.255.0'
+
+config device 'br_guest50'
+	option name 'br-guest50'
+	option type 'bridge'
+
+config interface 'guest20'
+	option device 'br-guest20'
+	option is_lan '1'
+	option proto 'static'
+	option ipaddr '192.168.20.1'
+	option netmask '255.255.255.0'
+
+config device 'br_guest20'
+	option name 'br-guest20'
+	option type 'bridge'
+```
+
+
+#### Veth Pair
+
+As incoming Wi-Fi client traffic will be ingressing over the wireless interfaces
+which reside in the **al_bridge**, we must create a veth pair so this traffic
+can ingress to the guest bridges.
+
+```
+config device 'guest_dev20'
+	option type 'veth'
+	option name 'guest_dev20'
+	option peer_name 'guest_peer20'
+
+config device 'guest_dev50'
+	option type 'veth'
+	option name 'guest_dev50'
+	option peer_name 'guest_peer50'
+```
+
+These veth pairs must now have their peers attached to the port list of the
+respective bridge and the source interface to the **al_bridge**.
+
+```
+config device 'br_lan'
+	option name 'br-lan'
+	option type 'bridge'
+	list ports 'eth1'
+	list ports 'eth2'
+	list ports 'eth3'
+	list ports 'eth4'
+	list ports 'guest_dev20'
+	list ports 'guest_dev50'
+
+config device 'br_guest50'
+	option name 'br-guest50'
+	option type 'bridge'
+	list ports 'guest_peer50'
+
+config device 'br_guest20'
+	option name 'br-guest20'
+	option type 'bridge'
+	list ports 'guest_peer20'
+```
+
+We will now find traffic ingressing over the **al_bridge** on the guest bridges
+as well.
+
+#### VLAN ID Tagging Rules
+
+Finally, we must setup the **bridge-vlan** sections providing netifd with
+information on the bridge filtering rules. These rules are in a layer 2 use-case
+automatically setup by map-agent.
+
+The **al_bridge** must not untag anything that is not the Primary VLAN ID
+(unless it is desired that it managed _some_ secondary VLAN IDs), and the guest
+bridges untag according to the VLAN IDs that they are supposed to manage.
+
+```
+# Ethernet ports and br-lan tag and untaged Primary VLAN ID
+config bridge-vlan 'vlan1'
+	option name 'vlan1'
+	option device 'br-lan'
+	option vlan '1'
+	option flags 'untagged pvid'
+	option local '1'
+	list ports 'eth1:*'
+	list ports 'eth2:*'
+	list ports 'eth3:*'
+	list ports 'eth4:*'
+
+# Ethernet ports (as many as desired) and br-lan allow VID 50 to be passed
+# guest_dev50 tags and untags VID 50
+config bridge-vlan 'vlan50'
+	option name 'vlan50'
+	option device 'br-lan'
+	option vlan '50'
+	option flags 'untagged'
+	option local '0'
+	list ports 'eth1:t'
+	list ports 'eth2:t'
+	list ports 'eth3:t'
+	list ports 'eth4:t'
+	list ports 'guest_dev50:*'
+
+# Ethernet ports (as many as desired) and br-lan allow VID 20 to be passed
+# guest_dev50 tags and untags VID 20
+config bridge-vlan 'vlan20'
+	option name 'vlan20'
+	option device 'br-lan'
+	option vlan '20'
+	option flags 'untagged'
+	option local '0'
+	list ports 'eth1:t'
+	list ports 'eth2:t'
+	list ports 'eth3:t'
+	list ports 'eth4:t'
+	list ports 'guest_dev20:*'
+```
+
+#### Bridge Filtering Configuration
+
+With the network configuration setup and loaded (recommended to be done via
+`/etc/init.d/network restart` as `reload` will not always apply the necessary
+tagging rules), **bridge vlan** configuration output should look as follows:
+
+```
+root@iopsys-021000000001:~# bridge vlan
+port              vlan-id  
+eth1              1 PVID Egress Untagged
+                  20
+                  50
+eth2              1 PVID Egress Untagged
+                  20
+                  50
+eth3              1 PVID Egress Untagged
+                  20
+                  50
+eth4              1 PVID Egress Untagged
+                  20
+                  50
+wl0               1 PVID Egress Untagged
+wl1               1 PVID Egress Untagged
+br-lan            1 PVID Egress Untagged
+guest_peer20      1 PVID Egress Untagged
+guest_dev20       20 PVID Egress Untagged
+guest_peer50      1 PVID Egress Untagged
+guest_dev50       50 PVID Egress Untagged
+br-guest20        1 PVID Egress Untagged
+br-guest50        1 PVID Egress Untagged
+wl1.1             1 PVID Egress Untagged
+wl0.1             1 PVID Egress Untagged
+```
+
+An example **brctl show** output would look as:
+
+```
+root@iopsys-021000000001:~# brctl show
+bridge name	bridge id		STP enabled	interfaces
+br-guest20		7fff.922f1251b15b	no		guest_peer20
+br-guest50		7fff.ec6c9a52b02b	no		guest_peer50
+br-lan		7fff.ec6c9a52b027	no		eth1
+							eth2
+							eth3
+							eth4
+							guest_dev20
+							guest_dev50
+							wl0
+							wl0.1
+							wl1
+							wl1.1
+```
+
+### Firewall Configuration
+
+In the firewall configuration, each network must be given input, output and
+forwarding rules by being attached to a firewall **zone**. In this example we
+create a zone for each network that accepts all traffic.
+
+```
+config zone 'guest20'
+	option name 'guest20'
+	list network 'guest20'
+	option input 'ACCEPT'
+	option output 'ACCEPT'
+	option forward 'ACCEPT'
+
+config zone 'guest50'
+	option name 'guest50'
+	list network 'guest50'
+	option input 'ACCEPT'
+	option output 'ACCEPT'
+	option forward 'ACCEPT'
+```
+Next we create the forwarding rules for these zones, forwarding traffic through
+the wan zone.
+
+```
+config forwarding
+	option src 'guest20'
+	option dest 'wan'
+
+config forwarding
+	option src 'guest50'
+	option dest 'wan'
+
+```
+
+With the configuration setup, we must now reload the firewall rules via i.e.
+`/etc/init.d/firewall reload`.
+
+### DHCP Configuration
+
+In the DHCP configuration, we create a DHCP server for each network, specifying
+the desired DHCP ranges.
+
+```
+config dhcp 'guest20'
+	option interface 'guest20'
+	option start '100'
+	option limit '150'
+	option leasetime '1h'
+	option dhcpv4 'server'
+	option dhcpv6 'server'
+	option ra 'server'
+	option ra_slaac '1'
+	list ra_flags 'managed-config'
+	list ra_flags 'other-config'
+
+config dhcp 'guest50'
+	option interface 'guest50'
+	option start '100'
+	option limit '150'
+	option leasetime '1h'
+	option dhcpv4 'server'
+	option dhcpv6 'server'
+	option ra 'server'
+	option ra_slaac '1'
+	list ra_flags 'managed-config'
+	list ra_flags 'other-config'
+```
+
+We can now restart dnsmasq via i.e. `/etc/init.d/dnsmasq restart`.
+
+### Map-Controller Configuration
+
+In map-controller we may now enable Traffic Separation and append two guest
+networks for VID 20 and VID 50.
+
+```
+config controller 'controller'
+	option enabled '1'
+	option registrar '5 2'
+	option debug '0'
+	option primary_vid '1'
+	option primary_pcp '0'
+	option enable_ts '1'
+```
+
+```
+config ap
+	option ssid 'iopsys-vid50'
+	option band '5'
+	option encryption 'sae-mixed'
+	option key '1234567890'
+	option vid '50'
+	option type 'fronthaul'
+
+config ap
+	option ssid 'iopsys-vid20'
+	option band '2'
+	option encryption 'sae-mixed'
+	option key '1234567890'
+	option vid '20'
+	option type 'fronthaul'
+```
+
+A SIGHUP can then be sent to map-controller as i.e.
+`kill -1 $(pidof mapcontroller)`.
+
+## Verification
+
+### Bridge VLAN Filtering
+
+The **bridge vlan** configuration should now look as such:
+
+```
+root@iopsys-021000000001:~# bridge vlan
+port              vlan-id  
+eth1              1 PVID Egress Untagged
+                  20
+                  50
+eth2              1 PVID Egress Untagged
+                  20
+                  50
+eth3              1 PVID Egress Untagged
+                  20
+                  50
+eth4              1 PVID Egress Untagged
+                  20
+                  50
+wl0               1 PVID Egress Untagged
+wl1               1 PVID Egress Untagged
+br-lan            1 PVID Egress Untagged
+guest_peer20      1 PVID Egress Untagged
+guest_dev20       20 PVID Egress Untagged
+guest_peer50      1 PVID Egress Untagged
+guest_dev50       50 PVID Egress Untagged
+br-guest20        1 PVID Egress Untagged
+br-guest50        1 PVID Egress Untagged
+wl1.1             1 PVID Egress Untagged
+wl0.1             1 PVID Egress Untagged
+wl1.2             1 Egress Untagged
+                  20 PVID Egress Untagged
+wl0.2             1 Egress Untagged
+                  50 PVID Egress Untagged
+```
+### Connecting a Wi-Fi Client
+
+Connecting a client to i.e. **wl1.2** we can see that it will receive a DHCP
+address from the 192.168.20.0/24 subnet:
+
+```
+root@iopsys-021000000001:~# ubus call wifi.ap.wl1.2 assoclist
+{
+	"assoclist": [
+		{
+			"wdev": "wl1.2",
+			"macaddr": "4a:93:6d:3c:48:21"
+		}
+	]
+}
+root@iopsys-021000000001:~# cat /tmp/dhcp.leases
+1670580192 4a:93:6d:3c:48:21 192.168.20.139 jakobs-S21 01:4a:93:6d:3c:48:21
+```
+
+### Tags on Bridge
+
+By using tcpdump, we can now observe that this clients traffic will now have its
+VLAN ID 20 tag intact over **br-lan**, which means it will not egress through from
+**br-lan** as now egress rules are set for VID 20 on **br-lan**.
+
+```
+root@iopsys-021000000001:~# tcpdump -nei br-lan icmp
+tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
+listening on br-lan, link-type EN10MB (Ethernet), capture size 262144 bytes
+10:08:08.357498 b2:f3:b5:c1:ca:54 > 4a:93:6d:3c:48:21, ethertype 802.1Q (0x8100), length 106: vlan 20, p 0, ethertype IPv4, 192.168.20.1 > 192.168.20.139: ICMP net 20.50.80.209 unreachable, length 68
+10:08:10.524492 b2:f3:b5:c1:ca:54 > 4a:93:6d:3c:48:21, ethertype 802.1Q (0x8100), length 106: vlan 20, p 0, ethertype IPv4, 192.168.20.1 > 192.168.20.139: ICMP net 52.114.74.223 unreachable, length 68
+10:08:10.993687 4a:93:6d:3c:48:21 > b2:f3:b5:c1:ca:54, ethertype 802.1Q (0x8100), length 102: vlan 20, p 0, ethertype IPv4, 192.168.20.139 > 123.123.123.123: ICMP echo request, id 2136, seq 1, length 64
+10:08:10.993886 b2:f3:b5:c1:ca:54 > 4a:93:6d:3c:48:21, ethertype 802.1Q (0x8100), length 102: vlan 20, p 0, ethertype IPv4, 123.123.123.123 > 192.168.20.139: ICMP echo reply, id 2136, seq 1, length 64
+```
+
+However, on **br-guest20** we can see the same traffic is untagged and can egress:
+```
+root@iopsys-021000000001:~# tcpdump -nei br-guest20 icmp
+tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
+listening on br-guest20, link-type EN10MB (Ethernet), capture size 262144 bytes
+10:09:29.127415 b2:f3:b5:c1:ca:54 > 4a:93:6d:3c:48:21, ethertype IPv4 (0x0800), length 590: 192.168.20.1 > 192.168.20.139: ICMP net 31.13.72.53 unreachable, length 556
+10:09:29.709500 b2:f3:b5:c1:ca:54 > 4a:93:6d:3c:48:21, ethertype IPv4 (0x0800), length 102: 192.168.20.1 > 192.168.20.139: ICMP net 52.114.74.223 unreachable, length 68
+10:09:32.640145 4a:93:6d:3c:48:21 > b2:f3:b5:c1:ca:54, ethertype IPv4 (0x0800), length 98: 192.168.20.139 > 123.123.123.123: ICMP echo request, id 2395, seq 1, length 64
+10:09:32.640227 b2:f3:b5:c1:ca:54 > 4a:93:6d:3c:48:21, ethertype IPv4 (0x0800), length 98: 123.123.123.123 > 192.168.20.139: ICMP echo reply, id 2395, seq 1, length 64
+```
+
+