TC ingress filters
tcfilter and luci-app-tcfilter manage persistent tc filter … ingress
rules through UCI, with a LuCI front-end and a live status view. Their purpose is
to drive hardware tc-flower offload (for example the Realtek DSA PIE engine)
on targets where no higher-level configuration layer for it exists.
Snapshot only. The kernel-side tc-flower offload and the tcfilter package
are in OpenWrt main and the snapshot feeds. They are not in any stable release
(23.05, 24.10) and are not planned for backport. luci-app-tcfilter reaches
the feed once openwrt/luci#9004 is
merged; until then see below.
Upstream history:
tcfilter— openwrt/packages#30447
Discussion and testing feedback: forum thread.
What it does
tcfilter does not model the flower match/action fields. The match and the
action are written verbatim as tc syntax in option spec; the package owns
the lifecycle:
- one
config ruleper filter —device,pref,spec,enabled, optional cosmeticlabel prefis mandatory — it is how a rule is removed again- rules are applied at boot, re-applied on
ifupand on netdevadd(hotplug), removed on stop - a procd reload trigger re-applies on config changes, so LuCI Save & Apply and
uci commit tcfilter && reload_configtake effect on their own - installed
(device, pref)pairs are tracked in/var/run/tcfilter.state, so a rule deleted from the config is still torn down on the next reload - the
clsactqdisc is added if missing but never removed (other users may share it); only the individual filters are deleted
luci-app-tcfilter adds Network → TC Filters: a grid to edit the rules
plus a status table below it, polled every 5 s, parsing
tc -s -j filter show … ingress — protocol, match, offload flag
(skip_sw / skip_hw), in_hw, action, packet count, label.
Installation
Snapshot images already contain the kernel-side offload. The packages come from
the snapshot feeds — installing the LuCI app pulls in tcfilter and
tc-full:
apk update apk add luci-app-tcfilter
Both packages are PKGARCH:=all. tcfilter depends on tc-full (the full
iproute2 tc; the BusyBox tc can do neither flower nor JSON output)
and, on offload targets, kmod-sched-flower.
While openwrt/luci#9004 is in
review, luci-app-tcfilter is not in the feed yet. Install tcfilter on its
own —
apk update apk add tcfilter
— which already provides the UCI config, the init script and the hotplug
integration; the LuCI GUI is optional. Pre-built luci-app-tcfilter packages
for testing, including for realtek/rtl930x, are linked from the
forum thread;
alternatively build #9004 yourself.
Configuration
/etc/config/tcfilter:
config tcfilter 'global' option enabled '1' config rule option label 'Drop-mDNS (IPv4)' option device 'lan1' option pref '49154' option spec 'protocol ip flower ip_proto udp dst_port 5353 skip_sw action drop' option enabled '1'
spec is everything that would follow
tc filter add dev <device> ingress pref <pref>
i.e. the optional protocol, the filter kind and its match, and the action. Use
skip_sw so a match the hardware cannot offload fails loudly instead of
silently installing in software.
The shipped config carries a few disabled examples (AVM FRITZ!Box powerline
discovery, mDNS) — set device, flip enabled to 1.
| Option | Required | Description |
|---|---|---|
device | yes | network device the filter is attached to |
pref | yes | tc preference number; how the rule is identified and removed |
spec | yes | the match + action, verbatim tc syntax |
enabled | no (default 1) | apply this rule |
label | no | cosmetic; shown in log messages and the LuCI view |
Commands
/etc/init.d/tcfilter start | stop | reload | show /etc/init.d/tcfilter reapply_dev lan1
Hardware offload on Realtek rtl930x
On the Realtek DSA target a flower … skip_sw ingress rule is programmed into
the switch PIE (Packet Inspection Engine) TCAM. The driver support and the fixes
that made this path usable are in OpenWrt main (pull requests in the box above)
and reached snapshots during the 2026-09 cycle. Verified on a Zyxel XGS1210-12
(RTL9302C):
- per-rule
tc -shardware packet counters work for every offloaded rule, not only the one that lands on PIE rule id 0 dst_port/src_portmatching offloads precisely — previously adst_port 5353rule silently degraded to “drop all UDP”- source / destination MAC matching offloads precisely — previously a zero-value MAC match (for example
dst_mac 00:00:00:00:00:00) could degrade to “match every frame” - generic EtherType matching, ARP / IPv4 / IPv6 fast paths,
drop/trap/ redirect actions, per-port binding via the source-port mask
Combining an L3 address match with an L4 port match in one offloaded rule is
rejected (-EOPNOTSUPP) on this hardware — the ingress PIE blocks do not carry
a template with both fields. A plain dst_port match works.
action vlan push / action vlan pop are rejected (-EOPNOTSUPP) for
offloaded rules — the rtl930x PIE VID-rewrite encoding is broken (issue
#25048) — so such a rule can
only be installed in software (without skip_sw).
Limitations
- ingress /
clsactonly - no dry-run validation — an invalid
specis reported vialogreadonly - the free-form
specis passed totcby word-split (no shell); anyone who can edit the config can install redirect / mirror rules