Can't find how to intercept or log traffic on bridge (wifi AP) with nft

Hi there,

Brief description of what I'd ultimately would like to achieve : my daughter's laptop is managed by her college and it's using some weird DNS outside of my control : I want to redirect her DNS requests to my pihole, so that I can ensure she's protected from everything that weird DNS when at home.

And actually, I blocked "webtoons" sites in pihole coz' she's spending here time on that instead of doing her homework.

So, I'm trying to use DNS hijacking on openwrt, on my tplink archer c6, running openwrt 22.03.

It is setup in "dumb AP mode" with some changes : I added a bridge and vlans as I'd like to have a "LAN" wifi for people, and an IoT wifi for IoT devices on another vlan/subnet with no internet access.

For now, I've tried to intercept my own DNS requests and to trace them or count them using nft. Unfortunately , I cant succeed in that. However, I do see packets coming in on wlan0, and goig out on eth0.1 ... hence I'm here asking for help.

The tcpdump is seeing the packets:

root@OpenWrt:~# tcpdump -i any 'host 192.168.1.16 and udp dst port 53'
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
09:51:25.088769 IP 192.168.1.16.49733 > dns.google.53: 36838+ A? google.fr. (27)
09:51:25.088799 IP 192.168.1.16.49733 > dns.google.53: 36838+ A? google.fr. (27)
09:51:25.100670 IP 192.168.1.16.54961 > dns.google.53: 7066+ AAAA? google.fr. (27)
09:51:25.100695 IP 192.168.1.16.54961 > dns.google.53: 7066+ AAAA? google.fr. (27)

root@OpenWrt:~# tcpdump  -i eth0.1 'host 192.168.1.16 and udp dst port 53'
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0.1, link-type EN10MB (Ethernet), capture size 262144 bytes
09:59:07.778067 IP 192.168.1.16.59813 > dns.google.53: 31314+ A? google.fr. (27)
09:59:07.789149 IP 192.168.1.16.53086 > dns.google.53: 35348+ AAAA? google.fr. (27)

I have installed the nft nridge kmod packet:

root@OpenWrt:~# opkg list-installed|grep bridge
kmod-nft-bridge - 5.10.161-1

There is something I must be missing, I've tried to create prerouting/forward/postrouting rules to no avail:

nft add table bridge filter
nft add chain bridge filter prerouting '{ type filter hook prerouting priority 0 ; }'
nft add rule bridge filter prerouting ip saddr 192.168.1.16 udp dport 53 counter nftrace set 1
nft add chain bridge filter postrouting '{ type filter hook postrouting priority 0 ; }'
nft add rule bridge filter postrouting ip saddr 192.168.1.16 udp dport 53 counter nftrace set 1
nft add chain bridge filter forward '{type filter hook forward priority 0; }'
nft add rule bridge  filter forward ip saddr 192.168.1.16 udp dport 53 counter nftrace set 1


nft add table ip filter
nft add chain ip filter prerouting '{ type filter hook prerouting priority 0 ; }'
nft add rule ip  filter prerouting ip saddr 192.168.1.16 udp dport 53 counter nftrace set 1
nft add chain ip filter postrouting '{ type filter hook postrouting priority 0 ; }'
nft add rule ip  filter postrouting ip saddr 192.168.1.16 udp dport 53 counter nftrace set 1
nft add chain ip filter forward '{type filter hook forward priority 0; }'
nft add rule ip  filter forward ip saddr 192.168.1.16 udp dport 53 counter nftrace set 1

With all these rules... I don't see anything when I run nft monitor trace and all rules remain at count 0 when listing rules with nft -a list ruleset. But tcpdump is happily showing packets :confused:

Any idea what I could be missing ?
Any help would be appreciated.

3 Likes

thanks, will try this

it seems I am/was missing that bridge devices operate at layer2 / ethernet, not on IP level, that's hopefully why my nft rules don't match anything. I will report here when I succeed (I hope :] )

OK, tried quite a few different itterations and did not get much working.

If I get it correctly, the wiki explains how to add a prerouting rule of type bridge that matches on udp port 53, and rewrites destination host at the ethernet level

For me, there is no WAN as everything is on LAN (wifi, and upstream router, all are on bridge), but I could adapt the wiki instructions if only I could get packet matching working.

I've tried adding several forward and prerouting rules to counter/trace packets : from what I can see, nothing seems to match as soon as udp port 53 is added in the rule. Things match when I set the ethernet mac address of my laptop, but when I add udp port to the rule, counters remain 0.

From what I've seen too, nft monitor trace actually does nothing even when packets match.

