Per MAC counters with nft/fw4

Hi folks,

I'd like to count Rx and Tx traffic to/from the WAN interface per MAC address on my network. Yes, I have seen nlbwmon, but that appears not to be sufficiently fine grained for me (reporting stats per month).

My thinking is that I can create nft rules with individual counters based on the entries in my /etc/ethers file, and add them to the accept_from_lan and accept_to_lan rules. Not 100% sure that this will not include traffic to the router itself, but that is most likely not to be significant for anyone other than myself! It also looks like it might include traffic to other interfaces (vpn, guest, iot), which is less desirable, but also not a trainsmash if there is no better way.

Is there a better place to put the rules? Make an entirely separate ruleset/chain, perhaps? I'm still trying to wrap my head around nftables, so am not sure how to make sure my rules are traversed, without negatively impacting the security of my router.

Once I have an example of a rule and place to put it, I am quite happy to mechanically/programmatically convert my /etc/ethers file to emit suitable rules. And then checking stats is as fine grained as I want to make it, each time I list the ruleset and reset the counters.

Having been poking around trying to figure this out, I discovered that fw4 can use include statements (https://openwrt.org/docs/guide-user/firewall/firewall_configuration#includes_2203_and_later_with_fw4), so I figured my best place for this would be in the accept_to_wan chain.

config include
        option  type            'nftables'
        option  path            '/etc/per_eth_counters.nft'
        option  position        'chain-pre'
        option  chain           'accept_to_wan'

with /etc/per_eth_counters.nft looking like:

ether saddr 20:0b:cf:xx:xx:xx counter comment "!RD: count packets from xxx"

Unfortunately, when the chain is accept_to_wan, it seems like the include never happens. fw4 print doesn't show the include, and there is no other sign of it in the output after running /etc/init.d/firewall reload.

It DOES work when the chain is input_lan, but that doesn't seem like the right chain to use.

Any ideas what I am doing wrong?

In my experience, to get anything to work by MAC in nftables bi-directionally, you need a table of the bridge family.

Here is a working example.

opkg update
opkg install kmod-nft-bridge
nft add table bridge traffic-monitor
nft add chain bridge traffic-monitor prerouting '{type filter hook prerouting priority -300; }'
nft add chain bridge traffic-monitor postrouting '{type filter hook postrouting priority 300; }'
nft add chain bridge traffic-monitor download
nft add chain bridge traffic-monitor upload
nft add rule bridge traffic-monitor prerouting jump upload
nft add rule bridge traffic-monitor postrouting jump download
nft add rule bridge traffic-monitor download ether daddr 00:25:d3:7d:38:c0 counter
nft add rule bridge traffic-monitor upload ether saddr 00:25:d3:7d:38:c0 counter

To check the counters:

root@OpenWrt:~# nft list chain bridge traffic-monitor download; nft list chain bridge traffic-monitor upload
table bridge traffic-monitor {
        chain download {
                ether daddr 00:25:d3:7d:38:c0 counter packets 52308 bytes 48295162
        }
}
table bridge traffic-monitor {
        chain upload {
                ether saddr 00:25:d3:7d:38:c0 counter packets 28952 bytes 10126360
        }
}

The downside here is that all traffic from/to the given MAC will be collected (not just from/to wan), but you could use this as a starting point and look for improvements.

Thanks for the response. I'm actually not interested in bidirectional statistics accruing to a single counter. I'd actually prefer Rx and Tx stats to be separate.

I ran a quick test to see which chains were allowed to have "include" files loaded, and which were not. This is my list of chains that are NOT allowed to have an include file:

prerouting
handle_reject
syn_flood
accept_from_lan
accept_to_lan
reject_to_lan
raw_prerouting

This is based on a dumb AP running Openwrt, I was not in a hurry to reload the firewall on my router repeatedly! :smiley:

So, it seems that trying to load my own rules into the accept_from_* chains is not going to work.

I was able to get the rules loaded into "output_lan" and "input_lan" chains, but then realised that the counters are only counting the initial packets, and do not count any subsequent packets, thanks to connection tracking and fast path. So, this entire technique is doomed to failure, it seems.

I did find https://unix.stackexchange.com/questions/755114/nftables-counter-for-nated-traffic, which suggests:

Creating a separate rule with a counter in a chain of type filter in the prerouting hook, but with an earlier priority than -200 (NF_IP_PRI_CONNTRACK) will count all packets of the conntrack.

Which should get me the rest of the packets, but not separated between RX and TX, it seems.

Perhaps I just need to schedule a regular nlbw dump of collected statistics, rather, and process those instead.

Take a look at https://github.com/mstojek/nlbw2collectd

This will allow you to export data from nlbwmon to collectd. It can be easy adapted to export per MAC instead of per IP.

Thank you. This seems like a good solution.

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