Skip to content

Tc filter selective removal - #234

Merged
stanek-michal merged 14 commits into
mainfrom
tc-filter-selective-removal
Jul 27, 2026
Merged

Tc filter selective removal#234
stanek-michal merged 14 commits into
mainfrom
tc-filter-selective-removal

Conversation

@stanek-michal

@stanek-michal stanek-michal commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Add functions to check if a TC filter exists, and to selectively delete one based on name, without touching other TC filters.

Remove NLM_F_EXCL so adding the clsact qdisc succeeds even when it
already exists, e.g. from a previous endpoint run or a third-party tool.
Add ELASTIC_TC_FILTER_MARKER ("el-endpo") and two new functions:
- netlink_filter_exists: checks if an interface has our BPF tc filter
- netlink_filter_del: removes only our BPF tc filter by marker match

Both scan RTM_GETTFILTER dumps on ingress and egress parents and match
by TCA_BPF_NAME substring. TCA_KIND is intentionally omitted from the
dump request — some Linux 6.x kernels silently drop dumps that carry
TCA_KIND, causing recvmsg to block forever.
@stanek-michal
stanek-michal requested a review from a team as a code owner July 14, 2026 22:54
stanek-michal and others added 8 commits July 15, 2026 16:14
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
CI uses clang-format 14.0.6 via pip. The previous formatting commit
used v21 which formats macro binary-op spacing differently.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- netlink_filter_del_on_parent: stop on first match bug - open del_rth
  once before the loop and continue scanning after each delete instead
  of goto out, so all matching filters are removed
- Both functions: check NLM_F_DUMP_INTR and NLMSG_DONE error payload to
  detect interrupted or failed netlink dumps
- Both functions: reject empty marker_name to prevent strstr("") matching
  every BPF filter and causing mass deletion
- netlink_filter_exists_on_parent: accept nlmsg_pid == 0 to match del's
  existing behaviour (some kernels send pid=0 in dump responses)
- netlink_filter_exists_on_parent: remove dead found variable
NETLINK_EXT_ACK was added in kernel 4.12; failing the socket open on
ENOPROTOOPT breaks qdisc/filter operations on 4.5-4.11 kernels. Mirror
iproute2 libnetlink.c which deliberately ignores the return value.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Endpoint attaches its tc BPF filters only to chain 0. Parse TCA_CHAIN from filter dumps and ignore entries from other chains so a nonzero-chain entry cannot drive a chain-0 deletion.
TC filter dumps use live cursors rather than snapshots. Delete only one matching filter per dump, then close the listing socket and start a fresh dump so stale Endpoint filters cannot be skipped.
Use one canonical el-endpo_ prefix when creating and identifying Endpoint tc filters. Prefix matching avoids deleting unrelated BPF filters that merely contain the shorter marker elsewhere in their names.
Restore exclusive qdisc creation so repeated setup cannot modify existing qdisc state. Preserve the kernel errno and accept EEXIST as the idempotent success case without logging it as an error.
@stanek-michal

Copy link
Copy Markdown
Contributor Author

Added more robustness fixes in separate commits. Compared with official kernel code for all the APIs as well. Compatibility remains wide as before, 4.13+

stanek-michal and others added 3 commits July 17, 2026 19:56
When RTM_NEWQDISC returns EEXIST, TC_H_CLSACT == TC_H_INGRESS (0xFFFFFFF1),
so the occupied handle may belong to an ingress qdisc instead of clsact.
Silent success in that case would leave us with no clsact attached and BPF
filters would not be installed.

Add netlink_qdisc_verify_clsact() which dumps qdiscs on the interface and
checks TCA_KIND of the entry at TC_H_MAKE(TC_H_CLSACT, 0).  Returns 0 only
when the kind is "clsact"; returns -EINVAL otherwise.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@stanek-michal

Copy link
Copy Markdown
Contributor Author

Fixed remaining issues - DUMP_INTR should now be handled properly everywhere, also if a qdisc already exists and is not clsact type (the one we need) - we bail. If one exists and is clsact we reuse it. If no qdisc existed we create one but we never clean it up - safer that way and there shouldn't be any issues holding one, no overhead (cause no filter).

The kernel may set NLM_F_DUMP_INTR on any message in a dump, not
necessarily on the final NLMSG_DONE. Track it with a persistent flag
checked at NLMSG_DONE, matching netlink_qdisc_verify_clsact, so an
interrupted dump cannot be reported as a complete successful scan.
Reset the flag when netlink_filter_del_on_parent restarts its dump.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@nicholasberlin nicholasberlin left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bots are happy, and LGTH(uman)

@stanek-michal
stanek-michal merged commit 437e75f into main Jul 27, 2026
26 checks passed
@stanek-michal
stanek-michal deleted the tc-filter-selective-removal branch July 27, 2026 23:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants