Skip to content

sqm: Replace iptables marking with nftables - #190

Merged
tohojo merged 1 commit into
tohojo:mainfrom
dhrm1k:support-simple-qos-nft
Aug 25, 2026
Merged

tohojo merged 1 commit into
tohojo:mainfrom
dhrm1k:support-simple-qos-nft

Conversation

@dhrm1k

@dhrm1k dhrm1k commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Replace the iptables-based marking setup with native nftables rules.

This keeps the existing tc fw filter model, but uses nftables to install packet marks.

@tohojo

tohojo commented Jul 3, 2026

Copy link
Copy Markdown
Owner

Thank you for the patch. Did you test these changes, and if so, how? On OpenWrt, I presume? :)

@dhrm1k

dhrm1k commented Jul 3, 2026

Copy link
Copy Markdown
Contributor Author

Yes, on openwrt.

I tested simple.qos with start-sqm/stop-sqm on a dummy if. nft rules, egress filters and IFB redirect installed on start, and cleaned up on stop.

@yhaenggi

yhaenggi commented Jul 3, 2026

Copy link
Copy Markdown

this would break things for everyone who uses iptables.

Comment thread src/simple.qos Outdated
if [ "$ZERO_DSCP_INGRESS" = "1" ]; then
sqm_debug "Squashing differentiated services code points (DSCP) from ingress."
ipt -t mangle -I PREROUTING -i $IFACE -m dscp ! --dscp 0 -j DSCP --set-dscp-class be
nft_rule " iifname \"${IFACE}\" ip dscp != cs0 ip dscp set cs0"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

use iif ipo iifname, tc interface is totally fixed, no need to strlen() + strcmp() for each packet.
probably pre-pend meta nfproto ipv[4|6] , not visible in read-back but entered in kernel to skip unnecessary "heavy lifting" before ipX dscp for "other" protocol....

Comment thread src/simple.qos Outdated
nft_rule " }"
nft_rule " chain output {"
nft_rule " type filter hook output priority mangle; policy accept;"
nft_rule " udp dport { 123, 53 } ip dscp set af42"

@brada4 brada4 Jul 3, 2026 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

skip payload expression by using conntrack meta 16-bits
meta nfproto ipv4 meta l4proto udp ct original proto-dst { 123 , 53 } ip dscp ...

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks. I’ll update the fixed interface matches to use iif/oif, keep iifname only for the wildcard case, and add the explicit nfproto guards around the DSCP rules.

I’ll also the conntrack form for the udp output rule and retest it.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Proposed

family 1 __set%d t 0
        element 00007b00  : 0 [end]     element 00003500  : 0 [end]
inet t c
  [ meta load nfproto => reg 1 ]
  [ cmp eq reg 1 0x00000002 ]
  [ meta load l4proto => reg 1 ]
  [ cmp eq reg 1 0x00000011 ]
  [ ct load proto_dst => reg 1 , dir original ]
  [ lookup reg 1 set __set%d ]
  [ payload load 1b @ network header + 1 => reg 1 ]
  [ bitwise reg 1 = ( reg 1 & 0x000000fc ) ^ 0x00000000 ]
  [ cmp eq reg 1 0x00000000 ]

Current

family 1 __set%d t 0
        element 00007b00  : 0 [end]     element 00003500  : 0 [end]
inet t c
  [ meta load l4proto => reg 1 ]
  [ cmp eq reg 1 0x00000011 ]
  [ payload load 2b @ transport header + 2 => reg 1 ]
  [ lookup reg 1 set __set%d ]
  [ meta load nfproto => reg 1 ] ^^^ till here unnecessary payload expression
  [ cmp eq reg 1 0x00000002 ]
  [ payload load 1b @ network header + 1 => reg 1 ]
  [ bitwise reg 1 = ( reg 1 & 0x000000fc ) ^ 0x00000000 ]
  [ cmp eq reg 1 0x00000000 ]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Thats nft -c -d netlink -f sqm.nft ....

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Oh nice, I didn’t know about nft -c -d netlink -f. Thanks for pointing that out.

I checked the generated form and updated the output rules so meta nfproto comes before the UDP/ct match.

@dhrm1k
dhrm1k force-pushed the support-simple-qos-nft branch from 62b4b09 to 781d74d Compare July 3, 2026 17:02
@dhrm1k

dhrm1k commented Jul 3, 2026

Copy link
Copy Markdown
Contributor Author

this would break things for everyone who uses iptables.

@yhaenggi Fair point. I originally leaned toward adding nftables while keeping iptables as fallback.

I made this version nft-only based on the earlier discussion here: openwrt/packages#29830 (comment)

