The best place to add nftables rules for hit counters is in a netdev hook. This is hit before all other hooks like raw/prerouting/input/output/forwarding. You can use something like this for the rules, just modify the interfaces for your needs. Don’t forget to update the interfaces in both the sets and the hook declarations:
table netdev iface_stats {
set ingress {
typeof meta protocol . meta iifname
counter
elements = {
ip . "eth0",
ip6 . "eth0",
ip . "eth1",
ip6 . "eth1"
}
}
set egress {
typeof meta protocol . meta oifname
counter
elements = {
ip . "eth0",
ip6 . "eth0",
ip . "eth1",
ip6 . "eth1"
}
}
chain ingress {
type filter hook ingress devices = { "eth0", "eth1" } priority filter; policy accept;
meta protocol . meta iifname @ingress
}
chain egress {
type filter hook egress devices = { "eth0", "eth1" } priority filter; policy accept;
meta protocol . meta oifname @egress
}
}
You can view the stats using this:
nft list sets netdev
You can also use a python script like this to check stats locally:
#!/usr/bin/env python3
import json
from tabulate import tabulate
from vyos.utils.process import cmd
def get_iface_stats() -> str:
stats = {}
for direction in ("ingress", "egress"):
data = json.loads(
cmd(f"nft -j list set netdev iface_stats {direction}")
)
for item in data["nftables"]:
if "set" not in item:
continue
for entry in item["set"].get("elem", []):
elem = entry["elem"]
af, interface = elem["val"]["concat"]
counter = elem["counter"]
stats.setdefault(interface, {}).setdefault(af, {})[direction] = counter
rows = []
for interface, families in stats.items():
for af in ("ip", "ip6"):
if af not in families:
continue
ingress = families[af].get("ingress", {})
egress = families[af].get("egress", {})
rows.append([
interface,
"IPv4" if af == "ip" else "IPv6",
ingress.get("packets", 0),
ingress.get("bytes", 0),
egress.get("packets", 0),
egress.get("bytes", 0),
])
return tabulate(
rows,
headers=[
"Interface",
"Address Family",
"Pkts In",
"Bytes In",
"Pkts Out",
"Bytes Out",
],
tablefmt="simple",
numalign="right",
)
if __name__ == "__main__":
print(get_iface_stats())
Output should look something like this:
python3 test.py
Interface Address Family Pkts In Bytes In Pkts Out Bytes Out
----------- ---------------- --------- ---------- ---------- -----------
eth0 IPv4 45260 12054809 52423 17216053
eth0 IPv6 2 128 0 0
eth1 IPv4 58346 17385847 47285 12886587
eth1 IPv6 2619 556135 0 0