These are the rules I've added and the counters I get after a nslookup on my laptop (2 packets, because there's a A and an AAAA request) :

nft add chain bridge filter prerouting '{ type filter hook prerouting priority -300 ; }'
nft add rule bridge filter prerouting meta l4proto udp ether saddr 7c:b7:xx:xx:xx:xx th dport 53 counter nftrace set 1
nft add rule bridge filter prerouting meta l4proto udp ether saddr 7c:b7:xx:xx:xx:xx counter nftrace set 1
nft add rule bridge filter prerouting meta l4proto udp th dport 53 counter nftrace set 1


(... dns lookups...)
nft -a list ruleset
(...)
table bridge filter { # handle 16
        chain forward { # handle 23
                type filter hook forward priority filter; policy accept;
                udp dport 53 counter packets 0 bytes 0 # handle 24
                ether type ip udp dport 53 counter packets 0 bytes 0 # handle 25
                meta l4proto udp ether saddr 7c:b7:xx:xx:xx:xx counter packets 114 bytes 42978 # handle 30
        }

        chain prerouting { # handle 36
                type filter hook prerouting priority dstnat; policy accept;
                ether saddr 7c:b7:xx:xx:xx:xx udp dport 53 counter packets 0 bytes 0 meta nftrace set 1 # handle 37
                meta l4proto udp ether saddr 7c:b7:xx:xx:xx:xx counter packets 2 bytes 754 meta nftrace set 1 # handle 38
                udp dport 53 counter packets 0 bytes 0 meta nftrace set 1 # handle 39
        }
}

Especially in the prerouting chain which I deleted and rewrote many times, I can see exactly 2 packets matches (A and AAAA request), and still 0 packet matched UDP ?? (tcpdump says it's udp, and I'm looking up a host using 8.8.8.8 as dns server)

I am missing something ?

It calculates WAN using the default route, so WAN should match LAN on the dumb AP/switch.

The forward chain is supposed to follow the prerouting chain, so it should not be involved after rewriting the destination MAC as this is no longer transit traffic.

None of the rules you have posted above actually rewrite the destination MAC.

Also be sure to enable DNS hijacking to rewrite the destination IP:
https://openwrt.org/docs/guide-user/firewall/fw3_configurations/intercept_dns
And verify that the client is not using DoH or DoT.

I tested this setup using a couple OpenWrt VMs connected in cascade with bridging.

Hi,
well, I actually did not even attempt to rewrite anything with these rules, I was only trying to match and "log" the packets or at least make sure the rules I wrote incremented some counters :confused:

I'll try to implement the wiki without "as is" as you suggest...

1 Like

Hm...

tried as described in the wiki as is : both adding the script for the bridge firewall + the dns hijacking (forwarding to pihole) : not working for me unfortunately.

I tried on another router (asus ax53) : same behaviour.

There must be something wrong with my setup that's preventing ethernet+udp matching (vlans ?)

This is my /etc/config/network for the record :

config interface 'loopback'
        option device 'lo'
        option proto 'static'
        option ipaddr '127.0.0.1'
        option netmask '255.0.0.0'

config globals 'globals'
        option packet_steering '1'
        option ula_prefix 'xxxx::/48'

config device
        option name 'br-lan'
        option type 'bridge'
        list ports 'lan1'
        list ports 'lan2'
        list ports 'lan3'
        list ports 'wan'
        option igmp_snooping '1'

config interface 'lan'
        option proto 'static'
        option netmask '255.255.255.0'
        option ip6assign '60'
        option ipaddr '192.168.x.y'
        option gateway '192.168.X.Y'
        option device 'br-lan.99'
        list dns '192.168.1.2'
        list dns_search 'home'
        list dns_search 'lan'
        option delegate '0'

config interface 'wifi_domo'
        option proto 'static'
        option ipaddr '192.168.Z.1'
        option netmask '255.255.255.0'
        option device 'vlan10'

config bridge-vlan
        option device 'br-lan'
        option vlan '99'
        list ports 'lan1:u*'
        list ports 'lan2:u*'
        list ports 'lan3:u*'
        list ports 'wan:u*'

config bridge-vlan
        option device 'br-lan'
        option vlan '10'
        list ports 'lan1:t'
        list ports 'lan2:t'
        list ports 'lan3:t'
        list ports 'wan:t'

config device
        option type 'bridge'
        option name 'vlan10'
        list ports 'br-lan.10'

on the asus router (where I disabled my "prod" home wifis, only left a test one) brctl show me :

root@OpenWrt:~# brctl show
bridge name     bridge id               STP enabled     interfaces
br-lan          7fff.c87xxx       no              lan2
                                                        wan
                                                        lan3
                                                        wlan1
                                                        wlan1-1
                                                        lan1
vlan10          7fff.c87xxx       no              wlan0
                                                        br-lan.10

on this router, it's wlan1 that's receiving the packets, so it's in br-lan.
don't know what else to try.. maybe openwrt 2023.5 when it's out ?

Were you using 22.03 on your VMs ?

I recommend testing DNS filtering with dnsmasq running locally before forwarding it elsewhere:
https://openwrt.org/docs/guide-user/base-system/dhcp_configuration#dns_filtering

When you verify that it works, configure forwarding using this method:
https://openwrt.org/docs/guide-user/base-system/dhcp_configuration#dns_forwarding

I'm not sure about VLANs since I don't have much experience with them.
If possible, try dividing a big problem into smaller ones.
Testing each part separately can be more efficient.

Yes, I even reproduced this setup once again from scratch.

Hi again (and thank you already for the time you spent answering),

So I started trying to reproduce this on VMs.

For now, I did a "simple" setup, following the wiki instructions for virtualbox (wiki rocks :] )

I have 2 VMs, one of them beeing a simple light client (static ip, as br-lan has static 192.168.4.1 on openwrt).

I've added a Vbox private/internal network interface on both VMs to simulate LAN on which client is hooked : client successfully accessed the net without much changes.

I then setup the bridge nftables rules I wrote above on the openwrt VM : the rules... all match. unlike what's hapening on my wifi router devices. damn.

nft add table bridge filter
nft add chain bridge filter prerouting '{ type filter hook prerouting priority -300 ; }'
nft add rule bridge filter prerouting meta l4proto udp th dport 53 counter nftrace set 1
nft add rule bridge filter prerouting meta l4proto udp th dport 53 ip saddr 192.168.4.2 counter nftrace set 1
nft add rule bridge filter prerouting meta l4proto udp ether saddr 08:00:27:a4:33:1a th dport 53 counter nftrace set 1

# ran a couple of nslookup on VM2

root@OpenWrt:~# nft -a list ruleset bridge
table bridge filter { # handle 2
        chain prerouting { # handle 1
                type filter hook prerouting priority dstnat; policy accept;
                udp dport 53 counter packets 6 bytes 330 meta nftrace set 1 # handle 2
                udp dport 53 ip saddr 192.168.4.2 counter packets 4 bytes 220 meta nftrace set 1 # handle 3
                ether saddr 08:00:27:a4:33:1a udp dport 53 counter packets 2 bytes 110 meta nftrace set 1 # handle 4
        }
}

As you can see al rules do match and counters increment, even those with udp port 53 + src IP or src MAC.

I'll try tomorrow to reproduce the setup I have on wifi devices with vlans on the bridge... more later.

1 Like

As far as I remember, filtering transit traffic over a built-in switch requires the source and destination to be in different VLANs, otherwise not even bridge firewall can help you.
Also avoid combining tagged and untagged traffic in one VLAN, as there may also be caveats related to hardware implementation specifics.

1 Like

Hi,

Mmm... I think it's possible to filter even on bridges and even without vlans (besides, bridge would not forward from one vlan to another vlan) - I think it would be possible with prerouting and forwarding in nftables :
https://wiki.nftables.org/wiki-nftables/index.php/Bridge_filtering

back to my issue : I added (local) vlans on the VMs and... this did not prevent packets matching in the bridge prerouting hook, thus i did not reproduce the issue I see on my wifi devices.

That's really confusing, maybe this is hardware related somehow as I reproduced the same config on the VM that I have on the wifi device... except the VM is not a wifi device with a hardware switch included.

Anyway, I think I found a way to work around this : I setup yet another wifi SSID, another openwrt interface for this wifi, and setup this wifi to routed mode with a dedicated dhcp in a new subnet : this way, openwrt masquerades the traffic, and setting up DNS hijacking using port forwarding seems to be working perfectly.

Before I try to go further and migrate my other wifi SSIDs to routed mode : would you know if it's possible to use 802.11r fast transition with both wifi router devices using routed mode ?

I'm reluctant to use one device as "main" routing wifi device and the other as just dumb AP, I really want to be able to poweroff one device and see things go on working... I'm wondering if both wifi devices can advertise the same subnets with the same gateway IP (dhcp would be a problem though, unless I split the subnet in 2 separate dhcp pools) ?

it would be really great if there could be some kind of sync between openwrt hosts like it's done in pfsense !

Regards