@brada4

brada4 commented Jul 3, 2026

Copy link
Copy Markdown

@yhaenggi addressing undefined ordering (set priority after default mangle hoks) nftables script can coexist with legacy tables, hope adding of nftables requirement is not a huge weight to carry.

@brada4

brada4 commented Jul 3, 2026

Copy link
Copy Markdown

@tohojo question about porting tos 4 - should it be 8 ranges or X known dscp codepoints falling in them?

@brada4

brada4 commented Jul 3, 2026

Copy link
Copy Markdown

@dhrm1k ill check back later when you get around my first sweep

@dhrm1k

dhrm1k commented Jul 3, 2026

Copy link
Copy Markdown
Contributor Author

@yhaenggi addressing undefined ordering (set priority after default mangle hoks) nftables script can coexist with legacy tables, hope adding of nftables requirement is not a huge weight to carry.

wait, your comment makes me think, you are under the impression we have kept legacy iptables intact in the code. is that the case? no, i ported it entirely to nft.

@brada4

brada4 commented Jul 3, 2026 •

Copy link
Copy Markdown

@dhrm1k

entirely to nft

You still want to evade unpredictable ordering vs fw4 hooks, which in turn saves both iptables-legacy installations (ie mine for testing and @yhaenggi 's one)

@dhrm1k

dhrm1k commented Jul 3, 2026

Copy link
Copy Markdown
Contributor Author

Got it, I misunderstood your point. You mean even with the script fully nft-based, the hook priority should be explicit so it does not race fw4 or legacy hooks.

@brada4

brada4 commented Jul 3, 2026

Copy link
Copy Markdown

Got it, I misunderstood your point. You mean even with the script fully nft-based, the hook priority should be explicit so it does not race fw4 or legacy hooks.

Yes, exactly. There is no problem under normal usage, but 2 days into uptime some hotplug event reloads one or other firewall table and packets start running into matrix unexplainably.

@dhrm1k
dhrm1k force-pushed the support-simple-qos-nft branch from 781d74d to b9a4757 Compare July 4, 2026 01:36
@brada4

brada4 commented Jul 4, 2026

Copy link
Copy Markdown

As much as fixed so far - perfect.

  • delete->destoy - also prepend one to table creation, there are users who dont stay in line and break stuff
  • dscp -> prio transforms , tos 4 - correct to wait until "final decision
  • jump tos_mark -> goto - I think I am right ;-)

@dhrm1k
dhrm1k force-pushed the support-simple-qos-nft branch from b9a4757 to cc5c10e Compare July 5, 2026 18:14
@dhrm1k

dhrm1k commented Jul 5, 2026

Copy link
Copy Markdown
Contributor Author

@tohojo what’s your take?

@dhrm1k

dhrm1k commented Jul 5, 2026

Copy link
Copy Markdown
Contributor Author

this is still pending.

dscp -> prio transforms , tos 4 - correct to wait until "final decision

@dhrm1k

dhrm1k commented Jul 18, 2026

Copy link
Copy Markdown
Contributor Author

gentle ping to the maintainers.

@tohojo tohojo left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

A few comments on the implementation, see below...

Comment thread src/simple.qos Outdated
local mark_bulk
local mark_prio

NFT_CLEAR_MASK=$(printf '0x%x' $((0xffffffff & ~MARK_MASK)))

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

AFAICT this is only used in nft_mark_set(), so let's just make it a local variable in there instead of this global variable that's not really declared anywhere else?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

moved the clear-mask calculation into nft_mark_set() and made it a local variable there, so it no longer leaks into global state.

Comment thread src/simple.qos
nft_rule " }"
nft_rule " chain prerouting {"
nft_rule " type filter hook prerouting priority mangle + 10; policy accept;"
nft_rule " iifname \"vtun*\" meta l4proto tcp ${mark_be}"

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

The comment explaining why this is there got dropped in the conversion

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

oops! restored the comment.

Comment thread src/simple.qos Outdated
ipt -t mangle -A QOS_MARK_${IFACE} -m dscp --dscp-class AF42 -j MARK --set-mark 0x1/${IPT_MASK}
ipt -t mangle -A QOS_MARK_${IFACE} -m tos --tos Minimize-Delay -j MARK --set-mark 0x1/${IPT_MASK}
nft_rule " meta nfproto ipv4 ip dscp cs1 ${mark_bulk}"
nft_rule " meta nfproto ipv6 ip6 dscp cs1 ${mark_bulk}"

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

The iptables version encapsulates the v4/v6 duplication in the function that installs the rules; let's do the same here? Something like:

nft_dscp_rule() {
  local arg
  arg="$1"

  nft_rule "        meta nfproto ipv4 ip $arg"
  nft_rule "        meta nfproto ipv6 ip6 $arg"
}

called like:

nft_dscp_rule "dscp cs6 ${mark_prio}"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

i added the suggested nft_dscp_rule() helper and converted the duplicated IPv4/IPv6 dscp rules to use it. I kept the minimize-delay equivalent separate because that rule is IPv4-only.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Did you forget to push the updated commits? I still see the old version in the GH interface... :)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Ah yes. So sorry. I will do it after few hours.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I have pushed changes

@tohojo

tohojo commented Aug 11, 2026

Copy link
Copy Markdown
Owner

this would break things for everyone who uses iptables.

As for this bit. Yes, this is obviously true. The question is - do we have any users actively using new versions of sqm-scripts and using the simple.qos script (i.e., not cake)? If so, I'd like to hear the details of this, so if you @yhaenggi (or someone else) know of such a use case, please do share the details!

@dhrm1k

dhrm1k commented Aug 11, 2026

Copy link
Copy Markdown
Contributor Author

Thank you for taking your time to review this. I will look into the points you raised.

As for this bit. Yes, this is obviously true.

I do agree with this. I want nft support, but I am open to keeping iptables intact. that's what I had in my mind until we discussed we want it to shift to nft entirely.

@brada4

brada4 commented Aug 11, 2026

Copy link
Copy Markdown

Use case is very low cpu devices

@moeller0

moeller0 commented Aug 11, 2026 •

Copy link
Copy Markdown
Collaborator

I am fine with dropping iptables, with a new sqm-scripts release, very low CPU devices likely can simply continue using older sqm-scripts, no?

@tohojo

tohojo commented Aug 11, 2026 via email

Copy link
Copy Markdown
Owner

@tohojo

tohojo commented Aug 11, 2026 via email

Copy link
Copy Markdown
Owner

Replace the iptables-based marking setup with native nftables rules.

This keeps the existing tc fw filter model, but uses nftables to install packet marks.

Signed-off-by: dharmik <dharmikparmar2004@yahoo.com>
@dhrm1k
dhrm1k force-pushed the support-simple-qos-nft branch from cc5c10e to 211c7d5 Compare August 12, 2026 19:48
@dhrm1k

dhrm1k commented Aug 23, 2026

Copy link
Copy Markdown
Contributor Author

just incase if notif got missed, I have made requested changes.

@tohojo
tohojo merged commit 77cf89e into tohojo:main Aug 25, 2026
1 check passed
@dhrm1k

dhrm1k commented Aug 25, 2026

Copy link
Copy Markdown
Contributor Author

Thank you for taking the time to review and work through this with me.

Are you planning to tag a new sqm-scripts release now that this is merged?
openwrt package is currently pinned to v1.7.2. Once there is a new release, I
can update the package and switch its dependencies from iptables and
iptables-mod-ipopt to nftables.

@tohojo

tohojo commented Aug 25, 2026 via email

Copy link
Copy Markdown
Owner

@fda77

fda77 commented Aug 26, 2026 •

Copy link
Copy Markdown

The simple.qos script now uses nftables instead of iptables to tag packets, and requires the nft binary to function. Other .qos scripts are unaffected.

I'm running openwrt without nft stuff but iptables. In SQM i'm using "Queue setup script" "piece_of_cake.qos". Does this still works without nft?
Or have it to use the older working version of sqm-script?

@dhrm1k

dhrm1k commented Aug 26, 2026

Copy link
Copy Markdown
Contributor Author

I'm running openwrt without nft stuff but iptables. In SQM i'm using "Queue setup script" "piece_of_cake.qos". Does this still works without nft? Or have it to use the older working version of sqm-script?

Yes, piece_of_cake.qos will continue to work without nftables. The nftables
requirement only applies to simple.qos.

@fda77

fda77 commented Aug 26, 2026

Copy link
Copy Markdown

Thanks for clarification!
In case you add nft to other qos scripts, please just add it and keep iptables as fallback

@brada4

brada4 commented Aug 26, 2026

Copy link
Copy Markdown

@fda77 nft is available for any curent lts kernel, you can use this nft alongside iptables-legacy (due to priority skew)

@fda77

fda77 commented Aug 26, 2026

Copy link
Copy Markdown

I know. I'm using iptables (only)

@brada4

brada4 commented Aug 26, 2026

Copy link
Copy Markdown

Good you know, there is no conflict then

@fda77

fda77 commented Aug 26, 2026

Copy link
Copy Markdown

omfg

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.

6 participants